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/misc/u6_llist.h"
26 #include "ultima/nuvie/core/game.h"
27 #include "ultima/nuvie/core/game_clock.h"
28 #include "ultima/nuvie/gui/widgets/map_window.h"
29 #include "ultima/nuvie/core/obj_manager.h"
30 #include "ultima/nuvie/actors/actor_manager.h"
31 #include "ultima/nuvie/views/view_manager.h"
32 #include "ultima/nuvie/usecode/u6_usecode.h"
33 #include "ultima/nuvie/core/party.h"
34 #include "ultima/nuvie/pathfinder/combat_path_finder.h"
35 #include "ultima/nuvie/pathfinder/seek_path.h"
36 #include "ultima/nuvie/core/converse.h"
37 #include "ultima/nuvie/core/effect.h"
38 #include "ultima/nuvie/actors/actor.h"
39 #include "ultima/nuvie/script/script.h"
40 #include "ultima/nuvie/core/events.h"
41 #include "ultima/nuvie/actors/u6_actor.h"
42 #include "ultima/shared/std/containers.h"
43
44 namespace Ultima {
45 namespace Nuvie {
46
47 uint8 walk_frame_tbl[4] = {0, 1, 2, 1};
48
49 class ActorManager;
50
Actor(Map * m,ObjManager * om,GameClock * c)51 Actor::Actor(Map *m, ObjManager *om, GameClock *c)
52 : sched(NULL), obj_inventory(NULL) {
53 map = m;
54 obj_manager = om;
55 usecode = NULL;
56 clock = c;
57 pathfinder = NULL;
58
59 direction = 0;
60 walk_frame = 0;
61 ethereal = false;
62 can_move = true;
63 temp_actor = false;
64 visible_flag = true;
65 met_player = false;
66 // active = false;
67
68 worktype = 0;
69 sched_pos = 0;
70 move_time = 0;
71 num_schedules = 0;
72
73 alignment = ACTOR_ALIGNMENT_NEUTRAL;
74
75 memset(readied_objects, 0, sizeof(readied_objects));
76 moves = 0;
77 light = 0;
78
79 name = "";
80 status_flags = 0;
81 talk_flags = 0;
82 obj_flags = 0;
83 body_armor_class = 0;
84 readied_armor_class = 0;
85
86 custom_tile_tbl = NULL;
87
88 clear_error();
89 }
90
~Actor()91 Actor::~Actor() {
92 // free sched array
93 if (sched != NULL) {
94 Schedule **cursched = sched;
95 while (*cursched != NULL)
96 free(*cursched++);
97
98 free(sched);
99 }
100 if (pathfinder)
101 delete pathfinder;
102
103 for (uint8 location = 0; location < ACTOR_MAX_READIED_OBJECTS; location++) {
104 if (readied_objects[location] != NULL) {
105 delete readied_objects[location];
106 }
107 }
108
109 if (custom_tile_tbl) {
110 delete custom_tile_tbl;
111 }
112
113 return;
114 }
115
init(uint8 obj_status)116 bool Actor::init(uint8 obj_status) {
117 if (pathfinder)
118 delete_pathfinder();
119 set_moves_left(dex);
120 return true;
121 }
122
init_from_obj(Obj * obj,bool change_base_obj)123 void Actor::init_from_obj(Obj *obj, bool change_base_obj) {
124 x = obj->x;
125 y = obj->y;
126 z = obj->z;
127
128 if (change_base_obj) {
129 base_obj_n = obj->obj_n;
130 }
131 obj_n = obj->obj_n;
132 frame_n = obj->frame_n;
133 obj_flags = obj->status;
134
135 set_dead_flag(false);
136 init();
137 show();
138 return;
139 }
140
141 /* Returns true if another NPC `n' is in proximity to location `where'.
142 */
is_nearby(MapCoord & where,uint8 thresh)143 bool Actor::is_nearby(MapCoord &where, uint8 thresh) {
144 MapCoord here(x, y, z);
145 if (here.xdistance(where) <= thresh && here.ydistance(where) <= thresh && z == where.z)
146 return (true);
147 return (false);
148 }
149
150
is_nearby(Actor * other)151 bool Actor::is_nearby(Actor *other) {
152 MapCoord there(other->get_location());
153 return (is_nearby(there));
154 }
155
156
is_nearby(uint8 actor_num)157 bool Actor::is_nearby(uint8 actor_num) {
158 return (is_nearby(Game::get_game()->get_actor_manager()->get_actor(actor_num)));
159 }
160
is_at_position(Obj * obj)161 bool Actor::is_at_position(Obj *obj) {
162 if (obj->x == x && obj->y == y && obj->z == z)
163 return true;
164
165 return false;
166 }
167
is_passable()168 bool Actor::is_passable() {
169 if (ethereal)
170 return true;
171 Tile *tile;
172
173 tile = obj_manager->get_obj_tile(obj_n, frame_n);
174
175 return tile->passable;
176 }
177
is_in_vehicle()178 bool Actor::is_in_vehicle() {
179 if (is_in_party() == false)
180 return false;
181
182 return Game::get_game()->get_party()->is_in_vehicle();
183 }
184
get_location(uint16 * ret_x,uint16 * ret_y,uint8 * ret_level)185 void Actor::get_location(uint16 *ret_x, uint16 *ret_y, uint8 *ret_level) {
186 if (ret_x) *ret_x = x;
187 if (ret_y) *ret_y = y;
188 if (ret_level) *ret_level = z;
189 }
190
191
get_location()192 MapCoord Actor::get_location() {
193 return (MapCoord(x, y, z));
194 }
195
196
get_tile_num()197 uint16 Actor::get_tile_num() {
198 if (custom_tile_tbl) {
199 return get_custom_tile_num(obj_n);
200 }
201
202 return obj_manager->get_obj_tile_num(obj_n);
203 }
204
get_tile_num(uint16 obj_num)205 uint16 Actor::get_tile_num(uint16 obj_num) {
206 if (custom_tile_tbl) {
207 return get_custom_tile_num(obj_num);
208 }
209
210 return obj_manager->get_obj_tile_num(obj_num);
211 }
212
get_custom_tile_num(uint16 obj_num)213 uint16 Actor::get_custom_tile_num(uint16 obj_num) {
214 if (custom_tile_tbl) {
215 Std::map<uint16, uint16>::iterator it;
216 it = custom_tile_tbl->find(obj_num);
217 if (it != custom_tile_tbl->end()) {
218 return it->_value;
219 }
220 }
221
222 return obj_manager->get_obj_tile_num(obj_num);
223 }
224
get_tile()225 Tile *Actor::get_tile() {
226 return Game::get_game()->get_tile_manager()->get_tile(get_tile_num() + frame_n);
227 }
228
get_worktype()229 uint8 Actor::get_worktype() {
230 return worktype;
231 }
232
get_sched_worktype()233 uint8 Actor::get_sched_worktype() {
234 if (sched[sched_pos])
235 return sched[sched_pos]->worktype;
236
237 return 0; //no worktype
238 }
239
get_downward_facing_tile_num()240 uint16 Actor::get_downward_facing_tile_num() {
241 return obj_manager->get_obj_tile_num(obj_n) + frame_n;
242 }
243
244 /* Set direction faced by actor and change walk frame. */
set_direction(uint8 d)245 void Actor::set_direction(uint8 d) {
246 if (is_alive() == false || is_immobile())
247 return;
248
249 if (d < 4)
250 direction = d;
251
252 walk_frame = (walk_frame + 1) % 4;
253
254 frame_n = direction * 4 + walk_frame_tbl[walk_frame];
255
256 }
257
258 /* Set direction as if moving in relative direction rel_x,rel_y. */
set_direction(sint16 rel_x,sint16 rel_y)259 void Actor::set_direction(sint16 rel_x, sint16 rel_y) {
260 uint8 new_direction = direction;
261 if (rel_x == 0 && rel_y == 0) // nowhere (just update frame)
262 new_direction = direction;
263 else if (rel_x == 0) // up or down
264 new_direction = (rel_y < 0) ? NUVIE_DIR_N : NUVIE_DIR_S;
265 else if (rel_y == 0) // left or right
266 new_direction = (rel_x < 0) ? NUVIE_DIR_W : NUVIE_DIR_E;
267 // Add 2 to current direction if it is opposite the new direction
268 else if (rel_x < 0 && rel_y < 0) { // up-left
269 if (direction != NUVIE_DIR_N && direction != NUVIE_DIR_W)
270 new_direction = direction + 2;
271 } else if (rel_x > 0 && rel_y < 0) { // up-right
272 if (direction != NUVIE_DIR_N && direction != NUVIE_DIR_E)
273 new_direction = direction + 2;
274 } else if (rel_x < 0 && rel_y > 0) { // down-left
275 if (direction != NUVIE_DIR_S && direction != NUVIE_DIR_W)
276 new_direction = direction + 2;
277 } else if (rel_x > 0 && rel_y > 0) { // down-right
278 if (direction != NUVIE_DIR_S && direction != NUVIE_DIR_E)
279 new_direction = direction + 2;
280 }
281 // wrap
282 if (new_direction >= 4)
283 new_direction -= 4;
284 set_direction(new_direction);
285 }
286
face_location(MapCoord & loc)287 void Actor::face_location(MapCoord &loc) {
288 face_location(loc.x, loc.y);
289 }
290
291 /* Set direction towards an x,y location on the map. */
face_location(uint16 lx,uint16 ly)292 void Actor::face_location(uint16 lx, uint16 ly) {
293 set_direction(lx - x, ly - y);
294 }
295 #if 0
296 /* Set direction towards an x,y location on the map.
297 */
298 void Actor::face_location(uint16 lx, uint16 ly) {
299 uint16 xdiff = abs(x - lx), ydiff = abs(y - ly);
300 if (ydiff) {
301 if (y < ly && direction != 2)
302 set_direction(2); // down
303 else if (y > ly && direction != 0)
304 set_direction(0); // up
305 }
306 if (xdiff) {
307 if (x < lx && direction != 1)
308 set_direction(1); // right
309 else if (x > lx && direction != 3)
310 set_direction(3); // left
311 }
312 }
313 #endif
314
face_actor(Actor * a)315 void Actor::face_actor(Actor *a) {
316 uint16 ax, ay;
317 uint8 al;
318
319 a->get_location(&ax, &ay, &al);
320 face_location(ax, ay);
321 }
322
323
set_poisoned(bool poisoned)324 void Actor::set_poisoned(bool poisoned) {
325 if (poisoned) {
326 status_flags |= ACTOR_STATUS_POISONED;
327 new HitEffect(this); // no direct hp loss
328 } else {
329 status_flags &= (0xff ^ ACTOR_STATUS_POISONED);
330 }
331
332 if (is_in_party())
333 Game::get_game()->get_view_manager()->update();
334 }
335
336 /* Returns the proper (NPC) name of this actor if the Player knows it, or their
337 * description if the name is unknown.
338 */
get_name(bool force_real_name)339 const char *Actor::get_name(bool force_real_name) {
340 ActorManager *actor_manager = Game::get_game()->get_actor_manager();
341 Converse *converse = Game::get_game()->get_converse();
342 Party *party = Game::get_game()->get_party();
343 //Actor *player = Game::get_game()->get_player()->get_actor();
344 const char *talk_name = NULL; // name from conversation script
345 bool statue = (Game::get_game()->get_game_type() == NUVIE_GAME_U6 && id_n >= 189 && id_n <= 200);
346
347 if (is_alive() && is_in_party()) {
348 sint8 party_pos = party->get_member_num(this);
349 if (party_pos != -1)
350 name = party->get_actor_name((uint8)party_pos);
351 } else if ((is_met() || is_in_party() || force_real_name)
352 && (talk_name = converse->npc_name(id_n)) // assignment
353 && !statue)
354 name = talk_name;
355 else
356 name = actor_manager->look_actor(this, false);
357 return (name.c_str());
358 }
359
add_surrounding_obj(Obj * obj)360 void Actor::add_surrounding_obj(Obj *obj) {
361 obj->set_actor_obj(true);
362 surrounding_objects.push_back(obj);
363 }
364
unlink_surrounding_objects(bool make_objects_temporary)365 void Actor::unlink_surrounding_objects(bool make_objects_temporary) {
366 // if(make_objects_temporary)
367 {
368 Std::list<Obj *>::iterator obj;
369
370 for (obj = surrounding_objects.begin(); obj != surrounding_objects.end(); obj++) {
371 if (make_objects_temporary)
372 (*obj)->set_temporary();
373 (*obj)->set_actor_obj(false);
374 }
375 }
376 surrounding_objects.clear();
377 }
378
moveRelative(sint16 rel_x,sint16 rel_y,ActorMoveFlags flags)379 bool Actor::moveRelative(sint16 rel_x, sint16 rel_y, ActorMoveFlags flags) {
380 return move(x + rel_x, y + rel_y, z, flags);
381 }
382
383
check_move(uint16 new_x,uint16 new_y,uint8 new_z,ActorMoveFlags flags)384 bool Actor::check_move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags) {
385 Actor *a;
386 bool ignore_actors = flags & ACTOR_IGNORE_OTHERS;
387 bool ignore_danger = (flags & ACTOR_IGNORE_DANGER);
388 // bool ignore_danger = true;
389 /*
390 uint16 pitch = map->get_width(new_z);
391 if(new_x < 0 || new_x >= pitch)
392 return(false);
393 if(new_y < 0 || new_y >= pitch)
394 return(false);
395 */
396 if (!ignore_actors) {
397 a = map->get_actor(new_x, new_y, new_z, false);
398 if (a /*&& a->is_visible()*/) {
399 bool ignore_party_members = (flags & ACTOR_IGNORE_PARTY_MEMBERS);
400 if (ignore_party_members && a->is_in_party())
401 return true;
402 return a->can_be_passed(this); // we can move over or under some actors. eg mice, dragons etc.
403 }
404 }
405
406 // if(map->is_passable(new_x,new_y,new_z) == false)
407 // return(false);
408
409 if (!ignore_danger)
410 if (map->is_damaging(new_x, new_y, new_z))
411 return false;
412
413 return (true);
414 }
415
check_moveRelative(sint16 rel_x,sint16 rel_y,ActorMoveFlags flags)416 bool Actor::check_moveRelative(sint16 rel_x, sint16 rel_y, ActorMoveFlags flags) {
417 return check_move(x + rel_x, y + rel_y, z, flags);
418 }
419
420
can_be_moved()421 bool Actor::can_be_moved() {
422 return can_move;
423 }
424
can_be_passed(Actor * other)425 bool Actor::can_be_passed(Actor *other) {
426 return (other->is_passable() || is_passable());
427 }
428
get_object_readiable_location(Obj * obj)429 uint8 Actor::get_object_readiable_location(Obj *obj) {
430 int loc = Game::get_game()->get_script()->call_obj_get_readiable_location(obj);
431
432 if (loc >= 0)
433 return (uint8)loc;
434
435 return ACTOR_NOT_READIABLE;
436 }
437
move(uint16 new_x,uint16 new_y,uint8 new_z,ActorMoveFlags flags)438 bool Actor::move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags) {
439 // assert(new_z < 6); // shouldn't need to check anymore
440
441 //const uint8 move_cost = 5; // base cost to move
442 bool force_move = (bool)(flags & ACTOR_FORCE_MOVE);
443 bool open_doors = (bool)(flags & ACTOR_OPEN_DOORS);
444 bool ignore_actors = (bool)(flags & ACTOR_IGNORE_OTHERS);
445 bool ignore_danger = (bool)(flags & ACTOR_IGNORE_DANGER);
446 // bool ignore_danger = true;
447 bool ignore_moves = (bool)(flags & ACTOR_IGNORE_MOVES);
448 Obj *obj = NULL;
449 MapCoord oldpos(x, y, z);
450
451 clear_error();
452 if (!usecode)
453 usecode = obj_manager->get_usecode();
454 // no moves left
455 if (!(force_move || ignore_moves) && moves <= 0) {
456 set_error(ACTOR_OUT_OF_MOVES);
457 // return false;
458 DEBUG(0, LEVEL_WARNING, "actor %d is out of moves %d\n", id_n, moves);
459 }
460
461 // blocking actors are checked for later
462 obj = obj_manager->get_obj(new_x, new_y, new_z, OBJ_SEARCH_TOP, OBJ_INCLUDE_IGNORED); //we include ignored objects here to pick up the sacred quest blocking object.
463 if (!force_move && !check_move(new_x, new_y, new_z, ACTOR_IGNORE_DANGER | ACTOR_IGNORE_OTHERS)) {
464 // open door
465 if (!(obj && usecode->is_unlocked_door(obj) && open_doors)
466 || (!usecode->use_obj(obj, this))) {
467 set_error(ACTOR_BLOCKED_BY_OBJECT);
468 error_struct.blocking_obj = obj;
469 return false; // blocked by object or map tile
470 }
471 }
472 // avoid dangerous objects
473 if (!ignore_danger
474 && !force_move
475 && ((is_in_party() && map->is_damaging(new_x, new_y, new_z))
476 || (obj && obj_manager->is_damaging(new_x, new_y, new_z)))) {
477 set_error(ACTOR_BLOCKED_BY_OBJECT);
478 error_struct.blocking_obj = obj;
479 return false;
480 }
481 // usecode must allow movement
482 if (obj && usecode->has_passcode(obj)) {
483 if (!usecode->pass_obj(obj, this, new_x, new_y) && !force_move) { // calling item is this actor
484 set_error(ACTOR_BLOCKED_BY_OBJECT);
485 error_struct.blocking_obj = obj;
486 return false;
487 }
488 }
489 Game *game = Game::get_game();
490 Actor *other = map->get_actor(new_x, new_y, new_z, false);
491 if (!ignore_actors && !force_move && other && !other->can_be_passed(this)
492 && (!game->get_party()->get_autowalk() || other->is_visible())) {
493 set_error(ACTOR_BLOCKED_BY_ACTOR);
494 error_struct.blocking_actor = other;
495 return false; // blocked by actor
496 }
497
498 // move
499 x = WRAPPED_COORD(new_x, new_z); // FIXME: this is probably needed because PathFinder is not wrapping coords
500 y = WRAPPED_COORD(new_y, new_z);
501 z = new_z;
502
503 can_move = true;
504 //FIXME move this into Player::moveRelative()
505 /*
506 if(!(force_move || ignore_moves) && (id_n == game->get_player()->get_actor()->id_n || id_n == 0 || (is_in_party() && game->get_party()->is_in_combat_mode() == false))) // subtract from moves left for party members only. Other actors have their movement points deducted in actor_update_all()
507 {
508 set_moves_left(moves - (move_cost+map->get_impedance(oldpos.x, oldpos.y, oldpos.z)));
509 if(oldpos.x != x && oldpos.y != y) // diagonal move, double cost
510 set_moves_left(moves - (move_cost+map->get_impedance(oldpos.x, oldpos.y, oldpos.z)));
511 }
512 */
513 // post-move
514 // close door
515 if (open_doors) {
516 obj = obj_manager->get_obj(oldpos.x, oldpos.y, z);
517 if (obj && usecode->is_door(obj))
518 usecode->use_obj(obj, this);
519 }
520
521 // re-center map if actor is player character
522 if (id_n == game->get_player()->get_actor()->id_n && game->get_player()->is_mapwindow_centered())
523 game->get_map_window()->centerMapOnActor(this);
524 // allows a delay to be set on actor movement, in lieu of using animations
525 move_time = clock->get_ticks();
526 return true;
527 }
528
529
update()530 void Actor::update() {
531 if (!is_alive()) //we don't need to update dead actors.
532 return;
533
534 if (pathfinder) {
535 // NOTE: don't delete pathfinder right after walking, because the scheduled
536 // activity still needs to be checked, and depends on pathfinder existing
537 if (pathfinder->reached_goal())
538 delete_pathfinder();
539 else walk_path();
540 }
541
542 // update_time = clock->get_ticks(); moved to move()
543 }
544
545 /* Returns true if actor moved. */
walk_path()546 bool Actor::walk_path() {
547 pathfinder->update_location(); // set location from actor, if already moved
548
549 // validate path and get move
550 MapCoord next_loc, loc(x, y, z);
551 if (!pathfinder->get_next_move(next_loc)) // nothing to do here
552 return false;
553 // FIXME: move to SchedPathFinder (or delete; worktype will handle refresh)
554 if (next_loc == loc) { // ran out of steps? get a new path
555 if (pathfinder->have_path())
556 pathfinder->find_path();
557 return false;
558 }
559 if (!move(next_loc.x, next_loc.y, next_loc.z, ACTOR_OPEN_DOORS))
560 return false; // don't get a new path; probably just blocked by an actor
561 set_direction(x - loc.x, y - loc.y);
562 pathfinder->actor_moved();
563 return true;
564 }
565
566 // gz 255 = current map plane
pathfind_to(uint16 gx,uint16 gy,uint8 gz)567 void Actor::pathfind_to(uint16 gx, uint16 gy, uint8 gz) {
568 if (gz == 255)
569 gz = z;
570 MapCoord d(gx, gy, gz);
571 pathfind_to(d);
572 }
573
pathfind_to(MapCoord & d)574 void Actor::pathfind_to(MapCoord &d) {
575 if (pathfinder) {
576 pathfinder->set_actor(this);
577 pathfinder->set_goal(d);
578 } else
579 set_pathfinder(new ActorPathFinder(this, d), new SeekPath);
580 pathfinder->update_location();
581 }
582
583 // actor will take management of new_pf, and delete it when no longer needed
set_pathfinder(ActorPathFinder * new_pf,Path * path_type)584 void Actor::set_pathfinder(ActorPathFinder *new_pf, Path *path_type) {
585 if (pathfinder != NULL && pathfinder != new_pf)
586 delete_pathfinder();
587 pathfinder = new_pf;
588 if (path_type != 0)
589 pathfinder->set_search(path_type);
590 }
591
delete_pathfinder()592 void Actor::delete_pathfinder() {
593 delete pathfinder;
594 pathfinder = NULL;
595 }
596
set_in_party(bool state)597 void Actor::set_in_party(bool state) {
598 if (Game::get_game()->is_ethereal())
599 set_ethereal(state);
600 //in_party = state;
601 delete_pathfinder();
602 if (state == true) { // joined
603 // obj_n = base_obj_n; U6Actor::set_worktype
604 can_move = true;
605 set_worktype(0x01); // U6_IN_PARTY
606 status_flags |= ACTOR_STATUS_IN_PARTY;
607 if (!is_charmed())
608 set_alignment(ACTOR_ALIGNMENT_GOOD);
609 else
610 set_old_alignment(ACTOR_ALIGNMENT_GOOD);
611 } else { // left
612 if (is_alive() == true) {
613 if (is_invisible())
614 visible_flag = false;
615 set_worktype(0x8f); // U6_WANDER_AROUND
616 status_flags ^= ACTOR_STATUS_IN_PARTY;
617 inventory_drop_all(); // needs to be after party status change
618 if (is_charmed())
619 set_old_alignment(ACTOR_ALIGNMENT_NEUTRAL);
620 else
621 set_alignment(ACTOR_ALIGNMENT_NEUTRAL);
622 }
623 }
624 }
625
626 /*void Actor::attack(MapCoord pos)
627 {
628 return;
629 }*/
630
get_weapon_obj(sint8 readied_obj_location)631 Obj *Actor::get_weapon_obj(sint8 readied_obj_location) {
632 if (readied_obj_location != ACTOR_NO_READIABLE_LOCATION && readied_objects[readied_obj_location] && readied_objects[readied_obj_location]->obj != NULL)
633 return readied_objects[readied_obj_location]->obj;
634 return NULL;
635 }
636
get_range(uint16 target_x,uint16 target_y)637 uint8 Actor::get_range(uint16 target_x, uint16 target_y) {
638 sint16 off_x, off_y;
639 uint16 map_pitch = map->get_width(z);
640
641 if (target_x <= x)
642 off_x = x - target_x;
643 else { //target_x > x
644 if (target_x - x < 8) //small positive offset
645 off_x = target_x - x;
646 else { // target wrapped around the map.
647 if (map_pitch - target_x + x < 11)
648 off_x = target_x - map_pitch - x; //negative offset
649 else
650 off_x = 9; // x out of range
651 }
652 }
653
654 if (target_y <= y)
655 off_y = y - target_y;
656 else { //target_y > y
657 if (target_y - y < 8) //small positive offset
658 off_y = target_y - y;
659 else { // target wrapped around the map.
660 if (map_pitch - target_y + y < 11)
661 off_y = target_y - map_pitch - y; //negative offset
662 else
663 off_y = 9; // y out of range
664 }
665 }
666
667 return Game::get_game()->get_script()->call_get_combat_range(abs(off_x), abs(off_y));
668 }
669
weapon_can_hit(const CombatType * weapon,uint16 target_x,uint16 target_y)670 bool Actor::weapon_can_hit(const CombatType *weapon, uint16 target_x, uint16 target_y) {
671 if (!weapon)
672 return false;
673
674 Script *script = Game::get_game()->get_script();
675 if (get_range(target_x, target_y) > script->call_get_weapon_range(weapon->obj_n))
676 return false;
677 return true;
678 }
679
680 // attack another actor with melee attack or a weapon (short or long range)
attack(sint8 readied_obj_location,MapCoord target,Actor * foe)681 void Actor::attack(sint8 readied_obj_location, MapCoord target, Actor *foe) {
682 const uint8 attack_cost = 10; // base cost to attack
683
684 Game::get_game()->get_script()->call_actor_attack(this, target, get_weapon_obj(readied_obj_location), foe);
685
686 set_moves_left(moves - attack_cost);
687 }
get_weapon(sint8 readied_obj_location)688 const CombatType *Actor::get_weapon(sint8 readied_obj_location) {
689 if (readied_obj_location == ACTOR_NO_READIABLE_LOCATION)
690 return get_hand_combat_type();
691
692 if (readied_objects[readied_obj_location])
693 return readied_objects[readied_obj_location]->combat_type;
694
695 return NULL;
696 }
697
get_inventory_list()698 U6LList *Actor::get_inventory_list() {
699 return obj_manager->get_actor_inventory(id_n);
700 }
701
702
inventory_has_object(uint16 objN,uint8 qual,bool match_quality,uint8 frameN,bool match_frame_n)703 bool Actor::inventory_has_object(uint16 objN, uint8 qual, bool match_quality, uint8 frameN, bool match_frame_n) {
704 if (inventory_get_object(objN, qual, match_quality, frameN, match_frame_n))
705 return (true);
706 return (false);
707 }
708
inventory_count_objects(bool inc_readied_objects)709 uint32 Actor::inventory_count_objects(bool inc_readied_objects) {
710 Obj *obj;
711 uint32 count = 0;
712 U6Link *link;
713 U6LList *inventory = get_inventory_list();
714
715 if (inc_readied_objects) {
716 return inventory->count();
717 } else {
718 for (link = inventory->start(); link != NULL; link = link->next) {
719 obj = (Obj *)link->data;
720 if (!obj->is_readied())
721 count++;
722 }
723 }
724
725 return count;
726 }
727
728
729 /* Returns the number of objects in the actor's inventory with matching object
730 * number and quality.
731 */
inventory_count_object(uint16 objN)732 uint32 Actor::inventory_count_object(uint16 objN) {
733 uint32 qty = 0;
734 U6Link *link = 0;
735 Obj *obj = 0;
736 U6LList *inv = get_inventory_list();
737
738 for (link = inv->start(); link != NULL; link = link->next) {
739 obj = (Obj *)link->data;
740 if (obj)
741 qty += obj->get_total_qty(objN);
742 }
743
744 return (qty);
745 }
746
747
748 /* Returns object descriptor of object in the actor's inventory, or NULL if no
749 * matching object is found. */
inventory_get_object(uint16 objN,uint8 qual,bool match_quality,uint8 frameN,bool match_frame_n)750 Obj *Actor::inventory_get_object(uint16 objN, uint8 qual, bool match_quality, uint8 frameN, bool match_frame_n) {
751 U6LList *inventory;
752 U6Link *link;
753 Obj *obj;
754
755 inventory = get_inventory_list();
756 for (link = inventory->start(); link != NULL; link = link->next) {
757 obj = (Obj *)link->data;
758 if (obj->obj_n == objN && (match_quality == false || obj->quality == qual)
759 && (match_frame_n == false || obj->frame_n == frameN)) //FIXME should qual = 0 be an all quality search!?
760 return (obj);
761 else if (obj->has_container()) {
762 if ((obj = obj->find_in_container(objN, qual, match_quality)))
763 return (obj);
764 }
765 }
766
767 return NULL;
768 }
769
is_double_handed_obj_readied()770 bool Actor::is_double_handed_obj_readied() {
771 if (readied_objects[ACTOR_ARM] != NULL && readied_objects[ACTOR_ARM]->double_handed == true)
772 return true;
773
774 return false;
775 }
776
inventory_get_readied_object(uint8 location)777 Obj *Actor::inventory_get_readied_object(uint8 location) {
778 if (readied_objects[location] != NULL)
779 return readied_objects[location]->obj;
780
781 return NULL;
782 }
783
inventory_get_readied_object_combat_type(uint8 location)784 const CombatType *Actor::inventory_get_readied_object_combat_type(uint8 location) {
785 if (readied_objects[location] != NULL)
786 return readied_objects[location]->combat_type;
787
788 return NULL;
789 }
790
791
inventory_add_object(Obj * obj,Obj * container,bool stack)792 bool Actor::inventory_add_object(Obj *obj, Obj *container, bool stack) {
793 obj_manager->unlink_from_engine(obj);
794 U6LList *inventory = get_inventory_list(), *add_to = inventory;
795
796 // we have the item now so we don't consider it stealing if we get it at any time in the future.
797 obj->set_ok_to_take(true);
798
799 //remove temp flag on inventory items.
800 obj->set_temporary(false);
801
802 if (container) { // assumes actor is holding the container
803 container->add(obj, stack);
804 } else {
805 // only objects outside containers are marked in_inventory
806 /* obj->status |= OBJ_STATUS_IN_INVENTORY; */ // luteijn: don't manipulate this directly!
807 obj->set_in_inventory();
808 obj->x = id_n;
809 obj->parent = (void *)this;
810
811 if (obj->is_lit()) // light up actor
812 add_light(TORCH_LIGHT_LEVEL);
813
814 obj_manager->list_add_obj(add_to, obj, stack);
815 }
816
817 return true;
818 }
819
820 /* Stacks the new object with existing objects if possible.
821 Returns a pointer to the new object in inventory. */
inventory_new_object(uint16 objN,uint32 qty,uint8 quality)822 Obj *Actor::inventory_new_object(uint16 objN, uint32 qty, uint8 quality) {
823 Obj *obj = 0;
824 uint8 frameN = 0;
825
826 if (objN >= 1024) {
827 frameN = (uint8)floorf(objN / 1024);
828 objN -= frameN * 1024;
829 }
830
831 obj = new Obj;
832 obj->obj_n = objN;
833 obj->frame_n = frameN;
834 obj->quality = quality;
835 obj->qty = obj_manager->is_stackable(obj) ? 1 : 0; // stackable objects must have a quantity
836 if (qty > 1) // this will combine with others, only if object is stackable
837 for (uint32 q = 1; q < qty; q++) {
838 inventory_add_object(obj_manager->copy_obj(obj), NULL);
839 }
840 inventory_add_object(obj, NULL);
841 return inventory_get_object(objN, quality);
842 }
843
844 /* Delete `qty' objects of type from inventory (or from a container).
845 * Returns the number removed (may be less than requested). */
inventory_del_object(uint16 objN,uint32 qty,uint8 quality)846 uint32 Actor::inventory_del_object(uint16 objN, uint32 qty, uint8 quality) {
847 Obj *obj;
848 uint16 oqty = 0;
849 uint32 deleted = 0;
850
851 while ((obj = inventory_get_object(objN, quality, false))
852 && (deleted < qty)) {
853 oqty = obj->qty == 0 ? 1 : obj->qty;
854 if (oqty <= (qty - deleted)) {
855 inventory_remove_obj(obj);
856 delete_obj(obj);
857 deleted += oqty;
858 } else {
859 obj->qty = oqty - (qty - deleted);
860 deleted += (qty - deleted);
861 }
862 }
863 return (deleted);
864 }
865
inventory_del_all_objs()866 void Actor::inventory_del_all_objs() {
867 U6LList *inventory = get_inventory_list();
868 if (!inventory)
869 return;
870
871 U6Link *link = inventory->start();
872 for (; link != NULL;) {
873 Obj *obj = (Obj *)link->data;
874 link = link->next;
875 inventory_remove_obj(obj);
876 delete_obj(obj);
877 }
878
879 }
880
inventory_remove_obj(Obj * obj,bool run_usecode)881 bool Actor::inventory_remove_obj(Obj *obj, bool run_usecode) {
882 U6LList *inventory;
883 Obj *container = NULL;
884
885 inventory = get_inventory_list();
886 if (obj->is_readied())
887 remove_readied_object(obj, run_usecode);
888 if (obj->is_in_container())
889 container = obj->get_container_obj();
890
891 obj->set_noloc(); //remove engine location
892
893 if (container) {
894 return container->remove(obj);
895 }
896
897 if (obj->status & OBJ_STATUS_LIT) // remove light from actor
898 subtract_light(TORCH_LIGHT_LEVEL);
899
900 return inventory->remove(obj);
901 }
902
get_inventory_weight()903 float Actor::get_inventory_weight() {
904 U6LList *inventory;
905 U6Link *link;
906 Obj *obj;
907 float weight = 0;
908
909 if (obj_manager->actor_has_inventory(id_n) == false)
910 return 0;
911
912 inventory = obj_manager->get_actor_inventory(id_n);
913
914 for (link = inventory->start(); link != NULL; link = link->next) {
915 obj = (Obj *)link->data;
916 weight += obj_manager->get_obj_weight(obj);
917 }
918
919 return (weight);
920 }
921
get_inventory_equip_weight()922 float Actor::get_inventory_equip_weight() {
923 U6LList *inventory;
924 U6Link *link;
925 Obj *obj;
926 float weight = 0;
927
928 if (obj_manager->actor_has_inventory(id_n) == false)
929 return 0;
930
931 inventory = obj_manager->get_actor_inventory(id_n);
932
933 for (link = inventory->start(); link != NULL; link = link->next) {
934 obj = (Obj *)link->data;
935 if (obj->is_readied()) //object readied
936 weight += obj_manager->get_obj_weight(obj);
937 }
938
939 return (weight);
940 }
941
942
943 /* Can the actor carry a new object of this type?
944 */
can_carry_object(uint16 objN,uint32 qty)945 bool Actor::can_carry_object(uint16 objN, uint32 qty) {
946 if (Game::get_game()->using_hackmove())
947 return true;
948 float obj_weight = obj_manager->get_obj_weight(objN);
949 if (qty) obj_weight *= qty;
950 return (can_carry_weight(obj_weight));
951 }
952
can_carry_object(Obj * obj)953 bool Actor::can_carry_object(Obj *obj) {
954 if (Game::get_game()->using_hackmove())
955 return true;
956 if (obj_manager->can_get_obj(obj) == false)
957 return false;
958
959 return can_carry_weight(obj);
960 }
961
can_carry_weight(Obj * obj)962 bool Actor::can_carry_weight(Obj *obj) {
963 return (can_carry_weight(obj_manager->get_obj_weight(obj, OBJ_WEIGHT_INCLUDE_CONTAINER_ITEMS, OBJ_WEIGHT_DO_SCALE)));
964 }
965
966 /* Can the actor carry new object(s) of this weight?
967 * (return from get_obj_weight())
968 */
can_carry_weight(float obj_weight)969 bool Actor::can_carry_weight(float obj_weight) {
970 if (Game::get_game()->using_hackmove())
971 return true;
972 // obj_weight /= 10;
973 float inv_weight = get_inventory_weight() + obj_weight;
974 float max_weight = inventory_get_max_weight();
975 return (inv_weight <= max_weight);
976 }
977
978
inventory_parse_readied_objects()979 void Actor::inventory_parse_readied_objects() {
980 U6LList *inventory;
981 U6Link *link;
982 Obj *obj;
983
984 if (obj_manager->actor_has_inventory(id_n) == false)
985 return;
986
987 inventory = obj_manager->get_actor_inventory(id_n);
988
989 for (link = inventory->start(); link != NULL;) {
990 obj = (Obj *)link->data;
991 link = link->next;
992 obj->parent = (void *)this;
993 if (obj->is_readied()) { //object readied
994 add_readied_object(obj);
995 }
996 if (obj->status & OBJ_STATUS_LIT) { // torch
997 add_light(TORCH_LIGHT_LEVEL); // light up actor
998 }
999 }
1000
1001 return;
1002 }
1003
can_ready_obj(Obj * obj)1004 bool Actor::can_ready_obj(Obj *obj) {
1005 uint8 location = get_object_readiable_location(obj);
1006
1007 switch (location) {
1008 case ACTOR_NOT_READIABLE :
1009 return false;
1010
1011 case ACTOR_ARM :
1012 if (readied_objects[ACTOR_ARM] != NULL) { //if full try other arm
1013 if (readied_objects[ACTOR_ARM]->double_handed)
1014 return false;
1015
1016 location = ACTOR_ARM_2;
1017 }
1018 break;
1019
1020 case ACTOR_ARM_2 :
1021 if (readied_objects[ACTOR_ARM] != NULL || readied_objects[ACTOR_ARM_2] != NULL)
1022 return false;
1023 location = ACTOR_ARM;
1024 break;
1025
1026 case ACTOR_HAND :
1027 if (readied_objects[ACTOR_HAND] != NULL) // if full try other hand
1028 location = ACTOR_HAND_2;
1029 break;
1030 }
1031
1032 if (readied_objects[location] != NULL)
1033 return false;
1034
1035 return true;
1036 }
1037
1038 //FIX handle not readiable, no place to put, double handed objects
add_readied_object(Obj * obj)1039 bool Actor::add_readied_object(Obj *obj) {
1040 uint8 location;
1041 bool double_handed = false;
1042
1043 location = get_object_readiable_location(obj);
1044
1045 switch (location) {
1046 case ACTOR_NOT_READIABLE :
1047 return false;
1048
1049 case ACTOR_ARM :
1050 if (readied_objects[ACTOR_ARM] != NULL) { //if full try other arm
1051 if (readied_objects[ACTOR_ARM]->double_handed)
1052 return false;
1053
1054 location = ACTOR_ARM_2;
1055 }
1056 break;
1057
1058 case ACTOR_ARM_2 :
1059 if (readied_objects[ACTOR_ARM] != NULL || readied_objects[ACTOR_ARM_2] != NULL)
1060 return false;
1061 location = ACTOR_ARM;
1062 double_handed = true;
1063 break;
1064
1065 case ACTOR_HAND :
1066 if (readied_objects[ACTOR_HAND] != NULL) // if full try other hand
1067 location = ACTOR_HAND_2;
1068 break;
1069 }
1070
1071 if (readied_objects[location] != NULL)
1072 return false;
1073
1074 readied_objects[location] = new ReadiedObj;
1075
1076 if (obj->is_in_container())
1077 inventory_add_object_nostack(obj);
1078
1079 readied_objects[location]->obj = obj;
1080 readied_objects[location]->combat_type = get_object_combat_type(obj->obj_n);
1081 readied_objects[location]->double_handed = double_handed;
1082
1083 if (readied_objects[location]->combat_type != NULL)
1084 readied_armor_class += readied_objects[location]->combat_type->defence;
1085
1086 obj->readied(); //set object to readied status
1087 return true;
1088 }
1089
remove_readied_object(Obj * obj,bool run_usecode)1090 void Actor::remove_readied_object(Obj *obj, bool run_usecode) {
1091 uint8 location;
1092
1093 for (location = 0; location < ACTOR_MAX_READIED_OBJECTS; location++) {
1094 if (readied_objects[location] != NULL && readied_objects[location]->obj == obj) {
1095 remove_readied_object(location, run_usecode);
1096 break;
1097 }
1098 }
1099
1100 return;
1101 }
1102
remove_readied_object(uint8 location,bool run_usecode)1103 void Actor::remove_readied_object(uint8 location, bool run_usecode) {
1104 Obj *obj;
1105
1106 obj = inventory_get_readied_object(location);
1107
1108 if (obj) {
1109 if (readied_objects[location]->combat_type)
1110 readied_armor_class -= readied_objects[location]->combat_type->defence;
1111 if (obj_manager->get_usecode()->has_readycode(obj) && run_usecode)
1112 obj_manager->get_usecode()->ready_obj(obj, this);
1113 delete readied_objects[location];
1114 readied_objects[location] = NULL;
1115 //ERIC obj->status ^= 0x18; // remove "readied" bit flag.
1116 //ERIC obj->status |= OBJ_STATUS_IN_INVENTORY; // keep "in inventory"
1117 obj->set_in_inventory();
1118
1119 if (location == ACTOR_ARM && readied_objects[ACTOR_ARM_2] != NULL) { //move contents of left hand to right hand.
1120 readied_objects[ACTOR_ARM] = readied_objects[ACTOR_ARM_2];
1121 readied_objects[ACTOR_ARM_2] = NULL;
1122 }
1123
1124 if (location == ACTOR_HAND && readied_objects[ACTOR_HAND_2] != NULL) { //move contents of left hand to right hand.
1125 readied_objects[ACTOR_HAND] = readied_objects[ACTOR_HAND_2];
1126 readied_objects[ACTOR_HAND_2] = NULL;
1127 }
1128 }
1129
1130 return;
1131 }
1132
remove_all_readied_objects()1133 void Actor::remove_all_readied_objects() {
1134 uint8 location;
1135
1136 for (location = 0; location < ACTOR_MAX_READIED_OBJECTS; location++) {
1137 if (readied_objects[location] != NULL)
1138 remove_readied_object(location);
1139 }
1140
1141 return;
1142 }
1143
1144 // returns true if the actor has one or more readied objects
has_readied_objects()1145 bool Actor::has_readied_objects() {
1146 uint8 location;
1147
1148 for (location = 0; location < ACTOR_MAX_READIED_OBJECTS; location++) {
1149 if (readied_objects[location] != NULL)
1150 return true;
1151 }
1152
1153 return false;
1154
1155 }
1156
inventory_drop_all()1157 void Actor::inventory_drop_all() {
1158 U6LList *inv = NULL;
1159 Obj *obj = NULL;
1160
1161 while (inventory_count_objects(true)) {
1162 inv = get_inventory_list();
1163 obj = (Obj *)(inv->start()->data);
1164 if (!inventory_remove_obj(obj))
1165 break;
1166
1167 Tile *obj_tile = obj_manager->get_obj_tile(obj->obj_n, obj->frame_n);
1168 if (obj_tile && (obj_tile->flags3 & TILEFLAG_IGNORE)) { //Don't drop charges.
1169 delete_obj(obj);
1170 } else {
1171 if (temp_actor)
1172 obj->status |= OBJ_STATUS_TEMPORARY;
1173 obj->status |= OBJ_STATUS_OK_TO_TAKE;
1174 obj->x = x;
1175 obj->y = y;
1176 obj->z = z;
1177 obj_manager->add_obj(obj, true); // add to map
1178 }
1179 }
1180 }
1181
1182 // Moves inventory and all readied items into a container object.
all_items_to_container(Obj * container_obj,bool stack)1183 void Actor::all_items_to_container(Obj *container_obj, bool stack) {
1184 U6LList *inventory;
1185 U6Link *link;
1186 Obj *obj;
1187
1188 inventory = get_inventory_list();
1189
1190 if (!inventory)
1191 return;
1192
1193 for (link = inventory->start(); link != NULL;) {
1194 obj = (Obj *)link->data;
1195 link = link->next;
1196
1197 if (temp_actor)
1198 obj->status |= OBJ_STATUS_TEMPORARY;
1199
1200 Tile *obj_tile = obj_manager->get_obj_tile(obj->obj_n, obj->frame_n);
1201 if (obj_tile && obj_tile->flags3 & TILEFLAG_IGNORE) {
1202 inventory_remove_obj(obj);
1203 delete_obj(obj);
1204 } else
1205 obj_manager->moveto_container(obj, container_obj, stack);
1206 }
1207
1208
1209 return;
1210 }
1211
loadSchedule(unsigned char * sched_data,uint16 num)1212 void Actor::loadSchedule(unsigned char *sched_data, uint16 num) {
1213 uint16 i;
1214 unsigned char *sched_data_ptr;
1215
1216 sched = (Schedule **)malloc(sizeof(Schedule *) * (num + 1));
1217 num_schedules = num;
1218 sched_data_ptr = sched_data;
1219
1220 for (i = 0; i < num; i++) {
1221 sched[i] = (Schedule *)malloc(sizeof(Schedule));
1222
1223 sched[i]->hour = sched_data_ptr[0] & 0x1f; // 5 bits for hour
1224 sched[i]->day_of_week = sched_data_ptr[0] >> 5; // 3 bits for day of week
1225 sched[i]->worktype = sched_data_ptr[1];
1226
1227 sched[i]->x = sched_data_ptr[2];
1228 sched[i]->x += (sched_data_ptr[3] & 0x3) << 8;
1229
1230 sched[i]->y = (sched_data_ptr[3] & 0xfc) >> 2;
1231 sched[i]->y += (sched_data_ptr[4] & 0xf) << 6;
1232
1233 sched[i]->z = (sched_data_ptr[4] & 0xf0) >> 4;
1234 sched_data_ptr += 5;
1235 #ifdef ACTOR_DEBUG
1236 DEBUG(0, LEVEL_DEBUGGING, "#%04d %03x,%03x,%x hour %2d day of week %2d worktype %02x\n", id_n, sched[i]->x, sched[i]->y, sched[i]->z, sched[i]->hour, sched[i]->day_of_week, sched[i]->worktype);
1237 #endif
1238 }
1239
1240 sched[i] = NULL;
1241
1242 return;
1243 }
1244
1245 //FIX for day_of_week
1246
updateSchedule(uint8 hour,bool teleport)1247 bool Actor::updateSchedule(uint8 hour, bool teleport) {
1248 //uint8 day_of_week;
1249 uint16 new_pos;
1250
1251 if (is_alive() == false //don't update schedule for dead actors.
1252 || (Game::get_game()->get_player()->get_actor() == this
1253 && Game::get_game()->get_event()->using_control_cheat()))
1254 return false;
1255
1256 //hour = clock->get_hour();
1257 // day_of_week = clock->get_day_of_week();
1258
1259 new_pos = getSchedulePos(hour);
1260
1261 if (new_pos == sched_pos) // schedules are the same so we do nothing.
1262 return false;
1263
1264 sched_pos = new_pos;
1265
1266 if (sched[sched_pos] == NULL)
1267 return false;
1268
1269 // U6: temp. fix for walking statues; they shouldn't have schedules
1270 if (Game::get_game()->get_game_type() == NUVIE_GAME_U6 && id_n >= 188 && id_n <= 200) {
1271 DEBUG(0, LEVEL_WARNING, "tried to update schedule for non-movable actor %d\n", id_n);
1272 return (false);
1273 }
1274
1275 set_worktype(sched[sched_pos]->worktype);
1276 if (teleport)
1277 move(sched[sched_pos]->x, sched[sched_pos]->y, sched[sched_pos]->z, ACTOR_FORCE_MOVE);
1278 return true;
1279 }
1280
1281 // returns the current schedule entry based on hour
getSchedulePos(uint8 hour)1282 uint16 Actor::getSchedulePos(uint8 hour) {
1283 uint16 i;
1284
1285 for (i = 0; sched[i] != NULL; i++) {
1286 if (sched[i]->hour > hour) {
1287 if (i != 0)
1288 return i - 1;
1289 else // i == 0 this means we are in the last schedule entry
1290 for (; sched[i + 1] != NULL;)
1291 i++;
1292 }
1293 }
1294
1295 if (i == 0)
1296 return 0;
1297
1298 return i - 1;
1299 }
1300
1301 /*
1302 // returns the current schedule entry based on hour
1303 uint16 Actor::getSchedulePos(uint8 hour, uint8 day_of_week)
1304 {
1305 uint16 i,j;
1306 if(id_n == 133)
1307 DEBUG(0,LEVEL_DEBUGGING,".");
1308
1309 i = getSchedulePos(hour);
1310
1311 for(j=i;sched[j] != NULL && sched[j]->hour == sched[i]->hour;j++)
1312 {
1313 if(sched[j]->day_of_week > day_of_week)
1314 {
1315 if(j != i)
1316 return j-1;
1317 else // hour is in the last schedule entry.
1318 {
1319 for(;sched[j+1] != NULL && sched[j+1]->hour == sched[i]->hour;) // move to the last schedule entry.
1320 j++;
1321 }
1322 }
1323 }
1324
1325 if(j==i)
1326 return j;
1327
1328 return j-1;
1329 }
1330
1331 inline uint16 Actor::getSchedulePos(uint8 hour)
1332 {
1333 uint16 i;
1334 uint8 cur_hour;
1335
1336 for(i=0;sched[i] != NULL;i++)
1337 {
1338 if(sched[i]->hour > hour)
1339 {
1340 if(i != 0)
1341 return i-1;
1342 else // hour is in the last schedule entry.
1343 {
1344 for(;sched[i+1] != NULL;) // move to the last schedule entry.
1345 i++;
1346
1347 if(sched[i]->day_of_week > 0) //rewind to the start of the hour set.
1348 {
1349 cur_hour = sched[i]->hour;
1350 for(;i >= 1 && sched[i-1]->hour == cur_hour;)
1351 i--;
1352 }
1353 }
1354 }
1355 else
1356 for(;sched[i+1] != NULL && sched[i+1]->hour == sched[i]->hour;) //skip to next hour set.
1357 i++;
1358 }
1359
1360 if(sched[i] != NULL && sched[i]->day_of_week > 0) //rewind to the start of the hour set.
1361 {
1362 cur_hour = sched[i]->hour;
1363 for(;i >= 1 && sched[i-1]->hour == cur_hour;)
1364 i--;
1365 }
1366
1367 if(i==0)
1368 return 0;
1369
1370 return i-1;
1371 }
1372 */
1373
set_combat_mode(uint8 new_mode)1374 void Actor::set_combat_mode(uint8 new_mode) {
1375 combat_mode = new_mode;
1376 if (Game::get_game()->get_party()->is_in_combat_mode()) {
1377 set_worktype(combat_mode);
1378 }
1379 }
1380
set_worktype(uint8 new_worktype,bool init)1381 void Actor::set_worktype(uint8 new_worktype, bool init) {
1382 worktype = new_worktype;
1383 work_location.x = x;
1384 work_location.y = y;
1385 work_location.z = z;
1386
1387
1388 return ;
1389 }
1390
get_flag(uint8 bitflag)1391 uint8 Actor::get_flag(uint8 bitflag) {
1392 if (bitflag > 7)
1393 return 0;
1394
1395 return (talk_flags >> bitflag) & 1;
1396 }
1397
1398 /* Set NPC flag `bitflag' to 1.
1399 */
set_flag(uint8 bitflag)1400 void Actor::set_flag(uint8 bitflag) {
1401 if (bitflag > 7)
1402 return;
1403 talk_flags = talk_flags | (1 << bitflag);
1404 }
1405
1406
1407 /* Set NPC flag `bitflag' to 0.
1408 */
clear_flag(uint8 bitflag)1409 void Actor::clear_flag(uint8 bitflag) {
1410 if (bitflag > 7)
1411 return;
1412 talk_flags = talk_flags & ~(1 << bitflag);
1413 }
1414
make_obj()1415 Obj *Actor::make_obj() {
1416 Obj *obj;
1417
1418 obj = new Obj();
1419
1420 obj->x = x;
1421 obj->y = y;
1422 obj->z = z;
1423
1424 obj->obj_n = obj_n;
1425 obj->frame_n = frame_n;
1426 obj->quality = id_n;
1427 obj->status = obj_flags;
1428
1429 return obj;
1430 }
1431
clear()1432 void Actor::clear() {
1433 x = 0;
1434 y = 0;
1435 z = 0;
1436 hide();
1437 Actor::set_worktype(0);
1438 light = 0;
1439 light_source.clear();
1440 }
1441
show()1442 void Actor::show() {
1443 visible_flag = true;
1444
1445 Std::list<Obj *>::iterator obj_iter;
1446 for (obj_iter = surrounding_objects.begin(); obj_iter != surrounding_objects.end(); obj_iter++) {
1447 (*obj_iter)->set_invisible(false);
1448 }
1449
1450 }
1451
hide()1452 void Actor::hide() {
1453 visible_flag = false;
1454
1455 Std::list<Obj *>::iterator obj_iter;
1456 for (obj_iter = surrounding_objects.begin(); obj_iter != surrounding_objects.end(); obj_iter++) {
1457 (*obj_iter)->set_invisible(true);
1458 }
1459 }
1460
1461 /* Get pushed by `pusher' to location determined by `where'. */
push(Actor * pusher,uint8 where)1462 bool Actor::push(Actor *pusher, uint8 where) {
1463 if (where == ACTOR_PUSH_HERE) { // move to pusher's square and use up moves
1464 MapCoord to(pusher->x, pusher->y, pusher->z), from(get_location());
1465 if (to.distance(from) > 1 || z != to.z)
1466 return (false);
1467 face_location(to.x, to.y);
1468 move(to.x, to.y, to.z, ACTOR_FORCE_MOVE); // can even move onto blocked squares
1469 if (moves > 0)
1470 set_moves_left(0); // we use up our moves exchanging positions
1471 return (true);
1472 } else if (where == ACTOR_PUSH_ANYWHERE) { // go to any neighboring direction
1473 MapCoord from(get_location());
1474 const uint16 square = 1;
1475 if (this->push(pusher, ACTOR_PUSH_FORWARD))
1476 return (true); // prefer forward push
1477 for (uint16 xp = (from.x - square); xp <= (from.x + square); xp += square)
1478 for (uint16 yp = (from.y - square); yp <= (from.y + square); yp += square)
1479 if (xp != from.x && yp != from.y && move(xp, yp, from.z))
1480 return (true);
1481 } else if (where == ACTOR_PUSH_FORWARD) { // move away from pusher
1482 MapCoord from(get_location());
1483 MapCoord pusher_loc(pusher->x, pusher->y, pusher->z);
1484 if (pusher_loc.distance(from) > 1 || z != pusher->z)
1485 return (false);
1486 sint8 rel_x = -(pusher_loc.x - from.x), rel_y = -(pusher_loc.y - from.y);
1487 if (moveRelative(rel_x, rel_y)) {
1488 set_direction(rel_x, rel_y);
1489 return (true);
1490 }
1491 }
1492 return (false);
1493 }
1494
1495 /* Subtract amount from hp. May die if hp is too low. */
reduce_hp(uint8 amount)1496 void Actor::reduce_hp(uint8 amount) {
1497 DEBUG(0, LEVEL_DEBUGGING, "hit %s for %d points\n", get_name(), amount);
1498
1499 if (amount <= hp)
1500 set_hp(hp - amount);
1501 else
1502 set_hp(0);
1503 // FIXME... game specific?
1504 if (hp == 0)
1505 die();
1506 }
1507
set_hp(uint8 val)1508 void Actor::set_hp(uint8 val) {
1509 hp = val;
1510 if (is_in_party()) {
1511 Game::get_game()->get_view_manager()->update();
1512 }
1513 }
1514
set_obj_flag(uint8 bitFlag,bool value)1515 void Actor::set_obj_flag(uint8 bitFlag, bool value) {
1516 if (value) {
1517 obj_flags |= (1 << bitFlag);
1518 } else {
1519 obj_flags &= (0xff ^ (1 << bitFlag));
1520 }
1521 }
1522
set_status_flag(uint8 bitFlag,bool value)1523 void Actor::set_status_flag(uint8 bitFlag, bool value) {
1524 if (value) {
1525 status_flags |= (1 << bitFlag);
1526 } else {
1527 status_flags &= (0xff ^ (1 << bitFlag));
1528 }
1529 }
1530
set_hit_flag(bool val)1531 void Actor::set_hit_flag(bool val) {
1532 if (val) {
1533 movement_flags |= ACTOR_MOVEMENT_HIT_FLAG;
1534 } else {
1535 movement_flags &= (0xff ^ ACTOR_MOVEMENT_HIT_FLAG);
1536 }
1537 }
1538
die(bool create_body)1539 void Actor::die(bool create_body) {
1540 hp = 0;
1541 visible_flag = false;
1542 Game *game = Game::get_game();
1543
1544 if (game->get_game_type() != NUVIE_GAME_U6) // set in U6 before removing items for torch usecode
1545 set_dead_flag(true); // may need to add it elsewhere for other games
1546
1547 if (game->get_player()->get_actor() == this && game->get_event()->using_control_cheat())
1548 game->get_event()->party_mode();
1549 if (is_temp())
1550 game->get_actor_manager()->clear_actor(this);
1551 }
1552
resurrect(MapCoord new_position,Obj * body_obj)1553 void Actor::resurrect(MapCoord new_position, Obj *body_obj) {
1554 U6Link *link;
1555 bool remove_obj = false;
1556
1557 if (body_obj == NULL) {
1558 body_obj = find_body();
1559 if (body_obj != NULL)
1560 remove_obj = true;
1561 }
1562
1563 set_dead_flag(false);
1564
1565 show();
1566
1567 x = new_position.x;
1568 y = new_position.y;
1569 z = new_position.z;
1570 obj_n = base_obj_n;
1571 init((Game::get_game()->get_game_type() == NUVIE_GAME_U6 && id_n == 130)
1572 ? OBJ_STATUS_MUTANT : NO_OBJ_STATUS);
1573
1574 frame_n = 0;
1575
1576 set_direction(NUVIE_DIR_N);
1577 if (Game::get_game()->get_game_type() == NUVIE_GAME_U6)
1578 ((U6Actor *)this)->do_twitch(); // fixes actors with more than 1 tile
1579
1580 set_hp(1);
1581 //actor->set_worktype(0x1);
1582
1583 if (is_in_party()) //actor in party
1584 Game::get_game()->get_party()->add_actor(this);
1585
1586 if (body_obj != NULL) {
1587 //add body container objects back into actor's inventory.
1588 if (body_obj->has_container()) {
1589 for (link = body_obj->container->start(); link != NULL;) {
1590 Obj *inv_obj = (Obj *)link->data;
1591 link = link->next;
1592 inventory_add_object(inv_obj);
1593 }
1594
1595 body_obj->container->removeAll();
1596 }
1597
1598 obj_manager->unlink_from_engine(body_obj);
1599 }
1600
1601 if (remove_obj)
1602 delete_obj(body_obj);
1603
1604 Game::get_game()->get_script()->call_actor_resurrect(this);
1605
1606 return;
1607 }
1608
display_condition()1609 void Actor::display_condition() {
1610 MsgScroll *scroll = Game::get_game()->get_scroll();
1611
1612 if (hp == get_maxhp())
1613 return;
1614 scroll->display_string(get_name());
1615 scroll->display_string(" ");
1616 if (hp < get_maxhp() / 4) // 25%
1617 scroll->display_string("critical!\n");
1618 else {
1619 if (hp < get_maxhp() / 2) // 50%
1620 scroll->display_string("heavily");
1621 else if (hp < get_maxhp() / 1.33) // 75%
1622 scroll->display_string("lightly");
1623 else
1624 scroll->display_string("barely");
1625 scroll->display_string(" wounded.\n");
1626 }
1627 }
1628
1629 /* Get hit and take damage by some indirect effect. (no source) */
hit(uint8 dmg,bool force_hit)1630 void Actor::hit(uint8 dmg, bool force_hit) {
1631 MsgScroll *scroll = Game::get_game()->get_scroll();
1632 uint8 total_armor_class = body_armor_class; //+ readied_armor_class;
1633
1634 if (dmg == 0) {
1635 scroll->display_string(get_name());
1636 scroll->display_string(" grazed!\n");
1637 } else if (dmg > total_armor_class || force_hit) {
1638 new HitEffect(this);
1639 reduce_hp(force_hit ? dmg : dmg - total_armor_class);
1640
1641 // if(!force_hit)
1642 // {
1643 if (hp == 0) {
1644 scroll->display_string(get_name());
1645 scroll->display_string(" killed!\n");
1646 } else {
1647 display_condition();
1648 }
1649 // }
1650 }
1651 }
1652
attract_to(Actor * target)1653 void Actor::attract_to(Actor *target) {
1654 delete_pathfinder();
1655 set_pathfinder(new CombatPathFinder(this));
1656 ((CombatPathFinder *)pathfinder)->set_chase_mode(target);
1657 }
1658
repel_from(Actor * target)1659 void Actor::repel_from(Actor *target) {
1660 delete_pathfinder();
1661 set_pathfinder(new CombatPathFinder(this, target));
1662 ((CombatPathFinder *)pathfinder)->set_flee_mode(target);
1663 ((CombatPathFinder *)pathfinder)->set_distance(2);
1664 }
1665
get_light_level()1666 uint8 Actor::get_light_level() {
1667 Tile *tile = get_tile();
1668 return MAX(light, GET_TILE_LIGHT_LEVEL(tile));
1669 }
1670
add_light(uint8 val)1671 void Actor::add_light(uint8 val) {
1672 if (is_in_party() || (Actor *)this == Game::get_game()->get_player()->get_actor())
1673 Game::get_game()->get_party()->add_light_source();
1674 // light += val;
1675 light_source.push_back(val);
1676 if (val > light)
1677 light = val;
1678 }
1679
subtract_light(uint8 val)1680 void Actor::subtract_light(uint8 val) {
1681 if (is_in_party() || (Actor *)this == Game::get_game()->get_player()->get_actor())
1682 Game::get_game()->get_party()->subtract_light_source();
1683 // if(light >= val)
1684 // light -= val;
1685 // else
1686 // light = 0;
1687 vector<uint8>::iterator l = light_source.begin();
1688 for (; l != light_source.end(); l++)
1689 if (*l == val) {
1690 light_source.erase(l);
1691 break;
1692 }
1693 light = 0; // change to next highest light source
1694 for (unsigned int lCtr = 0; lCtr < light_source.size(); lCtr++)
1695 if (light_source[lCtr] > light)
1696 light = light_source[lCtr];
1697 }
1698
set_moves_left(sint8 val)1699 void Actor::set_moves_left(sint8 val) {
1700 moves = clamp(val, -127, dex);
1701 }
1702
set_dead_flag(bool value)1703 void Actor::set_dead_flag(bool value) {
1704 if (value)
1705 status_flags |= ACTOR_STATUS_DEAD;
1706 else if (!is_alive()) //if not alive then clear dead flag
1707 status_flags ^= ACTOR_STATUS_DEAD;
1708
1709 return;
1710 }
1711
1712 /* Set error/status information. */
set_error(ActorErrorCode err)1713 void Actor::set_error(ActorErrorCode err) {
1714 clear_error();
1715 error_struct.err = err;
1716 }
1717
clear_error()1718 void Actor::clear_error() {
1719 error_struct.err = ACTOR_NO_ERROR;
1720 error_struct.blocking_obj = NULL;
1721 error_struct.blocking_actor = NULL;
1722 }
1723
get_error()1724 ActorError *Actor::get_error() {
1725 return (&error_struct);
1726 }
1727
1728 // frozen by worktype or status
is_immobile()1729 bool Actor::is_immobile() {
1730 return (false);
1731 }
1732
print()1733 void Actor::print() {
1734 Actor *actor = this;
1735 DEBUG(0, LEVEL_INFORMATIONAL, "\n");
1736 DEBUG(1, LEVEL_INFORMATIONAL, "%s at %x, %x, %x\n", get_name(), actor->x, actor->y, actor->z);
1737 DEBUG(1, LEVEL_INFORMATIONAL, "id_n: %d\n", actor->id_n);
1738
1739 DEBUG(1, LEVEL_INFORMATIONAL, "obj_n: %03d frame_n: %d\n", actor->obj_n, actor->frame_n);
1740 DEBUG(1, LEVEL_INFORMATIONAL, "base_obj_n: %03d old_frame_n: %d\n", actor->base_obj_n, actor->old_frame_n);
1741
1742 uint8 dir = actor->direction;
1743 DEBUG(1, LEVEL_INFORMATIONAL, "direction: %d (%s)\n", dir, (dir == NUVIE_DIR_N) ? "north" :
1744 (dir == NUVIE_DIR_E) ? "east" :
1745 (dir == NUVIE_DIR_S) ? "south" :
1746 (dir == NUVIE_DIR_W) ? "west" : "???");
1747 DEBUG(1, LEVEL_INFORMATIONAL, "walk_frame: %d\n", actor->walk_frame);
1748
1749 DEBUG(1, LEVEL_INFORMATIONAL, "can_move: %s\n", actor->can_move ? "true" : "false");
1750 DEBUG(1, LEVEL_INFORMATIONAL, "alive: %s\n", actor->is_alive() ? "true" : "false");
1751 DEBUG(1, LEVEL_INFORMATIONAL, "in_party: %s\n", is_in_party() ? "true" : "false");
1752 DEBUG(1, LEVEL_INFORMATIONAL, "visible_flag: %s\n", actor->visible_flag ? "true" : "false");
1753 DEBUG(1, LEVEL_INFORMATIONAL, "met_player: %s\n", actor->met_player ? "true" : "false");
1754 DEBUG(1, LEVEL_INFORMATIONAL, "is_immobile: %s\n", actor->is_immobile() ? "true" : "false");
1755
1756 DEBUG(1, LEVEL_INFORMATIONAL, "moves: %d\n", actor->moves);
1757
1758 const char *wt_string = get_worktype_string(actor->worktype);
1759 if (!wt_string) wt_string = "???";
1760 DEBUG(1, LEVEL_INFORMATIONAL, "worktype: 0x%02x/%03d %s\n", actor->worktype, actor->worktype, wt_string);
1761
1762 DEBUG(1, LEVEL_INFORMATIONAL, "NPC stats:\n");
1763 DEBUG(1, LEVEL_INFORMATIONAL, " level: %d exp: %d hp: %d / %d\n", actor->level, actor->exp,
1764 actor->hp, actor->get_maxhp());
1765 DEBUG(1, LEVEL_INFORMATIONAL, " strength: %d dex: %d int: %d\n", actor->strength, actor->dex,
1766 actor->intelligence);
1767 DEBUG(1, LEVEL_INFORMATIONAL, " magic: %d / %d\n", actor->magic, actor->get_maxmagic());
1768
1769 DEBUG(1, LEVEL_INFORMATIONAL, "alignment: %s (%d)\n", get_actor_alignment_str(actor->get_alignment()), actor->get_alignment());
1770
1771 uint8 combatMode = actor->combat_mode;
1772 wt_string = get_worktype_string(actor->combat_mode);
1773 if (!wt_string) wt_string = "???";
1774 DEBUG(1, LEVEL_INFORMATIONAL, "combat_mode: %d %s\n", combatMode, wt_string);
1775
1776 DEBUG(1, LEVEL_INFORMATIONAL, "Object flags: ");
1777 print_b(LEVEL_INFORMATIONAL, actor->obj_flags);
1778 DEBUG(1, LEVEL_INFORMATIONAL, "\n");
1779
1780 DEBUG(1, LEVEL_INFORMATIONAL, "NPC flags: ");
1781 print_b(LEVEL_INFORMATIONAL, actor->status_flags);
1782 DEBUG(1, LEVEL_INFORMATIONAL, "\n");
1783
1784 DEBUG(1, LEVEL_INFORMATIONAL, "Talk flags: ");
1785 print_b(LEVEL_INFORMATIONAL, actor->talk_flags);
1786 DEBUG(1, LEVEL_INFORMATIONAL, "\n");
1787
1788 uint32 inv = actor->inventory_count_objects(true);
1789 if (inv) {
1790 DEBUG(1, LEVEL_INFORMATIONAL, "Inventory (+readied): %d objects\n", inv);
1791 U6LList *inv_list = actor->get_inventory_list();
1792 for (U6Link *link = inv_list->start(); link != NULL; link = link->next) {
1793 Obj *obj = (Obj *)link->data;
1794 DEBUG(1, LEVEL_INFORMATIONAL, " %24s (%03d:%d) status=%d qual=%d qty=%d (weighs %f)\n",
1795 obj_manager->look_obj(obj), obj->obj_n, obj->frame_n, obj->status, obj->quality,
1796 obj->qty, obj_manager->get_obj_weight(obj, false));
1797 }
1798 DEBUG(1, LEVEL_INFORMATIONAL, "(weight %f / %f)\n", actor->get_inventory_weight(),
1799 actor->inventory_get_max_weight());
1800 }
1801 if (actor->sched && *actor->sched) {
1802 DEBUG(1, LEVEL_INFORMATIONAL, "Schedule:\n");
1803 Schedule **s = actor->sched;
1804 uint32 sp = 0;
1805 do {
1806 wt_string = get_worktype_string(s[sp]->worktype);
1807 if (!wt_string) wt_string = "???";
1808 if (sp == actor->sched_pos && s[sp]->worktype == actor->worktype)
1809 DEBUG(1, LEVEL_INFORMATIONAL, "*%d: location=0x%03x,0x%03x,0x%x time=%02d:00 day=%d worktype=0x%02x(%s)*\n", sp, s[sp]->x, s[sp]->y, s[sp]->z, s[sp]->hour, s[sp]->day_of_week, s[sp]->worktype, wt_string);
1810 else
1811 DEBUG(1, LEVEL_INFORMATIONAL, " %d: location=0x%03x,0x%03x,0x%x time=%02d:00 day=%d worktype=0x%02x(%s)\n", sp, s[sp]->x, s[sp]->y, s[sp]->z, s[sp]->hour, s[sp]->day_of_week, s[sp]->worktype, wt_string);
1812 } while (s[++sp]);
1813 }
1814
1815 if (!actor->surrounding_objects.empty())
1816 DEBUG(1, LEVEL_INFORMATIONAL, "Actor has multiple tiles\n");
1817 if (actor->pathfinder)
1818 DEBUG(1, LEVEL_INFORMATIONAL, "Actor is on a path\n");
1819 DEBUG(1, LEVEL_INFORMATIONAL, "\n");
1820 }
1821
1822
get_actor_alignment_str(uint8 alignment)1823 const char *get_actor_alignment_str(uint8 alignment) {
1824 switch (alignment) {
1825 case ACTOR_ALIGNMENT_DEFAULT :
1826 return "default";
1827 case ACTOR_ALIGNMENT_NEUTRAL :
1828 return "neutral";
1829 case ACTOR_ALIGNMENT_EVIL :
1830 return "evil";
1831 case ACTOR_ALIGNMENT_GOOD :
1832 return "good";
1833 case ACTOR_ALIGNMENT_CHAOTIC :
1834 return "chaotic";
1835 default :
1836 break;
1837 }
1838
1839 return "unknown";
1840 }
1841
set_invisible(bool invisible)1842 void Actor::set_invisible(bool invisible) {
1843 if (invisible) {
1844 if (!is_in_party())
1845 visible_flag = false;
1846 obj_flags |= OBJ_STATUS_INVISIBLE;
1847 } else {
1848 visible_flag = true;
1849 obj_flags &= ~OBJ_STATUS_INVISIBLE;
1850 }
1851 }
1852
count_readied_objects(sint32 objN,sint16 frameN,sint16 quality)1853 sint8 Actor::count_readied_objects(sint32 objN, sint16 frameN, sint16 quality) {
1854 sint8 count = 0;
1855 for (int o = 0; o < ACTOR_MAX_READIED_OBJECTS; o++) {
1856 if (readied_objects[o] == 0) continue;
1857 if (objN == -1
1858 || (readied_objects[o]->obj->obj_n == objN
1859 && (frameN == -1 || frameN == readied_objects[o]->obj->frame_n)
1860 && (quality == -1 || quality == readied_objects[o]->obj->quality)))
1861 ++count;
1862 }
1863 return count;
1864 }
1865
1866 // GOOD->CHAOTIC,EVIL
1867 // NEUTRAL->CHAOTIC
1868 // EVIL->GOOD,CHAOTIC
1869 // CHAOTIC->ALL except CHAOTIC
find_enemies()1870 ActorList *Actor::find_enemies() {
1871 const uint8 in_range = 24;
1872 ActorManager *actor_mgr = Game::get_game()->get_actor_manager();
1873 ActorList *actors = actor_mgr->filter_distance(actor_mgr->get_actor_list(), x, y, z, in_range);
1874 actor_mgr->filter_alignment(actors, alignment); // filter own alignment
1875 if (alignment != ACTOR_ALIGNMENT_CHAOTIC) {
1876 if (alignment == ACTOR_ALIGNMENT_NEUTRAL) {
1877 actor_mgr->filter_alignment(actors, ACTOR_ALIGNMENT_GOOD); // filter other friendlies
1878 actor_mgr->filter_alignment(actors, ACTOR_ALIGNMENT_EVIL);
1879 } else
1880 actor_mgr->filter_alignment(actors, ACTOR_ALIGNMENT_NEUTRAL);
1881 }
1882
1883 // remove party members and invisible actors FIXME: set party members to leader's alignment
1884 ActorIterator a = actors->begin();
1885 while (a != actors->end())
1886 if (is_in_party() && (*a)->is_in_party())
1887 a = actors->erase(a);
1888 else if ((*a)->is_invisible())
1889 a = actors->erase(a);
1890 else ++a;
1891 if (actors->empty()) {
1892 delete actors;
1893 return NULL; // no enemies in range
1894 }
1895 return actors;
1896 }
1897
find_body()1898 Obj *Actor::find_body() {
1899 Party *party;
1900 Actor *actor;
1901 Obj *body_obj = NULL;
1902 uint8 lvl;
1903
1904 party = Game::get_game()->get_party();
1905 actor = party->who_has_obj(339, id_n, true);
1906
1907 if (actor) //get from collective party inventory if possible
1908 return actor->inventory_get_object(339, id_n, OBJ_MATCH_QUALITY);
1909
1910 // try to find on map.
1911 for (lvl = 0; lvl < 5 && body_obj == NULL; lvl++)
1912 body_obj = obj_manager->find_obj(lvl, 339, id_n);
1913
1914 return body_obj;
1915 }
1916
1917 /* Change actor type. */
morph(uint16 objN)1918 bool Actor::morph(uint16 objN) {
1919 uint8 old_dir = get_direction(); // FIXME: this should get saved through init_from_obj()
1920
1921 Obj *actor_obj = make_obj();
1922 actor_obj->obj_n = objN;
1923 actor_obj->frame_n = 0;
1924 init_from_obj(actor_obj);
1925 set_dead_flag(false);
1926 set_direction(old_dir); // FIXME: this should get saved through init_from_obj()
1927 return true;
1928 }
1929
get_schedule_location(MapCoord * loc)1930 bool Actor::get_schedule_location(MapCoord *loc) {
1931 if (sched[sched_pos] == NULL)
1932 return false;
1933
1934 loc->x = sched[sched_pos]->x;
1935 loc->y = sched[sched_pos]->y;
1936 loc->z = sched[sched_pos]->z;
1937 return true;
1938 }
1939
is_at_scheduled_location()1940 bool Actor::is_at_scheduled_location() {
1941 if (sched[sched_pos] != NULL && x == sched[sched_pos]->x && y == sched[sched_pos]->y && z == sched[sched_pos]->z)
1942 return true;
1943
1944 return false;
1945 }
1946
get_schedule(uint8 index)1947 Schedule *Actor::get_schedule(uint8 index) {
1948 if (index >= num_schedules)
1949 return NULL;
1950
1951 return sched[index];
1952 }
1953
cure()1954 void Actor::cure() {
1955 set_poisoned(false);
1956 set_paralyzed(false);
1957 set_charmed(false);
1958 set_corpser_flag(false);
1959 set_cursed(false);
1960 set_asleep(false);
1961 }
1962
set_custom_tile_num(uint16 obj_num,uint16 tile_num)1963 void Actor::set_custom_tile_num(uint16 obj_num, uint16 tile_num) {
1964 if (custom_tile_tbl == NULL) {
1965 custom_tile_tbl = new Std::map<uint16, uint16>();
1966 }
1967
1968 (*custom_tile_tbl)[obj_num] = tile_num;
1969 }
1970
1971 } // End of namespace Nuvie
1972 } // End of namespace Ultima
1973