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