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