1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/shared/std/string.h"
24
25 #include "ultima/nuvie/core/nuvie_defs.h"
26 #include "ultima/nuvie/files/nuvie_io_file.h"
27
28 #include "ultima/nuvie/conf/configuration.h"
29 #include "ultima/nuvie/core/game.h"
30 #include "ultima/nuvie/core/tile_manager.h"
31 #include "ultima/nuvie/actors/actor_manager.h"
32 #include "ultima/nuvie/core/map.h"
33 #include "ultima/nuvie/gui/widgets/map_window.h"
34
35 #include "ultima/nuvie/misc/u6_misc.h"
36
37 namespace Ultima {
38 namespace Nuvie {
39
Map(Configuration * cfg)40 Map::Map(Configuration *cfg) {
41 config = cfg;
42
43 tile_manager = NULL;
44 obj_manager = NULL;
45 actor_manager = NULL;
46 surface = NULL;
47 roof_surface = NULL;
48 dungeons[4] = NULL;
49
50 config->value(config_get_game_key(config) + "/roof_mode", roof_mode, false);
51 }
52
~Map()53 Map::~Map() {
54 uint8 i;
55
56 if (surface == NULL)
57 return;
58
59 free(surface);
60
61 for (i = 0; i < 5; i++)
62 free(dungeons[i]);
63
64 if (roof_surface)
65 free(roof_surface);
66 }
67
68
get_map_data(uint8 level)69 unsigned char *Map::get_map_data(uint8 level) {
70 if (level == 0)
71 return surface;
72
73 if (level > 5)
74 return NULL;
75
76 return dungeons[level - 1];
77 }
78
get_roof_data(uint8 level)79 uint16 *Map::get_roof_data(uint8 level) {
80 if (level == 0)
81 return roof_surface;
82
83 return NULL;
84 }
85
get_tile(uint16 x,uint16 y,uint8 level,bool original_tile)86 Tile *Map::get_tile(uint16 x, uint16 y, uint8 level, bool original_tile) {
87 Tile *map_tile;
88 uint8 *ptr;
89
90 if (level > 5)
91 return NULL;
92
93 ptr = get_map_data(level);
94
95 WRAP_COORD(x, level);
96 WRAP_COORD(y, level);
97
98 if (original_tile)
99 map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
100 else
101 map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
102
103 return map_tile;
104 }
105
get_width(uint8 level)106 uint16 Map::get_width(uint8 level) {
107 if (level == 0)
108 return 1024; // surface
109
110 return 256; // dungeon
111 }
112
is_passable(uint16 x,uint16 y,uint8 level)113 bool Map::is_passable(uint16 x, uint16 y, uint8 level) {
114 uint8 *ptr;
115 Tile *map_tile;
116
117 WRAP_COORD(x, level);
118 WRAP_COORD(y, level);
119
120 uint8 obj_status = obj_manager->is_passable(x, y, level);
121 if (obj_status == OBJ_NOT_PASSABLE) {
122 return false;
123 }
124
125 //special case for bridges, hacked doors and dungeon entrances etc.
126 if (obj_status != OBJ_NO_OBJ && obj_manager->is_forced_passable(x, y, level))
127 return true;
128
129 ptr = get_map_data(level);
130 map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
131
132 return map_tile->passable;
133 }
134
135 /***
136 * Can we enter this map location by traveling in a given direction?
137 * Used by MD
138 */
is_passable(uint16 x,uint16 y,uint8 level,uint8 dir)139 bool Map::is_passable(uint16 x, uint16 y, uint8 level, uint8 dir) {
140 if (is_passable_from_dir(x, y, level, get_reverse_direction(dir))) {
141 sint16 rel_x, rel_y;
142 uint16 tx, ty;
143 get_relative_dir(get_reverse_direction(dir), &rel_x, &rel_y);
144 tx = wrap_signed_coord((sint16)x + rel_x, level);
145 ty = wrap_signed_coord((sint16)y + rel_y, level);
146 return is_passable_from_dir(tx, ty, level, dir);
147 }
148 return false;
149 }
150
is_passable_from_dir(uint16 x,uint16 y,uint8 level,uint8 dir)151 bool Map::is_passable_from_dir(uint16 x, uint16 y, uint8 level, uint8 dir) {
152 uint8 *ptr;
153 Tile *map_tile;
154
155 WRAP_COORD(x, level);
156 WRAP_COORD(y, level);
157
158 uint8 obj_status = obj_manager->is_passable(x, y, level);
159 if (obj_status == OBJ_NOT_PASSABLE) {
160 return false;
161 }
162
163 //special case for bridges, hacked doors and dungeon entrances etc.
164 if (obj_status != OBJ_NO_OBJ && obj_manager->is_forced_passable(x, y, level))
165 return true;
166
167 ptr = get_map_data(level);
168 map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
169
170 if (!map_tile->passable && !(map_tile->flags1 & TILEFLAG_WALL)) {
171 switch (dir) {
172 case NUVIE_DIR_W :
173 return (map_tile->flags1 & TILEFLAG_WALL_WEST);
174 case NUVIE_DIR_S :
175 return (map_tile->flags1 & TILEFLAG_WALL_SOUTH);
176 case NUVIE_DIR_E :
177 return (map_tile->flags1 & TILEFLAG_WALL_EAST);
178 case NUVIE_DIR_N :
179 return (map_tile->flags1 & TILEFLAG_WALL_NORTH);
180 case NUVIE_DIR_NE :
181 return !(!(map_tile->flags1 & TILEFLAG_WALL_NORTH) || !(map_tile->flags1 & TILEFLAG_WALL_EAST));
182 case NUVIE_DIR_NW :
183 return !(!(map_tile->flags1 & TILEFLAG_WALL_NORTH) || !(map_tile->flags1 & TILEFLAG_WALL_WEST));
184 case NUVIE_DIR_SE :
185 return !(!(map_tile->flags1 & TILEFLAG_WALL_SOUTH) || !(map_tile->flags1 & TILEFLAG_WALL_EAST));
186 case NUVIE_DIR_SW :
187 return !(!(map_tile->flags1 & TILEFLAG_WALL_SOUTH) || !(map_tile->flags1 & TILEFLAG_WALL_WEST));
188 }
189 }
190
191 return map_tile->passable;
192 }
193
194 /* Returns true if an entire area is_passable(). */
is_passable(uint16 x1,uint16 y1,uint16 x2,uint16 y2,uint8 level)195 bool Map::is_passable(uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint8 level) {
196 for (int x = x1; x <= x2; x++)
197 for (int y = y1; y <= y2; y++)
198 if (!is_passable((uint16)x, (uint16)y, level))
199 return false;
200 return true;
201 }
202
is_boundary(uint16 x,uint16 y,uint8 level)203 bool Map::is_boundary(uint16 x, uint16 y, uint8 level) {
204 uint8 *ptr;
205 Tile *map_tile;
206
207 WRAP_COORD(x, level);
208 WRAP_COORD(y, level);
209
210 ptr = get_map_data(level);
211 map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
212
213 if (map_tile->boundary && obj_manager->is_forced_passable(x, y, level) == false)
214 return true;
215
216 if (obj_manager->is_boundary(x, y, level))
217 return true;
218
219 return false;
220 }
221
is_missile_boundary(uint16 x,uint16 y,uint8 level,Obj * excluded_obj)222 bool Map::is_missile_boundary(uint16 x, uint16 y, uint8 level, Obj *excluded_obj) {
223 uint8 *ptr;
224 Tile *map_tile;
225
226 WRAP_COORD(x, level);
227 WRAP_COORD(y, level);
228
229 ptr = get_map_data(level);
230 map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
231
232 if ((map_tile->flags2 & TILEFLAG_MISSILE_BOUNDARY) != 0 && obj_manager->is_forced_passable(x, y, level) == false)
233 return true;
234
235 if (obj_manager->is_boundary(x, y, level, TILEFLAG_MISSILE_BOUNDARY, excluded_obj))
236 return true;
237
238 return false;
239 }
240
is_water(uint16 x,uint16 y,uint16 level,bool ignore_objects)241 bool Map::is_water(uint16 x, uint16 y, uint16 level, bool ignore_objects) {
242 uint8 *ptr;
243 Tile *map_tile;
244 Obj *obj;
245
246 WRAP_COORD(x, level);
247 WRAP_COORD(y, level);
248
249 if (!ignore_objects) {
250 obj = obj_manager->get_obj(x, y, level);
251 if (obj != NULL)
252 return false;
253 }
254
255 ptr = get_map_data(level);
256 map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
257
258 if (map_tile->water)
259 return true;
260
261 return false;
262 }
263
is_damaging(uint16 x,uint16 y,uint8 level,bool ignore_objects)264 bool Map::is_damaging(uint16 x, uint16 y, uint8 level, bool ignore_objects) {
265 uint8 *ptr = get_map_data(level);
266
267 WRAP_COORD(x, level);
268 WRAP_COORD(y, level);
269
270 Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
271
272 if (map_tile->damages)
273 return true;
274
275 if (!ignore_objects) {
276 if (obj_manager->is_damaging(x, y, level))
277 return true;
278 }
279 return false;
280 }
281
can_put_obj(uint16 x,uint16 y,uint8 level)282 bool Map::can_put_obj(uint16 x, uint16 y, uint8 level) {
283 LineTestResult lt;
284
285 if (lineTest(x, y, x, y, level, LT_HitActors | LT_HitUnpassable, lt)) {
286 if (lt.hitObj) {
287 // We can place an object on a bench or table. Or on any other object if
288 // the object is passable and not on a boundary.
289
290 Tile *obj_tile = obj_manager->get_obj_tile(lt.hitObj->obj_n, lt.hitObj->frame_n);
291 if ((obj_tile->flags3 & TILEFLAG_CAN_PLACE_ONTOP) ||
292 (obj_tile->passable && !is_boundary(lt.hit_x, lt.hit_y, lt.hit_level))) {
293 return true;
294 } else {
295 return false;
296 }
297 }
298 }
299
300 if (is_missile_boundary(x, y, level))
301 return false;
302
303 return true;
304 }
305
get_impedance(uint16 x,uint16 y,uint8 level,bool ignore_objects)306 uint8 Map::get_impedance(uint16 x, uint16 y, uint8 level, bool ignore_objects) {
307 uint8 *ptr = get_map_data(level);
308 WRAP_COORD(x, level);
309 WRAP_COORD(y, level);
310 Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
311 uint8 impedance = 0;
312
313 if (!ignore_objects) {
314 U6LList *obj_list = obj_manager->get_obj_list(x, y, level);
315 if (obj_list) {
316 for (U6Link *link = obj_list->start(); link != NULL; link = link->next) {
317 Obj *obj = (Obj *)link->data;
318 if (obj != NULL) {
319 uint8 tile_flag = obj_manager->get_obj_tile(obj->obj_n, obj->frame_n)->flags1;
320 if ((tile_flag & TILEFLAG_BLOCKING) == 0) {
321 impedance += (tile_flag & TILEFLAG_IMPEDANCE) >> TILEFLAG_IMPEDANCE_SHIFT;
322 }
323 }
324 }
325 }
326 }
327
328 if ((map_tile->flags1 & TILEFLAG_BLOCKING) == 0)
329 impedance += (map_tile->flags1 & TILEFLAG_IMPEDANCE) >> TILEFLAG_IMPEDANCE_SHIFT;
330
331 return impedance;
332 }
333
get_dmg_tile(uint16 x,uint16 y,uint8 level)334 Tile *Map::get_dmg_tile(uint16 x, uint16 y, uint8 level) {
335 Tile *tile = get_tile(x, y, level);
336
337 if (tile->damages)
338 return tile;
339
340 return obj_manager->get_obj_dmg_tile(x, y, level);
341 }
342
actor_at_location(uint16 x,uint16 y,uint8 level,bool inc_surrounding_objs)343 bool Map::actor_at_location(uint16 x, uint16 y, uint8 level, bool inc_surrounding_objs) {
344 WRAP_COORD(x, level);
345 WRAP_COORD(y, level);
346 //check for blocking Actor at location.
347 if (actor_manager->get_actor(x, y, level, inc_surrounding_objs) != NULL)
348 return true;
349
350 return false;
351 }
352
353 /* Return pointer to actor standing at map coordinates.
354 */
get_actor(uint16 x,uint16 y,uint8 z,bool inc_surrounding_objs)355 Actor *Map::get_actor(uint16 x, uint16 y, uint8 z, bool inc_surrounding_objs) {
356 WRAP_COORD(x, z);
357 WRAP_COORD(y, z);
358 return (actor_manager->get_actor(x, y, z, inc_surrounding_objs));
359 }
360
361
look(uint16 x,uint16 y,uint8 level)362 const char *Map::look(uint16 x, uint16 y, uint8 level) {
363 unsigned char *ptr;
364 uint16 tile_num;
365 Obj *obj;
366 uint16 qty = 0;
367
368 if (level == 0) {
369 ptr = surface;
370 } else
371 ptr = dungeons[level - 1];
372
373 WRAP_COORD(x, level);
374 WRAP_COORD(y, level);
375 obj = obj_manager->get_obj(x, y, level);
376 if (obj != NULL && !(obj->status & OBJ_STATUS_INVISIBLE) //only show visible objects.
377 && !Game::get_game()->get_map_window()->tile_is_black(obj->x, obj->y, obj)) {
378 // tile = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n)+obj->frame_n);
379 // tile_num = tile->tile_num;
380 // qty = obj->qty;
381 return obj_manager->look_obj(obj);
382 }
383 tile_num = ptr[y * get_width(level) + x];
384 return tile_manager->lookAtTile(tile_num, qty, true);
385 }
386
387
loadMap(TileManager * tm,ObjManager * om)388 bool Map::loadMap(TileManager *tm, ObjManager *om) {
389 Std::string filename;
390 NuvieIOFileRead map_file;
391 NuvieIOFileRead chunks_file;
392 unsigned char *map_data;
393 unsigned char *map_ptr;
394 unsigned char *chunk_data;
395
396 uint8 i;
397
398 tile_manager = tm;
399 obj_manager = om;
400
401 config_get_path(config, "map", filename);
402 if (map_file.open(filename) == false)
403 return false;
404
405 config_get_path(config, "chunks", filename);
406 if (chunks_file.open(filename) == false)
407 return false;
408
409 map_data = map_file.readAll();
410 if (map_data == NULL)
411 return false;
412
413 chunk_data = chunks_file.readAll();
414 if (chunk_data == NULL)
415 return false;
416
417 map_ptr = map_data;
418
419 surface = (unsigned char *)malloc(1024 * 1024);
420 if (surface == NULL)
421 return false;
422
423 for (i = 0; i < 64; i++) {
424 insertSurfaceSuperChunk(map_ptr, chunk_data, i);
425 map_ptr += 384;
426 }
427
428 for (i = 0; i < 5; i++) {
429 dungeons[i] = (unsigned char *)malloc(256 * 256);
430 if (dungeons[i] == NULL)
431 return false;
432
433 insertDungeonSuperChunk(map_ptr, chunk_data, i);
434 map_ptr += 1536;
435 }
436
437 free(map_data);
438 free(chunk_data);
439
440 if (roof_mode)
441 loadRoofData();
442
443 /* ERIC Useful for testing map wrapping
444 I plan to add a map patch function
445 to allow custom map changes to be
446 loaded easily into nuvie.
447
448 uint16 mx,my;
449 for(my=100;my<130;my++)
450 for(mx=0;mx<30;mx++)
451 surface[my * 1024 + mx] = 1;
452
453 for(my=100;my<130;my++)
454 for(mx=1000;mx<1024;mx++)
455 surface[my * 1024 + mx] = 111;
456 */
457 /*
458 printf("\n\n\n\n\n\n\n");
459
460 uint16 mx,my;
461 for(my=0;my<1024;my++)
462 {
463 for(mx=0;mx<1024;mx++)
464 {
465 printf("%3d,", surface[my * 1024 + mx]+1);
466 }
467 printf("\n");
468 }
469 */
470 return true;
471 }
472
has_roof(uint16 x,uint16 y,uint8 level)473 bool Map::has_roof(uint16 x, uint16 y, uint8 level) {
474 if (!roof_mode || level != 0)
475 return false;
476
477 if (roof_surface[y * 1024 + x] != 0)
478 return true;
479
480 return false;
481 }
482
getRoofDataFilename()483 Std::string Map::getRoofDataFilename() {
484 Std::string game_type, datadir, path, mapfile;
485
486 config->value("config/datadir", datadir, "");
487 config->value("config/GameID", game_type);
488
489 build_path(datadir, "maps", path);
490 datadir = path;
491 build_path(datadir, game_type, path);
492 datadir = path;
493 build_path(datadir, "roof_map_00.dat", mapfile);
494
495 return mapfile;
496 }
497
getRoofTilesetFilename()498 Std::string Map::getRoofTilesetFilename() {
499 Std::string datadir;
500 Std::string imagefile;
501 Std::string path;
502
503 config->value("config/datadir", datadir, "");
504 build_path(datadir, "images", path);
505 datadir = path;
506 build_path(datadir, "roof_tiles.bmp", imagefile);
507 return imagefile;
508 }
509
set_roof_mode(bool roofs)510 void Map::set_roof_mode(bool roofs) {
511 roof_mode = roofs;
512 if (roof_mode) {
513 if (roof_surface)
514 return;
515 else
516 loadRoofData();
517 } else {
518 if (roof_surface) {
519 free(roof_surface);
520 roof_surface = NULL;
521 }
522 }
523 }
524
loadRoofData()525 void Map::loadRoofData() {
526 NuvieIOFileRead file;
527 uint16 *ptr;
528 roof_surface = (uint16 *)malloc(1024 * 1024 * 2);
529
530 if (file.open(getRoofDataFilename())) {
531 memset(roof_surface, 0, 1024 * 1024 * 2);
532 ptr = roof_surface;
533 while (!file.is_eof()) {
534 uint16 offset = file.read2();
535 ptr += offset;
536 uint8 run_len = file.read1();
537 for (uint8 i = 0; i < run_len; i++) {
538 *ptr = file.read2();
539 ptr++;
540 }
541 }
542 } else {
543 if (roof_surface) {
544 free(roof_surface);
545 roof_surface = NULL;
546 }
547 roof_mode = false;
548 }
549 }
550
saveRoofData()551 void Map::saveRoofData() {
552 NuvieIOFileWrite file;
553 uint32 prev_offset = 0;
554 uint32 cur_offset = 0;
555 uint16 run_length = 0;
556
557 if (roof_surface && file.open(getRoofDataFilename())) {
558 for (; cur_offset < 1048576;) {
559 for (; cur_offset < prev_offset + 65535 && cur_offset < 1048576;) {
560 if (roof_surface[cur_offset] != 0) {
561 file.write2((uint16)(cur_offset - prev_offset));
562 for (run_length = 0; run_length < 256; run_length++) {
563 if (roof_surface[cur_offset + run_length] == 0)
564 break;
565 }
566 if (run_length == 256)
567 run_length--;
568
569 file.write1((uint8)run_length);
570 for (uint8 i = 0; i < run_length; i++) {
571 file.write2(roof_surface[cur_offset + i]);
572 }
573 cur_offset += run_length;
574 break;
575 }
576
577 cur_offset++;
578 if (cur_offset == prev_offset + 65535) {
579 //write blank.
580 file.write2(65535);
581 file.write1(0);
582 }
583 }
584
585 prev_offset = cur_offset;
586 }
587 }
588 }
589
insertSurfaceSuperChunk(unsigned char * schunk,unsigned char * chunk_data,uint8 schunk_num)590 void Map::insertSurfaceSuperChunk(unsigned char *schunk, unsigned char *chunk_data, uint8 schunk_num) {
591 uint16 world_x, world_y;
592 uint16 c1, c2;
593 uint8 i, j;
594
595 world_x = schunk_num % 8;
596 world_y = (schunk_num - world_x) / 8;
597
598 world_x *= 128;
599 world_y *= 128;
600
601 for (i = 0; i < 16; i++) {
602 for (j = 0; j < 16; j += 2) {
603 c1 = ((schunk[1] & 0xf) << 8) | schunk[0];
604 c2 = (schunk[2] << 4) | (schunk[1] >> 4);
605
606 insertSurfaceChunk(&chunk_data[c1 * 64], world_x + j * 8, world_y + i * 8);
607 insertSurfaceChunk(&chunk_data[c2 * 64], world_x + (j + 1) * 8, world_y + i * 8);
608
609 schunk += 3;
610 }
611 }
612 }
613
insertSurfaceChunk(unsigned char * chunk,uint16 x,uint16 y)614 void Map::insertSurfaceChunk(unsigned char *chunk, uint16 x, uint16 y) {
615 unsigned char *map_ptr;
616 uint8 i;
617
618 map_ptr = &surface[y * 1024 + x];
619
620 for (i = 0; i < 8; i++) {
621 memcpy(map_ptr, chunk, 8);
622 map_ptr += 1024;
623 chunk += 8;
624 }
625
626 }
627
insertDungeonSuperChunk(unsigned char * schunk,unsigned char * chunk_data,uint8 level)628 void Map::insertDungeonSuperChunk(unsigned char *schunk, unsigned char *chunk_data, uint8 level) {
629 uint16 c1, c2;
630 uint8 i, j;
631
632 for (i = 0; i < 32; i++) {
633 for (j = 0; j < 32; j += 2) {
634 c1 = ((schunk[1] & 0xf) << 8) | schunk[0];
635 c2 = (schunk[2] << 4) | (schunk[1] >> 4);
636
637 insertDungeonChunk(&chunk_data[c1 * 64], j * 8, i * 8, level);
638 insertDungeonChunk(&chunk_data[c2 * 64], (j + 1) * 8, i * 8, level);
639
640 schunk += 3;
641 }
642 }
643 }
644
insertDungeonChunk(unsigned char * chunk,uint16 x,uint16 y,uint8 level)645 void Map::insertDungeonChunk(unsigned char *chunk, uint16 x, uint16 y, uint8 level) {
646 unsigned char *map_ptr;
647 uint8 i;
648
649 map_ptr = &dungeons[level][y * 256 + x];
650
651 for (i = 0; i < 8; i++) {
652 memcpy(map_ptr, chunk, 8);
653 map_ptr += 256;
654 chunk += 8;
655 }
656
657 }
658
659
660 /* Get absolute coordinates for relative destination from MapCoord.
661 */
abs_coords(sint16 dx,sint16 dy)662 MapCoord MapCoord::abs_coords(sint16 dx, sint16 dy) {
663 // uint16 pitch = Map::get_width(z); cannot call function without object
664 uint16 pitch = (z == 0) ? 1024 : 256;
665 dx += x;
666 dy += y;
667 // wrap on map boundary for MD
668 if (dx < 0)
669 dx = pitch + dx;
670 else if (dx >= pitch)
671 dx = pitch - dx;
672 if (dy < 0)
673 dy = 0;
674 else if (dy >= pitch)
675 dy = pitch - 1;
676 return (MapCoord(dx, dy, z));
677 }
678
679
680 /* Returns true if this map coordinate is visible in the game window.
681 */
is_visible()682 bool MapCoord::is_visible() {
683 return (Game::get_game()->get_map_window()->in_window(x, y, z));
684 }
685
686
testIntersection(int x,int y,uint8 level,uint8 flags,LineTestResult & Result,Obj * excluded_obj)687 bool Map::testIntersection(int x, int y, uint8 level, uint8 flags, LineTestResult &Result, Obj *excluded_obj) {
688 /* more checks added, may need more testing (SB-X) */
689 #if 0
690 if (flags & LT_HitUnpassable) {
691 if (!is_passable(x, y, level)) {
692 Result.init(x, y, level, NULL, obj_manager->get_obj(x, y, level, true));
693 return true;
694 }
695 }
696
697 if (flags & LT_HitForcedPassable) {
698 if (obj_manager->is_forced_passable(x, y, level)) {
699 Result.init(x, y, level, NULL, obj_manager->get_obj(x, y, level, true));
700 return true;
701 }
702 }
703
704 if (flags & LT_HitActors) {
705 // TODO:
706 }
707
708 return false;
709 #else
710 if (flags & LT_HitUnpassable) {
711 if (!is_passable(x, y, level)) {
712 Obj *obj_hit = obj_manager->get_obj(x, y, level);
713 if (!obj_hit || !excluded_obj || obj_hit != excluded_obj) {
714 Result.init(x, y, level, NULL, obj_manager->get_obj(x, y, level, true));
715 return true;
716 }
717 }
718 }
719
720 if (flags & LT_HitMissileBoundary) {
721 if (is_missile_boundary(x, y, level, excluded_obj)) {
722 Result.init(x, y, level, NULL, obj_manager->get_obj(x, y, level, true));
723 return true;
724 }
725 }
726
727 if (flags & LT_HitForcedPassable) {
728 if (obj_manager->is_forced_passable(x, y, level)) {
729 Result.init(x, y, level, NULL, obj_manager->get_obj(x, y, level, true));
730 return true;
731 }
732 }
733
734 if (flags & LT_HitActors) {
735 if (actor_manager->get_actor(x, y, level)) {
736 Result.init(x, y, level, actor_manager->get_actor(x, y, level), NULL);
737 return true;
738 }
739 }
740
741 if ((flags & LT_HitLocation) && Result.loc_to_hit) {
742 if (x == Result.loc_to_hit->x && y == Result.loc_to_hit->y) {
743 Result.init(x, y, level, NULL, NULL);
744 Result.loc_to_hit->z = level;
745 Result.hitLoc = Result.loc_to_hit;
746 return true;
747 }
748 }
749
750 if (flags & LT_HitObjects) {
751 if (obj_manager->get_obj(x, y, level)) {
752 Result.init(x, y, level, NULL, obj_manager->get_obj(x, y, level, true));
753 return true;
754 }
755 }
756
757 return false;
758 #endif
759 }
760
761 // returns true if a line hits something travelling from (start_x, start_y) to
762 // (end_x, end_y). If a hit occurs Result is filled in with the relevant info.
lineTest(int start_x,int start_y,int end_x,int end_y,uint8 level,uint8 flags,LineTestResult & Result,uint32 skip,Obj * excluded_obj)763 bool Map::lineTest(int start_x, int start_y, int end_x, int end_y, uint8 level,
764 uint8 flags, LineTestResult &Result, uint32 skip, Obj *excluded_obj) {
765 // standard Bresenham's algorithm.
766 int deltax = abs(end_x - start_x);
767 int deltay = abs(end_y - start_y);
768
769 int x = start_x;
770 int y = start_y;
771 int d;
772 int xinc1, xinc2;
773 int yinc1, yinc2;
774 int dinc1, dinc2;
775 uint32 count;
776
777
778 if (deltax >= deltay) {
779 d = (deltay << 1) - deltax;
780
781 count = deltax + 1;
782 dinc1 = deltay << 1;
783 dinc2 = (deltay - deltax) << 1;
784 xinc1 = 1;
785 xinc2 = 1;
786 yinc1 = 0;
787 yinc2 = 1;
788 } else {
789 d = (deltax << 1) - deltay;
790
791 count = deltay + 1;
792 dinc1 = deltax << 1;
793 dinc2 = (deltax - deltay) << 1;
794 xinc1 = 0;
795 xinc2 = 1;
796 yinc1 = 1;
797 yinc2 = 1;
798 }
799
800 if (start_x > end_x) {
801 xinc1 = -xinc1;
802 xinc2 = -xinc2;
803 }
804 if (start_y > end_y) {
805 yinc1 = -yinc1;
806 yinc2 = -yinc2;
807 }
808
809 for (uint32 i = 0; i < count; i++) {
810 // test the current location
811 if ((i >= skip) && (testIntersection(x, y, level, flags, Result, excluded_obj) == true))
812 return true;
813
814 if (d < 0) {
815 d += dinc1;
816 x += xinc1;
817 y += yinc1;
818 } else {
819 d += dinc2;
820 x += xinc2;
821 y += yinc2;
822 }
823 }
824
825 return false;
826 }
827
828 } // End of namespace Nuvie
829 } // End of namespace Ultima
830