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