diff options
33 files changed, 1454 insertions, 38 deletions
diff --git a/crawl-ref/docs/crawl_manual.txt b/crawl-ref/docs/crawl_manual.txt index 3791b53304..fa34e0386e 100644 --- a/crawl-ref/docs/crawl_manual.txt +++ b/crawl-ref/docs/crawl_manual.txt @@ -2157,6 +2157,11 @@ Other game-playing commands: Ctrl-A Toggle autopickup. Note that encounters with invisible monsters always turns autopickup off. You need to switch it on with Ctrl-A afterwards. + Ctrl-V Toggle autoprayer. Note that encounters with + invisible monsters always turns autopickup off. + You need to switch it on with Ctrl-V afterwards. + ` Re-do previous command + 0 Repeat next command a given number of times Non-game playing commands: ? The help menu. @@ -2168,7 +2173,7 @@ Non-game playing commands: : Add note to dump file (see option take_notes). ?: Read the notes in-game. ~ Add or save macros and key mappings. - = Reassign inventory/spell/abilities letters. + = Reassign inventory/spell/abilities letters. Stashes: Ctrl-F Find. This searches in stashes and shops, you diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc index 3770c286a8..11f454d2db 100644 --- a/crawl-ref/source/abl-show.cc +++ b/crawl-ref/source/abl-show.cc @@ -63,6 +63,7 @@ #include "spells2.h" #include "spells3.h" #include "spells4.h" +#include "state.h" #include "stuff.h" #include "transfor.h" #include "tutorial.h" @@ -811,6 +812,7 @@ bool activate_ability() if ( talents.empty() ) { mpr("Sorry, you're not good enough to have a special ability."); + crawl_state.zero_turns_taken(); return false; } if ( you.duration[DUR_CONF] ) @@ -819,6 +821,7 @@ bool activate_ability() if ( talents.empty() ) { mpr("You're too confused!"); + crawl_state.zero_turns_taken(); return false; } } @@ -857,6 +860,7 @@ bool activate_ability() if ( selected < 0 ) { mpr("You can't do that."); + crawl_state.zero_turns_taken(); return (false); } } @@ -886,6 +890,7 @@ static bool activate_talent(const talent& tal) if (hungerCheck && you.hunger_state <= HS_STARVING) { mpr("You're too hungry."); + crawl_state.zero_turns_taken(); return (false); } @@ -893,10 +898,16 @@ static bool activate_talent(const talent& tal) // check that we can afford to pay the costs if (!enough_mp( abil.mp_cost, false )) + { + crawl_state.zero_turns_taken(); return (false); + } if (!enough_hp( abil.hp_cost, false )) + { + crawl_state.zero_turns_taken(); return (false); + } // no turning back now... {dlb} if (random2avg(100, 3) < tal.fail) diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index 7089da5f34..13e9a79ece 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -148,6 +148,8 @@ char info[ INFO_SIZE ]; // messaging queue extern'd everywhere {dlb} int stealth; // externed in view.cc +static key_recorder repeat_again_rec; + // Clockwise, around the compass from north (same order as enum RUN_DIR) const struct coord_def Compass[8] = { @@ -170,6 +172,9 @@ static void world_reacts(); static command_type get_next_cmd(); static keycode_type get_next_keycode(); static command_type keycode_to_command( keycode_type key ); +static void setup_cmd_repeat(); +static void do_prev_cmd_again(); +static void update_replay_state(); #ifdef DGL_SIMPLE_MESSAGING static void read_messages(); @@ -387,11 +392,48 @@ static void handle_wizard_command( void ) you.wizard = true; redraw_screen(); + + if (crawl_state.cmd_repeat_start) + { + crawl_state.cancel_cmd_repeat("Can't repeat entering wizard " + "mode."); + return; + } } mpr( "Enter Wizard Command (? - help): ", MSGCH_PROMPT ); wiz_command = getch(); + if (crawl_state.cmd_repeat_start) + { + // Easiest to list which wizard commands *can* be repeated. + switch (wiz_command) + { + case 'x': + case '$': + case 'a': + case 'c': + case 'h': + case 'H': + case 'm': + case 'M': + case 'X': + case '!': + case '[': + case '^': + case '%': + case 'o': + case 'z': + case 'Z': + break; + + default: + crawl_state.cant_cmd_repeat("You cannot repeat that " + "wizard command."); + return; + } + } + switch (wiz_command) { case '?': @@ -963,16 +1005,156 @@ static void recharge_rods() } } +static bool cmd_is_repeatable(command_type cmd, bool is_again = false) +{ + switch(cmd) + { + // Informational commands + case CMD_LOOK_AROUND: + case CMD_INSPECT_FLOOR: + case CMD_EXAMINE_OBJECT: + case CMD_LIST_WEAPONS: + case CMD_LIST_ARMOUR: + case CMD_LIST_JEWELLERY: + case CMD_CHARACTER_DUMP: + case CMD_DISPLAY_COMMANDS: + case CMD_DISPLAY_INVENTORY: + case CMD_DISPLAY_KNOWN_OBJECTS: + case CMD_DISPLAY_MUTATIONS: + case CMD_DISPLAY_SKILLS: + case CMD_DISPLAY_OVERMAP: + case CMD_DISPLAY_RELIGION: + case CMD_DISPLAY_CHARACTER_STATUS: + case CMD_DISPLAY_SPELLS: + case CMD_EXPERIENCE_CHECK: + case CMD_GET_VERSION: + case CMD_RESISTS_SCREEN: + case CMD_READ_MESSAGES: + case CMD_SEARCH_STASHES: + mpr("You can't repeat informational commands."); + return false; + + // Multi-turn commands + case CMD_PICKUP: + case CMD_DROP: + case CMD_BUTCHER: + case CMD_GO_UPSTAIRS: + case CMD_GO_DOWNSTAIRS: + case CMD_WIELD_WEAPON: + case CMD_WEAPON_SWAP: + case CMD_WEAR_JEWELLERY: + case CMD_REMOVE_JEWELLERY: + case CMD_MEMORISE_SPELL: + case CMD_EXPLORE: + case CMD_INTERLEVEL_TRAVEL: + mpr("You can't repeat multi-turn commands."); + return false; + + // Miscellaneous non-repeatable commands. + case CMD_TOGGLE_AUTOPICKUP: + case CMD_ADJUST_INVENTORY: + case CMD_REPLAY_MESSAGES: + case CMD_REDRAW_SCREEN: + case CMD_MACRO_ADD: + case CMD_SAVE_GAME: + case CMD_SAVE_GAME_NOW: + case CMD_SUSPEND_GAME: + case CMD_QUIT: + case CMD_DESTROY_ITEM: + case CMD_MARK_STASH: + case CMD_FORGET_STASH: + case CMD_FIX_WAYPOINT: + case CMD_CLEAR_MAP: + case CMD_INSCRIBE_ITEM: + case CMD_TOGGLE_AUTOPRAYER: + case CMD_MAKE_NOTE: + mpr("You can't repeat that command."); + return false; + + case CMD_DISPLAY_MAP: + mpr("You can't repeat map commands."); + return false; + + case CMD_MOUSE_MOVE: + case CMD_MOUSE_CLICK: + mpr("You can't repeat mouse clicks or movements."); + return false; + + case CMD_REPEAT_CMD: + mpr("You can't repeat the repeat command!"); + return false; + + case CMD_RUN_LEFT: + case CMD_RUN_DOWN: + case CMD_RUN_UP: + case CMD_RUN_RIGHT: + case CMD_RUN_UP_LEFT: + case CMD_RUN_DOWN_LEFT: + case CMD_RUN_UP_RIGHT: + case CMD_RUN_DOWN_RIGHT: + mpr("Why would you want to repeat a run command?"); + return false; + + case CMD_PREV_CMD_AGAIN: + ASSERT(!is_again); + if (crawl_state.prev_cmd == CMD_NO_CMD) + { + mpr("No previous command to repeat."); + return false; + } + + return cmd_is_repeatable(crawl_state.prev_cmd, true); + + case CMD_MOVE_NOWHERE: + case CMD_REST: + case CMD_SEARCH: + return i_feel_safe(true); + + case CMD_MOVE_LEFT: + case CMD_MOVE_DOWN: + case CMD_MOVE_UP: + case CMD_MOVE_RIGHT: + case CMD_MOVE_UP_LEFT: + case CMD_MOVE_DOWN_LEFT: + case CMD_MOVE_UP_RIGHT: + case CMD_MOVE_DOWN_RIGHT: + if (!i_feel_safe()) + return yesno("Really repeat movement command while monsters " + "are nearby?"); + + return true; + + case CMD_NO_CMD: + mpr("Unknown command, not repeating."); + return false; + + default: + return true; + } + + return false; +} + /* used to determine whether to apply the berserk penalty at end of round */ bool apply_berserk_penalty = false; /* - This function handles the player's input. It's called from main(), from - inside an endless loop. + * This function handles the player's input. It's called from main(), + * from inside an endless loop. */ static void input() { + if (crawl_state.is_replaying_keys() && crawl_state.is_repeating_cmd() + && kbhit()) + { + // User pressed a key, so stop repeating commands and discard + // the keypress + crawl_state.cancel_cmd_repeat(); + getchm(); + return; + } + you.turn_is_over = false; prep_input(); @@ -1015,6 +1197,9 @@ static void input() if ( you.duration[DUR_PARALYSIS] ) { + crawl_state.cancel_cmd_repeat("Paralyzed, cancelling command " + "repetition."); + world_reacts(); return; } @@ -1047,7 +1232,17 @@ static void input() crawl_state.check_term_size(); if (crawl_state.terminal_resized) handle_terminal_resize(); - + + repeat_again_rec.paused = (crawl_state.is_replaying_keys() + && !crawl_state.cmd_repeat_start); + + crawl_state.input_line_curr = 0; + if (!crawl_state.is_repeating_cmd() + && !crawl_state.doing_prev_cmd_again) + { + crawl_state.input_line_strs.clear(); + } + { // Enable the cursor to read input. The cursor stays on while // the command is being processed, so subsidiary prompts @@ -1061,6 +1256,12 @@ static void input() crawl_state.waiting_for_command = false; + if (cmd != CMD_PREV_CMD_AGAIN && cmd != CMD_REPEAT_CMD + && !crawl_state.is_replaying_keys()) + { + crawl_state.prev_cmd = cmd; + } + if (cmd != CMD_MOUSE_MOVE) c_input_reset(false); @@ -1070,8 +1271,17 @@ static void input() if (!you.turn_is_over && cmd != CMD_NEXT_CMD) process_command( cmd ); + repeat_again_rec.paused = true; + if (cmd != CMD_MOUSE_MOVE) c_input_reset(false, true); + + // If the command was CMD_REPEAT_CMD, then the key for the + // command to repeat has been placed into the macro buffer, + // so return now to let input() be called again while + // the keys to repeat are recorded. + if (cmd == CMD_REPEAT_CMD) + return; } if (need_to_autoinscribe()) @@ -1087,6 +1297,8 @@ static void input() else viewwindow(true, false); + update_replay_state(); + if (you.num_turns != -1) { PlaceInfo& curr_PlaceInfo = you.get_place_info(); @@ -1332,6 +1544,51 @@ void process_command( command_type cmd ) Options.show_more_prompt = true; break; + case CMD_REPEAT_KEYS: + ASSERT(crawl_state.is_repeating_cmd()); + + if (crawl_state.prev_repetition_turn == you.num_turns + && !(crawl_state.repeat_cmd == CMD_WIZARD + || (crawl_state.repeat_cmd == CMD_PREV_CMD_AGAIN + && crawl_state.prev_cmd == CMD_WIZARD))) + { + // This is a catch-all that shouldn't really happen. + // If the command always takes zero turns, then it + // should be prevented in cmd_is_repeatable(). If + // a command sometimes takes zero turns (because it + // can't be done, for instance), then + // crawl_state.zero_turns_taken() should be called when + // it does take zero turns, to cancel command repetition + // before we reach here. +#ifdef WIZARD + crawl_state.cant_cmd_repeat("Can't repeat a command which " + "takes no turns (unless it's a " + "wizard command), cancelling "); +#else + crawl_state.cant_cmd_repeat("Can't repeat a command which " + "takes no turns, cancelling " + "repetitions."); +#endif + crawl_state.cancel_cmd_repeat(); + return; + } + + crawl_state.cmd_repeat_count++; + if (crawl_state.cmd_repeat_count >= crawl_state.cmd_repeat_goal) + { + crawl_state.cancel_cmd_repeat(); + return; + } + + ASSERT(crawl_state.repeat_cmd_keys.size() > 0); + repeat_again_rec.paused = true; + macro_buf_add(crawl_state.repeat_cmd_keys); + macro_buf_add(KEY_REPEAT_KEYS); + + crawl_state.prev_repetition_turn = you.num_turns; + + break; + case CMD_TOGGLE_AUTOPICKUP: toggle_flag( &Options.autopickup_on, "Autopickup"); break; @@ -1785,6 +2042,14 @@ void process_command( command_type cmd ) version(); break; + case CMD_REPEAT_CMD: + setup_cmd_repeat(); + break; + + case CMD_PREV_CMD_AGAIN: + do_prev_cmd_again(); + break; + case CMD_NO_CMD: default: if (Options.tutorial_left) @@ -2637,6 +2902,7 @@ command_type keycode_to_command( keycode_type key ) { case KEY_MACRO_DISABLE_MORE: return CMD_DISABLE_MORE; case KEY_MACRO_ENABLE_MORE: return CMD_ENABLE_MORE; + case KEY_REPEAT_KEYS: return CMD_REPEAT_KEYS; case 'b': return CMD_MOVE_DOWN_LEFT; case 'h': return CMD_MOVE_LEFT; case 'j': return CMD_MOVE_DOWN; @@ -2717,8 +2983,9 @@ command_type keycode_to_command( keycode_type key ) case '(': return CMD_LIST_WEAPONS; case '\\': return CMD_DISPLAY_KNOWN_OBJECTS; case '\'': return CMD_WEAPON_SWAP; + case '`': return CMD_PREV_CMD_AGAIN; - case '0': return CMD_NO_CMD; + case '0': return CMD_REPEAT_CMD; case '5': return CMD_REST; case CONTROL('B'): return CMD_OPEN_DOOR_DOWN_LEFT; @@ -2978,7 +3245,6 @@ static void close_door(int door_x, int door_y) } } // end open_door() - // initialise whole lot of stuff... // returns true if a new character static bool initialise(void) @@ -3115,6 +3381,8 @@ static bool initialise(void) activate_notes(true); + add_key_recorder(&repeat_again_rec); + return (newc); } @@ -3205,6 +3473,8 @@ static void move_player(int move_x, int move_y) you.turn_is_over = true; mpr("Ouch!"); apply_berserk_penalty = true; + crawl_state.cancel_cmd_repeat(); + return; } } // end of if you.duration[DUR_CONF] @@ -3275,6 +3545,7 @@ static void move_player(int move_x, int move_y) move_y = 0; you.turn_is_over = 0; + crawl_state.cancel_cmd_repeat(); } if (you.running == RMODE_START) @@ -3314,3 +3585,326 @@ static void move_player(int move_x, int move_y) apply_berserk_penalty = !attacking; } // end move_player() + + +static int get_num_and_char_keyfun(int &ch) +{ + if (ch == CK_BKSP || isdigit(ch) || ch >= 128) + return 1; + + return -1; +} + +static int get_num_and_char(const char* prompt, char* buf, int buf_len) +{ + if (prompt != NULL) + mpr(prompt); + + line_reader reader(buf, buf_len); + + reader.set_keyproc(get_num_and_char_keyfun); + + return reader.read_line(true); +} + +static void setup_cmd_repeat() +{ + if (is_processing_macro()) + { + flush_input_buffer(FLUSH_ABORT_MACRO); + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + return; + } + + ASSERT(!crawl_state.is_repeating_cmd()); + + char buf[80]; + + // Function ensures that the buffer contains only digits. + int ch = get_num_and_char("Number of times to repeat, then command key: ", + buf, 80); + + if (ch == ESCAPE) + { + // This *might* be part of the trigger for a macro. + keyseq trigger; + trigger.push_back(ch); + + if (get_macro_buf_size() == 0) + { + // Was just a single ESCAPE key, so not a macro trigger. + canned_msg( MSG_OK ); + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + flush_input_buffer(FLUSH_REPLAY_SETUP_FAILURE); + return; + } + ch = getchm(); + trigger.push_back(ch); + + // Now that we have the entirety of the (possible) macro trigger, + // clear out the keypress recorder so that we won't have recorded + // the trigger twice. + repeat_again_rec.clear(); + + insert_macro_into_buff(trigger); + + ch = getchm(); + if (ch == ESCAPE) + { + if (get_macro_buf_size() > 0) + // User pressed an Alt key which isn't bound to a macro. + mpr("That key isn't bound to a macro."); + else + // Wasn't a macro trigger, just an ordinary escape. + canned_msg( MSG_OK ); + + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + flush_input_buffer(FLUSH_REPLAY_SETUP_FAILURE); + return; + } + // *WAS* a macro trigger, keep going. + } + + if (strlen(buf) == 0) + { + mpr("You must enter the number of times for the command to repeat."); + + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + flush_input_buffer(FLUSH_REPLAY_SETUP_FAILURE); + + return; + } + + int count = atoi(buf); + + if (crawl_state.doing_prev_cmd_again) + count = crawl_state.prev_cmd_repeat_goal; + + if (count <= 0) + { + canned_msg( MSG_OK ); + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + flush_input_buffer(FLUSH_REPLAY_SETUP_FAILURE); + return; + } + + if (crawl_state.doing_prev_cmd_again) + { + // If a "do previous command again" caused a command + // repetition to be redone, the keys to be repeated are + // already in the key rercording buffer, so we just need to + // discard all the keys saying how many times the command + // should be repeated. + do { + repeat_again_rec.keys.pop_front(); + } while (repeat_again_rec.keys[0] != ch); + + repeat_again_rec.keys.pop_front(); + } + + // User can type space or enter and then the command key, in case + // they want to repeat a command bound to a number key. + c_input_reset(true); + if (ch == ' ' || ch == CK_ENTER) + { + if (!crawl_state.doing_prev_cmd_again) + repeat_again_rec.keys.pop_back(); + + mpr("Enter command to be repeated: "); + // Enable the cursor to read input. The cursor stays on while + // the command is being processed, so subsidiary prompts + // shouldn't need to turn it on explicitly. + cursor_control con(true); + + crawl_state.waiting_for_command = true; + + ch = get_next_keycode(); + + crawl_state.waiting_for_command = false; + } + + command_type cmd = keycode_to_command( (keycode_type) ch); + + if (cmd != CMD_MOUSE_MOVE) + c_input_reset(false); + + if (!is_processing_macro() && !cmd_is_repeatable(cmd)) + { + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + flush_input_buffer(FLUSH_REPLAY_SETUP_FAILURE); + return; + } + + if (!crawl_state.doing_prev_cmd_again && cmd != CMD_PREV_CMD_AGAIN) + crawl_state.prev_cmd_keys = repeat_again_rec.keys; + + if (is_processing_macro()) + { + // Put back in first key of the expanded macro, which + // get_next_keycode() fetched + repeat_again_rec.paused = true; + macro_buf_add(ch, true); + + // If we're repeating a macro, get rid the keys saying how + // many times to repeat, because the way that macros are + // repeated means that the number keys will be repeated if + // they aren't discarded. + keyseq &keys = repeat_again_rec.keys; + ch = keys[keys.size() - 1]; + while (isdigit(ch) || ch == ' ' || ch == CK_ENTER) + { + keys.pop_back(); + ASSERT(keys.size() > 0); + ch = keys[keys.size() - 1]; + } + } + + repeat_again_rec.paused = false; + // Discard the setup for the command repetition, since what's + // going to be repeated is yet to be typed, except for the fist + // key typed which has to be put back in (unless we're repeating a + // macro, in which case everything to be repeated is already in + // the macro buffer). + if (!is_processing_macro()) + { + repeat_again_rec.clear(); + macro_buf_add(ch, crawl_state.doing_prev_cmd_again); + } + + crawl_state.cmd_repeat_start = true; + crawl_state.cmd_repeat_count = 0; + crawl_state.repeat_cmd = cmd; + crawl_state.cmd_repeat_goal = count; + crawl_state.prev_cmd_repeat_goal = count; + crawl_state.prev_repetition_turn = you.num_turns; + + crawl_state.cmd_repeat_started_unsafe = !i_feel_safe(); + + crawl_state.input_line_strs.clear(); +} + +static void do_prev_cmd_again() +{ + if (is_processing_macro()) + { + mpr("Can't re-do previous command from within a macro."); + flush_input_buffer(FLUSH_ABORT_MACRO); + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + return; + } + + if (crawl_state.prev_cmd == CMD_NO_CMD) + { + mpr("No previous command to re-do."); + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + return; + } + + ASSERT(!crawl_state.doing_prev_cmd_again + || (crawl_state.is_repeating_cmd() + && crawl_state.repeat_cmd == CMD_PREV_CMD_AGAIN)); + + crawl_state.doing_prev_cmd_again = true; + repeat_again_rec.paused = false; + + if (crawl_state.prev_cmd == CMD_REPEAT_CMD) + { + crawl_state.cmd_repeat_start = true; + crawl_state.cmd_repeat_count = 0; + crawl_state.cmd_repeat_goal = crawl_state.prev_cmd_repeat_goal; + crawl_state.prev_repetition_turn = you.num_turns; + } + + const keyseq &keys = crawl_state.prev_cmd_keys; + ASSERT(keys.size() > 0); + + // Insert keys at front of input buffer, rather than at the end, + // since if the player holds down the "`" key, then the buffer + // might get two "`" in a row, and if the keys to be replayed go after + // the second "`" then we get an assertion. + macro_buf_add(keys, true); + + bool was_doing_repeats = crawl_state.is_repeating_cmd(); + + input(); + + // crawl_state.doing_prev_cmd_again can be set to false + // while input() does its stuff if something causes + // crawl_state.cancel_cmd_again() to be called. + while (!was_doing_repeats && crawl_state.is_repeating_cmd() + && crawl_state.doing_prev_cmd_again) + { + input(); + } + + if (!was_doing_repeats && crawl_state.is_repeating_cmd() + && !crawl_state.doing_prev_cmd_again) + { + crawl_state.cancel_cmd_repeat(); + } + + crawl_state.doing_prev_cmd_again = false; +} + +static void update_replay_state() +{ + if (crawl_state.is_repeating_cmd()) + { + // First repeat is to copy down the keys the user enters, + // grab them so we can go on autopilot for the remaining + // iterations. + if (crawl_state.cmd_repeat_start) + { + ASSERT(repeat_again_rec.keys.size() > 0); + + crawl_state.cmd_repeat_start = false; + crawl_state.repeat_cmd_keys = repeat_again_rec.keys; + + // Setting up the "previous command key sequence" + // for a repeated command is different than normal, + // since in addition to all of the keystrokes for + // the command, it needs the repeat command plus the + // number of repeats at the very beginning of the + // sequence. + if (!crawl_state.doing_prev_cmd_again) + { + keyseq &prev = crawl_state.prev_cmd_keys; + keyseq &curr = repeat_again_rec.keys; + + if (is_processing_macro()) + prev = curr; + else + { + // Skip first key, because that's command key that's + // being repeated, which crawl_state.prev_cmd_keys + // aleardy contains. + keyseq::iterator begin = curr.begin(); + begin++; + + prev.insert(prev.end(), begin, curr.end()); + } + } + + repeat_again_rec.paused = true; + macro_buf_add(KEY_REPEAT_KEYS); + } + } + + if (!crawl_state.is_replaying_keys() && !crawl_state.cmd_repeat_start + && crawl_state.prev_cmd != CMD_NO_CMD) + { + if (repeat_again_rec.keys.size() > 0) + crawl_state.prev_cmd_keys = repeat_again_rec.keys; + } + + if (!is_processing_macro()) + repeat_again_rec.clear(); +} diff --git a/crawl-ref/source/cio.cc b/crawl-ref/source/cio.cc index 4296b65654..bc9909c29b 100644 --- a/crawl-ref/source/cio.cc +++ b/crawl-ref/source/cio.cc @@ -108,6 +108,19 @@ void get_input_line( char *const buff, int len ) { buff[0] = 0; // just in case + if (crawl_state.is_replaying_keys()) + { + ASSERT(crawl_state.input_line_curr < + crawl_state.input_line_strs.size()); + + unsigned int curr = crawl_state.input_line_curr++; + std::string &line = crawl_state.input_line_strs[curr]; + + strcpy(buff, line.c_str()); + + return; + } + #if defined(UNIX) get_input_line_from_curses( buff, len ); // implemented in libunix.cc #elif defined(WIN32CONSOLE) @@ -137,6 +150,8 @@ void get_input_line( char *const buff, int len ) else break; } + + crawl_state.input_line_strs.push_back(buff); } // Hacky wrapper around getch() that returns CK_ codes for keys diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index eb4c5fee51..e10ec51b62 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -1008,6 +1008,8 @@ void list_commands(bool wizard, int hotkey) "<w>p</w> : Pray\n" "<w>Z</w> : cast a spell\n" "<w>!</w> : shout or command allies\n" + "<w>`</w> : re-do previous command\n" + "<w>0</w> : repeat next command # of times\n" " \n", true, true, cmdhelp_textfilter); diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index ff127f49be..bc2307c6c4 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -82,6 +82,7 @@ #include "skills2.h" #include "spl-cast.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "terrain.h" #include "traps.h" @@ -328,7 +329,11 @@ void cast_spec_spell(void) if (spell == -1) canned_msg( MSG_OK ); else - your_spells( static_cast<spell_type>(spell), 0, false ); + if (your_spells( static_cast<spell_type>(spell), 0, false ) + == SPRET_ABORT) + { + crawl_state.cancel_cmd_repeat(); + } } #endif @@ -355,7 +360,11 @@ void cast_spec_spell_name(void) if (strstr( strlwr(spname), strlwr(specs) ) != NULL) { - your_spells(static_cast<spell_type>(i), 0, false); + if (your_spells(static_cast<spell_type>(i), 0, false) + == SPRET_ABORT) + { + crawl_state.cancel_cmd_repeat(); + } return; } } @@ -1250,6 +1259,8 @@ static void dump_item( const char *name, int num, const item_def &item ) get_ident_type( item.base_type, item.sub_type ) ); mprf(" x: %d; y: %d; link: %d", item.x, item.y, item.link ); + + crawl_state.cancel_cmd_repeat(); } //--------------------------------------------------------------- diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc index 5a34d2554d..38ec142228 100644 --- a/crawl-ref/source/decks.cc +++ b/crawl-ref/source/decks.cc @@ -38,6 +38,7 @@ #include "spells4.h" #include "spl-cast.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "terrain.h" #include "transfor.h" @@ -248,6 +249,7 @@ bool choose_deck_and_draw() if ( !is_deck(deck) ) { mpr("That isn't a deck!"); + crawl_state.zero_turns_taken(); return false; } evoke_deck(deck); @@ -261,12 +263,14 @@ bool deck_peek() if ( !wielding_deck() ) { mpr("You aren't wielding a deck!"); + crawl_state.zero_turns_taken(); return false; } item_def& item(you.inv[you.equip[EQ_WEAPON]]); if ( item.plus2 != 0 ) { mpr("You already know what the next card will be."); + crawl_state.zero_turns_taken(); return false; } @@ -297,12 +301,14 @@ bool deck_stack() if ( !wielding_deck() ) { mpr("You aren't wielding a deck!"); + crawl_state.zero_turns_taken(); return false; } item_def& item(you.inv[you.equip[EQ_WEAPON]]); if ( item.plus2 != 0 ) { mpr("You can't stack a marked deck."); + crawl_state.zero_turns_taken(); return false; } const int num_to_stack = (item.plus < 5 ? item.plus : 5); @@ -357,6 +363,7 @@ bool deck_triple_draw() if ( !wielding_deck() ) { mpr("You aren't wielding a deck!"); + crawl_state.zero_turns_taken(); return false; } @@ -366,6 +373,7 @@ bool deck_triple_draw() if ( item.plus2 != 0 ) { mpr("You can't triple draw from a marked deck."); + crawl_state.zero_turns_taken(); return false; } diff --git a/crawl-ref/source/defines.h b/crawl-ref/source/defines.h index 65d11999cf..ddda72ddab 100644 --- a/crawl-ref/source/defines.h +++ b/crawl-ref/source/defines.h @@ -298,5 +298,6 @@ #define KEY_MACRO_MORE_PROTECT -10 #define KEY_MACRO_DISABLE_MORE -1 #define KEY_MACRO_ENABLE_MORE -2 +#define KEY_REPEAT_KEYS -3 #endif diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc index 9097e371c2..b2b443c0d1 100644 --- a/crawl-ref/source/delay.cc +++ b/crawl-ref/source/delay.cc @@ -37,6 +37,7 @@ #include "randart.h" #include "religion.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "travel.h" #include "tutorial.h" @@ -102,6 +103,8 @@ static void clear_pending_delays() void start_delay( delay_type type, int turns, int parm1, int parm2 ) /***********************************************************/ { + ASSERT(!crawl_state.is_repeating_cmd() || type == DELAY_MACRO); + delay_queue_item delay; delay.type = type; @@ -146,6 +149,8 @@ void stop_delay( void ) delay_queue_item delay = you.delay_queue.front(); + ASSERT(!crawl_state.is_repeating_cmd() || delay.type == DELAY_MACRO); + const bool butcher_swap_warn = delay.type == DELAY_BUTCHER && (you.delay_queue.size() >= 2 @@ -304,6 +309,8 @@ void handle_delay( void ) delay_queue_item &delay = you.delay_queue.front(); + ASSERT(!crawl_state.is_repeating_cmd() || delay.type == DELAY_MACRO); + // Run delays and Lua delays don't have a specific end time. if (is_run_delay(delay.type)) { @@ -992,7 +999,7 @@ inline static void monster_warning(activity_interrupt_type ai, if (!mon->visible()) return; #ifndef DEBUG_DIAGNOSTICS - if (at.context != "uncharm") + if (at.context == "newly seen") { // Only say "comes into view" if the monster wasn't in view // during the previous turn. @@ -1084,7 +1091,10 @@ bool interrupt_activity( activity_interrupt_type ai, const activity_interrupt_data &at ) { paranoid_option_disable(ai, at); - + + if (crawl_state.is_repeating_cmd()) + return interrupt_cmd_repeat(ai, at); + const int delay = current_delay_action(); if (delay == DELAY_NOT_DELAYED) diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 9f246d67a9..02ef032460 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -46,6 +46,7 @@ #include "mon-util.h" #include "player.h" #include "shopping.h" +#include "state.h" #include "stuff.h" #include "spells4.h" #include "stash.h" @@ -234,6 +235,135 @@ static void draw_ray_glyph(const coord_def &pos, int colour, putch(glych); } +// We handle targeting for repeating commands and re-doing the +// previous command differently (i.e., not just letting the keys +// stuffed into the macro buffer replay as-is) because if the player +// targeted a monster using the movement keys and the monster then +// moved between repititions, then simply replaying the keys in the +// buffer will target an empty square. +static void direction_again(dist& moves, targeting_type restricts, + targ_mode_type mode, bool just_looking, + const char *prompt, targeting_behaviour *beh) +{ + moves.isValid = false; + moves.isTarget = false; + moves.isMe = false; + moves.isCancel = false; + moves.isEndpoint = false; + moves.choseRay = false; + + if (you.prev_targ == MHITNOT && you.prev_grd_targ == coord_def(0, 0)) + { + moves.isCancel = true; + crawl_state.cancel_cmd_repeat(); + return; + } + + int targ_types = 0; + if (you.prev_targ != MHITNOT && you.prev_targ != MHITYOU) + targ_types++; + if (you.prev_targ == MHITYOU) + targ_types++; + if (you.prev_grd_targ != coord_def(0, 0)) + targ_types++; + ASSERT(targ_types == 1); + + // Discard keys until we get to a set-target command + command_type key_command; + + while (crawl_state.is_replaying_keys()) + { + key_command = beh->get_command(); + + if (key_command == CMD_TARGET_PREV_TARGET + || key_command == CMD_TARGET_SELECT_ENDPOINT + || key_command == CMD_TARGET_SELECT + || key_command == CMD_TARGET_MAYBE_PREV_TARGET) + { + break; + } + } + + if (!crawl_state.is_replaying_keys()) + { + moves.isCancel = true; + + mpr("Ran out of keys."); + + return; + } + + if (key_command == CMD_TARGET_SELECT_ENDPOINT) + moves.isEndpoint = true; + + if (you.prev_grd_targ != coord_def(0, 0)) + { + if (!see_grid(you.prev_grd_targ)) + { + moves.isCancel = true; + + crawl_state.cancel_cmd_repeat("You can no longer see the dungeon " + "square you previously targeted."); + return; + } + else if (you.prev_grd_targ.x == you.x_pos + && you.prev_grd_targ.y == you.y_pos) + { + moves.isCancel = true; + + crawl_state.cancel_cmd_repeat("You are now standing on your " + "previously targeted dungeon " + "square."); + return; + } + + moves.tx = you.prev_grd_targ.x; + moves.ty = you.prev_grd_targ.y; + + ray_def ray; + find_ray(you.x_pos, you.y_pos, moves.tx, moves.ty, true, ray, + 0, true); + moves.ray = ray; + } + else if (you.prev_targ == MHITYOU) + { + moves.isMe = true; + moves.tx = you.x_pos; + moves.ty = you.y_pos; + + // Discard 'Y' player gave to yesno() + if (mode == TARG_ENEMY && Options.confirm_self_target) + getchm(); + } + else + { + const monsters *montarget = &menv[you.prev_targ]; + + if (!mons_near(montarget) || + !player_monster_visible( montarget )) + { + moves.isCancel = true; + + crawl_state.cancel_cmd_repeat("Your target is gone."); + + return; + } + + moves.tx = montarget->x; + moves.ty = montarget->y; + + ray_def ray; + find_ray(you.x_pos, you.y_pos, moves.tx, moves.ty, true, ray, + 0, true); + moves.ray = ray; + } + + moves.isValid = true; + moves.isTarget = true; + + return; +} + //--------------------------------------------------------------- // // direction @@ -264,7 +394,14 @@ void direction(dist& moves, targeting_type restricts, beh = &stock_behaviour; beh->just_looking = just_looking; - + + if (crawl_state.is_replaying_keys() && restricts != DIR_DIR) + { + direction_again(moves, restricts, mode, just_looking, + prompt, beh); + return; + } + // NOTE: Even if just_looking is set, moves is still interesting, // because we can travel there! @@ -536,10 +673,18 @@ void direction(dist& moves, targeting_type restricts, moves.isValid = true; moves.isTarget = true; loop_done = true; + + you.prev_grd_targ = coord_def(0, 0); + // maybe we should except just_looking here? mid = mgrd[moves.tx][moves.ty]; + if ( mid != NON_MONSTER ) you.prev_targ = mid; + else if (moves.tx == you.x_pos && moves.ty == you.y_pos) + you.prev_targ = MHITYOU; + else + you.prev_grd_targ = coord_def(moves.tx, moves.ty); break; case CMD_TARGET_OBJ_CYCLE_BACK: @@ -1922,7 +2067,9 @@ targeting_behaviour::~targeting_behaviour() int targeting_behaviour::get_key() { - flush_input_buffer(FLUSH_BEFORE_COMMAND); + if (!crawl_state.is_replaying_keys()) + flush_input_buffer(FLUSH_BEFORE_COMMAND); + return unmangle_direction_keys( getchm(KC_TARGETING), KC_TARGETING, false, false); } diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index d5b091f080..dbdef1f880 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -633,8 +633,17 @@ enum command_type CMD_ENABLE_MORE, // [ds] Silently ignored, requests another round of input. - CMD_NEXT_CMD + CMD_NEXT_CMD, + // Repeat previous command + CMD_PREV_CMD_AGAIN, + + // Repeat next command a given number of times + CMD_REPEAT_CMD, + + // Stick the keyspresses of the command to be repeated into the + // input buffer. + CMD_REPEAT_KEYS }; enum conduct_type @@ -1114,6 +1123,10 @@ enum flush_reason_type FLUSH_ON_PROMPT, // flush on MSGCH_PROMPT messages FLUSH_ON_UNSAFE_YES_OR_NO_PROMPT, // flush when !safe set to yesno() FLUSH_LUA, // flush when Lua wants to flush + FLUSH_KEY_REPLAY_CANCEL, // flush when key replay is cancelled + FLUSH_ABORT_MACRO, // something wrong with macro being + // processed, so stop it + FLUSH_REPLAY_SETUP_FAILURE, // setup for key replay failed NUM_FLUSH_REASONS }; diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index 083c5c83ce..8db0139a43 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -23,6 +23,7 @@ #include <set> #include <memory> #include <cstdlib> +#include <deque> #include <time.h> @@ -464,6 +465,8 @@ typedef std::vector<delay_queue_item> delay_queue_type; class KillMaster; + + class player : public actor { public: @@ -476,6 +479,7 @@ public: bool just_autoprayed; // autopray just kicked in unsigned char prev_targ; + coord_def prev_grd_targ; char your_name[kNameLen]; species_type species; diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc index b4b57ec247..7e2cf37bed 100644 --- a/crawl-ref/source/files.cc +++ b/crawl-ref/source/files.cc @@ -841,7 +841,8 @@ bool load( dungeon_feature_type stair_taken, int load_mode, } } - you.prev_targ = MHITNOT; + you.prev_targ = MHITNOT; + you.prev_grd_targ = coord_def(0, 0); // We clear twice - on save and on load. // Once would be enough... @@ -1026,7 +1027,8 @@ void save_level(int level_saved, level_area_type old_ltype, where_were_you, old_ltype, false ); - you.prev_targ = MHITNOT; + you.prev_targ = MHITNOT; + you.prev_grd_targ = coord_def(0, 0); FILE *saveFile = fopen(cha_fil.c_str(), "wb"); diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc index 260100732d..f2b8301a46 100644 --- a/crawl-ref/source/food.cc +++ b/crawl-ref/source/food.cc @@ -46,6 +46,7 @@ #include "religion.h" #include "skills2.h" #include "spells2.h" +#include "state.h" #include "stuff.h" #include "transfor.h" #include "tutorial.h" @@ -490,19 +491,23 @@ bool eat_food(bool run_hook) if (you.is_undead == US_UNDEAD) { mpr("You can't eat."); + crawl_state.zero_turns_taken(); return (false); } if (you.hunger >= 11000) { mpr("You're too full to eat anything."); + crawl_state.zero_turns_taken(); return (false); } // If user hook ran, we don't know whether something // was eaten or not... if (run_hook && userdef_eat_food()) + { return (false); + } if (igrd[you.x_pos][you.y_pos] != NON_ITEM) { diff --git a/crawl-ref/source/invent.cc b/crawl-ref/source/invent.cc index 23d47a1504..696dbf5bff 100644 --- a/crawl-ref/source/invent.cc +++ b/crawl-ref/source/invent.cc @@ -1165,6 +1165,7 @@ int prompt_invent_item( const char *prompt, { if (count) *count = items[0].quantity; + redraw_screen(); mesclr( true ); } diff --git a/crawl-ref/source/it_use3.cc b/crawl-ref/source/it_use3.cc index 417fd7f2d7..ac3c5f10e5 100644 --- a/crawl-ref/source/it_use3.cc +++ b/crawl-ref/source/it_use3.cc @@ -49,6 +49,7 @@ #include "spl-book.h" #include "spl-cast.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "view.h" @@ -379,6 +380,7 @@ bool evoke_wielded( void ) else if (wield == -1) { mpr("You aren't wielding anything!"); + crawl_state.zero_turns_taken(); return (false); } @@ -708,6 +710,8 @@ bool evoke_wielded( void ) if (!unevokable) you.turn_is_over = true; + else + crawl_state.zero_turns_taken(); return (did_work); } // end evoke_wielded() diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index 76a3aa313a..4970d4fc81 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -69,6 +69,7 @@ #include "spl-book.h" #include "spl-cast.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "transfor.h" #include "tutorial.h" @@ -3178,6 +3179,8 @@ bool drink_fountain() grd[you.x_pos][you.y_pos] = DNGN_DRY_FOUNTAIN_I; else grd[you.x_pos][you.y_pos] = DNGN_DRY_FOUNTAIN_II; + + crawl_state.cancel_cmd_repeat(); } you.turn_is_over = true; @@ -3553,6 +3556,7 @@ void read_scroll(void) if (scroll.base_type != OBJ_BOOKS && scroll.base_type != OBJ_SCROLLS) { mpr("You can't read that!"); + crawl_state.zero_turns_taken(); return; } @@ -3566,6 +3570,7 @@ void read_scroll(void) if (silenced(you.x_pos, you.y_pos)) { mpr("Magic scrolls do not work when you're silenced!"); + crawl_state.zero_turns_taken(); return; } diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index bad4a28d7a..e26558acdc 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -68,6 +68,7 @@ #include "spl-util.h" #include "stuff.h" #include "stash.h" +#include "state.h" #include "terrain.h" #include "transfor.h" #include "tutorial.h" @@ -156,6 +157,8 @@ static bool item_ok_to_clean(int item) // unsuccessful cleanup (should be exceedingly rare!) int cull_items(void) { + crawl_state.cancel_cmd_repeat(); + // XXX: Not the prettiest of messages, but the player // deserves to know whenever this kicks in. -- bwr mpr( "Too many items on level, removing some.", MSGCH_WARN ); @@ -261,6 +264,12 @@ bool dec_inv_item_quantity( int obj, int amount ) you.inv[obj].quantity = 0; ret = true; + + // If we're repeating a command, the repetitions used up the + // item stack being repeated on, so stop rather than move onto + // the next stack. + crawl_state.cancel_cmd_repeat(); + crawl_state.cancel_cmd_again(); } else { @@ -280,6 +289,11 @@ bool dec_mitm_item_quantity( int obj, int amount ) if (mitm[obj].quantity <= amount) { destroy_item( obj ); + // If we're repeating a command, the repetitions used up the + // item stack being repeated on, so stop rather than move onto + // the next stack. + crawl_state.cancel_cmd_repeat(); + crawl_state.cancel_cmd_again(); return (true); } diff --git a/crawl-ref/source/macro.cc b/crawl-ref/source/macro.cc index 219a43943e..803252ad7e 100644 --- a/crawl-ref/source/macro.cc +++ b/crawl-ref/source/macro.cc @@ -45,14 +45,15 @@ #include <cstdlib> #include "cio.h" +#include "delay.h" #include "externs.h" #include "message.h" +#include "state.h" #include "stuff.h" // for trim_string: #include "initfile.h" -typedef std::deque<int> keyseq; typedef std::deque<int> keybuf; typedef std::map<keyseq,keyseq> macromap; @@ -73,6 +74,8 @@ static keybuf Buffer; #define USERFUNCBASE -10000 static std::vector<std::string> userfunctions; +static std::vector<key_recorder*> recorders; + inline int userfunc_index(int key) { int index = (key <= USERFUNCBASE? USERFUNCBASE - key : -1); @@ -406,7 +409,7 @@ static void macro_del( macromap &mapref, keyseq key ) * Adds keypresses from a sequence into the internal keybuffer. Ignores * macros. */ -static void macro_buf_add( const keyseq &actions, bool reverse = false ) +void macro_buf_add( const keyseq &actions, bool reverse) { keyseq act; bool need_more_reset = false; @@ -426,14 +429,33 @@ static void macro_buf_add( const keyseq &actions, bool reverse = false ) Buffer.insert( reverse? Buffer.begin() : Buffer.end(), act.begin(), act.end() ); -} + + if (reverse) + { + for (int i = 0, size_i = recorders.size(); i < size_i; i++) + for (int j = act.size() - 1 ; j >= 0; j--) + recorders[i]->add_key(act[j], reverse); + } + else + { + for (int i = 0, size_i = recorders.size(); i < size_i; i++) + for (int j = 0, size_j = act.size(); j < size_j ; j++) + recorders[i]->add_key(act[j]); + } +} /* * Adds a single keypress into the internal keybuffer. */ -void macro_buf_add( int key ) +void macro_buf_add( int key, bool reverse ) { - Buffer.push_back( key ); + if (reverse) + Buffer.push_front( key ); + else + Buffer.push_back( key ); + + for (int i = 0, size = recorders.size(); i < size; i++) + recorders[i]->add_key(key, reverse); } @@ -491,6 +513,13 @@ static void macro_buf_add_long( keyseq actions, } } +static int macro_keys_left = -1; + +bool is_processing_macro() +{ + return (macro_keys_left >= 0); +} + /* * Command macros are only applied from the immediate front of the * buffer, and only when the game is expecting a command. @@ -506,11 +535,23 @@ static void macro_buf_apply_command_macro( void ) if (result.size() > 0) { + for (int i = 0, size_i = recorders.size(); i < size_i; i++) + recorders[i]->remove_trigger_keys(tmp.size()); + // Found macro, remove match from front: for (unsigned int i = 0; i < tmp.size(); i++) + { Buffer.pop_front(); + if (macro_keys_left >= 0) + macro_keys_left--; + } + + if (macro_keys_left == -1) + macro_keys_left = 0; + macro_keys_left += result.size(); macro_buf_add(result, true); + break; } @@ -519,17 +560,27 @@ static void macro_buf_apply_command_macro( void ) } /* - * Removes the earlies keypress from the keybuffer, and returns its + * Removes the earliest keypress from the keybuffer, and returns its * value. If buffer was empty, returns -1; */ static int macro_buf_get( void ) { if (Buffer.size() == 0) + { + // If we're trying to fetch a new keystroke, then the processing + // of the previous keystroke is complete. + if (macro_keys_left == 0) + macro_keys_left = -1; + return (-1); + } int key = Buffer.front(); Buffer.pop_front(); - + + if (macro_keys_left >= 0) + macro_keys_left--; + return (key); } @@ -577,8 +628,17 @@ void macro_save( void ) static keyseq getch_mul( int (*rgetch)() = NULL ) { keyseq keys; - int a; + int a; + // Something's gone wrong with replaying keys if crawl needs to + // get new keys from the user. + if (crawl_state.is_replaying_keys()) + { + mpr("(Key replay ran out of keys)"); + crawl_state.cancel_cmd_repeat(); + crawl_state.cancel_cmd_again(); + } + if (!rgetch) rgetch = m_getch; @@ -644,7 +704,26 @@ int getch_with_command_macros( void ) */ void flush_input_buffer( int reason ) { - if (Options.flush_input[ reason ]) + ASSERT(reason != FLUSH_KEY_REPLAY_CANCEL || + crawl_state.is_replaying_keys()); + + ASSERT(reason != FLUSH_ABORT_MACRO || is_processing_macro()); + + // Any attempt to flush means that the processing of the previously + // fetched keystroke is complete. + if (macro_keys_left == 0) + macro_keys_left = -1; + + if (crawl_state.is_replaying_keys() && reason != FLUSH_ABORT_MACRO + && reason != FLUSH_KEY_REPLAY_CANCEL && + reason != FLUSH_REPLAY_SETUP_FAILURE) + { + return; + } + + if (Options.flush_input[ reason ] || reason == FLUSH_ABORT_MACRO + || reason == FLUSH_KEY_REPLAY_CANCEL + || reason == FLUSH_REPLAY_SETUP_FAILURE) { while (!Buffer.empty()) { @@ -653,6 +732,7 @@ void flush_input_buffer( int reason ) if (key == KEY_MACRO_ENABLE_MORE) Options.show_more_prompt = true; } + macro_keys_left = -1; } } @@ -822,8 +902,103 @@ bool is_synthetic_key(int key) case KEY_MACRO_ENABLE_MORE: case KEY_MACRO_DISABLE_MORE: case KEY_MACRO_MORE_PROTECT: + case KEY_REPEAT_KEYS: return (true); default: return (false); } } + +key_recorder::key_recorder(key_recorder_callback cb, void* cb_data) + : paused(false), call_back(cb), call_back_data(cb_data) +{ + keys.clear(); + macro_trigger_keys.clear(); +} + +void key_recorder::add_key(int key, bool reverse) +{ + if (paused) + return; + + if (call_back) + { + // Don't record key if true + if ((*call_back)(this, key, reverse)) + return; + } + + if (reverse) + keys.push_front(key); + else + keys.push_back(key); +} + +void key_recorder::remove_trigger_keys(int num_keys) +{ + ASSERT(num_keys >= 1); + + if (paused) + return; + + for (int i = 0; i < num_keys; i++) + { + ASSERT(keys.size() >= 1); + + int key = keys[keys.size() - 1]; + + if (call_back) + { + // Key wasn't recorded in the first place, so no need to remove + // it + if ((*call_back)(this, key, true)) + continue; + } + + macro_trigger_keys.push_front(key); + keys.pop_back(); + } +} + +void key_recorder::clear() +{ + keys.clear(); + macro_trigger_keys.clear(); +} + +void add_key_recorder(key_recorder* recorder) +{ + for (int i = 0, size = recorders.size(); i < size; i++) + ASSERT(recorders[i] != recorder); + + recorders.push_back(recorder); +} + +void remove_key_recorder(key_recorder* recorder) +{ + std::vector<key_recorder*>::iterator i; + + for(i = recorders.begin(); i != recorders.end(); i++) + if (*i == recorder) + { + recorders.erase(i); + return; + } + + end(1, true, "remove_key_recorder(): recorder not found\n"); +} + +// Add macro trigger keys to beginning of the buffer, then expand +// them. +void insert_macro_into_buff(const keyseq& keys) +{ + for (int i = (int) keys.size() - 1; i >= 0; i--) + macro_buf_add(keys[i], true); + + macro_buf_apply_command_macro(); +} + +int get_macro_buf_size() +{ + return (Buffer.size()); +} diff --git a/crawl-ref/source/macro.h b/crawl-ref/source/macro.h index ff1c01b076..90d0557cda 100644 --- a/crawl-ref/source/macro.h +++ b/crawl-ref/source/macro.h @@ -14,6 +14,8 @@ #ifndef MACRO_H #define MACRO_H +#include <deque> + #ifndef MACRO_CC #undef getch @@ -30,6 +32,28 @@ enum KeymapContext { KC_CONTEXT_COUNT // Must always be the last }; +class key_recorder; +typedef bool (*key_recorder_callback)(key_recorder *recorder, + int &ch, bool reverse); +typedef std::deque<int> keyseq; + +class key_recorder { +public: + bool paused; + keyseq keys; + keyseq macro_trigger_keys; + key_recorder_callback call_back; + void* call_back_data; + +public: + key_recorder(key_recorder_callback cb = NULL, + void* cb_data = NULL); + + void add_key(int key, bool reverse = false); + void remove_trigger_keys(int num_keys); + void clear(); +}; + int getchm(int (*rgetch)() = NULL); // keymaps applied (ie for prompts) int getchm(KeymapContext context, int (*rgetch)() = NULL); @@ -43,11 +67,21 @@ void macro_save(void); void macro_userfn(const char *keys, const char *registryname); -void macro_buf_add(int key); +void macro_buf_add(int key, bool reverse = false); +void macro_buf_add(const keyseq &actions, bool reverse = false ); bool is_userfunction(int key); bool is_synthetic_key(int key); const char *get_userfunction(int key); +void add_key_recorder(key_recorder* recorder); +void remove_key_recorder(key_recorder* recorder); + +bool is_processing_macro(); + +void insert_macro_into_buff(const keyseq& keys); + +int get_macro_buf_size(); + #endif diff --git a/crawl-ref/source/makefile.obj b/crawl-ref/source/makefile.obj index cc3e36a36f..8819ab5b27 100644 --- a/crawl-ref/source/makefile.obj +++ b/crawl-ref/source/makefile.obj @@ -73,6 +73,7 @@ spl-book.o \ spl-cast.o \ spl-util.o \ sqldbm.o \ +state.o \ stash.o \ stuff.o \ tags.o \ diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc index c09c1056b1..bd3abfdc82 100644 --- a/crawl-ref/source/message.cc +++ b/crawl-ref/source/message.cc @@ -728,6 +728,14 @@ void more(void) return; } #endif + + if (crawl_state.is_repeating_cmd() + && !crawl_state.cmd_repeat_start) + { + mesclr(); + return; + } + if (Options.show_more_prompt && !suppress_messages) { char keypress = 0; diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index dcf7df5bdd..9163f37b87 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -43,16 +43,19 @@ #include "clua.h" #include "cloud.h" #include "delay.h" +#include "direct.h" #include "dgnevent.h" #include "direct.h" #include "dungeon.h" #include "files.h" #include "food.h" +#include "format.h" #include "hiscores.h" #include "it_use2.h" #include "itemprop.h" #include "items.h" #include "lev-pand.h" +#include "macro.h" #include "message.h" #include "mon-util.h" #include "monstuff.h" @@ -557,9 +560,11 @@ void up_stairs(dungeon_feature_type force_stair) ouch(INSTANT_DEATH, 0, KILLED_BY_LEAVING); } - you.prev_targ = MHITNOT; + you.prev_targ = MHITNOT; you.pet_target = MHITNOT; + you.prev_grd_targ = coord_def(0, 0); + if (player_in_branch( BRANCH_VESTIBULE_OF_HELL )) { mpr("Thank you for visiting Hell. Please come again soon."); @@ -803,9 +808,11 @@ void down_stairs( int old_level, dungeon_feature_type force_stair ) you.level_type = LEVEL_DUNGEON; } - you.prev_targ = MHITNOT; + you.prev_targ = MHITNOT; you.pet_target = MHITNOT; + you.prev_grd_targ = coord_def(0, 0); + if (stair_find == DNGN_ENTER_HELL) { you.where_are_you = BRANCH_VESTIBULE_OF_HELL; @@ -1623,4 +1630,3 @@ int speed_to_duration(int speed) return div_rand_round(100, speed); } - diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h index 48145be182..7a192db181 100644 --- a/crawl-ref/source/misc.h +++ b/crawl-ref/source/misc.h @@ -19,6 +19,8 @@ struct bolt; struct dist; +struct activity_interrupt_data; + // last updated 08jan2001 {gdl} /* *********************************************************************** @@ -116,4 +118,7 @@ int speed_to_duration(int speed); bool scramble(void); +bool interrupt_cmd_repeat( activity_interrupt_type ai, + const activity_interrupt_data &at ); + #endif diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index db65a5a960..47ecdc7e8d 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -58,6 +58,7 @@ #include "spl-util.h" #include "spells2.h" #include "spells4.h" +#include "state.h" #include "stuff.h" #include "terrain.h" #include "traps.h" @@ -464,7 +465,13 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) } if (you.prev_targ == monster_killed) + { you.prev_targ = MHITNOT; + crawl_state.cancel_cmd_repeat(); + } + + if (killer == KILL_YOU) + crawl_state.cancel_cmd_repeat(); const bool pet_kill = is_pet_kill(killer, i); @@ -5313,6 +5320,17 @@ bool monster_descriptor(int which_class, unsigned char which_descriptor) bool message_current_target() { + + if (crawl_state.is_replaying_keys()) + { + if (you.prev_targ == MHITNOT || you.prev_targ == MHITYOU) + return false; + + const monsters *montarget = &menv[you.prev_targ]; + return (you.prev_targ != MHITNOT && you.prev_targ != MHITYOU + && mons_near(montarget) && player_monster_visible(montarget)); + } + if (you.prev_targ != MHITNOT && you.prev_targ != MHITYOU) { const monsters *montarget = &menv[you.prev_targ]; diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index d432fa0e86..3d992a3eb1 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -60,6 +60,7 @@ #include "spells3.h" #include "spl-util.h" #include "spells4.h" +#include "state.h" #include "stuff.h" #include "terrain.h" #include "transfor.h" @@ -4168,6 +4169,8 @@ bool enough_hp(int minimum, bool suppress_msg) if (!suppress_msg) mpr("You haven't enough vitality at the moment."); + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); return false; } @@ -4181,6 +4184,8 @@ bool enough_mp(int minimum, bool suppress_msg) if (!suppress_msg) mpr("You haven't enough magic at the moment."); + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); return false; } @@ -5022,6 +5027,8 @@ void player::init() prev_targ = MHITNOT; pet_target = MHITNOT; + prev_grd_targ = coord_def(0, 0); + x_pos = 0; y_pos = 0; @@ -6044,3 +6051,4 @@ std::vector<PlaceInfo> player::get_all_place_info(bool visited_only, return list; } + diff --git a/crawl-ref/source/spells1.cc b/crawl-ref/source/spells1.cc index 7b71d46c16..bd82ad3684 100644 --- a/crawl-ref/source/spells1.cc +++ b/crawl-ref/source/spells1.cc @@ -45,6 +45,7 @@ #include "spells3.h" #include "spells4.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "terrain.h" #include "transfor.h" @@ -65,6 +66,14 @@ int blink(int pow, bool high_level_controlled_blink) { dist beam; + if (crawl_state.is_repeating_cmd()) + { + crawl_state.cant_cmd_repeat("You can't repeat controlled blinks."); + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + return(1); + } + // yes, there is a logic to this ordering {dlb}: if (scan_randarts(RAP_PREVENT_TELEPORTATION)) mpr("You feel a weird sense of stasis."); @@ -143,6 +152,9 @@ int blink(int pow, bool high_level_controlled_blink) } } + crawl_state.cancel_cmd_again(); + crawl_state.cancel_cmd_repeat(); + return (1); } // end blink() diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index f4fe9263c6..fcb9f7c09e 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -40,6 +40,7 @@ #include "religion.h" #include "spl-cast.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #define SPELLBOOK_SIZE 8 @@ -1477,12 +1478,14 @@ int staff_spell( int staff ) if (food && (you.hunger_state <= HS_STARVING || you.hunger <= food)) { mpr("You don't have the energy to cast that spell."); + crawl_state.zero_turns_taken(); return (-1); } if (istaff.plus < mana) { mpr("The rod doesn't have enough magic points."); + crawl_state.zero_turns_taken(); // Don't lose a turn for trying to evoke without enough MP - that's // needlessly cruel for an honest error. return (-1); @@ -1491,12 +1494,16 @@ int staff_spell( int staff ) if (you.experience_level < diff) { mprf("You need to be at least level %d to use that.", diff); + crawl_state.zero_turns_taken(); return (-1); } // All checks passed, we can cast the spell if (your_spells(spell, powc, false) == SPRET_ABORT) + { + crawl_state.zero_turns_taken(); return (-1); + } make_hungry( food, true ); diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 4057290db2..1fa829b87b 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -51,6 +51,7 @@ #include "spells4.h" #include "spl-book.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "transfor.h" #include "view.h" @@ -587,6 +588,7 @@ bool cast_a_spell() if (!you.spell_no) { mpr("You don't know any spells."); + crawl_state.zero_turns_taken(); return (false); } @@ -599,6 +601,7 @@ bool cast_a_spell() if (silenced(you.x_pos, you.y_pos)) { mpr("You cannot cast spells when silenced!"); + crawl_state.zero_turns_taken(); more(); return (false); } @@ -629,11 +632,15 @@ bool cast_a_spell() } if (keyin == ESCAPE) + { + canned_msg( MSG_OK ); return (false); + } if (!isalpha(keyin)) { mpr("You don't know that spell."); + crawl_state.zero_turns_taken(); return (false); } @@ -642,6 +649,7 @@ bool cast_a_spell() if (spell == SPELL_NO_SPELL) { mpr("You don't know that spell."); + crawl_state.zero_turns_taken(); return (false); } @@ -665,7 +673,10 @@ bool cast_a_spell() { const spret_type cast_result = your_spells( spell ); if (cast_result == SPRET_ABORT) + { + crawl_state.zero_turns_taken(); return (false); + } exercise_spell( spell, true, cast_result == SPRET_SUCCESS ); did_god_conduct( DID_SPELL_CASTING, 1 + random2(5) ); @@ -1005,6 +1016,7 @@ spret_type your_spells( spell_type spell, int powc, bool allow_fail ) break; case SPELL_DELAYED_FIREBALL: + crawl_state.cant_cmd_repeat("You can't repeat delayed fireball."); // This spell has two main advantages over Fireball: // // (1) The release is instantaneous, so monsters will not @@ -1177,6 +1189,8 @@ spret_type your_spells( spell_type spell, int powc, bool allow_fail ) break; case SPELL_SELECTIVE_AMNESIA: + crawl_state.cant_cmd_repeat("You can't repeat selective amnesia."); + if (!cast_selective_amnesia(false)) return (SPRET_ABORT); break; // Sif Muna power calls with true @@ -1422,6 +1436,7 @@ spret_type your_spells( spell_type spell, int powc, bool allow_fail ) break; case SPELL_TUKIMAS_DANCE: + crawl_state.cant_cmd_repeat("You can't repeat dancing weapon."); dancing_weapon(powc, false); break; @@ -1561,6 +1576,7 @@ spret_type your_spells( spell_type spell, int powc, bool allow_fail ) break; case SPELL_ALTER_SELF: + crawl_state.cant_cmd_repeat("You can't repeat alter self."); if (!enough_hp( you.hp_max / 2, true )) { mpr( "Your body is in too poor a condition " @@ -1586,6 +1602,7 @@ spret_type your_spells( spell_type spell, int powc, bool allow_fail ) break; case SPELL_PORTAL: + crawl_state.cant_cmd_repeat("You can't repeat create portal."); if (portal() == -1) return (SPRET_ABORT); break; @@ -1835,6 +1852,7 @@ spret_type your_spells( spell_type spell, int powc, bool allow_fail ) break; case SPELL_SWAP: + crawl_state.cant_cmd_repeat("You can't swap."); cast_swap(powc); break; diff --git a/crawl-ref/source/state.cc b/crawl-ref/source/state.cc new file mode 100644 index 0000000000..a664a3464b --- /dev/null +++ b/crawl-ref/source/state.cc @@ -0,0 +1,235 @@ +/* + * 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 "mon-util.h" +#include "menu.h" // For print_formatted_paragraph() +#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) +{ + reset_cmd_repeat(); + reset_cmd_again(); +} + +/////////////////////////////////////////////////////////////////////////// +// 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()) + 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 mehtod is called to prevent the "no repeating zero turns +// commands" message that input() generates (in the abscence 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 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) + { + return true; + } + + 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 (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(); +} diff --git a/crawl-ref/source/state.h b/crawl-ref/source/state.h index 2be86b8a76..0f4da3bbbd 100644 --- a/crawl-ref/source/state.h +++ b/crawl-ref/source/state.h @@ -38,14 +38,40 @@ struct game_state void (*terminal_resize_handler)(); void (*terminal_resize_check)(); - 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) - { - } + bool doing_prev_cmd_again; + command_type prev_cmd; + std::deque<int> prev_cmd_keys; + + command_type repeat_cmd; + std::deque<int> repeat_cmd_keys; + bool cmd_repeat_start; + int cmd_repeat_count; + int cmd_repeat_goal; + int prev_cmd_repeat_goal; + int prev_repetition_turn; + bool cmd_repeat_started_unsafe; + + std::vector<std::string> input_line_strs; + unsigned int input_line_curr; + +protected: + void reset_cmd_repeat(); + void reset_cmd_again(); + +public: + game_state(); + + bool is_replaying_keys() const; + + bool is_repeating_cmd() const; + + void cancel_cmd_repeat(std::string reason = ""); + void cancel_cmd_again(std::string reason = ""); + + void cant_cmd_repeat(std::string reason = ""); + void cant_cmd_again(std::string reason = ""); + + void zero_turns_taken(); void check_term_size() const { diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc index 36eb33d543..1b6afe54dd 100644 --- a/crawl-ref/source/stuff.cc +++ b/crawl-ref/source/stuff.cc @@ -621,27 +621,34 @@ void canned_msg(canned_message_type which_message) break; case MSG_TOO_BERSERK: mpr("You are too berserk!"); + crawl_state.cancel_cmd_repeat(); break; case MSG_PRESENT_FORM: mpr("You can't do that in your present form."); + crawl_state.cancel_cmd_repeat(); break; case MSG_NOTHING_CARRIED: mpr("You aren't carrying anything."); + crawl_state.cancel_cmd_repeat(); break; case MSG_CANNOT_DO_YET: mpr("You can't do that yet."); + crawl_state.cancel_cmd_repeat(); break; case MSG_OK: mpr("Okay, then."); + crawl_state.cancel_cmd_repeat(); break; case MSG_UNTHINKING_ACT: mpr("Why would you want to do that?"); + crawl_state.cancel_cmd_repeat(); break; case MSG_SPELL_FIZZLES: mpr("The spell fizzles."); break; case MSG_HUH: mpr("Huh?"); + crawl_state.cancel_cmd_repeat(); break; case MSG_EMPTY_HANDED: mpr("You are now empty-handed."); @@ -656,8 +663,9 @@ void canned_msg(canned_message_type which_message) bool yesno( const char *str, bool safe, int safeanswer, bool clear_after, bool interrupt_delays, bool noprompt ) { - if (interrupt_delays) + if (interrupt_delays && !crawl_state.is_repeating_cmd()) interrupt_activity( AI_FORCE_INTERRUPT ); + for (;;) { if ( !noprompt ) @@ -691,7 +699,9 @@ bool yesno( const char *str, bool safe, int safeanswer, bool clear_after, // like yesno(), but returns 0 for no, 1 for yes, and -1 for quit int yesnoquit( const char* str, bool safe, int safeanswer, bool clear_after ) { - interrupt_activity( AI_FORCE_INTERRUPT ); + if (!crawl_state.is_repeating_cmd()) + interrupt_activity( AI_FORCE_INTERRUPT ); + while (1) { mpr(str, MSGCH_PROMPT); diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h index 6f9e9f70b5..3afd19bab6 100644 --- a/crawl-ref/source/view.h +++ b/crawl-ref/source/view.h @@ -62,6 +62,7 @@ enum element_type void init_char_table(char_set_type set); void init_feature_table(); +void init_monsters_seens(); // last updated 29may2000 {dlb} /* *********************************************************************** |