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> ¢er, 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