Files
scummvm-cursorfix/engines/ultima/nuvie/actors/u6_actor.cpp
2026-02-02 04:50:13 +01:00

1690 lines
43 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/core/nuvie_defs.h"
#include "ultima/nuvie/misc/u6_misc.h"
#include "ultima/nuvie/misc/u6_llist.h"
#include "ultima/nuvie/core/game.h"
#include "ultima/nuvie/usecode/u6_usecode.h"
#include "ultima/nuvie/pathfinder/sched_path_finder.h"
#include "ultima/nuvie/pathfinder/u6_astar_path.h"
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
#include "ultima/nuvie/actors/u6_actor.h"
#include "ultima/nuvie/core/party.h"
#include "ultima/nuvie/actors/actor_manager.h"
#include "ultima/nuvie/views/view_manager.h"
#include "ultima/nuvie/sound/sound_manager.h"
#include "ultima/nuvie/core/converse.h"
#include "ultima/nuvie/script/script.h"
#include "ultima/nuvie/core/effect.h"
#include "ultima/nuvie/pathfinder/combat_path_finder.h"
#include "ultima/nuvie/actors/u6_actor_types.h"
#include "ultima/nuvie/actors/u6_work_types.h"
#include "ultima/nuvie/core/weather.h"
namespace Ultima {
namespace Nuvie {
U6Actor::U6Actor(Map *m, ObjManager *om, GameClock *c): Actor(m, om, c),
actor_type(nullptr), walk_frame_inc(1), base_actor_type(nullptr),
current_movetype(MOVETYPE_U6_NONE) {
}
U6Actor::~U6Actor() {
}
bool U6Actor::init(uint8 obj_status) {
Actor::init();
base_actor_type = get_actor_type(base_obj_n);
if (base_actor_type->base_obj_n != base_obj_n) {
base_obj_n = base_actor_type->base_obj_n;
}
set_actor_obj_n(obj_n); //set actor_type
current_movetype = actor_type->movetype;
body_armor_class = base_actor_type->body_armor_class;
if (actor_type->tile_type == ACTOR_QT && frame_n == 0) //set the two quad tile actors to correct frame number.
frame_n = 3;
discover_direction();
if (has_surrounding_objs())
clear_surrounding_objs_list(); //clean up the old list if required.
if (is_alive() && x != 0 && y != 0) { //only try to init multi-tile actors if they are alive.
switch (obj_n) { //gather surrounding objects from map if required
case OBJ_U6_SHIP :
init_ship();
break;
case OBJ_U6_HYDRA :
init_hydra();
break;
case OBJ_U6_DRAGON :
init_dragon();
break;
case OBJ_U6_SILVER_SERPENT :
init_silver_serpent();
break;
case OBJ_U6_GIANT_SCORPION :
case OBJ_U6_GIANT_ANT :
case OBJ_U6_COW :
case OBJ_U6_ALLIGATOR :
case OBJ_U6_HORSE :
case OBJ_U6_HORSE_WITH_RIDER :
init_splitactor(obj_status);
break;
default :
break;
}
}
if (actor_type->can_sit) { // For some reason U6 starts with actors standing on their chairs.
// We need to sit them down.
Obj *obj = obj_manager->get_obj(x, y, z);
sit_on_chair(obj); // attempt to sit on obj.
}
inventory_make_all_objs_ok_to_take();
return true;
}
bool U6Actor::init_ship() {
Obj *obj;
uint16 obj1_x, obj1_y, obj2_x, obj2_y;
obj1_x = x;
obj1_y = y;
obj2_x = x;
obj2_y = y;
switch (direction) {
case NUVIE_DIR_N :
obj1_y = y + 1;
obj2_y = y - 1;
break;
case NUVIE_DIR_E :
obj1_x = x + 1;
obj2_x = x - 1;
break;
case NUVIE_DIR_S :
obj1_y = y - 1;
obj2_y = y + 1;
break;
case NUVIE_DIR_W :
obj1_x = x - 1;
obj2_x = x + 1;
break;
default:
error("Invalid direction in U6Actor::init_ship");
}
obj = obj_manager->get_obj(obj1_x, obj1_y, z);
if (obj == nullptr)
return false;
add_surrounding_obj(obj);
obj = obj_manager->get_obj(obj2_x, obj2_y, z);
if (obj == nullptr)
return false;
add_surrounding_obj(obj);
return true;
}
bool U6Actor::init_splitactor(uint8 obj_status) {
uint16 obj_x, obj_y;
obj_x = x;
obj_y = y;
switch (direction) {
case NUVIE_DIR_N :
obj_y = WRAPPED_COORD(y + 1, z);
break;
case NUVIE_DIR_E :
obj_x = WRAPPED_COORD(x - 1, z);
break;
case NUVIE_DIR_S :
obj_y = WRAPPED_COORD(y - 1, z);
break;
case NUVIE_DIR_W :
obj_x = WRAPPED_COORD(x + 1, z);
break;
default:
error("Invalid direction in U6Actor::init_splitactor");
}
// init back object
if (obj_status & OBJ_STATUS_MUTANT) {
init_surrounding_obj(obj_x, obj_y, z, obj_n, (get_reverse_direction(direction) * actor_type->tiles_per_direction + actor_type->tiles_per_frame - 1));
} else {
init_surrounding_obj(obj_x, obj_y, z, obj_n, frame_n + 8);
}
return true;
}
bool U6Actor::init_dragon() {
uint16 head_x, head_y, tail_x, tail_y;
uint16 wing1_x, wing1_y, wing2_x, wing2_y;
head_x = tail_x = x;
wing1_x = wing2_x = x;
head_y = tail_y = y;
wing1_y = wing2_y = y;
switch (direction) {
case NUVIE_DIR_N :
head_y = y - 1;
tail_y = y + 1;
wing1_x = x - 1;
wing2_x = x + 1;
break;
case NUVIE_DIR_E :
head_x = x + 1;
tail_x = x - 1;
wing1_y = y - 1;
wing2_y = y + 1;
break;
case NUVIE_DIR_S :
head_y = y + 1;
tail_y = y - 1;
wing1_x = x + 1;
wing2_x = x - 1;
break;
case NUVIE_DIR_W :
head_x = x - 1;
tail_x = x + 1;
wing1_y = y + 1;
wing2_y = y - 1;
break;
default:
error("Invalid direction in U6Actor::init_dragon");
}
init_surrounding_obj(head_x, head_y, z, obj_n, frame_n + 8);
init_surrounding_obj(tail_x, tail_y, z, obj_n, frame_n + 16);
init_surrounding_obj(wing1_x, wing1_y, z, obj_n, frame_n + 24);
init_surrounding_obj(wing2_x, wing2_y, z, obj_n, frame_n + 32);
return true;
}
bool U6Actor::init_hydra() {
// For some reason a Hydra has a different object number for its tenticles. :-(
init_surrounding_obj(x, y - 1, z, OBJ_U6_HYDRA_BODY, 0);
init_surrounding_obj(x + 1, y - 1, z, OBJ_U6_HYDRA_BODY, 4);
init_surrounding_obj(x + 1, y, z, OBJ_U6_HYDRA_BODY, 8);
init_surrounding_obj(x + 1, y + 1, z, OBJ_U6_HYDRA_BODY, 12);
init_surrounding_obj(x, y + 1, z, OBJ_U6_HYDRA_BODY, 16);
init_surrounding_obj(x - 1, y + 1, z, OBJ_U6_HYDRA_BODY, 20);
init_surrounding_obj(x - 1, y, z, OBJ_U6_HYDRA_BODY, 24);
init_surrounding_obj(x - 1, y - 1, z, OBJ_U6_HYDRA_BODY, 28);
return true;
}
bool U6Actor::init_silver_serpent() {
uint16 sx, sy, sz;
Obj *obj;
uint8 tmp_frame_n = 0;
sx = x;
sy = y;
sz = z;
switch (direction) {
case NUVIE_DIR_N :
sy++;
tmp_frame_n = 1;
break;
case NUVIE_DIR_E :
sx--;
tmp_frame_n = 3;
break;
case NUVIE_DIR_S :
sy--;
tmp_frame_n = 5;
break;
case NUVIE_DIR_W :
sx++;
tmp_frame_n = 7;
break;
default:
error("Invalid direction in U6Actor::init_silver_serpent");
}
obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_SILVER_SERPENT, 1, id_n, sx, sy, sz);
if (obj != nullptr) //old snake
gather_snake_objs_from_map(obj, x, y, z);
else { //new snake
//FIXME: we need to make long, randomly laid out snakes here!
init_new_silver_serpent();
}
// FIXME: Unused variable
(void)tmp_frame_n;
return true;
}
void U6Actor::init_new_silver_serpent() {
const struct {
uint8 body_frame_n;
uint8 tail_frame_n;
sint8 x_offset;
sint8 y_offset;
} movetbl[4] = { {10, 1, 0, 1}, {13, 7, 1, 0}, {12, 5, 0, -1}, {11, 3, -1, 0} };
uint8 i, j;
uint16 nx, ny;
Obj *obj;
uint8 length = 4 + NUVIE_RAND() % 5; //FIXME. The original worked out length from qty in the serpent embryo obj.
nx = x;
ny = y;
set_direction(NUVIE_DIR_N); //make sure we are facing north.
for (i = 0, j = 0; i < length; i++) {
nx += movetbl[j].x_offset;
ny += movetbl[j].y_offset;
init_surrounding_obj(nx, ny, z, OBJ_U6_SILVER_SERPENT, (i == length - 1 ? movetbl[j].tail_frame_n : movetbl[j].body_frame_n));
obj = (Obj *)surrounding_objects.back();
obj->quality = i + 1; //body segment number
obj->qty = id_n; //actor id number
j = (j + 1) % 4;
}
return;
}
void U6Actor::gather_snake_objs_from_map(Obj *start_obj, uint16 ax, uint16 ay, uint16 az) {
Obj *obj;
uint16 px, py; // , pz;
uint16 nx, ny, nz;
uint8 seg_num;
px = ax;
py = ay;
// pz = az;
obj = start_obj;
add_surrounding_obj(obj);
for (seg_num = 2; obj && obj->frame_n >= 8; seg_num++) {
nx = obj->x;
ny = obj->y;
nz = obj->z;
//work out the location of the next obj based on the current frame_n and relative movement.
switch (obj->frame_n) {
//up down
case 8 :
if (ny - 1 == py)
ny++;
else
ny--;
break;
//left right
case 9 :
if (nx - 1 == px)
nx++;
else
nx--;
break;
//up right
case 10 :
if (ny - 1 == py)
nx++;
else
ny--;
break;
//down right
case 11 :
if (ny + 1 == py)
nx++;
else
ny++;
break;
//left down
case 12 :
if (nx - 1 == px)
ny++;
else
nx--;
break;
//left up
case 13 :
if (nx - 1 == px)
ny--;
else
nx--;
break;
}
px = obj->x;
py = obj->y;
//pz = obj->z;
obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_SILVER_SERPENT, seg_num, id_n, nx, ny, nz);
if (obj)
add_surrounding_obj(obj);
}
}
uint16 U6Actor::get_downward_facing_tile_num() const {
uint8 shift = 0;
if (base_actor_type->frames_per_direction > 1) //we want the second frame for most actor types.
shift = 1;
return get_tile_num(base_actor_type->base_obj_n) + base_actor_type->tile_start_offset + (NUVIE_DIR_S * base_actor_type->tiles_per_direction + base_actor_type->tiles_per_frame - 1) + shift;
}
bool U6Actor::updateSchedule(uint8 hour, bool teleport) {
bool ret;
handle_lightsource(hour);
if ((ret = Actor::updateSchedule(hour, teleport)) == true) { //walk to next schedule location if required.
if (sched[sched_pos] != nullptr && (sched[sched_pos]->x != x || sched[sched_pos]->y != y || sched[sched_pos]->z != z
|| worktype == WORKTYPE_U6_SLEEP)) { // needed to go underneath bed if teleporting
set_worktype(WORKTYPE_U6_WALK_TO_LOCATION);
MapCoord loc(sched[sched_pos]->x, sched[sched_pos]->y, sched[sched_pos]->z);
pathfind_to(loc);
}
}
return ret;
}
// workout our direction based on actor_type and frame_n
inline void U6Actor::discover_direction() {
if (actor_type->frames_per_direction != 0)
direction = static_cast<NuvieDir>((frame_n - actor_type->tile_start_offset) / actor_type->tiles_per_direction);
else
direction = NUVIE_DIR_S;
}
void U6Actor::change_base_obj_n(uint16 val) {
Actor::change_base_obj_n(val);
clear_surrounding_objs_list(REMOVE_SURROUNDING_OBJS);
init();
}
void U6Actor::set_direction(NuvieDir d) {
if (is_alive() == false || is_immobile())
return;
uint8 frames_per_dir = (actor_type->frames_per_direction != 0)
? actor_type->frames_per_direction : 4;
if (d >= 4) // ignore diagonals
return;
if (walk_frame == 0)
walk_frame_inc = 1; // loop forward
else if (walk_frame == (frames_per_dir - 1))
walk_frame_inc = -1; // loop backward
walk_frame = (walk_frame + walk_frame_inc) % frames_per_dir;
if (has_surrounding_objs()) {
if (direction != d)
set_direction_of_surrounding_objs(d);
else {
if (can_move && actor_type->twitch_rand) //only twitch actors with a non zero twitch_rand.
twitch_surrounding_objs();
}
}
direction = d;
//only change direction frame if the actor can twitch ie isn't sitting or in bed etc.
if (can_move && obj_n != OBJ_U6_SLIME)
frame_n = actor_type->tile_start_offset + (direction * actor_type->tiles_per_direction +
(walk_frame * actor_type->tiles_per_frame) + actor_type->tiles_per_frame - 1);
// tangle vines' north and east frames are in the wrong direction
// FIXME: see if the ActorType values can be changed to fix this
if (obj_n == OBJ_U6_TANGLE_VINE)
if (direction == NUVIE_DIR_N || direction == NUVIE_DIR_E)
frame_n += 3;
}
void U6Actor::face_location(uint16 lx, uint16 ly) {
if (obj_n != OBJ_U6_SILVER_SERPENT //snakes cannot turn on the spot.
&& obj_n != OBJ_U6_TANGLE_VINE && obj_n != OBJ_U6_TANGLE_VINE_POD)
Actor::face_location(lx, ly);
return;
}
void U6Actor::clear() {
if (has_surrounding_objs()) {
remove_surrounding_objs_from_map();
clear_surrounding_objs_list(REMOVE_SURROUNDING_OBJS);
}
Actor::clear();
return;
}
bool U6Actor::move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags) {
assert(new_z < 6);
// bool force_move = flags & ACTOR_FORCE_MOVE;
bool ret;
sint16 rel_x, rel_y;
//MsgScroll *scroll = Game::get_game()->get_scroll();
Player *player = Game::get_game()->get_player();
//Party *party = player->get_party();
MapCoord old_pos = get_location();
if (has_surrounding_objs())
remove_surrounding_objs_from_map();
rel_x = new_x - x;
rel_y = new_y - y;
if ((flags & ACTOR_OPEN_DOORS) && worktype != WORKTYPE_U6_WALK_TO_LOCATION)
flags ^= ACTOR_OPEN_DOORS; // only use doors when walking to schedule location
ret = Actor::move(new_x, new_y, new_z, flags);
if (ret == true) {
if (has_surrounding_objs())
move_surrounding_objs_relative(rel_x, rel_y);
Obj *obj = obj_manager->get_obj(new_x, new_y, new_z, false); // Ouch, we get obj in Actor::move() too :(
if (obj) {
if (actor_type->can_sit)
sit_on_chair(obj); // make the Actor sit if they are on top of a chair.
}
set_hit_flag(false);
Game::get_game()->get_script()->call_actor_map_dmg(this, get_location());
}
// temp. fix; this too should be done with UseCode (and don't move the mirror)
if (old_pos.y > 0 && new_y > 0) {
Obj *old_mirror = obj_manager->get_obj_of_type_from_location(OBJ_U6_MIRROR, old_pos.x, old_pos.y - 1, old_pos.z);
Obj *mirror = obj_manager->get_obj_of_type_from_location(OBJ_U6_MIRROR, new_x, new_y - 1, new_z);
if (old_mirror && old_mirror->frame_n != 2) old_mirror->frame_n = 0;
if (mirror && mirror->frame_n != 2) mirror->frame_n = 1;
}
// Cyclops: shake ground if player is near
if (actor_type->base_obj_n == OBJ_U6_CYCLOPS && is_nearby(player->get_actor())) {
Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_EARTH_QUAKE);
new QuakeEffect(1, 200, player->get_actor());
}
if (has_surrounding_objs()) //add our surrounding objects back onto the map.
add_surrounding_objs_to_map();
return ret;
}
bool U6Actor::check_move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags) {
// bool ignore_actors = flags & ACTOR_IGNORE_OTHERS;
const Tile *map_tile;
if (Actor::check_move(new_x, new_y, new_z, flags) == false)
return false;
if (obj_n == OBJ_U6_SILVER_SERPENT && check_move_silver_serpent(new_x, new_y) == false)
return false;
switch (current_movetype) {
case MOVETYPE_U6_ETHEREAL:
return true;
case MOVETYPE_U6_NONE :
return false;
case MOVETYPE_U6_WATER_HIGH : // for HIGH we only want to move to open water.
// No shorelines.
map_tile = map->get_tile(new_x, new_y, new_z, MAP_ORIGINAL_TILE);
if (map_tile->tile_num >= 16 && map_tile->tile_num <= 47)
return false;
if (!map->is_water(new_x, new_y, new_z))
return false;
break;
case MOVETYPE_U6_WATER_LOW :
if (!map->is_water(new_x, new_y, new_z))
return false;
break;
case MOVETYPE_U6_AIR_LOW :
map_tile = map->get_tile(new_x, new_y, new_z, MAP_ORIGINAL_TILE);
if (map_tile->flags1 & TILEFLAG_WALL) //low air boundary
return false;
map_tile = obj_manager->get_obj_tile(new_x, new_y, new_z, false);
if (map_tile && ((map_tile->flags1 & TILEFLAG_WALL) ||
(map_tile->flags2 & (TILEFLAG_DOUBLE_WIDTH | TILEFLAG_DOUBLE_HEIGHT)) == (TILEFLAG_DOUBLE_WIDTH | TILEFLAG_DOUBLE_HEIGHT)))
return false;
break;
case MOVETYPE_U6_AIR_HIGH :
if (map->is_boundary(new_x, new_y, new_z))
return false; //FIX for proper air boundary
break;
case MOVETYPE_U6_LAND :
default :
if (map->is_passable(new_x, new_y, new_z) == false) {
if (obj_n == OBJ_U6_MOUSE // try to go through mousehole
&& (obj_manager->get_obj_of_type_from_location(OBJ_U6_MOUSEHOLE, new_x, new_y, new_z) != nullptr
|| obj_manager->get_obj_of_type_from_location(OBJ_U6_BARS, new_x, new_y, new_z) != nullptr
|| obj_manager->get_obj_of_type_from_location(OBJ_U6_PORTCULLIS, new_x, new_y, new_z) != nullptr))
return true;
if (obj_n == OBJ_U6_SILVER_SERPENT //silver serpents can crossover themselves
&& obj_manager->get_obj_of_type_from_location(OBJ_U6_SILVER_SERPENT, new_x, new_y, new_z) != nullptr)
return true;
return false;
}
}
return true;
}
bool U6Actor::check_move_silver_serpent(uint16 new_x, uint16 new_y) {
if (new_x != x && new_y != y) //snakes can't move diagonally
return false;
Obj *obj = (Obj *)surrounding_objects.front(); //retrieve the first body segment.
if (obj->x == new_x && obj->y == new_y) //snakes can't move backwards.
return false;
return true;
}
// attempt to sit if obj is a chair.
bool U6Actor::sit_on_chair(Obj *obj) {
if (actor_type->can_sit && obj) {
if (obj->obj_n == OBJ_U6_CHAIR) { // make the actor sit on a chair.
if (obj_n == OBJ_U6_MUSICIAN_PLAYING)
frame_n = (obj->frame_n * 2);
else
frame_n = (obj->frame_n * 4) + 3;
direction = static_cast<NuvieDir>(obj->frame_n);
can_move = false;
return true;
}
//make actor sit on LB's throne.
if (obj->obj_n == OBJ_U6_THRONE && obj->x != x) { //throne is a double width obj. We only sit on the left tile.
frame_n = 8 + 3; //sitting facing south.
direction = NUVIE_DIR_S;
can_move = false;
return true;
}
}
return false;
}
uint8 U6Actor::get_object_readiable_location(Obj *obj) {
uint16 i;
for (i = 0; readiable_objects[i].obj_n != OBJ_U6_NOTHING; i++) {
if (obj->obj_n == readiable_objects[i].obj_n)
return readiable_objects[i].readiable_location;
}
return ACTOR_NOT_READIABLE;
}
const CombatType *U6Actor::get_object_combat_type(uint16 objN) {
uint16 i;
for (i = 0; u6combat_objects[i].obj_n != OBJ_U6_NOTHING; i++) {
if (objN == u6combat_objects[i].obj_n)
return &u6combat_objects[i];
}
return nullptr;
}
const CombatType *U6Actor::get_hand_combat_type() const {
if (obj_n == OBJ_U6_SHIP)
return &u6combat_ship_cannon;
return &u6combat_hand;
}
bool U6Actor::weapon_can_hit(const CombatType *weapon, Actor *target, uint16 *hit_x, uint16 *hit_y) {
if (Actor::weapon_can_hit(weapon, target->get_x(), target->get_y())) {
*hit_x = target->get_x();
*hit_y = target->get_y();
return true;
}
const Std::list<Obj *> &surrounding_objs = target->get_surrounding_obj_list();
for (Obj *obj : surrounding_objs) {
if (Actor::weapon_can_hit(weapon, obj->x, obj->y)) {
*hit_x = obj->x;
*hit_y = obj->y;
return true;
}
}
uint16 target_x = target->get_x();
uint16 target_y = target->get_y();
Tile *tile = target->get_tile();
if (tile->dbl_width && tile->dbl_height) {
if (Actor::weapon_can_hit(weapon, target_x - 1, target_y - 1)) {
*hit_x = target_x - 1;
*hit_y = target_y - 1;
return true;
}
}
if (tile->dbl_width) {
if (Actor::weapon_can_hit(weapon, target_x - 1, target_y)) {
*hit_x = target_x - 1;
*hit_y = target_y;
return true;
}
}
if (tile->dbl_height) {
if (Actor::weapon_can_hit(weapon, target_x, target_y - 1)) {
*hit_x = target_x;
*hit_y = target_y - 1;
return true;
}
}
return false;
}
void U6Actor::twitch() {
if (can_twitch() == false)
return;
if (NUVIE_RAND() % actor_type->twitch_rand == 1)
do_twitch();
return;
}
void U6Actor::do_twitch() {
if (actor_type->frames_per_direction == 0)
walk_frame = (walk_frame + 1) % 4;
else
walk_frame = NUVIE_RAND() % actor_type->frames_per_direction;
if (has_surrounding_objs()) {
switch (obj_n) {
case OBJ_U6_HYDRA :
twitch_surrounding_hydra_objs();
break;
case OBJ_U6_DRAGON :
default :
twitch_surrounding_objs();
break;
}
}
frame_n = actor_type->tile_start_offset + (direction * actor_type->tiles_per_direction + (walk_frame * actor_type->tiles_per_frame) + actor_type->tiles_per_frame - 1);
if (obj_n == OBJ_U6_WISP) {
Game::get_game()->get_map_window()->updateAmbience();
}
}
void U6Actor::set_paralyzed(bool paralyzed) {
if (paralyzed) {
status_flags |= ACTOR_STATUS_PARALYZED;
} else {
status_flags &= (0xff ^ ACTOR_STATUS_PARALYZED);
}
}
void U6Actor::set_protected(bool val) {
if (val) {
status_flags |= ACTOR_STATUS_PROTECTED;
} else {
status_flags &= (0xff ^ ACTOR_STATUS_PROTECTED);
}
}
void U6Actor::set_charmed(bool val) {
if (val) {
obj_flags |= OBJ_STATUS_CHARMED;
} else {
obj_flags &= (0xff ^ OBJ_STATUS_CHARMED);
}
}
void U6Actor::set_corpser_flag(bool val) {
if (val) {
movement_flags |= ACTOR_MOVEMENT_FLAGS_CORPSER;
} else {
movement_flags &= (0xff ^ ACTOR_MOVEMENT_FLAGS_CORPSER);
}
}
void U6Actor::set_cursed(bool val) {
if (val) {
obj_flags |= OBJ_STATUS_CURSED;
} else {
obj_flags &= (0xff ^ OBJ_STATUS_CURSED);
}
}
void U6Actor::set_asleep(bool val) {
if (val) {
status_flags |= ACTOR_STATUS_ASLEEP;
if (actor_type->dead_obj_n != OBJ_U6_NOTHING && actor_type->can_laydown) {
obj_n = actor_type->dead_obj_n;
frame_n = actor_type->dead_frame_n;
}
} else {
status_flags &= (0xff ^ ACTOR_STATUS_ASLEEP);
if (obj_n == base_actor_type->dead_obj_n || obj_n == OBJ_U6_PERSON_SLEEPING) {
if (worktype == WORKTYPE_U6_SLEEP)
can_move = true;
actor_type = base_actor_type;
obj_n = base_actor_type->base_obj_n;
frame_n = 0;
}
}
}
void U6Actor::set_worktype(uint8 new_worktype, bool init) {
if (new_worktype == worktype)
return;
if (worktype == WORKTYPE_U6_SLEEP || worktype == WORKTYPE_U6_PLAY_LUTE) {
frame_n = old_frame_n;
}
//reset to base obj_n
if ((!is_in_party() || worktype > 0xe) && base_actor_type->base_obj_n != OBJ_U6_NOTHING) //don't revert for party worktypes as they might be riding a horse.
set_actor_obj_n(base_actor_type->base_obj_n);
if (worktype == WORKTYPE_U6_SLEEP && (status_flags & ACTOR_STATUS_ASLEEP)) //FIXME do we still need this??
status_flags ^= ACTOR_STATUS_ASLEEP;
Actor::set_worktype(new_worktype);
if (worktype == WORKTYPE_U6_WALK_TO_LOCATION) {
setup_walk_to_location();
}
//FIX from here.
switch (worktype) {
case WORKTYPE_U6_FACE_NORTH :
set_direction(NUVIE_DIR_N);
break;
case WORKTYPE_U6_FACE_EAST :
set_direction(NUVIE_DIR_E);
break;
case WORKTYPE_U6_FACE_SOUTH :
set_direction(NUVIE_DIR_S);
break;
case WORKTYPE_U6_FACE_WEST :
set_direction(NUVIE_DIR_W);
break;
case WORKTYPE_U6_SLEEP :
wt_sleep(init);
break;
case WORKTYPE_U6_PLAY_LUTE :
wt_play_lute();
break;
}
}
void U6Actor::pathfind_to(const MapCoord &d) {
if (pathfinder) {
pathfinder->set_actor(this);
pathfinder->set_goal(d);
} else
set_pathfinder(new SchedPathFinder(this, d, new U6AStarPath));
pathfinder->update_location();
}
void U6Actor::setup_walk_to_location() {
if (sched[sched_pos] != nullptr) {
if (x == sched[sched_pos]->x && y == sched[sched_pos]->y
&& z == sched[sched_pos]->z) {
set_worktype(sched[sched_pos]->worktype);
delete_pathfinder();
return;
}
if (!pathfinder) {
work_location.x = sched[sched_pos]->x;
work_location.y = sched[sched_pos]->y;
work_location.z = sched[sched_pos]->z;
// if(!work_location.is_visible() || !get_location().is_visible())
// set_pathfinder(new OffScreenPathFinder(this, work_location, new U6AStarPath));
// else
set_pathfinder(new SchedPathFinder(this, work_location, new U6AStarPath));
}
}
}
// wander around but don't cross boundaries or fences. Used for cows and horses.
// now that hazards are working properly, this isn't needed --SB-X
/*void U6Actor::wt_farm_animal_wander()
{
uint8 new_direction;
sint8 rel_x = 0, rel_y = 0;
if(NUVIE_RAND()%8 == 1)
{
new_direction = NUVIE_RAND()%4;
switch(new_direction)
{
case NUVIE_DIR_N : rel_y = -1; break;
case NUVIE_DIR_E : rel_x = 1; break;
case NUVIE_DIR_S : rel_y = 1; break;
case NUVIE_DIR_W : rel_x = -1; break;
}
if(obj_manager->get_obj_of_type_from_location(OBJ_U6_FENCE,x + rel_x, y + rel_y, z) == nullptr)
{
if(moveRelative(rel_x,rel_y))
set_direction(new_direction);
}
}
else set_moves_left(moves - 5);
return;
}*/
void U6Actor::wt_sleep(bool init) {
if (init && !is_sleeping())
return;
Obj *obj = obj_manager->get_obj(x, y, z);
can_move = false;
status_flags |= ACTOR_STATUS_ASLEEP;
if (obj) {
if (obj->obj_n == OBJ_U6_BED) {
if (obj->frame_n == 1 || obj->frame_n == 5) { //horizontal bed
old_frame_n = frame_n;
obj_n = OBJ_U6_PERSON_SLEEPING;
frame_n = 0;
}
if (obj->frame_n == 7 || obj->frame_n == 10) { //vertical bed
old_frame_n = frame_n;
obj_n = OBJ_U6_PERSON_SLEEPING;
frame_n = 1;
}
return;
}
}
// lay down on the ground using the dead body frame
if (actor_type->can_laydown) {
old_frame_n = frame_n;
obj_n = actor_type->dead_obj_n;
frame_n = actor_type->dead_frame_n;
}
}
void U6Actor::wt_play_lute() {
set_actor_obj_n(OBJ_U6_MUSICIAN_PLAYING);
frame_n = direction * actor_type->tiles_per_direction;
Obj *obj = obj_manager->get_obj(x, y, z);
sit_on_chair(obj); // attempt to sit on obj.
return;
}
void U6Actor::set_actor_obj_n(uint16 new_obj_n) {
old_frame_n = frame_n;
obj_n = new_obj_n;
actor_type = get_actor_type(new_obj_n);
return;
}
inline const U6ActorType *U6Actor::get_actor_type(uint16 new_obj_n) {
const U6ActorType *type;
for (type = u6ActorTypes; type->base_obj_n != OBJ_U6_NOTHING; type++) {
if (type->base_obj_n == new_obj_n)
break;
}
return type;
}
inline bool U6Actor::has_surrounding_objs() {
if (actor_type->tile_type == ACTOR_DT || actor_type->tile_type == ACTOR_MT)
return true;
return false;
}
inline void U6Actor::remove_surrounding_objs_from_map() {
for (Obj *obj : surrounding_objects)
obj_manager->remove_obj_from_map(obj);
return;
}
inline void U6Actor::add_surrounding_objs_to_map() {
for (Obj *obj : surrounding_objects)
obj_manager->add_obj(obj, OBJ_ADD_TOP);
return;
}
inline void U6Actor::move_surrounding_objs_relative(sint16 rel_x, sint16 rel_y) {
if (obj_n == OBJ_U6_SILVER_SERPENT) {
move_silver_serpent_objs_relative(rel_x, rel_y);
} else {
for (Obj *obj : surrounding_objects) {
obj->x = WRAPPED_COORD(obj->x + rel_x, z);
obj->y = WRAPPED_COORD(obj->y + rel_y, z);
}
}
return;
}
inline void U6Actor::move_silver_serpent_objs_relative(sint16 rel_x, sint16 rel_y) {
static const uint8 new_frame_n_tbl[5][5] = {
{ 8, 10, 0, 13, 0},
{12, 9, 0, 0, 13},
{ 0, 0, 0, 0, 0},
{11, 0, 0, 9, 10},
{ 0, 11, 0, 12, 8}
};
static const uint8 new_tail_frame_n_tbl[8][6] = {
{0, 0, 0, 0, 0, 0},
{1, 0, 0, 3, 7, 0},
{0, 0, 0, 0, 0, 0},
{0, 3, 0, 0, 5, 1},
{0, 0, 0, 0, 0, 0},
{5, 0, 3, 0, 0, 7},
{0, 0, 0, 0, 0, 0},
{0, 7, 1, 5, 0, 0}
};
if (surrounding_objects.empty())
return;
Std::list<Obj *>::iterator obj = surrounding_objects.begin();
sint8 new_pos = 2 + rel_x + (rel_y * 2);
uint16 old_x = (*obj)->x;
uint16 old_y = (*obj)->y;
(*obj)->x = x - rel_x; // old actor x
(*obj)->y = y - rel_y; // old actor y
sint8 old_pos = 2 + ((*obj)->x - old_x) + (((*obj)->y - old_y) * 2);
uint8 objFrameN = (*obj)->frame_n;
(*obj)->frame_n = new_frame_n_tbl[new_pos][old_pos];
obj++;
for (; obj != surrounding_objects.end(); obj++) {
uint16 tmp_x = (*obj)->x;
uint16 tmp_y = (*obj)->y;
uint8 tmp_frame_n = (*obj)->frame_n;
(*obj)->x = old_x;
(*obj)->y = old_y;
if (tmp_frame_n < 8) //tail, work out new tail direction
(*obj)->frame_n = new_tail_frame_n_tbl[tmp_frame_n][objFrameN - 8];
else
(*obj)->frame_n = objFrameN;
old_x = tmp_x;
old_y = tmp_y;
objFrameN = tmp_frame_n;
}
return;
}
inline void U6Actor::set_direction_of_surrounding_objs(NuvieDir new_direction) {
remove_surrounding_objs_from_map();
switch (obj_n) {
case OBJ_U6_SHIP :
set_direction_of_surrounding_ship_objs(new_direction);
break;
case OBJ_U6_GIANT_SCORPION :
case OBJ_U6_GIANT_ANT :
case OBJ_U6_COW :
case OBJ_U6_ALLIGATOR :
case OBJ_U6_HORSE :
case OBJ_U6_HORSE_WITH_RIDER :
set_direction_of_surrounding_splitactor_objs(new_direction);
break;
case OBJ_U6_DRAGON :
set_direction_of_surrounding_dragon_objs(new_direction);
break;
}
add_surrounding_objs_to_map();
return;
}
inline void U6Actor::set_direction_of_surrounding_ship_objs(NuvieDir new_direction) {
Std::list<Obj *>::iterator obj = surrounding_objects.begin();
if (obj == surrounding_objects.end())
return;
uint16 pitch = map->get_width(z);
(*obj)->x = x;
(*obj)->y = y;
(*obj)->frame_n = new_direction * actor_type->tiles_per_direction + actor_type->tiles_per_frame - 1;
switch (new_direction) {
case NUVIE_DIR_N :
if (y == 0)
(*obj)->y = pitch - 1;
else
(*obj)->y = y - 1;
break;
case NUVIE_DIR_E :
if (x == pitch - 1)
(*obj)->x = 0;
else
(*obj)->x = x + 1;
break;
case NUVIE_DIR_S :
if (y == pitch - 1)
(*obj)->y = 0;
else
(*obj)->y = y + 1;
break;
case NUVIE_DIR_W :
if (x == 0)
(*obj)->x = pitch - 1;
else
(*obj)->x = x - 1;
break;
default:
error("Invalid dir for U6Actor::set_direction_of_surrounding_ship_objs");
}
obj++;
if (obj == surrounding_objects.end())
return;
(*obj)->x = x;
(*obj)->y = y;
(*obj)->frame_n = 16 + (new_direction * actor_type->tiles_per_direction + actor_type->tiles_per_frame - 1);
switch (new_direction) {
case NUVIE_DIR_N :
if (y == pitch - 1)
(*obj)->y = 0;
else
(*obj)->y = y + 1;
break;
case NUVIE_DIR_E :
if (x == 0)
(*obj)->x = pitch - 1;
else
(*obj)->x = x - 1;
break;
case NUVIE_DIR_S :
if (y == 0)
(*obj)->y = pitch - 1;
else
(*obj)->y = y - 1;
break;
case NUVIE_DIR_W :
if (x == pitch - 1)
(*obj)->x = 0;
else
(*obj)->x = x + 1;
break;
default:
error("Invalid dir for U6Actor::set_direction_of_surrounding_ship_objs");
}
}
inline void U6Actor::set_direction_of_surrounding_splitactor_objs(NuvieDir new_direction) {
if (surrounding_objects.empty())
return;
uint16 pitch = map->get_width(z);
Obj *obj = surrounding_objects.back();
if (obj->frame_n < 8)
obj->frame_n = (get_reverse_direction(new_direction) * actor_type->tiles_per_direction + actor_type->tiles_per_frame - 1); //mutant actor
else
obj->frame_n = 8 + (new_direction * actor_type->tiles_per_direction + actor_type->tiles_per_frame - 1);
obj->x = x;
obj->y = y;
switch (new_direction) {
case NUVIE_DIR_N :
if (y == pitch - 1)
obj->y = 0;
else
obj->y = y + 1;
break;
case NUVIE_DIR_E :
if (x == 0)
obj->x = pitch - 1;
else
obj->x = x - 1;
break;
case NUVIE_DIR_S :
if (y == 0)
obj->y = pitch - 1;
else
obj->y = y - 1;
break;
case NUVIE_DIR_W :
if (x == pitch - 1)
obj->x = 0;
else
obj->x = x + 1;
break;
default:
error("Invalid direction in U6Actor::set_direction_of_surrounding_splitactor_objs");
}
}
inline void U6Actor::set_direction_of_surrounding_dragon_objs(NuvieDir new_direction) {
Std::list<Obj *>::iterator obj;
uint8 frame_offset = (new_direction * actor_type->tiles_per_direction + actor_type->tiles_per_frame - 1);
Obj *head, *tail, *wing1, *wing2;
//NOTE! this is dependent on the order the in which the objects are loaded in U6Actor::init_dragon()
obj = surrounding_objects.begin();
if (obj == surrounding_objects.end())
return;
head = *obj;
head->frame_n = 8 + frame_offset;
head->x = x;
head->y = y;
obj++;
if (obj == surrounding_objects.end())
return;
tail = *obj;
tail->frame_n = 16 + frame_offset;
tail->x = x;
tail->y = y;
obj++;
if (obj == surrounding_objects.end())
return;
wing1 = *obj;
wing1->frame_n = 24 + frame_offset;
wing1->x = x;
wing1->y = y;
obj++;
if (obj == surrounding_objects.end())
return;
wing2 = *obj;
wing2->frame_n = 32 + frame_offset;
wing2->x = x;
wing2->y = y;
switch (new_direction) {
case NUVIE_DIR_N :
head->y = y - 1;
tail->y = y + 1;
wing1->x = x - 1;
wing2->x = x + 1;
break;
case NUVIE_DIR_E :
head->x = x + 1;
tail->x = x - 1;
wing1->y = y - 1;
wing2->y = y + 1;
break;
case NUVIE_DIR_S :
head->y = y + 1;
tail->y = y - 1;
wing1->x = x + 1;
wing2->x = x - 1;
break;
case NUVIE_DIR_W :
head->x = x - 1;
tail->x = x + 1;
wing1->y = y + 1;
wing2->y = y - 1;
break;
default:
error("Invalid direction in U6Actor::set_direction_of_surrounding_dragon_objs");
}
}
inline void U6Actor::twitch_surrounding_objs() {
for (Obj *obj : surrounding_objects)
twitch_obj(obj);
}
inline void U6Actor::twitch_surrounding_dragon_objs() {
}
inline void U6Actor::twitch_surrounding_hydra_objs() {
Std::list<Obj *>::iterator obj;
int i;
//Note! list order is important here. As it corresponds to the frame order in the tile set. This is defined in init_hydra()
for (i = 0, obj = surrounding_objects.begin(); obj != surrounding_objects.end(); obj++, i += 4) {
if (NUVIE_RAND() % 4 == 0)
(*obj)->frame_n = i + (((*obj)->frame_n - i + 1) % 4);
}
}
inline void U6Actor::twitch_obj(Obj *obj) {
if (actor_type->frames_per_direction == 0) {
DEBUG(0, LEVEL_WARNING, "FIXME: %s frames_per_direction == 0\n", get_name());
obj->frame_n = (obj->frame_n / (1 * 4) * (1 * 4)) + direction * actor_type->tiles_per_direction +
walk_frame * actor_type->tiles_per_frame;
return;
}
switch (obj->obj_n) {
case OBJ_U6_GIANT_SCORPION :
case OBJ_U6_GIANT_ANT :
case OBJ_U6_COW :
case OBJ_U6_ALLIGATOR :
case OBJ_U6_HORSE :
if (obj->frame_n < 8) { //mutant actor with two heads
obj->frame_n = get_reverse_direction(direction) * actor_type->tiles_per_direction +
walk_frame * actor_type->tiles_per_frame;
return;
}
break;
default :
break;
}
obj->frame_n = (obj->frame_n / (actor_type->frames_per_direction * 4) * (actor_type->frames_per_direction * 4)) + direction * actor_type->tiles_per_direction +
walk_frame * actor_type->tiles_per_frame;
}
inline void U6Actor::clear_surrounding_objs_list(bool delete_objs) {
if (surrounding_objects.empty())
return;
if (delete_objs == false) {
surrounding_objects.clear();
return;
}
while (!surrounding_objects.empty()) {
Obj *obj = surrounding_objects.front();
obj_manager->remove_obj_from_map(obj);
delete_obj(obj);
surrounding_objects.pop_front();
}
return;
}
inline void U6Actor::init_surrounding_obj(uint16 x_, uint16 y_, uint8 z_, uint16 actor_obj_n, uint16 objFrame_n) {
Obj *obj;
obj = obj_manager->get_obj_of_type_from_location(actor_obj_n, id_n, -1, x_, y_, z_);
if (obj == nullptr)
obj = obj_manager->get_obj_of_type_from_location(actor_obj_n, 0, -1, x_, y_, z_);
if (obj == nullptr) {
obj = new Obj();
obj->x = x_;
obj->y = y_;
obj->z = z_;
obj->obj_n = actor_obj_n;
obj->frame_n = objFrame_n;
obj_manager->add_obj(obj);
}
obj->quality = id_n;
add_surrounding_obj(obj);
return;
}
void U6Actor::die(bool create_body) {
Game *game = Game::get_game();
Party *party = game->get_party();
Player *player = game->get_player();
MapCoord actor_loc = get_location();
if (party->get_member_num(this) == 0) //avatar
return; //The avatar can't die. They just get teleported back to LB's castle.
if (has_surrounding_objs())
clear_surrounding_objs_list(true);
set_dead_flag(true); // needed sooner for unready usecode of torches
if (game->is_armageddon())
inventory_drop_all();
else if (base_actor_type->dead_obj_n != OBJ_U6_NOTHING) {
if (create_body) {
Obj *dead_body = new Obj;
dead_body->obj_n = base_actor_type->dead_obj_n;
if (base_actor_type->dead_frame_n == 255) // dog, cat, mouse, deer, wolf, drake, mongbat
dead_body->frame_n = frame_n; // same frame the actor died
else if (base_actor_type->dead_obj_n == OBJ_U6_BLOOD)
dead_body->frame_n = NUVIE_RAND() % 3;
else
dead_body->frame_n = base_actor_type->dead_frame_n;
dead_body->x = actor_loc.x;
dead_body->y = actor_loc.y;
dead_body->z = actor_loc.z;
dead_body->quality = id_n;
dead_body->status = OBJ_STATUS_OK_TO_TAKE;
if (temp_actor)
dead_body->status |= OBJ_STATUS_TEMPORARY;
if (base_actor_type->dead_obj_n == OBJ_U6_BLOOD)
inventory_drop_all();
else // move my inventory into the dead body container
all_items_to_container(dead_body, false);
obj_manager->add_obj(dead_body, true);
}
} else if (create_body)
inventory_drop_all();
Actor::die();
if (is_in_party()) {
party->remove_actor(this, true);
if (player->get_actor() == this)
player->set_party_mode(party->get_actor(0)); //set party mode with the avatar as the leader.
}
if (party->get_member_num(this) != 0)
move(0, 0, 0, ACTOR_FORCE_MOVE); // FIXME: move to another plane, same coords
}
// frozen by worktype or status
bool U6Actor::is_immobile() const {
return (((worktype == WORKTYPE_U6_MOTIONLESS
|| worktype == WORKTYPE_U6_IMMOBILE) && !is_in_party())
|| get_corpser_flag() || is_sleeping() || is_paralyzed()
/*|| can_move == false*/); // can_move really means can_twitch/animate
}
bool U6Actor::can_twitch() {
return ((can_move || obj_n == OBJ_U6_MUSICIAN_PLAYING)
&& visible_flag
&& actor_type->twitch_rand != 0
&& !get_corpser_flag()
&& !is_sleeping()
&& !is_paralyzed());
}
bool U6Actor::can_be_passed(const Actor *other, bool ignoreParty) const {
// FIXME: Original U6 instead uses a function that checks if a game object (actor or object)
// can occupy a world location:
// Figure out which of the remaining tests are relevant to actors and add them here.
const U6Actor *other_ = static_cast<const U6Actor *>(other);
if (other_->ethereal)
return true;
if (other_ == this)
return true;
if (!other_->isFlying() && is_passable())
goto FinalTest;
// Flying actors can not pass each other unless both are in the party
if (other_->isFlying() && !isFlying())
goto FinalTest;
// If the ignoreParty flag is set and we are in the party and not immobilized,
// other members can pass us.
if (ignoreParty && is_in_party() && other_->is_in_party() && !is_immobile()
&& this != Game::get_game()->get_player()->get_actor())
return true;
return false; // default
FinalTest:
// Same type can not pass us
if (obj_n == other_->get_obj_n())
return false;
// Some actors do not block others (e.g. mice, rabbits etc.)
if (!isNonBlocking())
return false;
return true;
}
void U6Actor::print() {
Actor::print();
// might print U6Actor members here
}
/* Returns name of NPC worktype/activity (game specific) or nullptr. */
const char *U6Actor::get_worktype_string(uint32 wt) const {
const char *wt_string = nullptr;
if (wt == WORKTYPE_U6_MOTIONLESS) wt_string = "Motionless";
else if (wt == WORKTYPE_U6_PLAYER) wt_string = "Player";
else if (wt == WORKTYPE_U6_IN_PARTY) wt_string = "In Party";
else if (wt == WORKTYPE_U6_ANIMAL_WANDER) wt_string = "Graze (animal wander)";
else if (wt == WORKTYPE_U6_WALK_TO_LOCATION) wt_string = "Walk to Schedule";
else if (wt == WORKTYPE_U6_FACE_NORTH) wt_string = "Stand (North)";
else if (wt == WORKTYPE_U6_FACE_SOUTH) wt_string = "Stand (South)";
else if (wt == WORKTYPE_U6_FACE_EAST) wt_string = "Stand (East)";
else if (wt == WORKTYPE_U6_FACE_WEST) wt_string = "Stand (West)";
else if (wt == WORKTYPE_U6_WALK_NORTH_SOUTH) wt_string = "Guard North/South";
else if (wt == WORKTYPE_U6_WALK_EAST_WEST) wt_string = "Guard East/West";
else if (wt == WORKTYPE_U6_WANDER_AROUND) wt_string = "Wander";
else if (wt == WORKTYPE_U6_WORK) wt_string = "Loiter (work)";
else if (wt == WORKTYPE_U6_SLEEP) wt_string = "Sleep";
else if (wt == WORKTYPE_U6_PLAY_LUTE) wt_string = "Play";
else if (wt == WORKTYPE_U6_BEG) wt_string = "Converse";
else if (wt == WORKTYPE_U6_COMBAT_FRONT) wt_string = "Combat Front";
else if (wt == 0x04) wt_string = "Combat Rear";
else if (wt == 0x05) wt_string = "Combat Flank";
else if (wt == 0x06) wt_string = "Combat Berserk";
else if (wt == 0x07) wt_string = "Combat Retreat";
else if (wt == 0x08) wt_string = "Combat Assault/Wild";
else if (wt == 0x09) wt_string = "Shy";
else if (wt == 0x0a) wt_string = "Like";
else if (wt == 0x0b) wt_string = "Unfriendly";
else if (wt == 0x0d) wt_string = "Tangle";
else if (wt == 0x0e) wt_string = "Immobile";
else if (wt == 0x92) wt_string = "Sit";
else if (wt == 0x93) wt_string = "Eat";
else if (wt == 0x94) wt_string = "Farm";
else if (wt == 0x98) wt_string = "Ring Bell";
else if (wt == 0x99) wt_string = "Brawl";
else if (wt == 0x9a) wt_string = "Mousing";
else if (wt == 0x9b) wt_string = "Attack Party";
return wt_string;
}
/* Return the first food or drink object in inventory. */
Obj *U6Actor::inventory_get_food(Obj *container) {
U6UseCode *uc = (U6UseCode *)Game::get_game()->get_usecode();
U6LList *inv = container ? container->container : get_inventory_list();
U6Link *link = nullptr;
for (link = inv->start(); link != nullptr; link = link->next) {
Obj *obj = (Obj *)link->data;
if (uc->is_food(obj))
return obj;
if (obj->container) { // search within container
if ((obj = inventory_get_food(obj)))
return obj;
}
}
return 0;
}
void U6Actor::inventory_make_all_objs_ok_to_take() {
U6LList *inventory = get_inventory_list();
if (!inventory)
return;
for (U6Link *link = inventory->start(); link != nullptr;) {
Obj *obj = (Obj *)link->data;
link = link->next;
obj->set_ok_to_take(true, true);
}
return;
}
/* Set worktype to normal non-combat activity. */
void U6Actor::revert_worktype() {
const Party *party = Game::get_game()->get_party();
if (is_in_party())
set_worktype(WORKTYPE_U6_IN_PARTY);
if (party->get_leader_actor() == this)
set_worktype(WORKTYPE_U6_PLAYER);
}
/* Maximum magic points is derived from Intelligence and base_obj_n. */
uint8 U6Actor::get_maxmagic() const {
return Game::get_game()->get_script()->actor_get_max_magic_points(this);
}
bool U6Actor::will_not_talk() const {
if (worktype == WORKTYPE_U6_COMBAT_RETREAT || worktype == 0x12 // guard arrest player
|| Game::get_game()->is_armageddon()
|| worktype == WORKTYPE_U6_ATTACK_PARTY || worktype == 0x13) // repel undead and retreat
return true;
return false;
}
void U6Actor::handle_lightsource(uint8 hour) {
Obj *torch = inventory_get_readied_object(ACTOR_ARM);
if (torch && torch->obj_n != OBJ_U6_TORCH)
torch = nullptr;
Obj *torch2 = inventory_get_readied_object(ACTOR_ARM_2);
if (torch2 && torch2->obj_n != OBJ_U6_TORCH)
torch2 = nullptr;
if (torch || torch2) {
U6UseCode *useCode = (U6UseCode *)Game::get_game()->get_usecode();
if ((hour < 6 || hour > 18 || (z != 0 && z != 5)
|| Game::get_game()->get_weather()->is_eclipse())) {
if (torch && torch->frame_n == 0) {
if (torch->qty != 1)
torch->qty = 1;
useCode->torch(torch, USE_EVENT_USE);
}
if (torch2 && torch2->frame_n == 0) {
if (torch2->qty != 1)
torch2->qty = 1;
useCode->torch(torch2, USE_EVENT_USE);
}
} else {
if (torch && torch->frame_n == 1)
useCode->torch(torch, USE_EVENT_USE);
if (torch2 && torch2->frame_n == 1)
useCode->torch(torch2, USE_EVENT_USE);
}
}
}
uint8 U6Actor::get_hp_text_color() const {
uint8 hp_text_color = 0x48; // standard text color
if (is_poisoned()) // actor is poisoned, display their hp in green
hp_text_color = 0xa;
else if (get_hp() < 10) // actor is critical, display their hp in red.
hp_text_color = 0x0c;
return hp_text_color;
}
} // End of namespace Nuvie
} // End of namespace Ultima