diff options
Diffstat (limited to 'crawl-ref/source/state.cc')
-rw-r--r-- | crawl-ref/source/state.cc | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/crawl-ref/source/state.cc b/crawl-ref/source/state.cc new file mode 100644 index 0000000000..1d86dc1862 --- /dev/null +++ b/crawl-ref/source/state.cc @@ -0,0 +1,367 @@ +/* + * File: state.cc + * Summary: Game state functions. + * Written by: Matthew Cline + * + * Modified for Crawl Reference by $Author$ on $Date$ + * + * Change History (most recent first): + * + * <1> 09/18/07 MPC Created + */ + +#include "AppHdr.h" +#include "externs.h" + +#include "delay.h" +#include "direct.h" +#include "macro.h" +#include "menu.h" // For print_formatted_paragraph() +#include "message.h" +#include "mon-util.h" +#include "player.h" +#include "state.h" +#include "tutorial.h" +#include "view.h" + +game_state::game_state() + : mouse_enabled(false), waiting_for_command(false), + terminal_resized(false), io_inited(false), need_save(false), + saving_game(false), updating_scores(false), seen_hups(0), + map_stat_gen(false), unicode_ok(false), glyph2strfn(NULL), + multibyte_strlen(NULL), terminal_resize_handler(NULL), + terminal_resize_check(NULL), doing_prev_cmd_again(false), + prev_cmd(CMD_NO_CMD), repeat_cmd(CMD_NO_CMD), cmd_repeat_count(0), + cmd_repeat_goal(0), prev_repetition_turn(0), + cmd_repeat_started_unsafe(false), input_line_curr(0), + level_annotation_shown(false) +{ + reset_cmd_repeat(); + reset_cmd_again(); +} + +void game_state::add_startup_error(const std::string &err) +{ + startup_errors.push_back(err); +} + +void game_state::show_startup_errors() +{ + formatted_scroller error_menu; + error_menu.set_flags(MF_NOSELECT | MF_ALWAYS_SHOW_MORE | MF_NOWRAP + | MF_EASY_EXIT); + error_menu.set_more( + formatted_string::parse_string( + "<cyan>[ + : Page down. - : Page up." + " Esc or Enter to continue.]")); + error_menu.set_title( + new MenuEntry("Warning: Crawl encountered errors during startup:", + MEL_TITLE)); + for (int i = 0, size = startup_errors.size(); i < size; ++i) + error_menu.add_entry(new MenuEntry(startup_errors[i])); + error_menu.show(); +} + +/////////////////////////////////////////////////////////////////////////// +// Repeating commands and doing the previous command over again. + +bool game_state::is_replaying_keys() const +{ + return (crawl_state.doing_prev_cmd_again + || (crawl_state.is_repeating_cmd() + && !crawl_state.cmd_repeat_start)); +} + +bool game_state::is_repeating_cmd() const +{ + ASSERT((cmd_repeat_goal == 0 && cmd_repeat_count == 0 + && repeat_cmd == CMD_NO_CMD && !cmd_repeat_start) + || (cmd_repeat_goal > 0 && cmd_repeat_count <= cmd_repeat_goal + && repeat_cmd != CMD_NO_CMD)); + + return (repeat_cmd != CMD_NO_CMD); +} + +void game_state::cancel_cmd_repeat(std::string reason) +{ + if (!is_repeating_cmd()) + return; + + if (is_replaying_keys() || cmd_repeat_start) + flush_input_buffer(FLUSH_KEY_REPLAY_CANCEL); + + if (is_processing_macro()) + flush_input_buffer(FLUSH_ABORT_MACRO); + + reset_cmd_repeat(); + + if (reason != "") + mpr(reason.c_str()); +} + +void game_state::cancel_cmd_again(std::string reason) +{ + if (!doing_prev_cmd_again) + return; + + flush_input_buffer(FLUSH_KEY_REPLAY_CANCEL); + + if (is_processing_macro()) + flush_input_buffer(FLUSH_ABORT_MACRO); + + reset_cmd_again(); + + if (reason != "") + mpr(reason.c_str()); +} + +void game_state::cant_cmd_repeat(std::string reason) +{ + if (reason == "") + reason = "Can't repeat that command."; + + cancel_cmd_repeat(reason); +} + +void game_state::cant_cmd_again(std::string reason) +{ + if (reason == "") + reason = "Can't redo that command."; + + cancel_cmd_again(reason); +} + +// The method is called to prevent the "no repeating zero turns +// commands" message that input() generates (in the absence of +// cancelling the repeition) for a repeated command that took no +// turns. A wrapper around cancel_cmd_repeat(), its only purpose it +// to make it clear why cancel_cmd_repeat() is being called. +void game_state::zero_turns_taken() +{ + ASSERT(!you.turn_is_over); + cancel_cmd_repeat(); +} + +bool interrupt_cmd_repeat( activity_interrupt_type ai, + const activity_interrupt_data &at ) +{ + if (crawl_state.cmd_repeat_start) + return false; + + if (crawl_state.repeat_cmd == CMD_WIZARD) + return false; + + switch (ai) + { + case AI_STATUE: + case AI_HUNGRY: + case AI_TELEPORT: + case AI_FORCE_INTERRUPT: + case AI_HP_LOSS: + case AI_MONSTER_ATTACKS: + crawl_state.cancel_cmd_repeat("Command repetition interrupted."); + return true; + + default: + break; + } + + if (ai == AI_SEE_MONSTER) + { + const monsters* mon = static_cast<const monsters*>(at.data); + if (!mon->visible()) + return false; + + if (crawl_state.cmd_repeat_started_unsafe + && at.context != "newly seen") + { + return false; + } + + crawl_state.cancel_cmd_repeat(); + +#ifndef DEBUG_DIAGNOSTICS + if (at.context == "newly seen") + { + std::string text = get_monster_desc(mon, false); + text += " comes into view."; + print_formatted_paragraph(text, get_number_of_cols(), MSGCH_WARN); + } + + if (Options.tutorial_left) + { + // enforce that this message comes first + tutorial_first_monster(*mon); + if (get_mons_colour(mon) != mon->colour) + learned_something_new(TUT_MONSTER_BRAND); + } +#else + formatted_string fs( channel_to_colour(MSGCH_WARN) ); + fs.cprintf("%s (", mon->name(DESC_PLAIN, true).c_str()); + fs.add_glyph( mon ); + fs.cprintf(") in view: (%d,%d), see_grid: %s", + mon->x, mon->y, + see_grid(mon->x, mon->y)? "yes" : "no"); + formatted_mpr(fs, MSGCH_WARN); +#endif + + return true; + } + + // If command repitition is being used to immitate the rest command, + // then everything interrupts it. + if (crawl_state.repeat_cmd == CMD_MOVE_NOWHERE + || crawl_state.repeat_cmd == CMD_SEARCH) + { + if (ai == AI_FULL_MP) + crawl_state.cancel_cmd_repeat("Magic restored."); + else if (ai == AI_FULL_HP) + crawl_state.cancel_cmd_repeat("HP restored."); + else + crawl_state.cancel_cmd_repeat("Command repetition interrupted."); + return true; + } + + if (crawl_state.cmd_repeat_started_unsafe) + return false; + + if (ai == AI_HIT_MONSTER) + { + // This check is for when command repetition is used to + // whack away at a 0xp monster, since the player feels safe + // when the only monsters around are 0xp. + const monsters* mon = static_cast<const monsters*>(at.data); + + if (mons_class_flag(mon->type, M_NO_EXP_GAIN) + && player_monster_visible(mon)) + { + return false; + } + + crawl_state.cancel_cmd_repeat("Command repetition interrupted."); + return true; + } + + return false; +} + +void game_state::reset_cmd_repeat() +{ + repeat_cmd = CMD_NO_CMD; + cmd_repeat_count = 0; + cmd_repeat_goal = 0; + cmd_repeat_start = false; + prev_repetition_turn = 0; + + repeat_cmd_keys.clear(); +} + +void game_state::reset_cmd_again() +{ + doing_prev_cmd_again = false; + prev_cmd = CMD_NO_CMD; + + prev_cmd_keys.clear(); +} + +/////////////////////////////////////////////////////////// +// Keeping track of which god is currently doing something +/////////////////////////////////////////////////////////// + +god_act_state::god_act_state() +{ + reset(); +} + +void god_act_state::reset() +{ + which_god = GOD_NO_GOD; + retribution = false; + depth = 0; +} + +bool game_state::is_god_acting() const +{ + ASSERT(god_act.depth >= 0); + ASSERT(!(god_act.depth > 0 && god_act.which_god == GOD_NO_GOD)); + ASSERT(!(god_act.depth == 0 && god_act.which_god != GOD_NO_GOD)); + ASSERT(!(god_act.depth == 0 && god_act_stack.size() > 0)); + + return (god_act.depth > 0); +} + +bool game_state::is_god_retribution() const +{ + ASSERT(is_god_acting()); + + return (god_act.retribution); +} + +god_type game_state::which_god_acting() const +{ + return god_act.which_god; +} + +void game_state::inc_god_acting(bool is_retribution) +{ + inc_god_acting(you.religion, is_retribution); +} + +void game_state::inc_god_acting(god_type which_god, bool is_retribution) +{ + ASSERT(which_god != GOD_NO_GOD); + + if (god_act.which_god != GOD_NO_GOD && + god_act.which_god != which_god) + { + ASSERT(god_act.depth >= 1); + + god_act_stack.push_back(god_act); + god_act.reset(); + } + + god_act.which_god = which_god; + god_act.retribution = is_retribution || god_act.retribution; + god_act.depth++; +} + +void game_state::dec_god_acting() +{ + dec_god_acting(you.religion); +} + +void game_state::dec_god_acting(god_type which_god) +{ + ASSERT(which_god != GOD_NO_GOD); + ASSERT(god_act.depth > 0); + ASSERT(god_act.which_god == which_god); + + god_act.depth--; + + if (god_act.depth == 0) + { + god_act.reset(); + if (god_act_stack.size() > 0) + { + god_act = god_act_stack[god_act_stack.size() - 1]; + god_act_stack.pop_back(); + ASSERT(god_act.depth >= 1); + ASSERT(god_act.which_god != GOD_NO_GOD); + ASSERT(god_act.which_god != which_god); + } + } +} + +void game_state::clear_god_acting() +{ + ASSERT(!is_god_acting()); + ASSERT(god_act_stack.size() == 0); + + god_act.reset(); +} + +std::vector<god_act_state> game_state::other_gods_acting() const +{ + ASSERT(is_god_acting()); + return god_act_stack; +} |