/* 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 . * */ #include "ultima/nuvie/core/nuvie_defs.h" #include "ultima/nuvie/conf/configuration.h" #include "ultima/nuvie/misc/u6_misc.h" #include "ultima/nuvie/files/nuvie_io.h" #include "ultima/nuvie/actors/actor_manager.h" #include "ultima/nuvie/actors/actor.h" #include "ultima/nuvie/core/obj_manager.h" #include "ultima/nuvie/gui/widgets/map_window.h" #include "ultima/nuvie/core/game_clock.h" #include "ultima/nuvie/gui/widgets/msg_scroll.h" #include "ultima/nuvie/core/party.h" #include "ultima/nuvie/save/obj_list.h" #include "ultima/nuvie/core/u6_objects.h" #include "ultima/nuvie/sound/sound_manager.h" #include "ultima/nuvie/core/weather.h" #include "ultima/nuvie/script/script.h" #include "ultima/nuvie/core/player.h" namespace Ultima { namespace Nuvie { static const int PLAYER_BASE_MOVEMENT_COST = 5; Player::Player(const Configuration *cfg) : config(cfg), _clock(nullptr), party(nullptr), actor(nullptr), actor_manager(nullptr), obj_manager(nullptr), map_window(nullptr), karma(0), gender(0), questf(0), gargishf(0), alcohol(0), current_weapon(0), party_mode(false), mapwindow_centered(false) { config->value("config/GameType", game_type); } bool Player::init(ObjManager *om, ActorManager *am, MapWindow *mw, GameClock *c, Party *p) { _clock = c; actor_manager = am; obj_manager = om; map_window = mw; party = p; current_weapon = -1; init(); return true; } void Player::init() { actor = nullptr; party_mode = true; mapwindow_centered = true; } bool Player::load(NuvieIO *objlist) { uint8 solo_member_num = OBJLIST_PARTY_MODE; init(); // We can get the name from the player actor. --SB-X /* objlist->seek(0xf00); objlist->readToBuf((unsigned char *)name,14); // read in Player name.*/ if (game_type == NUVIE_GAME_U6) { objlist->seek(OBJLIST_OFFSET_U6_QUEST_FLAG); // U6 Quest Flag questf = objlist->read1(); objlist->seek(OBJLIST_OFFSET_U6_KARMA); // Player Karma. karma = objlist->read1(); objlist->seek(OBJLIST_OFFSET_U6_ALCOHOL); // Alcohol consumed alcohol = objlist->read1(); objlist->seek(OBJLIST_OFFSET_U6_GARGISH_LANG); // U6 Gargish Flag gargishf = objlist->read1(); objlist->seek(OBJLIST_OFFSET_U6_SOLO_MODE); //Party Mode = 0xff other wise it is solo mode party member number starting from 0. solo_member_num = objlist->read1(); objlist->seek(OBJLIST_OFFSET_U6_GENDER); // Player Gender. gender = objlist->read1(); } if (game_type == NUVIE_GAME_MD) { objlist->seek(OBJLIST_OFFSET_MD_GENDER); // Player Gender. gender = objlist->read1(); } if (solo_member_num == OBJLIST_PARTY_MODE) { party_mode = true; set_party_mode(find_actor()); } else set_solo_mode(party->get_actor(solo_member_num)); return true; } bool Player::save(NuvieIO *objlist) { if (game_type == NUVIE_GAME_U6) { objlist->seek(OBJLIST_OFFSET_U6_QUEST_FLAG); // U6 Quest Flag objlist->write1(questf); objlist->seek(OBJLIST_OFFSET_U6_KARMA); // Player Karma. objlist->write1(karma); objlist->seek(OBJLIST_OFFSET_U6_ALCOHOL); // Alcohol consumed objlist->write1(alcohol); objlist->seek(OBJLIST_OFFSET_U6_GARGISH_LANG); // U6 Gargish Flag objlist->write1(gargishf); objlist->seek(OBJLIST_OFFSET_U6_SOLO_MODE); // solo member num. if (party_mode) objlist->write1(OBJLIST_PARTY_MODE); // 0xff else objlist->write1(party->get_member_num(actor)); //write the party member number of the solo actor objlist->seek(OBJLIST_OFFSET_U6_GENDER); // Player Gender. objlist->write1(gender); } if (game_type == NUVIE_GAME_MD) { objlist->seek(OBJLIST_OFFSET_MD_GENDER); // Player Gender. objlist->write1(gender); } return true; } Actor *Player::find_actor() { for (int p = 0; p < ACTORMANAGER_MAX_ACTORS; p++) { Actor *theActor = actor_manager->get_actor(p); if (theActor->get_worktype() == 0x02 && theActor->is_immobile() == false) // WT_U6_PLAYER return theActor; } sint8 party_leader = party->get_leader(); if (party_leader != -1) return party->get_actor(party_leader); return actor_manager->get_avatar(); } // keep MapWindow focused on Player actor, or remove focus void Player::set_mapwindow_centered(bool state) { uint16 x, y; uint8 z; mapwindow_centered = state; if (mapwindow_centered == false) return; map_window->centerMapOnActor(actor); // center immediately get_location(&x, &y, &z); actor_manager->updateActors(x, y, z); obj_manager->update(x, y, z); // spawn eggs when teleporting. eg red moongate. } void Player::set_actor(Actor *new_actor) { MsgScroll *scroll = Game::get_game()->get_scroll(); if (new_actor == nullptr) { return; } if (actor != nullptr) { if (party->contains_actor(actor)) actor->set_worktype(0x01); //WT_U6_IN_PARTY else actor->set_worktype(0x00); //no worktype } bool same_actor = (actor == new_actor); actor = new_actor; actor->set_worktype(0x02); // WT_U6_PLAYER actor->delete_pathfinder(); current_weapon = ACTOR_NO_READIABLE_LOCATION; map_window->centerCursor(); if (same_actor) return; actor_manager->set_player(actor); Std::string prompt = get_name(); if (game_type == NUVIE_GAME_U6) { prompt += ":\n"; } prompt += ">"; scroll->set_prompt(prompt.c_str()); } Actor *Player::get_actor() { return actor; } const Actor *Player::get_actor() const { return actor; } void Player::get_location(uint16 *ret_x, uint16 *ret_y, uint8 *ret_level) const { actor->get_location(ret_x, ret_y, ret_level); } uint8 Player::get_location_level() const { return actor->z; } const char *Player::get_name() { if (actor->get_actor_num() == ACTOR_VEHICLE_ID_N) return actor_manager->get_avatar()->get_name(); return actor->get_name(true); } /* Add to Player karma. Handle appropriately the karma min/max limits. */ void Player::add_karma(uint8 val) { karma = ((karma + val) <= 99) ? karma + val : 99; } /* Subtract from Player karma. Handle appropriately the karma min/max limits. */ void Player::subtract_karma(uint8 val) { karma = ((karma - val) >= 0) ? karma - val : 0; } void Player::subtract_movement_points(uint8 points) { Game::get_game()->get_script()->call_actor_subtract_movement_points(get_actor(), points); } const char *Player::get_gender_title() const { switch (game_type) { case NUVIE_GAME_U6 : if (gender == 0) return "milord"; else return "milady"; case NUVIE_GAME_MD : if (gender == 0) return "Sir"; else return "Madam"; default : break; } return "Sir"; //FIXME is this needed for SE? } bool Player::check_moveRelative(sint16 rel_x, sint16 rel_y) { if (!actor->moveRelative(rel_x, rel_y, ACTOR_IGNORE_DANGER)) { /**MOVE**/ ActorError *ret = actor->get_error(); // FIXME: When in combat, U6 attempts to move party members with role "front" forward instead of swapping. if (ret->err == ACTOR_BLOCKED_BY_ACTOR && (game_type != NUVIE_GAME_U6 || actor->obj_n != OBJ_U6_MOUSE) // Only exchange positions if player is not U6 mouse && party->contains_actor(ret->blocking_actor) && ret->blocking_actor->is_immobile() == false) ret->blocking_actor->push(actor, ACTOR_PUSH_HERE); // There could be more party members at the destination, but U6 ignores them - hence the ACTOR_IGNORE_PARTY_MEMBERS. if (!actor->moveRelative(rel_x, rel_y, ACTOR_IGNORE_DANGER | ACTOR_IGNORE_PARTY_MEMBERS)) /**MOVE**/ return false; } return true; } // walk to adjacent square void Player::moveRelative(sint16 rel_x, sint16 rel_y, bool mouse_movement) { const NuvieDir raft_movement_tbl[] = { NUVIE_DIR_N, NUVIE_DIR_NE, NUVIE_DIR_N, NUVIE_DIR_NW, NUVIE_DIR_N, NUVIE_DIR_NE, NUVIE_DIR_NW, NUVIE_DIR_N, NUVIE_DIR_NE, NUVIE_DIR_NE, NUVIE_DIR_E, NUVIE_DIR_N, NUVIE_DIR_NE, NUVIE_DIR_E, NUVIE_DIR_NE, NUVIE_DIR_N, NUVIE_DIR_NE, NUVIE_DIR_E, NUVIE_DIR_SE, NUVIE_DIR_E, NUVIE_DIR_E, NUVIE_DIR_E, NUVIE_DIR_SE, NUVIE_DIR_NE, NUVIE_DIR_E, NUVIE_DIR_SE, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_E, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_SW, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_S, NUVIE_DIR_SW, NUVIE_DIR_W, NUVIE_DIR_S, NUVIE_DIR_SW, NUVIE_DIR_SW, NUVIE_DIR_SW, NUVIE_DIR_S, NUVIE_DIR_SW, NUVIE_DIR_W, NUVIE_DIR_NW, NUVIE_DIR_W, NUVIE_DIR_SW, NUVIE_DIR_W, NUVIE_DIR_NW, NUVIE_DIR_SW, NUVIE_DIR_W, NUVIE_DIR_W, NUVIE_DIR_NW, NUVIE_DIR_N, NUVIE_DIR_W, NUVIE_DIR_NW, NUVIE_DIR_N, NUVIE_DIR_NW, NUVIE_DIR_W, NUVIE_DIR_NW }; const uint8 ship_cost[8] = {0xA, 5, 3, 4, 5, 4, 3, 5}; const uint8 skiff_cost[8] = {3, 4, 5, 7, 0xA, 7, 5, 4}; MovementStatus movementStatus = CAN_MOVE; bool can_change_rel_dir = true; NuvieDir wind_dir = Game::get_game()->get_weather()->get_wind_dir(); uint16 x, y; uint8 z; actor->get_location(&x, &y, &z); if (game_type == NUVIE_GAME_U6) { if (actor->id_n == 0) { // vehicle actor if (actor->obj_n == OBJ_U6_INFLATED_BALLOON && (!Game::get_game()->has_free_balloon_movement() || !party->has_obj(OBJ_U6_FAN, 0, false))) { can_change_rel_dir = false; NuvieDir dir = get_reverse_direction(Game::get_game()->get_weather()->get_wind_dir()); if (dir == NUVIE_DIR_NONE) { Game::get_game()->get_scroll()->display_string("Thou canst not move without wind!\n\n"); Game::get_game()->get_scroll()->display_prompt(); actor->set_moves_left(0); rel_x = 0; rel_y = 0; } else { get_relative_dir(dir, &rel_x, &rel_y); } } else if (actor->obj_n == OBJ_U6_RAFT) { NuvieDir dir = NUVIE_DIR_N; can_change_rel_dir = false; const Tile *t = Game::get_game()->get_game_map()->get_tile(x, y, z, true); if (t->flags1 & TILEFLAG_BLOCKING) { //deep water tiles are blocking. Shore tiles should allow player movement. //deep water, so take control away from player. if (t->tile_num >= 8 && t->tile_num < 16) { dir = static_cast(t->tile_num - 8); } if (wind_dir != NUVIE_DIR_NONE) { dir = raft_movement_tbl[dir * 8 + get_reverse_direction(wind_dir)]; } else { dir = get_nuvie_dir_code(dir); } get_relative_dir(dir, &rel_x, &rel_y); } } } else { // normal actor if (alcohol > 3 && NUVIE_RAND() % 4 != 0) { rel_x = NUVIE_RAND() % 3 - 1; // stumble and change direction rel_y = NUVIE_RAND() % 3 - 1; can_change_rel_dir = false; Game::get_game()->get_scroll()->display_string("Hic!\n"); } } ActorMoveFlags move_flags = ACTOR_IGNORE_DANGER | ACTOR_IGNORE_PARTY_MEMBERS; // don't allow diagonal move between blocked tiles (player only) if (rel_x && rel_y && !actor->check_move(x + rel_x, y + 0, z, move_flags) && !actor->check_move(x + 0, y + rel_y, z, move_flags)) { movementStatus = BLOCKED; } } else if (game_type == NUVIE_GAME_MD) { if (Game::get_game()->get_clock()->get_timer(GAMECLOCK_TIMER_MD_BLUE_BERRY) != 0 && NUVIE_RAND() % 2 == 0) { rel_x = NUVIE_RAND() % 3 - 1; // stumble and change direction rel_y = NUVIE_RAND() % 3 - 1; can_change_rel_dir = false; Game::get_game()->get_scroll()->display_string("you are dizzy!\n"); //FIXME need i18n support here. } } if (actor->is_immobile() && actor->id_n != 0) movementStatus = BLOCKED; if (movementStatus != BLOCKED && game_type != NUVIE_GAME_U6) { movementStatus = Game::get_game()->get_script()->call_player_before_move_action(&rel_x, &rel_y); } if (movementStatus != BLOCKED) { if (movementStatus == FORCE_MOVE) { actor->moveRelative(rel_x, rel_y, ACTOR_FORCE_MOVE); } else if (!check_moveRelative(rel_x, rel_y)) { movementStatus = BLOCKED; if (mouse_movement && rel_x != 0 && rel_y != 0 && can_change_rel_dir) { if (check_moveRelative(rel_x, 0)) { // try x movement only rel_y = 0; movementStatus = CAN_MOVE; } else if (check_moveRelative(0, rel_y)) { // try y movement only rel_x = 0; movementStatus = CAN_MOVE; } } // Try opening a door FIXME: shouldn't be U6 specific if (movementStatus == BLOCKED) { if (obj_manager->is_door(x + rel_x, y + rel_y, z)) try_open_door(x + rel_x, y + rel_y, z); } if (movementStatus == BLOCKED) { Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_BLOCKED); if (actor->id_n == 0) //vehicle actor. actor->set_moves_left(0); //zero movement points here so U6 can change wind direction by advancing game time. } } } actor->set_direction(rel_x, rel_y); // post-move if (movementStatus != BLOCKED) { if (party_mode && party->is_leader(actor)) { // lead party party->follow(rel_x, rel_y); } else if (actor->id_n == 0 && game_type != NUVIE_GAME_MD) { // using vehicle; drag party along MapCoord new_xyz = actor->get_location(); party->move(new_xyz.x, new_xyz.y, new_xyz.z); } if (game_type == NUVIE_GAME_U6 && (actor->obj_n == OBJ_U6_INFLATED_BALLOON || actor->obj_n == OBJ_U6_RAFT)) { actor->set_moves_left(actor->get_moves_left() - PLAYER_BASE_MOVEMENT_COST); } else if (game_type == NUVIE_GAME_U6 && actor->obj_n == OBJ_U6_SHIP && wind_dir != WEATHER_WIND_CALM) { NuvieDir nuvie_dir = get_direction_code(rel_x, rel_y); if (nuvie_dir != NUVIE_DIR_NONE) { sint8 dir = get_original_dir_code(nuvie_dir); actor->set_moves_left(actor->get_moves_left() - ship_cost[abs(dir - wind_dir)]); //DEBUG(0, LEVEL_DEBUGGING, "Ship movement cost = %d\n", ship_cost[abs(dir-wind_dir)]); } } else if (game_type == NUVIE_GAME_U6 && actor->obj_n == OBJ_U6_SKIFF) { NuvieDir nuvie_dir = get_direction_code(rel_x, rel_y); if (nuvie_dir != NUVIE_DIR_NONE) { sint8 dir = get_original_dir_code(nuvie_dir); sint8 water_dir = dir; const Tile *t = Game::get_game()->get_game_map()->get_tile(x, y, z, true); if (t->tile_num >= 8 && t->tile_num < 16) { dir = t->tile_num - 8; } actor->set_moves_left(actor->get_moves_left() - skiff_cost[abs(dir - water_dir)]); //DEBUG(0, LEVEL_DEBUGGING, "Skiff movement cost = %d\n", skiff_cost[abs(dir-water_dir)]); } } else { actor->set_moves_left(actor->get_moves_left() - (PLAYER_BASE_MOVEMENT_COST + Game::get_game()->get_game_map()->get_impedance(x, y, z))); if (rel_x != 0 && rel_y != 0) // diagonal move, double cost actor->set_moves_left(actor->get_moves_left() - PLAYER_BASE_MOVEMENT_COST); } } if (game_type != NUVIE_GAME_U6) { Game::get_game()->get_script()->call_player_post_move_action(movementStatus != BLOCKED); actor->get_location(&x, &y, &z); //update location in case we have moved. } // update world around player actor_manager->updateActors(x, y, z); obj_manager->update(x, y, z); // remove temporary objs, hatch eggs _clock->inc_move_counter(); // doesn't update time actor_manager->startActors(); // end player turn } void Player::try_open_door(uint16 x, uint16 y, uint8 z) { UseCode *usecode = Game::get_game()->get_usecode(); Obj *obj = obj_manager->get_obj(x, y, z); if (!usecode->is_door(obj)) return; usecode->use_obj(obj, get_actor()); subtract_movement_points(MOVE_COST_USE); map_window->updateBlacking(); } // teleport-type move void Player::move(sint16 new_x, sint16 new_y, uint8 new_level, bool teleport) { if (actor->move(new_x, new_y, new_level, ACTOR_FORCE_MOVE)) { //map_window->centerMapOnActor(actor); if (party->is_leader(actor)) { // lead party if (actor_manager->get_avatar()->get_hp() == 0) { // need to end turn if Avatar died actor_manager->startActors(); return; } party->move(new_x, new_y, new_level); // center everyone first party->follow(0, 0); // then try to move them to correct positions } actor_manager->updateActors(new_x, new_y, new_level); if (teleport && new_level != 0 && new_level != 5) Game::get_game()->get_weather()->set_wind_dir(NUVIE_DIR_NONE); obj_manager->update(new_x, new_y, new_level, teleport); // remove temporary objs, hatch eggs // it's still the player's turn } } void Player::moveLeft() { moveRelative(-1, 0); } void Player::moveRight() { moveRelative(1, 0); } void Player::moveUp() { moveRelative(0, -1); } void Player::moveDown() { moveRelative(0, 1); } void Player::pass() { Game::get_game()->get_script()->call_player_pass(); // uint16 x = actor->x, y = actor->y; // uint8 z = actor->z; // Move balloon / raft if required. if (game_type == NUVIE_GAME_U6 && (actor->obj_n == OBJ_U6_INFLATED_BALLOON || actor->obj_n == OBJ_U6_RAFT)) { if (Game::get_game()->get_weather()->get_wind_dir() != NUVIE_DIR_NONE) { moveRelative(0, 0); //return; } } if (actor->get_moves_left() > 0) actor->set_moves_left(0); // Pass and use up moves // update world around player if (party_mode && party->is_leader(actor)) // lead party party->follow(0, 0); // actor_manager->updateActors(x, y, z); // not needed because position is unchanged _clock->inc_move_counter_by_a_minute(); // doesn't update time actor_manager->startActors(); // end player turn //actor_manager->moveActors(); Game::get_game()->time_changed(); } /* Enter party mode, with everyone following actor. (must be in the party) */ bool Player::set_party_mode(Actor *new_actor) { if (party->contains_actor(new_actor) || party->is_in_vehicle()) { party_mode = true; set_actor(new_actor); return true; } return false; } /* Enter solo mode as actor. (must be in the party) */ bool Player::set_solo_mode(Actor *new_actor) { if (party->contains_actor(new_actor)) { if (new_actor->is_charmed()) { Game::get_game()->get_scroll()->display_fmt_string("%s fails to respond.\n\n", new_actor->get_name()); return false; } party_mode = false; set_actor(new_actor); return true; } return false; } /* Returns the delay in continuous movement for the actor type we control. */ uint32 Player::get_walk_delay() const { if (game_type != NUVIE_GAME_U6) return 125; // normal movement about 8 spaces per second if (actor->obj_n == OBJ_U6_BALLOON_BASKET) return 10; // 10x normal (wow!) else if (actor->obj_n == OBJ_U6_SHIP) return 20; // 5x normal else if (actor->obj_n == OBJ_U6_SKIFF) return 50; // 2x normal else if (actor->obj_n == OBJ_U6_RAFT) return 100; // normal else if (actor->obj_n == OBJ_U6_HORSE_WITH_RIDER && party->is_everyone_horsed()) return 50; // 2x normal else return 125; // normal movement about 8 spaces per second } /* Returns true if it's time for the player to take another step. * (call during continuous movement) */ bool Player::check_walk_delay() { static uint32 walk_delay = 0, // start with no delay last_time = _clock->get_ticks(); uint32 this_time = _clock->get_ticks(); uint32 time_passed = this_time - last_time; // subtract time_passed until delay is 0 if (sint32(walk_delay - time_passed) < 0) walk_delay = 0; else walk_delay -= time_passed; last_time = this_time; // set each call to get time_passed if (walk_delay == 0) { walk_delay = get_walk_delay(); // reset return true; } return false; // not time yet } bool Player::weapon_can_hit(uint16 x, uint16 y) { return actor->weapon_can_hit(actor->get_weapon(current_weapon), x, y); } void Player::attack_select_init(bool use_attack_text) { uint16 x, y; uint8 z; current_weapon = ACTOR_NO_READIABLE_LOCATION; if (attack_select_next_weapon(false, use_attack_text) == false) attack_select_weapon_at_location(ACTOR_NO_READIABLE_LOCATION, use_attack_text); // attack with hands. map_window->centerCursor(); CombatTarget target = party->get_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor)); Actor *target_actor = nullptr; switch (target.type) { case TARGET_ACTOR : target_actor = actor_manager->get_actor(target.actor_num); uint16 target_x, target_y; map_window->get_pos(&x, &y, &z); target_x = x; target_y = y; if (target_actor && target_actor->is_onscreen() && target_actor->is_alive() && target_actor->is_visible() && actor->weapon_can_hit(actor->get_weapon(current_weapon), target_actor, &target_x, &target_y)) { map_window->moveCursor(target_x - x, target_y - y); } else { party->clear_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor)); } break; case TARGET_LOCATION : if (target.loc.z == actor->get_z() && weapon_can_hit(target.loc.x, target.loc.y)) { map_window->get_pos(&x, &y, &z); map_window->moveCursor(target.loc.x - x, target.loc.y - y); } else { party->clear_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor)); } break; default : break; } return; } bool Player::attack_select_next_weapon(bool add_newline, bool use_attack_text) { sint8 i; for (i = current_weapon + 1; i < ACTOR_MAX_READIED_OBJECTS; i++) { if (attack_select_weapon_at_location(i, add_newline, use_attack_text) == true) return true; } return false; } bool Player::attack_select_weapon_at_location(sint8 location, bool add_newline, bool use_attack_text) { const CombatType *weapon; MsgScroll *scroll = Game::get_game()->get_scroll(); if (location == ACTOR_NO_READIABLE_LOCATION) { current_weapon = location; if (use_attack_text == false) return true; if (add_newline) scroll->display_string("\n"); if (game_type == NUVIE_GAME_U6 && actor->obj_n == OBJ_U6_SHIP) scroll->display_string("Attack with ship cannons-"); else scroll->display_string("Attack with bare hands-"); return true; } weapon = actor->get_weapon(location); if (weapon && weapon->attack > 0) { current_weapon = location; if (use_attack_text == false) return true; if (add_newline) scroll->display_string("\n"); scroll->display_fmt_string("Attack with %s-", obj_manager->get_obj_name(weapon->obj_n)); return true; } return false; } void Player::attack(MapCoord target, Actor *target_actor) { MsgScroll *scroll = Game::get_game()->get_scroll(); if (weapon_can_hit(target.x, target.y)) { if (!target_actor) target_actor = actor_manager->get_actor(target.x, target.y, actor->get_z()); actor->attack(current_weapon, target, target_actor); if (target_actor) { party->set_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor), target_actor); } else { Obj *target_obj = obj_manager->get_obj(target.x, target.y, actor->get_z()); if (target_obj) { party->set_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor), MapCoord(target_obj)); } } } else scroll->display_string("\nOut of range!\n"); //actor_manager->startActors(); // end player turn return; } // Switch to controlling another actor void Player::update_player(Actor *next_player) { MsgScroll *scroll = Game::get_game()->get_scroll(); bool same_actor = (next_player == get_actor()); set_actor(next_player); // redirects to ActorManager::set_player() set_mapwindow_centered(true); if (!scroll->can_display_prompt() && same_actor) return; scroll->display_string("\n"); scroll->display_prompt(); } /* Rest and repair ship. */ void Player::repairShip() { MsgScroll *scroll = Game::get_game()->get_scroll(); Actor *ship = get_actor(); if (ship->get_obj_n() != OBJ_U6_SHIP) return; // ship actor's health is hull strength if (ship->get_hp() + 5 > 100) ship->set_hp(100); else ship->set_hp(ship->get_hp() + 5); scroll->display_fmt_string("Hull Strength: %d%%\n", ship->get_hp()); Game::get_game()->get_script()->call_advance_time(5); Game::get_game()->time_changed(); } } // End of namespace Nuvie } // End of namespace Ultima