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/nuvie/core/nuvie_defs.h"
24 #include "ultima/nuvie/conf/configuration.h"
25 #include "ultima/nuvie/actors/actor_manager.h"
26 #include "ultima/nuvie/core/egg_manager.h"
27 #include "ultima/nuvie/core/tile_manager.h"
28 #include "ultima/nuvie/core/obj_manager.h"
29 #include "ultima/nuvie/usecode/usecode.h"
30 #include "ultima/nuvie/misc/u6_misc.h"
31 #include "ultima/nuvie/core/u6_objects.h"
32 #include "ultima/nuvie/misc/u6_llist.h"
33 #include "ultima/nuvie/files/nuvie_io_file.h"
34 #include "ultima/nuvie/core/game.h"
35 #include "ultima/nuvie/gui/widgets/map_window.h"
36 #include "ultima/nuvie/script/script.h"
37 #include "ultima/nuvie/gui/widgets/msg_scroll.h"
38 
39 namespace Ultima {
40 namespace Nuvie {
41 
42 static const int obj_egg_table[5] = {0,   // NUVIE_GAME_NONE
43 									 335, // NUVIE_GAME_U6
44 									 466,   // NUVIE_GAME_MD
45 									 0,
46 									 230
47 									};  // NUVIE_GAME_SE
48 
get_iAVLKey(const void * item)49 static iAVLKey get_iAVLKey(const void *item) {
50 	return ((const ObjTreeNode *)item)->key;
51 }
52 
ObjManager(Configuration * cfg,TileManager * tm,EggManager * em)53 ObjManager::ObjManager(Configuration *cfg, TileManager *tm, EggManager *em) {
54 	uint8 i;
55 	Std::string show_eggs_key;
56 
57 	config = cfg;
58 	tile_manager = tm;
59 	egg_manager = em;
60 	usecode = NULL;
61 	obj_save_count = 0;
62 
63 	load_basetile();
64 	load_weight_table();
65 
66 	memset(actor_inventories, 0, sizeof(actor_inventories));
67 
68 	for (i = 0; i < 64; i++) {
69 		surface[i] = iAVLAllocTree(get_iAVLKey);
70 	}
71 
72 	for (i = 0; i < 5; i++) {
73 		dungeon[i] = iAVLAllocTree(get_iAVLKey);
74 	}
75 
76 	last_obj_blk_x = 0;
77 	last_obj_blk_y = 0;
78 	last_obj_blk_z = OBJ_TEMP_INIT;
79 
80 	config->value("config/GameType", game_type);
81 
82 //save the egg tile_num incase we want to switch egg display on again.
83 	egg_tile_num = get_obj_tile_num(obj_egg_table[game_type]);
84 
85 	show_eggs_key = config_get_game_key(config);
86 	show_eggs_key.append("/show_eggs");
87 
88 	config->value(show_eggs_key, show_eggs);
89 //if(!show_eggs)
90 //  show_egg_objs(false);
91 
92 	Std::string custom_tile_str;
93 	config->value(config_get_game_key(config) + "/custom_actor_tiles", custom_tile_str, "default");
94 	if (custom_tile_str == "default") {
95 		if (Game::get_game()->is_new_style())
96 			custom_actor_tiles = true;
97 		else
98 			custom_actor_tiles = false;
99 	} else if (custom_tile_str == "yes")
100 		custom_actor_tiles = true;
101 	else
102 		custom_actor_tiles = false;
103 }
104 
~ObjManager()105 ObjManager::~ObjManager() {
106 	clean();
107 
108 	unsigned int i;
109 	for (i = 0; i < 64; i++)
110 		iAVLFreeTree(surface[i], clean_obj_tree_node);
111 
112 	for (i = 0; i < 5; i++)
113 		iAVLFreeTree(dungeon[i], clean_obj_tree_node);
114 
115 	for (i = 0; i < 256; i++) {
116 		if (actor_inventories[i]) {
117 			delete actor_inventories[i];
118 		}
119 	}
120 }
121 
load_basetile()122 bool ObjManager::load_basetile() {
123 	Std::string filename;
124 	NuvieIOFileRead basetile;
125 	uint16 i;
126 
127 	config_get_path(config, "basetile", filename);
128 
129 	if (basetile.open(filename) == false)
130 		return false;
131 
132 	for (i = 0; i < 1024; i++) {
133 		obj_to_tile[i] = basetile.read2();
134 		obj_stackable[i] = (uint8)tile_manager->tile_is_stackable(obj_to_tile[i]);
135 	} // FIXME: tile_manager's tile_is_stackable is incorrect for (at least) Zu Ylem, silver snake venom.
136 
137 	return true;
138 }
139 
load_weight_table()140 bool ObjManager::load_weight_table() {
141 	Std::string filename;
142 	NuvieIOFileRead tileflag;
143 
144 	config_get_path(config, "tileflag", filename);
145 
146 	if (tileflag.open(filename) == false)
147 		return false;
148 
149 	tileflag.seek(0x1000);
150 
151 	tileflag.readToBuf(obj_weight, 1024);
152 
153 	return true;
154 }
155 
156 
load_super_chunk(NuvieIO * chunk_buf,uint8 level,uint8 chunk_offset)157 bool ObjManager::load_super_chunk(NuvieIO *chunk_buf, uint8 level, uint8 chunk_offset) {
158 	NuvieIOFileRead file;
159 	U6LList *list;
160 	uint16 num_objs;
161 	Obj *obj;
162 	uint16 i;
163 	U6LList *inventory_list;
164 	iAVLTree *obj_tree;
165 
166 	if (level == 0)
167 		obj_tree = surface[chunk_offset];
168 	else
169 		obj_tree = dungeon[level - 1];
170 
171 	list = new U6LList();
172 
173 	num_objs = chunk_buf->read2();
174 //DEBUG(0,LEVEL_DEBUGGING,"chunk %02d number of objects: %d\n", chunk_offset, num_objs);
175 
176 	for (i = 0; i < num_objs; i++) {
177 		obj = loadObj(chunk_buf);
178 
179 		list->add(obj);
180 
181 		if (obj->obj_n == obj_egg_table[game_type]) {
182 			egg_manager->add_egg(obj);
183 			// set egg visibility
184 			obj->set_invisible(Game::get_game()->are_cheats_enabled() ? !show_eggs : true);
185 		}
186 
187 		if (usecode->is_container(obj)) { //object type is container, but may be empty
188 			obj->make_container();
189 		}
190 
191 		if (obj->get_engine_loc() == OBJ_LOC_INV || obj->get_engine_loc() == OBJ_LOC_READIED) { //triggered when object in actor's inventory OR equipped
192 			//FIXME need to add to inventory properly!! eg set engine loc.
193 			inventory_list = get_actor_inventory(obj->x);
194 			inventory_list->add(obj);
195 		} else {
196 			if (obj->is_in_container()) { //object in container
197 				addObjToContainer(list, obj);
198 			} else {
199 				add_obj(obj); // show remaining objects
200 				/*         if(show_eggs || obj->obj_n != obj_egg_table[game_type]) // show remaining objects, hiding eggs if neccecary.
201 				            {
202 				             add_obj(obj);
203 				            // print_obj(obj,false);
204 				            }*/
205 			}
206 
207 		}
208 		//print_obj(obj,false);
209 	}
210 
211 	// Unused variable
212 	(void)obj_tree;
213 
214 	delete list;
215 
216 	return true;
217 }
218 
save_super_chunk(NuvieIO * save_buf,uint8 level,uint8 chunk_offset)219 bool ObjManager::save_super_chunk(NuvieIO *save_buf, uint8 level, uint8 chunk_offset) {
220 	iAVLTree *obj_tree;
221 	ObjTreeNode *item;
222 	U6Link *link;
223 	iAVLCursor node;
224 	uint32 start_pos;
225 	uint32 finish_pos;
226 	uint16 egg_type = obj_egg_table[game_type];
227 
228 	if (level == 0)
229 		obj_tree = surface[chunk_offset];
230 	else
231 		obj_tree = dungeon[level - 1];
232 
233 	item = (ObjTreeNode *)iAVLFirst(&node, obj_tree);
234 
235 	start_pos = save_buf->position();
236 
237 //skip the 2 bytes for number of objects.
238 	save_buf->write2(0); // we'll fill this in later on.
239 
240 	obj_save_count = 0;
241 
242 	for (; item;) {
243 		for (link = item->obj_list->end(); link != NULL; link = link->prev) {
244 			if (((Obj *)link->data)->obj_n != egg_type) // we don't save eggs here. They are saved in save_eggs()
245 				save_obj(save_buf, (Obj *)link->data, obj_save_count);
246 		}
247 
248 		item = (ObjTreeNode *)iAVLNext(&node);
249 	}
250 
251 	finish_pos = save_buf->position();
252 	save_buf->seek(start_pos);
253 
254 	save_buf->write2(obj_save_count);
255 	save_buf->seek(finish_pos);
256 
257 	return true;
258 }
259 
save_eggs(NuvieIO * save_buf)260 bool ObjManager::save_eggs(NuvieIO *save_buf) {
261 	uint32 start_pos;
262 	uint32 finish_pos;
263 	Std::list<Egg *> *egg_list;
264 	Std::list<Egg *>::iterator egg;
265 
266 	start_pos = save_buf->position();
267 
268 //skip number of objects we will fill that in at the end.
269 	save_buf->write2(0);
270 
271 	egg_list = egg_manager->get_egg_list();
272 
273 	obj_save_count = 0;
274 
275 	for (egg = egg_list->begin(); egg != egg_list->end(); egg++)
276 		save_obj(save_buf, (*egg)->obj, obj_save_count);
277 
278 	finish_pos = save_buf->position();
279 	save_buf->seek(start_pos);
280 
281 	save_buf->write2(obj_save_count);
282 	save_buf->seek(finish_pos);
283 
284 	DEBUG(0, LEVEL_DEBUGGING, "Eggs: %d\n", obj_save_count);
285 
286 	return true;
287 }
288 
save_inventories(NuvieIO * save_buf)289 bool ObjManager::save_inventories(NuvieIO *save_buf) {
290 	uint32 start_pos;
291 	uint32 finish_pos;
292 	U6Link *link;
293 	uint16 i;
294 
295 	start_pos = save_buf->position();
296 
297 	save_buf->write2(0);
298 
299 	obj_save_count = 0;
300 
301 	for (i = 0; i < 256; i++) {
302 		if (actor_inventories[i] != NULL) {
303 			for (link = actor_inventories[i]->start(); link != NULL; link = link->next) {
304 				save_obj(save_buf, (Obj *)link->data, obj_save_count);
305 			}
306 		}
307 	}
308 
309 	DEBUG(0, LEVEL_DEBUGGING, "Actor Inventories: %d\n", obj_save_count);
310 
311 	finish_pos = save_buf->position();
312 	save_buf->seek(start_pos);
313 
314 	save_buf->write2(obj_save_count);
315 	save_buf->seek(finish_pos);
316 
317 	return true;
318 }
319 
save_obj(NuvieIO * save_buf,Obj * obj,uint16 parent_objblk_n)320 bool ObjManager::save_obj(NuvieIO *save_buf, Obj *obj, uint16 parent_objblk_n) {
321 	uint8 b;
322 	U6Link *link;
323 	uint16 objblk_n;
324 
325 	if (obj->is_in_container()) { //obj is in a container
326 		//obj->in_container(); // in container
327 		obj->x = parent_objblk_n & 0x3ff; //save 10bits in x
328 		obj->y &= 0xffc0; //clear lower 6 bits
329 		obj->y |= (parent_objblk_n >> 10); //save top 6bits
330 	} else {
331 		if (!obj->is_readied()) {
332 			obj->status &= (0xff ^ OBJ_STATUS_IN_CONTAINER);
333 		}
334 	}
335 
336 	if (obj->is_in_inventory(OBJ_DONT_CHECK_PARENT))
337 		obj->x = obj->get_actor_holding_obj()->get_actor_num();
338 
339 //set original status location bits.
340 	obj->status &= OBJ_STATUS_MASK_SET;
341 	switch (obj->get_engine_loc()) {
342 	case OBJ_LOC_MAP :
343 		obj->status |= OBJ_STATUS_ON_MAP;
344 		break;
345 	case OBJ_LOC_CONT :
346 		obj->status |= OBJ_STATUS_IN_CONTAINER;
347 		break;
348 	case OBJ_LOC_INV :
349 		obj->status |= OBJ_STATUS_IN_INVENTORY;
350 		break;
351 	case OBJ_LOC_READIED :
352 		obj->status |= OBJ_STATUS_READIED;
353 		break;
354 	}
355 
356 	save_buf->write1(obj->status);
357 	save_buf->write1(obj->x & 0xff);
358 	b = obj->x >> 8;
359 	b += obj->y << 2;
360 	save_buf->write1(b);
361 
362 	b = obj->y >> 6;
363 	b += obj->z << 4;
364 
365 	save_buf->write1(b);
366 
367 	save_buf->write1(obj->obj_n & 0xff);
368 
369 	b = obj->obj_n >> 8;
370 	b += obj->frame_n << 2;
371 
372 	save_buf->write1(b);
373 
374 	save_buf->write1((uint8)(obj->qty & 0xff)); //only save the lower byte to disk.
375 
376 	if (is_stackable(obj))
377 		save_buf->write1(obj->qty >> 8);
378 	else
379 		save_buf->write1(obj->quality);
380 
381 	objblk_n = obj_save_count;
382 
383 	obj_save_count += 1;
384 
385 	if (obj->container) {
386 		for (link = obj->container->end(); link != NULL; link = link->prev)
387 			save_obj(save_buf, (Obj *)link->data, objblk_n);
388 	}
389 
390 	return true;
391 }
392 
clean()393 void ObjManager::clean() {
394 	uint8 i;
395 
396 	egg_manager->clean(Game::get_game()->are_cheats_enabled() ? show_eggs : false); //show_eggs determines wether we delete the actual Objs from egg manager.
397 
398 	for (i = 0; i < 64; i++)
399 		iAVLCleanTree(surface[i], clean_obj_tree_node);
400 
401 	for (i = 0; i < 5; i++)
402 		iAVLCleanTree(dungeon[i], clean_obj_tree_node);
403 
404 	clean_actor_inventories();
405 
406 // remove the temporary object list. The objects were deleted from the surface and dungeon trees.
407 	temp_obj_list.clear();
408 
409 	for (Std::list<Obj *>::iterator it = tile_obj_list.begin(); it != tile_obj_list.end(); ++it) {
410 		delete *it;
411 	}
412 	tile_obj_list.clear();
413 
414 	return;
415 }
416 
clean_actor_inventories()417 void ObjManager::clean_actor_inventories() {
418 	U6Link *link;
419 	uint16 i;
420 
421 	for (i = 0; i < 256; i++) {
422 		if (actor_inventories[i]) {
423 			for (link = actor_inventories[i]->start(); link != NULL;) {
424 				Obj *obj = (Obj *)link->data;
425 				link = link->next;
426 				delete_obj(obj);
427 			}
428 			actor_inventories[i]->removeAll();
429 		}
430 	}
431 
432 	return;
433 }
434 
435 /*
436 U6LList *ObjManager::get_obj_superchunk(uint16 x, uint16 y, uint8 level)
437 {
438  uint16 i;
439 
440  if(level == 0)
441    {
442 	i = y * 8 + x;
443 	return surface[i];
444    }
445 
446  return dungeon[level-1];
447 }
448 */
449 
is_boundary(uint16 x,uint16 y,uint8 level,uint8 boundary_type,Obj * excluded_obj)450 bool ObjManager::is_boundary(uint16 x, uint16 y, uint8 level, uint8 boundary_type, Obj *excluded_obj) {
451 	U6Link *link;
452 	U6LList *obj_list;
453 	Obj *obj;
454 	Tile *tile, *tile1;
455 	uint16 tile_num;
456 	bool check_tile;
457 	uint16 i, j;
458 	uint16 next_x, next_y;
459 
460 	next_x = WRAPPED_COORD(x + 1, level);
461 	next_y = WRAPPED_COORD(y + 1, level);
462 
463 	for (j = y; j <= y + 1; j++) {
464 		for (i = x; i <= x + 1; i++) {
465 			obj_list = get_obj_list(WRAPPED_COORD(i, level), WRAPPED_COORD(j, level), level);
466 
467 			if (obj_list != NULL) {
468 				link = obj_list->end();
469 
470 				for (check_tile = false; link != NULL; link = link->prev) {
471 					obj = (Obj *)link->data;
472 					if (obj == excluded_obj)
473 						continue;
474 					tile_num = get_obj_tile_num(obj->obj_n) + obj->frame_n;
475 					tile = tile_manager->get_original_tile(tile_num);
476 
477 					if (obj->x == x && obj->y == y) {
478 						check_tile = true;
479 					}
480 					if (tile->dbl_width && obj->x == next_x && obj->y == y) {
481 						tile_num--;
482 						check_tile = true;
483 					}
484 					if (tile->dbl_height && obj->x == x && obj->y == next_y) {
485 						tile_num--;
486 						check_tile = true;
487 					}
488 					if (obj->x == next_x && obj->y == next_y && tile->dbl_width && tile->dbl_height) {
489 						tile_num -= 2;
490 						check_tile = true;
491 					}
492 					if (check_tile) {
493 						tile1 = tile_manager->get_tile(tile_num);
494 						if (tile1->flags2 & boundary_type) //either TILEFLAG_BOUNDARY or TILEFLAG_MISSILE_BOUNDARY
495 							return true;
496 
497 						check_tile = false;
498 					}
499 				}
500 			}
501 		}
502 	}
503 
504 	return false;
505 }
506 
507 /* no longer needed.
508 //FIX this needs to be moved magicnumbers :(
509 bool ObjManager::is_door(Obj * obj)
510 {
511  //for U6
512  if((obj->obj_n >= 297 && obj->obj_n <= 300) || obj->obj_n == 334 || obj->obj_n == 213) //OBJ_U6_MOUSEHOLE)
513    return true;
514 
515  return false;
516 }
517 */
518 
is_passable(uint16 x,uint16 y,uint8 level)519 uint8 ObjManager::is_passable(uint16 x, uint16 y, uint8 level) {
520 	U6Link *link;
521 	U6LList *obj_list;
522 	Obj *obj;
523 	Tile *tile, *tile1;
524 	uint16 tile_num;
525 	bool check_tile;
526 	bool object_at_location = false;
527 	uint16 i, j;
528 
529 	uint16 x2 = WRAPPED_COORD((x + 1), level); // wrap on map edge
530 	uint16 y2 = WRAPPED_COORD((y + 1), level);
531 
532 	for (i = x;; i = x2) { // only checks x and x2
533 		for (j = y;; j = y2) { // only checks y and y2
534 			obj_list = get_obj_list(i, j, level);
535 			if (i == x && j == y && obj_list) {
536 				if (obj_list->end() != NULL)
537 					object_at_location = true;
538 			}
539 			if (obj_list != NULL) {
540 				link = obj_list->end();
541 
542 				for (check_tile = false; link != NULL; link = link->prev) {
543 					obj = (Obj *)link->data;
544 					tile_num = get_obj_tile_num(obj->obj_n) + obj->frame_n;
545 					tile = tile_manager->get_original_tile(tile_num);
546 
547 					if (obj->x == x && obj->y == y) {
548 						check_tile = true;
549 					}
550 					if (tile->dbl_width && obj->x == x2 && obj->y == y) {
551 						tile_num--;
552 						check_tile = true;
553 					}
554 					if (tile->dbl_height && obj->x == x && obj->y == y2) {
555 						tile_num--;
556 						check_tile = true;
557 					}
558 					if (obj->x == x2 && obj->y == y2 && tile->dbl_width && tile->dbl_height) {
559 						tile_num -= 3;
560 						check_tile = true;
561 					}
562 					if (check_tile) {
563 						tile1 = tile_manager->get_original_tile(tile_num);
564 						if (tile1->passable == false)
565 							return OBJ_NOT_PASSABLE;
566 						check_tile = false;
567 					}
568 				}
569 			}
570 			if (j == y) j = y2;
571 			else break;
572 		}
573 		if (i == x) i = x2;
574 		else break;
575 	}
576 
577 	if (object_at_location)
578 		return OBJ_PASSABLE;
579 
580 	return OBJ_NO_OBJ;
581 }
582 
is_forced_passable(uint16 x,uint16 y,uint8 level)583 bool ObjManager::is_forced_passable(uint16 x, uint16 y, uint8 level) {
584 	U6LList *obj_list;
585 	U6Link *link;
586 	Obj *obj;
587 	Tile *tile;
588 
589 	obj_list = get_obj_list(x, y, level);
590 
591 	if (obj_list) {
592 		for (link = obj_list->start(); link != NULL; link = link->next) {
593 			obj = (Obj *)link->data;
594 			tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
595 			if (tile->flags3 & TILEFLAG_FORCED_PASSABLE)
596 				return true;
597 		}
598 	}
599 
600 	return false;
601 }
602 
is_door(uint16 x,uint16 y,uint8 level)603 bool ObjManager::is_door(uint16 x, uint16 y, uint8 level) {
604 	U6LList *obj_list = get_obj_list(x, y, level);
605 	U6Link *link;
606 	Obj *obj;
607 
608 	if (obj_list) {
609 		for (link = obj_list->start(); link != NULL; link = link->next) {
610 			obj = (Obj *)link->data;
611 			if (usecode->is_door(obj))
612 				return true;
613 		}
614 	}
615 	return false;
616 }
617 
is_damaging(uint16 x,uint16 y,uint8 level)618 bool ObjManager::is_damaging(uint16 x, uint16 y, uint8 level) {
619 	U6LList *obj_list;
620 	U6Link *link;
621 	Obj *obj;
622 	Tile *tile;
623 
624 	obj_list = get_obj_list(x, y, level);
625 
626 	if (obj_list) {
627 		for (link = obj_list->start(); link != NULL; link = link->next) {
628 			obj = (Obj *)link->data;
629 			tile = tile_manager->get_original_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n); //get_tile(get_obj_tile_num(obj->obj_n)+obj->frame_n);
630 			if (tile->flags1 & TILEFLAG_DAMAGING)
631 				return true;
632 		}
633 	}
634 
635 	return false;
636 }
637 
is_stackable(Obj * obj)638 bool ObjManager::is_stackable(Obj *obj) {
639 // Tile *tile;
640 
641 	if (obj == NULL)
642 		return false;
643 	if (obj->is_readied()) // readied objects cannot be stacked --SB-X
644 		return false;
645 	/*
646 	 tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n)+obj->frame_n);
647 
648 	 if(tile_manager->tile_is_stackable(tile->tile_num))
649 	   return true;
650 
651 	 return false;
652 	*/
653 	if (game_type == NUVIE_GAME_U6) {
654 		switch (obj->obj_n) {
655 		case OBJ_U6_TORCH: // 0x5A, // torch
656 			if (obj->frame_n == 1) {
657 				return false;
658 			} else {
659 				return true;
660 			}
661 		case OBJ_U6_LOCK_PICK: // 0x3F, // lock pick
662 		case OBJ_U6_GEM: // 0x4D, // gem
663 		case OBJ_U6_ARROW: // 0x37, // arrow
664 		case OBJ_U6_BOLT: // 0x38, // bolt
665 		case OBJ_U6_BLACK_PEARL: // 0x41, // black pearl
666 		case OBJ_U6_BLOOD_MOSS: // 0x42, // bit of blood moss
667 		case OBJ_U6_GARLIC: // 0x43, // bulb of garlic
668 		case OBJ_U6_GINSENG: // 0x44, // ginseng root
669 		case OBJ_U6_MANDRAKE_ROOT: // 0x45, // mandrake root
670 		case OBJ_U6_NIGHTSHADE: // 0x46, // nightshade mushroom
671 		case OBJ_U6_SPIDER_SILK: // 0x47, // strand of spidersilk
672 		case OBJ_U6_SULFUROUS_ASH: // 0x48, // bit of sulfurous ash
673 		case OBJ_U6_EFFECT: // 0x151, // effect
674 		case OBJ_U6_BREAD: // 0x80, // loaf of bread
675 		case OBJ_U6_MEAT_PORTION: // 0x81, // portion of meat
676 		case OBJ_U6_FLASK_OF_OIL: // 0x53, // flask of oil
677 		case OBJ_U6_EGG: // 0x14F, // egg
678 		case OBJ_U6_GOLD_NUGGET: // 0x59, // gold nugget
679 		case OBJ_U6_ZU_YLEM: // 0x5B, // Zu Ylem
680 		case OBJ_U6_SNAKE_VENOM: // 0x5C, // silver snake venom
681 		case OBJ_U6_GOLD: // 0x58  // Gold coin
682 			return true;
683 		default:
684 			return false;
685 		}
686 	}
687 
688 	if (game_type == NUVIE_GAME_SE) {
689 		switch (obj->obj_n) {
690 		case OBJ_SE_MAGNESIUM_RIBBON:
691 		case OBJ_SE_SPEAR:
692 		case OBJ_SE_THROWING_AXE:
693 		case OBJ_SE_POISONED_DART:
694 		case OBJ_SE_RIFLE_BULLET:
695 		case OBJ_SE_KNIFE:
696 		case OBJ_SE_ARROW:
697 		case OBJ_SE_TURTLE_BAIT:
698 		case OBJ_SE_FEATHER:
699 		case OBJ_SE_CHOCOLATL:
700 		case OBJ_SE_PINDE:
701 		case OBJ_SE_YOPO:
702 		case OBJ_SE_GOLD:
703 		case OBJ_SE_GOLD_NUGGET:
704 		case OBJ_SE_DIAMOND:
705 		case OBJ_SE_EMERALD:
706 		case OBJ_SE_RUBY:
707 		case OBJ_SE_CORN_MEAL:
708 		case OBJ_SE_TORTILLA:
709 		case OBJ_SE_MEAT_103:
710 		case OBJ_SE_BERRY:
711 		case OBJ_SE_CAKE:
712 		case OBJ_SE_CORN:
713 		case OBJ_SE_BEAN:
714 		case OBJ_SE_MEAT_110:
715 		case OBJ_SE_ORCHID:
716 		case OBJ_SE_PEPPER:
717 		case OBJ_SE_SULFUR:
718 		case OBJ_SE_CHARCOAL:
719 		case OBJ_SE_POTASSIUM_NITRATE:
720 		case OBJ_SE_SOFT_CLAY_POT:
721 		case OBJ_SE_FIRED_CLAY_POT:
722 		case OBJ_SE_CLOTH_STRIP:
723 		case OBJ_SE_GRENADE:
724 		case OBJ_SE_TAR:
725 		case OBJ_SE_WATER:
726 		case OBJ_SE_CLOTH:
727 		case OBJ_SE_TARRED_CLOTH_STRIP:
728 		case OBJ_SE_CLAY:
729 		case OBJ_SE_GUNPOWDER:
730 		case OBJ_SE_BRANCH:
731 		case OBJ_SE_TORCH:
732 		case OBJ_SE_FLAX:
733 		case OBJ_SE_RIB_BONE:
734 		case OBJ_SE_CHOP:
735 		case OBJ_SE_DEVICE:
736 			return true;
737 		default:
738 			return false;
739 		}
740 	}
741 
742 	if (game_type == NUVIE_GAME_MD) {
743 		switch (obj->obj_n) {
744 		case OBJ_MD_PISTOL_ROUND:
745 		case OBJ_MD_SHOTGUN_SHELL:
746 		case OBJ_MD_RIFLE_ROUND:
747 		case OBJ_MD_ELEPHANT_GUN_ROUND:
748 		case OBJ_MD_SLING_STONE:
749 		case OBJ_MD_ARROW:
750 		case OBJ_MD_CAN_OF_LAMP_OIL:
751 		case OBJ_MD_MATCH:
752 		case OBJ_MD_TORCH:
753 		case OBJ_MD_BLOB_OF_OXIUM:
754 		case OBJ_MD_BERRY:
755 		case OBJ_MD_BERRY1:
756 		case OBJ_MD_BERRY2:
757 		case OBJ_MD_BERRY4:
758 		case OBJ_MD_CHIP_OF_RADIUM:
759 		case OBJ_MD_DOLLAR:
760 		case OBJ_MD_RUBLE:
761 		case OBJ_MD_WORMSBANE_SEED:
762 		case OBJ_MD_PAGE:
763 		case OBJ_MD_BERRY3:
764 		case OBJ_MD_OXYGENATED_AIR_BOTTLE:
765 			return true;
766 		default:
767 			return false;
768 		}
769 	}
770 
771 	return (bool)obj_stackable[obj->obj_n];
772 }
773 
is_breakable(Obj * obj)774 bool ObjManager::is_breakable(Obj *obj) {
775 	if (game_type == NUVIE_GAME_U6) {
776 		switch (obj->obj_n) {
777 		case OBJ_U6_FLASK_OF_OIL:
778 		case OBJ_U6_SNAKE_VENOM:
779 		case OBJ_U6_CRYSTAL_BALL:
780 		case OBJ_U6_MIRROR:
781 		case OBJ_U6_WINE:
782 		case OBJ_U6_MEAD:
783 		case OBJ_U6_ALE:
784 		case OBJ_U6_WINE_GLASS:
785 		case OBJ_U6_PLATE:
786 		case OBJ_U6_MUG:
787 		case OBJ_U6_HONEY_JAR:
788 		case OBJ_U6_JAR_OF_HONEY:
789 		case OBJ_U6_POTION:
790 		case OBJ_U6_WATER_VASE:
791 		case OBJ_U6_DRAGON_EGG:
792 			return true;
793 		default:
794 			break;
795 		}
796 	} else if (game_type == NUVIE_GAME_SE) {
797 		switch (obj->obj_n) {
798 		case OBJ_SE_MORTAR:
799 		case OBJ_SE_GRINDING_STONE:
800 		case OBJ_SE_JUG_OF_PLACHTA:
801 		case OBJ_SE_BOTTLE_OF_LIQUOR:
802 		case OBJ_SE_JAR:
803 		case OBJ_SE_FIRED_CLAY_POT:
804 		case OBJ_SE_GRENADE:
805 		case OBJ_SE_JUG:
806 		case OBJ_SE_POT:
807 			return true;
808 		default:
809 			break;
810 		}
811 	}
812 
813 	return false;
814 }
815 
can_store_obj(Obj * target,Obj * src)816 bool ObjManager::can_store_obj(Obj *target, Obj *src) {
817 	if (target == src || !can_get_obj(src) || target == NULL)
818 		return false;
819 
820 	if (game_type == NUVIE_GAME_U6) {
821 		if (src->obj_n == OBJ_U6_TRAP)
822 			return false;
823 
824 		if (target->obj_n == OBJ_U6_BAG
825 		        || target->obj_n == OBJ_U6_BACKPACK
826 		        || target->obj_n == OBJ_U6_BASKET
827 		        || (target->obj_n == OBJ_U6_CRATE && target->frame_n == 0)
828 		        || (target->obj_n == OBJ_U6_BARREL && target->frame_n == 0)
829 		        || (target->obj_n == OBJ_U6_CHEST && target->frame_n == 0)
830 		        || (target->obj_n == OBJ_U6_SPELLBOOK && src->obj_n == OBJ_U6_SPELL
831 		            && !target->find_in_container(OBJ_U6_SPELL, src->quality)
832 		            && !target->find_in_container(OBJ_U6_SPELL, 255)) // this quality contains all spells
833 		        || (target->obj_n == OBJ_U6_VORTEX_CUBE && src->obj_n == OBJ_U6_MOONSTONE))
834 			return true;
835 
836 		if ((target->is_in_inventory() || Game::get_game()->doubleclick_opens_containers())
837 		        && ((target->obj_n == OBJ_U6_CHEST && target->frame_n == 1)
838 		            || target->obj_n == OBJ_U6_DEAD_BODY
839 		            || target->obj_n == OBJ_U6_MOUSE
840 		            || target->obj_n == OBJ_U6_REMAINS
841 		            || target->obj_n == OBJ_U6_DRAKE
842 		            || target->obj_n == OBJ_U6_MONGBAT))
843 			return true;
844 
845 		if (Game::get_game()->doubleclick_opens_containers()
846 		        && (target->obj_n == OBJ_U6_DESK
847 		            || target->obj_n == OBJ_U6_DRAWER
848 		            || target->obj_n == OBJ_U6_GRAVE
849 		            || target->obj_n == OBJ_U6_REAPER
850 		            || target->obj_n == OBJ_U6_DEAD_GARGOYLE
851 		            || target->obj_n == OBJ_U6_DEAD_CYCLOPS))
852 			return true;
853 	} else if (game_type == NUVIE_GAME_SE) {
854 		if (src->has_container() || usecode->is_container(src))
855 			return false;
856 		if (target->obj_n == OBJ_SE_JUG || target->obj_n == OBJ_SE_POUCH
857 		        || target->obj_n == OBJ_SE_BASKET || target->obj_n == OBJ_SE_POT)
858 			return true;
859 		if (target->obj_n == OBJ_SE_MORTAR || target->obj_n == OBJ_SE_GRINDING_STONE
860 		        || target->obj_n == OBJ_SE_JAR) {
861 			switch (src->obj_n) {
862 			case OBJ_SE_MAGNESIUM_RIBBON:
863 			case OBJ_SE_CHOCOLATL:
864 			case OBJ_SE_PINDE:
865 			case OBJ_SE_YOPO:
866 			case OBJ_SE_CORN_MEAL:
867 			case OBJ_SE_CORN:
868 			case OBJ_SE_SULFUR:
869 			case OBJ_SE_CHARCOAL:
870 			case OBJ_SE_POTASSIUM_NITRATE:
871 			case OBJ_SE_GUNPOWDER:
872 				if (target->obj_n == OBJ_SE_JAR) {
873 					if (target->container_count_objects() == 0 || // only allow one object
874 					        target->find_in_container(src->obj_n, src->quality))
875 						return true;
876 					else
877 						return false;
878 				}
879 				return true;
880 			default:
881 				return false;
882 			}
883 		}
884 	} else { // MD
885 		if (src->has_container() || usecode->is_container(src))
886 			return false;
887 		switch (target->obj_n) {
888 		case OBJ_MD_BRASS_CHEST:
889 		case OBJ_MD_OBSIDIAN_BOX:
890 		case OBJ_MD_WOODEN_CRATE:
891 		case OBJ_MD_STEAMER_TRUNK:
892 		case OBJ_MD_BARREL:
893 		case OBJ_MD_CRATE:
894 		case OBJ_MD_BRASS_TRUNK:
895 			if (target->frame_n == 0) {
896 				return true;
897 			} else
898 				return false;
899 		case OBJ_MD_BACKPACK:
900 		case OBJ_MD_LARGE_SACK:
901 		case OBJ_MD_SMALL_POUCH:
902 		case OBJ_MD_CARPET_BAG:
903 		case OBJ_MD_BAG:
904 		case OBJ_MD_LEAD_BOX:
905 			return true;
906 		default:
907 			return false;
908 		}
909 	}
910 	return false;
911 }
912 
can_get_obj(Obj * obj)913 bool ObjManager::can_get_obj(Obj *obj) {
914 	// objects with 0 weight aren't gettable.
915 	//255 is the max weight and means an object is movable but not getable.
916 	//we can't get object that contains toptiles either. This makes dragon bits ungettable etc.
917 	// excluding container items here, we just want the object itself to
918 	// check if it weighs 0 or 255. no need to scale as we don't compare
919 	// with other weights
920 	if (obj == NULL)
921 		return false;
922 	if (Game::get_game()->get_script()->call_can_get_obj_override(obj))
923 		return true;
924 
925 	float weight = get_obj_weight(obj, OBJ_WEIGHT_EXCLUDE_CONTAINER_ITEMS, OBJ_WEIGHT_DONT_SCALE, OBJ_WEIGHT_EXCLUDE_QTY);
926 	if ((weight != 0 && weight != 255 && has_toptile(obj) == false
927 	        && (!obj->is_on_map() || !Game::get_game()->get_map_window()->tile_is_black(obj->x, obj->y, obj)))
928 	        || Game::get_game()->using_hackmove())
929 		return true;
930 
931 	return false;
932 }
933 
has_reduced_weight(uint16 obj_n)934 bool ObjManager::has_reduced_weight(uint16 obj_n) {
935 	// FIXME: HERE BE HARDCODED VALUES! FIXME: not sure if this list is complete!
936 	if (game_type == NUVIE_GAME_U6) { // luteijn: I only know about U6...
937 		if ((obj_n == OBJ_U6_GOLD)
938 		        || (obj_n == OBJ_U6_BLACK_PEARL) // not using range because don't want to depend on underlying magic numbers relations
939 		        || (obj_n == OBJ_U6_BLOOD_MOSS)
940 		        || (obj_n == OBJ_U6_GARLIC)
941 		        || (obj_n == OBJ_U6_GINSENG)
942 		        || (obj_n == OBJ_U6_MANDRAKE_ROOT)
943 		        || (obj_n == OBJ_U6_NIGHTSHADE)
944 		        || (obj_n == OBJ_U6_SPIDER_SILK)
945 		        || (obj_n == OBJ_U6_SULFUROUS_ASH)
946 		   ) {
947 			return true;
948 		}
949 	} else if (game_type == NUVIE_GAME_SE) {
950 		switch (obj_n) {
951 		case OBJ_SE_RIFLE_BULLET:
952 		case OBJ_SE_FEATHER:
953 		case OBJ_SE_CHOCOLATL:
954 		case OBJ_SE_PINDE:
955 		case OBJ_SE_YOPO:
956 		case OBJ_SE_GOLD:
957 		case OBJ_SE_DIAMOND:
958 		case OBJ_SE_EMERALD:
959 		case OBJ_SE_RUBY:
960 		case OBJ_SE_PEPPER:
961 		case OBJ_SE_SULFUR:
962 		case OBJ_SE_CHARCOAL:
963 		case OBJ_SE_POTASSIUM_NITRATE:
964 		case OBJ_SE_CLOTH_STRIP:
965 			return true;
966 		default:
967 			return false;
968 		}
969 	} else if (game_type == NUVIE_GAME_MD) {
970 		switch (obj_n) {
971 		case OBJ_MD_PISTOL_ROUND:
972 		case OBJ_MD_SHOTGUN_SHELL:
973 		case OBJ_MD_RIFLE_ROUND:
974 		case OBJ_MD_ELEPHANT_GUN_ROUND:
975 		case OBJ_MD_SLING_STONE:
976 		case OBJ_MD_ARROW:
977 		case OBJ_MD_POCKETWATCH:
978 		case OBJ_MD_SPECTACLES:
979 		case OBJ_MD_MASONIC_SYMBOL:
980 		case OBJ_MD_MATCH:
981 		case OBJ_MD_BLOB_OF_OXIUM:
982 		case OBJ_MD_BERRY:
983 		case OBJ_MD_BERRY1:
984 		case OBJ_MD_BERRY2:
985 		case OBJ_MD_BERRY4:
986 		case OBJ_MD_DREAMSTUFF:
987 		case OBJ_MD_DOLLAR:
988 		case OBJ_MD_RUBLE:
989 			return true;
990 		default:
991 			return false;
992 		}
993 	}
994 
995 	return false;
996 }
997 
has_toptile(Obj * obj)998 bool ObjManager::has_toptile(Obj *obj) {
999 	Tile *tile;
1000 	uint8 i = 1;
1001 
1002 	tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1003 
1004 	if (tile->dbl_width)
1005 		i++;
1006 
1007 	if (tile->dbl_height)
1008 		i++;
1009 
1010 	if (tile->dbl_width && tile->dbl_height)
1011 		i++;
1012 
1013 	for (; i > 0; i--) {
1014 		if (tile->toptile)
1015 			return true;
1016 
1017 		if (i != 1)
1018 			tile = tile_manager->get_tile(tile->tile_num - 1);
1019 	}
1020 
1021 	return false;
1022 }
1023 
1024 //gets the linked list of objects at a particular location.
1025 
get_obj_list(uint16 x,uint16 y,uint8 level)1026 U6LList *ObjManager::get_obj_list(uint16 x, uint16 y, uint8 level) {
1027 	iAVLTree *obj_tree;
1028 	iAVLKey key;
1029 	ObjTreeNode *item;
1030 
1031 	WRAP_COORD(x, level); // wrap on map edge
1032 	WRAP_COORD(y, level);
1033 
1034 	obj_tree = get_obj_tree(x, y, level);
1035 	key = get_obj_tree_key(x, y, level);
1036 
1037 	item = (ObjTreeNode *)iAVLSearch(obj_tree, key);
1038 	if (item)
1039 		return item->obj_list;
1040 
1041 	return NULL;
1042 }
1043 
get_obj_tile(uint16 obj_n,uint8 frame_n)1044 Tile *ObjManager::get_obj_tile(uint16 obj_n, uint8 frame_n) {
1045 	return  tile_manager->get_tile(get_obj_tile_num(obj_n) + frame_n);
1046 }
1047 
get_obj_tile(uint16 x,uint16 y,uint8 level,bool top_obj)1048 Tile *ObjManager::get_obj_tile(uint16 x, uint16 y, uint8 level, bool top_obj) {
1049 	Obj *obj;
1050 	Tile *tile;
1051 	uint16 tile_num;
1052 
1053 	obj = get_obj(x, y, level, top_obj);
1054 	if (obj == NULL)
1055 		return NULL;
1056 
1057 	tile_num = get_obj_tile_num(obj->obj_n) + obj->frame_n;
1058 	tile = tile_manager->get_tile(tile_num);
1059 
1060 	if (tile->dbl_width && obj->x == x + 1 && obj->y == y)
1061 		tile_num--;
1062 	if (tile->dbl_height && obj->x == x && obj->y == y + 1)
1063 		tile_num--;
1064 	if (obj->x == x + 1 && obj->y == y + 1 && tile->dbl_width && tile->dbl_height)
1065 		tile_num -= 2;
1066 
1067 	return tile_manager->get_original_tile(tile_num);
1068 }
1069 
get_obj_dmg_tile(uint16 x,uint16 y,uint8 level)1070 Tile *ObjManager::get_obj_dmg_tile(uint16 x, uint16 y, uint8 level) {
1071 	Tile *tile;
1072 	U6LList *obj_list;
1073 	U6Link *link;
1074 	Obj *obj = NULL;
1075 
1076 	obj_list = get_obj_list(x, y, level);
1077 
1078 	if (obj_list != NULL) {
1079 		for (link = obj_list->end(); link != NULL; link = link->prev) {
1080 			obj = (Obj *)link->data;
1081 			tile = tile_manager->get_original_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1082 
1083 			if (tile->damages == true)
1084 				return tile;
1085 		}
1086 	}
1087 
1088 	return NULL;
1089 }
1090 
obj_is_damaging(Obj * obj,Actor * actor)1091 bool ObjManager::obj_is_damaging(Obj *obj, Actor *actor) {
1092 	if (!obj)
1093 		return false;
1094 
1095 	Tile *tile = tile_manager->get_original_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1096 
1097 	if (tile && tile->damages == true) {
1098 		if (actor) {
1099 			MsgScroll *scroll = Game::get_game()->get_scroll();
1100 			scroll->display_string("\n\nNot possible\n");
1101 			Game::get_game()->get_script()->call_actor_tile_dmg(actor, tile->tile_num);
1102 			actor->display_condition(); // indicate that object hurt the player
1103 			scroll->display_string("\n");
1104 			scroll->display_prompt();
1105 		}
1106 		return true;
1107 	} else
1108 		return false;
1109 }
1110 
get_obj(uint16 x,uint16 y,uint8 level,bool top_obj,bool include_ignored_objects,Obj * excluded_obj)1111 Obj *ObjManager::get_obj(uint16 x, uint16 y, uint8 level, bool top_obj, bool include_ignored_objects, Obj *excluded_obj) {
1112 	Obj *obj;
1113 	Tile *tile;
1114 
1115 	obj = get_objBasedAt(x, y, level, top_obj, include_ignored_objects, excluded_obj);
1116 	if (obj != NULL)
1117 		return obj;
1118 
1119 	obj = get_objBasedAt(x + 1, y + 1, level, top_obj, include_ignored_objects, excluded_obj);
1120 	if (obj != NULL) {
1121 		tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1122 		if (tile->dbl_width && tile->dbl_height)
1123 			return obj;
1124 	}
1125 
1126 	obj = get_objBasedAt(x, y + 1, level, top_obj, include_ignored_objects, excluded_obj);
1127 	if (obj != NULL) {
1128 		tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1129 		if (tile->dbl_height)
1130 			return obj;
1131 	}
1132 
1133 	obj = get_objBasedAt(x + 1, y, level, top_obj, include_ignored_objects, excluded_obj);
1134 	if (obj != NULL) {
1135 		tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1136 		if (tile->dbl_width)
1137 			return obj;
1138 	}
1139 
1140 
1141 	return NULL;
1142 }
1143 
get_obj_of_type_from_location_inc_multi_tile(uint16 obj_n,uint16 x,uint16 y,uint8 z)1144 Obj *ObjManager::get_obj_of_type_from_location_inc_multi_tile(uint16 obj_n, uint16 x, uint16 y, uint8 z) {
1145 	return get_obj_of_type_from_location_inc_multi_tile(obj_n, -1, -1, x, y, z);
1146 }
1147 
get_obj_of_type_from_location_inc_multi_tile(uint16 obj_n,sint16 quality,sint32 qty,uint16 x,uint16 y,uint8 z)1148 Obj *ObjManager::get_obj_of_type_from_location_inc_multi_tile(uint16 obj_n, sint16 quality, sint32 qty, uint16 x, uint16 y, uint8 z) {
1149 	Obj *obj;
1150 	Tile *tile;
1151 
1152 	obj = get_obj_of_type_from_location(obj_n, quality, qty, x, y, z);
1153 	if (obj != NULL)
1154 		return obj;
1155 
1156 	obj = get_obj_of_type_from_location(obj_n, quality, qty, x + 1, y + 1, z);
1157 	if (obj != NULL) {
1158 		tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1159 		if (tile->dbl_width && tile->dbl_height)
1160 			return obj;
1161 	}
1162 
1163 	obj = get_obj_of_type_from_location(obj_n, quality, qty, x, y + 1, z);
1164 	if (obj != NULL) {
1165 		tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1166 		if (tile->dbl_height)
1167 			return obj;
1168 	}
1169 
1170 	obj = get_obj_of_type_from_location(obj_n, quality, qty, x + 1, y, z);
1171 	if (obj != NULL) {
1172 		tile = tile_manager->get_tile(get_obj_tile_num(obj->obj_n) + obj->frame_n);
1173 		if (tile->dbl_width)
1174 			return obj;
1175 	}
1176 
1177 	return NULL;
1178 }
1179 
1180 
get_obj_of_type_from_location(uint16 obj_n,uint16 x,uint16 y,uint8 z)1181 Obj *ObjManager::get_obj_of_type_from_location(uint16 obj_n, uint16 x, uint16 y, uint8 z) {
1182 	return get_obj_of_type_from_location(obj_n, -1, -1, x, y, z);
1183 }
1184 
get_obj_of_type_from_location(uint16 obj_n,sint16 quality,sint32 qty,uint16 x,uint16 y,uint8 z)1185 Obj *ObjManager::get_obj_of_type_from_location(uint16 obj_n, sint16 quality, sint32 qty, uint16 x, uint16 y, uint8 z) {
1186 	U6LList *obj_list;
1187 	U6Link *link;
1188 	Obj *obj;
1189 
1190 	obj_list = get_obj_list(x, y, z);
1191 
1192 	if (obj_list == NULL)
1193 		return NULL;
1194 // start from the top of the stack
1195 	for (link = obj_list->end(); link != NULL; link = link->prev) {
1196 		obj = (Obj *)link->data;
1197 		if (obj->obj_n == obj_n) {
1198 			if (quality != -1 && obj->quality != (uint8)quality)
1199 				continue;
1200 
1201 			if (qty != -1 && obj->qty != (uint16)qty)
1202 				continue;
1203 
1204 			return obj;
1205 		}
1206 	}
1207 
1208 	return NULL;
1209 }
1210 
1211 // x, y in world coords
get_objBasedAt(uint16 x,uint16 y,uint8 level,bool top_obj,bool include_ignored_objects,Obj * excluded_obj)1212 Obj *ObjManager::get_objBasedAt(uint16 x, uint16 y, uint8 level, bool top_obj, bool include_ignored_objects, Obj *excluded_obj) {
1213 	U6Link *link;
1214 	U6LList *obj_list;
1215 	Obj *obj;
1216 
1217 	obj_list = get_obj_list(x, y, level);
1218 
1219 	if (obj_list != NULL) {
1220 		if (top_obj)
1221 			link = obj_list->end();
1222 		else
1223 			link = obj_list->start();
1224 
1225 		while (link != NULL) {
1226 			obj = (Obj *)link->data;
1227 
1228 			if (obj != excluded_obj) {
1229 				if (include_ignored_objects)
1230 					return obj;
1231 
1232 				Tile *tile = get_obj_tile(obj->obj_n, obj->frame_n);
1233 				if ((tile->flags3 & TILEFLAG_IGNORE) != TILEFLAG_IGNORE)
1234 					return obj;
1235 			}
1236 
1237 			if (top_obj)
1238 				link = link->prev;
1239 			else
1240 				link = link->next;
1241 		}
1242 	}
1243 
1244 	return NULL;
1245 }
1246 
1247 // ObjManager keeps one instance of tile_obj per object.
1248 // SE has 3 tile objects (Trees, Yucca Plants, and Oven Fires)
get_tile_obj(uint16 obj_n)1249 Obj *ObjManager::get_tile_obj(uint16 obj_n) {
1250 	for (Std::list<Obj *>::iterator it = tile_obj_list.begin(); it != tile_obj_list.end(); ++it) {
1251 		if ((*it)->obj_n == obj_n) {
1252 			return *it;
1253 		}
1254 	}
1255 	Obj *obj = new Obj();
1256 	obj->obj_n = obj_n;
1257 	obj->set_on_map(NULL);
1258 	tile_obj_list.push_back(obj);
1259 	return obj;
1260 }
1261 
1262 /*
1263 bool ObjManager::add_obj(Obj *obj, bool addOnTop)
1264 {
1265  return add_obj(get_obj_tree(obj->x,obj->y,obj->z), obj, addOnTop);
1266 }
1267 */
1268 
remove_obj_from_map(Obj * obj)1269 bool ObjManager::remove_obj_from_map(Obj *obj) {
1270 	U6LList *obj_list;
1271 
1272 	if (obj->get_engine_loc() != OBJ_LOC_MAP)
1273 		return false;
1274 
1275 	obj_list = (U6LList *)obj->parent;
1276 
1277 	if (obj_list == NULL)
1278 		return false;
1279 
1280 	obj_list->remove(obj);
1281 	remove_obj(obj);
1282 
1283 	return true;
1284 }
1285 
remove_obj(Obj * obj)1286 void ObjManager::remove_obj(Obj *obj) {
1287 	if (obj->status & OBJ_STATUS_TEMPORARY)
1288 		temp_obj_list_remove(obj);
1289 
1290 	if (obj->obj_n == obj_egg_table[game_type]) {
1291 		egg_manager->remove_egg(obj);
1292 	}
1293 
1294 	obj->set_noloc();
1295 
1296 	return;
1297 }
1298 
1299 // remove all objects of type obj_n from location (x,y,z)
1300 
remove_obj_type_from_location(uint16 obj_n,uint16 x,uint16 y,uint8 z)1301 bool ObjManager::remove_obj_type_from_location(uint16 obj_n, uint16 x, uint16 y, uint8 z) {
1302 	U6LList *obj_list;
1303 	U6Link *link;
1304 	Obj *obj;
1305 	bool objects_deleted = false;
1306 
1307 	obj_list = get_obj_list(x, y, z);
1308 
1309 	if (obj_list != NULL) {
1310 		for (link = obj_list->start(); link != NULL;) {
1311 			obj = (Obj *)link->data;
1312 			link = link->next;
1313 
1314 			if (obj->obj_n == obj_n) {
1315 				remove_obj_from_map(obj);
1316 				delete_obj(obj);
1317 				objects_deleted = true;
1318 			}
1319 		}
1320 	}
1321 
1322 	return objects_deleted;
1323 }
1324 
copy_obj(Obj * obj)1325 Obj *ObjManager::copy_obj(Obj *obj) {
1326 	Obj *new_obj;
1327 
1328 	if (obj == NULL)
1329 		return NULL;
1330 
1331 	new_obj = new Obj(*obj);
1332 	/* changed to direct copy in case we add new members to Obj --SB-X
1333 	 new_obj->obj_n = obj->obj_n;
1334 	 new_obj->frame_n = obj->frame_n;
1335 
1336 	 new_obj->status = obj->status;
1337 	 new_obj->qty = obj->qty;
1338 	 new_obj->quality = obj->quality;
1339 
1340 	 new_obj->x = obj->x;
1341 	 new_obj->y = obj->y;
1342 	 new_obj->z = obj->z;*/
1343 
1344 // should we copy container???
1345 	new_obj->container = 0;
1346 
1347 	return new_obj;
1348 }
1349 
move(Obj * obj,uint16 x,uint16 y,uint8 level)1350 bool ObjManager::move(Obj *obj, uint16 x, uint16 y, uint8 level) {
1351 	if (remove_obj_from_map(obj) == false)
1352 		return false;
1353 
1354 	obj->x = x;
1355 	obj->y = y;
1356 	obj->z = level;
1357 
1358 	add_obj(obj, true); // add the object on top of the stack
1359 
1360 	return true;
1361 }
1362 
1363 /* Returns an objects look-string, its general description.
1364  */
look_obj(Obj * obj,bool show_prefix)1365 const char *ObjManager::look_obj(Obj *obj, bool show_prefix) {
1366 	const char *desc;
1367 	if (obj == NULL)
1368 		return NULL;
1369 
1370 	desc = tile_manager->lookAtTile(get_obj_tile_num(obj) + obj->frame_n, obj->qty, show_prefix);
1371 
1372 	return desc;
1373 }
1374 
get_obj_name(Obj * obj)1375 const char *ObjManager::get_obj_name(Obj *obj) {
1376 	return tile_manager->lookAtTile(get_obj_tile_num(obj->obj_n), 0, false);
1377 }
1378 
get_obj_name(uint16 obj_n)1379 const char *ObjManager::get_obj_name(uint16 obj_n) {
1380 	return tile_manager->lookAtTile(get_obj_tile_num(obj_n), 0, false);
1381 }
1382 
get_obj_name(uint16 obj_n,uint8 frame_n)1383 const char *ObjManager::get_obj_name(uint16 obj_n, uint8 frame_n) {
1384 	return tile_manager->lookAtTile(get_obj_tile_num(obj_n) + frame_n, 0, false);
1385 }
1386 
get_obj_weight(uint16 obj_n)1387 float ObjManager::get_obj_weight(uint16 obj_n) {
1388 	float weight = (float)get_obj_weight_unscaled(obj_n);
1389 	if (has_reduced_weight(obj_n)) {
1390 		weight /= 10;
1391 	}
1392 
1393 	return weight / 10;
1394 }
1395 
get_obj_weight(Obj * obj,bool include_container_items,bool scale,bool include_qty)1396 float ObjManager::get_obj_weight(Obj *obj, bool include_container_items, bool scale, bool include_qty) {
1397 	float weight;
1398 	U6Link *link;
1399 
1400 	weight = obj_weight[obj->obj_n];
1401 
1402 	if (is_stackable(obj)) {
1403 		if (include_qty) {
1404 			if (obj->qty == 0)
1405 				obj->qty = 1;
1406 			weight *= obj->qty;
1407 		}
1408 		/* luteijn: only some need to be divided by an extra 10 for a total of 100.
1409 		 * unfortunately can't seem to find a tileflag that controls this so would have to be hardcoded!
1410 		 */
1411 		if (has_reduced_weight(obj)) {
1412 			weight /= 10; // luteijn: regardless of the scaling flag!
1413 		}
1414 	}
1415 
1416 	if (obj->container != NULL && include_container_items == OBJ_WEIGHT_INCLUDE_CONTAINER_ITEMS) {
1417 		for (link = obj->container->start(); link != NULL; link = link->next)
1418 			/* weight += get_obj_weight(reinterpret_cast<Obj*>(link->data), false);*/ //don't scale container objects yet.
1419 			weight += get_obj_weight(reinterpret_cast<Obj *>(link->data), OBJ_WEIGHT_INCLUDE_CONTAINER_ITEMS, OBJ_WEIGHT_DONT_SCALE); //don't scale container objects yet. luteijn: and use the right flag to do so!
1420 	}
1421 
1422 	if (scale == OBJ_WEIGHT_DO_SCALE) {
1423 		weight /= 10;
1424 	}
1425 
1426 	return weight;
1427 }
1428 
get_obj_tile_num(uint16 obj_num)1429 uint16 ObjManager::get_obj_tile_num(uint16 obj_num) { //assume obj_num is < 1024 :)
1430 	return obj_to_tile[obj_num];
1431 }
1432 
is_corpse(Obj * obj)1433 inline bool ObjManager::is_corpse(Obj *obj) {
1434 	if (game_type == NUVIE_GAME_U6) {
1435 		switch (obj->obj_n) {
1436 		case OBJ_U6_DEAD_BODY:
1437 		case OBJ_U6_DEAD_CYCLOPS:
1438 		case OBJ_U6_DEAD_GARGOYLE:
1439 		case OBJ_U6_DOG: // Kador id_n 135
1440 		case OBJ_U6_MOUSE: // Sherry id_n 9
1441 		case OBJ_U6_HORSE_CARCASS: // Pushme Pullyu id 130, Smith id 132
1442 			return true;
1443 		default:
1444 			break;
1445 		}
1446 	} else if (game_type == NUVIE_GAME_SE) {
1447 		/* TODO - add SE body obj numbers
1448 		switch (obj->obj_n) {
1449 		default:
1450 			break;
1451 		}
1452 		*/
1453 	} else { // MD
1454 		/* TODO - add MD body obj numbers
1455 		switch (obj->obj_n) {
1456 		default:
1457 			break;
1458 		}
1459 		*/
1460 	}
1461 	return false;
1462 }
1463 
get_obj_tile_num(Obj * obj)1464 uint16 ObjManager::get_obj_tile_num(Obj *obj) { //assume obj_num is < 1024 :)
1465 	if (custom_actor_tiles && is_corpse(obj)) {
1466 		return Game::get_game()->get_actor_manager()->get_actor(obj->quality)->get_custom_tile_num(obj->obj_n);
1467 	}
1468 
1469 	uint16 obj_num = obj->obj_n;
1470 	// Savage Empire Tile Object (Get Tile from Map Location)
1471 	if (game_type == NUVIE_GAME_SE &&
1472 	        Game::get_game()->get_script()->call_is_tile_object(obj_num)) {
1473 		return (Game::get_game()->get_game_map()->get_tile(obj->x, obj->y, obj->z)->tile_num);
1474 	}
1475 	return get_obj_tile_num(obj_num);
1476 }
1477 
set_obj_tile_num(uint16 obj_num,uint16 tile_num)1478 void ObjManager::set_obj_tile_num(uint16 obj_num, uint16 tile_num) {
1479 	obj_to_tile[obj_num] = tile_num;
1480 	return;
1481 }
1482 
1483 /* Animate all visible tiles of an object `loop_count' times. */
animate_forwards(Obj * obj,uint32 loop_count)1484 void ObjManager::animate_forwards(Obj *obj, uint32 loop_count) {
1485 	// In U6 there is no place where one object must animate and nearby objects
1486 	// of the same type don't also animate, so just forward to TileManager.
1487 	tile_manager->set_anim_loop(get_obj_tile_num(obj->obj_n), loop_count, 0);
1488 }
1489 
1490 /* Animate in reverse all visible tiles of an object `loop_count' times. */
animate_backwards(Obj * obj,uint32 loop_count)1491 void ObjManager::animate_backwards(Obj *obj, uint32 loop_count) {
1492 	tile_manager->set_anim_loop(get_obj_tile_num(obj->obj_n), loop_count, 1);
1493 }
1494 
1495 
get_actor_inventory(uint16 actor_num)1496 U6LList *ObjManager::get_actor_inventory(uint16 actor_num) {
1497 	if (actor_num >= 256)
1498 		return NULL;
1499 
1500 	if (actor_inventories[actor_num] == NULL) {
1501 		actor_inventories[actor_num] = new U6LList();
1502 	}
1503 
1504 	return actor_inventories[actor_num];
1505 }
1506 
actor_has_inventory(uint16 actor_num)1507 bool ObjManager::actor_has_inventory(uint16 actor_num) {
1508 	if (actor_inventories[actor_num] != NULL) {
1509 		if (actor_inventories[actor_num]->start() != NULL)
1510 			return true;
1511 	}
1512 
1513 	return false;
1514 }
1515 
find_next_obj(uint8 level,Obj * prev_obj,bool match_frame_n,bool match_quality)1516 Obj *ObjManager::find_next_obj(uint8 level, Obj *prev_obj, bool match_frame_n, bool match_quality) {
1517 	if (prev_obj == NULL)
1518 		return NULL;
1519 
1520 	Obj **p = &prev_obj;
1521 
1522 	return find_obj(level, prev_obj->obj_n, prev_obj->quality, match_quality, prev_obj->frame_n, match_frame_n, p);
1523 }
1524 
find_obj(uint8 level,uint16 obj_n,uint8 quality,bool match_quality,uint16 frame_n,bool match_frame_n,Obj ** prev_obj)1525 Obj *ObjManager::find_obj(uint8 level, uint16 obj_n, uint8 quality, bool match_quality, uint16 frame_n, bool match_frame_n, Obj **prev_obj) {
1526 	uint8 i;
1527 	Obj *new_obj;
1528 
1529 	if (level == 0) {
1530 		for (i = 0; i < 64; i++) {
1531 			new_obj = find_obj_in_tree(obj_n, quality, match_quality, frame_n, match_frame_n, prev_obj, surface[i]);
1532 			if (new_obj != NULL)
1533 				return new_obj;
1534 		}
1535 	} else {
1536 		new_obj = find_obj_in_tree(obj_n, quality, match_quality, frame_n, match_frame_n, prev_obj, dungeon[level - 1]);
1537 		if (new_obj != NULL)
1538 			return new_obj;
1539 	}
1540 
1541 	return NULL;
1542 }
1543 
find_obj_in_tree(uint16 obj_n,uint8 quality,bool match_quality,uint8 frame_n,bool match_frame_n,Obj ** prev_obj,iAVLTree * obj_tree)1544 inline Obj *ObjManager::find_obj_in_tree(uint16 obj_n, uint8 quality, bool match_quality, uint8 frame_n, bool match_frame_n, Obj **prev_obj, iAVLTree *obj_tree) {
1545 	iAVLCursor cursor;
1546 	ObjTreeNode *node;
1547 	U6Link *link;
1548 	Obj *new_obj;
1549 
1550 	node = (ObjTreeNode *)iAVLFirst(&cursor, obj_tree);
1551 
1552 	for (; node != NULL;) {
1553 		link = ((U6LList *)(node->obj_list))->start();
1554 		for (; link != NULL; link = link->next) {
1555 			new_obj = (Obj *)link->data;
1556 			if (new_obj->obj_n == obj_n && (match_quality == false || new_obj->quality == quality) && (match_frame_n == false || new_obj->frame_n == frame_n)) {
1557 				if (prev_obj != NULL && new_obj == *prev_obj)
1558 					*prev_obj = NULL;
1559 				else {
1560 					if (prev_obj == NULL || *prev_obj == NULL)
1561 						return new_obj;
1562 				}
1563 			}
1564 			/* Don't search containers.
1565 			     if(prev_obj == NULL)
1566 			     {
1567 			         new_obj = new_obj->find_in_container(obj_n, quality, match_quality, frame_n, match_frame_n, prev_obj);
1568 			         if(new_obj)
1569 			           return new_obj;
1570 			     }
1571 			*/
1572 		}
1573 
1574 		node = (ObjTreeNode *)iAVLNext(&cursor);
1575 	}
1576 
1577 	return NULL;
1578 }
1579 
add_obj(Obj * obj,bool addOnTop)1580 bool ObjManager::add_obj(Obj *obj, bool addOnTop) {
1581 	iAVLTree *obj_tree;
1582 	ObjTreeNode *node;
1583 	U6LList *obj_list;
1584 	iAVLKey key;
1585 
1586 	obj_tree = get_obj_tree(obj->x, obj->y, obj->z);
1587 	key = get_obj_tree_key(obj);
1588 
1589 	node = (ObjTreeNode *)iAVLSearch(obj_tree, key);
1590 
1591 	if (node == NULL) {
1592 		obj_list = new U6LList();
1593 
1594 		node = new ObjTreeNode;
1595 		node->key = key;
1596 		node->obj_list = obj_list;
1597 
1598 		iAVLInsert(obj_tree, node);
1599 	} else {
1600 		obj_list = node->obj_list;
1601 	}
1602 
1603 	if (addOnTop)
1604 		obj_list->add(obj);
1605 	else
1606 		obj_list->addAtPos(0, obj);
1607 
1608 	if (obj->status & OBJ_STATUS_TEMPORARY)
1609 		temp_obj_list_add(obj);
1610 
1611 	obj->set_on_map(obj_list); //mark object as on map.
1612 
1613 	return true;
1614 }
addObjToContainer(U6LList * llist,Obj * obj)1615 bool ObjManager::addObjToContainer(U6LList *llist, Obj *obj) {
1616 	U6Link *link;
1617 	Obj *c_obj = NULL; //container object
1618 	uint16 index;
1619 
1620 	index = ((obj->y & 0x3f) << 10) + obj->x; //10 bits from x and 6 bits from y
1621 
1622 	link = llist->gotoPos(index);
1623 	if (link != NULL)
1624 		c_obj = (Obj *)link->data;
1625 
1626 	if (c_obj) { // we've found our container.
1627 		c_obj->add(obj);
1628 
1629 		//DEBUG(0,LEVEL_DEBUGGING,"Cont: %s\n", tile_manager->lookAtTile(get_obj_tile_num(c_obj->obj_n)+c_obj->frame_n,0,false));
1630 		//DEBUG(0,LEVEL_DEBUGGING,"Add to container %s", tile_manager->lookAtTile(get_obj_tile_num(obj->obj_n)+obj->frame_n,0,false));
1631 		//DEBUG(1,LEVEL_DEBUGGING," -> %s (%x,%x,%x)\n", tile_manager->lookAtTile(get_obj_tile_num(c_obj->obj_n)+c_obj->frame_n,0,false),c_obj->x,c_obj->y,c_obj->z);
1632 		return true;
1633 	}
1634 
1635 	return false;
1636 }
1637 
loadObj(NuvieIO * buf)1638 Obj *ObjManager::loadObj(NuvieIO *buf) {
1639 	uint8 b1, b2;
1640 	Obj *obj;
1641 
1642 	obj = new Obj();
1643 //obj->objblk_n = objblk_n;
1644 
1645 	obj->status = buf->read1();
1646 
1647 //set new nuvie location bits.
1648 	switch (obj->status & OBJ_STATUS_MASK_GET) {
1649 	case OBJ_STATUS_ON_MAP :
1650 		obj->set_on_map(NULL);
1651 		break;//obj->nuvie_status |= OBJ_LOC_MAP; break;
1652 	case OBJ_STATUS_IN_CONTAINER :
1653 		obj->set_in_container(NULL);
1654 		break;//obj->nuvie_status |= OBJ_LOC_CONT; break;
1655 	case OBJ_STATUS_IN_INVENTORY :
1656 		obj->set_in_inventory();
1657 		break;//obj->nuvie_status |= OBJ_LOC_INV; break;
1658 	case OBJ_STATUS_READIED :
1659 		obj->readied();
1660 		break;//obj->nuvie_status |= OBJ_LOC_READIED; break;
1661 	}
1662 
1663 	obj->x = buf->read1(); // h
1664 	b1 = buf->read1();
1665 	obj->x += (b1 & 0x3) << 8;
1666 
1667 	obj->y = (b1 & 0xfc) >> 2;
1668 	b2 = buf->read1();
1669 	obj->y += (b2 & 0xf) << 6;
1670 
1671 	obj->z = (b2 & 0xf0) >> 4;
1672 
1673 	b1 = buf->read1();
1674 	b2 = buf->read1();
1675 	obj->obj_n = b1;
1676 	obj->obj_n += (b2 & 0x3) << 8;
1677 
1678 	obj->frame_n = (b2 & 0xfc) >> 2;
1679 
1680 	obj->qty = buf->read1();
1681 	obj->quality = buf->read1();
1682 	if (is_stackable(obj))
1683 		obj->qty = (uint16)(obj->quality << 8) + obj->qty;
1684 
1685 //if(obj->qty == 0)
1686 //  obj->qty = 1;
1687 
1688 	return obj;
1689 }
1690 
1691 
get_obj_tree(uint16 x,uint16 y,uint8 level)1692 iAVLTree *ObjManager::get_obj_tree(uint16 x, uint16 y, uint8 level) {
1693 	if (level == 0) {
1694 		x >>= 7; // x = floor(x / 128)   128 = superchunk width
1695 		y >>= 7; // y = floor(y / 128)   128 = superchunk height
1696 
1697 		return surface[x + y * 8];
1698 	}
1699 
1700 	if (level > 5)
1701 		return NULL;
1702 
1703 	return dungeon[level - 1];
1704 }
1705 
get_obj_tree_key(Obj * obj)1706 inline iAVLKey ObjManager::get_obj_tree_key(Obj *obj) {
1707 	return get_obj_tree_key(obj->x, obj->y, obj->z);
1708 }
1709 
get_obj_tree_key(uint16 x,uint16 y,uint8 level)1710 iAVLKey ObjManager::get_obj_tree_key(uint16 x, uint16 y, uint8 level) {
1711 	iAVLKey key;
1712 	if (level == 0)
1713 		key._int = y * 1024 + x;
1714 	else
1715 		key._int = y * 256 + x;
1716 
1717 	return key;
1718 }
1719 
update(uint16 x,uint16 y,uint8 z,bool teleport)1720 void ObjManager::update(uint16 x, uint16 y, uint8 z, bool teleport) {
1721 	uint16 cur_blk_x, cur_blk_y;
1722 
1723 	cur_blk_x = x >> 3; // x / 8;
1724 	cur_blk_y = y >> 3; // y / 8;
1725 
1726 // We're changing levels so clean out all temp objects on the current level.
1727 	if (last_obj_blk_z != z) {
1728 		if (last_obj_blk_z != OBJ_TEMP_INIT)
1729 			temp_obj_list_clean_level(last_obj_blk_z);
1730 
1731 		egg_manager->spawn_eggs(x, y, z, teleport);
1732 
1733 		last_obj_blk_x = cur_blk_x;
1734 		last_obj_blk_y = cur_blk_y;
1735 		last_obj_blk_z = z;
1736 
1737 		return;
1738 	}
1739 
1740 //FIX for level change. we want to remove all temps on level change.
1741 	if (cur_blk_x != last_obj_blk_x || cur_blk_y != last_obj_blk_y) {
1742 		last_obj_blk_x = cur_blk_x;
1743 		last_obj_blk_y = cur_blk_y;
1744 
1745 		temp_obj_list_clean_area(x, y);
1746 		egg_manager->spawn_eggs(x, y, z, teleport);
1747 
1748 	}
1749 
1750 	return;
1751 }
1752 
temp_obj_list_add(Obj * obj)1753 bool ObjManager::temp_obj_list_add(Obj *obj) {
1754 	if (obj == NULL)
1755 		return false;
1756 
1757 	temp_obj_list.push_back(obj);
1758 
1759 	return true;
1760 }
1761 
temp_obj_list_remove(Obj * obj)1762 bool ObjManager::temp_obj_list_remove(Obj *obj) {
1763 	temp_obj_list.remove(obj);
1764 	return true;
1765 }
1766 
remove_temp_obj(Obj * tmp_obj)1767 void ObjManager::remove_temp_obj(Obj *tmp_obj) {
1768 	//FIXME MD has special temp object flag override logic. This should be implemented in lua script.
1769 	if (game_type != NUVIE_GAME_MD || (tmp_obj->obj_n != OBJ_MD_DREAM_TELEPORTER && tmp_obj->frame_n != 0)) {
1770 		DEBUG(0,
1771 		      LEVEL_DEBUGGING,
1772 		      "Removing obj %s.\n",
1773 		      tile_manager->lookAtTile(get_obj_tile_num((tmp_obj)->obj_n) + (tmp_obj)->frame_n, 0, false));
1774 		remove_obj_from_map(tmp_obj);
1775 		delete_obj(tmp_obj);
1776 	}
1777 }
1778 
1779 // clean objects from a whole level.
temp_obj_list_clean_level(uint8 z)1780 void ObjManager::temp_obj_list_clean_level(uint8 z) {
1781 	Std::list<Obj *>::iterator obj;
1782 	Obj *tmp_obj;
1783 
1784 	for (obj = temp_obj_list.begin(); obj != temp_obj_list.end();) {
1785 		if ((*obj)->z == z) {
1786 			tmp_obj = *obj++;
1787 			remove_temp_obj(tmp_obj);
1788 		} else
1789 			obj++;
1790 	}
1791 
1792 	return;
1793 }
1794 
1795 
1796 // Clean objects more than 19 tiles from position
temp_obj_list_clean_area(uint16 x,uint16 y)1797 void ObjManager::temp_obj_list_clean_area(uint16 x, uint16 y) {
1798 	Std::list<Obj *>::iterator obj;
1799 	Obj *tmp_obj;
1800 	sint16 dist_x, dist_y;
1801 
1802 	for (obj = temp_obj_list.begin(); obj != temp_obj_list.end();) {
1803 		dist_x = abs((sint16)(*obj)->x - x);
1804 		dist_y = abs((sint16)(*obj)->y - y);
1805 
1806 		if (dist_x > 19 || dist_y > 19) {
1807 			tmp_obj = *obj++;
1808 			remove_temp_obj(tmp_obj);
1809 		} else
1810 			obj++;
1811 	}
1812 
1813 	return;
1814 }
1815 
1816 /*
1817 inline U6LList *ObjManager::get_schunk_list(uint16 x, uint16 y, uint8 level)
1818 {
1819  uint16 sx, sy;
1820 
1821  if(level == 0)
1822    {
1823 	sx = x / 128;
1824 	sy = y / 128;
1825 	return surface[sy * 8 + sx];
1826    }
1827 
1828  return dungeon[level-1];
1829 }
1830 */
1831 //prints a human readable list of object number / names.
1832 
print_object_list()1833 void ObjManager::print_object_list() {
1834 	uint16 i;
1835 
1836 	DEBUG(0, LEVEL_INFORMATIONAL, "print_object_list:\n");
1837 	for (i = 0; i < 1024; i++) {
1838 		DEBUG(1, LEVEL_INFORMATIONAL, "%04d: %s\n", i, tile_manager->lookAtTile(get_obj_tile_num(i), 0, false));
1839 	}
1840 
1841 	return;
1842 }
1843 
print_egg_list()1844 void ObjManager::print_egg_list() {
1845 	uint8 i;
1846 
1847 	for (i = 0; i < 64; i++)
1848 		print_egg_tree(surface[i]);
1849 
1850 	for (i = 0; i < 5; i++)
1851 		print_egg_tree(dungeon[i]);
1852 
1853 	return;
1854 }
1855 
print_egg_tree(iAVLTree * obj_tree)1856 inline void ObjManager::print_egg_tree(iAVLTree *obj_tree) {
1857 	ObjTreeNode *tree_node;
1858 	iAVLCursor cursor;
1859 	U6LList *obj_list;
1860 	U6Link *link;
1861 	Obj *obj;
1862 
1863 	tree_node = (ObjTreeNode *)iAVLFirst(&cursor, obj_tree);
1864 
1865 	for (; tree_node != NULL; tree_node = (ObjTreeNode *)iAVLNext(&cursor)) {
1866 		obj_list = (U6LList *)tree_node->obj_list;
1867 		for (link = obj_list->start(); link != NULL; link = link->next) {
1868 			obj = (Obj *)link->data;
1869 			if (obj->obj_n == 335) {
1870 				print_obj(obj, false);
1871 			}
1872 		}
1873 	}
1874 
1875 	return;
1876 }
1877 
print_obj(Obj * obj,bool in_container,uint8 indent)1878 void ObjManager::print_obj(Obj *obj, bool in_container, uint8 indent) {
1879 	U6Link *link;
1880 	Obj *container_obj;
1881 	const CombatType *c_type = NULL;
1882 	Actor *a = Game::get_game()->get_player()->get_actor();
1883 
1884 	if (a != NULL)
1885 		c_type = a->get_object_combat_type(obj->obj_n);
1886 
1887 	DEBUG(1, LEVEL_INFORMATIONAL, "\n");
1888 	print_indent(LEVEL_INFORMATIONAL, indent);
1889 	DEBUG(1, LEVEL_INFORMATIONAL, "%s ", tile_manager->lookAtTile(get_obj_tile_num(obj->obj_n) + obj->frame_n, 0, false));
1890 
1891 	if (in_container == false)
1892 		DEBUG(1, LEVEL_INFORMATIONAL, "at %x, %x, %x (%d,%d,%d)", obj->x, obj->y, obj->z, obj->x, obj->y, obj->z);
1893 	DEBUG(1, LEVEL_INFORMATIONAL, "\n");
1894 
1895 	print_indent(LEVEL_INFORMATIONAL, indent);
1896 	DEBUG(1, LEVEL_INFORMATIONAL, "object (Obj *) %p\n", obj);
1897 	print_indent(LEVEL_INFORMATIONAL, indent);
1898 
1899 	DEBUG(1, LEVEL_INFORMATIONAL, "engine loc: ");
1900 	switch (obj->get_engine_loc()) {
1901 	case OBJ_LOC_MAP :
1902 		DEBUG(1, LEVEL_INFORMATIONAL, "MAP");
1903 		break;
1904 	case OBJ_LOC_CONT :
1905 		DEBUG(1, LEVEL_INFORMATIONAL, "CONTAINER");
1906 		break;
1907 	case OBJ_LOC_INV :
1908 		DEBUG(1, LEVEL_INFORMATIONAL, "INVENTORY");
1909 		break;
1910 	case OBJ_LOC_READIED :
1911 		DEBUG(1, LEVEL_INFORMATIONAL, "INVENTORY READIED");
1912 		break;
1913 	case OBJ_LOC_NONE :
1914 		DEBUG(1, LEVEL_INFORMATIONAL, "NONE");
1915 		break;
1916 	default :
1917 		DEBUG(1, LEVEL_INFORMATIONAL, "**UNKNOWN**");
1918 		break;
1919 	}
1920 	if (obj->is_actor_obj())
1921 		DEBUG(1, LEVEL_INFORMATIONAL, " (ACTOR_OBJ)");
1922 	DEBUG(1, LEVEL_INFORMATIONAL, "\n");
1923 
1924 	DEBUG(1, LEVEL_INFORMATIONAL, "parent (");
1925 	switch (obj->get_engine_loc()) {
1926 	case OBJ_LOC_MAP :
1927 		DEBUG(1, LEVEL_INFORMATIONAL, "U6LList");
1928 		break;
1929 	case OBJ_LOC_CONT :
1930 		DEBUG(1, LEVEL_INFORMATIONAL, "Obj");
1931 		break;
1932 	case OBJ_LOC_INV :
1933 	case OBJ_LOC_READIED :
1934 		DEBUG(1, LEVEL_INFORMATIONAL, "Actor");
1935 		break;
1936 	default :
1937 		DEBUG(1, LEVEL_INFORMATIONAL, "void");
1938 		break;
1939 	}
1940 	DEBUG(1, LEVEL_INFORMATIONAL, " *) %p\n", obj->parent);
1941 
1942 	print_indent(LEVEL_INFORMATIONAL, indent);
1943 // DEBUG(1,LEVEL_DEBUGGING,"objblk_n: %d\n", obj->objblk_n);
1944 
1945 	print_indent(LEVEL_INFORMATIONAL, indent);
1946 	DEBUG(1, LEVEL_INFORMATIONAL, "obj_n: %d\n", obj->obj_n);
1947 
1948 	print_indent(LEVEL_INFORMATIONAL, indent);
1949 	DEBUG(1, LEVEL_INFORMATIONAL, "frame_n: %d\n", obj->frame_n);
1950 
1951 	print_indent(LEVEL_INFORMATIONAL, indent);
1952 	DEBUG(1, LEVEL_INFORMATIONAL, "Tile: %d\n", get_obj_tile_num(obj->obj_n));
1953 
1954 	print_indent(LEVEL_INFORMATIONAL, indent);
1955 	DEBUG(1, LEVEL_INFORMATIONAL, "Status: ");
1956 	print_b(LEVEL_INFORMATIONAL, obj->status);
1957 	if (obj->status != 0) {
1958 		DEBUG(1, LEVEL_INFORMATIONAL, " ( ");
1959 		if (obj->is_readied())
1960 			DEBUG(1, LEVEL_INFORMATIONAL, "POS:Ready ");
1961 		else if (obj->is_in_container())
1962 			DEBUG(1, LEVEL_INFORMATIONAL, "POS:Cont ");
1963 		else if (obj->is_in_inventory())
1964 			DEBUG(1, LEVEL_INFORMATIONAL, "POS:Inv ");
1965 		if (obj->is_ok_to_take())
1966 			DEBUG(1, LEVEL_INFORMATIONAL, "OK ");
1967 		if (obj->is_temporary())
1968 			DEBUG(1, LEVEL_INFORMATIONAL, "TEMP ");
1969 		if (obj->is_invisible())
1970 			DEBUG(1, LEVEL_INFORMATIONAL, "INVIS ");
1971 		if (obj->is_egg_active()) {
1972 			if (obj->obj_n < 256)
1973 				DEBUG(1, LEVEL_INFORMATIONAL, "MUTANT ");
1974 			else
1975 				DEBUG(1, LEVEL_INFORMATIONAL, "BROKEN ");
1976 		}
1977 
1978 		DEBUG(1, LEVEL_INFORMATIONAL, ")");
1979 	}
1980 
1981 	DEBUG(1, LEVEL_INFORMATIONAL, "\n");
1982 
1983 	if (in_container) {
1984 		print_indent(LEVEL_INFORMATIONAL, indent);
1985 		DEBUG(1, LEVEL_INFORMATIONAL, "parent_id = %d, y = %d, z = %d\n", obj->x, obj->y, obj->z);
1986 	}
1987 
1988 	print_indent(LEVEL_INFORMATIONAL, indent);
1989 	DEBUG(1, LEVEL_INFORMATIONAL, "Quantity: %d\n", obj->qty);
1990 	print_indent(LEVEL_INFORMATIONAL, indent);
1991 	DEBUG(1, LEVEL_INFORMATIONAL, "Quality: %d\n", obj->quality);
1992 	if (c_type != NULL) {
1993 		DEBUG(1, LEVEL_INFORMATIONAL, "attack/damage = %d, defence/defense = %d\n", c_type->damage, c_type->defense); // FIXME add the rest of the combat values
1994 	}
1995 
1996 	if (obj->container) {
1997 		print_indent(LEVEL_INFORMATIONAL, indent);
1998 		DEBUG(1, LEVEL_INFORMATIONAL, "Container\n");
1999 		print_indent(LEVEL_INFORMATIONAL, indent);
2000 		DEBUG(1, LEVEL_INFORMATIONAL, "---------");
2001 
2002 		for (link = obj->container->start(); link != NULL; link = link->next) {
2003 			container_obj = (Obj *)link->data;
2004 			print_obj(container_obj, true, indent + 2);
2005 		}
2006 
2007 		print_indent(LEVEL_INFORMATIONAL, indent);
2008 		DEBUG(1, LEVEL_INFORMATIONAL, "---------\n");
2009 	}
2010 
2011 	if (in_container == false)
2012 		DEBUG(1, LEVEL_INFORMATIONAL, "\n");
2013 
2014 	return;
2015 }
2016 
new_obj(uint16 obj_n,uint8 frame_n,uint16 x,uint16 y,uint16 z)2017 Obj *new_obj(uint16 obj_n, uint8 frame_n, uint16 x, uint16 y, uint16 z) {
2018 	Obj *obj;
2019 
2020 	obj = new Obj();
2021 
2022 	obj->obj_n = obj_n;
2023 	obj->frame_n = frame_n;
2024 
2025 	obj->x = x;
2026 	obj->y = y;
2027 	obj->z = z;
2028 
2029 	return obj;
2030 }
2031 
delete_obj(Obj * obj)2032 void delete_obj(Obj *obj) {
2033 	U6Link *link;
2034 
2035 	if (obj->is_script_obj() == false) {
2036 		if (obj->container) {
2037 			for (link = obj->container->start(); link != NULL;) {
2038 				Obj *cont_obj = (Obj *)link->data;
2039 				link = link->next;
2040 
2041 				delete_obj(cont_obj);
2042 			}
2043 		}
2044 
2045 		if (obj->container)
2046 			delete obj->container;
2047 		delete obj;
2048 	}
2049 
2050 	return;
2051 }
2052 
2053 // add object to list, stacking with existing objects if possible
2054 // This is used for adding objects to inventory OR a container.
2055 // *It will stack onto the new object and delete the existing object!*
2056 
2057 //FIXME!!!!! We need to set on_map() etc if going to the map.
2058 
list_add_obj(U6LList * llist,Obj * obj,bool stack_objects,uint32 pos)2059 bool ObjManager::list_add_obj(U6LList *llist, Obj *obj, bool stack_objects, uint32 pos) {
2060 	Obj *stack_with;
2061 	uint16 new_qty;
2062 	U6Link *link;
2063 
2064 	if (!llist || !obj)
2065 		return false;
2066 
2067 	assert(pos == 0 || pos < llist->count());
2068 
2069 	if (stack_objects && is_stackable(obj)) {
2070 		for (link = llist->start(); link != NULL;) {
2071 			stack_with = (Obj *)link->data;
2072 			link = link->next;
2073 
2074 			if (stack_with->obj_n == obj->obj_n && stack_with->frame_n == obj->frame_n
2075 			        && stack_with->quality == obj->quality && is_stackable(stack_with)) {
2076 				new_qty = obj->qty + stack_with->qty;
2077 				obj->qty = new_qty;
2078 				llist->addAtPos(llist->findPos(stack_with), obj);
2079 
2080 				llist->remove(stack_with);
2081 				delete_obj(stack_with);
2082 
2083 				return true;
2084 			}
2085 		}
2086 	}
2087 
2088 	llist->addAtPos(pos, obj);
2089 
2090 	return true;
2091 }
2092 
2093 /* Call load usecode for all objects (after loading them). This should be in
2094  * loadObj() but that was crashing when usecode tried to use timers.
2095  */
startObjs()2096 void ObjManager::startObjs() {
2097 	uint8 i;
2098 
2099 //iterate through surface chunks.
2100 	for (i = 0; i < 64; i++)
2101 		start_obj_usecode(surface[i]);
2102 
2103 //iterate through dungeon chunks.
2104 	for (i = 0; i < 5; i++)
2105 		start_obj_usecode(dungeon[i]);
2106 
2107 }
2108 
start_obj_usecode(iAVLTree * obj_tree)2109 inline void ObjManager::start_obj_usecode(iAVLTree *obj_tree) {
2110 	ObjTreeNode *tree_node;
2111 	iAVLCursor cursor;
2112 	U6LList *obj_list;
2113 	U6Link *link;
2114 	Obj *obj;
2115 
2116 	tree_node = (ObjTreeNode *)iAVLFirst(&cursor, obj_tree);
2117 	for (; tree_node != NULL; tree_node = (ObjTreeNode *)iAVLNext(&cursor)) {
2118 		obj_list = (U6LList *)tree_node->obj_list;
2119 		for (link = obj_list->start(); link != NULL; link = link->next) {
2120 			obj = (Obj *)link->data;
2121 			if (usecode->has_loadcode(obj))
2122 				usecode->load_obj(obj);
2123 		}
2124 	}
2125 }
2126 
2127 
2128 /* Subtract an object stack with quantity set to `count' from original object
2129  * stack `obj'.
2130  * Returns a new object if a stack could be subtracted from the original,
2131  * leaving the original intact.
2132  * Returns the original if its quantity was smaller than the requested count or
2133  * it is not stackable.
2134  */
get_obj_from_stack(Obj * obj,uint32 count)2135 Obj *ObjManager::get_obj_from_stack(Obj *obj, uint32 count) {
2136 	if (count == 0 || obj->qty <= count || !is_stackable(obj))
2137 		return (obj);
2138 	// requested is over 0, original quantity is greater than requested, object
2139 	//  is stackable
2140 	Obj *new_obj = copy_obj(obj);
2141 	new_obj->qty = count;
2142 	obj->qty -= count; // remove requested from original
2143 	return (new_obj);
2144 }
2145 
clean_obj_tree_node(void * node)2146 void clean_obj_tree_node(void *node) {
2147 	U6Link *link;
2148 	ObjTreeNode *obj_node = (ObjTreeNode *)node;
2149 
2150 	for (link = obj_node->obj_list->start(); link != NULL;) {
2151 		Obj *obj = (Obj *)link->data;
2152 		link = link->next;
2153 
2154 		delete_obj(obj);
2155 	}
2156 
2157 	delete obj_node->obj_list;
2158 	delete obj_node;
2159 
2160 	return;
2161 }
2162 
unlink_from_engine(Obj * obj,bool run_usecode)2163 bool ObjManager::unlink_from_engine(Obj *obj, bool run_usecode) {
2164 	Actor *a;
2165 	Obj *cont_obj;
2166 
2167 	switch (obj->get_engine_loc()) {
2168 	case OBJ_LOC_NONE :
2169 		break;
2170 	case OBJ_LOC_MAP :
2171 		remove_obj_from_map(obj);
2172 		break;
2173 // inventory_remove_obj unreadies
2174 	case OBJ_LOC_READIED :/* a = (Actor *)obj->parent;
2175 						   a->remove_readied_object(obj, run_usecode);
2176 						   a->inventory_remove_obj(obj, run_usecode);
2177 						   break;
2178 */
2179 	case OBJ_LOC_INV :
2180 		a = (Actor *)obj->parent;
2181 		a->inventory_remove_obj(obj, run_usecode);
2182 		break;
2183 
2184 	case OBJ_LOC_CONT :
2185 		cont_obj = obj->get_container_obj();
2186 		if (cont_obj)
2187 			cont_obj->remove(obj); //remove from parent container.
2188 		break;
2189 		break;
2190 	}
2191 
2192 	return true;
2193 }
2194 
moveto_map(Obj * obj,MapCoord location)2195 bool ObjManager::moveto_map(Obj *obj, MapCoord location) {
2196 	unlink_from_engine(obj);
2197 
2198 	obj->x = location.x;
2199 	obj->y = location.y;
2200 	obj->z = location.z;
2201 
2202 	add_obj(obj, OBJ_ADD_TOP);
2203 
2204 	return true;
2205 }
2206 
moveto_inventory(Obj * obj,uint16 actor_num)2207 bool ObjManager::moveto_inventory(Obj *obj, uint16 actor_num) {
2208 	ActorManager *am = Game::get_game()->get_actor_manager();
2209 	if (!am)
2210 		return false;
2211 
2212 	return moveto_inventory(obj, am->get_actor(actor_num));
2213 }
2214 
moveto_inventory(Obj * obj,Actor * actor)2215 bool ObjManager::moveto_inventory(Obj *obj, Actor *actor) {
2216 	unlink_from_engine(obj);
2217 	actor->inventory_add_object(obj);
2218 
2219 	return true;
2220 }
2221 
moveto_container(Obj * obj,Obj * container_obj,bool stack)2222 bool ObjManager::moveto_container(Obj *obj, Obj *container_obj, bool stack) {
2223 	if (obj == container_obj)
2224 		return false;
2225 
2226 	unlink_from_engine(obj);
2227 	container_obj->add(obj, stack);
2228 	if (game_type == NUVIE_GAME_SE) {
2229 		if (container_obj->obj_n == OBJ_SE_JAR) { // frame changes depending on contents
2230 			switch (obj->obj_n) {
2231 			case OBJ_SE_CORN_MEAL:
2232 			case OBJ_SE_CORN:
2233 			case OBJ_SE_SULFUR:
2234 				container_obj->frame_n = 1; // yellow jar
2235 				break;
2236 			case OBJ_SE_MAGNESIUM_RIBBON:
2237 			case OBJ_SE_POTASSIUM_NITRATE:
2238 				container_obj->frame_n = 2; // white jar
2239 				break;
2240 			default:
2241 				container_obj->frame_n = 3; // black jar
2242 				break;
2243 			}
2244 		}
2245 	}
2246 	return true;
2247 }
2248 
2249 } // End of namespace Nuvie
2250 } // End of namespace Ultima
2251