1 /* Battle Tanks_imp_map.get(yp, xp) Game
2  * Copyright (C) 2006-2009 Battle Tanks team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18 
19 /*
20  * Additional rights can be granted beyond the GNU General Public License
21  * on the terms provided in the Exception. If you modify this file,
22  * you may extend this exception to your version of the file,
23  * but you are not obligated to do so. If you do not wish to provide this
24  * exception without modification, you must delete this exception statement
25  * from your version and license this file solely under the GPL without exception.
26 */
27 
28 #include "map.h"
29 #include "layer.h"
30 #include "base_object.h"
31 #include "rt_config.h"
32 
33 #include "mrt/file.h"
34 #include "mrt/b64.h"
35 #include "mrt/gzip.h"
36 #include "mrt/logger.h"
37 #include "mrt/exception.h"
38 
39 #include "sdlx/surface.h"
40 #include "sdlx/c_map.h"
41 #include "object.h"
42 
43 #include <assert.h>
44 #include <limits>
45 
46 #include "math/binary.h"
47 
48 #include "config.h"
49 #include "resource_manager.h"
50 #include "player_manager.h"
51 #include "finder.h"
52 #include "zbox.h"
53 
54 #include "generator.h"
55 #include "mrt/scoped_ptr.h"
56 
57 #include "math/range_list.h"
58 
59 static range_list<Uint32> tile_stats;
60 
61 IMPLEMENT_SINGLETON(Map, IMap);
62 
IMap()63 IMap::IMap() : _w(0), _h(0), _tw(0), _th(0), _ptw(0), _pth(0), _firstgid(0), _split(0),
64 	_generator(new MapGenerator), _torus(false)
65 {
66 	_lastz = -1001;
67 	_image = NULL;
68 }
69 
getMatrix(int z,const bool only_pierceable)70 Matrix<int> &IMap::getMatrix(int z, const bool only_pierceable) {
71 	const int box = ZBox::getBox(z);
72 	MatrixMap::iterator i = _imp_map.find(MatrixMap::key_type(box, only_pierceable));
73 	if (i != _imp_map.end())
74 		return i->second;
75 
76 	Matrix<int> map;
77 	GET_CONFIG_VALUE("map.default-impassability", int, def_im, 0);
78 	map.set_size(_h * _split, _w * _split, 0);
79 	map.useDefault(-1);
80 	std::pair<MatrixMap::iterator, bool> r = _imp_map.insert(MatrixMap::value_type(MatrixMap::key_type(box, only_pierceable), map));
81 	return r.first->second;
82 }
83 
getMatrix(const std::string & name)84 Matrix<int> &IMap::getMatrix(const std::string &name) {
85 	ObjectAreaMap::iterator i = _area_map.find(name);
86 	if (i != _area_map.end())
87 		return i->second;
88 
89 	Matrix<int> map;
90 	map.set_size(_h * _split, _w * _split, 0);
91 	map.useDefault(0);
92 	std::pair<ObjectAreaMap::iterator, bool> r =_area_map.insert(ObjectAreaMap::value_type(name, map));
93 	return r.first->second;
94 }
95 
96 
getAreaMatrix(const std::string & name)97 const Matrix<int>& IMap::getAreaMatrix(const std::string &name) {
98 	return getMatrix(name);
99 }
100 
101 
get_impassability_matrix(const int z,const bool only_pierceable)102 const Matrix<int>& IMap::get_impassability_matrix(const int z, const bool only_pierceable) {
103 	return getMatrix(z, only_pierceable);
104 }
105 
collides(const Object * obj,const int dx,const int dy,const sdlx::CollisionMap * tile) const106 inline const bool IMap::collides(const Object *obj, const int dx, const int dy, const sdlx::CollisionMap *tile) const {
107 	if (tile == NULL) {
108 		return false;
109 	}
110 	return obj->collides(tile, -dx, -dy);
111 }
112 
hiddenBy(const Object * obj,const int dx,const int dy,const sdlx::CollisionMap * tile) const113 inline const bool IMap::hiddenBy(const Object *obj, const int dx, const int dy, const sdlx::CollisionMap *tile) const {
114 	if (tile == NULL)
115 		return false;
116 	return obj->collides(tile, -dx, -dy, true);
117 }
118 
119 /*
120 static const int im2(const int im1, const int im2) {
121 	assert(im1 < 101 && im2 < 101);
122 	if (im1 == -1)
123 		return im2;
124 	if (im2 == -1)
125 		return im1;
126 	return math::max(im1, im2);
127 }
128 */
129 
getImpassability(const Object * obj,const v2<int> & pos,TilePosition * tile_pos,bool * hidden) const130 const int IMap::getImpassability(const Object *obj, const v2<int>&pos, TilePosition *tile_pos, bool *hidden) const {
131 TRY {
132 	assert(obj != NULL);
133 
134 	if (obj->impassability >= 0 && obj->impassability < 1.0f) {
135 		return 0;
136 	}
137 	//LOG_DEBUG((">>IMap::getImpassability"));
138 	if (hidden)
139 		*hidden = false;
140 
141 	GET_CONFIG_VALUE("engine.disable-outlines", bool, disable_outlines, false);
142 
143 	if (disable_outlines) {
144 		hidden = NULL;
145 	}
146 
147 	v2<float> position, velocity;
148 	obj->get_position(position);
149 	obj->get_velocity(velocity);
150 
151 	GET_CONFIG_VALUE("engine.debug-map-collision-code", bool, debug, false);
152 
153 	const int obj_z = obj->get_z();
154 	int w = (int)obj->size.x, h = (int)obj->size.y;
155 	int dx1, dx2, dy1, dy2;
156 	int xt1, xt2, yt1, yt2;
157 	{
158 		//hide x1 and other common ids into {} block :)
159 		int x, x1;
160 		int y, y1;
161 		x = x1 = pos.x;
162 		y = y1 = pos.y;
163 
164 		int x2 = x1 + w - 1; int y2 = y1 + h - 1;
165 
166 		xt1 = x1 / _tw; xt2 = x2 / _tw;
167 		yt1 = y1 / _th; yt2 = y2 / _th;
168 		dx1 = x - xt1 * _tw; dx2 = x - xt2 * _tw;
169 		dy1 = y - yt1 * _th; dy2 = y - yt2 * _th;
170 		if (debug)
171 			LOG_DEBUG(("%d:%d:%d:%d (%+d:%+d:%+d:%+d)--> %d:%d %d:%d", x1, y1, w, h, dx1, dy1, dx2, dy2, xt1, yt1, xt2, yt2));
172 	}
173 	int hidden_mask = 0, prev_im = 0;
174 
175 	int empty_mask = 0x0f;
176 	int im[4] = {101, 101, 101, 101};
177 
178 	if (collides(obj, dx1, dy1, &_full_tile))
179 		empty_mask &= ~0x01;
180 	if (dy1 != dy2 && collides(obj, dx1, dy2, &_full_tile))
181 		empty_mask &= ~0x02;
182 	if (dx1 != dx2) {
183 		if (collides(obj, dx2, dy1, &_full_tile))
184 			empty_mask &= ~0x04;
185 		if (dy1 != dy2 && collides(obj, dx2, dy2, &_full_tile))
186 			empty_mask &= ~0x08;
187 	}
188 
189 	for(LayerMap::const_reverse_iterator l = _layers.rbegin(); l != _layers.rend(); ++l) {
190 
191 		const Layer *layer = l->second;
192 		int layer_im = layer->impassability;
193 
194 		if (hidden && l->second->visible && l->first > obj_z) {
195 			if (!(hidden_mask & 1)) {
196 				if ((empty_mask & 1) || hiddenBy(obj, dx1, dy1, getVisibilityMap(layer, xt1, yt1)))
197 					hidden_mask |= 1;
198 			}
199 
200 			if (!(hidden_mask & 2)) {
201 				if ((empty_mask & 2) || hiddenBy(obj, dx1, dy2, getVisibilityMap(layer, xt1, yt2)))
202 					hidden_mask |= 2;
203 			}
204 
205 			if (!(hidden_mask & 4)) {
206 				if ((empty_mask & 4) || hiddenBy(obj, dx2, dy1, getVisibilityMap(layer, xt2, yt1)))
207 					hidden_mask |= 4;
208 			}
209 
210 			if (!(hidden_mask & 8)) {
211 				if ((empty_mask & 8) || hiddenBy(obj, dx2, dy2, getVisibilityMap(layer, xt2, yt2)))
212 					hidden_mask |= 8;
213 			}
214 		}
215 
216 		if (layer_im == -1 ||
217 			(layer->pierceable && obj->piercing) ||
218 			!ZBox::sameBox(l->first, obj->get_z()))
219 			continue;
220 
221 		if (!(empty_mask & 1) && im[0] == 101) {
222 			if (collides(obj, dx1, dy1, getCollisionMap(layer, xt1, yt1))) {
223 				im[0] = layer_im;
224 				if (layer_im < 100 && layer_im > prev_im)
225 					prev_im = layer_im;
226 				if (debug)
227 					LOG_DEBUG(("%d: im[0] = %d", l->first, layer_im));
228 			}
229 		}
230 
231 		if (!(empty_mask & 2) && im[1] == 101) {
232 			if (collides(obj, dx1, dy2, getCollisionMap(layer, xt1, yt2))) {
233 				im[1] = layer_im;
234 				if (layer_im < 100 && layer_im > prev_im)
235 					prev_im = layer_im;
236 				if (debug)
237 					LOG_DEBUG(("%d: im[1] = %d", l->first, layer_im));
238 			}
239 		}
240 
241 		if (!(empty_mask & 4) && im[2] == 101) {
242 			if (collides(obj, dx2, dy1, getCollisionMap(layer, xt2, yt1))) {
243 				im[2] = layer_im;
244 				if (layer_im < 100 && layer_im > prev_im)
245 					prev_im = layer_im;
246 				if (debug)
247 					LOG_DEBUG(("%d: im[2] = %d", l->first, layer_im));
248 			}
249 		}
250 
251 		if (!(empty_mask & 8) && im[3] == 101) {
252 			if (collides(obj, dx2, dy2, getCollisionMap(layer, xt2, yt2))) {
253 				im[3] = layer_im;
254 				if (layer_im < 100 && layer_im > prev_im)
255 					prev_im = layer_im;
256 				if (debug)
257 					LOG_DEBUG(("%d: im[3] = %d", l->first, layer_im));
258 			}
259 		}
260 	}
261 
262 	int result_im = 0;
263 
264 	if (empty_mask & 1)
265 		im[0] = -1;
266 	if (empty_mask & 2)
267 		im[1] = -1;
268 	if (empty_mask & 4)
269 		im[2] = -1;
270 	if (empty_mask & 8)
271 		im[3] = -1;
272 
273 	GET_CONFIG_VALUE("map.default-impassability", int, def_im, 0);
274 
275 	if (debug) {
276 		LOG_DEBUG(("im : %d %d", im[0], im[2]));
277 		LOG_DEBUG(("im : %d %d", im[1], im[3]));
278 		LOG_DEBUG(("empty_mask: 0x%02x, default im: %d", empty_mask, def_im));
279 	}
280 
281 	if (obj->piercing)
282 		def_im = 0;
283 
284 	for(int i = 0; i < 4; ++i)
285 		if (im[i] == 101)
286 			im[i] = def_im; //default im value for a layer.
287 
288 	if (tile_pos) {
289 		tile_pos->prev_im = prev_im;
290 		tile_pos->merged_x = tile_pos->merged_y = false;
291 
292 		bool v1 = im[0] == 100 || im[1] == 100;
293 		bool v2 = im[2] == 100 || im[3] == 100;
294 
295 		if (v1 && !v2) {
296 			tile_pos->position.x = _tw/2 + _tw * xt1;
297 		} else if (v2 && !v1) {
298 			tile_pos->position.x = _tw/2 + _tw * xt2;
299 		} else {
300 			tile_pos->position.x = _tw * xt2;
301 			tile_pos->merged_x = true;
302 		}
303 
304 		bool h1 = im[0] == 100 || im[2] == 100;
305 		bool h2 = im[1] == 100 || im[3] == 100;
306 		if (h1 && !h2) {
307 			tile_pos->position.y = _th/2 + _th * yt1;
308 		} else if (h2 && !h1) {
309 			tile_pos->position.y = _th/2 + _th * yt2;
310 		} else {
311 			tile_pos->merged_y = true;
312 			tile_pos->position.y = _th * yt2;
313 		}
314 	}
315 
316 	/*
317 	const int im_l = im2(im[0], im[1]);
318 	const int im_r = im2(im[2], im[3]);
319 	const int im_u = im2(im[0], im[2]);
320 	const int im_d = im2(im[1], im[3]);
321 
322 	if (velocity.y < 0 && im_u < 101 ) {
323 		result_im = math::max(result_im, im_u);
324 		//LOG_DEBUG(("y<0 : %d", t));
325 	}
326 	if (velocity.y > 0 && im_d < 101 ) {
327 		result_im = math::max(result_im, im_d);
328 		//LOG_DEBUG(("y>0 : %d", t));
329 	}
330 	if (velocity.x < 0 && im_l < 101 ) {
331 		result_im = math::max(result_im, im_l);
332 		//LOG_DEBUG(("x<0 : %d", t));
333 	}
334 	if (velocity.x > 0 && im_r < 101 ) {
335 		result_im = math::max(result_im, im_r);
336 		//LOG_DEBUG(("x>0 : %d", t));
337 	}
338 	*/
339 	for(int i = 0; i < 4; ++i) {
340 		if (im[i] > result_im)
341 			result_im = im[i];
342 	}
343 
344 	if (xt1 == xt2) {
345 		hidden_mask |= 0x0c;
346 	}
347 	if (yt1 == yt2) {
348 		hidden_mask |= 0x0a;
349 	}
350 
351 	if (hidden_mask == 0x0f && hidden)
352 		*hidden = true;
353 
354 	assert(result_im >= 0 && result_im < 101);
355 
356 	if (debug)
357 		LOG_DEBUG(("*** im = %d", result_im));
358 	if (result_im == 100)
359 		return 100;
360 
361 	//LOG_DEBUG(("<<IMap::getImpassability"));
362 	return (int)(100 * obj->get_effective_impassability(result_im / 100.0f));
363 } CATCH(mrt::format_string("Map::getImpassability(%p, (%d:%d), %p, %p)",
364 	(void *)obj, pos.x, pos.y, (void *)tile_pos, (void *)hidden ).c_str(), throw;);
365 	return 0;
366 }
367 
updateMatrix(const int x,const int y)368 void IMap::updateMatrix(const int x, const int y) {
369 	if (x < 0 || x >= _w || y < 0 || y >= _h)
370 		return;
371 	//LOG_DEBUG(("updating matrix at [%d,%d]", y, x));
372 
373 	for(LayerMap::reverse_iterator l = _layers.rbegin(); l != _layers.rend(); ++l) {
374 				int im = l->second->impassability;
375 				if (im == -1)
376 					continue;
377 
378 				int tid = l->second->get(x, y);
379 				if (tid == 0)
380 					continue;
381 				const sdlx::CollisionMap *cmap = getCollisionMap(l->second, x, y);
382 				if (cmap == NULL || cmap->is_empty())
383 					continue;
384 
385 				Matrix<int> &imp_map = getMatrix(l->first, false);
386 				Matrix<int> *pmap = (l->second->pierceable) ? &getMatrix(l->first, true): NULL;
387 
388 				//break;
389 				//if (im == 100)
390 				//	im = -1; //inf :)
391 				//_imp_map.set(y, x, im);
392 
393 
394 				Matrix<bool> proj;
395 				cmap->project(proj, _split, _split);
396 				//LOG_DEBUG(("projection: %s", proj.dump().c_str()));
397 				//_imp_map.set(y, x, im);
398 				const bool destructable = dynamic_cast<const DestructableLayer *>(l->second) != NULL;
399 				if (destructable)
400 					im = -100;
401 
402 				for(int yy = 0; yy < _split; ++yy)
403 					for(int xx = 0; xx < _split; ++xx) {
404 						int yp = y * _split + yy, xp = x * _split + xx;
405 						if (proj.get(yy, xx) && imp_map.get(yp, xp) == -2) {
406 							imp_map.set(yp, xp, im);
407 							if (pmap)
408 								pmap->set(yp, xp, im);
409 						}
410 					}
411 	}
412 
413 	GET_CONFIG_VALUE("map.default-impassability", int, def_im, 0);
414 
415 	for(MatrixMap::iterator i = _imp_map.begin(); i != _imp_map.end(); ++i) {
416 		Matrix<int>& imp_map = i->second;
417 		for(int yy = 0; yy < _split; ++yy)
418 			for(int xx = 0; xx < _split; ++xx) {
419 				int yp = y * _split + yy, xp = x * _split + xx;
420 
421 				if (imp_map.get(yp, xp) == -2)
422 					imp_map.set(yp, xp, def_im);
423 
424 				if (imp_map.get(yp, xp) >= 100)
425 					imp_map.set(yp, xp, -1);
426 				}
427 	}
428 }
429 
updateMatrix(Matrix<int> & imp_map,const Layer * layer)430 void IMap::updateMatrix(Matrix<int> &imp_map, const Layer *layer) {
431 	for(int y = 0; y < layer->get_height(); ++y)
432 		for(int x = 0; x < layer->get_width(); ++x) {
433 			int tid = layer->get(x, y);
434 			if (tid == 0)
435 				continue;
436 
437 			const sdlx::CollisionMap *cmap = getCollisionMap(layer, x, y);
438 			if (cmap == NULL || cmap->is_empty())
439 				continue;
440 
441 			Matrix<bool> proj;
442 			cmap->project(proj, _split, _split);
443 
444 			for(int yy = 0; yy < _split; ++yy)
445 				for(int xx = 0; xx < _split; ++xx) {
446 					int yp = y * _split + yy, xp = x * _split + xx;
447 					if (proj.get(yy, xx))
448 						imp_map.set(yp, xp, 1);
449 				}
450 		}
451 }
452 
correctGids()453 void IMap::correctGids() {
454 	//int delta = 0;
455 	unsigned max = 0x7fffffff;
456 	for(CorrectionMap::reverse_iterator i = _corrections.rbegin(); i != _corrections.rend(); ++i) {
457 		const int d = i->second - i->first;
458 		LOG_DEBUG(("correcting: gid: %d-%u, delta: %d", i->first, max, d));
459 		for(LayerMap::iterator j = _layers.begin(); j != _layers.end(); ++j) {
460 			j->second->correct(i->first, max, d);
461 		}
462 		max = i->first;
463 		//delta += d;
464 	}
465 }
466 
load(const std::string & name)467 void IMap::load(const std::string &name) {
468 	clear();
469 
470 	LOG_DEBUG(("loading map '%s'", name.c_str()));
471 	std::string file;
472 	{
473 		IFinder::FindResult fr;
474 		Finder->findAll(fr, "maps/" + name + ".tmx");
475 		if (fr.empty())
476 			throw_ex(("could not find map '%s'", name.c_str()));
477 		_path = fr[0].first;
478 		file = fr[0].second;
479 	}
480 
481 	parse_file(file);
482 	delete _image;
483 	_image = NULL;
484 
485 	correctGids();
486 
487 	_full_tile.create(_tw, _th, true);
488 
489 	LOG_DEBUG(("optimizing layers..."));
490 
491 
492 	for(std::map<const std::string, std::string>::const_iterator i = _damage4.begin(); i != _damage4.end(); ++i) {
493 		Layer *dl = NULL, *l = NULL;
494 		dl = _layers[_layer_z[i->first]];
495 		if (dl == NULL)
496 			throw_ex(("layer %s doesnt exits", i->first.c_str()));
497 		int slave_z = _layer_z[i->second];
498 		l = _layers[slave_z];
499 		if (l == NULL)
500 			throw_ex(("layer %s doesnt exits", i->second.c_str()));
501 		LOG_DEBUG(("mapping damage layers: %s -> %s", i->first.c_str(), i->second.c_str()));
502 		ChainedDestructableLayer *cl = dynamic_cast<ChainedDestructableLayer *>(dl);
503 		if (cl == NULL)
504 			throw_ex(("layer %s is not destructable", i->first.c_str()));
505 		cl->setSlave(slave_z, l);
506 	}
507 
508 	_name = name;
509 	LOG_DEBUG(("loading completed"));
510 	for(range_list<Uint32>::const_iterator i = tile_stats.begin(); i != tile_stats.end(); ++i) {
511 		LOG_DEBUG(("%u-%u", i->first, i->second));
512 	}
513 
514 	{
515 		PropertyMap::const_iterator p = properties.find("config:map.torus");
516 		if (p != properties.end()) {
517 			if (p->second.find("true") != std::string::npos) {
518 				_torus = true;
519 				LOG_DEBUG(("torus mode switched on..."));
520 			}
521 		}
522 	}
523 
524 	load_map_signal.emit();
525 }
526 
generateMatrixes()527 void IMap::generateMatrixes() {
528 	_cover_map.set_size(_h, _w, -10000);
529 	_cover_map.useDefault(-10000);
530 
531 	if (!RTConfig->editor_mode) {
532 	unsigned int ot = 0;
533 	for(LayerMap::iterator l = _layers.begin(); l != _layers.end(); ++l) {
534 		if (!l->second->velocity.is0() || !l->second->visible)
535 			continue;
536 
537 		for(int ty = 0; ty < _h; ++ty) {
538 			for(int tx = 0; tx < _w; ++tx) {
539 				const sdlx::CollisionMap * vmap = getVisibilityMap(l->second, tx, ty);
540 				if (vmap == NULL)
541 					continue;
542 				if (vmap->is_full()) {
543 					_cover_map.set(ty, tx, l->first);
544 					++ot;
545 				}
546 			}
547 		}
548 	}
549 
550 	LOG_DEBUG(("created render optimization map. opaque tiles found: %u, dump: \n%s", ot, _cover_map.dump().c_str()));
551 	}
552 
553 	_imp_map.clear();
554 	for(LayerMap::const_iterator i = _layers.begin(); i != _layers.end(); ++i) {
555 		const Layer *layer = i->second;
556 
557 		getMatrix(i->first, false).fill(-2);
558 		if (layer->pierceable)
559 			getMatrix(i->first, true).fill(-2);
560 
561 	}
562 
563 	for(int y = 0; y < _h; ++y) {
564 		for(int x = 0; x < _w; ++x) {
565 			updateMatrix(x, y);
566 		}
567 	}
568 	for(MatrixMap::const_iterator i = _imp_map.begin(); i != _imp_map.end(); ++i) {
569 		LOG_DEBUG(("z: %d(pierceable: %s)\n%s", i->first.first, i->first.second?"yes":"no", i->second.dump().c_str()));
570 	}
571 
572 	for(LayerMap::const_iterator i = _layers.begin(); i != _layers.end(); ++i) {
573 		const Layer *layer = i->second;
574 		//LOG_DEBUG(("size(%s.properties) == %u", layer->name.c_str(), (unsigned)layer->properties.size()));
575 		for(PropertyMap::const_iterator p = layer->properties.begin(); p != layer->properties.end(); ++p) {
576 			//LOG_DEBUG(("%s.%s=%s", layer->name.c_str(), p->first.c_str(), p->second.c_str()));
577 			if (p->first.compare(0, 8, "ai-hint:") == 0) {
578 				LOG_DEBUG(("layer %d %s provide hint for %s", i->first, layer->name.c_str(), p->second.c_str()));
579 				updateMatrix(getMatrix(p->second), layer);
580 			}
581 		}
582 	}
583 
584 	for(ObjectAreaMap::const_iterator i = _area_map.begin(); i != _area_map.end(); ++i) {
585 		LOG_DEBUG(("hint for '%s'\n%s", i->first.c_str(), i->second.dump().c_str()));
586 	}
587 
588 	load_map_final_signal.emit();
589 }
590 
get_zBoxes(std::set<int> & layers)591 void IMap::get_zBoxes(std::set<int> &layers) {
592 	layers.clear();
593 	for(MatrixMap::const_iterator i = _imp_map.begin(); i != _imp_map.end(); ++i) {
594 		layers.insert(i->first.first);
595 	}
596 }
597 
getSurroundings(Matrix<int> & matrix,const Object * obj,const int filler) const598 void IMap::getSurroundings(Matrix<int> &matrix, const Object *obj, const int filler) const {
599 	if (matrix.get_width() % 2 == 0 || matrix.get_height() % 2 == 0)
600 		throw_ex(("use only odd values for surrond matrix. (used: %d, %d)", matrix.get_height(), matrix.get_width()));
601 
602 	const int box = ZBox::getBox(obj->get_z());
603 	MatrixMap::const_iterator map = _imp_map.find(MatrixMap::key_type(box, false));
604 	if (map == _imp_map.end()) {
605 		matrix.fill(filler);
606 		return;
607 	}
608 
609 	MatrixMap::const_iterator pmap = (obj->piercing)?_imp_map.find(MatrixMap::key_type(box, true)):_imp_map.end();
610 
611 	int dx = (matrix.get_width() - 1) / 2;
612 	int dy = (matrix.get_height() - 1) / 2;
613 
614 	v2<int> p;
615 	obj->get_center_position(p);
616 	p.x /= _tw;
617 	p.y /= _th;
618 
619 	int y0 = p.y - dy, x0 = p.x - dx;
620 	for(int y = y0; y <= p.y + dy; ++y)
621 		for(int x = x0; x <= p.x + dx; ++x) {
622 			int i = map->second.get(y, x);
623 			if (filler != -1 && i < 0)
624 				i = filler;
625 
626 			if (obj->piercing && pmap != _imp_map.end()) {
627 				if (pmap->second.get(y, x))
628 					i = 0;
629 			}
630 			matrix.set(y - y0, x - x0, i);
631 		}
632 }
633 
634 
start(const std::string & name,Attrs & attrs)635 void IMap::start(const std::string &name, Attrs &attrs) {
636 	//LOG_DEBUG(("started %s", name.c_str()));
637 	Entity e(attrs);
638 
639 	if (name == "map") {
640 		LOG_DEBUG(("map file version %s", e.attrs["version"].c_str()));
641 		_w = atol(e.attrs["width"].c_str());
642 		_h = atol(e.attrs["height"].c_str());
643 		_tw = atol(e.attrs["tilewidth"].c_str());
644 		_th = atol(e.attrs["tileheight"].c_str());
645 
646 		GET_CONFIG_VALUE("map.pathfinding-step", int, ps, 32);
647 
648 		_split = 2 * ((_tw - 1) / 2 + 1) / ps;
649 		LOG_DEBUG(("split mode: %dx", _split));
650 
651 		_pth = _tw / _split;
652 		_ptw = _th / _split;
653 
654 		if (_tw < 1 || _th < 1 || _w < 1 || _h < 1)
655 			throw_ex(("invalid map parameters. %dx%d tile: %dx%d", _w, _h, _tw, _th));
656 
657 		LOG_DEBUG(("initializing map. size: %dx%d, tilesize: %dx%d", _w, _h, _tw, _th));
658 	} else if (name == "tileset") {
659 		status = "tileset";
660 		_firstgid = atol(e.attrs["firstgid"].c_str());
661 		if (_firstgid < 1)
662 			throw_ex(("tileset.firstgid must be > 0"));
663 		LOG_DEBUG(("tileset: '%s'. firstgid = %d", e.attrs["name"].c_str(), _firstgid));
664 	} else if (name == "layer") {
665 		_properties.clear();
666 		_layer = true;
667 		_layer_name = e.attrs["name"];
668 		if (_layer_name.empty())
669 			throw_ex(("layer name cannot be empty!"));
670 	} else if (name == "properties") {
671 		if (!_layer)
672 			status = "properties";
673 	}
674 
675 	_stack.push(e);
676 	NotifyingXMLParser::start(name, attrs);
677 }
678 
end(const std::string & name)679 void IMap::end(const std::string &name) {
680 	assert(!_stack.empty());
681 	Entity &e = _stack.top();
682 
683 	if (name == "tile") {
684 		status = "tileset";
685 		if (e.attrs.find("id") == e.attrs.end())
686 			throw_ex(("tile.id was not found"));
687 
688 		if (_image == NULL)
689 			throw_ex(("tile must contain <image> inside it."));
690 
691 		unsigned int id = atol(e.attrs["id"].c_str());
692 		id += _firstgid;
693 		LOG_DEBUG(("tile gid = %d, image: %p", id, (void *)_image));
694 
695 		//TileManager->set(id, _image);
696 		//_tiles.reserve(id + 2);
697 		if (id >= _tiles.size())
698 			_tiles.resize(id + 20);
699 
700 		TileMap::value_type &tile = _tiles[id];
701 		if (tile.surface != NULL)
702 			throw_ex(("duplicate tile %d found", id));
703 
704 		tile.cmap = new sdlx::CollisionMap;
705 		tile.cmap->init(_image, sdlx::CollisionMap::OnlyOpaque);
706 		tile.vmap = new sdlx::CollisionMap;
707 		tile.vmap->init(_image, sdlx::CollisionMap::AnyVisible);
708 		tile.surface = _image;
709 
710 		_image = NULL;
711 
712 	} else if (name == "data") {
713 		std::string enc = e.attrs["encoding"];
714 		if (enc.empty()) enc = "none";
715 		std::string comp = e.attrs["compression"];
716 		if (comp.empty()) comp = "none";
717 
718 		LOG_DEBUG(("data found. encoding: %s, compression: %s", enc.c_str(), comp.c_str()));
719 
720 		mrt::Chunk data;
721 		if (enc == "base64") {
722 			mrt::Base64::decode(data, e.data);
723 		} else if (enc == "none") {
724 			data.set_data(e.data.c_str(), e.data.size());
725 		} else throw_ex(("unknown encoding %s used", enc.c_str()));
726 
727 		//LOG_DEBUG(("decoded size: %d", data.get_size()));
728 		//LOG_DEBUG(("decoded data: %s -> %s", e.data.c_str(), data.dump().c_str()));
729 
730 		if (comp == "gzip") {
731 			mrt::ZStream::decompress(_data, data, true);
732 		} else if (comp == "none") {
733 			_data = data;
734 		} else throw_ex(("unknown compression method ('%s') used. ", comp.c_str()));
735 		data.free();
736 		//LOG_DEBUG(("%s", _data.dump().c_str()));
737 	} else if (name == "image") {
738 		status = "tileset";
739 		delete _image;
740 		_image = NULL;
741 
742 		_image = new sdlx::Surface;
743 		std::string source = e.attrs["source"];
744 		if (source.size()) {
745 			LOG_DEBUG(("loading tileset from single file ('%s')", source.c_str()));
746 			_image_source = source;
747 			_image_name = Finder->find("maps/" + source, false);
748 			if (_image_name.empty()) {
749 				//last resort, try match filename with any tilesets folder.
750 				_image_name = Finder->find("tilesets/" + mrt::FSNode::get_filename(source));
751 			}
752 			source = _image_name;
753 
754 			scoped_ptr<mrt::BaseFile> file(Finder->get_file(source, "rb"));
755 
756 			mrt::Chunk data;
757 			file->read_all(data);
758 			file->close();
759 
760 			_image->load_image(data);
761 			_image_is_tileset = true;
762 		} else {
763 			_image->load_image(_data);
764 			_image_is_tileset = false;
765 		}
766 		_image->display_format_alpha();
767 
768 		LOG_DEBUG(("image loaded. (%dx%d)", _image->get_width(), _image->get_height()));
769 	} else if (name == "layer") {
770 		status = "layer";
771 		int w = atol(e.attrs["width"].c_str());
772 		int h = atol(e.attrs["height"].c_str());
773 		int z = (_properties.find("z") == _properties.end())?++_lastz:atol(_properties["z"].c_str());
774 		_lastz = z;
775 		int impassability = (_properties.find("impassability") != _properties.end())?atoi(_properties["impassability"].c_str()):-1;
776 
777 		bool pierceable = false;
778 
779 		int hp = (_properties.find("hp") != _properties.end())?atoi(_properties["hp"].c_str()):0;
780 
781 		PropertyMap::const_iterator pi = _properties.find("pierceable");
782 		if (pi != _properties.end()) {
783 			pierceable = true;
784 			if (!pi->second.empty()) {
785 				unsigned char pc = pi->second[0];
786 				pierceable = pc == 't' || pc == 'T' || pc == '1';
787 			}
788 		}
789 		Layer *layer = NULL;
790 		if (!RTConfig->editor_mode) {
791 
792 		if (!_properties["visible-if-damaged"].empty()) {
793 			layer = new DestructableLayer(true);
794 		}
795 		if (!_properties["invisible-if-damaged"].empty()) {
796 			if (layer != NULL)
797 				throw_ex(("visible/invisible options is mutually exclusive"));
798 			layer = new DestructableLayer(false);
799 		}
800 		const std::string damage = _properties["damage-for"];
801 		if (!damage.empty()) {
802 			if (layer != NULL)
803 				throw_ex(("damage-for cannot be combined with (in)visible-if-damaged"));
804 			layer = new ChainedDestructableLayer();
805 			_damage4[_layer_name] = damage;
806 		}
807 
808 		} //editor
809 
810 		LOG_DEBUG(("layer '%s'. %dx%d. z: %d, size: %u, impassability: %d", e.attrs["name"].c_str(), w, h, z, (unsigned)_data.get_size(), impassability));
811 		if (_layers.find(z) != _layers.end())
812 			throw_ex(("layer with z %d already exists", z));
813 		if(layer == NULL)
814 			layer = new Layer;
815 
816 		if (RTConfig->editor_mode) {
817 			int visible = (!e.attrs["visible"].empty())?atoi(e.attrs["visible"].c_str()):-1;
818 			LOG_DEBUG(("visible = %d", visible));
819 			if (visible == 0)
820 				layer->visible = false;
821 		} else {
822 			//hide layers with 'hidden' attribute set
823 			PropertyMap::const_iterator i = _properties.find("hidden");
824 			if (i != _properties.end() && !i->second.empty() && (i->second[0] == 't' || i->second[0] == 'T' || i->second[0] == '1')) {
825 				layer->visible = false;
826 			}
827 		}
828 		layer->properties = _properties;
829 		layer->name = e.attrs["name"];
830 
831 		const std::string a_frame_size = _properties["animation-frame-size"];
832 		const std::string a_frames = _properties["animation-frames"];
833 		const std::string a_speed = _properties["animation-speed"];
834 		if (!a_frame_size.empty() && !a_frames.empty()) {
835 			int fs = atoi(a_frame_size.c_str());
836 			int fn = atoi(a_frames.c_str());
837 			float speed = (a_speed.empty())?1.0f:(float)atof(a_speed.c_str());
838 			if (a_speed.empty())
839 				LOG_WARN(("layer '%s': default speed of 1 used.", e.attrs["name"].c_str()));
840 			LOG_DEBUG(("layer '%s': animation-frame-size: %d, animation-speed: %g", e.attrs["name"].c_str(), fs, speed));
841 			layer->setAnimation(fs, fn, speed);
842 		}
843 
844 		const std::string a_velocity = _properties["shifting-velocity"];
845 		const std::string a_size = _properties["shifting-size"];
846 		if (!a_velocity.empty() && !a_size.empty()) {
847 			v2<int> vel, size;
848 			vel.fromString(a_velocity);
849 			size.fromString(a_size);
850 			if (size.x <= 0 || size.y <= 0)
851 				throw_ex(("shift size must not be negative or zero"));
852 			layer->velocity = vel.convert<float>();
853 			layer->size = size * v2<int>(_tw, _th);
854 			LOG_DEBUG(("shifting rendering: velocity: (%g,%g) wrapping: %dx%d", layer->velocity.x, layer->velocity.y, layer->size.x, layer->size.y));
855 		}
856 
857 		layer->impassability = impassability;
858 		layer->pierceable = pierceable;
859 		layer->hp = hp;
860 
861 		TRY {
862 			GET_CONFIG_VALUE("map.log-tile-stats", bool, lts, false);
863 			if (lts) {
864 				Uint32 n = w * h;
865 				Uint32 *p = (Uint32 *)_data.get_ptr();
866 				for(size_t i = 0; i < n; ++i) {
867 					Uint32 t = SDL_SwapLE32(*p++);
868 					if (t > 0)
869 						tile_stats.insert(t);
870 				}
871 			}
872 
873 			layer->init(w, h, _data);
874 		} CATCH(mrt::format_string("layer '%s'", _layer_name.c_str()).c_str(),
875 			{delete layer; layer = NULL; throw; }
876 		);
877 
878 		if (!RTConfig->editor_mode)
879 		for(PropertyMap::iterator i = _properties.begin(); i != _properties.end(); ++i) {
880 			if (i->first.compare(0, 10, "generator:") == 0) {
881 				TRY {
882 					_generator->exec(layer, i->first.substr(i->first.find(":", 11) + 1), i->second);
883 				} CATCH("executing generator's commands", {})
884 			}
885 		}
886 
887 		_layers[z] = layer;
888 		_layer_z[_layer_name] = z;
889 		//LOG_DEBUG(("(1,1) = %d", _layers[z]->get(1,1)));
890 		_layer = false;
891 	} else if (name == "property") {
892 		std::string name = e.attrs["name"];
893 		mrt::trim(name);
894 		if (_layer) {
895 			if (_properties.find(name) != _properties.end())
896 				throw_ex(("duplicate property name '%s' found in layer %s", name.c_str(), _layer_name.c_str()));
897 			_properties[name] = e.attrs["value"];
898 		} else {
899 			if (properties.find(name) != properties.end())
900 				throw_ex(("duplicate property name '%s' found", name.c_str()));
901 			properties[name] = e.attrs["value"];
902 		}
903 	} else if (name == "tileset" && _image != NULL && _image_is_tileset) {
904 		status = "tileset";
905 
906 		int n = ((_image->get_width() - 1) / _tw + 1) * ((_image->get_height() - 1) / _th + 1);
907 		LOG_DEBUG(("tileset: %s, first_gid: %d, estimated tiles: %d", _image_source.c_str(), _firstgid, n));
908 
909 		int gid = _tilesets.add(_image_source, _firstgid, n);
910 		if (gid != _firstgid)
911 			_corrections.insert(CorrectionMap::value_type(_firstgid, gid));
912 		_firstgid = gid;
913 		_generator->tileset(_image_name, _firstgid);
914 
915 		addTiles(_image, _firstgid);
916 
917 		delete _image;
918 		_image = NULL;
919 	}
920 
921 	_stack.pop();
922 	NotifyingXMLParser::end(name);
923 }
924 
addTileset(const std::string & tileset)925 void IMap::addTileset(const std::string &tileset) {
926 	if (!loaded())
927 		throw_ex(("addTileset(%s) on uninitialized map", tileset.c_str()));
928 	const sdlx::Surface *image = ResourceManager->load_surface("../maps/" + tileset);
929 	std::string fname = Finder->find("tiles/" + tileset);
930 	int gid = _tilesets.last() + 1;
931 	int n = addTiles(image, gid);
932 	_generator->tileset(fname, gid);
933 	_tilesets.add(tileset, gid, n);
934 }
935 
936 
cdata(const std::string & d)937 void IMap::cdata(const std::string &d) {
938 	assert(!_stack.empty());
939 	//LOG_DEBUG(("char1 %s", d.c_str()));
940 	std::string data(d);
941 	mrt::trim(data);
942 	if (data.empty())
943 		return;
944 
945 	//LOG_DEBUG(("char2 %s", data.c_str()));
946 	_stack.top().data += d;
947 }
948 
hasSoloLayers() const949 const bool IMap::hasSoloLayers() const {
950 	bool solo_layer = false;
951 	if (RTConfig->editor_mode) {
952 		for(LayerMap::const_iterator l = _layers.begin(); l != _layers.end(); ++l)
953 			if (l->second->solo) {
954 				solo_layer = true;
955 				break;
956 			}
957 	}
958 	return solo_layer;
959 }
960 
render(sdlx::Surface & window,const sdlx::Rect & src,const sdlx::Rect & dst,const int z1,const int z2) const961 void IMap::render(sdlx::Surface &window, const sdlx::Rect &src, const sdlx::Rect &dst, const int z1, const int z2) const {
962 	if (_w == 0 || z1 >= z2)  //not loaded
963 		return;
964 
965 	int txn = (dst.w - 1) / _tw + 2;
966 	int tyn = (dst.h - 1) / _th + 2;
967 
968 	//unsigned int skipped = 0;
969 	const bool _solo_layer = hasSoloLayers();
970 	const v2<int> tile_size(_tw, _th);
971 	GET_CONFIG_VALUE("engine.strip-alpha-from-map-tiles", bool, strip_alpha, false);
972 
973 	for(LayerMap::const_iterator l = _layers.lower_bound(z1); l != _layers.end(); ++l) {
974 		const int z = l->first;
975 		if (_solo_layer && !l->second->solo)
976 			continue;
977 
978 		if (z < z1)
979 			continue;
980 
981 		if (z >= z2)
982 			break;
983 
984 		if (!l->second->visible && (!_solo_layer || !l->second->solo))
985 			continue;
986 
987 		//if (strip_alpha && l->second->impassability == -1)
988 		//	continue;
989 
990 		const bool shifting = !l->second->velocity.is0();
991 		//LOG_DEBUG(("z: %d << %d, layer: %d", z1, z2, l->first));
992 
993 		v2<int> pos = v2<int>(src.x, src.y) - l->second->position.convert<int>();
994 		pos.x %= _tw * _w; pos.y %= _th * _h;
995 		if (pos.x < 0) pos.x += _tw * _w;
996 		if (pos.y < 0) pos.y += _th * _h;
997 
998 		v2<int> tile_pos = pos / tile_size;
999 		v2<int> shift_pos = -(pos % tile_size);
1000 
1001 		for(int ty = -1; ty < tyn; ++ty) {
1002 			for(int tx = -1; tx < txn; ++tx) {
1003 				int sx = (tile_pos.x + tx) % _w, sy = (tile_pos.y + ty) % _h;
1004 
1005 				if (sx < 0)
1006 					sx += _w;
1007 				if (sy < 0)
1008 					sy += _h;
1009 
1010 				if (!strip_alpha && !shifting && z < _cover_map.get(sy, sx)) {//this tile covered by another tile
1011 					//++skipped;
1012 					continue;
1013 				}
1014 
1015 				const sdlx::Surface * s = get_surface(l->second, sx, sy);
1016 				if (s == NULL)
1017 					continue;
1018 
1019 				const int dx = dst.x + tx * _tw + shift_pos.x, dy = dst.y + ty * _th + shift_pos.y;
1020 				window.blit(*s, dx, dy);
1021 			}
1022 		}
1023 	}
1024 	//LOG_DEBUG(("blits skipped: %u", skipped));
1025 	//LOG_DEBUG(("====================================="));
1026 }
1027 
1028 
clear()1029 void IMap::clear() {
1030 	LOG_DEBUG(("cleaning up..."));
1031 
1032 	tile_stats.clear();
1033 	//LOG_DEBUG(("clearing layers..."));
1034 	for(LayerMap::iterator i = _layers.begin(); i != _layers.end(); ++i) {
1035 		delete i->second;
1036 	}
1037 	_layers.clear();
1038 
1039 	//LOG_DEBUG(("clearing surfaces and collision maps..."));
1040 	for(TileMap::iterator i = _tiles.begin(); i != _tiles.end(); ++i) {
1041 		delete i->surface;
1042 		delete i->cmap;
1043 		delete i->vmap;
1044 	}
1045 	_tiles.clear();
1046 
1047 	//LOG_DEBUG(("clearing properties..."));
1048 	properties.clear();
1049 	_properties.clear();
1050 
1051 	//LOG_DEBUG(("deleting intermediate parser objects..."));
1052 	delete _image;
1053 	_image = NULL;
1054 	_lastz = -1001;
1055 	_w = _h = _tw = _th = _firstgid = 0;
1056 
1057 	//LOG_DEBUG(("clearing damage layers and optimization maps..."));
1058 
1059 	_imp_map.clear();
1060 	_area_map.clear();
1061 
1062 	_damage4.clear();
1063 	_layer_z.clear();
1064 	_cover_map.set_size(0, 0, 0);
1065 
1066 	_corrections.clear();
1067 
1068 	LOG_DEBUG(("clearing map generator..."));
1069 	_generator->clear();
1070 
1071 	_tilesets.clear();
1072 	_name.clear();
1073 	_path.clear();
1074 	_torus = false;
1075 }
1076 
~IMap()1077 IMap::~IMap() {
1078 	LOG_DEBUG(("cleaning up map..."));
1079 	clear();
1080 	LOG_DEBUG(("clear() succeedes, deleting map generator..."));
1081 	delete _generator;
1082 }
1083 
loaded() const1084 const bool IMap::loaded() const {
1085 	return _w != 0;
1086 }
1087 
get_size() const1088 const v2<int> IMap::get_size() const {
1089 	return v2<int>(_tw * _w,_th * _h);
1090 }
1091 
getTileSize() const1092 const v2<int> IMap::getTileSize() const {
1093 	return v2<int>(_tw, _th);
1094 }
1095 
getPathTileSize() const1096 const v2<int> IMap::getPathTileSize() const {
1097 	return v2<int>(_ptw, _pth);
1098 }
1099 
1100 
damage(const v2<float> & position,const int hp)1101 void IMap::damage(const v2<float> &position, const int hp) {
1102 	if (PlayerManager->is_client())
1103 		return;
1104 
1105 	v2<int> pos = position.convert<int>();
1106 	validate(pos);
1107 	pos.x /= _tw;
1108 	pos.y /= _th;
1109 
1110 	std::set<v3<int> > destroyed_cells;
1111 	//LOG_DEBUG(("map damage: %g:%g -> %d:%d for %d hp", position.x, position.y, pos.x, pos.y, hp));
1112 	for(LayerMap::iterator i = _layers.begin(); i != _layers.end(); ++i) {
1113 		if (i->second->damage(pos.x, pos.y, hp))
1114 			destroyed_cells.insert(v3<int>(pos.x, pos.y, i->first));
1115 	}
1116 	if (!destroyed_cells.empty())
1117 		destroyed_cells_signal.emit(destroyed_cells);
1118 }
1119 
damage(const v2<float> & center,const int hp,const float radius)1120 void IMap::damage(const v2<float> &center, const int hp, const float radius) {
1121 	if (PlayerManager->is_client())
1122 		return;
1123 
1124 	v2<float> position2 = center + radius, position = center - radius;
1125 	std::set<v3<int> > destroyed_cells;
1126 
1127 	v2<float> p;
1128 	float r = radius * radius;
1129 	for(p.y = position.y; p.y < position2.y; p.y += _th) {
1130 		for(p.x = position.x; p.x < position2.x; p.x += _tw) {
1131 			if (p.quick_distance(center) <= r) {
1132 				v2<int> pos ((int)(p.x / _tw), (int)(p.y / _th));
1133 				validate(pos);
1134 				//LOG_DEBUG(("map damage: %g:%g -> %d:%d for %d hp", position.x, position.y, pos.x, pos.y, hp));
1135 				for(LayerMap::iterator i = _layers.begin(); i != _layers.end(); ++i) {
1136 					if (i->second->damage(pos.x, pos.y, hp))
1137 						destroyed_cells.insert(v3<int>(pos.x, pos.y, i->first));
1138 				}
1139 			}
1140 		}
1141 	}
1142 	if (!destroyed_cells.empty())
1143 		destroyed_cells_signal.emit(destroyed_cells);
1144 }
1145 
tick(const float dt)1146 void IMap::tick(const float dt) {
1147 	for(LayerMap::iterator i = _layers.begin(); i != _layers.end(); ++i) {
1148 		i->second->tick(dt);
1149 	}
1150 }
1151 
_destroy(const int z,const v2<int> & cell)1152 void IMap::_destroy(const int z, const v2<int> &cell) {
1153 	LayerMap::iterator l = _layers.find(z);
1154 	if (l == _layers.end())
1155 		throw_ex(("cannot destroy cell at %d %d (z = %d)", cell.x, cell.y, z));
1156 	l->second->_destroy(cell.x, cell.y);
1157 }
1158 
invalidateTile(const int x,const int y)1159 void IMap::invalidateTile(const int x, const int y) {
1160 	_cover_map.set(y, x, -10000);
1161 	for(MatrixMap::iterator i = _imp_map.begin(); i != _imp_map.end(); ++i)
1162 		for(int yy = 0; yy < _split; ++yy)
1163 			for(int xx = 0; xx < _split; ++xx) {
1164 				int yp = y * _split + yy, xp = x * _split + xx;
1165 
1166 				i->second.set(yp, xp, -2);
1167 		}
1168 	updateMatrix(x, y);
1169 }
1170 
getTile(const Layer * l,const int x,const int y) const1171 const Uint32 IMap::getTile(const Layer *l, const int x, const int y) const {
1172 	if (!_torus)
1173 		return l->get(x, y);
1174 	int mx = x % _w, my = y % _h;
1175 	return l->get(mx >= 0? mx: mx + _w, my >= 0? my: my + _h);
1176 }
1177 
get_surface(const Layer * l,const int x,const int y) const1178 const sdlx::Surface* IMap::get_surface(const Layer *l, const int x, const int y) const {
1179 	Uint32 t = getTile(l, x, y);
1180 	if (t == 0 || t >= _tiles.size())
1181 		return NULL;
1182 	return _tiles[t].surface;
1183 }
getCollisionMap(const Layer * l,const int x,const int y) const1184 const sdlx::CollisionMap* IMap::getCollisionMap(const Layer *l, const int x, const int y) const {
1185 	Uint32 t = getTile(l, x, y);
1186 	if (t == 0 || t >= _tiles.size())
1187 		return NULL;
1188 	return _tiles[t].cmap;
1189 }
getVisibilityMap(const Layer * l,const int x,const int y) const1190 const sdlx::CollisionMap* IMap::getVisibilityMap(const Layer *l, const int x, const int y) const {
1191 	Uint32 t = getTile(l, x, y);
1192 	if (t == 0 || t >= _tiles.size())
1193 		return NULL;
1194 	return _tiles[t].vmap;
1195 }
1196 
serialize(mrt::Serializator & s) const1197 void IMap::serialize(mrt::Serializator &s) const {
1198 	s.add(_name);
1199 	s.add(_path);
1200 	s.add(_w); s.add(_h);
1201 	s.add(_tw); s.add(_th);
1202 	s.add(_ptw); s.add(_pth);
1203 	s.add(_split);
1204 
1205 	s.add((int)_tilesets.size());
1206 	s.add((int)_layers.size());
1207 
1208 	for(size_t i = 0; i < _tilesets.size(); ++i ) {
1209 		s.add(_tilesets[i].first);
1210 		s.add(_tilesets[i].second);
1211 	}
1212 
1213 	for(LayerMap::const_iterator i = _layers.begin(); i != _layers.end(); ++i) {
1214 		s.add(i->first);
1215 		int type = 'l';
1216 		if (dynamic_cast<ChainedDestructableLayer *>(i->second) != NULL)
1217 			type = 'c';
1218 		else if (dynamic_cast<DestructableLayer *>(i->second) != NULL)
1219 			type = 'd';
1220 
1221 		s.add(type);
1222 		s.add(*i->second);
1223 	}
1224 
1225 	s.add((int)properties.size());
1226 	for(PropertyMap::const_iterator i = properties.begin(); i != properties.end(); ++i) {
1227 		s.add(i->first);
1228 		s.add(i->second);
1229 	}
1230 }
1231 
deserialize(const mrt::Serializator & s)1232 void IMap::deserialize(const mrt::Serializator &s) {
1233 	clear();
1234 
1235 	s.get(_name);
1236 	s.get(_path);
1237 	s.get(_w); s.get(_h);
1238 	s.get(_tw); s.get(_th);
1239 	s.get(_ptw); s.get(_pth);
1240 	s.get(_split);
1241 
1242 	_full_tile.create(_tw, _th, true);
1243 
1244 	int tn, ln;
1245 
1246 	s.get(tn);
1247 	s.get(ln);
1248 	reset_progress.emit(tn + ln);
1249 
1250 	while(tn--) {
1251 		std::string name;
1252 		int gid;
1253 		s.get(name);
1254 		s.get(gid);
1255 		sdlx::Surface *image  = NULL;
1256 		int n = 0;
1257 		TRY {
1258 			std::string fname = Finder->find("maps/" + name, false);
1259 			if (fname.empty()) {
1260 				//last resort, try match filename with any tilesets folder.
1261 				fname = Finder->find("tilesets/" + mrt::FSNode::get_filename(name));
1262 			}
1263 
1264 			scoped_ptr<mrt::BaseFile> file(Finder->get_file(fname, "rb"));
1265 
1266 			mrt::Chunk data;
1267 			file->read_all(data);
1268 			file->close();
1269 
1270 			image = new sdlx::Surface;
1271 			image->load_image(data);
1272 			image->display_format_alpha();
1273 
1274 			n = addTiles(image, gid);
1275 
1276 			delete image;
1277 			image = NULL;
1278 		} CATCH("deserialize", { delete image; throw; });
1279 
1280 		_tilesets.add(name, gid, n);
1281 		notify_progress.emit(1, "tileset");
1282 	}
1283 
1284 	while(ln--) {
1285 		int z;
1286 		int type;
1287 		s.get(z);
1288 		s.get(type);
1289 
1290 		Layer *layer = NULL;
1291 		TRY {
1292 			switch(type) {
1293 			case 'c':
1294 				layer = new ChainedDestructableLayer();
1295 				break;
1296 			case 'd':
1297 				layer = new DestructableLayer(true);
1298 				break;
1299 			case 'l':
1300 				layer = new Layer;
1301 				break;
1302 			default:
1303 				throw_ex(("unknown layer type '%02x'(%c)", type, (type >= 0x20)?type:' '));
1304 			}
1305 			layer->deserialize(s);
1306 			_layers.insert(LayerMap::value_type(z, layer));
1307 		} CATCH("deserialize", {
1308 			delete layer;
1309 			throw;
1310 		});
1311 
1312 		notify_progress.emit(1, "layer");
1313 	}
1314 
1315 	for(LayerMap::iterator i = _layers.begin(); i != _layers.end(); ++i) {
1316 		ChainedDestructableLayer * cdl = dynamic_cast<ChainedDestructableLayer *>(i->second);
1317 		if (cdl == NULL)
1318 			continue;
1319 		LayerMap::iterator l = _layers.find(cdl->slave_z);
1320 		if (l == _layers.end())
1321 			throw_ex(("no slave layer found (z: %d)", cdl->slave_z));
1322 		cdl->setSlave(cdl->slave_z, l->second);
1323 	}
1324 
1325 	int pn;
1326 	s.get(pn);
1327 	while(pn--) {
1328 		std::string name, value;
1329 		s.get(name);
1330 		s.get(value);
1331 		properties.insert(PropertyMap::value_type(name, value));
1332 	}
1333 
1334 	{
1335 		PropertyMap::const_iterator p = properties.find("config:map.torus");
1336 		if (p != properties.end()) {
1337 			if (p->second.find("true") != std::string::npos) {
1338 				_torus = true;
1339 				LOG_DEBUG(("torus mode switched on..."));
1340 			}
1341 		}
1342 	}
1343 
1344 	load_map_signal.emit();
1345 }
1346 
addTiles(const sdlx::Surface * image,const int first_gid)1347 const int IMap::addTiles(const sdlx::Surface *image, const int first_gid) {
1348 	int id = 0;
1349 TRY {
1350 	const_cast<sdlx::Surface *>(image)->set_alpha(0, 0);
1351 	int w = image->get_width(), h = image->get_height();
1352 
1353 	for(int y = 0; y < h; y += _th) {
1354 		for(int x = 0; x < w; x += _tw) {
1355 			sdlx::Surface *s = new sdlx::Surface;
1356 			s->create_rgb(_tw, _th, 24);
1357 			s->display_format_alpha();
1358 
1359 			sdlx::Rect from(x, y, _tw, _th);
1360 			s->blit(*image, from);
1361 			GET_CONFIG_VALUE("engine.strip-alpha-from-map-tiles", bool, strip_alpha, false);
1362 			bool locked = false;
1363 			if (strip_alpha) {
1364 				s->lock();
1365 				locked = true;
1366 				Uint8 r,g,b,a;
1367 				for(int y = 0; y < s->get_height(); ++y)
1368 					for(int x = 0; x < s->get_width(); ++x) {
1369 						s->get_rgba(s->get_pixel(x, y), r, g, b, a);
1370 						if (a != 255)
1371 							s->put_pixel(x, y, s->map_rgba(r, g, b, (a > 51)?51:a));
1372 					}
1373 			}
1374 
1375 			GET_CONFIG_VALUE("engine.mark-map-tiles", bool, marks, false);
1376 			if (marks) {
1377 				if (!locked) {
1378 					s->lock();
1379 					locked = true;
1380 				}
1381 				Uint32 color = s->map_rgba(255,0,255,249); //magic value to avoid Collision map confusing
1382 				s->put_pixel(0, 0, color);
1383 				s->put_pixel(1, 0, color);
1384 				s->put_pixel(0, 1, color);
1385 			}
1386 			if (locked)
1387 				s->unlock();
1388 
1389 			//s->save_bmp(mrt::format_string("tile-%d.bmp", id));
1390 
1391 			//LOG_DEBUG(("cut tile %d from tileset [%d:%d, %d:%d]", first_gid + id, x, y, _tw, _th));
1392 			if ((size_t)(first_gid + id) >= _tiles.size())
1393 				_tiles.resize(first_gid + id + 20);
1394 
1395 			delete _tiles[first_gid + id].surface;
1396 			_tiles[first_gid + id].surface = NULL;
1397 			delete _tiles[first_gid + id].cmap;
1398 			_tiles[first_gid + id].cmap = NULL;
1399 			delete _tiles[first_gid + id].vmap;
1400 			_tiles[first_gid + id].vmap = NULL;
1401 
1402 			_tiles[first_gid + id].cmap = new sdlx::CollisionMap;
1403 			_tiles[first_gid + id].cmap->init(s, sdlx::CollisionMap::OnlyOpaque);
1404 			_tiles[first_gid + id].vmap = new sdlx::CollisionMap;
1405 			_tiles[first_gid + id].vmap->init(s, sdlx::CollisionMap::AnyVisible);
1406 			_tiles[first_gid + id].surface = s;
1407 			++id;
1408 			s = NULL;
1409 		}
1410 	}
1411 	const_cast<sdlx::Surface *>(image)->set_alpha(0, SDL_SRCALPHA);	//fixme: dangerous
1412 } CATCH("addTiles", {const_cast<sdlx::Surface *>(image)->set_alpha(0, SDL_SRCALPHA); throw; })
1413 	return id;
1414 }
1415 
getLayers(std::set<int> & layers_z) const1416 void IMap::getLayers(std::set<int> &layers_z) const {
1417 	layers_z.clear();
1418 	for(LayerMap::const_iterator i = _layers.begin(); i != _layers.end(); ++i) {
1419 		layers_z.insert(i->first);
1420 	}
1421 }
1422 
getLayer(const int z)1423 Layer* IMap::getLayer(const int z) {
1424 	LayerMap::iterator i = _layers.find(z);
1425 	if (i == _layers.end())
1426 		throw_ex(("getLayer(%d) could not find layer with given z", z));
1427 	return i->second;
1428 }
1429 
getTile(const size_t idx) const1430 const IMap::TileDescriptor & IMap::getTile(const size_t idx) const {
1431 	if (idx >= _tiles.size())
1432 		throw_ex(("getTile(%u) is out of range 0-%u", (unsigned)idx, (unsigned)_tiles.size()));
1433 	return _tiles[idx];
1434 }
1435 
generateXML(std::string & result) const1436 void IMap::generateXML(std::string &result) const {
1437 	result = mrt::format_string(
1438 		"<?xml version=\"1.0\"?>\n"
1439 		"<map version=\"0.99b\" orientation=\"orthogonal\" width=\"%d\" height=\"%d\" tilewidth=\"%d\" tileheight=\"%d\">\n",
1440 		_w, _h, _tw, _th
1441 		);
1442 	if (!properties.empty()) {
1443 		result += "\t<properties>\n";
1444 		for(PropertyMap::const_iterator i = properties.begin(); i != properties.end(); ++i) {
1445 			result += mrt::format_string("\t\t<property name=\"%s\" value=\"%s\"/>\n", escape(i->first).c_str(), escape(i->second).c_str());
1446 		}
1447 		result += "\t</properties>\n";
1448 	}
1449 
1450 	size_t n = _tilesets.size();
1451 	for(size_t i = 0; i < n; ++i) {
1452 		const TilesetList::value_type &ts = _tilesets[i];
1453 		result += mrt::format_string("\t<tileset name=\"%s\" firstgid=\"%d\" tilewidth=\"%d\" tileheight=\"%d\">\n",
1454 			escape(mrt::FSNode::get_filename(ts.first, false)).c_str(), ts.second, _tw, _th);
1455 		result += mrt::format_string("\t\t<image source=\"%s\"/>\n", escape(ts.first).c_str());
1456 		result += "\t</tileset>\n";
1457 	}
1458 
1459 	for(LayerMap::const_iterator i = _layers.begin(); i != _layers.end(); ++i) {
1460 		std::string layer;
1461 		i->second->generateXML(layer);
1462 		result += layer;
1463 	}
1464 	result += "</map>\n";
1465 }
1466 
deleteLayer(const int kill_z)1467 void IMap::deleteLayer(const int kill_z) {
1468 	LayerMap::iterator i = _layers.find(kill_z);
1469 	if (i == _layers.end())
1470 		throw_ex(("no layer with z %d", kill_z));
1471 
1472 	LayerMap new_map;
1473 	int z = -1000;
1474 
1475 	for(LayerMap::iterator l = _layers.begin(); l != _layers.end(); ) {
1476 		if (l->first == kill_z) {
1477 			delete l->second;
1478 			_layers.erase(l++);
1479 			continue;
1480 		}
1481 		if (l->second->properties.find("z") != l->second->properties.end()) {
1482 			z = atoi(l->second->properties["z"].c_str());
1483 		}
1484 		//LOG_DEBUG(("%s -> %d", l->second->name.c_str(), z));
1485 		assert(new_map.find(z) == new_map.end());
1486 		new_map[z++] = l->second;
1487 		++l;
1488 	}
1489 	_layers = new_map;
1490 	generateMatrixes();
1491 }
1492 
addLayer(const int after_z,const std::string & name)1493 void IMap::addLayer(const int after_z, const std::string &name) {
1494 	int z = -1000;
1495 	Layer *layer = NULL;
1496 
1497 	if (!_layers.empty()) {
1498 		LayerMap::iterator i = _layers.find(after_z);
1499 		if (i == _layers.end())
1500 			throw_ex(("no layer with z %d", after_z));
1501 	} else {
1502 		layer = new Layer(); //first layer
1503 		layer->name = name;
1504 		layer->init(_w, _h);
1505 		_layers.insert(LayerMap::value_type(z++, layer));
1506 		return;
1507 	}
1508 
1509 	LayerMap new_map;
1510 
1511 	for(LayerMap::iterator l = _layers.begin(); l != _layers.end(); ++l) {
1512 		if (l->second->properties.find("z") != l->second->properties.end()) {
1513 			z = atoi(l->second->properties["z"].c_str());
1514 		}
1515 		//LOG_DEBUG(("%s -> %d", l->second->name.c_str(), z));
1516 		if (new_map.find(z) != new_map.end()) {
1517 			if (layer != NULL)
1518 				delete layer;
1519 			throw_ex(("no room for layer"));
1520 		}
1521 		new_map[z++] = l->second;
1522 
1523 		if (z == after_z + 1) {
1524 			layer = new Layer();
1525 			layer->name = name;
1526 			layer->init(_w, _h);
1527 			new_map.insert(LayerMap::value_type(z++, layer));
1528 		}
1529 	}
1530 	_layers = new_map;
1531 }
1532 
swapLayers(const int z1,const int z2)1533 const bool IMap::swapLayers(const int z1, const int z2) {
1534 	LOG_DEBUG(("swap layers %d <-> %d", z1, z2));
1535 	LayerMap::iterator l1 = _layers.find(z1), l2 = _layers.find(z2);
1536 	if (l1 == _layers.end())
1537 		throw_ex(("layer with z %d was not found", z1));
1538 	if (l2 == _layers.end())
1539 		throw_ex(("layer with z %d was not found", z2));
1540 
1541 	bool has_z1 = l1->second->properties.find("z") != l1->second->properties.end();
1542 	bool has_z2 = l2->second->properties.find("z") != l2->second->properties.end();
1543 	if (has_z1 && has_z2) {
1544 		LOG_DEBUG(("cannot swap two absolutely positioned layers."));
1545 		return false;
1546 	}
1547 	math::exchange(l1->second, l2->second);
1548 	LayerMap new_map;
1549 
1550 	int z = -1000;
1551 	for(LayerMap::iterator l = _layers.begin(); l != _layers.end(); ++l) {
1552 		if (l->second->properties.find("z") != l->second->properties.end()) {
1553 			z = atoi(l->second->properties["z"].c_str());
1554 		}
1555 		//LOG_DEBUG(("%s -> %d", l->second->name.c_str(), z));
1556 		if (new_map.find(z) != new_map.end()) {
1557 			LOG_DEBUG(("no room for new layer. restore changes..."));
1558 			math::exchange(l1->second, l2->second);
1559 			return false;
1560 		}
1561 		new_map[z++] = l->second;
1562 	}
1563 	_layers = new_map;
1564 	return true;
1565 }
1566 
1567 template<typename T>
c2v(T & pos,const std::string & str)1568 static void c2v(T &pos, const std::string &str) {
1569 	std::string pos_str = str;
1570 
1571 	const bool tiled_pos = pos_str[0] == '@';
1572 	if (tiled_pos) {
1573 		pos_str = pos_str.substr(1);
1574 	}
1575 
1576 	TRY {
1577 		pos.fromString(pos_str);
1578 	} CATCH(mrt::format_string("parsing '%s'", str.c_str()).c_str() , throw;)
1579 
1580 	if (tiled_pos) {
1581 		v2<int> tile_size = Map->getTileSize();
1582 		pos.x *= tile_size.x;
1583 		pos.y *= tile_size.y;
1584 		//keep z untouched.
1585 	}
1586 }
1587 
resize(const int left_cut,const int right_cut,const int up_cut,const int down_cut)1588 void IMap::resize(const int left_cut, const int right_cut, const int up_cut, const int down_cut) {
1589 	if (!loaded() || (left_cut == 0 && right_cut == 0 && up_cut == 0 && down_cut == 0))
1590 		return;
1591 
1592 	LOG_DEBUG(("cutting map: %d %d %d %d", left_cut, right_cut, up_cut, down_cut));
1593 	if (left_cut < 0 && right_cut < 0 && -left_cut - right_cut >= _w)
1594 		throw_ex(("invalid left/right shrink width"));
1595 	if (up_cut < 0 && down_cut < 0 && -up_cut - down_cut >= _h)
1596 		throw_ex(("invalid up/down shrink height"));
1597 	for(LayerMap::iterator i = _layers.begin(); i != _layers.end(); ++i) {
1598 		i->second->resize(left_cut, right_cut, up_cut, down_cut);
1599 	}
1600 	_w += left_cut + right_cut;
1601 	_h += up_cut + down_cut;
1602 
1603 	for(PropertyMap::iterator i = properties.begin(); i != properties.end(); ++i) {
1604 		const std::string &name = i->first;
1605 		std::string &value = i->second;
1606 		if (name.compare(0, 6, "spawn:") == 0 || name.compare(0, 9, "waypoint:") == 0) {
1607 			v3<int> pos;
1608 			c2v< v3<int> >(pos, value);
1609 			pos.x += left_cut * _tw;
1610 			pos.y += up_cut * _th;
1611 			value = mrt::format_string("%d,%d,%d", pos.x, pos.y, pos.z);
1612 			LOG_DEBUG(("fixed %s->%s", name.c_str(), value.c_str()));
1613 		} else if (name.compare(0, 5, "zone:") == 0) {
1614 			std::vector<std::string> res;
1615 			mrt::split(res, value, ":", 2);
1616 
1617 			v3<int> pos;
1618 			c2v< v3<int> >(pos, res[0]);
1619 			pos.x += left_cut * _tw;
1620 			pos.y += up_cut * _th;
1621 
1622 			value = mrt::format_string("%d,%d,%d:", pos.x, pos.y, pos.z) + res[1];
1623 			LOG_DEBUG(("fixed %s->%s", name.c_str(), value.c_str()));
1624 		}
1625 	}
1626 
1627 	map_resize_signal.emit(left_cut * _tw, right_cut * _tw, up_cut * _th, down_cut * _th);
1628 }
1629