1 
2 /* Battle Tanks Game
3  * Copyright (C) 2006-2009 Battle Tanks team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 
20 /*
21  * Additional rights can be granted beyond the GNU General Public License
22  * on the terms provided in the Exception. If you modify this file,
23  * you may extend this exception to your version of the file,
24  * but you are not obligated to do so. If you do not wish to provide this
25  * exception without modification, you must delete this exception statement
26  * from your version and license this file solely under the GPL without exception.
27 */
28 
29 #include "resource_manager.h"
30 #include "mrt/logger.h"
31 #include "sdlx/surface.h"
32 #include "sdlx/font.h"
33 #include "sdlx/c_map.h"
34 #include "object.h"
35 #include "animation_model.h"
36 #include "utils.h"
37 #include "sound/mixer.h"
38 #include "config.h"
39 #include "finder.h"
40 #include "xml_parser.h"
41 #include "rt_config.h"
42 
43 #include <algorithm>
44 
45 IMPLEMENT_SINGLETON(ResourceManager, IResourceManager);
46 
47 class PreloadParser : public XMLParser {
48 public:
start(const std::string & name,Attrs & attr)49 	virtual void start(const std::string &name, Attrs &attr) {
50 		if (name == "object") {
51 			std::string id = attr["id"];
52 			if (id.empty())
53 				return;
54 
55 			if (current_map.empty()) {
56 				//parent object
57 				current_object = attr["id"];
58 			} else {
59 				data[current_map].insert(id);
60 			}
61 		} else if (name == "map") {
62 			current_map = attr["id"];
63 		} else if (name == "animation") {
64 			std::string id = attr["id"];
65 			if (current_object.empty() || id.empty())
66 				return;
67 			object_data[current_object].insert(id);
68 		}
69 	}
end(const std::string & name)70 	virtual void end(const std::string &name) {
71 		if (name == "object") {
72 			current_object.clear();
73 		} else if (name == "map") {
74 			current_map.clear();
75 		}
76 	}
update(IResourceManager::PreloadMap & preload_map,IResourceManager::PreloadMap & object_map,const std::string & base) const77 	void update(IResourceManager::PreloadMap &preload_map, IResourceManager::PreloadMap &object_map, const std::string &base) const {
78 		for(PreloadMap::const_iterator i = object_data.begin(); i != object_data.end(); ++i) {
79 			const std::set<std::string> &src = i->second;
80 			std::set<std::string> &dst = object_map[std::pair<std::string, std::string>(base, i->first)];
81 			for(std::set<std::string>::const_iterator j = src.begin(); j != src.end(); ++j) {
82 				dst.insert(*j);
83 			}
84 		}
85 
86 		for(PreloadMap::const_iterator i = data.begin(); i != data.end(); ++i) {
87 			const std::set<std::string> &src = i->second;
88 			std::set<std::string> &dst = preload_map[std::pair<std::string, std::string>(base, i->first)];
89 			for(std::set<std::string>::const_iterator j = src.begin(); j != src.end(); ++j) {
90 				dst.insert(*j);
91 			}
92 		}
93 	}
94 private:
95 	typedef std::map<const std::string, std::set<std::string> > PreloadMap;
96 
97 	std::string current_object, current_map;
98 	PreloadMap data, object_data;
99 };
100 
onFile(const std::string & base,const std::string & file)101 void IResourceManager::onFile(const std::string &base, const std::string &file) {
102 	_base_dir = base;
103 
104 	if (base.empty())
105 		return;
106 
107 	TRY {
108 		std::string preload = Finder->find(base, "preload.xml", false);
109 		if (preload.empty())
110 			return;
111 		LOG_DEBUG(("parsing preload file: %s", preload.c_str()));
112 		PreloadParser p;
113 		p.parse_file(preload);
114 		p.update(_preload_map, _object_preload_map, base);
115 	} CATCH("parsing preload file", {});
116 }
117 
start(const std::string & name,Attrs & attr)118 void IResourceManager::start(const std::string &name, Attrs &attr) {
119 	if (name == "resources") {
120 		_tw = atol(attr["tile_width"].c_str());
121 		if (_tw == 0)
122 			throw_ex(("resources tag must contain `tile_width' attribute (default tile width)"));
123 		_th = atol(attr["tile_height"].c_str());
124 		if (_th == 0)
125 			throw_ex(("resources tag must contain `tile_height' attribute (default tile height)"));
126 		if (attr["version"].empty())
127 			throw_ex(("resources tag must contain `version' attribute"));
128 		LOG_DEBUG(("file version: %s", attr["version"].c_str()));
129 	} else if (name == "animation") {
130 		status = "animation";
131 		const std::string &id = attr["id"];
132 		if (id.empty())
133 			throw_ex(("animation.id was not set"));
134 
135 		const std::string &model = attr["model"];
136 		if (model.empty())
137 			throw_ex(("animation.model was not set"));
138 
139 		long tw = atol(attr["tile_width"].c_str());
140 		long th = atol(attr["tile_height"].c_str());
141 		long sz = atol(attr["size"].c_str());
142 		if (tw == 0) tw = _tw;
143 		if (th == 0) th = _th;
144 		if (sz != 0) tw = th = sz;
145 
146 		sdlx::Surface *s = NULL;
147 		sdlx::CollisionMap *cmap = NULL;
148 		bool real_load = !attr["persistent"].empty();
149 
150 		GET_CONFIG_VALUE("engine.preload", bool , preload_all, false);
151 		real_load |= preload_all;
152 
153 		std::string &tile = attr["tile"];
154 		if (_base_dir.empty())
155 			throw_ex(("base directory was not defined (multiply resources tag ? invalid resource structure?)"));
156 
157 		if (_surfaces.find(tile) == _surfaces.end()) {
158 			TRY {
159 				if (real_load) {
160 					mrt::Chunk data;
161 					std::string tname = "tiles/" + tile;
162 					Finder->load(data, tname);
163 
164 					s = new sdlx::Surface;
165 					s->load_image(data);
166 					s->display_format_alpha();
167 
168 					cmap = create_cmap(s, tname);
169 
170 					LOG_DEBUG(("loaded animation '%s'", id.c_str()));
171 				}
172 
173 				_surfaces[tile] = s;
174 				s = NULL;
175 
176 				_cmaps[tile] = cmap;
177 				cmap = NULL;
178 
179 			} CATCH(mrt::format_string("loading animation \"%s\"", tile.c_str()).c_str(), { delete s; s = NULL; delete cmap; cmap = NULL; throw; });
180 		//
181 		} else {
182 			LOG_DEBUG(("tile '%s' was already loaded, skipped.", tile.c_str()));
183 		}
184 
185 		_animations[id] = new Animation(model, _base_dir, tile, tw, th);
186 
187 	} else if (name == "animation-model") {
188 		status = "model";
189 
190 		const std::string & id = attr["id"];
191 		if (id.empty())
192 			throw_ex(("animation model must have id"));
193 
194 		float speed = atof(attr["speed"].c_str());
195 		if (speed == 0)
196 			throw_ex(("animation model must have default speed"));
197 
198 		_am = new AnimationModel(speed);
199 		_am_id = id;
200 	} else if (name == "pose") {
201 		if (_am == NULL)
202 			throw_ex(("pose tag must have parent animation-model"));
203 		_pose_id = attr["id"];
204 		if (_pose_id.empty())
205 			throw_ex(("pose must have id"));
206 		float speed = atof(attr["speed"].c_str());
207 		if (speed == 0)
208 			speed = _am->default_speed;
209 
210 		int z = (!attr["z"].empty())?atoi(attr["z"].c_str()) : -100001;
211 		const std::string &sound = attr["sound"];
212 		_pose = new Pose(speed, z, sound);
213 		const std::string &gain = attr["gain"];
214 		if (!gain.empty()) {
215 			_pose->gain = atof(gain.c_str());
216 			LOG_DEBUG(("adjusting gain for sample %s to %g", sound.c_str(), _pose->gain));
217 		}
218 		const std::string &notify = attr["notify"];
219 		if (!notify.empty() && (notify[0] == 't' || notify[0] == '1'))
220 			_pose->need_notify = true;
221 		if (!sound.empty() && sound[0] != '@')
222 			Mixer->loadSample(sound);
223 	} else if (name == "object") {
224 		status = "object";
225 		const std::string classname = attr["class"];
226 		if (classname.empty())
227 			throw_ex(("tag 'object' must provide its classname id."));
228 		ObjectMap::iterator object;
229 		if ((object = _objects.find(classname)) == _objects.end()) {
230 			throw_ex(("class '%s' was not registered. ", classname.c_str()));
231 		}
232 		LOG_DEBUG(("setting up class '%s'", classname.c_str()));
233 
234 		if (attr.find("parent") != attr.end())  {
235 			ObjectMap::iterator parent;
236 			if ((parent = _objects.find(attr["parent"])) == _objects.end()) {
237 				throw_ex(("class '%s' declared as parent of '%s' was not registered. skipped.", attr["parent"].c_str(), classname.c_str()));
238 			}
239 			object->second->inherit_parameters(parent->second);
240 		}
241 
242 		for (Attrs::iterator i = attr.begin(); i != attr.end(); ++i) {
243 			const std::string &name = i->first;
244 			const std::string &value = i->second;
245 			if (name == "speed") {
246 				object->second->speed = atol(value.c_str());
247 			} else if (name == "mass") {
248 				object->second->mass = atof(value.c_str());
249 			} else if (name == "ttl") {
250 				object->second->ttl = atof(value.c_str());
251 			} else if (name == "piercing") {
252 				object->second->piercing = (value[0] == 't' || value[0] == '1' || value[0] == 'y');
253 			} else if (name == "pierceable") {
254 				object->second->pierceable = (value[0] == 't' || value[0] == '1' || value[0] == 'y');
255 			} else if (name == "hp") {
256 				object->second->max_hp = object->second->hp = atol(value.c_str());
257 			} else if (name == "impassability") {
258 				object->second->impassability = atof(value.c_str());
259 			} else if (name == "fadeout_time") {
260 				object->second->fadeout_time = atof(value.c_str());
261 			} else if (name == "z") {
262 				object->second->set_z(atoi(value.c_str()));
263 			} else if (name != "class" && name != "parent")
264 				LOG_WARN(("attr '%s' is not supported", name.c_str()));
265 		}
266 		LOG_DEBUG(("%s", object->second->dump().c_str()));
267 	} else if (name == "alias") {
268 		status = "object";
269 		std::string name = attr["name"];
270 		std::string classname = attr["class"];
271 		if (name.empty() || classname.empty())
272 			throw_ex(("alias must have both 'name' and 'class' attributes"));
273 		createAlias(name, classname);
274 	} else if (name == "sound") {
275 		status = "sound";
276 		std::string file = attr["file"];
277 		if (file.empty())
278 			throw_ex(("sound.file MUST not be empty."));
279 		TRY {
280 			Mixer->loadSample(file, attr["class"]);
281 		} CATCH("loadSample", {});
282 	} else LOG_WARN(("unhandled tag: %s", name.c_str()));
283 	NotifyingXMLParser::start(name, attr);
284 }
285 
end(const std::string & name)286 void IResourceManager::end(const std::string &name) {
287 	mrt::trim(_data);
288 	if (name == "pose") {
289 		LOG_DEBUG(("pose frames: %s", _data.c_str()));
290 		std::vector<std::string> frames;
291 		mrt::split(frames, _data, ",");
292 
293 		for(size_t i = 0; i < frames.size(); ++i) {
294 			//LOG_DEBUG(("%d: %s", i, frames[i].c_str()));
295 			mrt::trim(frames[i]);
296 			unsigned int frame = atoi(frames[i].c_str());
297 			//LOG_DEBUG(("%d: %d", i, frame));
298 			_pose->frames.push_back(frame);
299 		}
300 		_am->addPose(_pose_id, _pose);
301 		_pose = NULL;
302 	} else if (name == "animation-model") {
303 		delete _animation_models[_am_id];
304 		_animation_models[_am_id] = _am;
305 		_am = NULL;
306 		LOG_DEBUG(("added animation model '%s'", _am_id.c_str()));
307 	} else if (name == "resources") {
308 		_base_dir.clear();
309 	}
310 	NotifyingXMLParser::end(name);
311 	_data.clear();
312 }
cdata(const std::string & data)313 void IResourceManager::cdata(const std::string &data) {
314 	_data += data;
315 }
316 
IResourceManager()317 IResourceManager::IResourceManager() : _am(0) {
318 }
319 
hasAnimation(const std::string & id) const320 const bool IResourceManager::hasAnimation(const std::string &id) const {
321 	return _animations.find(id) != _animations.end();
322 }
323 
getAnimation(const std::string & id)324 Animation *IResourceManager::getAnimation(const std::string &id) {
325 	AnimationMap::iterator i = _animations.find(id);
326 #ifdef DEBUG
327 	assert(i != _animations.end());
328 #endif
329 	if (i == _animations.end())
330 		throw_ex(("could not find animation with id '%s'", id.c_str()));
331 	return i->second;
332 }
333 
getAnimation(const std::string & id) const334 const Animation *IResourceManager::getAnimation(const std::string &id) const {
335 	AnimationMap::const_iterator i = _animations.find(id);
336 #ifdef DEBUG
337 	assert(i != _animations.end());
338 #endif
339 	if (i == _animations.end())
340 		throw_ex(("could not find animation with id '%s'", id.c_str()));
341 	return i->second;
342 }
343 
get_animation_model(const std::string & id)344 AnimationModel *IResourceManager::get_animation_model(const std::string &id) {
345 	AnimationModelMap::iterator i = _animation_models.find(id);
346 	if (i == _animation_models.end())
347 		throw_ex(("could not find animation model with id '%s'", id.c_str()));
348 	return i->second;
349 }
350 
get_surface(const std::string & id) const351 const sdlx::Surface *IResourceManager::get_surface(const std::string &id) const  {
352 	SurfaceMap::const_iterator i = _surfaces.find(id);
353 	if (i == _surfaces.end())
354 		throw_ex(("could not find surface with id '%s'", id.c_str()));
355 	return i->second;
356 }
357 
unload_surface(const std::string & id)358 void IResourceManager::unload_surface(const std::string &id) {
359 	SurfaceMap::iterator i = _surfaces.find(id);
360 	if (i == _surfaces.end())
361 		return;
362 	delete i->second;
363 	_surfaces.erase(i);
364 }
365 
load_surface(const std::string & id,int scale_to_w,int scale_to_h)366 const sdlx::Surface *IResourceManager::load_surface(const std::string &id, int scale_to_w, int scale_to_h) {
367 	SurfaceMap::iterator i = _surfaces.find(id);
368 	if (i != _surfaces.end() && i->second != NULL)
369 		return i->second;
370 
371 	sdlx::Surface *s = NULL;
372 		TRY {
373 			GET_CONFIG_VALUE("engine.generate-alpha-tiles", bool, gat, false);
374 			mrt::Chunk data;
375 			std::string tname = "tiles/" + id;
376 			Finder->load(data, tname);
377 
378 			s = new sdlx::Surface;
379 			s->load_image(data);
380 			LOG_DEBUG(("loaded surface '%s'", id.c_str()));
381 			if (scale_to_w != 0 || scale_to_h != 0) {
382 				if (scale_to_w == 0)
383 					scale_to_w = scale_to_h * s->get_width() / s->get_height();
384 				if (scale_to_h == 0)
385 					scale_to_h = scale_to_w * s->get_height() / s->get_width();
386 				LOG_DEBUG(("scaling surface to %dx%d", scale_to_w, scale_to_h));
387 				s->zoom(1.0 * scale_to_w / s->get_width(), 1.0 * scale_to_h / s->get_height());
388 			}
389 			s->display_format_alpha();
390 			_surfaces[id] = s;
391 		} CATCH("loading surface", { delete s; throw; });
392 	return s;
393 }
394 
loadFont(const std::string & name,const bool alpha)395 const sdlx::Font *IResourceManager::loadFont(const std::string &name, const bool alpha) {
396 	std::pair<std::string, bool> id(name, alpha);
397 	FontMap::iterator i = _fonts.find(id);
398 	if (i != _fonts.end() && i->second != NULL)
399 		return i->second;
400 
401 	sdlx::Font *f = NULL;
402 		TRY {
403 			mrt::Chunk data;
404 			Finder->load(data, "font/" + name + ".png");
405 			f = new sdlx::Font;
406 			f->load(data, sdlx::Font::Ascii, alpha);
407 			LOG_DEBUG(("loaded font '%s'", name.c_str()));
408 			_fonts[id] = f;
409 		} CATCH("loading font", { delete f; throw; });
410 
411 		mrt::Chunk data;
412 		const std::string page0400 = Finder->find("font/" + name + "_0400.png", false);
413 		if (!page0400.empty()) {
414 			Finder->load(data, "font/" + name + "_0400.png");
415 			f->add_page(0x0400, data, alpha);
416 		}
417 
418 		const std::string page0080 = Finder->find("font/" + name + "_0080.png", false);
419 		if (!page0080.empty()) {
420 			Finder->load(data, "font/" + name + "_0080.png");
421 			f->add_page(0x00a0, data, alpha);
422 		}
423 
424 		const std::string page2460 = Finder->find("font/" + name + "_2460.png", false);
425 		if (!page2460.empty()) {
426 			Finder->load(data, "font/" + name + "_2460.png");
427 			f->add_page(0x2460, data, alpha);
428 		}
429 	return f;
430 }
431 
432 
getCollisionMap(const std::string & id) const433 const sdlx::CollisionMap *IResourceManager::getCollisionMap(const std::string &id) const  {
434 	CollisionMap::const_iterator i = _cmaps.find(id);
435 	if (i == _cmaps.end())
436 		throw_ex(("could not find collision map with id '%s'", id.c_str()));
437 	return i->second;
438 }
439 
440 
init(const std::vector<std::pair<std::string,std::string>> & fname)441 void IResourceManager::init(const std::vector<std::pair<std::string, std::string> > &fname) {
442 	parse_files(fname);
443 	status = "menu"; //small hack. menu takes some time to setup and load.
444 }
445 
446 #include "mrt/file.h"
447 #include "tmx/map.h"
448 #include "mrt/directory.h"
449 
clear()450 void IResourceManager::clear() {
451 	LOG_DEBUG(("freeing resources"));
452 	std::for_each(_animations.begin(), _animations.end(), delete_ptr2<AnimationMap::value_type>());
453 	_animations.clear();
454 	std::for_each(_animation_models.begin(), _animation_models.end(), delete_ptr2<AnimationModelMap::value_type>());
455 	_animation_models.clear();
456 	std::for_each(_surfaces.begin(), _surfaces.end(), delete_ptr2<SurfaceMap::value_type>());
457 	_surfaces.clear();
458 	std::for_each(_cmaps.begin(), _cmaps.end(), delete_ptr2<CollisionMap::value_type>());
459 	_cmaps.clear();
460 	std::for_each(_fonts.begin(), _fonts.end(), delete_ptr2<FontMap::value_type>());
461 	_fonts.clear();
462 	std::for_each(_objects.begin(), _objects.end(), delete_ptr2<ObjectMap::value_type>());
463 	_objects.clear();
464 
465 	_am = NULL;
466 
467 	if (RTConfig->editor_mode)
468 		return;
469 
470 	std::map<const std::string, std::string> xml_data;
471 	for(PreloadMap::const_iterator i = _preload_map.begin(); i != _preload_map.end(); ++i) {
472 		std::string &dst = xml_data[i->first.first];
473 		dst += mrt::format_string("\t<map id=\"%s\">\n", escape(i->first.second).c_str());
474 		for(std::set<std::string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
475 			//LOG_DEBUG(("map: %s, %s", i->first.c_str(), j->c_str()));
476 			 dst += mrt::format_string("\t\t<object id=\"%s\"/>\n", escape(*j).c_str());
477 		}
478 		dst += "\t</map>\n";
479 	}
480 	for(PreloadMap::const_iterator i = _object_preload_map.begin(); i != _object_preload_map.end(); ++i) {
481 		std::string &dst = xml_data[i->first.first];
482 		dst += mrt::format_string("\t<object id=\"%s\">\n", escape(i->first.second).c_str());
483 		for(std::set<std::string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
484 			//LOG_DEBUG(("map: %s, %s", i->first.c_str(), j->c_str()));
485 			 dst += mrt::format_string("\t\t<animation id=\"%s\"/>\n", escape(*j).c_str());
486 		}
487 		dst += "\t</object>\n";
488 	}
489 
490 
491 	for(std::map<const std::string, std::string>::iterator i = xml_data.begin(); i != xml_data.end(); ++i) {
492 		//LOG_DEBUG(("xml data for %s, size: %u", i->first.c_str(), (unsigned)i->second.size()));
493 		TRY {
494 			assert(!i->first.empty());
495 			if (Finder->packed(i->first))
496 				continue;
497 
498 			try {
499 				mrt::Directory dir;
500 				dir.create(i->first, true);
501 			} catch(...) {}
502 
503 			mrt::File f;
504 			f.open(i->first + "/preload.xml", "wb");
505 			i->second.insert(0, "<?xml version=\"1.0\"?>\n<preload>\n");
506 			i->second += "</preload>\n";
507 			f.write_all(i->second);
508 		} CATCH("writing to the preload cache", {});
509 	}
510 }
511 
~IResourceManager()512 IResourceManager::~IResourceManager() {
513 }
514 
registerObject(const std::string & classname,Object * o)515 void IResourceManager::registerObject(const std::string &classname, Object *o) {
516 	Variants vars;
517 	vars.parse(classname);
518 	if (!vars.empty())
519 		throw_ex(("registering object with variants ('%s') is prohibited", classname.c_str()));
520 
521 	assert(!classname.empty());
522 	*const_cast<std::string *>(&o->registered_name) = classname;
523 	assert(!o->registered_name.empty());
524 
525 	Object *old = _objects[classname];
526 	if (old != NULL)
527 		LOG_DEBUG(("overriding object %s", classname.c_str()));
528 	delete old;
529 	_objects[classname] = o;
530 	//LOG_DEBUG(("classname %s registered at %p", classname.c_str(), (void*)o));
531 }
532 
createAlias(const std::string & name,const std::string & _classname)533 void IResourceManager::createAlias(const std::string &name, const std::string &_classname) {
534 	Variants vars;
535 	vars.parse(name);
536 	if (!vars.empty())
537 		throw_ex(("registering object with variants ('%s') is prohibited", name.c_str()));
538 
539 	std::string classname = vars.parse(_classname);
540 
541 	LOG_DEBUG(("creating alias '%s' -> '%s' (variants: '%s')", name.c_str(), classname.c_str(), vars.dump().c_str()));
542 	ObjectMap::const_iterator i = _objects.find(classname);
543 
544 	if (i == _objects.end())
545 		throw_ex(("object %s was not registered", classname.c_str()));
546 
547 	if (_objects.find(name) != _objects.end())
548 		throw_ex(("attempt to create alias with duplicate name ('%s')", name.c_str()));
549 
550 	Object * r = i->second->clone();
551 	if (r == NULL)
552 		throw_ex(("%s->clone(\"\") returns NULL", classname.c_str()));
553 
554 	*const_cast<std::string *>(&r->registered_name) = name;
555 
556 	r->update_variants(vars);
557 	_objects[name] = r;
558 }
559 
createObject(const std::string & _classname) const560 Object *IResourceManager::createObject(const std::string &_classname) const {
561 	Variants vars;
562 	std::string classname = vars.parse(_classname);
563 	assert(classname.find('(') == classname.npos);
564 
565 	ObjectMap::const_iterator i = _objects.find(classname);
566 	if (i == _objects.end())
567 		throw_ex(("classname '%s' was not registered", classname.c_str()));
568 	Object * r = i->second->clone();
569 
570 	if (r == NULL)
571 		throw_ex(("%s->clone() returns NULL", classname.c_str()));
572 
573 	if (r->registered_name.empty())
574 		throw_ex(("%s::clone() did not use copy ctor. (you must write \" return new Class(*this)\" or smth.)", classname.c_str()));
575 
576 	r->update_variants(vars);
577 
578 	return r;
579 }
580 
581 #include "tmx/map.h"
582 
createObject(const std::string & classname,const std::string & animation) const583 Object *IResourceManager::createObject(const std::string &classname, const std::string &animation) const {
584 	if (!Map->getName().empty()) {
585 		std::string stripped_classname = Variants::strip(classname);
586 		_preload_map[PreloadMap::key_type(Map->getPath(), Map->getName())].insert(stripped_classname);
587 		_object_preload_map[PreloadMap::key_type(Map->getPath(), stripped_classname)].insert(animation);
588 	}
589 
590 	Object *r = createObject(classname);
591 
592 	r->init(animation);
593 	//LOG_DEBUG(("base: %s", i->second->dump().c_str()));
594 	//LOG_DEBUG(("clone: %s", r->dump().c_str()));
595 	r->animation = animation;
596 
597 	return r;
598 }
599 
getClass(const std::string & classname) const600 const Object *IResourceManager::getClass(const std::string &classname) const {
601 	ObjectMap::const_iterator i = _objects.find(classname);
602 	if (i == _objects.end())
603 		throw_ex(("classname '%s' was not registered", classname.c_str()));
604 	return i->second;
605 }
606 
hasClass(const std::string & classname) const607 const bool IResourceManager::hasClass(const std::string &classname) const {
608 	return _objects.find(classname) != _objects.end();
609 }
610 
611 #include "mrt/fs_node.h"
612 
check_surface(const std::string & animation,const sdlx::Surface * & surface_ptr,const sdlx::CollisionMap * & cmap_ptr)613 void IResourceManager::check_surface(const std::string &animation, const sdlx::Surface *& surface_ptr, const sdlx::CollisionMap *& cmap_ptr) {
614 	if (surface_ptr != NULL && cmap_ptr != NULL)
615 		return;
616 
617 	const Animation * a = getAnimation(animation);
618 	std::string tname = "tiles/" + a->surface;
619 
620 	sdlx::Surface *s = _surfaces[a->surface];
621 	sdlx::CollisionMap *cmap = _cmaps[a->surface];
622 
623 
624 	if (s == NULL) {
625 		TRY {
626 			mrt::Chunk data;
627 			Finder->load(data, tname);
628 			s = new sdlx::Surface;
629 			s->load_image(data);
630 			s->display_format_alpha();
631 			GET_CONFIG_VALUE("engine.strip-alpha-from-object-tiles", bool, strip_alpha, false);
632 			if (strip_alpha) {
633 				s->lock();
634 				Uint8 r,g,b,a;
635 				for(int y = 0; y < s->get_height(); ++y)
636 					for(int x = 0; x < s->get_width(); ++x) {
637 						s->get_rgba(s->get_pixel(x, y), r, g, b, a);
638 						if (a != 255)
639 							s->put_pixel(x, y, s->map_rgba(r, g, b, (a > 51)?51:a));
640 					}
641 				s->unlock();
642 			}
643 
644 			LOG_DEBUG(("loaded animation '%s'", animation.c_str()));
645 			_surfaces[a->surface] = s;
646 		} CATCH("loading surface", { delete s; throw; });
647 	}
648 	surface_ptr = s;
649 
650 	if (cmap == NULL) {
651 		cmap = create_cmap(s, tname);
652 		_cmaps[a->surface] = cmap;
653 	}
654 	cmap_ptr = cmap;
655 }
656 
getAllClasses(std::set<std::string> & classes)657 void IResourceManager::getAllClasses(std::set<std::string> &classes) {
658 	classes.clear();
659 	for(ObjectMap::const_iterator i = _objects.begin(); i != _objects.end(); ++i) {
660 		classes.insert(i->first);
661 	}
662 }
663 
preload()664 void IResourceManager::preload() {
665 	LOG_DEBUG(("preloading surfaces..."));
666 	std::pair<std::string, std::string> map_id(Map->getPath(), Map->getName());
667 	PreloadMap::const_iterator map = _preload_map.find(map_id);
668 	if (map == _preload_map.end())
669 		return;
670 
671 	const std::set<std::string>& objects = map->second;
672 	std::set<std::string> animations;
673 
674 	for(std::set<std::string>::const_iterator i = objects.begin(); i != objects.end(); ++i) {
675 		PreloadMap::const_iterator o = _object_preload_map.find(PreloadMap::key_type(Map->getPath(), *i));
676 		if (o != _object_preload_map.end()) {
677 			const std::set<std::string>& anims = o->second;
678 			for(std::set<std::string>::const_iterator j = anims.begin(); j != anims.end(); ++j) {
679 				animations.insert(*j);
680 			}
681 		}
682 	}
683 
684 	if (animations.empty())
685 		return;
686 	LOG_DEBUG(("found %u surfaces, loading...", (unsigned)animations.size()));
687 
688 	reset_progress.emit(animations.size());
689 	for(std::set<std::string>::iterator i = animations.begin(); i != animations.end(); ++i) {
690 		const std::string &name = *i;
691 		if (hasAnimation(name)) {
692 			Animation *a = getAnimation(name);
693 			load_surface(a->surface);
694 		}
695 		notify_progress.emit(1, "animation");
696 	}
697 }
698 
create_cmap(const sdlx::Surface * s,const std::string & name)699 sdlx::CollisionMap * IResourceManager::create_cmap(const sdlx::Surface *s, const std::string &name) {
700 	sdlx::CollisionMap * cmap = new sdlx::CollisionMap;
701 	GET_CONFIG_VALUE("engine.generate-static-collision-maps", bool, gscm, false);
702 	if (true || !gscm) {
703 		try {
704 			mrt::Chunk data;
705 			Finder->load(data, name + ".map");
706 			if (cmap->load(s->get_width(), s->get_height(), data))
707 				return cmap;
708 		} CATCH("create_map(load)", {});
709 	}
710 	cmap->init(s, sdlx::CollisionMap::OnlyOpaque);
711 	if (gscm) {
712 		LOG_DEBUG(("generating collision map for the %s", name.c_str()));
713 		IFinder::FindResult r;
714 		Finder->findAll(r, name);
715 		if (r.empty()) {
716 			return cmap;
717 		}
718 		const std::string &base = r[0].first;
719 		try {
720 			std::string fname = base + "/" + name + ".map";
721 			LOG_DEBUG(("saving collision map in %s", fname.c_str()));
722 			cmap->save(fname);
723 		} CATCH("create_map(save)", {})
724 	}
725 	return cmap;
726 }
727