1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ultima/ultima4/map/tile.h"
24 #include "ultima/ultima4/core/config.h"
25 #include "ultima/ultima4/game/context.h"
26 #include "ultima/ultima4/game/creature.h"
27 #include "ultima/ultima4/gfx/image.h"
28 #include "ultima/ultima4/gfx/imagemgr.h"
29 #include "ultima/ultima4/gfx/screen.h"
30 #include "ultima/ultima4/map/location.h"
31 #include "ultima/ultima4/core/settings.h"
32 #include "ultima/ultima4/map/tileanim.h"
33 #include "ultima/ultima4/map/tilemap.h"
34 #include "ultima/ultima4/map/tileset.h"
35 #include "ultima/ultima4/core/utils.h"
36 
37 namespace Ultima {
38 namespace Ultima4 {
39 
40 TileId Tile::_nextId = 0;
41 
Tile(Tileset * tileset)42 Tile::Tile(Tileset *tileset)
43 	: _id(_nextId++)
44 	, _name()
45 	, _tileSet(tileset)
46 	, _w(0)
47 	, _h(0)
48 	, _frames(0)
49 	, _scale(1)
50 	, _anim(nullptr)
51 	, _opaque(false)
52 	, _foreground()
53 	, _waterForeground()
54 	, rule(nullptr)
55 	, _imageName()
56 	, _looksLike()
57 	, _image(nullptr)
58 	, _tiledInDungeon(false)
59 	, _directions()
60 	, _animationRule("") {
61 }
62 
~Tile()63 Tile::~Tile() {
64 	deleteImage();
65 }
66 
loadProperties(const ConfigElement & conf)67 void Tile::loadProperties(const ConfigElement &conf) {
68 	if (conf.getName() != "tile")
69 		return;
70 
71 	_name = conf.getString("name");	// Get the name of the tile
72 
73 	// Get the animation for the tile, if one is specified
74 	if (conf.exists("animation")) {
75 		_animationRule = conf.getString("animation");
76 	}
77 
78 	// See if the tile is opaque
79 	_opaque = conf.getBool("opaque");
80 
81 	_foreground = conf.getBool("usesReplacementTileAsBackground");
82 	_waterForeground = conf.getBool("usesWaterReplacementTileAsBackground");
83 
84 	/* find the rule that applies to the current tile, if there is one.
85 	   if there is no rule specified, it defaults to the "default" rule */
86 	if (conf.exists("rule")) {
87 		rule = g_tileRules->findByName(conf.getString("rule"));
88 		if (rule == nullptr)
89 			rule = g_tileRules->findByName("default");
90 	} else {
91 		rule = g_tileRules->findByName("default");
92 	}
93 
94 	// Get the number of frames the tile has
95 	_frames = conf.getInt("frames", 1);
96 
97 	// Get the name of the image that belongs to this tile
98 	if (conf.exists("image"))
99 		_imageName = conf.getString("image");
100 	else
101 		_imageName = Common::String("tile_") + _name;
102 
103 	_tiledInDungeon = conf.getBool("tiledInDungeon");
104 
105 	if (conf.exists("directions")) {
106 		Common::String dirs = conf.getString("directions");
107 		if (dirs.size() != (unsigned) _frames)
108 			error("Error: %ld directions for tile but only %d frames", (long) dirs.size(), _frames);
109 		for (unsigned i = 0; i < dirs.size(); i++) {
110 			if (dirs[i] == 'w')
111 				_directions.push_back(DIR_WEST);
112 			else if (dirs[i] == 'n')
113 				_directions.push_back(DIR_NORTH);
114 			else if (dirs[i] == 'e')
115 				_directions.push_back(DIR_EAST);
116 			else if (dirs[i] == 's')
117 				_directions.push_back(DIR_SOUTH);
118 			else
119 				error("Error: unknown direction specified by %c", dirs[i]);
120 		}
121 	}
122 }
123 
getImage()124 Image *Tile::getImage() {
125 	if (!_image)
126 		loadImage();
127 	return _image;
128 }
129 
loadImage()130 void Tile::loadImage() {
131 	if (!_image) {
132 		_scale = settings._scale;
133 
134 		SubImage *subimage = nullptr;
135 
136 		ImageInfo *info = imageMgr->get(_imageName);
137 		if (!info) {
138 			subimage = imageMgr->getSubImage(_imageName);
139 			if (subimage)
140 				info = imageMgr->get(subimage->_srcImageName);
141 		}
142 		if (!info) { // IF still no info loaded
143 			warning("Error: couldn't load image for tile '%s'", _name.c_str());
144 			return;
145 		}
146 
147 		/* FIXME: This is a hack to address the fact that there are 4
148 		   frames for the guard in VGA mode, but only 2 in EGA. Is there
149 		   a better way to handle this? */
150 		if (_name == "guard") {
151 			if (settings._videoType == "EGA")
152 				_frames = 2;
153 			else
154 				_frames = 4;
155 		}
156 
157 
158 		if (info->_image)
159 			info->_image->alphaOff();
160 
161 		if (info) {
162 			_w = (subimage ? subimage->width() *_scale : info->_width * _scale / info->_prescale);
163 			_h = (subimage ? (subimage->height() * _scale) / _frames : (info->_height * _scale / info->_prescale) / _frames);
164 			_image = Image::create(_w, _h * _frames, false, Image::HARDWARE);
165 
166 
167 			//info->image->alphaOff();
168 
169 			// Draw the tile from the image we found to our tile image
170 			Image *tiles = info->_image;
171 			assert(tiles);
172 
173 			if (subimage) {
174 				tiles->drawSubRectOn(_image, 0, 0,
175 					subimage->left * _scale, subimage->top * _scale,
176 					subimage->width() * _scale, subimage->height() * _scale);
177 			} else {
178 				tiles->drawOn(_image, 0, 0);
179 			}
180 		}
181 
182 		if (_animationRule.size() > 0) {
183 			_anim = nullptr;
184 			if (g_screen->_tileAnims)
185 				_anim = g_screen->_tileAnims->getByName(_animationRule);
186 			if (_anim == nullptr)
187 				warning("Warning: animation style '%s' not found", _animationRule.c_str());
188 		}
189 
190 		/* if we have animations, we always used 'animated' to draw from */
191 		//if (anim)
192 		//    image->alphaOff();
193 
194 
195 	}
196 }
197 
deleteImage()198 void Tile::deleteImage() {
199 	if (_image) {
200 		delete _image;
201 		_image = nullptr;
202 	}
203 	_scale = settings._scale;
204 }
205 
isDungeonFloor() const206 bool Tile::isDungeonFloor() const {
207 	Tile *floor = _tileSet->getByName("brick_floor");
208 	if (_id == floor->_id)
209 		return true;
210 	return false;
211 }
212 
isOpaque() const213 bool Tile::isOpaque() const {
214 	return g_context->_opacity ? _opaque : false;
215 }
216 
isForeground() const217 bool Tile::isForeground() const {
218 	return (rule->_mask & MASK_FOREGROUND);
219 }
220 
directionForFrame(int frame) const221 Direction Tile::directionForFrame(int frame) const {
222 	if (static_cast<unsigned>(frame) >= _directions.size())
223 		return DIR_NONE;
224 	else
225 		return _directions[frame];
226 }
227 
frameForDirection(Direction d) const228 int Tile::frameForDirection(Direction d) const {
229 	for (int i = 0; (unsigned) i < _directions.size() && i < _frames; i++) {
230 		if (_directions[i] == d)
231 			return i;
232 	}
233 	return -1;
234 }
235 
236 } // End of namespace Ultima4
237 } // End of namespace Ultima
238