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