3863 lines
119 KiB
C++
3863 lines
119 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/conf/configuration.h"
|
|
#include "ultima/nuvie/core/game.h"
|
|
#include "ultima/nuvie/core/game_clock.h"
|
|
#include "ultima/nuvie/gui/widgets/map_window.h"
|
|
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
|
|
#include "ultima/nuvie/actors/actor_manager.h"
|
|
#include "ultima/nuvie/actors/actor.h"
|
|
#include "ultima/nuvie/core/converse.h"
|
|
#include "ultima/nuvie/core/player.h"
|
|
#include "ultima/nuvie/core/party.h"
|
|
#include "ultima/nuvie/core/book.h"
|
|
#include "ultima/nuvie/views/view_manager.h"
|
|
#include "ultima/nuvie/views/portrait_view.h"
|
|
#include "ultima/nuvie/core/timed_event.h"
|
|
#include "ultima/nuvie/views/inventory_view.h"
|
|
#include "ultima/nuvie/views/party_view.h"
|
|
#include "ultima/nuvie/views/actor_view.h"
|
|
#include "ultima/nuvie/gui/widgets/command_bar.h"
|
|
#include "ultima/nuvie/core/events.h"
|
|
#include "ultima/nuvie/core/u6_objects.h"
|
|
#include "ultima/nuvie/core/effect.h"
|
|
#include "ultima/nuvie/core/effect_manager.h"
|
|
#include "ultima/nuvie/files/nuvie_io_file.h"
|
|
#include "ultima/nuvie/core/magic.h"
|
|
#include "ultima/nuvie/gui/gui_yes_no_dialog.h"
|
|
#include "ultima/nuvie/menus/asset_viewer_dialog.h"
|
|
#include "ultima/nuvie/menus/game_menu_dialog.h"
|
|
#include "ultima/nuvie/views/inventory_widget.h"
|
|
#include "ultima/nuvie/keybinding/keys.h"
|
|
#include "ultima/nuvie/views/spell_view.h"
|
|
#include "ultima/nuvie/gui/widgets/fps_counter.h"
|
|
#include "ultima/nuvie/script/script.h"
|
|
|
|
#include "common/system.h"
|
|
#include "backends/keymapper/keymapper.h"
|
|
|
|
namespace Ultima {
|
|
namespace Nuvie {
|
|
|
|
Events *Events::g_events;
|
|
|
|
using Std::string;
|
|
|
|
EventInput_s::~EventInput_s() {
|
|
if (target_init) delete target_init;
|
|
if (str) delete str;
|
|
if (loc) delete loc;
|
|
}
|
|
|
|
void EventInput_s::set_loc(const MapCoord &c) {
|
|
if ((type == EVENTINPUT_MAPCOORD || type == EVENTINPUT_MAPCOORD_DIR) && loc != 0) delete loc;
|
|
loc = new MapCoord(c);
|
|
}
|
|
|
|
Events::Events(const Configuration *cfg)
|
|
: config(cfg), converse(nullptr),
|
|
keybinder(nullptr), showingQuitDialog(false), fps_counter_widget(nullptr),
|
|
cursor_mode(false){
|
|
g_events = this;
|
|
clear();
|
|
}
|
|
|
|
Events::~Events() {
|
|
g_events = nullptr;
|
|
|
|
delete time_queue;
|
|
delete game_time_queue;
|
|
}
|
|
|
|
void Events::clear() {
|
|
clear_alt_code();
|
|
active_alt_code = 0;
|
|
alt_code_input_num = 0;
|
|
|
|
game = Game::get_game();
|
|
gui = nullptr;
|
|
obj_manager = nullptr;
|
|
map_window = nullptr;
|
|
scroll = nullptr;
|
|
clock = nullptr;
|
|
player = nullptr;
|
|
view_manager = nullptr;
|
|
usecode = nullptr;
|
|
magic = nullptr;
|
|
drop_obj = nullptr;
|
|
ts = 0;
|
|
drop_qty = 0;
|
|
drop_x = drop_y = -1;
|
|
rest_time = 0;
|
|
rest_guard = 0;
|
|
push_obj = nullptr;
|
|
push_actor = nullptr;
|
|
drop_from_key = false;
|
|
move_in_inventory = false;
|
|
time_queue = game_time_queue = nullptr;
|
|
showingDialog = false;
|
|
gamemenu_dialog = nullptr;
|
|
assetviewer_dialog = nullptr;
|
|
ignore_timeleft = false;
|
|
in_control_cheat = false;
|
|
looking_at_spellbook = false;
|
|
using_pickpocket_cheat = false;
|
|
do_not_show_target_cursor = false;
|
|
config->value("config/input/direction_selects_target", direction_selects_target, true);
|
|
|
|
mode = MOVE_MODE;
|
|
last_mode = MOVE_MODE;
|
|
|
|
fps_timestamp = 0;
|
|
fps_counter = 0;
|
|
|
|
scriptThread = nullptr;
|
|
}
|
|
|
|
bool Events::init(ObjManager *om, MapWindow *mw, MsgScroll *ms, Player *p, Magic *mg,
|
|
GameClock *gc, ViewManager *vm, UseCode *uc, GUI *g, KeyBinder *kb) {
|
|
clear();
|
|
|
|
gui = g;
|
|
obj_manager = om;
|
|
map_window = mw;
|
|
scroll = ms;
|
|
clock = gc;
|
|
player = p;
|
|
view_manager = vm;
|
|
usecode = uc;
|
|
|
|
mode = MOVE_MODE;
|
|
last_mode = MOVE_MODE;
|
|
input.get_direction = false;
|
|
input.get_text = false;
|
|
cursor_mode = false;
|
|
input.target_init = nullptr;
|
|
|
|
time_queue = new TimeQueue;
|
|
game_time_queue = new TimeQueue;
|
|
magic = mg;
|
|
keybinder = kb;
|
|
|
|
fps_timestamp = clock->get_ticks();
|
|
|
|
fps_counter_widget = new FpsCounter(game);
|
|
gui->AddWidget(fps_counter_widget);
|
|
fps_counter_widget->Hide();
|
|
scriptThread = nullptr;
|
|
_keymapperStateBeforeKEYINPUT = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Events::update_timers() {
|
|
time_queue->call_timers(clock->get_ticks());
|
|
game_time_queue->call_timers(clock->get_game_ticks());
|
|
}
|
|
|
|
bool Events::update() {
|
|
bool idle = true;
|
|
// timed
|
|
time_queue->call_timers(clock->get_ticks());
|
|
game_time_queue->call_timers(clock->get_game_ticks());
|
|
|
|
// polled
|
|
Common::Event evt;
|
|
while (pollEvent(evt)) {
|
|
idle = false;
|
|
switch (gui->HandleEvent(&evt)) {
|
|
case GUI_PASS :
|
|
if (handleEvent(&evt) == false) {
|
|
game->quit();
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case GUI_QUIT :
|
|
game->quit();
|
|
return false;
|
|
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (idle)
|
|
gui->Idle(); // run Idle() for all widgets
|
|
|
|
if (showingDialog) // temp. fix to show normal cursor over quit dialog
|
|
game->set_mouse_pointer(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
Events::MouseButton Events::whichButton(Common::EventType type) {
|
|
if (type == Common::EVENT_LBUTTONDOWN || type == Common::EVENT_LBUTTONUP)
|
|
return BUTTON_LEFT;
|
|
else if (type == Common::EVENT_RBUTTONDOWN || type == Common::EVENT_RBUTTONUP)
|
|
return BUTTON_RIGHT;
|
|
else if (type == Common::EVENT_MBUTTONDOWN || type == Common::EVENT_MBUTTONUP)
|
|
return BUTTON_MIDDLE;
|
|
else
|
|
return BUTTON_NONE;
|
|
}
|
|
|
|
bool Events::pollEvent(Common::Event &evt) {
|
|
// Event handling
|
|
if (g_system->getEventManager()->pollEvent(evt)) {
|
|
switch (evt.type) {
|
|
case Common::EVENT_MOUSEMOVE:
|
|
_mousePos = evt.mouse;
|
|
break;
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
case Common::EVENT_MBUTTONDOWN:
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
_buttonsDown |= BUTTON_MASK(whichButton(evt.type));
|
|
_mousePos = evt.mouse;
|
|
break;
|
|
case Common::EVENT_LBUTTONUP:
|
|
case Common::EVENT_MBUTTONUP:
|
|
case Common::EVENT_RBUTTONUP:
|
|
_buttonsDown &= ~BUTTON_MASK(whichButton(evt.type));
|
|
_mousePos = evt.mouse;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Events::handleSDL_KEYDOWN(const Common::Event *event_) {
|
|
// when casting the magic class will handle keyboard events
|
|
if (mode == KEYINPUT_MODE) {
|
|
Common::KeyCode sym = event_->kbd.keycode;
|
|
ActionKeyType action_key_type = OTHER_KEY;
|
|
|
|
if (!((magic->is_selecting_spell() && ((sym >= Common::KEYCODE_a && sym <= Common::KEYCODE_z) || sym == Common::KEYCODE_BACKSPACE)) ||
|
|
((magic->is_waiting_for_location() || last_mode == USE_MODE) && sym >= Common::KEYCODE_1 && sym <= Common::KEYCODE_9))) {
|
|
ActionType a = keybinder->get_ActionType(event_->kbd);
|
|
action_key_type = keybinder->GetActionKeyType(a);
|
|
//switch (action_key_type) {
|
|
//default:
|
|
if (keybinder->handle_always_available_keys(a)) return true;
|
|
// break;
|
|
//}
|
|
}
|
|
input.type = EVENTINPUT_KEY;
|
|
input.key = sym;
|
|
input.action_key_type = action_key_type;
|
|
// callback should return a true value if it handled the event_
|
|
if (action_key_type != CANCEL_ACTION_KEY && message(CB_DATA_READY, (char *) &input))
|
|
return true;
|
|
callback_target = 0;
|
|
endAction(); // no more keys for you! (end KEYINPUT_MODE)
|
|
keybinder->HandleEvent(event_);
|
|
return true;
|
|
}
|
|
|
|
keybinder->HandleEvent(event_);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Events::target_spell() {
|
|
input.type = EVENTINPUT_KEY;
|
|
input.key = Common::KEYCODE_RETURN; // only needed to overwrite old value so it isn't a number or backspace
|
|
input.action_key_type = DO_ACTION_KEY;
|
|
message(CB_DATA_READY, (char *) &input);
|
|
callback_target = 0;
|
|
endAction();
|
|
doAction();
|
|
}
|
|
|
|
void Events::close_spellbook() {
|
|
if (callback_target) {
|
|
callback_target = 0;
|
|
endAction();
|
|
}
|
|
cancelAction();
|
|
}
|
|
|
|
bool Events::handleEvent(const Common::Event *event_) {
|
|
if (game->user_paused())
|
|
return true;
|
|
|
|
// if input was requested, handle it first so other events do not interfere
|
|
if (input.get_text && scroll->has_input()) {
|
|
if (active_alt_code) {
|
|
endAction(); // exit INPUT_MODE
|
|
alt_code_input(scroll->get_input().c_str());
|
|
} else {
|
|
doAction();
|
|
}
|
|
}
|
|
|
|
switch (event_->type) {
|
|
case Common::EVENT_MOUSEMOVE:
|
|
break;
|
|
case Common::EVENT_KEYUP:
|
|
break;
|
|
|
|
case Common::EVENT_KEYDOWN:
|
|
handleSDL_KEYDOWN(event_);
|
|
break;
|
|
|
|
case Common::EVENT_QUIT:
|
|
return false;
|
|
|
|
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
|
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
|
|
keybinder->handleScummVMBoundEvent(event_);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Events::get_direction(const char *prompt) {
|
|
// use_obj = src;
|
|
assert(mode != INPUT_MODE);
|
|
set_mode(INPUT_MODE); // saves previous mode
|
|
if (prompt)
|
|
scroll->display_string(prompt);
|
|
input.get_direction = true;
|
|
|
|
moveCursorToMapWindow();
|
|
map_window->centerCursor();
|
|
map_window->set_show_cursor(false);
|
|
// map_window->set_show_use_cursor(true); // set in moveCursorToMapWindow()
|
|
if (do_not_show_target_cursor && direction_selects_target)
|
|
map_window->set_show_use_cursor(false);
|
|
input.target_init = new MapCoord(map_window->get_cursorCoord()); // depends on MapWindow size
|
|
}
|
|
/* This version of get_direction() doesn't show the cursor. */
|
|
void Events::get_direction(const MapCoord &from, const char *prompt) {
|
|
get_direction(prompt);
|
|
map_window->moveCursor(from.x - map_window->get_cur_x(), from.y - map_window->get_cur_y());
|
|
input.target_init->x = from.x;
|
|
input.target_init->y = from.y;
|
|
if (input_really_needs_directon()) { // actually getting a direction
|
|
if (!direction_selects_target)
|
|
map_window->set_show_cursor(true);
|
|
map_window->set_show_use_cursor(false);
|
|
map_window->set_mousecenter(from.x - map_window->get_cur_x(), from.y - map_window->get_cur_y());
|
|
}
|
|
}
|
|
|
|
void Events::get_target(const char *prompt) {
|
|
// use_obj = src;
|
|
assert(mode != INPUT_MODE);
|
|
set_mode(INPUT_MODE); // saves previous mode
|
|
if (prompt)
|
|
scroll->display_string(prompt);
|
|
input.get_direction = false;
|
|
|
|
map_window->centerCursor();
|
|
moveCursorToMapWindow();
|
|
}
|
|
|
|
void Events::get_target(const MapCoord &init, const char *prompt) {
|
|
get_target(prompt);
|
|
map_window->moveCursor(init.x, init.y);
|
|
}
|
|
|
|
/* Switch focus to MsgScroll and start getting user input. */
|
|
void Events::get_scroll_input(const char *allowed,
|
|
bool can_escape,
|
|
bool using_target_cursor,
|
|
bool set_numbers_only_to_true) {
|
|
assert(scroll);
|
|
if (!using_target_cursor) {
|
|
assert(mode != INPUT_MODE);
|
|
set_mode(INPUT_MODE); // saves previous mode
|
|
}
|
|
input.get_text = true;
|
|
scroll->set_input_mode(true, allowed, can_escape, using_target_cursor, set_numbers_only_to_true);
|
|
//no need to grab focus because any input will eventually reach MsgScroll,
|
|
// scroll->grab_focus();
|
|
}
|
|
|
|
void Events::get_inventory_obj(Actor *actor, bool getting_target) {
|
|
if (getting_target) {
|
|
get_target("");
|
|
moveCursorToInventory();
|
|
} else if (!game->is_new_style())
|
|
view_manager->set_inventory_mode();
|
|
if (game->is_new_style()) {
|
|
//view_manager->set_inventory_mode();
|
|
view_manager->open_container_view(actor); //FIXME need to open container gump in pickpocket mode.
|
|
view_manager->open_doll_view(actor);
|
|
} else {
|
|
view_manager->get_inventory_view()->set_actor(actor, true);
|
|
}
|
|
}
|
|
|
|
void Events::get_spell_num(Actor *caster, Obj *spell_container) {
|
|
//get_target("");
|
|
view_manager->set_spell_mode(caster, spell_container, true);
|
|
view_manager->get_current_view()->grab_focus();
|
|
}
|
|
|
|
/* Send all keyboard input to caller, with user_data.
|
|
ESC always cancels sending any further input. */
|
|
void Events::key_redirect(CallBack *caller, void *user_data) {
|
|
assert(mode != INPUT_MODE && mode != KEYINPUT_MODE);
|
|
request_input(caller, user_data);
|
|
set_mode(KEYINPUT_MODE); // saves previous mode
|
|
}
|
|
|
|
void Events::cancel_key_redirect() {
|
|
assert(mode == KEYINPUT_MODE);
|
|
endAction();
|
|
}
|
|
|
|
/* Switch focus to PortraitView, display a portrait, and wait for user input. */
|
|
void Events::display_portrait(Actor *actor, const char *name) {
|
|
view_manager->set_portrait_mode(actor, name);
|
|
view_manager->get_portrait_view()->set_waiting(true);
|
|
}
|
|
|
|
/* Set callback & callback_user_data so that a message will be sent to the
|
|
* caller when input has been gathered. */
|
|
void Events::request_input(CallBack *caller, void *user_data) {
|
|
callback_target = caller;
|
|
callback_user_data = (char *) user_data;
|
|
}
|
|
|
|
// typically this will be coming from inventory
|
|
bool Events::select_obj(Obj *obj, Actor *actor) {
|
|
if (looking_at_spellbook && view_manager->get_spell_view() != nullptr) {
|
|
view_manager->get_spell_view()->close_look();
|
|
return false;
|
|
}
|
|
assert(mode == INPUT_MODE);
|
|
//assert(input.select_from_inventory == true);
|
|
|
|
input.type = EVENTINPUT_OBJECT;
|
|
input.obj = obj;
|
|
input.actor = actor;
|
|
endAction(); // mode = prev_mode
|
|
doAction();
|
|
return true;
|
|
}
|
|
|
|
bool Events::select_actor(Actor *actor) {
|
|
assert(mode == INPUT_MODE);
|
|
|
|
if (last_mode == PUSH_MODE && !move_in_inventory && (push_actor || push_obj)) {
|
|
// Prevent selecting an actor as target when pushing on the map
|
|
cancelAction();
|
|
return false;
|
|
}
|
|
input.type = EVENTINPUT_MAPCOORD;
|
|
input.actor = actor;
|
|
input.set_loc(actor->get_location());
|
|
endAction(); // mode = prev_mode
|
|
doAction();
|
|
return true;
|
|
}
|
|
|
|
bool Events::select_direction(sint16 rel_x, sint16 rel_y) {
|
|
assert(mode == INPUT_MODE);
|
|
assert(input.get_direction == true);
|
|
|
|
input.type = EVENTINPUT_MAPCOORD_DIR;
|
|
input.set_loc(MapCoord(rel_x, rel_y));
|
|
// assumes mapwindow cursor is at the location
|
|
input.actor = map_window->get_actorAtCursor();
|
|
input.obj = map_window->get_objAtCursor();
|
|
endAction(); // mode = prev_mode
|
|
doAction();
|
|
return true;
|
|
}
|
|
|
|
// automatically converted to direction if requested
|
|
bool Events::select_target(uint16 x, uint16 y, uint8 z) {
|
|
// FIXME: is this even correct behavior?! if an arrow key is used, a direction
|
|
// should be returned, but you can still select any target with the mouse
|
|
// (which works, but then what's the point of using directions?)
|
|
if (input.get_direction)
|
|
return select_direction(x - input.target_init->x,
|
|
y - input.target_init->y);
|
|
if (mode != ATTACK_MODE) { // FIXME: make ATTACK_MODE use INPUT_MODE
|
|
// need to handle weapon range
|
|
assert(mode == INPUT_MODE);
|
|
|
|
input.type = EVENTINPUT_MAPCOORD;
|
|
input.set_loc(MapCoord(x, y, z));
|
|
// assumes mapwindow cursor is at the location
|
|
input.actor = map_window->get_actorAtCursor();
|
|
input.obj = map_window->get_objAtCursor();
|
|
endAction(); // mode = prev_mode
|
|
}
|
|
doAction();
|
|
return true;
|
|
}
|
|
|
|
// called when selecting an actor by number
|
|
bool Events::select_party_member(uint8 num) {
|
|
Party *party = player->get_party();
|
|
if (num < party->get_party_size()) {
|
|
select_actor(party->get_actor(num));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Events::select_spell_num(sint16 spell_num) {
|
|
//assert(mode == INPUT_MODE);
|
|
//assert(input.select_from_inventory == true);
|
|
|
|
|
|
|
|
input.type = EVENTINPUT_SPELL_NUM;
|
|
input.spell_num = spell_num;
|
|
//endAction(); // mode = prev_mode
|
|
game->get_view_manager()->close_spell_mode();
|
|
doAction();
|
|
return true;
|
|
}
|
|
|
|
// move the cursor or walk around; do action for direction-targeted modes
|
|
bool Events::move(sint16 rel_x, sint16 rel_y) {
|
|
MapCoord cursor_coord;
|
|
|
|
if (game->user_paused())
|
|
return false;
|
|
EventMode current_mode;
|
|
if (last_mode == MULTIUSE_MODE && game->get_party()->is_in_combat_mode())
|
|
current_mode = ATTACK_MODE;
|
|
else
|
|
current_mode = mode;
|
|
|
|
switch (current_mode) {
|
|
case ATTACK_MODE :
|
|
cursor_coord = map_window->get_cursorCoord();
|
|
cursor_coord.x = WRAPPED_COORD(cursor_coord.x + rel_x, cursor_coord.z);
|
|
cursor_coord.y = WRAPPED_COORD(cursor_coord.y + rel_y, cursor_coord.z);
|
|
if (!player->weapon_can_hit(cursor_coord.x, cursor_coord.y))
|
|
break;
|
|
DEBUG(0, LEVEL_DEBUGGING, "attack select(%d,%d)\n", cursor_coord.x, cursor_coord.y);
|
|
map_window->moveCursorRelative(rel_x, rel_y);
|
|
break;
|
|
|
|
case EQUIP_MODE :
|
|
map_window->moveCursorRelative(rel_x, rel_y);
|
|
break;
|
|
|
|
case INPUT_MODE : {
|
|
bool needs_dir = input_really_needs_directon();
|
|
if (!direction_selects_target && needs_dir) {
|
|
cursor_coord = map_window->get_cursorCoord();
|
|
cursor_coord.x = WRAPPED_COORD(cursor_coord.x + rel_x, cursor_coord.z);
|
|
cursor_coord.y = WRAPPED_COORD(cursor_coord.y + rel_y, cursor_coord.z);
|
|
if (input.target_init->distance(cursor_coord) > 1)
|
|
break;
|
|
} else if (last_mode == CAST_MODE) {
|
|
cursor_coord = map_window->get_cursorCoord();
|
|
cursor_coord.x = WRAPPED_COORD(cursor_coord.x + rel_x, cursor_coord.z);
|
|
cursor_coord.y = WRAPPED_COORD(cursor_coord.y + rel_y, cursor_coord.z);
|
|
if (player->get_actor()->get_range(cursor_coord.x, cursor_coord.y) > 7)
|
|
break;
|
|
}
|
|
map_window->moveCursorRelative(rel_x, rel_y);
|
|
if (direction_selects_target && needs_dir)
|
|
select_direction(rel_x, rel_y);
|
|
break;
|
|
}
|
|
default :
|
|
if (player->check_walk_delay() && !view_manager->gumps_are_active()) {
|
|
player->moveRelative(rel_x, rel_y);
|
|
game->time_changed();
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Begin a conversation with an actor if him/her/it is willing to talk.
|
|
* Returns true if conversation starts.
|
|
*/
|
|
bool Events::perform_talk(Actor *actor) {
|
|
ActorManager *actor_manager = game->get_actor_manager();
|
|
Actor *pc = player->get_actor();
|
|
uint8 id = actor->get_actor_num();
|
|
|
|
if (game->get_game_type() != NUVIE_GAME_U6) {
|
|
return game->get_script()->call_talk_to_actor(actor);
|
|
}
|
|
|
|
if (actor->is_in_vehicle()) {
|
|
scroll->display_string("Not in vehicle.\n");
|
|
return false;
|
|
}
|
|
if (id == pc->get_actor_num()) { // actor is controlled by player
|
|
// Note: being the player, this should ALWAYS use the real name
|
|
scroll->display_string(actor->get_name());
|
|
scroll->display_string("\n");
|
|
scroll->display_string("Talking to yourself?\n");
|
|
return false;
|
|
}
|
|
if (actor->is_in_party() && !actor->is_onscreen()) {
|
|
scroll->display_string(actor->get_name());
|
|
scroll->display_string("\n");
|
|
scroll->display_string("Not on screen.\n");
|
|
return false;
|
|
}
|
|
// FIXME: this check and the "no response" messages should be in Converse
|
|
if (!player->in_party_mode() && !pc->is_avatar()) { //only the avatar can talk in solo mode
|
|
// always display look-string on failure
|
|
scroll->display_string(actor->get_name());
|
|
scroll->display_string("\n");
|
|
scroll->display_string("Not in solo mode.\n");
|
|
} else if (actor->is_sleeping() || actor->is_paralyzed() || actor->get_corpser_flag()
|
|
|| actor->get_alignment() == ACTOR_ALIGNMENT_EVIL
|
|
|| actor->get_alignment() == ACTOR_ALIGNMENT_CHAOTIC
|
|
|| (actor->get_alignment() == ACTOR_ALIGNMENT_NEUTRAL && actor->will_not_talk())) {
|
|
// always display name or look-string on failure
|
|
scroll->display_string(actor->get_name());
|
|
scroll->display_string("\n\nNo response\n");
|
|
} else if (game->get_converse()->start(actor)) { // load and begin npc script
|
|
// try to use real name
|
|
scroll->display_string(actor->get_name());
|
|
scroll->display_string("\n");
|
|
// turn towards eachother
|
|
pc->face_actor(actor);
|
|
if (!actor->is_immobile())
|
|
actor->face_actor(pc);
|
|
return true;
|
|
} else { // some actor that has no script
|
|
// always display look-string on failure
|
|
scroll->display_string(actor_manager->look_actor(actor));
|
|
scroll->display_string("\n");
|
|
scroll->display_string("Funny, no response.\n");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Talk to `actor'. Return to the prompt if no conversation starts.
|
|
* Returns the result of the talk function.
|
|
*/
|
|
bool Events::talk(Actor *actor) {
|
|
bool talking = true;
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
endAction();
|
|
|
|
if (!actor) {
|
|
scroll->display_string("nothing!\n");
|
|
talking = false;
|
|
} else if (!perform_talk(actor))
|
|
talking = false;
|
|
|
|
if (!talking) {
|
|
// scroll->display_string("\n");
|
|
// scroll->display_prompt();
|
|
endAction(true);
|
|
}
|
|
return talking;
|
|
}
|
|
|
|
bool Events::talk_cursor() {
|
|
Actor *actor = map_window->get_actorAtCursor();
|
|
if (actor && input.actor->is_visible())
|
|
return talk(actor);
|
|
return talk(map_window->get_objAtCursor());
|
|
}
|
|
|
|
bool Events::talk_start() {
|
|
if (game->user_paused())
|
|
return false;
|
|
close_gumps();
|
|
get_target("Talk-");
|
|
return true;
|
|
}
|
|
|
|
/* You can talk to some objects using their quality as actor number. */
|
|
bool Events::talk(Obj *obj) {
|
|
ActorManager *actor_manager = game->get_actor_manager();
|
|
if (obj) {
|
|
if (game->get_game_type() == NUVIE_GAME_U6) {
|
|
if (obj->obj_n == OBJ_U6_SHRINE
|
|
|| obj->obj_n == OBJ_U6_STATUE_OF_MONDAIN
|
|
|| obj->obj_n == OBJ_U6_STATUE_OF_MINAX
|
|
|| obj->obj_n == OBJ_U6_STATUE_OF_EXODUS)
|
|
return (talk(actor_manager->get_actor(obj->quality)));
|
|
} else {
|
|
endAction();
|
|
bool status = game->get_script()->call_talk_to_obj(obj);
|
|
if (status == false) {
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
}
|
|
return status;
|
|
}
|
|
}
|
|
scroll->display_string("nothing!\n");
|
|
endAction();
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
return false;
|
|
}
|
|
|
|
void Events::try_next_attack() {
|
|
if (Game::get_game()->get_actor_manager()->get_avatar()->get_hp() == 0) { // need to end turn if Avatar died
|
|
endAction();
|
|
Game::get_game()->get_actor_manager()->startActors();
|
|
return;
|
|
} else if (player->attack_select_next_weapon(true) == false) {
|
|
player->subtract_movement_points(10);
|
|
game->get_actor_manager()->startActors(); // end player turn
|
|
endAction();
|
|
} else {
|
|
map_window->set_show_cursor(true);
|
|
mode = ATTACK_MODE; // FIXME: need to return after WAIT_MODE
|
|
//endAction(false);
|
|
//newAction(ATTACK_MODE);
|
|
}
|
|
}
|
|
|
|
bool Events::attack() {
|
|
MapCoord target = map_window->get_cursorCoord();
|
|
Actor *actor = map_window->get_actorAtCursor();
|
|
Actor *p = player->get_actor();
|
|
bool tile_is_black = map_window->tile_is_black(target.x, target.y);
|
|
|
|
if (game->get_script()->call_out_of_ammo(p, p->get_weapon_obj(player->get_current_weapon()), true)) {
|
|
// the function prints out the message
|
|
try_next_attack(); // SE and MD have weapons that need ammo and only take up 1 slot
|
|
return true;
|
|
} else if (tile_is_black)
|
|
scroll->display_string("nothing!\n");
|
|
else if (actor) {
|
|
if (actor->get_actor_num() == player->get_actor()->get_actor_num() //don't attack yourself.
|
|
|| (actor->is_in_party() && actor->get_alignment() == ACTOR_ALIGNMENT_GOOD)) {
|
|
ActorManager *actor_manager = game->get_actor_manager();
|
|
Actor *a = actor_manager->get_actor(actor->get_x(), actor->get_y(), actor->get_z(), true, actor);
|
|
if (a) // exclude previous target if we find another actor
|
|
actor = a;
|
|
else if (actor->get_actor_num() == player->get_actor()->get_actor_num()) {
|
|
scroll->display_string("pass.\n");
|
|
player->subtract_movement_points(10);
|
|
endAction(true);
|
|
return true;
|
|
}
|
|
}
|
|
if (actor->is_visible()) {
|
|
scroll->display_string(actor->get_name());
|
|
scroll->display_string(".\n");
|
|
}
|
|
}
|
|
if ((!actor || !actor->is_visible()) && !tile_is_black) {
|
|
Obj *obj = map_window->get_objAtCursor();
|
|
if (obj && (!obj->is_on_map() || !map_window->tile_is_black(obj->x, obj->y, obj))) {
|
|
scroll->display_string(obj_manager->get_obj_name(obj->obj_n, obj->frame_n));
|
|
scroll->display_string(".\n");
|
|
} else {
|
|
scroll->display_string(game->get_game_map()->look(target.x, target.y, target.z));
|
|
scroll->display_string(".\n");
|
|
}
|
|
}
|
|
|
|
map_window->set_show_cursor(false);
|
|
player->attack(target, actor);
|
|
|
|
try_next_attack();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Events::get_start() {
|
|
if (game->user_paused())
|
|
return false;
|
|
if (game->get_script()->call_is_ranged_select(GET))
|
|
get_target("Get-");
|
|
else
|
|
get_direction("Get-");
|
|
return true;
|
|
}
|
|
|
|
bool Events::push_start() {
|
|
if (game->user_paused())
|
|
return false;
|
|
push_obj = nullptr;
|
|
push_actor = nullptr;
|
|
if (game->get_script()->call_is_ranged_select(MOVE))
|
|
get_target("Move-");
|
|
else
|
|
get_direction("Move-");
|
|
return true;
|
|
}
|
|
|
|
/* Get object into an actor. (no mode change) */
|
|
bool Events::perform_get(Obj *obj, Obj *container_obj, Actor *actor) {
|
|
bool got_object = false;
|
|
bool can_perform_get = false;
|
|
//float weight;
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
if (obj) {
|
|
if (!actor)
|
|
actor = player->get_actor();
|
|
|
|
if (obj->is_on_map() && map_window->tile_is_black(obj->x, obj->y, obj)) {
|
|
scroll->display_string("nothing");
|
|
} else {
|
|
scroll->display_string(obj_manager->look_obj(obj));
|
|
|
|
if (game->using_hackmove())
|
|
can_perform_get = true;
|
|
else if (!map_window->can_get_obj(actor, obj)) {
|
|
scroll->display_string("\n\nCan't reach it.");
|
|
} else if (obj->is_on_map()) {
|
|
MapCoord target(obj->x, obj->y, obj->z);
|
|
if (!game->get_script()->call_is_ranged_select(GET)
|
|
&& player->get_actor()->get_location().distance(target) > 1
|
|
&& map_window->get_interface() == INTERFACE_NORMAL) {
|
|
scroll->display_string("\n\nOut of range!");
|
|
} else if (obj_manager->obj_is_damaging(obj, actor)) {
|
|
return false;
|
|
} else {
|
|
can_perform_get = true;
|
|
}
|
|
} else {
|
|
can_perform_get = true;
|
|
}
|
|
}
|
|
} else
|
|
scroll->display_string("nothing");
|
|
|
|
if (can_perform_get) {
|
|
// perform GET usecode (can't add to container)
|
|
if (usecode->has_getcode(obj) && (usecode->get_obj(obj, actor) == false)) {
|
|
game->get_script()->call_actor_subtract_movement_points(actor, 3);
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
map_window->updateBlacking();
|
|
return false; // ???
|
|
}
|
|
|
|
got_object = game->get_script()->call_actor_get_obj(actor, obj, container_obj);
|
|
}
|
|
|
|
scroll->display_string("\n\n");
|
|
scroll->display_prompt();
|
|
map_window->updateBlacking();
|
|
return got_object;
|
|
}
|
|
|
|
/* Get object at selected position, and end action. */
|
|
bool Events::get(sint16 rel_x, sint16 rel_y) {
|
|
uint16 x, y;
|
|
uint8 level;
|
|
|
|
player->get_location(&x, &y, &level);
|
|
return get(MapCoord((uint16)(x + rel_x), (uint16)(y + rel_y), level));
|
|
}
|
|
|
|
bool Events::get(const MapCoord &coord) {
|
|
Obj *obj = obj_manager->get_obj(coord.x, coord.y, coord.z, OBJ_SEARCH_TOP, OBJ_EXCLUDE_IGNORED);
|
|
bool got_object;
|
|
if (!game->is_new_style())
|
|
got_object = perform_get(obj, view_manager->get_inventory_view()->get_inventory_widget()->get_container(),
|
|
player->get_actor());
|
|
else
|
|
got_object = perform_get(obj, nullptr, player->get_actor());
|
|
view_manager->update(); //redraw views to show new item.
|
|
endAction();
|
|
|
|
return got_object;
|
|
}
|
|
|
|
bool Events::use_start() {
|
|
if (game->user_paused())
|
|
return false;
|
|
if (game->get_script()->call_is_ranged_select(USE))
|
|
get_target("Use-");
|
|
else
|
|
get_direction("Use-");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Events::use(Obj *obj) {
|
|
if (game->user_paused())
|
|
return false;
|
|
if (obj && obj->is_on_map() && map_window->tile_is_black(obj->x, obj->y, obj)) {
|
|
Obj *bottom_obj = obj_manager->get_obj(obj->x, obj->y, obj->z, false);
|
|
if (game->get_game_type() == NUVIE_GAME_U6 && bottom_obj->obj_n == OBJ_U6_SECRET_DOOR // hack for frame 2
|
|
&& !map_window->tile_is_black(obj->x, obj->y, bottom_obj))
|
|
obj = bottom_obj;
|
|
else
|
|
obj = nullptr;
|
|
}
|
|
if (!obj) {
|
|
scroll->display_string("nothing\n");
|
|
endAction(true);
|
|
return true;
|
|
}
|
|
MapCoord target(obj->x, obj->y, obj->z);
|
|
MapCoord player_loc = player->get_actor()->get_location();
|
|
bool display_prompt = true;
|
|
|
|
scroll->display_string(obj_manager->look_obj(obj));
|
|
scroll->display_string("\n");
|
|
|
|
if (!usecode->has_usecode(obj)) {
|
|
scroll->display_string("\nNot usable\n");
|
|
DEBUG(0, LEVEL_DEBUGGING, "Object %d:%d\n", obj->obj_n, obj->frame_n);
|
|
} else if (!obj->is_in_inventory() && map_window->get_interface() == INTERFACE_NORMAL
|
|
&& !game->get_script()->call_is_ranged_select(USE) && player->get_actor()->get_location().distance(target) > 1) {
|
|
scroll->display_string("\nOut of range!\n");
|
|
DEBUG(0, LEVEL_DEBUGGING, "distance to object: %d\n", player->get_actor()->get_location().distance(target));
|
|
} else if (!player->in_party_mode() && obj->is_in_inventory() && !obj->get_actor_holding_obj()->is_onscreen()) {
|
|
scroll->display_string("\nNot on screen.\n");
|
|
} else if (!obj->is_in_inventory() && !game->get_script()->call_is_ranged_select(USE)
|
|
&& !map_window->can_get_obj(player->get_actor(), obj) && player_loc != target) {
|
|
scroll->display_string("\nCan't reach it\n");
|
|
} else { // Usable
|
|
display_prompt = usecode->use_obj(obj, player->get_actor());
|
|
player->subtract_movement_points(MOVE_COST_USE);
|
|
}
|
|
|
|
if (mode == USE_MODE && usecode->get_running_script() == nullptr) // check mode because UseCode may have changed it
|
|
endAction(display_prompt);
|
|
return true;
|
|
}
|
|
|
|
bool Events::use(Actor *actor, uint16 x, uint16 y) {
|
|
if (game->user_paused())
|
|
return false;
|
|
bool display_prompt = true;
|
|
Obj *obj = actor->make_obj();
|
|
|
|
if (!map_window->tile_is_black(x, y) && usecode->has_usecode(actor)) {
|
|
if (game->get_game_type() == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_HORSE_WITH_RIDER)
|
|
scroll->display_string("horse");
|
|
else
|
|
scroll->display_string(obj_manager->look_obj(obj));
|
|
scroll->display_string("\n");
|
|
|
|
MapCoord player_loc = player->get_actor()->get_location();
|
|
MapCoord target = MapCoord(x, y, player_loc.z);
|
|
|
|
if (player_loc.distance(target) > 1
|
|
&& map_window->get_interface() == INTERFACE_NORMAL) {
|
|
scroll->display_string("\nOut of range!\n");
|
|
DEBUG(0, LEVEL_DEBUGGING, "distance to object: %d\n", player_loc.distance(target));
|
|
} else if (!can_get_to_actor(actor, x, y))
|
|
scroll->display_string("\nBlocked.\n");
|
|
else {
|
|
display_prompt = usecode->use_obj(obj, player->get_actor());
|
|
player->subtract_movement_points(5);
|
|
}
|
|
} else {
|
|
scroll->display_string("nothing\n");
|
|
DEBUG(0, LEVEL_DEBUGGING, "Object %d:%d\n", obj->obj_n, obj->frame_n);
|
|
}
|
|
// FIXME: usecode might request input, causing the obj to be accessed again,
|
|
// so we can't delete it in that case
|
|
assert(mode == USE_MODE || game->user_paused());
|
|
delete_obj(obj); // we were using an actor so free the temp Obj
|
|
if (mode == USE_MODE) // check mode because UseCode may have changed it
|
|
endAction(display_prompt);
|
|
return true;
|
|
}
|
|
|
|
bool Events::use(sint16 rel_x, sint16 rel_y) {
|
|
map_window->centerCursor();
|
|
map_window->moveCursorRelative(rel_x, rel_y);
|
|
return use(map_window->get_cursorCoord());
|
|
}
|
|
|
|
bool Events::use(const MapCoord &coord) {
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
if (!map_window->tile_is_black(coord.x, coord.y)) {
|
|
Actor *actor = game->get_actor_manager()->get_actor(coord.x, coord.y, coord.z);
|
|
Obj *obj = map_window->get_objAtCoord(coord, OBJ_SEARCH_TOP, OBJ_EXCLUDE_IGNORED, true);
|
|
|
|
if (obj && obj->is_on_map() && map_window->tile_is_black(obj->x, obj->y, obj)) {
|
|
Obj *bottom_obj = obj_manager->get_obj(obj->x, obj->y, obj->z, false);
|
|
if (game->get_game_type() == NUVIE_GAME_U6 && bottom_obj->obj_n == OBJ_U6_SECRET_DOOR // hack for frame 2
|
|
&& !map_window->tile_is_black(obj->x, obj->y, bottom_obj))
|
|
obj = bottom_obj;
|
|
else
|
|
obj = nullptr;
|
|
}
|
|
bool visible_actor = actor && actor->is_visible();
|
|
|
|
if (obj && (!visible_actor || !usecode->has_usecode(actor)))
|
|
return use(obj);
|
|
if (visible_actor) {
|
|
return use(actor, coord.x, coord.y);
|
|
}
|
|
}
|
|
|
|
scroll->display_string("nothing\n");
|
|
endAction(true);
|
|
return true;
|
|
}
|
|
|
|
bool Events::look_start() {
|
|
if (game->user_paused())
|
|
return false;
|
|
get_target("Look-");
|
|
return true;
|
|
}
|
|
|
|
/* Returns true if object can be searched. (false if prompt shouldn't be shown)
|
|
*/
|
|
bool Events::look(Obj *obj) {
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
if (obj) {
|
|
if (game->get_game_type() == NUVIE_GAME_U6) {
|
|
if (obj->obj_n == OBJ_U6_STATUE_OF_MONDAIN
|
|
|| obj->obj_n == OBJ_U6_STATUE_OF_MINAX
|
|
|| obj->obj_n == OBJ_U6_STATUE_OF_EXODUS) {
|
|
Actor *actor = game->get_actor_manager()->get_actor(obj->quality);
|
|
look(actor);
|
|
return false;
|
|
} else if (obj->obj_n == OBJ_U6_SPELLBOOK) {
|
|
looking_at_spellbook = true;
|
|
game->get_script()->call_look_obj(obj);
|
|
Actor *reader = obj->get_actor_holding_obj();
|
|
if (!reader)
|
|
reader = player->get_actor();
|
|
view_manager->close_all_gumps();
|
|
view_manager->set_spell_mode(reader, obj, false);
|
|
gui->lock_input(view_manager->get_current_view());
|
|
view_manager->get_current_view()->grab_focus();
|
|
return false;
|
|
}
|
|
}
|
|
obj_manager->print_obj(obj, false); // DEBUG
|
|
/* if(game->is_new_style())
|
|
{
|
|
new TextEffect(obj_manager->look_obj(obj, true), MapCoord((obj->x - map_window->get_cur_x())*16,(obj->y-map_window->get_cur_y())*16,obj->z));
|
|
}*/
|
|
if (game->get_script()->call_look_obj(obj) == false) {
|
|
scroll->display_prompt();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Returns true if there was a portrait for actor. */
|
|
bool Events::look(Actor *actor) {
|
|
ActorManager *actor_manager = game->get_actor_manager();
|
|
sint16 p_id = -1; // party member number of actor
|
|
bool had_portrait = false;
|
|
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
if (actor->get_actor_num() != 0) {
|
|
display_portrait(actor);
|
|
had_portrait = view_manager->get_portrait_view()->get_waiting();
|
|
}
|
|
|
|
actor_manager->print_actor(actor); // DEBUG
|
|
scroll->display_string("Thou dost see ");
|
|
// show real actor name and portrait if in avatar's party
|
|
if ((p_id = player->get_party()->get_member_num(actor)) >= 0)
|
|
scroll->display_string(player->get_party()->get_actor_name(p_id));
|
|
else
|
|
scroll->display_string(actor_manager->look_actor(actor, true));
|
|
scroll->display_string("\n");
|
|
return had_portrait;
|
|
}
|
|
|
|
bool Events::search(Obj *obj) {
|
|
MapCoord player_loc = player->get_actor()->get_location(),
|
|
target_loc = map_window->get_cursorCoord();
|
|
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
if (obj->get_engine_loc() == OBJ_LOC_MAP && player_loc.distance(target_loc) <= 1) {
|
|
scroll->display_string("\nSearching here, you find ");
|
|
if (!usecode->search_obj(obj, player->get_actor()))
|
|
scroll->display_string("nothing.\n");
|
|
else {
|
|
scroll->display_string(".\n");
|
|
map_window->updateBlacking(); // secret doors
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// looks at the whatever is at MapWindow cursor location
|
|
bool Events::lookAtCursor(bool delayed, uint16 x, uint16 y, uint8 z, Obj *obj, Actor *actor) {
|
|
bool display_prompt = true;
|
|
|
|
if (!delayed) {
|
|
x = map_window->get_cursorCoord().x;
|
|
y = map_window->get_cursorCoord().y;
|
|
z = map_window->get_cursorCoord().z;
|
|
obj = map_window->get_objAtCursor();
|
|
actor = map_window->get_actorAtCursor();
|
|
}
|
|
|
|
if (obj && obj->is_on_map() && ((obj->status & OBJ_STATUS_INVISIBLE) || map_window->tile_is_black(x, y, obj))) {
|
|
Obj *bottom_obj = obj_manager->get_obj(x, y, z, false);
|
|
if (bottom_obj && game->get_game_type() == NUVIE_GAME_U6 && bottom_obj->obj_n == OBJ_U6_SECRET_DOOR // hack for frame 2
|
|
&& !map_window->tile_is_black(x, y, bottom_obj))
|
|
obj = bottom_obj;
|
|
else
|
|
obj = nullptr;
|
|
}
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
if (map_window->tile_is_black(x, y))
|
|
scroll->display_string("Thou dost see darkness.\n");
|
|
else if (actor && actor->is_visible())
|
|
display_prompt = !look(actor);
|
|
else if (obj) {
|
|
if (look(obj))
|
|
search(obj);
|
|
else
|
|
display_prompt = false;
|
|
} else { // ground
|
|
scroll->display_string("Thou dost see ");
|
|
/* if(game->is_new_style())
|
|
new TextEffect(game->get_game_map()->look(x, y, z), MapCoord((x - map_window->get_cur_x())*16,(y-map_window->get_cur_y())*16,z));*/
|
|
scroll->display_string(game->get_game_map()->look(x, y, z));
|
|
scroll->display_string("\n");
|
|
}
|
|
|
|
endAction(display_prompt);
|
|
return true;
|
|
}
|
|
|
|
bool Events::pushTo(Obj *obj, Actor *actor) {
|
|
bool ok = false;
|
|
|
|
if (obj) {
|
|
if (game->get_game_type() == NUVIE_GAME_SE || push_obj != obj)
|
|
scroll->display_string(obj_manager->look_obj(obj));
|
|
scroll->display_string("\n");
|
|
|
|
if (obj_manager->can_store_obj(obj, push_obj)) {
|
|
if (obj->is_in_inventory()) {
|
|
Actor *src_actor = game->get_player()->get_actor();
|
|
Actor *target_actor = obj->get_actor_holding_obj();
|
|
if (can_move_obj_between_actors(push_obj, src_actor, target_actor, false))
|
|
obj_manager->moveto_container(push_obj, obj);
|
|
scroll->message("\n\n");
|
|
endAction();
|
|
return true;
|
|
}
|
|
ok = obj_manager->moveto_container(push_obj, obj);
|
|
}
|
|
} else {
|
|
if (actor) {
|
|
Actor *src_actor;
|
|
if (push_obj->is_in_inventory())
|
|
src_actor = push_obj->get_actor_holding_obj();
|
|
else
|
|
src_actor = game->get_player()->get_actor();
|
|
|
|
if (can_move_obj_between_actors(push_obj, src_actor, actor, true))
|
|
obj_manager->moveto_inventory(push_obj, actor);
|
|
scroll->message("\n\n");
|
|
endAction();
|
|
return true;
|
|
} else {
|
|
scroll->message("nobody.\n\n");
|
|
endAction();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!ok) {
|
|
if (obj == push_obj) {
|
|
if (game->get_game_type() == NUVIE_GAME_MD)
|
|
scroll->display_string("\nAn item can't be placed inside itself!\n\n");
|
|
else if (game->get_game_type() == NUVIE_GAME_SE)
|
|
scroll->display_string("\nYou can't do that!\n\n");
|
|
else if (obj->container)
|
|
scroll->display_string("\nHow can a container go into itself!\n\n");
|
|
else
|
|
scroll->display_string("\nnot a container\n\n");
|
|
} else if (game->get_game_type() == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_VORTEX_CUBE)
|
|
scroll->display_string("\nOnly moonstones can go into the vortex cube.\n\n");
|
|
else if (game->get_game_type() == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_SPELLBOOK) {
|
|
if (push_obj->obj_n == OBJ_U6_SPELL)
|
|
scroll->display_string("\nThe spellbook already has this spell.\n\n");
|
|
else
|
|
scroll->display_string("\nOnly spells can go into the spellbook.\n\n");
|
|
} else if (game->get_game_type() == NUVIE_GAME_U6 && !obj->container)
|
|
scroll->display_string("\nnot a container\n\n");
|
|
else if (game->get_game_type() == NUVIE_GAME_U6)
|
|
scroll->display_string("\nNot possible!\n\n");
|
|
else
|
|
scroll->display_string("\nYou can't do that!\n\n");
|
|
}
|
|
|
|
scroll->display_prompt();
|
|
endAction();
|
|
return true;
|
|
}
|
|
|
|
/* Move selected object in direction relative to object.
|
|
* (coordinates can be relative to player or object)
|
|
*/
|
|
bool Events::pushTo(sint16 rel_x, sint16 rel_y, bool push_from) {
|
|
Tile *obj_tile;
|
|
bool can_move = false; // some checks must determine if object can_move
|
|
Map *map = game->get_game_map();
|
|
MapCoord pusher = player->get_actor()->get_location();
|
|
MapCoord from, to; // absolute locations: object, target
|
|
sint16 pushrel_x, pushrel_y; // direction relative to object
|
|
LineTestResult lt;
|
|
Script *script = game->get_script();
|
|
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
if (!push_actor && !push_obj) {
|
|
scroll->display_string("what?\n\n");
|
|
scroll->display_prompt();
|
|
endAction();
|
|
return false;
|
|
}
|
|
|
|
if (push_actor)
|
|
from = push_actor->get_location();
|
|
else {
|
|
if (push_obj->is_on_map()) {
|
|
from = MapCoord(push_obj->x, push_obj->y, push_obj->z);
|
|
} else {
|
|
// exchange inventory.
|
|
Actor *src_actor = push_obj->get_actor_holding_obj();
|
|
if (!src_actor) // container on the map (container gump)
|
|
src_actor = player->get_actor();
|
|
// if(src_actor)
|
|
{
|
|
Actor *target_actor = map->get_actor(rel_x, rel_y, src_actor->get_z());
|
|
if (can_move_obj_between_actors(push_obj, src_actor, target_actor, true)) {
|
|
obj_manager->moveto_inventory(push_obj, target_actor);
|
|
script->call_actor_subtract_movement_points(src_actor, 5);
|
|
}
|
|
}
|
|
scroll->message("\n\n");
|
|
endAction();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (push_from == PUSH_FROM_PLAYER) { // coordinates must be converted
|
|
to.x = pusher.x + rel_x;
|
|
to.y = pusher.y + rel_y;
|
|
} else {
|
|
to.x = from.x + rel_x;
|
|
to.y = from.y + rel_y;
|
|
}
|
|
pushrel_x = to.x - from.x;
|
|
pushrel_y = to.y - from.y;
|
|
|
|
sint8 wrappedXDir = get_wrapped_rel_dir(to.x, from.x, to.z);
|
|
sint8 wrappedYDir = get_wrapped_rel_dir(to.y, from.y, to.z);
|
|
|
|
if (map_window->get_interface() == INTERFACE_NORMAL || push_actor) {
|
|
// you can only push one space at a time
|
|
pushrel_x = wrappedXDir;
|
|
pushrel_y = wrappedYDir;
|
|
}
|
|
to.x = from.x + pushrel_x;
|
|
to.y = from.y + pushrel_y;
|
|
to.z = from.z;
|
|
|
|
// Use wrapped direction since we could have crossed a map boundary
|
|
scroll->display_string(get_direction_name(wrappedXDir, wrappedYDir));
|
|
scroll->display_string(".\n\n");
|
|
|
|
// Coordinates could be out of the map's bounds now, make them wrap around
|
|
WRAP_COORD(to.x, to.z);
|
|
WRAP_COORD(to.y, to.z);
|
|
|
|
if (pushrel_x == 0 && pushrel_y == 0) {
|
|
scroll->display_prompt();
|
|
endAction();
|
|
return true;
|
|
}
|
|
|
|
if (push_actor || push_obj->is_on_map()) {
|
|
const uint16 pushObjN = push_obj ? push_obj->obj_n : push_actor->get_obj_n();
|
|
// Objects/Actors with a base weight of 0 are not movable
|
|
bool isUnmovable = obj_manager->get_obj_weight_unscaled(pushObjN) == 0;
|
|
// U6 does not allow pushing dragons
|
|
if (game->get_game_type() == NUVIE_GAME_U6)
|
|
isUnmovable = isUnmovable || pushObjN == OBJ_U6_DRAGON;
|
|
|
|
if (isUnmovable) {
|
|
scroll->display_string("Not possible\n\n");
|
|
scroll->display_prompt();
|
|
endAction();
|
|
return false;
|
|
}
|
|
}
|
|
CanDropOrMoveMsg can_move_check;
|
|
if (push_obj && (can_move_check = map_window->can_drop_or_move_obj(to.x, to.y, player->get_actor(), push_obj))
|
|
!= MSG_SUCCESS) {
|
|
// scroll->display_string("Blocked.\n"); // using text from can_drop_or_move_obj
|
|
map_window->display_can_drop_or_move_msg(can_move_check, "");
|
|
endAction(true);
|
|
return true;
|
|
}
|
|
DEBUG(0, LEVEL_WARNING, "deduct moves from player\n");
|
|
|
|
if (push_actor) {
|
|
const auto playerActor = player->get_actor();
|
|
auto strengthCheckFailed = [=]() {
|
|
// Using adjusted strength here, which takes cursed status into account
|
|
const uint playerStr = script->call_actor_str_adj(playerActor);
|
|
const uint pushedActorStr = script->call_actor_str_adj(push_actor);
|
|
return (pushedActorStr / 2 + 30 - playerStr) / 2 > getRandom(29) + 1;
|
|
};
|
|
|
|
const ActorMoveFlags moveFlags = ACTOR_IGNORE_MOVES | ACTOR_IGNORE_DANGER | ACTOR_IGNORE_PARTY_MEMBERS;
|
|
|
|
// Can not push self and must pass strength test
|
|
if (push_actor == playerActor || !push_actor->can_be_moved() || strengthCheckFailed())
|
|
scroll->display_string("Failed.\n\n");
|
|
else if (!push_actor->move(to.x, to.y, to.z, moveFlags))
|
|
scroll->display_string("Blocked.\n\n");
|
|
else
|
|
player->subtract_movement_points(5);
|
|
} else {
|
|
if (map_window->get_interface() != INTERFACE_IGNORE_BLOCK
|
|
&& map_window->blocked_by_wall(player->get_actor(), push_obj)) {
|
|
scroll->display_string("Blocked.\n\n");
|
|
} else if (!usecode->has_movecode(push_obj) || usecode->move_obj(push_obj, pushrel_x, pushrel_y)) {
|
|
if (game->get_game_type() == NUVIE_GAME_U6 && (push_obj->obj_n == OBJ_U6_SKIFF
|
|
|| push_obj->obj_n == OBJ_U6_RAFT)) {
|
|
Obj *to_obj = obj_manager->get_obj(to.x, to.y, to.z, true);
|
|
if (to_obj) {
|
|
if (obj_manager->can_store_obj(to_obj, push_obj))
|
|
can_move = obj_manager->moveto_container(push_obj, to_obj);
|
|
} else if (map->lineTest(to.x, to.y, to.x, to.y, to.z, LT_HitActors | LT_HitUnpassable, lt)) {
|
|
if (!lt.hitActor && map->is_water(to.x, to.y, to.z))
|
|
can_move = obj_manager->move(push_obj, to.x, to.y, to.z);
|
|
} else
|
|
can_move = obj_manager->move(push_obj, to.x, to.y, to.z);
|
|
} else if (map_window->get_interface() != INTERFACE_IGNORE_BLOCK &&
|
|
map->lineTest(to.x,
|
|
to.y,
|
|
to.x,
|
|
to.y,
|
|
to.z,
|
|
LT_HitUnpassable,
|
|
lt,
|
|
0,
|
|
game->get_game_type() == NUVIE_GAME_U6 ? nullptr
|
|
: push_obj)) { //FIXME should we exclude push_obj for U6 too?
|
|
if (lt.hitObj) {
|
|
if (obj_manager->can_store_obj(lt.hitObj, push_obj)) { //if we are moving onto a container.
|
|
can_move = obj_manager->moveto_container(push_obj, lt.hitObj);
|
|
} else {
|
|
// We can place an object on a bench or table. Or on any other object if
|
|
// the object is passable and not on a boundary.
|
|
|
|
obj_tile = obj_manager->get_obj_tile(lt.hitObj->obj_n, lt.hitObj->frame_n);
|
|
if ((obj_tile->flags3 & TILEFLAG_CAN_PLACE_ONTOP) ||
|
|
(obj_tile->passable && !map->is_boundary(lt.hit_x, lt.hit_y, lt.hit_level))) {
|
|
/* do normal move if no usecode or return from usecode was true */
|
|
//if(!usecode->has_movecode(push_obj) || usecode->move_obj(push_obj,pushrel_x,pushrel_y))
|
|
can_move = obj_manager->move(push_obj, to.x, to.y, from.z);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Obj *obj = obj_manager->get_obj(to.x, to.y, to.z);
|
|
if (obj && obj_manager->can_store_obj(obj, push_obj)) { //if we are moving onto a container.
|
|
can_move = obj_manager->moveto_container(push_obj, obj);
|
|
} else {
|
|
/* do normal move if no usecode or return from usecode was true */
|
|
//if(!usecode->has_movecode(push_obj) || usecode->move_obj(push_obj,pushrel_x,pushrel_y))
|
|
can_move = obj_manager->move(push_obj, to.x, to.y, from.z);
|
|
}
|
|
}
|
|
if (!can_move)
|
|
scroll->display_string("Blocked.\n\n");
|
|
}
|
|
if (can_move)
|
|
player->subtract_movement_points(5);
|
|
}
|
|
scroll->display_prompt();
|
|
endAction();
|
|
return true;
|
|
}
|
|
|
|
bool Events::pushFrom(Obj *obj) {
|
|
scroll->display_string(obj_manager->look_obj(obj));
|
|
push_obj = obj;
|
|
if (game->get_game_type() == NUVIE_GAME_MD)
|
|
get_target("\nWhere? ");
|
|
else
|
|
get_target("\nTo ");
|
|
return true;
|
|
}
|
|
|
|
/* Select object to move. */
|
|
bool Events::pushFrom(sint16 rel_x, sint16 rel_y) {
|
|
MapCoord from = player->get_actor()->get_location();
|
|
MapCoord target = MapCoord(from.x + rel_x, from.y + rel_y, from.z);
|
|
return pushFrom(target);
|
|
}
|
|
|
|
/* Select object to move. */
|
|
bool Events::pushFrom(const MapCoord &target) {
|
|
ActorManager *actor_manager = game->get_actor_manager();
|
|
Script *script = game->get_script();
|
|
MapCoord from = player->get_actor()->get_location();
|
|
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
map_window->set_show_use_cursor(false);
|
|
if (from.x != target.x || from.y != target.y) {
|
|
push_obj = obj_manager->get_obj(target.x, target.y, from.z);
|
|
}
|
|
push_actor = actor_manager->get_actor(target.x, target.y, from.z);
|
|
if (map_window->tile_is_black(target.x, target.y, push_obj)) {
|
|
scroll->display_string("nothing.\n");
|
|
endAction(true);
|
|
return false;
|
|
}
|
|
|
|
if (push_actor && push_actor->is_visible()) {
|
|
scroll->display_string(push_actor->get_name());
|
|
push_obj = nullptr;
|
|
} else if (push_obj) {
|
|
scroll->display_string(obj_manager->look_obj(push_obj));
|
|
push_actor = nullptr;
|
|
} else {
|
|
scroll->display_string("nothing.\n");
|
|
endAction(true);
|
|
return false;
|
|
}
|
|
|
|
if (from.distance(target) > 1 && !script->call_is_ranged_select(MOVE)
|
|
&& map_window->get_interface() == INTERFACE_NORMAL) {
|
|
scroll->display_string("\n\nOut of range!\n");
|
|
endAction(true);
|
|
} else if (map_window->get_interface() != INTERFACE_NORMAL
|
|
&& ((push_obj && !map_window->can_get_obj(player->get_actor(), push_obj))
|
|
|| (push_actor && !can_get_to_actor(push_actor, target.x, target.y)))) {
|
|
scroll->display_string("\n\nCan't reach it\n");
|
|
endAction(true);
|
|
} else {
|
|
get_direction(MapCoord(target.x, target.y), "\nTo ");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Events::actor_exists(const Actor *a) const {
|
|
if (a->get_z() > 5 || a->get_actor_num() == 0
|
|
|| ((a->is_temp() || a->get_strength() == 0) && a->get_x() == 0 && a->get_y() == 0
|
|
&& a->get_z() == 0) // temp actor that has been cleaned up or invalid normal npc
|
|
/*|| strcmp(a->get_name(true), "Nothing") == 0*/) { // This last one probably isn't needed anymore
|
|
scroll->display_string("\nnpc is invalid or at invalid location");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Send input to active alt-code. */
|
|
void Events::alt_code_input(const char *in) {
|
|
ActorManager *am = game->get_actor_manager();
|
|
Actor *a = am->get_actor((uint8) strtol(in, nullptr, 10));
|
|
static string teleport_string = "";
|
|
static Obj obj;
|
|
uint8 a_num = 0;
|
|
switch (active_alt_code) {
|
|
case 300: // show NPC portrait (FIXME: should be show portrait number)
|
|
if (a) {
|
|
am->print_actor(a); //print actor debug info
|
|
display_portrait(a);
|
|
}
|
|
scroll->display_string("\n");
|
|
active_alt_code = 0;
|
|
break;
|
|
|
|
case 301: // Show Midgame graphics
|
|
game->get_script()->call_play_midgame_sequence((uint16) strtol(in, nullptr, 10));
|
|
scroll->display_string("\n");
|
|
active_alt_code = 0;
|
|
break;
|
|
|
|
case 400: // talk to NPC (FIXME: get portrait and inventory too)
|
|
a_num = (uint8) strtol(in, nullptr, 10);
|
|
if (a_num == 0 || !game->get_converse()->start(a_num)) {
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
}
|
|
active_alt_code = 0;
|
|
break;
|
|
|
|
/* case 214:
|
|
alt_code_teleport(in); //teleport player & party to location string
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
active_alt_code = 0;
|
|
break;
|
|
*/
|
|
|
|
case 214: // teleport player & party to location string
|
|
teleport_string += " ";
|
|
teleport_string += in;
|
|
++alt_code_input_num;
|
|
if (alt_code_input_num == 1) {
|
|
if (game->get_game_type() == NUVIE_GAME_U6)
|
|
scroll->display_string("\n<uai>: ");
|
|
else
|
|
scroll->display_string("\ny: ");
|
|
get_scroll_input(nullptr, true, false, false);
|
|
} else if (alt_code_input_num == 2) {
|
|
if (game->get_game_type() == NUVIE_GAME_U6)
|
|
scroll->display_string("\n<zi>: ");
|
|
else
|
|
scroll->display_string("\nz: ");
|
|
get_scroll_input(nullptr, true, false, false);
|
|
} else {
|
|
alt_code_teleport(teleport_string.c_str());
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
teleport_string = "";
|
|
alt_code_input_num = 0;
|
|
active_alt_code = 0;
|
|
}
|
|
break;
|
|
|
|
case 314: // teleport player & party to selected location
|
|
if (strtol(in, nullptr, 10) != 0)
|
|
alt_code_teleport_menu((uint32) strtol(in, nullptr, 10));
|
|
if (strtol(in, nullptr, 10) == 0 || alt_code_input_num > 2) {
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
alt_code_input_num = 0;
|
|
active_alt_code = 0;
|
|
}
|
|
break;
|
|
|
|
case 414: // teleport player & party to NPC location
|
|
if (actor_exists(a))
|
|
alt_code_teleport_to_person((uint32) strtol(in, nullptr, 10));
|
|
scroll->display_string("\n\n");
|
|
scroll->display_prompt();
|
|
active_alt_code = 0;
|
|
break;
|
|
|
|
case 500: // control/watch anyone
|
|
if (!actor_exists(a)) {
|
|
scroll->display_string("\n\n");
|
|
} else if (!a->is_alive()) {
|
|
scroll->display_string("\n");
|
|
scroll->display_string(a->get_name(true));
|
|
scroll->display_string(" is dead\n\n");
|
|
} else {
|
|
player->set_actor(a);
|
|
player->set_mapwindow_centered(true);
|
|
if (!game->is_new_style())
|
|
view_manager->set_inventory_mode(); // reset inventoryview
|
|
if (game->get_party()->contains_actor(player->get_actor())) {
|
|
in_control_cheat = false;
|
|
uint8 member_num = game->get_party()->get_member_num(player->get_actor());
|
|
if (!game->is_new_style())
|
|
view_manager->get_inventory_view()->set_party_member(member_num);
|
|
} else {
|
|
in_control_cheat = true;
|
|
if (!game->is_new_style())
|
|
view_manager->get_inventory_view()->set_actor(player->get_actor());
|
|
}
|
|
game->get_party()->update_light_sources();
|
|
scroll->display_string("\n\n");
|
|
}
|
|
scroll->display_prompt();
|
|
active_alt_code = 0;
|
|
break;
|
|
|
|
case 501: { // resurrect npc
|
|
if (!actor_exists(a)) {
|
|
// Do nothing. It already prints a message
|
|
} else if (a->is_alive()) {
|
|
scroll->display_string("\n");
|
|
scroll->display_string(a->get_name(true));
|
|
scroll->display_string(" is not dead.");
|
|
} else {
|
|
bool failed = true;
|
|
for (int i = 1; i < 8; i++) {
|
|
uint16 newx = NUVIE_RAND() % 10 + player->get_actor()->get_x() - 5;
|
|
uint16 newy = NUVIE_RAND() % 10 + player->get_actor()->get_y() - 5;
|
|
if (a->move(newx, newy, player->get_actor()->get_z())) {
|
|
failed = false;
|
|
MapCoord res_loc(newx, newy, player->get_actor()->get_z());
|
|
a->resurrect(res_loc);
|
|
}
|
|
}
|
|
if (failed) // No location found. Resurrect anyway.
|
|
a->resurrect(player->get_actor()->get_location());
|
|
}
|
|
scroll->display_string("\n\n");
|
|
scroll->display_prompt();
|
|
active_alt_code = 0;
|
|
break;
|
|
}
|
|
case 456: // polymorph
|
|
if (alt_code_input_num == 0) {
|
|
obj.obj_n = strtol(in, nullptr, 10);
|
|
scroll->display_string("\nNpc number? ");
|
|
get_scroll_input();
|
|
++alt_code_input_num;
|
|
} else {
|
|
a->morph(obj.obj_n);
|
|
scroll->display_string("\nMorphed!\n\n");
|
|
scroll->display_prompt();
|
|
alt_code_input_num = 0;
|
|
active_alt_code = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Use alt-code in `c'.
|
|
*/
|
|
void Events::alt_code(int c) {
|
|
switch (c) {
|
|
case 300: // display portrait by number
|
|
scroll->display_string("Portrait? ");
|
|
get_scroll_input();
|
|
active_alt_code = c;
|
|
break;
|
|
|
|
case 301: // display midgame sequence
|
|
scroll->display_string("Midgame? ");
|
|
get_scroll_input();
|
|
active_alt_code = c;
|
|
break;
|
|
|
|
case 400: // talk to anyone (FIXME: get portrait and inventory too)
|
|
scroll->display_string("Npc number? ");
|
|
get_scroll_input();
|
|
active_alt_code = c;
|
|
break;
|
|
|
|
case 500: // control/watch anyone
|
|
if (player->is_in_vehicle()
|
|
|| game->get_party()->is_in_combat_mode()) {
|
|
if (player->is_in_vehicle())
|
|
display_not_aboard_vehicle(false);
|
|
else
|
|
scroll->display_string("\nNot while in combat mode!\n\n");
|
|
scroll->display_prompt();
|
|
active_alt_code = 0;
|
|
break;
|
|
}
|
|
scroll->display_string("Npc number? ");
|
|
get_scroll_input();
|
|
active_alt_code = c;
|
|
break;
|
|
|
|
case 501: // resurrect npc
|
|
if (player->is_in_vehicle()) {
|
|
display_not_aboard_vehicle(false);
|
|
scroll->display_prompt();
|
|
active_alt_code = 0;
|
|
break;
|
|
}
|
|
scroll->display_string("Npc number? ");
|
|
get_scroll_input();
|
|
active_alt_code = c;
|
|
break;
|
|
|
|
case 456: // polymorph
|
|
scroll->display_string("Object number? ");
|
|
get_scroll_input();
|
|
active_alt_code = c;
|
|
break;
|
|
|
|
case 213:
|
|
alt_code_infostring();
|
|
active_alt_code = 0;
|
|
break;
|
|
|
|
/* case 214:
|
|
scroll->display_string("Location: \n",2);
|
|
scroll->display_string(" ",0);
|
|
get_scroll_input();
|
|
active_alt_code = c;
|
|
break;
|
|
*/
|
|
case 214:
|
|
if (player->is_in_vehicle()) {
|
|
if (game->get_game_type() == NUVIE_GAME_U6)
|
|
scroll->display_string("\n<nat uail abord wip!>\n");
|
|
else
|
|
display_not_aboard_vehicle();
|
|
scroll->display_prompt();
|
|
active_alt_code = 0;
|
|
} else {
|
|
if (game->get_game_type() == NUVIE_GAME_U6)
|
|
scroll->display_string("\n<gotu eks>: ");
|
|
else
|
|
scroll->display_string("\ngoto x: ");
|
|
get_scroll_input(nullptr, true, false, false);
|
|
active_alt_code = c;
|
|
}
|
|
break;
|
|
|
|
case 215:
|
|
//clock->advance_to_next_hour();
|
|
game->get_script()->call_advance_time(60);
|
|
scroll->display_string(clock->get_time_string());
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
game->time_changed();
|
|
active_alt_code = 0;
|
|
break;
|
|
|
|
case 216:
|
|
scroll->display_string(clock->get_time_string());
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
active_alt_code = 0;
|
|
break;
|
|
case 222: {
|
|
bool ethereal = !game->is_ethereal();
|
|
game->set_ethereal(ethereal);
|
|
game->get_party()->set_ethereal(ethereal);
|
|
const char *message = ethereal ? "Party desolidifies!\n\n" : "Party solidifies!\n\n";
|
|
scroll->message(message);
|
|
break;
|
|
}
|
|
case 314: // teleport player & party to selected location
|
|
if (player->is_in_vehicle()) {
|
|
display_not_aboard_vehicle();
|
|
active_alt_code = 0;
|
|
} else {
|
|
alt_code_teleport_menu(0);
|
|
active_alt_code = c;
|
|
}
|
|
break;
|
|
|
|
case 414: // teleport player & party to NPC location
|
|
if (player->is_in_vehicle()) {
|
|
display_not_aboard_vehicle();
|
|
active_alt_code = 0;
|
|
break;
|
|
}
|
|
scroll->display_string("Npc number? ");
|
|
get_scroll_input();
|
|
active_alt_code = c;
|
|
break;
|
|
|
|
case 600: // map editor
|
|
view_manager->open_mapeditor_view();
|
|
active_alt_code = 0;
|
|
break;
|
|
|
|
default: // attempt to handle the altcode with lua script.
|
|
Game::get_game()->get_script()->call_handle_alt_code(c);
|
|
scroll->display_prompt();
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool Events::alt_code_teleport(const char *location_string) {
|
|
char *next_num;
|
|
uint16 x, y, z;
|
|
|
|
if (!location_string || !strlen(location_string))
|
|
return false;
|
|
|
|
x = strtol(location_string, &next_num, 16);
|
|
y = strtol(next_num, &next_num, 16);
|
|
z = strtol(next_num, &next_num, 16);
|
|
|
|
if ((x == 0 && y == 0) || z > 5)
|
|
return false;
|
|
player->move(x, y, z, true);
|
|
|
|
// This is a bit of a hack but we would like to update the music when teleporting.
|
|
game->get_party()->update_music();
|
|
|
|
return true;
|
|
}
|
|
|
|
// changed to show time instead of date (SB-X)
|
|
void Events::alt_code_infostring() {
|
|
char buf[14]; // kkhhmmxxxyyyz
|
|
uint8 karma;
|
|
uint8 hour;
|
|
uint8 minute;
|
|
uint16 x, y;
|
|
uint8 z;
|
|
|
|
karma = player->get_karma();
|
|
player->get_location(&x, &y, &z);
|
|
|
|
hour = clock->get_hour();
|
|
minute = clock->get_minute();
|
|
|
|
Common::sprintf_s(buf, "%02d%02d%02d%03X%03X%x", karma, hour, minute, x, y, z);
|
|
|
|
scroll->display_string(buf);
|
|
scroll->display_string("\n");
|
|
new PeerEffect((x - x % 8) - 18, (y - y % 8) - 18, z); // wrap to chunk boundary, and center
|
|
// in 11x11 MapWindow
|
|
}
|
|
|
|
/* Move player to NPC location. */
|
|
bool Events::alt_code_teleport_to_person(uint32 npc) {
|
|
ActorManager *actor_manager = game->get_actor_manager();
|
|
MapCoord actor_location = actor_manager->get_actor(npc)->get_location();
|
|
player->move(actor_location.x, actor_location.y, actor_location.z, true);
|
|
if (!actor_manager->toss_actor(player->get_actor(), 2, 2))
|
|
actor_manager->toss_actor(player->get_actor(), 4, 4);
|
|
return true;
|
|
}
|
|
|
|
/* Display teleport destinations, get input. */
|
|
void Events::alt_code_teleport_menu(uint32 selection) {
|
|
static uint8 category = 0;
|
|
const char *teleport_dest = "";
|
|
if (alt_code_input_num == 0) { // select category
|
|
if (game->get_game_type() == NUVIE_GAME_U6) {
|
|
scroll->display_string("\nLazy Teleporters' Menu!\n");
|
|
scroll->display_string(" 1) Cities\n");
|
|
scroll->display_string(" 2) Major Areas\n");
|
|
scroll->display_string(" 3) Shrines\n");
|
|
scroll->display_string(" 4) Gargoyles\n");
|
|
scroll->display_string(" 5) Dungeons\n");
|
|
scroll->display_string(" 6) More Dungeons\n");
|
|
scroll->display_string(" 7) Other\n");
|
|
scroll->display_string("Category? ");
|
|
get_scroll_input("01234567");
|
|
} else if (game->get_game_type() == NUVIE_GAME_SE) {
|
|
scroll->display_string("\nLazy Teleporters' Menu!\n");
|
|
scroll->display_string(" 1) Villages\n");
|
|
scroll->display_string(" 2) More Villages\n");
|
|
scroll->display_string(" 3) S. Places\n");
|
|
scroll->display_string(" 4) Resources\n");
|
|
scroll->display_string(" 5) Teleport Pads\n");
|
|
scroll->display_string(" 6) Caves\n");
|
|
scroll->display_string(" 7) Myrm. Holes\n");
|
|
scroll->display_string("Category? ");
|
|
get_scroll_input("01234567");
|
|
}
|
|
} else if (alt_code_input_num == 1) { // selected category, select location
|
|
category = selection;
|
|
scroll->display_string("\n");
|
|
if (game->get_game_type() == NUVIE_GAME_U6) {
|
|
switch (selection) {
|
|
case 1:
|
|
scroll->display_string("Cities\n");
|
|
scroll->display_string(" 1) Britain\n");
|
|
scroll->display_string(" 2) Trinsic\n");
|
|
scroll->display_string(" 3) Yew\n");
|
|
scroll->display_string(" 4) Minoc\n");
|
|
scroll->display_string(" 5) Moonglow\n");
|
|
scroll->display_string(" 6) Jhelom\n");
|
|
scroll->display_string(" 7) Skara Brae\n");
|
|
scroll->display_string(" 8) New Magincia\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 9) Buc's Den\n");
|
|
else
|
|
scroll->display_string(" 9) Buccaneer's Den\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123456789");
|
|
break;
|
|
case 2:
|
|
scroll->display_string("Major Areas\n");
|
|
scroll->display_string(" 1) Cove\n");
|
|
scroll->display_string(" 2) Paws\n");
|
|
scroll->display_string(" 3) Serpent's Hold\n");
|
|
scroll->display_string(" 4) Empath Abbey\n");
|
|
scroll->display_string(" 5) Lycaeum\n");
|
|
scroll->display_string(" 6) Library\n");
|
|
scroll->display_string(" 7) Sutek's Island\n");
|
|
scroll->display_string(" 8) Stonegate\n");
|
|
scroll->display_string(" 9) The Codex\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123456789");
|
|
break;
|
|
case 3:
|
|
scroll->display_string("Shrines\n");
|
|
scroll->display_string(" 1) Honesty\n");
|
|
scroll->display_string(" 2) Compassion\n");
|
|
scroll->display_string(" 3) Valor\n");
|
|
scroll->display_string(" 4) Justice\n");
|
|
scroll->display_string(" 5) Sacrifice\n");
|
|
scroll->display_string(" 6) Honor\n");
|
|
scroll->display_string(" 7) Humility\n");
|
|
scroll->display_string(" 8) Spirituality\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("012345678");
|
|
break;
|
|
case 4:
|
|
scroll->display_string("Gargoyles\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 1) Hall\n");
|
|
else
|
|
scroll->display_string(" 1) Hall of Knowledge\n");
|
|
scroll->display_string(" 2) Singularity\n");
|
|
scroll->display_string(" 3) King's Temple\n");
|
|
scroll->display_string(" 4) Tomb of Kings\n");
|
|
scroll->display_string(" 5) Hythloth\n");
|
|
scroll->display_string(" 6) Control\n");
|
|
scroll->display_string(" 7) Passion\n");
|
|
scroll->display_string(" 8) Diligence\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("012345678");
|
|
break;
|
|
case 5:
|
|
scroll->display_string("Dungeons\n");
|
|
scroll->display_string(" 1) Ant Mound\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 2) Buc's Cave\n");
|
|
else
|
|
scroll->display_string(" 2) Buccaneer's Cave\n");
|
|
scroll->display_string(" 3) Covetous\n");
|
|
scroll->display_string(" 4) Crypts\n");
|
|
scroll->display_string(" 5) Cyclops Cave\n");
|
|
scroll->display_string(" 6) Deceit\n");
|
|
scroll->display_string(" 7) Despise\n");
|
|
scroll->display_string(" 8) Destard\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 9) Heftimus's\n");
|
|
else
|
|
scroll->display_string(" 9) Heftimus's Cave\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123456789");
|
|
break;
|
|
case 6:
|
|
scroll->display_string("More Dungeons\n");
|
|
scroll->display_string(" 1) Hero's Hole\n");
|
|
scroll->display_string(" 2) Hythloth\n");
|
|
scroll->display_string(" 3) Pirate Cave\n");
|
|
scroll->display_string(" 4) Sewers\n");
|
|
scroll->display_string(" 5) Shame\n");
|
|
scroll->display_string(" 6) Spider Cave\n");
|
|
scroll->display_string(" 7) Sutek's Island\n");
|
|
scroll->display_string(" 8) Swamp Cave\n");
|
|
scroll->display_string(" 9) Wrong\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123456789");
|
|
break;
|
|
case 7:
|
|
scroll->display_string("Other\n");
|
|
scroll->display_string(" 1) Iolo's Hut\n");
|
|
scroll->display_string(" 2) Lumberjack\n");
|
|
scroll->display_string(" 3) Saw Mill\n");
|
|
scroll->display_string(" 4) Thieves Guild\n");
|
|
scroll->display_string(" 5) Wisps\n");
|
|
scroll->display_string(" 6) Dagger Isle\n");
|
|
scroll->display_string(" 7) Shipwreck\n");
|
|
scroll->display_string(" 8) Phoenix\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("012345678");
|
|
break;
|
|
}
|
|
} else if (game->get_game_type() == NUVIE_GAME_SE) {
|
|
switch (selection) {
|
|
case 1:
|
|
scroll->display_string("Villages\n");
|
|
scroll->display_string(" 1) Barako\n");
|
|
scroll->display_string(" 2) Kurak\n");
|
|
scroll->display_string(" 3) Pindiro\n");
|
|
scroll->display_string(" 4) Yolaru\n");
|
|
scroll->display_string(" 5) Tichticatl\n");
|
|
scroll->display_string(" 6) Jukari\n");
|
|
scroll->display_string(" 7) Disquiqui\n");
|
|
scroll->display_string(" 8) Barrab\n");
|
|
scroll->display_string(" 9) Urali\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123456789");
|
|
break;
|
|
case 2:
|
|
scroll->display_string("More Villages\n");
|
|
scroll->display_string(" 1) Haakur\n");
|
|
scroll->display_string(" 2) Sakkhra\n");
|
|
scroll->display_string(" 3) Old Pindiro\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123");
|
|
break;
|
|
case 3:
|
|
scroll->display_string("Special Places\n");
|
|
scroll->display_string(" 1) Laboratory\n");
|
|
scroll->display_string(" 2) Drum Hill\n");
|
|
scroll->display_string(" 3) Topuru's Isle\n");
|
|
scroll->display_string(" 4) Gem Stand\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 5) Thunderer\n");
|
|
else
|
|
scroll->display_string(" 5) Thunderer Peak\n");
|
|
scroll->display_string(" 6) Great Mesa\n");
|
|
scroll->display_string(" 7) Kotl City\n");
|
|
scroll->display_string(" 8) Disq. Tyran.\n");
|
|
scroll->display_string(" 9) Silverback\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123456789");
|
|
break;
|
|
case 4:
|
|
scroll->display_string("Resources\n");
|
|
scroll->display_string(" 1) Sulphur Pits\n");
|
|
scroll->display_string(" 2) Tar Pits\n");
|
|
scroll->display_string(" 3) Pot.Nitrate\n");
|
|
scroll->display_string(" 4) Yucca Plants\n");
|
|
scroll->display_string(" 5) Bamboo\n");
|
|
scroll->display_string(" 6) River Banks\n");
|
|
scroll->display_string(" 7) Corn Stalks\n");
|
|
scroll->display_string(" 8) Blue Stone\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("012345678");
|
|
break;
|
|
case 5:
|
|
scroll->display_string("Teleport Pads\n");
|
|
scroll->display_string(" 1) Barako\n");
|
|
scroll->display_string(" 2) Dead Pad\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 3) K./Y.\n");
|
|
else
|
|
scroll->display_string(" 3) Kurak/Yolaru\n");
|
|
scroll->display_string(" 4) Nahuatla\n");
|
|
scroll->display_string(" 5) Jukari\n");
|
|
scroll->display_string(" 6) Disquiqui\n");
|
|
scroll->display_string(" 7) Barrab\n");
|
|
scroll->display_string(" 8) Sakkhra\n");
|
|
scroll->display_string(" 9) Hub\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123456789");
|
|
break;
|
|
case 6:
|
|
scroll->display_string("Caves\n");
|
|
scroll->display_string(" 1) Spider\n");
|
|
scroll->display_string(" 2) Jukari Ritual\n");
|
|
scroll->display_string(" 3) Silverback\n");
|
|
scroll->display_string(" 4) Fritz\n");
|
|
scroll->display_string(" 5) Urali Spirit\n");
|
|
scroll->display_string(" 6) Urali Chief\n");
|
|
scroll->display_string(" 7) To Urali\n");
|
|
scroll->display_string(" 8) From Urali\n");
|
|
scroll->display_string(" 9) Denys\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("0123456789");
|
|
break;
|
|
case 7:
|
|
scroll->display_string("Myrmidex Holes\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 1) S of Disq.\n");
|
|
else
|
|
scroll->display_string(" 1) S of Disquiqui\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 2) W of G. Mesa\n");
|
|
else
|
|
scroll->display_string(" 2) W of Great Mesa\n");
|
|
scroll->display_string(" 3) W of Hub\n");
|
|
if (!game->is_new_style())
|
|
scroll->display_string(" 4) E of Drum H.\n");
|
|
else
|
|
scroll->display_string(" 4) E of Drum Hill\n");
|
|
scroll->display_string(" 5) SW of Kurak\n");
|
|
scroll->display_string(" 6) Old Pindiro\n");
|
|
scroll->display_string(" 7) S of Pindiro\n");
|
|
scroll->display_string("Location? ");
|
|
get_scroll_input("01234567");
|
|
break;
|
|
}
|
|
}
|
|
} else if (alt_code_input_num == 2) { // selected location, teleport
|
|
if (game->get_game_type() == NUVIE_GAME_U6) {
|
|
switch (category) {
|
|
case 1:
|
|
if (selection == 1) // Britain
|
|
teleport_dest = "133 1a3 0";
|
|
else if (selection == 2) // Trinsic
|
|
teleport_dest = "19b 2e2 0";
|
|
else if (selection == 3) // Yew
|
|
teleport_dest = "ec a7 0";
|
|
else if (selection == 4) // Minoc
|
|
teleport_dest = "254 63 0";
|
|
else if (selection == 5) // Moonglow
|
|
teleport_dest = "38a 203 0";
|
|
else if (selection == 6) // Jhelom
|
|
teleport_dest = "a0 36b 0";
|
|
else if (selection == 7) // Skara Brae
|
|
teleport_dest = "54 203 0";
|
|
else if (selection == 8) // New Magincia
|
|
teleport_dest = "2e3 2ab 0";
|
|
else if (selection == 9) // Buc's Den
|
|
teleport_dest = "246 274 0";
|
|
break;
|
|
case 2:
|
|
if (selection == 1) // Cove
|
|
teleport_dest = "223 163 0";
|
|
else if (selection == 2) // Paws
|
|
teleport_dest = "198 264 0";
|
|
else if (selection == 3) // Serpent's Hold
|
|
teleport_dest = "22e 3bc 0";
|
|
else if (selection == 4) // Empath Abbey
|
|
teleport_dest = "83 db 0";
|
|
else if (selection == 5) // Lycaeum
|
|
teleport_dest = "37b 1a4 0";
|
|
else if (selection == 6) // Library
|
|
teleport_dest = "37b 1b4 0";
|
|
else if (selection == 7) // Sutek's Island
|
|
teleport_dest = "316 3d4 0";
|
|
else if (selection == 8) // Stonegate
|
|
teleport_dest = "25f 11d 0";
|
|
else if (selection == 9) // The Codex
|
|
teleport_dest = "39b 354 0";
|
|
break;
|
|
case 3:
|
|
if (selection == 1) // Honesty
|
|
teleport_dest = "3a7 109 0";
|
|
else if (selection == 2) // Compassion
|
|
teleport_dest = "1f7 168 0";
|
|
else if (selection == 3) // Valor
|
|
teleport_dest = "9f 3b1 0";
|
|
else if (selection == 4) // Justice
|
|
teleport_dest = "127 28 0";
|
|
else if (selection == 5) // Sacrifice
|
|
teleport_dest = "33e a6 0";
|
|
else if (selection == 6) // Honor
|
|
teleport_dest = "147 339 0";
|
|
else if (selection == 7) // Humility
|
|
teleport_dest = "397 3a8 0";
|
|
else if (selection == 8) // Spirituality
|
|
teleport_dest = "18 16 1";
|
|
break;
|
|
case 4:
|
|
if (selection == 1) // Hall of Knowledge
|
|
teleport_dest = "7f af 5";
|
|
else if (selection == 2) // Temple of Singularity
|
|
teleport_dest = "7f 37 5";
|
|
else if (selection == 3) // Temple of Kings
|
|
teleport_dest = "7f 50 5";
|
|
else if (selection == 4) // Tomb of Kings
|
|
teleport_dest = "7f 9 4";
|
|
else if (selection == 5) // Hythloth exit
|
|
teleport_dest = "dc db 5";
|
|
else if (selection == 6) // Shrine of Control
|
|
teleport_dest = "43 2c 5";
|
|
else if (selection == 7) // Shrine of Passion
|
|
teleport_dest = "bc 2c 5";
|
|
else if (selection == 8) // Shrine of Diligence
|
|
teleport_dest = "6c dc 5";
|
|
break;
|
|
case 5:
|
|
if (selection == 1) // Ant Mound
|
|
teleport_dest = "365 bb 0";
|
|
else if (selection == 2) // Buc's Cave
|
|
teleport_dest = "234 253 0";
|
|
else if (selection == 3) // Covetous
|
|
teleport_dest = "273 73 0";
|
|
else if (selection == 4) // Crypts
|
|
teleport_dest = "364 15a 0";
|
|
else if (selection == 5) // Cyclops Cave
|
|
teleport_dest = "b9 1b5 0";
|
|
else if (selection == 6) // Deceit
|
|
teleport_dest = "3c4 136 0";
|
|
else if (selection == 7) // Despise
|
|
teleport_dest = "16D 10a 0";
|
|
else if (selection == 8) // Destard
|
|
teleport_dest = "11c 292 0";
|
|
else if (selection == 9) // Heftimus's
|
|
teleport_dest = "84 35b 0";
|
|
break;
|
|
case 6:
|
|
if (selection == 1) // Hero's Hole
|
|
teleport_dest = "15c 32a 0";
|
|
else if (selection == 2) // Hythloth
|
|
teleport_dest = "3b4 3a4 0";
|
|
else if (selection == 3) // Pirate Cave
|
|
teleport_dest = "2c3 342 0";
|
|
else if (selection == 4) // Sewers
|
|
teleport_dest = "123 17a 0";
|
|
else if (selection == 5) // Shame
|
|
teleport_dest = "eb 19b 0";
|
|
else if (selection == 6) // Spider Cave
|
|
teleport_dest = "5c fb 0";
|
|
else if (selection == 7) // Sutek's Island
|
|
teleport_dest = "316 3d4 0";
|
|
else if (selection == 8) // Swamp Cave
|
|
teleport_dest = "263 16c 0";
|
|
else if (selection == 9) // Wrong
|
|
teleport_dest = "1f4 53 0";
|
|
break;
|
|
case 7:
|
|
if (selection == 1) // Iolo's Hut
|
|
teleport_dest = "c3 e8 0";
|
|
else if (selection == 2) // Lumberjack (Yew)
|
|
teleport_dest = "b2 94 0";
|
|
else if (selection == 3) // Saw Mill (Minoc)
|
|
teleport_dest = "2a4 65 0";
|
|
else if (selection == 4) // Thieves Guild
|
|
teleport_dest = "233 25e 0";
|
|
else if (selection == 5) // Wisps
|
|
teleport_dest = "a5 115 0";
|
|
else if (selection == 6) // Dagger Island
|
|
teleport_dest = "3a9 d3 0";
|
|
else if (selection == 7) // Shipwreck
|
|
teleport_dest = "1aa 3a6 0";
|
|
else if (selection == 8) // Phoenix
|
|
teleport_dest = "76 46 3";
|
|
break;
|
|
}
|
|
} else if (game->get_game_type() == NUVIE_GAME_SE) {
|
|
// Modifications needed when collision working
|
|
// Currently NPC's end in 'bad spots' on some locations
|
|
switch (category) {
|
|
case 1:
|
|
if (selection == 1) // Barako
|
|
teleport_dest = "153 d1 0";
|
|
else if (selection == 2) // Kurak
|
|
teleport_dest = "19c 11a 0";
|
|
else if (selection == 3) // Pindiro
|
|
teleport_dest = "244 7f 0";
|
|
else if (selection == 4) // Yolaru
|
|
teleport_dest = "24b 142 0";
|
|
else if (selection == 5) // Tichticatl
|
|
teleport_dest = "242 22f 0";
|
|
else if (selection == 6) // Jukari
|
|
teleport_dest = "2ad 331 0";
|
|
else if (selection == 7) // Disquiqui
|
|
teleport_dest = "17d 228 0";
|
|
else if (selection == 8) // Barrab
|
|
teleport_dest = "f3 27a 0";
|
|
else if (selection == 9) // Urali
|
|
teleport_dest = "3e5 157 0";
|
|
break;
|
|
case 2:
|
|
if (selection == 1) // Haakur
|
|
teleport_dest = "34d 28e 0";
|
|
else if (selection == 2) // Sakkhra
|
|
teleport_dest = "6c 25e 0";
|
|
else if (selection == 3) // Old Pindiro
|
|
teleport_dest = "18e 2f 0";
|
|
break;
|
|
case 3:
|
|
if (selection == 1) // Laboratory
|
|
teleport_dest = "1db 18a 0";
|
|
else if (selection == 2) // Drum Hill
|
|
teleport_dest = "216 1c8 0";
|
|
else if (selection == 3) // Topuru's Isle
|
|
teleport_dest = "10e b9 0";
|
|
else if (selection == 4) // Gem Stand
|
|
teleport_dest = "a9 1e4 0";
|
|
else if (selection == 5) // Thunderer Peak
|
|
teleport_dest = "d8 192 0";
|
|
else if (selection == 6) // Great Mesa
|
|
teleport_dest = "c2 210 0";
|
|
else if (selection == 7) // Kotl City ??? Approx
|
|
teleport_dest = "bd 1c9 0";
|
|
else if (selection == 8) // Disq. Tyran.
|
|
teleport_dest = "1a6 249 0";
|
|
else if (selection == 9) // Silverback
|
|
teleport_dest = "110 49 0";
|
|
break;
|
|
case 4:
|
|
if (selection == 1) // Sulphur Pits
|
|
teleport_dest = "2da 2bb 0";
|
|
else if (selection == 2) // Tar Pits
|
|
teleport_dest = "1c3 150 0";
|
|
else if (selection == 3) // Pot.Nitrate
|
|
teleport_dest = "2dd 19e 0";
|
|
else if (selection == 4) // Yucca Plants
|
|
teleport_dest = "19f 115 0";
|
|
else if (selection == 5) // Bamboo
|
|
teleport_dest = "2fe 23e 0";
|
|
else if (selection == 6) // River Banks
|
|
teleport_dest = "253 5e 0";
|
|
else if (selection == 7) // Corn Stalks
|
|
teleport_dest = "18b 221 0";
|
|
else if (selection == 8) // Blue Stone
|
|
teleport_dest = "a8 259 0";
|
|
break;
|
|
case 5:
|
|
if (selection == 1) // Barako
|
|
teleport_dest = "178 ac 0";
|
|
else if (selection == 2) // Dead Pad
|
|
teleport_dest = "198 1e 0";
|
|
else if (selection == 3) // Kurak/Yolaru
|
|
teleport_dest = "216 11d 0";
|
|
else if (selection == 4) // Nahuatla
|
|
teleport_dest = "26b 259 0";
|
|
else if (selection == 5) // Jukari
|
|
teleport_dest = "2ba 306 0";
|
|
else if (selection == 6) // Disquiqui
|
|
teleport_dest = "171 25b 0";
|
|
else if (selection == 7) // Barrab
|
|
teleport_dest = "ce 26a 0";
|
|
else if (selection == 8) // Sakkhra
|
|
teleport_dest = "67 266 0";
|
|
else if (selection == 9) // Hub
|
|
teleport_dest = "b8 1c6 0";
|
|
break;
|
|
case 6:
|
|
if (selection == 1) // Spider
|
|
teleport_dest = "389 2ed 0";
|
|
else if (selection == 2) // Jukari Ritual
|
|
teleport_dest = "3a1 34d 0";
|
|
else if (selection == 3) // Silverback
|
|
teleport_dest = "123 45 0";
|
|
else if (selection == 4) // Fritz
|
|
teleport_dest = "1f1 4d 0";
|
|
else if (selection == 5) // Urali Spirit
|
|
teleport_dest = "3bc ec 0";
|
|
else if (selection == 6) // Urali Chief
|
|
teleport_dest = "3db 19e 0";
|
|
else if (selection == 7) // To Urali
|
|
teleport_dest = "2ad 176 0";
|
|
else if (selection == 8) // From Urali
|
|
teleport_dest = "335 15e 0";
|
|
else if (selection == 9) // Denys
|
|
teleport_dest = "2d5 19e 0";
|
|
break;
|
|
case 7:
|
|
if (selection == 1) // S of Disquiqui
|
|
teleport_dest = "15e 277 0";
|
|
else if (selection == 2) // W of Great Mesa
|
|
teleport_dest = "8d 1fc 0";
|
|
else if (selection == 3) // W of Hub
|
|
teleport_dest = "8d 1ca 0";
|
|
else if (selection == 4) // E of Drum Hill
|
|
teleport_dest = "27b 1d3 0";
|
|
else if (selection == 5) // SW of Kurak
|
|
teleport_dest = "173 14f 0";
|
|
else if (selection == 6) // Old Pindiro
|
|
teleport_dest = "189 45 0";
|
|
else if (selection == 7) // S of Pindiro
|
|
teleport_dest = "257 dc 0";
|
|
break;
|
|
}
|
|
}
|
|
if (strlen(teleport_dest)) {
|
|
scroll->display_string("\n(");
|
|
scroll->display_string(teleport_dest);
|
|
scroll->display_string(")\n");
|
|
alt_code_teleport(teleport_dest);
|
|
}
|
|
}
|
|
++alt_code_input_num;
|
|
}
|
|
|
|
void Events::wait() {
|
|
if (!ignore_timeleft)
|
|
g_system->delayMillis(TimeLeft());
|
|
}
|
|
|
|
//Protected
|
|
|
|
inline uint32 Events::TimeLeft() {
|
|
static uint32 next_time = 0;
|
|
uint32 now;
|
|
|
|
now = clock->get_ticks();
|
|
if (fps_counter == 60) {
|
|
fps_counter = 0;
|
|
float fps = 1000 / ((float)(now - fps_timestamp) / 60);
|
|
//printf("FPS: %f %d\n", fps, (uint32)(now - fps_timestamp));
|
|
fps_counter_widget->setFps(fps);
|
|
fps_timestamp = now;
|
|
} else
|
|
fps_counter++;
|
|
|
|
if (next_time <= now) {
|
|
next_time = now + NUVIE_INTERVAL;
|
|
return 0;
|
|
}
|
|
uint32 delay = next_time - now;
|
|
next_time += NUVIE_INTERVAL;
|
|
return delay;
|
|
}
|
|
|
|
void Events::toggleFpsDisplay() {
|
|
if (fps_counter_widget->Status() == WIDGET_VISIBLE)
|
|
fps_counter_widget->Hide();
|
|
else
|
|
fps_counter_widget->Show();
|
|
if (!game->is_new_style())
|
|
game->get_gui()->force_full_redraw();
|
|
}
|
|
|
|
void Events::quitDialog() {
|
|
GUI_Widget *quit_dialog;
|
|
if (mode == MOVE_MODE || mode == EQUIP_MODE) {
|
|
map_window->set_looking(false);
|
|
map_window->set_walking(false);
|
|
showingDialog = true;
|
|
|
|
close_gumps();
|
|
uint16 x_off = game->get_game_x_offset();
|
|
uint16 y_off = game->get_game_y_offset();
|
|
|
|
x_off += (game->get_game_width() - 170) / 2;
|
|
y_off += (game->get_game_height() - 80) / 2;
|
|
quit_dialog = new GUI_YesNoDialog(gui,
|
|
x_off,
|
|
y_off,
|
|
170,
|
|
80,
|
|
"Do you want to Quit",
|
|
this,
|
|
this);
|
|
|
|
gui->AddWidget(quit_dialog);
|
|
gui->lock_input(quit_dialog);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void Events::gameMenuDialog() {
|
|
if (mode == MOVE_MODE && !view_manager->gumps_are_active()) {
|
|
showingDialog = true;
|
|
map_window->set_looking(false);
|
|
map_window->set_walking(false);
|
|
gamemenu_dialog = new GameMenuDialog(this);
|
|
gui->AddWidget(gamemenu_dialog);
|
|
gui->lock_input(gamemenu_dialog);
|
|
keybinder->set_enable_joy_repeat(false);
|
|
} else {
|
|
cancelAction();
|
|
}
|
|
}
|
|
|
|
void Events::assetViewer() {
|
|
if (mode != MOVE_MODE || view_manager->gumps_are_active())
|
|
return;
|
|
showingDialog = true;
|
|
map_window->set_looking(false);
|
|
map_window->set_walking(false);
|
|
assetviewer_dialog = new AssetViewerDialog(this);
|
|
gui->AddWidget(assetviewer_dialog);
|
|
gui->lock_input(assetviewer_dialog);
|
|
keybinder->set_enable_joy_repeat(false);
|
|
}
|
|
|
|
uint16 Events::callback(uint16 msg, CallBack *caller, void *data) {
|
|
GUI_Widget *widget;
|
|
|
|
switch (msg) { // Handle callback from quit dialog.
|
|
case YESNODIALOG_CB_YES :
|
|
showingDialog = false;
|
|
game->get_gui()->unlock_input();
|
|
return GUI_QUIT;
|
|
case YESNODIALOG_CB_NO :
|
|
widget = (GUI_Widget *)data;
|
|
widget->Delete();
|
|
|
|
showingDialog = false;
|
|
if (gamemenu_dialog != nullptr)
|
|
gui->lock_input(gamemenu_dialog);
|
|
else
|
|
game->get_gui()->unlock_input();
|
|
return GUI_YUM;
|
|
case GAMEMENUDIALOG_CB_DELETE :
|
|
showingDialog = false;
|
|
gamemenu_dialog = nullptr;
|
|
assetviewer_dialog = nullptr;
|
|
keybinder->set_enable_joy_repeat(true);
|
|
return GUI_YUM;
|
|
}
|
|
|
|
return GUI_PASS;
|
|
}
|
|
|
|
/* Switch to solo mode.
|
|
*/
|
|
void Events::solo_mode(uint32 party_member) {
|
|
Actor *actor = player->get_party()->get_actor(party_member);
|
|
|
|
if (game->user_paused())
|
|
return;
|
|
|
|
if (!actor || player->is_in_vehicle())
|
|
return;
|
|
|
|
if (player->get_party()->is_in_combat_mode())
|
|
scroll->display_string("Not in combat mode!\n\n");
|
|
else if (player->set_solo_mode(actor)) {
|
|
scroll->display_string("Solo mode\n\n");
|
|
player->set_mapwindow_centered(true);
|
|
actor->set_worktype(0x02); // Player
|
|
if (in_control_cheat)
|
|
game->get_party()->update_light_sources();
|
|
in_control_cheat = false;
|
|
|
|
if (game->is_new_style()) {// do nothing for now
|
|
} else if (view_manager->get_current_view() == view_manager->get_inventory_view())
|
|
view_manager->get_inventory_view()->set_party_member(party_member);
|
|
else if (view_manager->get_current_view() == view_manager->get_actor_view())
|
|
view_manager->get_actor_view()->set_party_member(party_member);
|
|
}
|
|
scroll->display_prompt();
|
|
}
|
|
|
|
/* Switch to party mode. */
|
|
bool Events::party_mode() {
|
|
bool was_in_control_cheat; // go in party mode no matter what (we know are we not in combat or vehicle)
|
|
MapCoord leader_loc;
|
|
if (in_control_cheat) {
|
|
in_control_cheat = false;
|
|
was_in_control_cheat = true;
|
|
view_manager->set_party_mode();
|
|
game->get_party()->update_light_sources();
|
|
} else
|
|
was_in_control_cheat = false;
|
|
Actor *actor = player->get_party()->get_actor(0);
|
|
assert(actor); // there must be a leader
|
|
|
|
if (game->user_paused() && !was_in_control_cheat) // don't return if died in control cheat
|
|
return false;
|
|
|
|
if (player->is_in_vehicle())
|
|
return false;
|
|
|
|
bool success = false;
|
|
leader_loc = actor->get_location();
|
|
|
|
if (player->get_party()->is_in_combat_mode())
|
|
scroll->display_string("Not in combat mode!\n");
|
|
else if (player->get_party()->is_at(leader_loc, 6) || was_in_control_cheat) {
|
|
if (player->set_party_mode(player->get_party()->get_actor(0))) {
|
|
success = true;
|
|
scroll->display_string("Party mode\n");
|
|
player->set_mapwindow_centered(true);
|
|
}
|
|
} else
|
|
scroll->display_string("Not everyone is here.\n");
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
return success;
|
|
}
|
|
|
|
/* Switch to or from combat mode. */
|
|
bool Events::toggle_combat() {
|
|
Party *party = player->get_party();
|
|
bool combat_mode = !party->is_in_combat_mode();
|
|
|
|
if (!player->in_party_mode()) {
|
|
scroll->display_string("Not in solo mode.\n\n");
|
|
scroll->display_prompt();
|
|
} else if (party->is_in_vehicle()) {
|
|
display_not_aboard_vehicle();
|
|
} else if (in_control_cheat) {
|
|
scroll->display_string("\nNot while using control cheat!\n\n");
|
|
scroll->display_prompt();
|
|
} else
|
|
party->set_in_combat_mode(combat_mode);
|
|
|
|
if (party->is_in_combat_mode() == combat_mode) {
|
|
if (combat_mode)
|
|
scroll->display_string("Begin combat!\n\n");
|
|
else {
|
|
scroll->display_string("Break off combat!\n\n");
|
|
player->set_actor(party->get_leader_actor()); // return control to leader
|
|
player->set_mapwindow_centered(true); // center mapwindow
|
|
}
|
|
scroll->display_prompt();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Make actor wear an object they are holding. */
|
|
bool Events::ready(Obj *obj, Actor *actor) {
|
|
if (!actor)
|
|
actor = game->get_actor_manager()->get_actor(obj->x);
|
|
bool readied = false;
|
|
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
scroll->display_fmt_string("Ready-%s\n", obj_manager->look_obj(obj, false));
|
|
float obj_weight = obj_manager->get_obj_weight(obj, OBJ_WEIGHT_INCLUDE_CONTAINER_ITEMS,
|
|
OBJ_WEIGHT_DO_SCALE, OBJ_WEIGHT_EXCLUDE_QTY);
|
|
float equip_weight = actor->get_inventory_equip_weight() + obj_weight;
|
|
float total_weight = actor->get_inventory_weight();
|
|
|
|
if (obj->get_actor_holding_obj() != actor)
|
|
total_weight += obj_weight;
|
|
|
|
if ((actor->get_strength() < equip_weight
|
|
|| actor->get_strength() * 2 < total_weight) && !game->using_hackmove())
|
|
scroll->display_string("\nToo heavy!\n");
|
|
// perform READY usecode
|
|
else if (actor->can_ready_obj(obj) && usecode->has_readycode(obj) && (usecode->ready_obj(obj, actor) == false)) {
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
return (obj->is_readied()); // handled by usecode
|
|
} else if (obj->is_in_container() && obj->get_actor_holding_obj() != actor
|
|
&& !Game::get_game()->get_map_window()->can_get_obj(actor, obj->get_container_obj()))
|
|
scroll->display_string("\nCan't reach it\n");
|
|
else if (!(readied = actor->add_readied_object(obj))) {
|
|
if (actor->get_object_readiable_location(obj) == ACTOR_NOT_READIABLE)
|
|
scroll->display_string("\nCan't be readied!\n");
|
|
else
|
|
scroll->display_string("\nNo place to put!\n");
|
|
}
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
return readied;
|
|
}
|
|
|
|
/* Make actor hold an object they are wearing. */
|
|
bool Events::unready(Obj *obj) {
|
|
Actor *actor = game->get_actor_manager()->get_actor(obj->x);
|
|
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
scroll->display_fmt_string("Unready-%s\n", obj_manager->look_obj(obj, false));
|
|
|
|
// perform unREADY usecode
|
|
if (usecode->has_readycode(obj) && (usecode->ready_obj(obj, actor) == false)) {
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
return (!obj->is_readied()); // handled by usecode
|
|
}
|
|
|
|
actor->remove_readied_object(obj, false); // already ran usecode so don't run when unequipping
|
|
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
return true;
|
|
}
|
|
|
|
bool Events::drop_start() {
|
|
if (game->user_paused())
|
|
return false;
|
|
drop_obj = nullptr;
|
|
drop_qty = 0;
|
|
drop_x = drop_y = -1;
|
|
|
|
// get_obj_from_inventory(some actor, "Drop-");
|
|
// get_obj_from_inventory("Drop-");
|
|
get_target("Drop-");
|
|
// moveCursorToInventory(); done in newAction()
|
|
return true;
|
|
}
|
|
|
|
/* Print object name and select it as object to be dropped. If qty is 0, the
|
|
* amount to drop may be requested.
|
|
*/
|
|
bool Events::drop_select(Obj *obj, uint16 qty) {
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
drop_obj = obj;
|
|
scroll->display_string(drop_obj ? obj_manager->look_obj(drop_obj) : "nothing");
|
|
scroll->display_string("\n");
|
|
if (drop_from_key)
|
|
close_gumps();
|
|
if (drop_obj) {
|
|
if (qty == 0 && obj_manager->is_stackable(drop_obj) && drop_obj->qty > 1) {
|
|
scroll->display_string("How many? ");
|
|
// newAction(DROPCOUNT_MODE);
|
|
get_scroll_input(); // "How many?"
|
|
return true;
|
|
}
|
|
drop_count(1);
|
|
} else endAction(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Select quantity of `drop_obj' to be dropped. (qty 0 = drop nothing) */
|
|
bool Events::drop_count(uint16 qty) {
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
drop_qty = qty;
|
|
scroll->display_string("\n");
|
|
|
|
if (drop_qty != 0) {
|
|
if (drop_x == -1)
|
|
get_target("Location:");
|
|
else { // h4x0r3d by SB-X... eventually integrate MapWindow dragndrop better with this drop-action
|
|
scroll->display_string("Location:");
|
|
perform_drop(); // use already selected target: drop_x,drop_y
|
|
}
|
|
} else
|
|
endAction(true); // cancelled
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Make actor holding selected object drop it at cursor coordinates. Wait for
|
|
* drop effect to complete before ending the action.
|
|
*/
|
|
bool Events::perform_drop() {
|
|
if (game->user_paused())
|
|
return false;
|
|
if (drop_x == -1 || drop_y == -1) {
|
|
if (input.loc == nullptr) {
|
|
scroll->display_string("Not possible\n");
|
|
endAction(true);
|
|
return false;
|
|
}
|
|
|
|
if (drop_x == -1) drop_x = input.loc->x;
|
|
if (drop_y == -1) drop_y = input.loc->y;
|
|
}
|
|
|
|
return (drop(drop_obj, drop_qty, uint16(drop_x), uint16(drop_y)));
|
|
}
|
|
|
|
/* Make actor holding object drop it at x,y. */
|
|
bool Events::drop(Obj *obj, uint16 qty, uint16 x, uint16 y) {
|
|
if (game->user_paused())
|
|
return false;
|
|
|
|
bool drop_from_map = obj->get_engine_loc() == OBJ_LOC_MAP;
|
|
|
|
Actor *actor = (obj->is_in_inventory()) // includes held containers
|
|
? obj->get_actor_holding_obj()
|
|
: player->get_actor();
|
|
MapCoord actor_loc = actor->get_location();
|
|
MapCoord drop_loc(x, y, actor_loc.z);
|
|
/* not used in the original game engine
|
|
sint16 rel_x = x - actor_loc.x;
|
|
sint16 rel_y = y - actor_loc.y;
|
|
if(rel_x != 0 || rel_y != 0)
|
|
{
|
|
scroll->display_string(get_direction_name(rel_x, rel_y));
|
|
scroll->display_string(".");
|
|
}*/
|
|
CanDropOrMoveMsg can_drop;
|
|
if (!drop_from_map // already checked in map window
|
|
&& (can_drop = map_window->can_drop_or_move_obj(drop_loc.x, drop_loc.y, actor, obj)) != MSG_SUCCESS) {
|
|
// scroll->display_string("\n\nNot possible\n"); // using text from can_drop_or_move_obj
|
|
map_window->display_can_drop_or_move_msg(can_drop, "\n\n");
|
|
endAction(true); // because the DropEffect is never called to do this
|
|
return false;
|
|
}
|
|
|
|
// all object management is contained in the effect (use requested quantity)
|
|
if (!usecode->has_dropcode(obj)
|
|
|| usecode->drop_obj(obj, actor, drop_loc.x, drop_loc.y, qty ? qty : obj->qty)) {
|
|
bool interface_fullscreen = map_window->get_interface() != INTERFACE_NORMAL;
|
|
if (interface_fullscreen) {
|
|
if (qty < obj->qty && obj_manager->is_stackable(obj))
|
|
obj = obj_manager->get_obj_from_stack(obj, qty);
|
|
Obj *dest_obj = obj_manager->get_obj(drop_loc.x, drop_loc.y, drop_loc.z);
|
|
if (obj_manager->can_store_obj(dest_obj, obj))
|
|
obj_manager->moveto_container(obj, dest_obj);
|
|
else
|
|
obj_manager->moveto_map(obj, drop_loc);
|
|
} else if (drop_from_map) {
|
|
if (qty >= obj->qty || !obj_manager->is_stackable(obj))
|
|
obj_manager->remove_obj_from_map(obj); // stop ghosting from drop effect
|
|
}
|
|
|
|
if (!drop_from_map) // preserve ok to take if it was never in inventory
|
|
obj->status |= OBJ_STATUS_OK_TO_TAKE;
|
|
|
|
if (!interface_fullscreen)
|
|
new DropEffect(obj, qty ? qty : obj->qty, actor, &drop_loc);
|
|
if (drop_from_map && map_window->original_obj_loc.distance(drop_loc) > 1) // get plus drop
|
|
player->subtract_movement_points(6); // get plus drop
|
|
else if (drop_from_map) // move
|
|
player->subtract_movement_points(5);
|
|
else
|
|
game->get_script()->call_actor_subtract_movement_points(actor, 3);
|
|
scroll->message("\n\n");
|
|
endAction(false);
|
|
set_mode(MOVE_MODE);
|
|
return true;
|
|
}
|
|
// handled by usecode
|
|
endAction(true); // because the DropEffect is never called to do this
|
|
return false;
|
|
}
|
|
|
|
bool Events::rest() {
|
|
if (rest_time != 0) { // already got time & started the campfire; time to Rest
|
|
assert(last_mode == REST_MODE); // we'll need to clear Rest mode after
|
|
// exiting Wait mode
|
|
player->get_party()->rest_sleep(rest_time, rest_guard - 1);
|
|
return true;
|
|
}
|
|
scroll->display_string("Rest");
|
|
|
|
string err_str;
|
|
if (!player->get_party()->can_rest(err_str)) {
|
|
scroll->display_string(err_str);
|
|
scroll->display_string("\n");
|
|
endAction(true);
|
|
return false;
|
|
}
|
|
|
|
if (player->get_actor()->get_obj_n() == OBJ_U6_SHIP) {
|
|
scroll->display_string("\n");
|
|
player->repairShip();
|
|
endAction(true);
|
|
} else {
|
|
scroll->display_string("\nHow many hours? ");
|
|
get_scroll_input("0123456789");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Get hours to Rest, or number of party member who will guard. These must be
|
|
entered in order. */
|
|
bool Events::rest_input(uint16 input_) {
|
|
Party *party = player->get_party();
|
|
scroll->set_input_mode(false);
|
|
scroll->display_string("\n");
|
|
if (rest_time == 0) {
|
|
rest_time = input_;
|
|
if (rest_time == 0) {
|
|
endAction(true);
|
|
return false;
|
|
}
|
|
if (party->get_party_size() > 1) {
|
|
scroll->display_string("Who will guard? ");
|
|
get_target("");
|
|
get_scroll_input("0123456789", true, true);
|
|
} else {
|
|
party->rest_gather(); // nobody can guard; start now
|
|
}
|
|
} else {
|
|
rest_guard = input_;
|
|
if (rest_guard > party->get_party_size())
|
|
rest_guard = 0;
|
|
if (rest_guard == 0)
|
|
scroll->display_string("none\n");
|
|
else {
|
|
scroll->display_string(party->get_actor(rest_guard - 1)->get_name());
|
|
scroll->display_string("\n");
|
|
}
|
|
scroll->display_string("\n");
|
|
party->rest_gather();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Events::cast_spell_directly(uint8 spell_num) {
|
|
endAction(false);
|
|
newAction(SPELL_MODE);
|
|
input.type = EVENTINPUT_KEY;
|
|
input.spell_num = spell_num;
|
|
doAction();
|
|
}
|
|
|
|
/* Walk the player towards the mouse cursor. (just 1 space for now) */
|
|
void Events::walk_to_mouse_cursor(uint32 mx, uint32 my) {
|
|
// FIXME: might add generic walk_to() action to Player
|
|
// player->walk_to(uint16 x, uint16 y, uint16 move_max, uint16 timeout_seconds);
|
|
// int wx, wy;
|
|
sint16 rx, ry;
|
|
|
|
if (game->user_paused() || !player->check_walk_delay())
|
|
return;
|
|
|
|
// Mouse->World->RelativeDirection
|
|
// map_window->mouseToWorldCoords((int)mx, (int)my, wx, wy);
|
|
map_window->get_movement_direction((uint16)mx, (uint16)my, rx, ry);
|
|
player->moveRelative(rx, ry, true);
|
|
game->time_changed();
|
|
}
|
|
|
|
/* Talk to NPC, read a sign, or use an object at map coordinates.
|
|
* FIXME: should be able to handle objects from inventory
|
|
*/
|
|
void Events::multiuse(uint16 wx, uint16 wy) {
|
|
ActorManager *actor_manager = game->get_actor_manager();
|
|
Obj *obj = nullptr;
|
|
Actor *actor = nullptr, *player_actor = player->get_actor();
|
|
bool using_actor = false; //, talking = false;
|
|
MapCoord player_location(player_actor->get_location());
|
|
MapCoord target(player_actor->get_location()); // changes to target location
|
|
bool in_combat = player->get_party()->is_in_combat_mode();
|
|
|
|
if (game->user_paused() || map_window->tile_is_black(wx, wy))
|
|
return;
|
|
|
|
obj = obj_manager->get_obj(wx, wy, target.z);
|
|
actor = actor_manager->get_actor(wx, wy, target.z);
|
|
|
|
// use object or actor?
|
|
if (actor) {
|
|
if ((!actor->is_visible() && !in_combat) || (in_combat
|
|
&& (actor->get_actor_num() == player->get_actor()->get_actor_num() //don't attack yourself.
|
|
|| actor->get_alignment() == ACTOR_ALIGNMENT_GOOD))) {
|
|
Actor *a = actor_manager->get_actor(actor->get_x(), actor->get_y(), actor->get_z(), true, actor);
|
|
if (a || (!in_combat && (!actor->is_visible() // null invisible actors if not in combat and no one is found
|
|
|| (actor == player_actor && !game->is_new_style()
|
|
&& actor->get_actor_num() != 0)))) // pass if in combat if player and not showing inventory
|
|
actor = a;
|
|
}
|
|
|
|
if (actor) {
|
|
using_actor = true;
|
|
target.x = actor->get_location().x;
|
|
target.y = actor->get_location().y;
|
|
DEBUG(0, LEVEL_DEBUGGING, "Use actor at %d,%d\n", target.x, target.y);
|
|
}
|
|
}
|
|
if (obj && !using_actor) {
|
|
target.x = obj->x;
|
|
target.y = obj->y;
|
|
DEBUG(0, LEVEL_DEBUGGING, "Use object at %d,%d\n", obj->x, obj->y);
|
|
}
|
|
|
|
if (in_combat && (obj || using_actor)) {
|
|
if (!using_actor || actor->get_alignment() != ACTOR_ALIGNMENT_GOOD) {
|
|
newAction(ATTACK_MODE);
|
|
if (get_mode() == ATTACK_MODE) {
|
|
map_window->moveCursor(wx - map_window->get_cur_x(), wy - map_window->get_cur_y());
|
|
select_target(uint16(wx), uint16(wy), target.z);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (using_actor) { // use or talk to an actor
|
|
if (using_pickpocket_cheat && game->are_cheats_enabled()) {
|
|
get_inventory_obj(actor, false);
|
|
return;
|
|
}
|
|
bool can_use;
|
|
if (game->get_game_type() == NUVIE_GAME_U6 && (actor->get_actor_num() == 132 // Smith
|
|
|| actor->get_actor_num() == 130)) // Pushme Pullyu
|
|
can_use = false;
|
|
else
|
|
can_use = usecode->has_usecode(actor);
|
|
if (can_use) {
|
|
scroll->display_string("Use-", MSGSCROLL_NO_MAP_DISPLAY);
|
|
set_mode(USE_MODE);
|
|
use(actor, wx, wy);
|
|
} else {
|
|
if (game->is_new_style() && actor == actor_manager->get_player()) {
|
|
//open inventory here.
|
|
view_manager->open_doll_view(in_control_cheat ? actor : nullptr);
|
|
} else if (target == player_location)
|
|
using_actor = false;
|
|
else {
|
|
newAction(TALK_MODE);
|
|
talk(actor);
|
|
}
|
|
}
|
|
if (using_actor)
|
|
return;
|
|
}
|
|
if (!obj)
|
|
return;
|
|
else if (usecode->is_readable(obj)) {
|
|
scroll->display_string("Look-", MSGSCROLL_NO_MAP_DISPLAY);
|
|
set_mode(LOOK_MODE);
|
|
look(obj);
|
|
endAction(false); // FIXME: should be in look()
|
|
} else if (game->get_game_type() == NUVIE_GAME_U6
|
|
&& (obj->obj_n == OBJ_U6_SHRINE
|
|
|| obj->obj_n == OBJ_U6_STATUE_OF_MONDAIN
|
|
|| obj->obj_n == OBJ_U6_STATUE_OF_MINAX
|
|
|| obj->obj_n == OBJ_U6_STATUE_OF_EXODUS)) {
|
|
scroll->display_string("Talk-", MSGSCROLL_NO_MAP_DISPLAY);
|
|
set_mode(TALK_MODE);
|
|
talk(obj);
|
|
} else { // use a real object
|
|
if (newAction(USE_MODE))
|
|
select_obj(obj);
|
|
}
|
|
}
|
|
|
|
/* Do the final action for the current mode, with a selected target. */
|
|
void Events::doAction() {
|
|
if (game->user_paused())
|
|
return;
|
|
|
|
if (mode == MOVE_MODE) {
|
|
scroll->display_string("what?\n", MSGSCROLL_NO_MAP_DISPLAY);
|
|
endAction(true);
|
|
return;
|
|
}
|
|
if (mode == INPUT_MODE) { // set input to current cursor coord
|
|
if (input.get_text) {
|
|
if (last_mode == REST_MODE && rest_time != 0 && !scroll->has_input()) {
|
|
select_target(map_window->get_cursorCoord().x,
|
|
map_window->get_cursorCoord().y,
|
|
map_window->get_cursorCoord().z);
|
|
return;
|
|
}
|
|
assert(scroll->has_input()); // doAction should only be called when input is ready
|
|
assert(input.str == 0);
|
|
input.str = new string(scroll->get_input());
|
|
endAction();
|
|
doAction();
|
|
} else if (input.select_from_inventory) // some redirection here...
|
|
view_manager->get_inventory_view()->select_objAtCursor();
|
|
else
|
|
select_target(map_window->get_cursorCoord().x, map_window->get_cursorCoord().y, map_window->get_cursorCoord().z);
|
|
// the above function will switch back to the previous mode that
|
|
// started getting input, and call doAction() again, which should
|
|
// eventually result in an endAction()
|
|
return;
|
|
} else if (callback_target) { // send input elsewhere
|
|
message(CB_DATA_READY, (char *) &input);
|
|
callback_target = 0;
|
|
endAction(true);
|
|
return;
|
|
}
|
|
|
|
if (mode == LOOK_MODE) {
|
|
if (looking_at_spellbook && view_manager->get_spell_view() != nullptr) {
|
|
view_manager->get_spell_view()->close_look();
|
|
return;
|
|
}
|
|
if (input.type == EVENTINPUT_OBJECT && input.obj && (!input.obj->is_on_map()
|
|
|| (!(input.obj->status & OBJ_STATUS_INVISIBLE)
|
|
&& !map_window->tile_is_black(input.obj->x,
|
|
input.obj->y,
|
|
input.obj)))) { // look() returns false if prompt was already printed
|
|
bool prompt_in_endAction = look(input.obj);
|
|
endAction(prompt_in_endAction);
|
|
} else if (input.type == EVENTINPUT_MAPCOORD && input.actor && input.actor->is_visible()) {
|
|
bool prompt = !look(input.actor);
|
|
endAction(prompt);
|
|
} else {
|
|
lookAtCursor();
|
|
}
|
|
} else if (mode == TALK_MODE) {
|
|
if (input.type == EVENTINPUT_OBJECT)
|
|
talk(input.obj);
|
|
else if (input.type == EVENTINPUT_MAPCOORD && input.actor && input.actor->is_visible())
|
|
talk(input.actor);
|
|
else
|
|
talk_cursor();
|
|
endAction();
|
|
} else if (mode == USE_MODE) {
|
|
|
|
if (usecode) {
|
|
ScriptThread *usecode_script = usecode->get_running_script();
|
|
if (usecode_script != nullptr) {
|
|
uint8 script_state = usecode_script->get_state();
|
|
switch (script_state) {
|
|
case NUVIE_SCRIPT_GET_DIRECTION :
|
|
if (input.type == EVENTINPUT_MAPCOORD_DIR) {
|
|
usecode_script->resume_with_direction(get_direction_code(input.loc->sx, input.loc->sy));
|
|
}
|
|
break;
|
|
case NUVIE_SCRIPT_GET_OBJ :
|
|
usecode_script->resume_with_obj(input.obj);
|
|
if (!game->is_new_style()) {
|
|
view_manager->get_inventory_view()->release_focus();
|
|
sint8 leader = game->get_party()->get_leader();
|
|
if (leader >= 0) {
|
|
view_manager->get_inventory_view()->set_party_member(leader);
|
|
}
|
|
} else
|
|
view_manager->close_all_gumps();
|
|
break;
|
|
}
|
|
} else {
|
|
// if(game->is_new_style()) // don't do this it wll crash when using containers inside a gump
|
|
// view_manager->close_all_gumps();
|
|
|
|
if (input.type == EVENTINPUT_OBJECT)
|
|
use(input.obj);
|
|
else if (input.type == EVENTINPUT_MAPCOORD_DIR) {
|
|
if (input.actor && input.actor->is_visible() && usecode->has_usecode(input.actor)) {
|
|
MapCoord loc = game->get_player()->get_actor()->get_location();
|
|
use(input.actor, loc.x + input.loc->sx, loc.y + input.loc->sy);
|
|
} else
|
|
use(input.loc->sx, input.loc->sy);
|
|
} else if (input.type == EVENTINPUT_MAPCOORD) {
|
|
use(*input.loc);
|
|
} else {
|
|
scroll->display_string("what?\n");
|
|
endAction(true);
|
|
}
|
|
}
|
|
|
|
usecode_script = usecode->get_running_script();
|
|
if (usecode_script != nullptr) {
|
|
uint8 script_state = usecode_script->get_state();
|
|
switch (script_state) {
|
|
case NUVIE_SCRIPT_GET_DIRECTION :
|
|
get_direction("");
|
|
break;
|
|
case NUVIE_SCRIPT_GET_OBJ :
|
|
get_target("");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mode == USE_MODE && (usecode_script == nullptr || usecode_script->is_running() == false)) {
|
|
endAction(true);
|
|
}
|
|
}
|
|
|
|
// assert(mode != USE_MODE);
|
|
} else if (mode == GET_MODE) {
|
|
if (input.type == EVENTINPUT_OBJECT)
|
|
perform_get(input.obj);
|
|
else if (input.type == EVENTINPUT_MAPCOORD_DIR)
|
|
get(input.loc->sx, input.loc->sy);
|
|
else if (input.type == EVENTINPUT_MAPCOORD)
|
|
get(*input.loc);
|
|
else {
|
|
scroll->display_string("what?\n");
|
|
endAction(true);
|
|
}
|
|
endAction();
|
|
} else if (mode == ATTACK_MODE) {
|
|
attack();
|
|
} else if (mode == PUSH_MODE) {
|
|
assert(
|
|
input.type == EVENTINPUT_MAPCOORD_DIR || input.type == EVENTINPUT_OBJECT || input.type == EVENTINPUT_MAPCOORD);
|
|
if (input.type == EVENTINPUT_MAPCOORD_DIR) {
|
|
if (!push_obj && !push_actor)
|
|
pushFrom(input.loc->sx, input.loc->sy);
|
|
else
|
|
pushTo(input.loc->sx, input.loc->sy, PUSH_FROM_OBJECT);
|
|
} else if (input.type == EVENTINPUT_MAPCOORD && !move_in_inventory) {
|
|
if (!push_obj && !push_actor)
|
|
pushFrom(*input.loc);
|
|
else
|
|
pushTo(input.loc->x, input.loc->y);
|
|
} else {
|
|
if (!push_obj) {
|
|
move_in_inventory = true;
|
|
pushFrom(input.obj);
|
|
} else {
|
|
pushTo(input.obj, input.actor);
|
|
}
|
|
}
|
|
} else if (mode == DROP_MODE) { // called repeatedly
|
|
if (!drop_obj) {
|
|
if (input.select_from_inventory == false)
|
|
return endAction(true);
|
|
|
|
if (input.type == EVENTINPUT_MAPCOORD) {
|
|
scroll->display_string("nothing\n");
|
|
return endAction(true);
|
|
}
|
|
|
|
assert(input.type == EVENTINPUT_OBJECT);
|
|
drop_select(input.obj);
|
|
} else if (!drop_qty) {
|
|
assert(input.str);
|
|
if (input.str->empty()) {
|
|
char buf[6];
|
|
snprintf(buf, sizeof(buf), "%u", drop_obj->qty);
|
|
scroll->display_string(buf);
|
|
drop_count(drop_obj->qty);
|
|
} else
|
|
drop_count(strtol(input.str->c_str(), nullptr, 10));
|
|
} else
|
|
perform_drop();
|
|
} else if (mode == REST_MODE) {
|
|
if (rest_time != 0 && !input.str) {
|
|
sint8 party_num;
|
|
if (input.actor)
|
|
party_num = game->get_party()->get_member_num(input.actor) + 1;
|
|
else
|
|
party_num = 0;
|
|
rest_input(party_num > 0 ? party_num : 0);
|
|
return;
|
|
}
|
|
assert(input.str);
|
|
if (input.str->empty()) {
|
|
if (rest_time == 0)
|
|
scroll->display_string("0");
|
|
rest_input(0);
|
|
} else
|
|
rest_input(strtol(input.str->c_str(), nullptr, 10));
|
|
} else if (mode == CAST_MODE || mode == SPELL_MODE) {
|
|
if (input.type == EVENTINPUT_MAPCOORD) {
|
|
if (magic->is_waiting_for_location())
|
|
magic->resume(MapCoord(input.loc->x, input.loc->y, input.loc->z));
|
|
else if (magic->is_waiting_for_obj())
|
|
magic->resume(input.obj);
|
|
else {
|
|
magic->resume();
|
|
if (!game->is_new_style() && game->get_party()->get_leader() != -1)
|
|
view_manager->get_inventory_view()->set_party_member(game->get_party()->get_leader());
|
|
}
|
|
} else if (input.type == EVENTINPUT_MAPCOORD_DIR) {
|
|
magic->resume(get_direction_code(input.loc->sx, input.loc->sy));
|
|
} else if (input.type == EVENTINPUT_OBJECT) {
|
|
magic->resume(input.obj);
|
|
if (!game->is_new_style() && game->get_party()->get_leader() != -1) {
|
|
view_manager->get_inventory_view()->release_focus();
|
|
view_manager->get_inventory_view()->set_party_member(game->get_party()->get_leader());
|
|
} else
|
|
view_manager->get_inventory_view()->Hide();
|
|
} else if (input.type == EVENTINPUT_SPELL_NUM) {
|
|
if (input.spell_num != -1)
|
|
magic->resume_with_spell_num(input.spell_num);
|
|
else
|
|
magic->resume();
|
|
} else {
|
|
if (mode == CAST_MODE)
|
|
magic->cast();
|
|
else
|
|
magic->cast_spell_directly(input.spell_num);
|
|
}
|
|
|
|
for (; magic->is_waiting_to_talk();) {
|
|
talk(magic->get_actor_from_script());
|
|
magic->resume();
|
|
}
|
|
|
|
if (magic->is_waiting_for_location() || magic->is_waiting_for_obj())
|
|
get_target("");
|
|
else if (magic->is_waiting_for_direction())
|
|
get_direction("");
|
|
else if (magic->is_waiting_for_inventory_obj()) {
|
|
get_inventory_obj(magic->get_actor_from_script());
|
|
} else if (magic->is_waiting_for_spell()) {
|
|
get_spell_num(player->get_actor(), magic->get_spellbook_obj());
|
|
gui->lock_input(view_manager->get_spell_view());
|
|
} else {
|
|
endAction(true);
|
|
}
|
|
} else if (mode == MULTIUSE_MODE) {
|
|
if (input.loc) { // on map
|
|
set_mode(MOVE_MODE);
|
|
multiuse(input.loc->sx, input.loc->sy);
|
|
} else { // tryed on views/gumps
|
|
Obj *obj = input.obj; // newAction(USE_MODE) will nullptr input.obj
|
|
if (!obj) { // not sure if this is needed
|
|
set_mode(MOVE_MODE);
|
|
return;
|
|
}
|
|
|
|
if (usecode->is_readable(obj)) { // look at a scroll or book
|
|
set_mode(LOOK_MODE);
|
|
look(obj);
|
|
endAction(false); // FIXME: should be in look()
|
|
return;
|
|
}
|
|
set_mode(USE_MODE);
|
|
use(obj);
|
|
}
|
|
} else if (cursor_mode) {
|
|
MapCoord loc = map_window->get_cursorCoord(); // need to preserve locations if a target is needed
|
|
uint16 cursor_x = loc.x - map_window->get_cur_x();
|
|
uint16 cursor_y = loc.y - map_window->get_cur_y();
|
|
|
|
if (!game->get_command_bar()->try_selected_action(-1)) { // no input needed
|
|
map_window->set_show_cursor(false);
|
|
return;
|
|
}
|
|
map_window->moveCursor(cursor_x, cursor_y);
|
|
select_target(loc.x, loc.y, loc.z); // the returned location
|
|
} else if (mode == SCRIPT_MODE) {
|
|
if (scriptThread != nullptr) {
|
|
uint8 script_state = scriptThread->get_state();
|
|
switch (script_state) {
|
|
case NUVIE_SCRIPT_GET_DIRECTION :
|
|
if (input.type == EVENTINPUT_MAPCOORD_DIR) {
|
|
scriptThread->resume_with_direction(get_direction_code(input.loc->sx, input.loc->sy));
|
|
}
|
|
break;
|
|
case NUVIE_SCRIPT_GET_TARGET :
|
|
case NUVIE_SCRIPT_GET_OBJ :
|
|
if (input.type == EVENTINPUT_MAPCOORD) {
|
|
scriptThread->resume_with_location(MapCoord(input.loc->x, input.loc->y, input.loc->z));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
script_state = scriptThread->get_state();
|
|
switch (script_state) {
|
|
case NUVIE_SCRIPT_GET_DIRECTION :
|
|
get_direction("");
|
|
break;
|
|
case NUVIE_SCRIPT_GET_TARGET :
|
|
get_target("");
|
|
break;
|
|
|
|
case NUVIE_SCRIPT_FINISHED:
|
|
delete scriptThread;
|
|
scriptThread = nullptr;
|
|
endAction(true);
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
cancelAction();
|
|
}
|
|
|
|
/* Cancel the action for the current mode, switch back to MOVE_MODE if possible. */
|
|
void Events::cancelAction() {
|
|
if (game->user_paused())
|
|
return;
|
|
if (view_manager->gumps_are_active() && (magic == nullptr || !magic->is_waiting_for_inventory_obj()))
|
|
return close_gumps();
|
|
if (mode == INPUT_MODE) { // cancel action of previous mode
|
|
if (magic != nullptr && magic->is_waiting_for_inventory_obj()) {
|
|
if (!game->is_new_style() && game->get_party()->get_leader() != -1) {
|
|
view_manager->get_inventory_view()->release_focus();
|
|
view_manager->get_inventory_view()->set_party_member(game->get_party()->get_leader());
|
|
} else
|
|
view_manager->get_inventory_view()->Hide();
|
|
} else {
|
|
if (usecode) {
|
|
if (usecode->is_script_running()) {
|
|
if (!game->is_new_style()
|
|
&& game->get_party()->get_leader() != -1) { //FIXME consolidate this logic with magic script logic above
|
|
view_manager->get_inventory_view()->release_focus();
|
|
view_manager->get_inventory_view()->set_party_member(game->get_party()->get_leader());
|
|
}
|
|
// else
|
|
// view_manager->close_all_gumps();
|
|
}
|
|
}
|
|
if (last_mode == PUSH_MODE) {
|
|
if (push_obj || push_actor) {
|
|
if (move_in_inventory)
|
|
scroll->display_string("nobody.\n");
|
|
else
|
|
scroll->display_string("nowhere.\n");
|
|
endAction();
|
|
endAction(true);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
endAction();
|
|
cancelAction();
|
|
return;
|
|
}
|
|
|
|
if (mode == MOVE_MODE) {
|
|
player->pass();
|
|
} else if (mode == CAST_MODE) {
|
|
if (magic->is_waiting_to_resume())
|
|
magic->resume();
|
|
else {
|
|
|
|
scroll->display_string("nothing\n");
|
|
}
|
|
view_manager->close_spell_mode();
|
|
} else if (mode == USE_MODE) {
|
|
if (usecode->is_script_running()) {
|
|
usecode->get_running_script()->resume_with_nil();
|
|
}
|
|
|
|
if (callback_target) {
|
|
message(CB_INPUT_CANCELED, (char *) &input);
|
|
callback_target = nullptr;
|
|
callback_user_data = nullptr;
|
|
}
|
|
} else if (mode == EQUIP_MODE) {
|
|
endAction();
|
|
return;
|
|
} else if (looking_at_spellbook && view_manager->get_spell_view() != nullptr) {
|
|
view_manager->get_spell_view()->close_look();
|
|
return;
|
|
} else {
|
|
scroll->display_string("what?\n");
|
|
if (mode == ATTACK_MODE) {
|
|
player->subtract_movement_points(10);
|
|
game->get_actor_manager()->startActors(); // end player turn
|
|
endAction();
|
|
return;
|
|
}
|
|
}
|
|
|
|
endAction(true);
|
|
}
|
|
|
|
/* Request new EventMode, for selecting a target.
|
|
* Returns true the mode is changed. (basically if a new "select an
|
|
* object/direction for this action" prompt is displayed)
|
|
*/
|
|
bool Events::newAction(EventMode new_mode) {
|
|
map_window->set_looking(false);
|
|
map_window->set_walking(false);
|
|
|
|
if (game->user_paused())
|
|
return false;
|
|
cursor_mode = false;
|
|
// FIXME: make ATTACK_MODE use INPUT_MODE
|
|
if (mode == ATTACK_MODE && new_mode == ATTACK_MODE) {
|
|
close_gumps();
|
|
doAction();
|
|
return (mode == ATTACK_MODE);
|
|
}
|
|
if (looking_at_spellbook && view_manager->get_spell_view() != nullptr) { // pushed L while looking at spell book
|
|
view_manager->get_spell_view()->close_look();
|
|
return false;
|
|
}
|
|
// since INPUT_MODE must be set to get input, it wouldn't make sense that
|
|
// a mode would be requested again to complete the action
|
|
assert(mode != new_mode);
|
|
|
|
CommandBar *commandbar = game->get_command_bar();
|
|
if (commandbar)
|
|
commandbar->on_new_action(new_mode);
|
|
|
|
// called again (same key pressed twice); equivalent of pressing ENTER so call doAction() to set input
|
|
if (mode == INPUT_MODE && new_mode == last_mode) {
|
|
doAction();
|
|
return (!(mode == MOVE_MODE));
|
|
} else if (mode != MOVE_MODE && mode != EQUIP_MODE) { // already in another mode; exit
|
|
cancelAction();
|
|
return false;
|
|
}
|
|
move_in_inventory = false;
|
|
|
|
set_mode(new_mode);
|
|
if (new_mode != COMBAT_MODE)
|
|
game->set_mouse_pointer(1);
|
|
switch (new_mode) {
|
|
case CAST_MODE:
|
|
/* TODO check if spellbook ready before changing mode */
|
|
scroll->display_string("Cast-");
|
|
if (!magic->start_new_spell()) {
|
|
mode = MOVE_MODE;
|
|
scroll->display_prompt();
|
|
} else
|
|
key_redirect((CallBack *) magic, nullptr);
|
|
break;
|
|
case SPELL_MODE:
|
|
break;
|
|
case LOOK_MODE:
|
|
look_start();
|
|
break;
|
|
case TALK_MODE:
|
|
talk_start();
|
|
break;
|
|
case USE_MODE:
|
|
use_start();
|
|
break;
|
|
case GET_MODE:
|
|
get_start();
|
|
break;
|
|
case MULTIUSE_MODE:
|
|
get_target("");
|
|
if (game->get_party()->is_in_combat_mode())
|
|
player->attack_select_init(false);
|
|
break;
|
|
case ATTACK_MODE:
|
|
close_gumps();
|
|
if (game->get_game_type() == NUVIE_GAME_U6
|
|
&& player->is_in_vehicle()
|
|
&& player->get_actor()->get_obj_n() != OBJ_U6_SHIP) {
|
|
scroll->display_string("Attack-");
|
|
display_not_aboard_vehicle(false);
|
|
endAction(true);
|
|
return false;
|
|
}
|
|
if (game->get_game_type() != NUVIE_GAME_U6) {
|
|
scriptThread = game->get_script()->call_function_in_thread("player_attack");
|
|
mode = SCRIPT_MODE;
|
|
scriptThread->start();
|
|
switch (scriptThread->get_state()) {
|
|
case NUVIE_SCRIPT_GET_TARGET:
|
|
get_target("");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
player->attack_select_init();
|
|
map_window->set_show_cursor(true);
|
|
break;
|
|
case PUSH_MODE:
|
|
push_start();
|
|
break;
|
|
case DROP_MODE:
|
|
drop_start();
|
|
// fall through
|
|
case EQUIP_MODE: // if this was called from moveCursorToInventory, the
|
|
// mode has now changed, so it won't be called again
|
|
moveCursorToInventory();
|
|
break;
|
|
// case DROPCOUNT_MODE:
|
|
// get_scroll_input(); /* "How many?" */
|
|
// break;
|
|
case REST_MODE:
|
|
rest_time = rest_guard = 0;
|
|
rest();
|
|
break;
|
|
case COMBAT_MODE:
|
|
toggle_combat();
|
|
mode = MOVE_MODE;
|
|
break;
|
|
default:
|
|
cancelAction(); // "what?"
|
|
return false;
|
|
}
|
|
return true; // ready for object/direction
|
|
}
|
|
|
|
/* Revert to default MOVE_MODE. (walking)
|
|
* This clears visible cursors, and resets all variables used by actions.
|
|
*/
|
|
void Events::endAction(bool prompt) {
|
|
if (mode == KEYINPUT_MODE)
|
|
// Leaving KEYINPUT_MODE: restore keymapper state.
|
|
g_system->getEventManager()->getKeymapper()->setEnabled(_keymapperStateBeforeKEYINPUT);
|
|
|
|
// Finished selecting a spell for enchant or looking at spellbook: undo spellbook input locking.
|
|
if (mode == CAST_MODE || (mode == LOOK_MODE && !is_looking_at_spellbook())) {
|
|
const GUI_Widget *const lockedWidget = gui->get_locked_widget();
|
|
if (lockedWidget && lockedWidget == view_manager->get_spell_view())
|
|
gui->unlock_input();
|
|
}
|
|
|
|
if (prompt) {
|
|
scroll->display_string("\n");
|
|
scroll->display_prompt();
|
|
}
|
|
|
|
if (mode == PUSH_MODE) {
|
|
push_obj = nullptr;
|
|
push_actor = nullptr;
|
|
map_window->reset_mousecenter();
|
|
} else if (mode == DROP_MODE) {
|
|
drop_obj = nullptr;
|
|
drop_qty = 0;
|
|
drop_from_key = false;
|
|
} else if (mode == REST_MODE) {
|
|
rest_time = rest_guard = 0;
|
|
scroll->set_using_target_cursor(false);
|
|
}
|
|
if (cursor_mode || mode == EQUIP_MODE) {
|
|
cursor_mode = false;
|
|
map_window->set_show_cursor(false);
|
|
}
|
|
if (mode == ATTACK_MODE) { // FIXME: make ATTACK_MODE use INPUT_MODE
|
|
map_window->set_show_cursor(false);
|
|
}
|
|
|
|
// Clear any switches in MD
|
|
if (game->get_command_bar())
|
|
game->get_command_bar()->on_new_action(MOVE_MODE);
|
|
|
|
// Revert to the previous mode, instead of MOVE_MODE.
|
|
/* Switching from INPUT_MODE, clear state indicating the type of input
|
|
to return, but leave returned input. Clear returned input only when
|
|
entering INPUT_MODE, or deleting Events. */
|
|
if (/*game->user_paused() ||*/ mode == INPUT_MODE || mode == KEYINPUT_MODE) {
|
|
mode = last_mode;
|
|
// callback_target = 0;
|
|
input.get_text = false;
|
|
// input.select_from_inventory = false; // indicates cursor location
|
|
input.get_direction = false;
|
|
do_not_show_target_cursor = false;
|
|
map_window->set_show_use_cursor(false);
|
|
map_window->set_show_cursor(false);
|
|
if (!game->is_new_style())
|
|
view_manager->get_inventory_view()->set_show_cursor(false);
|
|
// game->set_mouse_pointer(0);
|
|
return;
|
|
} else if (!looking_at_spellbook)
|
|
set_mode(MOVE_MODE);
|
|
|
|
map_window->updateBlacking();
|
|
}
|
|
// save current mode if switching to WAIT_MODE or INPUT_MODE
|
|
void Events::set_mode(EventMode new_mode) {
|
|
DEBUG(0,
|
|
LEVEL_DEBUGGING,
|
|
"new mode = %s, mode = %s, last mode = %s\n",
|
|
print_mode(new_mode),
|
|
print_mode(mode),
|
|
print_mode(last_mode));
|
|
|
|
Common::Keymapper *const keymapper = g_system->getEventManager()->getKeymapper();
|
|
if (mode == KEYINPUT_MODE)
|
|
// Switching away from KEYINPUT_MODE: restore keymapper state.
|
|
keymapper->setEnabled(_keymapperStateBeforeKEYINPUT);
|
|
|
|
if (new_mode == KEYINPUT_MODE) {
|
|
// Switching to KEYINPUT_MODE: save keymapper state and disable.
|
|
_keymapperStateBeforeKEYINPUT = keymapper->isEnabled();
|
|
keymapper->setEnabled(false);
|
|
}
|
|
|
|
if (new_mode == WAIT_MODE && (last_mode == EQUIP_MODE || last_mode == REST_MODE))
|
|
last_mode = mode;
|
|
else if ((new_mode == INPUT_MODE || new_mode == KEYINPUT_MODE))
|
|
last_mode = mode;
|
|
else
|
|
last_mode = MOVE_MODE;
|
|
mode = new_mode;
|
|
|
|
// re-init input state
|
|
if (mode == INPUT_MODE || mode == KEYINPUT_MODE) {
|
|
if (input.target_init) delete input.target_init;
|
|
if (input.str) delete input.str;
|
|
if (input.loc) delete input.loc;
|
|
input.target_init = 0;
|
|
input.str = 0;
|
|
input.loc = 0;
|
|
input.actor = 0;
|
|
input.obj = 0;
|
|
}
|
|
}
|
|
|
|
void Events::moveCursorToInventory() {
|
|
if (push_actor)
|
|
return;
|
|
cursor_mode = false;
|
|
if (mode == MOVE_MODE)
|
|
newAction(EQUIP_MODE);
|
|
else {
|
|
map_window->set_show_cursor(false); // hide both MapWindow cursors
|
|
map_window->set_show_use_cursor(false);
|
|
if (!game->is_new_style()) {
|
|
view_manager->get_inventory_view()->set_show_cursor(true);
|
|
view_manager->get_inventory_view()->grab_focus(); // Inventory wants keyboard input
|
|
} else {
|
|
//view_manager->open_container_view(player->get_actor());
|
|
}
|
|
}
|
|
input.select_from_inventory = true;
|
|
}
|
|
|
|
// Note that the cursor is not recentered here.
|
|
void Events::moveCursorToMapWindow(bool ToggleCursor) {
|
|
input.select_from_inventory = false;
|
|
if (!game->is_new_style()) {
|
|
view_manager->get_inventory_view()->set_show_cursor(false);
|
|
view_manager->get_inventory_view()->release_focus();
|
|
} else {
|
|
//Removed due to delete issues while dragging. view_manager->close_container_view(player->get_actor());
|
|
}
|
|
if (input.get_direction) // show the correct MapWindow cursor
|
|
map_window->set_show_use_cursor(true);
|
|
else if (ToggleCursor && mode == EQUIP_MODE) {
|
|
if (game->get_command_bar()->get_selected_action() == -1)
|
|
mode = MOVE_MODE;
|
|
else {
|
|
cursor_mode = true;
|
|
map_window->centerCursor();
|
|
map_window->set_show_cursor(true);
|
|
}
|
|
} else
|
|
map_window->set_show_cursor(true);
|
|
|
|
// map_window->grab_focus(); FIXME add move() and keyhandler to MapWindow, and uncomment this
|
|
}
|
|
|
|
static const char eventModeStrings[][17] = {
|
|
"LOOK_MODE",
|
|
"USE_MODE",
|
|
"GET_MODE",
|
|
"MOVE_MODE",
|
|
"DROP_MODE",
|
|
"TALK_MODE", /* finding an actor to talk to */
|
|
"ATTACK_MODE",
|
|
"PUSH_MODE",
|
|
"REST_MODE",
|
|
"CAST_MODE",
|
|
"COMBAT_MODE", /* only used to cancel previous actions */
|
|
"SPELL_MODE", //direct spell casting without spell select etc.
|
|
"EQUIP_MODE",
|
|
"WAIT_MODE", /* waiting for something, optionally display prompt when finished */
|
|
"INPUT_MODE",
|
|
"MULTIUSE_MODE",
|
|
"KEYINPUT_MODE",
|
|
"SCRIPT_MODE"
|
|
};
|
|
|
|
const char *Events::print_mode(EventMode mode_) {
|
|
return eventModeStrings[mode_];
|
|
}
|
|
|
|
bool Events::can_target_icon() {
|
|
if (mode == INPUT_MODE && (last_mode == TALK_MODE
|
|
|| last_mode == CAST_MODE || last_mode == SPELL_MODE
|
|
|| last_mode == LOOK_MODE || move_in_inventory
|
|
|| last_mode == USE_MODE || last_mode == REST_MODE))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void Events::display_not_aboard_vehicle(bool show_prompt) {
|
|
if (player->get_actor()->get_obj_n() == OBJ_U6_INFLATED_BALLOON)
|
|
scroll->display_string("Not while aboard balloon!\n\n");
|
|
else
|
|
scroll->display_string("Not while aboard ship!\n\n");
|
|
if (show_prompt)
|
|
scroll->display_prompt();
|
|
}
|
|
|
|
bool Events::can_move_obj_between_actors(Obj *obj,
|
|
Actor *src_actor,
|
|
Actor *target_actor,
|
|
bool display_name) { // exchange inventory
|
|
MapCoord from = src_actor->get_location();
|
|
|
|
if (target_actor) {
|
|
if (display_name) {
|
|
scroll->display_string(target_actor == src_actor ? "yourself" : target_actor->get_name());
|
|
scroll->display_string(".");
|
|
}
|
|
|
|
if (!target_actor->is_in_party() && target_actor != player->get_actor()) {
|
|
scroll->display_string("\n\nOnly within the party!");
|
|
return false;
|
|
}
|
|
|
|
if (game->using_hackmove())
|
|
return true;
|
|
if (player->is_in_vehicle()) {
|
|
display_not_aboard_vehicle();
|
|
return false;
|
|
}
|
|
|
|
if (target_actor == src_actor && obj->is_in_inventory())
|
|
return true;
|
|
|
|
MapCoord to = target_actor->get_location();
|
|
|
|
if (!map_window->tile_is_black(from.x, from.y)
|
|
&& !map_window->tile_is_black(to.x, to.y)) {
|
|
if (from.distance(to) < 5 || (map_window->get_interface() != INTERFACE_NORMAL
|
|
&& target_actor->is_onscreen() && src_actor->is_onscreen())) {
|
|
if (game->get_script()->call_actor_get_obj(target_actor, obj))
|
|
return true;
|
|
} else
|
|
scroll->display_string("\n\nOut of range!");
|
|
} else
|
|
scroll->display_string("\n\nBlocked!"); // original said Out of Range!
|
|
} else
|
|
scroll->display_string("\n\nnobody.");
|
|
|
|
return false;
|
|
}
|
|
|
|
void Events::display_move_text(Actor *target_actor, Obj *obj) {
|
|
scroll->display_string("Move-");
|
|
scroll->display_string(obj_manager->look_obj(obj, OBJ_SHOW_PREFIX));
|
|
if (game->get_game_type() == NUVIE_GAME_MD)
|
|
scroll->display_string("\nWhere? ");
|
|
else
|
|
scroll->display_string(" To ");
|
|
scroll->display_string(target_actor->get_name());
|
|
scroll->display_string(".");
|
|
}
|
|
|
|
bool Events::can_get_to_actor(const Actor *actor, uint16 x, uint16 y) { // need the exact tile
|
|
if (map_window->get_interface() == INTERFACE_IGNORE_BLOCK
|
|
|| player->get_actor() == actor)
|
|
return true;
|
|
|
|
LineTestResult lt;
|
|
Map *map = game->get_game_map();
|
|
MapCoord player_loc = player->get_actor()->get_location();
|
|
|
|
// FIXME: false obj matches can occur (should be extremely rare)
|
|
if (map->lineTest(player_loc.x, player_loc.y, x, y, player_loc.z, LT_HitUnpassable, lt)
|
|
&& (!lt.hitObj || lt.hitObj->quality != actor->get_actor_num())) // actor part
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Events::select_view_obj(Obj *obj, Actor *actor) {
|
|
if ((last_mode == CAST_MODE || last_mode == SPELL_MODE)
|
|
&& !magic->is_waiting_for_obj() && !magic->is_waiting_for_inventory_obj())
|
|
cancelAction();
|
|
else {
|
|
if (!obj || push_actor != nullptr)
|
|
return false;
|
|
if (usecode->cannot_unready(obj) && ((last_mode == DROP_MODE && drop_obj == nullptr)
|
|
|| (last_mode == PUSH_MODE && push_obj == nullptr))) {
|
|
scroll->display_string(obj_manager->look_obj(obj, false));
|
|
scroll->display_string("\n");
|
|
usecode->ready_obj(obj, obj->get_actor_holding_obj());
|
|
endAction(true);
|
|
set_mode(MOVE_MODE);
|
|
} else
|
|
select_obj(obj, actor);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Events::close_gumps() {
|
|
view_manager->close_all_gumps();
|
|
}
|
|
|
|
bool Events::dont_show_target_cursor() const {
|
|
if (do_not_show_target_cursor || push_actor)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool Events::input_really_needs_directon() const {
|
|
if ((input.get_direction && (map_window->get_interface() == INTERFACE_NORMAL || last_mode == CAST_MODE)) ||
|
|
dont_show_target_cursor())
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void Events::toggleAltCodeMode(bool enable) {
|
|
if (!enable && altCodeVal != 0)
|
|
alt_code(altCodeVal); // leaving alt-code mode: evaluate it
|
|
// a code was either just handled or we newly entered alt-code mode: reset it
|
|
clear_alt_code();
|
|
}
|
|
|
|
void Events::appendAltCode(int code) {
|
|
altCodeVal *= 10;
|
|
altCodeVal += code;
|
|
}
|
|
|
|
bool shouldQuit() {
|
|
return g_engine->shouldQuit();
|
|
}
|
|
|
|
} // End of namespace Nuvie
|
|
} // End of namespace Ultima
|