/* * 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 "directn.h" #include "macro.h" #include "menu.h" // For print_formatted_paragraph() #include "message.h" #include "mon-util.h" #include "output.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( "[ + : 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.empty()) 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.empty()) mpr(reason.c_str()); } void game_state::cant_cmd_repeat(std::string reason) { if (reason.empty()) reason = "Can't repeat that command."; cancel_cmd_repeat(reason); } void game_state::cant_cmd_again(std::string reason) { if (reason.empty()) 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(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) tutorial_first_monster(*mon); #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(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 game_state::other_gods_acting() const { ASSERT(is_god_acting()); return god_act_stack; }