Files
2026-02-02 04:50:13 +01:00

239 lines
6.2 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima4/map/tile.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/creature.h"
#include "ultima/ultima4/gfx/image.h"
#include "ultima/ultima4/gfx/imagemgr.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/map/tileanim.h"
#include "ultima/ultima4/map/tilemap.h"
#include "ultima/ultima4/map/tileset.h"
#include "ultima/ultima4/core/utils.h"
namespace Ultima {
namespace Ultima4 {
TileId Tile::_nextId = 0;
Tile::Tile(Tileset *tileset)
: _id(_nextId++)
, _name()
, _tileSet(tileset)
, _w(0)
, _h(0)
, _frames(0)
, _scale(1)
, _anim(nullptr)
, _opaque(false)
, _foreground()
, _waterForeground()
, rule(nullptr)
, _imageName()
, _looksLike()
, _image(nullptr)
, _tiledInDungeon(false)
, _directions()
, _animationRule("") {
}
Tile::~Tile() {
deleteImage();
}
void Tile::loadProperties(const ConfigElement &conf) {
if (conf.getName() != "tile")
return;
_name = conf.getString("name"); // Get the name of the tile
// Get the animation for the tile, if one is specified
if (conf.exists("animation")) {
_animationRule = conf.getString("animation");
}
// See if the tile is opaque
_opaque = conf.getBool("opaque");
_foreground = conf.getBool("usesReplacementTileAsBackground");
_waterForeground = conf.getBool("usesWaterReplacementTileAsBackground");
/* find the rule that applies to the current tile, if there is one.
if there is no rule specified, it defaults to the "default" rule */
if (conf.exists("rule")) {
rule = g_tileRules->findByName(conf.getString("rule"));
if (rule == nullptr)
rule = g_tileRules->findByName("default");
} else {
rule = g_tileRules->findByName("default");
}
// Get the number of frames the tile has
_frames = conf.getInt("frames", 1);
// Get the name of the image that belongs to this tile
if (conf.exists("image"))
_imageName = conf.getString("image");
else
_imageName = Common::String("tile_") + _name;
_tiledInDungeon = conf.getBool("tiledInDungeon");
if (conf.exists("directions")) {
Common::String dirs = conf.getString("directions");
if (dirs.size() != (unsigned) _frames)
error("Error: %ld directions for tile but only %d frames", (long) dirs.size(), _frames);
for (unsigned i = 0; i < dirs.size(); i++) {
if (dirs[i] == 'w')
_directions.push_back(DIR_WEST);
else if (dirs[i] == 'n')
_directions.push_back(DIR_NORTH);
else if (dirs[i] == 'e')
_directions.push_back(DIR_EAST);
else if (dirs[i] == 's')
_directions.push_back(DIR_SOUTH);
else
error("Error: unknown direction specified by %c", dirs[i]);
}
}
}
Image *Tile::getImage() {
if (!_image)
loadImage();
return _image;
}
void Tile::loadImage() {
if (!_image) {
_scale = settings._scale;
SubImage *subimage = nullptr;
ImageInfo *info = imageMgr->get(_imageName);
if (!info) {
subimage = imageMgr->getSubImage(_imageName);
if (subimage)
info = imageMgr->get(subimage->_srcImageName);
}
if (!info) { // IF still no info loaded
warning("Error: couldn't load image for tile '%s'", _name.c_str());
return;
}
/* FIXME: This is a hack to address the fact that there are 4
frames for the guard in VGA mode, but only 2 in EGA. Is there
a better way to handle this? */
if (_name == "guard") {
if (settings._videoType == "EGA")
_frames = 2;
else
_frames = 4;
}
if (info->_image)
info->_image->alphaOff();
if (info) {
// Draw the tile from the image we found to our tile image
Image *tiles = info->_image;
assert(tiles);
_w = (subimage ? subimage->width() *_scale : info->_width * _scale / info->_prescale);
_h = (subimage ? (subimage->height() * _scale) / _frames : (info->_height * _scale / info->_prescale) / _frames);
_image = Image::create(_w, _h * _frames, tiles->format());
if (_image->isIndexed())
_image->setPaletteFromImage(tiles);
//info->image->alphaOff();
if (subimage) {
tiles->drawSubRectOn(_image, 0, 0,
subimage->left * _scale, subimage->top * _scale,
subimage->width() * _scale, subimage->height() * _scale);
} else {
tiles->drawOn(_image, 0, 0);
}
}
if (_animationRule.size() > 0) {
_anim = nullptr;
if (g_screen->_tileAnims)
_anim = g_screen->_tileAnims->getByName(_animationRule);
if (_anim == nullptr)
warning("Warning: animation style '%s' not found", _animationRule.c_str());
}
/* if we have animations, we always used 'animated' to draw from */
//if (anim)
// image->alphaOff();
}
}
void Tile::deleteImage() {
if (_image) {
delete _image;
_image = nullptr;
}
_scale = settings._scale;
}
bool Tile::isDungeonFloor() const {
Tile *floor = _tileSet->getByName("brick_floor");
if (_id == floor->_id)
return true;
return false;
}
bool Tile::isOpaque() const {
return g_context->_opacity ? _opaque : false;
}
bool Tile::isForeground() const {
return (rule->_mask & MASK_FOREGROUND);
}
Direction Tile::directionForFrame(int frame) const {
if (static_cast<unsigned>(frame) >= _directions.size())
return DIR_NONE;
else
return _directions[frame];
}
int Tile::frameForDirection(Direction d) const {
for (int i = 0; (unsigned) i < _directions.size() && i < _frames; i++) {
if (_directions[i] == d)
return i;
}
return -1;
}
} // End of namespace Ultima4
} // End of namespace Ultima