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/tileset.h"
24 #include "ultima/ultima4/core/config.h"
25 #include "ultima/ultima4/gfx/screen.h"
26 #include "ultima/ultima4/core/settings.h"
27 #include "ultima/ultima4/map/tile.h"
28 #include "ultima/ultima4/map/tilemap.h"
29 
30 namespace Ultima {
31 namespace Ultima4 {
32 
33 TileRules *g_tileRules;
34 TileSets *g_tileSets;
35 
TileRules()36 TileRules::TileRules() {
37 	g_tileRules = this;
38 }
39 
~TileRules()40 TileRules::~TileRules() {
41 	// Delete the tile rules
42 	for (iterator it = begin(); it != end(); ++it)
43 		delete it->_value;
44 
45 	g_tileRules = nullptr;
46 }
47 
load()48 void TileRules::load() {
49 	const Config *config = Config::getInstance();
50 	Std::vector<ConfigElement> rules = config->getElement("tileRules").getChildren();
51 
52 	for (Std::vector<ConfigElement>::iterator i = rules.begin(); i != rules.end(); i++) {
53 		TileRule *rule = new TileRule();
54 		rule->initFromConf(*i);
55 		(*this)[rule->_name] = rule;
56 	}
57 
58 	if (findByName("default") == nullptr)
59 		error("no 'default' rule found in tile rules");
60 }
61 
findByName(const Common::String & name)62 TileRule *TileRules::findByName(const Common::String &name) {
63 	TileRuleMap::iterator i = find(name);
64 	if (i != end())
65 		return i->_value;
66 	return nullptr;
67 }
68 
69 /*-------------------------------------------------------------------*/
70 
TileSets()71 TileSets::TileSets() {
72 	g_tileSets = this;
73 	loadAll();
74 }
75 
~TileSets()76 TileSets::~TileSets() {
77 	unloadAll();
78 	g_tileSets = nullptr;
79 }
80 
loadAll()81 void TileSets::loadAll() {
82 	const Config *config = Config::getInstance();
83 	Std::vector<ConfigElement> conf;
84 
85 	unloadAll();
86 
87 	// Get the config element for all tilesets
88 	conf = config->getElement("tilesets").getChildren();
89 
90 	// Load tile rules
91 	if (g_tileRules->empty())
92 		g_tileRules->load();
93 
94 	// Load all of the tilesets
95 	for (Std::vector<ConfigElement>::iterator i = conf.begin(); i != conf.end(); i++) {
96 		if (i->getName() == "tileset") {
97 
98 			Tileset *tileset = new Tileset();
99 			tileset->load(*i);
100 
101 			(*this)[tileset->_name] = tileset;
102 		}
103 	}
104 }
105 
unloadAll()106 void TileSets::unloadAll() {
107 	iterator i;
108 
109 	for (i = begin(); i != end(); i++) {
110 		i->_value->unload();
111 		delete i->_value;
112 	}
113 	clear();
114 
115 	Tile::resetNextId();
116 }
117 
unloadAllImages()118 void TileSets::unloadAllImages() {
119 	iterator i;
120 
121 	for (i = begin(); i != end(); i++) {
122 		i->_value->unloadImages();
123 	}
124 
125 	Tile::resetNextId();
126 }
127 
get(const Common::String & name)128 Tileset *TileSets::get(const Common::String &name) {
129 	if (find(name) != end())
130 		return (*this)[name];
131 	else
132 		return nullptr;
133 }
134 
findTileByName(const Common::String & name)135 Tile *TileSets::findTileByName(const Common::String &name) {
136 	iterator i;
137 	for (i = begin(); i != end(); i++) {
138 		Tile *t = i->_value->getByName(name);
139 		if (t)
140 			return t;
141 	}
142 
143 	return nullptr;
144 }
145 
findTileById(TileId id)146 Tile *TileSets::findTileById(TileId id) {
147 	iterator i;
148 	for (i = begin(); i != end(); i++) {
149 		Tile *t = i->_value->get(id);
150 		if (t)
151 			return t;
152 	}
153 
154 	return nullptr;
155 }
156 
157 
158 /*-------------------------------------------------------------------*/
159 
initFromConf(const ConfigElement & conf)160 bool TileRule::initFromConf(const ConfigElement &conf) {
161 	uint i;
162 
163 	static const struct {
164 		const char *name;
165 		uint mask;
166 	} booleanAttributes[] = {
167 		{ "dispel", MASK_DISPEL },
168 		{ "talkover", MASK_TALKOVER },
169 		{ "door", MASK_DOOR },
170 		{ "lockeddoor", MASK_LOCKEDDOOR },
171 		{ "chest", MASK_CHEST },
172 		{ "ship", MASK_SHIP },
173 		{ "horse", MASK_HORSE },
174 		{ "balloon", MASK_BALLOON },
175 		{ "canattackover", MASK_ATTACKOVER },
176 		{ "canlandballoon", MASK_CANLANDBALLOON },
177 		{ "replacement", MASK_REPLACEMENT },
178 		{ "foreground", MASK_FOREGROUND },
179 		{ "onWaterOnlyReplacement", MASK_WATER_REPLACEMENT},
180 		{ "livingthing", MASK_LIVING_THING }
181 	};
182 
183 	static const struct {
184 		const char *_name;
185 		uint _mask;
186 	} movementBooleanAttr[] = {
187 		{ "swimable", MASK_SWIMABLE },
188 		{ "sailable", MASK_SAILABLE },
189 		{ "unflyable", MASK_UNFLYABLE },
190 		{ "creatureunwalkable", MASK_CREATURE_UNWALKABLE }
191 	};
192 	static const char *speedEnumStrings[] = { "fast", "slow", "vslow", "vvslow", nullptr };
193 	static const char *effectsEnumStrings[] = { "none", "fire", "sleep", "poison", "poisonField", "electricity", "lava", nullptr };
194 
195 	_mask = 0;
196 	_movementMask = 0;
197 	_speed = FAST;
198 	_effect = EFFECT_NONE;
199 	_walkOnDirs = MASK_DIR_ALL;
200 	_walkOffDirs = MASK_DIR_ALL;
201 	_name = conf.getString("name");
202 
203 	for (i = 0; i < sizeof(booleanAttributes) / sizeof(booleanAttributes[0]); i++) {
204 		if (conf.getBool(booleanAttributes[i].name))
205 			_mask |= booleanAttributes[i].mask;
206 	}
207 
208 	for (i = 0; i < sizeof(movementBooleanAttr) / sizeof(movementBooleanAttr[0]); i++) {
209 		if (conf.getBool(movementBooleanAttr[i]._name))
210 			_movementMask |= movementBooleanAttr[i]._mask;
211 	}
212 
213 	Common::String cantwalkon = conf.getString("cantwalkon");
214 	if (cantwalkon == "all")
215 		_walkOnDirs = 0;
216 	else if (cantwalkon == "west")
217 		_walkOnDirs = DIR_REMOVE_FROM_MASK(DIR_WEST, _walkOnDirs);
218 	else if (cantwalkon == "north")
219 		_walkOnDirs = DIR_REMOVE_FROM_MASK(DIR_NORTH, _walkOnDirs);
220 	else if (cantwalkon == "east")
221 		_walkOnDirs = DIR_REMOVE_FROM_MASK(DIR_EAST, _walkOnDirs);
222 	else if (cantwalkon == "south")
223 		_walkOnDirs = DIR_REMOVE_FROM_MASK(DIR_SOUTH, _walkOnDirs);
224 	else if (cantwalkon == "advance")
225 		_walkOnDirs = DIR_REMOVE_FROM_MASK(DIR_ADVANCE, _walkOnDirs);
226 	else if (cantwalkon == "retreat")
227 		_walkOnDirs = DIR_REMOVE_FROM_MASK(DIR_RETREAT, _walkOnDirs);
228 
229 	Common::String cantwalkoff = conf.getString("cantwalkoff");
230 	if (cantwalkoff == "all")
231 		_walkOffDirs = 0;
232 	else if (cantwalkoff == "west")
233 		_walkOffDirs = DIR_REMOVE_FROM_MASK(DIR_WEST, _walkOffDirs);
234 	else if (cantwalkoff == "north")
235 		_walkOffDirs = DIR_REMOVE_FROM_MASK(DIR_NORTH, _walkOffDirs);
236 	else if (cantwalkoff == "east")
237 		_walkOffDirs = DIR_REMOVE_FROM_MASK(DIR_EAST, _walkOffDirs);
238 	else if (cantwalkoff == "south")
239 		_walkOffDirs = DIR_REMOVE_FROM_MASK(DIR_SOUTH, _walkOffDirs);
240 	else if (cantwalkoff == "advance")
241 		_walkOffDirs = DIR_REMOVE_FROM_MASK(DIR_ADVANCE, _walkOffDirs);
242 	else if (cantwalkoff == "retreat")
243 		_walkOffDirs = DIR_REMOVE_FROM_MASK(DIR_RETREAT, _walkOffDirs);
244 
245 	_speed = static_cast<TileSpeed>(conf.getEnum("speed", speedEnumStrings));
246 	_effect = static_cast<TileEffect>(conf.getEnum("effect", effectsEnumStrings));
247 
248 	return true;
249 }
250 
load(const ConfigElement & tilesetConf)251 void Tileset::load(const ConfigElement &tilesetConf) {
252 	_name = tilesetConf.getString("name");
253 	if (tilesetConf.exists("imageName"))
254 		_imageName = tilesetConf.getString("imageName");
255 	if (tilesetConf.exists("extends"))
256 		_extends = g_tileSets->get(tilesetConf.getString("extends"));
257 	else
258 		_extends = nullptr;
259 
260 	int index = 0;
261 	Std::vector<ConfigElement> children = tilesetConf.getChildren();
262 	for (Std::vector<ConfigElement>::iterator i = children.begin(); i != children.end(); i++) {
263 		if (i->getName() != "tile")
264 			continue;
265 
266 		Tile *tile = new Tile(this);
267 		tile->loadProperties(*i);
268 
269 		// Add the tile to our tileset
270 		_tiles[tile->getId()] = tile;
271 		_nameMap[tile->getName()] = tile;
272 
273 		index += tile->getFrames();
274 	}
275 	_totalFrames = index;
276 }
277 
unloadImages()278 void Tileset::unloadImages() {
279 	Tileset::TileIdMap::iterator i;
280 
281 	// Free all the image memory and nullify so that reloading can automatically take place lazily
282 	for (i = _tiles.begin(); i != _tiles.end(); i++) {
283 		i->_value->deleteImage();
284 	}
285 }
286 
unload()287 void Tileset::unload() {
288 	Tileset::TileIdMap::iterator i;
289 
290 	// Free all the memory for the tiles
291 	for (i = _tiles.begin(); i != _tiles.end(); i++)
292 		delete i->_value;
293 
294 	_tiles.clear();
295 	_totalFrames = 0;
296 	_imageName.clear();
297 }
298 
get(TileId id)299 Tile *Tileset::get(TileId id) {
300 	if (_tiles.find(id) != _tiles.end())
301 		return _tiles[id];
302 	else if (_extends)
303 		return _extends->get(id);
304 	return nullptr;
305 }
306 
getByName(const Common::String & name)307 Tile *Tileset::getByName(const Common::String &name) {
308 	if (_nameMap.find(name) != _nameMap.end())
309 		return _nameMap[name];
310 	else if (_extends)
311 		return _extends->getByName(name);
312 	else
313 		return nullptr;
314 }
315 
getImageName() const316 Common::String Tileset::getImageName() const {
317 	if (_imageName.empty() && _extends)
318 		return _extends->getImageName();
319 	else
320 		return _imageName;
321 }
322 
numTiles() const323 uint Tileset::numTiles() const {
324 	return _tiles.size();
325 }
326 
numFrames() const327 uint Tileset::numFrames() const {
328 	return _totalFrames;
329 }
330 
331 } // End of namespace Ultima4
332 } // End of namespace Ultima
333