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/misc/u6_misc.h"
25 #include "ultima/nuvie/conf/configuration.h"
26 #include "ultima/nuvie/files/nuvie_file_list.h"
27 #include "ultima/nuvie/save/obj_list.h"
28
29 #include "ultima/nuvie/actors/actor.h"
30 #include "ultima/nuvie/actors/u6_actor.h"
31 #include "ultima/nuvie/actors/se_actor.h"
32 #include "ultima/nuvie/actors/md_actor.h"
33 #include "ultima/nuvie/actors/u6_work_types.h"
34 #include "ultima/nuvie/core/tile_manager.h"
35 #include "ultima/nuvie/misc/u6_llist.h"
36 #include "ultima/nuvie/actors/actor_manager.h"
37 #include "ultima/nuvie/files/nuvie_io_file.h"
38 #include "ultima/nuvie/core/game_clock.h"
39 #include "ultima/nuvie/core/game.h"
40 #include "ultima/nuvie/core/party.h"
41 #include "ultima/nuvie/portraits/portrait.h"
42 #include "ultima/nuvie/script/script.h"
43 #include "ultima/nuvie/core/u6_objects.h"
44 #include "ultima/nuvie/gui/widgets/map_window.h"
45 #include "ultima/nuvie/views/view_manager.h"
46
47 namespace Ultima {
48 namespace Nuvie {
49
50 #define ACTOR_TEMP_INIT 255
51 #define SCHEDULE_SIZE 5
52
53 void config_get_path(Configuration *config, Std::string filename, Std::string &path);
54
ActorManager(Configuration * cfg,Map * m,TileManager * tm,ObjManager * om,GameClock * c)55 ActorManager::ActorManager(Configuration *cfg, Map *m, TileManager *tm, ObjManager *om, GameClock *c) {
56 uint16 i;
57
58 config = cfg;
59 map = m;
60 tile_manager = tm;
61 obj_manager = om;
62 clock = c;
63
64 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++)
65 actors[i] = NULL;
66 temp_actor_offset = 224;
67 init();
68 }
69
~ActorManager()70 ActorManager::~ActorManager() {
71 clean();
72 }
73
init()74 void ActorManager::init() {
75 player_actor = 1;
76
77 last_obj_blk_x = cur_x = 0;
78 last_obj_blk_y = cur_y = 0;
79 last_obj_blk_z = cur_z = OBJ_TEMP_INIT;
80 cmp_actor_loc = 0;
81
82 update = true;
83 wait_for_player = true;
84 combat_movement = false;
85 should_clean_temp_actors = true;
86
87 return;
88 }
89
clean()90 void ActorManager::clean() {
91 uint16 i;
92
93 //delete all actors
94 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
95 if (actors[i]) {
96 delete actors[i];
97 actors[i] = NULL;
98 }
99 }
100
101 init();
102
103 return;
104 }
105
load(NuvieIO * objlist)106 bool ActorManager::load(NuvieIO *objlist) {
107 uint16 i;
108 uint8 b1, b2, b3;
109 int game_type;
110
111 clean();
112
113 config->value("config/GameType", game_type);
114
115 objlist->seek(0x100); // Start of Actor position info
116 if (game_type == NUVIE_GAME_U6)
117 temp_actor_offset = 203;
118 else
119 temp_actor_offset = 224;
120
121 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
122 switch (game_type) {
123 case NUVIE_GAME_U6 :
124 actors[i] = new U6Actor(map, obj_manager, clock);
125 break;
126 case NUVIE_GAME_MD :
127 actors[i] = new MDActor(map, obj_manager, clock);
128 break;
129 case NUVIE_GAME_SE :
130 actors[i] = new SEActor(map, obj_manager, clock);
131 break;
132 }
133
134 b1 = objlist->read1();
135 b2 = objlist->read1();
136 b3 = objlist->read1();
137
138 actors[i]->x = b1;
139 actors[i]->x += (b2 & 0x3) << 8;
140
141 actors[i]->y = (b2 & 0xfc) >> 2;
142 actors[i]->y += (b3 & 0xf) << 6;
143
144 actors[i]->z = (b3 & 0xf0) >> 4;
145 actors[i]->id_n = (uint8)i;
146
147 actors[i]->temp_actor = is_temp_actor(actors[i]->id_n);
148 }
149
150 // objlist.seek(0x15f1);
151
152 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
153 b1 = objlist->read1();
154 b2 = objlist->read1();
155 actors[i]->obj_n = b1;
156 actors[i]->obj_n += (b2 & 0x3) << 8;
157
158 actors[i]->frame_n = (b2 & 0xfc) >> 2;
159 actors[i]->direction = actors[i]->frame_n / 4;
160 if (actors[i]->obj_n == 0) { //Hack to get rid of Exodus.
161 actors[i]->x = 0;
162 actors[i]->y = 0;
163 actors[i]->z = 0;
164 }
165 }
166
167 // Object flags.
168
169 objlist->seek(0x000);
170
171 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
172 actors[i]->obj_flags = objlist->read1();
173 }
174
175 // Actor status flags.
176
177 objlist->seek(0x800);
178
179 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
180 actors[i]->status_flags = objlist->read1();
181 actors[i]->alignment = ((actors[i]->status_flags & ACTOR_STATUS_ALIGNMENT_MASK) >> 5) + 1;
182 }
183
184 //old obj_n & frame_n values
185
186 objlist->seek(game_type == NUVIE_GAME_U6 ? 0x15f1 : 0x16f1);
187
188 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
189 b1 = objlist->read1();
190 b2 = objlist->read1();
191 actors[i]->base_obj_n = b1;
192 actors[i]->base_obj_n += (b2 & 0x3) << 8;
193
194 actors[i]->old_frame_n = (b2 & 0xfc) >> 2;
195
196 if (actors[i]->obj_n == 0) {
197 //actors[i]->obj_n = actors[i]->base_obj_n;
198 //actors[i]->frame_n = actors[i]->old_frame_n;
199 actors[i]->hide();
200 }
201
202 if (actors[i]->base_obj_n == 0) {
203 actors[i]->base_obj_n = actors[i]->obj_n;
204 actors[i]->old_frame_n = actors[i]->frame_n;
205 }
206 }
207 // Strength
208
209 objlist->seek(0x900);
210
211 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
212 actors[i]->strength = objlist->read1();
213 }
214
215 // Dexterity
216
217 objlist->seek(0xa00);
218
219 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
220 actors[i]->dex = objlist->read1();
221 }
222
223 // Intelligence
224
225 objlist->seek(0xb00);
226
227 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
228 actors[i]->intelligence = objlist->read1();
229 }
230
231 // Experience
232
233 objlist->seek(0xc00);
234
235 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
236 actors[i]->exp = objlist->read2();
237 }
238
239 // Health
240
241 objlist->seek(0xe00);
242
243 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
244 actors[i]->hp = objlist->read1();
245 }
246
247 // Experience Level
248
249 objlist->seek(0xff1);
250
251 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
252 actors[i]->level = objlist->read1();
253 }
254
255
256 // Combat mode
257
258 objlist->seek(0x12f1);
259
260 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
261 switch (game_type) {
262 case NUVIE_GAME_U6 :
263 actors[i]->combat_mode = objlist->read1();
264 break;
265
266 case NUVIE_GAME_MD : // FIXME not sure what this is supposed to be
267 case NUVIE_GAME_SE :
268 actors[i]->magic = objlist->read1();
269 break;
270 }
271 }
272
273 // Magic Points
274
275 objlist->seek(0x13f1);
276
277 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
278 switch (game_type) {
279 case NUVIE_GAME_U6 :
280 actors[i]->magic = objlist->read1();
281 break;
282
283 case NUVIE_GAME_MD :
284 case NUVIE_GAME_SE :
285 actors[i]->combat_mode = objlist->read1();
286 break;
287 }
288 }
289
290 if (game_type == NUVIE_GAME_U6) {
291 objlist->seek(OBJLIST_OFFSET_U6_TALK_FLAGS); // Start of Talk flags
292 } else {
293 objlist->seek(OBJLIST_OFFSET_MD_TALK_FLAGS); //MD talk flags location. FIXME: check SE
294 }
295
296 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
297 actors[i]->talk_flags = objlist->read1();
298 }
299
300 objlist->seek(game_type == NUVIE_GAME_MD ? OBJLIST_OFFSET_MD_MOVEMENT_FLAGS : OBJLIST_OFFSET_U6_MOVEMENT_FLAGS);
301
302 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) { //movement flags.
303 actors[i]->movement_flags = objlist->read1();
304 }
305
306 loadActorSchedules();
307
308 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
309 actors[i]->inventory_parse_readied_objects();
310
311 actors[i]->init(); //let the actor object do some init
312 }
313
314 // Moves
315
316 objlist->seek(game_type == NUVIE_GAME_MD ? OBJLIST_OFFSET_MD_MOVEMENT_POINTS : OBJLIST_OFFSET_U6_MOVEMENT_POINTS);
317
318 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
319 actors[i]->moves = objlist->read1();
320 }
321
322 // Current Worktype
323
324 objlist->seek(0x11f1);
325
326 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
327 actors[i]->set_worktype(objlist->read1(), true);
328 }
329
330 //cleanup party actor if not currently set as the player.
331
332 if (actors[ACTOR_VEHICLE_ID_N]->get_worktype() != ACTOR_WT_PLAYER) {
333 Actor *a = actors[ACTOR_VEHICLE_ID_N];
334 a->set_obj_n(0);
335 a->x = 0;
336 a->y = 0;
337 a->z = 0;
338 //a->status_flags = ACTOR_STATUS_DEAD;
339 //a->hide();
340 }
341
342 updateSchedules();
343 loadCustomTiles(game_type);
344
345 return true;
346 }
347
save(NuvieIO * objlist)348 bool ActorManager::save(NuvieIO *objlist) {
349 uint16 i;
350 uint8 b;
351 int game_type;
352
353 config->value("config/GameType", game_type);
354
355 objlist->seek(0x100); // Start of Actor position info
356
357 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
358 objlist->write1(actors[i]->x & 0xff);
359
360 b = actors[i]->x >> 8;
361 b += actors[i]->y << 2;
362 objlist->write1(b);
363
364 b = actors[i]->y >> 6;
365 b += actors[i]->z << 4;
366 objlist->write1(b);
367 }
368
369 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
370 objlist->write1(actors[i]->obj_n & 0xff);
371 b = actors[i]->obj_n >> 8;
372 b += actors[i]->frame_n << 2;
373 objlist->write1(b);
374 }
375
376 //old obj_n & frame_n values
377
378 objlist->seek(game_type == NUVIE_GAME_U6 ? 0x15f1 : 0x16f1);
379
380 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
381 objlist->write1(actors[i]->base_obj_n & 0xff);
382 b = actors[i]->base_obj_n >> 8;
383 b += actors[i]->old_frame_n << 2;
384 objlist->write1(b);
385 }
386
387 // Strength
388
389 objlist->seek(0x900);
390
391 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
392 objlist->write1(actors[i]->strength);
393 }
394
395 // Dexterity
396
397 objlist->seek(0xa00);
398
399 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
400 objlist->write1(actors[i]->dex);
401 }
402
403 // Intelligence
404
405 objlist->seek(0xb00);
406
407 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
408 objlist->write1(actors[i]->intelligence);
409 }
410
411 // Experience
412
413 objlist->seek(0xc00);
414
415 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
416 objlist->write2(actors[i]->exp);
417 }
418
419 // Health
420
421 objlist->seek(0xe00);
422
423 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
424 objlist->write1(actors[i]->hp);
425 }
426
427 // Experience Level
428
429 objlist->seek(0xff1);
430
431 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
432 objlist->write1(actors[i]->level);
433 }
434
435
436 // Combat mode
437
438 objlist->seek(0x12f1);
439
440 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
441 switch (game_type) {
442 case NUVIE_GAME_U6 :
443 objlist->write1(actors[i]->combat_mode);
444 break;
445
446 case NUVIE_GAME_MD : // FIXME not sure what this is supposed to be
447 case NUVIE_GAME_SE :
448 objlist->write1(actors[i]->magic);
449 break;
450 }
451 }
452
453 // Magic Points
454
455 objlist->seek(0x13f1);
456
457 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
458 switch (game_type) {
459 case NUVIE_GAME_U6 :
460 objlist->write1(actors[i]->magic);
461 break;
462
463 case NUVIE_GAME_MD :
464 case NUVIE_GAME_SE :
465 objlist->write1(actors[i]->combat_mode);
466 break;
467 }
468 }
469
470 // Moves
471
472 objlist->seek(0x14f1);
473
474 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
475 objlist->write1(actors[i]->moves);
476 }
477
478 objlist->seek(0); // Start of Obj flags
479
480 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
481 objlist->write1(actors[i]->obj_flags);
482 }
483
484 objlist->seek(0x800); // Start of Status flags
485
486 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
487 actors[i]->status_flags &= 0x9f;
488 actors[i]->status_flags |= (actors[i]->alignment - 1) << 5;
489 objlist->write1(actors[i]->status_flags);
490 }
491
492 if (game_type == NUVIE_GAME_U6) {
493 objlist->seek(0x17f1); // Start of Talk flags
494 } else {
495 objlist->seek(0x18f1); //MD talk flags location. FIXME: check SE
496 }
497
498 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
499 objlist->write1(actors[i]->talk_flags);
500 }
501
502 objlist->seek(0x19f1);
503
504 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) { //movement flags.
505 objlist->write1(actors[i]->movement_flags);
506 }
507 /*
508 for(i=0;i < ACTORMANAGER_MAX_ACTORS; i++)
509 {
510 actors[i]->inventory_parse_readied_objects();
511
512 actors[i]->init(); //let the actor object do some init
513 }
514 */
515 // Current Worktype
516
517 objlist->seek(0x11f1);
518
519 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
520 objlist->write1(actors[i]->get_worktype());
521 }
522
523 return true;
524 }
525
get_actor_list()526 ActorList *ActorManager::get_actor_list() {
527 ActorList *_actors = new ActorList(ACTORMANAGER_MAX_ACTORS);
528 for (uint16 i = 0; i < ACTORMANAGER_MAX_ACTORS; i++)
529 (*_actors)[i] = actors[i];
530 return _actors;
531 }
532
get_actor(uint8 actor_num)533 Actor *ActorManager::get_actor(uint8 actor_num) {
534 return actors[actor_num];
535 }
536
get_actor(uint16 x,uint16 y,uint8 z,bool inc_surrounding_objs,Actor * excluded_actor)537 Actor *ActorManager::get_actor(uint16 x, uint16 y, uint8 z, bool inc_surrounding_objs, Actor *excluded_actor) {
538 uint16 i;
539
540 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
541 if (actors[i]->x == x && actors[i]->y == y && actors[i]->z == z && actors[i] != excluded_actor)
542 return actors[i];
543 }
544
545 if (inc_surrounding_objs) {
546 Obj *obj = obj_manager->get_obj(x, y, z);
547 if (obj && obj->is_actor_obj()) {
548 if (obj->obj_n == OBJ_U6_SILVER_SERPENT && Game::get_game()->get_game_type() == NUVIE_GAME_U6)
549 return actors[obj->qty];
550
551 return actors[obj->quality];
552 }
553
554 return get_multi_tile_actor(x, y, z);
555 }
556
557 return NULL;
558 }
559
get_multi_tile_actor(uint16 x,uint16 y,uint8 z)560 Actor *ActorManager::get_multi_tile_actor(uint16 x, uint16 y, uint8 z) {
561 Actor *actor = get_actor(x + 1, y + 1, z, false); //search for 2x2 tile actor.
562 if (actor) {
563 Tile *tile = actor->get_tile();
564 if (tile->dbl_width && tile->dbl_height)
565 return actor;
566 }
567
568 actor = get_actor(x, y + 1, z, false); //search for 1x2 tile actor.
569 if (actor) {
570 Tile *tile = actor->get_tile();
571 if (tile->dbl_height)
572 return actor;
573 }
574
575 actor = get_actor(x + 1, y, z, false); //search for 1x2 tile actor.
576 if (actor) {
577 Tile *tile = actor->get_tile();
578 if (tile->dbl_width)
579 return actor;
580 }
581
582
583 return NULL;
584 }
585
get_avatar()586 Actor *ActorManager::get_avatar() {
587 return get_actor(ACTOR_AVATAR_ID_N);
588 }
589
get_player()590 Actor *ActorManager::get_player() {
591 return actors[player_actor]; //FIX here for dead party leader etc.
592 }
593
set_player(Actor * a)594 void ActorManager::set_player(Actor *a) {
595 player_actor = a->id_n;
596 }
597
598 /* Returns an actor's "look-string," a general description of their occupation
599 * or appearance. (the tile description)
600 */
look_actor(Actor * a,bool show_prefix)601 const char *ActorManager::look_actor(Actor *a, bool show_prefix) {
602 uint16 tile_num = obj_manager->get_obj_tile_num(a->base_obj_n);
603 if (tile_num == 0) {
604 uint8 actor_num = a->id_n;
605 if (actor_num == 191) // U6: Statue of Exodus
606 return tile_manager->lookAtTile(obj_manager->get_obj_tile_num(399), 0, show_prefix);
607 else if (actor_num == 189) // Statue of Mondain
608 return tile_manager->lookAtTile(obj_manager->get_obj_tile_num(397), 0, show_prefix);
609 else if (actor_num == 190) // Statue of Minax
610 return tile_manager->lookAtTile(obj_manager->get_obj_tile_num(398), 0, show_prefix);
611 else if (a->id_n >= 192 && a->id_n <= 200) // shrines
612 return tile_manager->lookAtTile(obj_manager->get_obj_tile_num(393), 0, show_prefix);
613
614 return tile_manager->lookAtTile(obj_manager->get_obj_tile_num(a->obj_n), 0, show_prefix);
615 }
616 return tile_manager->lookAtTile(tile_num, 0, show_prefix);
617 }
618
619 // Update area, and spawn or remove actors.
updateActors(uint16 x,uint16 y,uint8 z)620 void ActorManager::updateActors(uint16 x, uint16 y, uint8 z) {
621 // uint8 cur_hour;
622 // uint16 i;
623
624 // if(!update)
625 // return;
626 //DEBUG(0,LEVEL_DEBUGGING,"updateActors()\n");
627
628 cur_x = x;
629 cur_y = y;
630 cur_z = z;
631
632 uint16 cur_blk_x = x >> 3; // x / 8;
633 uint16 cur_blk_y = y >> 3; // y / 8;
634
635 update_temp_actors(x, y, z); // Remove out of range temp actors
636
637 last_obj_blk_x = cur_blk_x; // moved from update_temp_actors() (SB-X)
638 last_obj_blk_y = cur_blk_y;
639 last_obj_blk_z = z;
640
641 return;
642 }
643
644 // After player/party moves, continue moving actors.
startActors()645 void ActorManager::startActors() {
646 //DEBUG(0,LEVEL_DEBUGGING,"startActors()\n");
647
648 wait_for_player = false;
649 //ERIC Game::get_game()->pause_user();
650 }
651
updateSchedules(bool teleport)652 void ActorManager::updateSchedules(bool teleport) {
653 uint8 cur_hour = clock->get_hour();
654
655 for (int i = 0; i < ACTORMANAGER_MAX_ACTORS; i++)
656 if (!actors[i]->is_in_party()) // don't do scheduled activities while partying
657 actors[i]->updateSchedule(cur_hour, teleport);
658 }
659
twitchActors()660 void ActorManager::twitchActors() {
661 uint16 i;
662
663 // while Actors are part of the world, their twitching is considered animation
664 if (Game::get_game()->anims_paused())
665 return;
666
667 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++)
668 actors[i]->twitch();
669
670 }
671
672 // Update actors. StopActors() if no one can move.
moveActors()673 void ActorManager::moveActors() {
674 if (!update || wait_for_player) {
675 return;// nothing to do
676 }
677
678 Game::get_game()->pause_user();
679 Game::get_game()->get_script()->call_actor_update_all();
680 Game::get_game()->get_map_window()->updateAmbience();
681 Game::get_game()->get_view_manager()->update();
682 //updateTime();
683 Game::get_game()->unpause_user();
684 wait_for_player = true;
685
686 return;
687 }
688
loadActorSchedules()689 bool ActorManager::loadActorSchedules() {
690 Std::string filename;
691 NuvieIOFileRead schedule;
692 uint16 i;
693 uint16 total_schedules;
694 uint16 num_schedules[ACTORMANAGER_MAX_ACTORS]; // an array to hold the number of schedule entries for each Actor.
695 uint32 bytes_read;
696 unsigned char *sched_data;
697 uint16 *sched_offsets;
698 unsigned char *s_ptr;
699
700 config_get_path(config, "schedule", filename);
701 if (schedule.open(filename) == false)
702 return false;
703
704 sched_offsets = (uint16 *)malloc(ACTORMANAGER_MAX_ACTORS * sizeof(uint16));
705
706 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++)
707 sched_offsets[i] = schedule.read2();
708
709 total_schedules = schedule.read2();
710
711 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
712 //if (sched_offsets[i] == 0)
713 // num_schedules[i] = 0;
714 //else
715 if (sched_offsets[i] > (total_schedules - 1))
716 num_schedules[i] = 0;
717 else
718 // sched_offsets[i] is valid
719 {
720 if (i == ACTORMANAGER_MAX_ACTORS - 1)
721 num_schedules[i] = total_schedules - sched_offsets[i];
722 else if (sched_offsets[i + 1] > (total_schedules - 1))
723 num_schedules[i] = total_schedules - sched_offsets[i];
724 else
725 // sched_offsets[i+1] is valid
726 num_schedules[i] = sched_offsets[i + 1] - sched_offsets[i];
727 }
728 }
729
730 sched_data = schedule.readBuf(total_schedules * SCHEDULE_SIZE, &bytes_read);
731
732 if (!sched_data || bytes_read != (uint32)(total_schedules * SCHEDULE_SIZE)) {
733 if (sched_data)
734 free(sched_data);
735 free(sched_offsets);
736 DEBUG(0, LEVEL_ERROR, "Failed to read schedules!\n");
737 return false;
738 }
739
740 for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
741 s_ptr = sched_data + (sched_offsets[i] * SCHEDULE_SIZE);
742 actors[i]->loadSchedule(s_ptr, num_schedules[i]);
743 }
744
745 free(sched_data);
746 free(sched_offsets);
747
748 return true;
749 }
750
clear_actor(Actor * actor)751 void ActorManager::clear_actor(Actor *actor) {
752 if (is_temp_actor(actor))
753 clean_temp_actor(actor);
754 else
755 actor->clear();
756
757 return;
758 }
759
resurrect_actor(Obj * actor_obj,MapCoord new_position)760 bool ActorManager::resurrect_actor(Obj *actor_obj, MapCoord new_position) {
761 Actor *actor;
762
763 if (!is_temp_actor(actor_obj->quality)) {
764 actor = get_actor(actor_obj->quality);
765 actor->resurrect(new_position, actor_obj);
766 }
767
768 return true;
769 }
770
is_temp_actor(Actor * actor)771 bool ActorManager::is_temp_actor(Actor *actor) {
772 if (actor)
773 return is_temp_actor(actor->id_n);
774
775 return false;
776 }
777
is_temp_actor(uint8 id_n)778 bool ActorManager::is_temp_actor(uint8 id_n) {
779 if (id_n >= temp_actor_offset)
780 return true;
781
782 return false;
783 }
784
create_temp_actor(uint16 obj_n,uint8 obj_status,uint16 x,uint16 y,uint8 z,uint8 alignment,uint8 worktype,Actor ** new_actor)785 bool ActorManager::create_temp_actor(uint16 obj_n, uint8 obj_status, uint16 x, uint16 y, uint8 z, uint8 alignment, uint8 worktype, Actor **new_actor) {
786 Actor *actor;
787 actor = find_free_temp_actor();
788
789
790 if (actor) {
791 actor->inventory_del_all_objs(); //We need to do this because the original game doesn't unset inventory flag when temp actors die.
792
793 actor->base_obj_n = obj_n;
794 actor->obj_n = obj_n;
795 actor->frame_n = 0;
796
797 actor->x = x;
798 actor->y = y;
799 actor->z = z;
800
801 actor->temp_actor = true;
802
803 actor->obj_flags = 0;
804 actor->status_flags = 0;
805 actor->talk_flags = 0;
806 actor->movement_flags = 0;
807 actor->alignment = ACTOR_ALIGNMENT_NEUTRAL;
808
809 actor->init(obj_status);
810
811 Game::get_game()->get_script()->call_actor_init(actor, alignment);
812
813 // spawn double-tiled actors, like cows, facing west (SB-X)
814 if (actor->get_tile_type() == ACTOR_DT)
815 actor->set_direction(-1, 0);
816 //if(worktype != 0)
817 actor->set_worktype(worktype);
818 actor->show();
819
820 DEBUG(0, LEVEL_INFORMATIONAL, "Adding Temp Actor #%d: %s (%x,%x,%x).\n", actor->id_n, tile_manager->lookAtTile(obj_manager->get_obj_tile_num(actor->obj_n) + actor->frame_n, 0, false), actor->x, actor->y, actor->z);
821
822 if (new_actor)
823 *new_actor = actor;
824 actor->handle_lightsource(clock->get_hour());
825 return true;
826 } else
827 DEBUG(0, LEVEL_NOTIFICATION, "***All Temp Actor Slots Full***\n");
828
829 if (new_actor)
830 *new_actor = NULL;
831 return false;
832 }
833
find_free_temp_actor()834 inline Actor *ActorManager::find_free_temp_actor() {
835 uint16 i;
836
837 for (i = temp_actor_offset; i < ACTORMANAGER_MAX_ACTORS; i++) {
838 if (actors[i]->obj_n == 0)
839 return actors[i];
840 }
841
842 return NULL;
843 }
844
845 //FIX? should this be in Player??
846 // NO!
update_temp_actors(uint16 x,uint16 y,uint8 z)847 void ActorManager::update_temp_actors(uint16 x, uint16 y, uint8 z) {
848 uint16 cur_blk_x, cur_blk_y;
849
850 // We're changing levels so clean out all temp actors on the current level.
851 if (last_obj_blk_z != z) {
852 if (last_obj_blk_z != ACTOR_TEMP_INIT) { //don't clean actors on startup.
853 clean_temp_actors_from_level(last_obj_blk_z);
854 return;
855 }
856 // last_obj_blk_z = z;
857 }
858
859 cur_blk_x = x >> 3; // x / 8;
860 cur_blk_y = y >> 3; // y / 8;
861
862 if (cur_blk_x != last_obj_blk_x || cur_blk_y != last_obj_blk_y) {
863 // last_obj_blk_x = cur_blk_x;
864 // last_obj_blk_y = cur_blk_y;
865
866 clean_temp_actors_from_area(x, y);
867 }
868
869 return;
870 }
871
clean_temp_actors_from_level(uint8 level)872 void ActorManager::clean_temp_actors_from_level(uint8 level) {
873 uint16 i;
874
875 for (i = temp_actor_offset; i < ACTORMANAGER_MAX_ACTORS; i++) {
876 if ((actors[i]->is_visible() || actors[i]->x != 0 || actors[i]->y != 0 || actors[i]->z != 0)
877 && actors[i]->is_in_party() == false && actors[i]->z == level)
878 clean_temp_actor(actors[i]);
879 }
880
881 return;
882 }
883
clean_temp_actors_from_area(uint16 x,uint16 y)884 void ActorManager::clean_temp_actors_from_area(uint16 x, uint16 y) {
885 uint16 i;
886 uint16 dist_x, dist_y;
887
888 if (!should_clean_temp_actors)
889 return;
890
891 for (i = temp_actor_offset; i < ACTORMANAGER_MAX_ACTORS; i++) {
892 if ((actors[i]->is_visible() || actors[i]->x != 0 || actors[i]->y != 0 || actors[i]->z != 0)
893 && actors[i]->is_in_party() == false) {
894 dist_x = abs((sint16)actors[i]->x - x);
895 dist_y = abs((sint16)actors[i]->y - y);
896
897 if (dist_x > 19 || dist_y > 19) {
898 clean_temp_actor(actors[i]);
899 }
900 }
901 }
902
903 return;
904 }
905
clean_temp_actor(Actor * actor)906 inline void ActorManager::clean_temp_actor(Actor *actor) {
907 DEBUG(0, LEVEL_INFORMATIONAL, "Removing Temp Actor #%d: %s (%x,%x,%x).\n", actor->id_n, tile_manager->lookAtTile(obj_manager->get_obj_tile_num(actor->obj_n) + actor->frame_n, 0, false), actor->x, actor->y, actor->z);
908 actor->obj_n = 0;
909 actor->clear();
910
911 return;
912 }
913
clone_actor(Actor * actor,Actor ** new_actor,MapCoord new_location)914 bool ActorManager::clone_actor(Actor *actor, Actor **new_actor, MapCoord new_location) {
915 if (actor == NULL)
916 return false;
917
918 if (create_temp_actor(actor->obj_n, NO_OBJ_STATUS, new_location.x, new_location.y, new_location.z, actor->alignment, actor->worktype, new_actor) == false)
919 return false;
920
921 (*new_actor)->strength = actor->strength;
922 (*new_actor)->dex = actor->dex;
923 (*new_actor)->intelligence = actor->intelligence;
924
925 (*new_actor)->magic = actor->magic;
926 (*new_actor)->exp = actor->exp;
927 (*new_actor)->hp = actor->hp;
928
929 return true;
930 }
931
932 /* Move an actor to a random location within range.
933 * Returns true when tossed.
934 */
toss_actor(Actor * actor,uint16 xrange,uint16 yrange)935 bool ActorManager::toss_actor(Actor *actor, uint16 xrange, uint16 yrange) {
936 // maximum number of tries
937 const uint32 toss_max = MAX(xrange, yrange) * MIN(xrange, yrange) * 2;
938 uint32 t = 0;
939 LineTestResult lt;
940 if (xrange > 0) --xrange; // range includes the starting location
941 if (yrange > 0) --yrange;
942 while (t++ < toss_max) { // TRY RANDOM LOCATION
943 sint16 x = (actor->x - xrange) + (NUVIE_RAND() % ((actor->x + xrange) - (actor->x - xrange) + 1)),
944 y = (actor->y - yrange) + (NUVIE_RAND() % ((actor->y + yrange) - (actor->y - yrange) + 1));
945 if (!map->lineTest(actor->x, actor->y, x, y, actor->z, LT_HitUnpassable, lt))
946 if (!get_actor(x, y, actor->z))
947 return (actor->move(x, y, actor->z));
948 }
949 // TRY ANY LOCATION
950 for (int y = actor->y - yrange; y < actor->y + yrange; y++)
951 for (int x = actor->x - xrange; x < actor->x + xrange; x++)
952 if (!map->lineTest(actor->x, actor->y, x, y, actor->z, LT_HitUnpassable, lt))
953 if (!get_actor(x, y, actor->z))
954 return (actor->move(x, y, actor->z));
955 return (false);
956 }
957
958 /* Find a location to put actor within range.
959 * Returns true when tossed.
960 */
toss_actor_get_location(uint16 start_x,uint16 start_y,uint8 start_z,uint16 xrange,uint16 yrange,MapCoord * location)961 bool ActorManager::toss_actor_get_location(uint16 start_x, uint16 start_y, uint8 start_z, uint16 xrange, uint16 yrange, MapCoord *location) {
962 // maximum number of tries
963 const uint32 toss_max = MAX(xrange, yrange) * MIN(xrange, yrange) * 2;
964 uint32 t = 0;
965 LineTestResult lt;
966 if (xrange > 0) --xrange; // range includes the starting location
967 if (yrange > 0) --yrange;
968 while (t++ < toss_max) { // TRY RANDOM LOCATION
969 sint16 x = (start_x - xrange) + (NUVIE_RAND() % ((start_x + xrange) - (start_x - xrange) + 1)),
970 y = (start_y - yrange) + (NUVIE_RAND() % ((start_y + yrange) - (start_y - yrange) + 1));
971 if (!map->lineTest(start_x, start_y, x, y, start_z, LT_HitUnpassable, lt)) {
972 if (!get_actor(x, y, start_z)) {
973 location->x = x;
974 location->y = y;
975 location->z = start_z;
976 return can_put_actor(*location);
977 }
978
979 }
980 }
981 // TRY ANY LOCATION
982 for (int y = start_y - yrange; y < start_y + yrange; y++)
983 for (int x = start_x - xrange; x < start_x + xrange; x++)
984 if (!map->lineTest(start_x, start_y, x, y, start_z, LT_HitUnpassable, lt)) {
985 if (!get_actor(x, y, start_z)) {
986 location->x = x;
987 location->y = y;
988 location->z = start_z;
989 return can_put_actor(*location);
990 }
991 }
992
993 return (false);
994 }
995
996
997 /* Returns the actor whose inventory contains an object. */
get_actor_holding_obj(Obj * obj)998 Actor *ActorManager::get_actor_holding_obj(Obj *obj) {
999 assert(obj->is_in_inventory());
1000
1001 while (obj->is_in_container())
1002 obj = obj->get_container_obj();
1003 return (Actor *)obj->parent;
1004 }
1005
1006 // Remove list actors who fall out of a certain range from a location.
filter_distance(ActorList * list,uint16 x,uint16 y,uint8 z,uint16 dist)1007 ActorList *ActorManager::filter_distance(ActorList *list, uint16 x, uint16 y, uint8 z, uint16 dist) {
1008 ActorIterator i = list->begin();
1009 while (i != list->end()) {
1010 Actor *actor = *i;
1011 MapCoord loc(x, y, z);
1012 MapCoord actor_loc(actor->x, actor->y, actor->z);
1013 if (loc.distance(actor_loc) > dist || loc.z != actor_loc.z)
1014 i = list->erase(i);
1015 else ++i;
1016 }
1017 return list;
1018 }
1019
1020 // Remove actors who don't need to move in this turn. That includes anyone not
1021 // in a certain range of xyz, and actors that are already out of moves.
filter_active_actors(ActorList * list,uint16 x,uint16 y,uint8 z)1022 inline ActorList *ActorManager::filter_active_actors(ActorList *list, uint16 x, uint16 y, uint8 z) {
1023 const uint8 dist = 24;
1024 ActorIterator i = list->begin();
1025 while (i != list->end()) {
1026 Actor *actor = *i;
1027 MapCoord loc(x, y, z);
1028 MapCoord actor_loc(actor->x, actor->y, actor->z);
1029 if (!actor->is_in_party()) {
1030 if ((loc.distance(actor_loc) > dist || loc.z != actor_loc.z)
1031 && actor->worktype != WORKTYPE_U6_WALK_TO_LOCATION)
1032 actor->set_moves_left(0);
1033 if (actor->is_sleeping() || actor->is_immobile() || !actor->is_alive())
1034 actor->set_moves_left(0);
1035 }
1036 if ((actor->is_in_party() == true && combat_movement == false) || actor->moves <= 0)
1037 i = list->erase(i);
1038 else ++i;
1039 }
1040 return list;
1041 }
1042
1043 // Sort list by distance to a location. Remove actors on different planes.
sort_nearest(ActorList * list,uint16 x,uint16 y,uint8 z)1044 ActorList *ActorManager::sort_nearest(ActorList *list, uint16 x, uint16 y, uint8 z) {
1045 struct Actor::cmp_distance_to_loc cmp_func; // comparison function object
1046 MapCoord loc(x, y, z);
1047 cmp_func(loc); // set location in function object
1048 Common::sort(list->begin(), list->end(), cmp_func);
1049
1050 ActorIterator a = list->begin();
1051 while (a != list->end())
1052 if ((*a)->z != z)
1053 a = list->erase(a); // only return actors on the same map
1054 else ++a;
1055 return list;
1056 }
1057
print_actor(Actor * actor)1058 void ActorManager::print_actor(Actor *actor) {
1059 actor->print();
1060 }
1061
can_put_actor(MapCoord location)1062 bool ActorManager::can_put_actor(MapCoord location) {
1063 if (!map->is_passable(location.x, location.y, location.z))
1064 return false;
1065
1066 if (get_actor(location.x, location.y, location.z) != NULL)
1067 return false;
1068
1069 return true;
1070 }
1071
1072 // Remove actors with a certain alignment from the list. Returns the same list.
filter_alignment(ActorList * list,uint8 align)1073 ActorList *ActorManager::filter_alignment(ActorList *list, uint8 align) {
1074 ActorIterator i = list->begin();
1075 while (i != list->end()) {
1076 Actor *actor = *i;
1077 if (actor->alignment == align)
1078 i = list->erase(i);
1079 else ++i;
1080 }
1081 return list;
1082 }
1083
1084 // Remove actors in the party. Returns the original list pointer.
filter_party(ActorList * list)1085 ActorList *ActorManager::filter_party(ActorList *list) {
1086 ActorIterator i = list->begin();
1087 while (i != list->end()) {
1088 Actor *actor = *i;
1089 if (actor->is_in_party() == true || actor->id_n == 0) // also remove vehicle
1090 i = list->erase(i);
1091 else ++i;
1092 }
1093 return list;
1094 }
1095
set_combat_movement(bool c)1096 void ActorManager::set_combat_movement(bool c) {
1097 combat_movement = c;
1098 }
1099
loadCustomTiles(nuvie_game_t game_type)1100 bool ActorManager::loadCustomTiles(nuvie_game_t game_type) {
1101 if (obj_manager->use_custom_actor_tiles() == false) {
1102 return false;
1103 }
1104
1105 Std::string datadir = "images";
1106 Std::string path;
1107
1108 build_path(datadir, "tiles", path);
1109 datadir = path;
1110 build_path(datadir, get_game_tag(game_type), path);
1111 datadir = path;
1112
1113 tile_manager->freeCustomTiles(); //FIXME this might need to change if we start using custom tiles outside of ActorManager. eg custom map/object tilesets
1114
1115 loadCustomBaseTiles(datadir);
1116 loadAvatarTiles(datadir);
1117 loadNPCTiles(datadir);
1118
1119 return true;
1120 }
1121
loadCustomBaseTiles(Std::string datadir)1122 void ActorManager::loadCustomBaseTiles(Std::string datadir) {
1123 Std::string imagefile;
1124 build_path(datadir, "custom_tiles.bmp", imagefile);
1125
1126 //attempt to load custom base tiles if the file exists.
1127 tile_manager->loadCustomTiles(Game::get_game()->get_data_file_path(imagefile), true, true, 0);
1128 }
1129
loadAvatarTiles(Std::string datadir)1130 void ActorManager::loadAvatarTiles(Std::string datadir) {
1131 Std::string imagefile;
1132
1133 uint8 avatar_portrait = Game::get_game()->get_portrait()->get_avatar_portrait_num();
1134
1135 Std::set<Std::string> files = getCustomTileFilenames(datadir, "avatar_");
1136
1137 for (Std::set<Std::string>::iterator iter = files.begin(); iter != files.end(); iter++) {
1138 Std::string filename = *iter;
1139 if (filename.length() != 19) { // avatar_nnn_nnnn.bmp
1140 continue;
1141 }
1142 Std::string num_str = filename.substr(7, 3);
1143 uint8 portrait_num = (uint8)strtol(num_str.c_str(), NULL, 10);
1144
1145 if (portrait_num == avatar_portrait) {
1146 num_str = filename.substr(11, 4);
1147 uint16 obj_n = (uint16)strtol(num_str.c_str(), NULL, 10);
1148
1149 Std::string path;
1150 build_path(datadir, filename, path);
1151 imagefile = Game::get_game()->get_data_file_path(path);
1152 Tile *start_tile = tile_manager->loadCustomTiles(imagefile, false, true, actors[1]->get_tile_num());
1153 if (start_tile) {
1154 actors[1]->set_custom_tile_num(obj_n, start_tile->tile_num);
1155 }
1156 }
1157 }
1158 return;
1159 }
1160
loadNPCTiles(Std::string datadir)1161 void ActorManager::loadNPCTiles(Std::string datadir) {
1162 Std::string imagefile;
1163
1164 Std::set<Std::string> files = getCustomTileFilenames(datadir, "actor_");
1165
1166 for (Std::set<Std::string>::iterator iter = files.begin(); iter != files.end(); iter++) {
1167 Std::string filename = *iter;
1168 if (filename.length() != 18) { // actor_nnn_nnnn.bmp
1169 continue;
1170 }
1171 Std::string num_str = filename.substr(6, 3);
1172 uint8 actor_num = (uint8)strtol(num_str.c_str(), NULL, 10);
1173
1174 num_str = filename.substr(10, 4);
1175 uint16 obj_n = (uint16)strtol(num_str.c_str(), NULL, 10);
1176
1177 Std::string path;
1178 build_path(datadir, filename, path);
1179 imagefile = Game::get_game()->get_data_file_path(path);
1180 Tile *start_tile = tile_manager->loadCustomTiles(imagefile, false, true, actors[actor_num]->get_tile_num());
1181 if (start_tile) {
1182 actors[actor_num]->set_custom_tile_num(obj_n, start_tile->tile_num);
1183 }
1184 }
1185 return;
1186 }
1187
getCustomTileFilenames(Std::string datadir,Std::string filenamePrefix)1188 Std::set<Std::string> ActorManager::getCustomTileFilenames(Std::string datadir, Std::string filenamePrefix) {
1189 NuvieFileList filelistDataDir;
1190 NuvieFileList filelistSaveGameDir;
1191 Std::string path;
1192
1193 build_path(GUI::get_gui()->get_data_dir(), datadir, path);
1194 filelistDataDir.open(path.c_str(), filenamePrefix.c_str(), NUVIE_SORT_NAME_ASC);
1195
1196 path = "data";
1197 build_path(path, datadir, path);
1198 filelistSaveGameDir.open(path.c_str(), filenamePrefix.c_str(), NUVIE_SORT_NAME_ASC);
1199
1200 Std::set<Std::string> files = filelistSaveGameDir.get_filenames();
1201 Std::set<Std::string> dataFiles = filelistDataDir.get_filenames();
1202 files.insert(dataFiles.begin(), dataFiles.end());
1203 return files;
1204 }
1205
1206 } // End of namespace Nuvie
1207 } // End of namespace Ultima
1208