diff options
Diffstat (limited to 'crawl-ref/source/acr.cc')
-rw-r--r-- | crawl-ref/source/acr.cc | 1211 |
1 files changed, 1018 insertions, 193 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index a32a1f67e4..ee535c1371 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -136,6 +136,7 @@ #include "tutorial.h" #include "view.h" #include "stash.h" +#include "xom.h" crawl_environment env; player you; @@ -148,6 +149,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 +173,15 @@ 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(); + +static void show_commandline_options_help(); +static void wanderer_startup_message(); +static void god_greeting_message( bool game_start ); +static void take_starting_note(); +static void startup_tutorial(); #ifdef DGL_SIMPLE_MESSAGING static void read_messages(); @@ -187,27 +199,8 @@ int main( int argc, char *argv[] ) // parse command line args -- look only for initfile & crawl_dir entries if (!parse_args(argc, argv, true)) { - // print help - puts("Command line options:"); - puts(" -name <string> character name"); - puts(" -race <arg> preselect race (by letter, abbreviation, or name)"); - puts(" -class <arg> preselect class (by letter, abbreviation, or name)"); - puts(" -pizza <string> crawl pizza"); - puts(" -plain don't use IBM extended characters"); - puts(" -dir <path> crawl directory"); - puts(" -rc <file> init file name"); - puts(" -morgue <dir> directory to save character dumps"); - puts(" -macro <dir> directory to save/find macro.txt"); - puts(""); - puts("Command line options override init file options, which override"); - puts("environment options (CRAWL_NAME, CRAWL_PIZZA, CRAWL_DIR, CRAWL_RC)."); - puts(""); - puts("Highscore list options: (Can now be redirected to more, etc)"); - puts(" -scores [N] highscore list"); - puts(" -tscores [N] terse highscore list"); - puts(" -vscores [N] verbose highscore list"); - puts(" -scorefile <filename> scorefile to report on"); - exit(1); + show_commandline_options_help(); + return 1; } // Init monsters up front - needed to handle the mon_glyph option right. @@ -222,7 +215,7 @@ int main( int argc, char *argv[] ) if (Options.sc_entries != 0 || !SysEnv.scorefile.empty()) { hiscores_print_all( Options.sc_entries, Options.sc_format ); - exit(0); + return 0; } else { @@ -235,130 +228,35 @@ int main( int argc, char *argv[] ) // override some options for tutorial init_tutorial_options(); - if (game_start || Options.always_greet) - { - msg::stream << "Welcome, " << you.your_name << " the " - << species_name( you.species,you.experience_level ) - << " " << you.class_name << "." - << std::endl; - // Activate markers only after the welcome message, so the - // player can see any resulting messages. - env.markers.activate_all(); + msg::stream << "Welcome, " << you.your_name << " the " + << species_name( you.species,you.experience_level ) + << " " << you.class_name << "." + << std::endl; - // Starting messages can go here as this should only happen - // at the start of a new game -- bwr - // This message isn't appropriate for Options.always_greet - if (you.char_class == JOB_WANDERER && game_start) - { - int skill_levels = 0; - for (int i = 0; i <= NUM_SKILLS; i++) - skill_levels += you.skills[ i ]; + // Activate markers only after the welcome message, so the + // player can see any resulting messages. + env.markers.activate_all(); - if (skill_levels <= 2) - { - // Demigods and Demonspawn wanderers stand to not be - // able to see any of their skills at the start of - // the game (one or two skills should be easily guessed - // from starting equipment)... Anyways, we'll give the - // player a message to warn them (and give a reason why). -- bwr - mpr("You wake up in a daze, and can't recall much."); - } - } + if (game_start && you.char_class == JOB_WANDERER) + wanderer_startup_message(); - // These need some work -- should make sure that the god's - // name is metioned, else the message might be confusing. - switch (you.religion) - { - case GOD_ZIN: - simple_god_message( " says: Spread the light, my child." ); - break; - case GOD_SHINING_ONE: - simple_god_message( " says: Smite the infidels!" ); - break; - case GOD_KIKUBAAQUDGHA: - case GOD_YREDELEMNUL: - case GOD_NEMELEX_XOBEH: - simple_god_message( " says: Welcome..." ); - break; - case GOD_XOM: - if (game_start) - simple_god_message( " says: A new plaything!" ); - break; - case GOD_VEHUMET: - god_speaks( you.religion, "Let it end in hellfire!"); - break; - case GOD_OKAWARU: - simple_god_message(" says: Welcome, disciple."); - break; - case GOD_MAKHLEB: - god_speaks( you.religion, "Blood and souls for Makhleb!" ); - break; - case GOD_SIF_MUNA: - simple_god_message( " whispers: I know many secrets..."); - break; - case GOD_TROG: - simple_god_message( " says: Kill them all!" ); - break; - case GOD_ELYVILON: - simple_god_message( " says: Go forth and aid the weak!" ); - break; - case GOD_LUGONU: - simple_god_message( " says: Spread carnage and corruption!"); - break; - case GOD_BEOGH: - simple_god_message( - " says: Drown the unbelievers in a sea of blood!"); - break; - default: - break; - } + god_greeting_message( game_start ); - // warn player about their weapon, if unsuitable - wield_warning(false); - } + // warn player about their weapon, if unsuitable + wield_warning(false); if ( game_start ) - { + { if (Options.tutorial_left) - { - // don't allow triggering at game start - Options.tut_just_triggered = true; - // print stats and everything - prep_input(); - msg::streams(MSGCH_TUTORIAL) - << "Press any key to start the tutorial intro, " - "or Escape to skip it." - << std::endl; - const int ch = c_getch(); - - if (ch != ESCAPE) - tut_starting_screen(); - } - - std::ostringstream notestr; - notestr << you.your_name << ", the " - << species_name(you.species,you.experience_level) << " " - << you.class_name - << ", began the quest for the Orb."; - take_note(Note(NOTE_USER_NOTE, 0, 0, notestr.str().c_str())); - - notestr.str(""); - notestr.clear(); - - notestr << "HP: " << you.hp << "/" << you.hp_max - << " MP: " << you.magic_points << "/" << you.max_magic_points; - take_note(Note(NOTE_XP_LEVEL_CHANGE, you.experience_level, 0, - notestr.str().c_str())); + startup_tutorial(); + take_starting_note(); } while (true) - { input(); - } // Should never reach this stage, right? - #ifdef UNIX unixcurses_shutdown(); #endif @@ -366,6 +264,130 @@ int main( int argc, char *argv[] ) return 0; } // end main() +static void show_commandline_options_help() +{ + puts("Command line options:"); + puts(" -name <string> character name"); + puts(" -race <arg> preselect race (by letter, abbreviation, or name)"); + puts(" -class <arg> preselect class (by letter, abbreviation, or name)"); + puts(" -pizza <string> crawl pizza"); + puts(" -plain don't use IBM extended characters"); + puts(" -dir <path> crawl directory"); + puts(" -rc <file> init file name"); + puts(" -morgue <dir> directory to save character dumps"); + puts(" -macro <dir> directory to save/find macro.txt"); + puts(""); + puts("Command line options override init file options, which override"); + puts("environment options (CRAWL_NAME, CRAWL_PIZZA, CRAWL_DIR, CRAWL_RC)."); + puts(""); + puts("Highscore list options: (Can now be redirected to more, etc)"); + puts(" -scores [N] highscore list"); + puts(" -tscores [N] terse highscore list"); + puts(" -vscores [N] verbose highscore list"); + puts(" -scorefile <filename> scorefile to report on"); +} + +static void wanderer_startup_message() +{ + int skill_levels = 0; + for (int i = 0; i < NUM_SKILLS; i++) + skill_levels += you.skills[ i ]; + + if (skill_levels <= 2) + { + // Demigods and Demonspawn wanderers stand to not be + // able to see any of their skills at the start of + // the game (one or two skills should be easily guessed + // from starting equipment)... Anyways, we'll give the + // player a message to warn them (and give a reason why). -- bwr + mpr("You wake up in a daze, and can't recall much."); + } +} + +static void god_greeting_message( bool game_start ) +{ + switch (you.religion) + { + case GOD_ZIN: + simple_god_message( " says: Spread the light, my child." ); + break; + case GOD_SHINING_ONE: + simple_god_message( " says: Smite the infidels!" ); + break; + case GOD_KIKUBAAQUDGHA: + case GOD_YREDELEMNUL: + case GOD_NEMELEX_XOBEH: + simple_god_message( " says: Welcome..." ); + break; + case GOD_XOM: + if (game_start) + simple_god_message( " says: A new plaything!" ); + break; + case GOD_VEHUMET: + simple_god_message(" says: Let it end in hellfire!"); + break; + case GOD_OKAWARU: + simple_god_message(" says: Welcome, disciple."); + break; + case GOD_MAKHLEB: + god_speaks( you.religion, "Blood and souls for Makhleb!" ); + break; + case GOD_SIF_MUNA: + simple_god_message( " whispers: I know many secrets..."); + break; + case GOD_TROG: + simple_god_message( " says: Kill them all!" ); + break; + case GOD_ELYVILON: + simple_god_message( " says: Go forth and aid the weak!" ); + break; + case GOD_LUGONU: + simple_god_message( " says: Spread carnage and corruption!"); + break; + case GOD_BEOGH: + simple_god_message( + " says: Drown the unbelievers in a sea of blood!"); + break; + case GOD_NO_GOD: + case NUM_GODS: + case GOD_RANDOM: + break; + } +} + +static void take_starting_note() +{ + std::ostringstream notestr; + notestr << you.your_name << ", the " + << species_name(you.species,you.experience_level) << " " + << you.class_name + << ", began the quest for the Orb."; + take_note(Note(NOTE_MESSAGE, 0, 0, notestr.str().c_str())); + + notestr.str(""); + notestr.clear(); + + notestr << "HP: " << you.hp << "/" << you.hp_max + << " MP: " << you.magic_points << "/" << you.max_magic_points; + take_note(Note(NOTE_XP_LEVEL_CHANGE, you.experience_level, 0, + notestr.str().c_str())); +} + +static void startup_tutorial() +{ + // don't allow triggering at game start + Options.tut_just_triggered = true; + // print stats and everything + prep_input(); + msg::streams(MSGCH_TUTORIAL) + << "Press any key to start the tutorial intro, or Escape to skip it." + << std::endl; + const int ch = c_getch(); + + if (ch != ESCAPE) + tut_starting_screen(); +} + #ifdef WIZARD static void handle_wizard_command( void ) { @@ -390,16 +412,53 @@ 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 '%': + case 'o': + case 'z': + case 'Z': + break; + + default: + crawl_state.cant_cmd_repeat("You cannot repeat that " + "wizard command."); + return; + } + } + switch (wiz_command) { case '?': - list_commands(true); // tell it to list wizard commands - redraw_screen(); + list_commands(true, 0, true); // tell it to list wizard commands break; case CONTROL('G'): @@ -549,13 +608,41 @@ static void handle_wizard_command( void ) break; + case 'C': + { + // this command isn't very exciting... feel free to replace + i = prompt_invent_item( "(Un)curse which item?", MT_INVLIST, -1 ); + if (i == PROMPT_ABORT) + { + canned_msg( MSG_OK ); + break; + } + + item_def& item(you.inv[i]); + + if (item_cursed(item)) + do_uncurse_item(item); + else + do_curse_item(item); + + break; + } + case 'B': if (you.level_type != LEVEL_ABYSS) - banished( DNGN_ENTER_ABYSS ); + banished( DNGN_ENTER_ABYSS, "wizard command" ); else down_stairs(you.your_level, DNGN_EXIT_ABYSS); break; + case CONTROL('A'): + if (you.level_type == LEVEL_ABYSS) + abyss_teleport(true); + else + mpr("You can only abyss_teleport() inside the Abyss."); + + break; + case 'g': debug_add_skills(); break; @@ -574,21 +661,29 @@ static void handle_wizard_command( void ) you.duration[DUR_POISONING] = 0; you.disease = 0; set_hp( abs(you.hp_max), false ); - set_hunger( 5000 + abs(you.hunger), true ); + set_hunger( 10999, true ); break; case 'H': you.rotting = 0; you.duration[DUR_POISONING] = 0; + if (you.duration[DUR_BEHELD]) + { + you.duration[DUR_BEHELD] = 0; + you.beheld_by.clear(); + } + you.duration[DUR_CONF] = 0; you.disease = 0; inc_hp( 10, true ); set_hp( you.hp_max, false ); - set_hunger( 12000, true ); + set_hunger( 10999, true ); you.redraw_hit_points = 1; break; case 'b': - blink(1000, true); // wizards can always blink + // wizards can always blink, with no restrictions or + // magical contamination. + blink(1000, true, true); break; case '~': @@ -664,23 +759,34 @@ static void handle_wizard_command( void ) case 'P': { - mpr( "Destination for portal? ", MSGCH_PROMPT ); - get_input_line( specs, sizeof( specs ) ); + mpr( "Destination for portal (defaults to 'bazaar')? ", MSGCH_PROMPT ); + if (cancelable_get_line( specs, sizeof( specs ) )) + { + canned_msg( MSG_OK ); + break; + } std::string dst = specs; dst = trim_string(dst); + dst = replace_all(dst, " ", "_"); if (dst == "") - canned_msg( MSG_OK ); - else + dst = "bazaar"; + + if (find_map_by_name(dst) == -1 && + random_map_for_tag(dst, false) == -1) { - grd[you.x_pos][you.y_pos] = DNGN_ENTER_PORTAL_VAULT; - map_wiz_props_marker - *marker = new map_wiz_props_marker(you.pos()); - marker->set_property("dst", dst); - marker->set_property("desc", "wizard portal, dest = " + dst); - env.markers.add(marker); + mprf("No map named '%s' or tagged '%s'.", + dst.c_str(), dst.c_str()); + break; } + + grd[you.x_pos][you.y_pos] = DNGN_ENTER_PORTAL_VAULT; + map_wiz_props_marker + *marker = new map_wiz_props_marker(you.pos()); + marker->set_property("dst", dst); + marker->set_property("desc", "wizard portal, dest = " + dst); + env.markers.add(marker); break; } @@ -809,7 +915,7 @@ static void handle_wizard_command( void ) // Use mpr_comma_separated_list() because the list // might be *LONG*. - mpr_comma_separated_list(prefix, matches, ", ", " and ", + mpr_comma_separated_list(prefix, matches, " and ", ", ", MSGCH_DIAGNOSTICS); return; } @@ -849,6 +955,19 @@ static void handle_wizard_command( void ) break; case '{': + if (testbits(env.level_flags, LFLAG_NOT_MAPPABLE) + || testbits(get_branch_flags(), BFLAG_NOT_MAPPABLE)) + { + if (!yesno("Force level to be mappable? ")) + { + canned_msg( MSG_OK ); + return; + } + + unset_level_flags(LFLAG_NOT_MAPPABLE | LFLAG_NO_MAGIC_MAP); + unset_branch_flags(BFLAG_NOT_MAPPABLE | BFLAG_NO_MAGIC_MAP); + } + magic_mapping(1000, 100, true, true); break; @@ -913,6 +1032,7 @@ static void handle_wizard_command( void ) formatted_mpr(formatted_string::parse_string("Not a <magenta>Wizard</magenta> Command.")); break; } + you.turn_is_over = false; } #endif @@ -988,16 +1108,161 @@ 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_LIST_EQUIPMENT: + 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; +static void drift_player(int move_x, int move_y); /* - 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() { + crawl_state.clear_god_acting(); + check_beholders(); + + 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(); @@ -1041,8 +1306,18 @@ static void input() learned_something_new(TUT_RETREAT_CASTER); } - if ( you.duration[DUR_PARALYSIS] ) + if ( you.cannot_act() ) { + crawl_state.cancel_cmd_repeat("Cannot move, cancelling command " + "repetition."); + + // may sleep walk + if (!you.paralysed() && you.mutation[MUT_DRIFTING] + && (random2(100) <= you.mutation[MUT_DRIFTING] * 5) ) + { + drift_player(0, 0); + } + world_reacts(); return; } @@ -1075,7 +1350,12 @@ 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; + { // Enable the cursor to read input. The cursor stays on while // the command is being processed, so subsidiary prompts @@ -1089,6 +1369,13 @@ 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; + crawl_state.input_line_strs.clear(); + } + if (cmd != CMD_MOUSE_MOVE) c_input_reset(false); @@ -1098,8 +1385,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()) @@ -1115,6 +1411,8 @@ static void input() else viewwindow(true, false); + update_replay_state(); + if (you.num_turns != -1) { PlaceInfo& curr_PlaceInfo = you.get_place_info(); @@ -1176,6 +1474,8 @@ static void input() curr_PlaceInfo += delta; curr_PlaceInfo.assert_validity(); } + + crawl_state.clear_god_acting(); } static bool toggle_flag( bool* flag, const char* flagname ) @@ -1185,11 +1485,26 @@ static bool toggle_flag( bool* flag, const char* flagname ) return *flag; } +static bool stairs_check_beheld() +{ + if (you.duration[DUR_BEHELD] && !you.duration[DUR_CONF]) + { + mprf("You cannot move away from %s!", + menv[you.beheld_by[0]].name(DESC_NOCAP_THE, true).c_str()); + return true; + } + + return false; +} + static void go_downstairs(); static void go_upstairs() { const dungeon_feature_type ygrd = grd(you.pos()); + if (stairs_check_beheld()) + return; + // Allow both < and > to work for Abyss exits. if (ygrd == DNGN_EXIT_ABYSS) { @@ -1228,7 +1543,21 @@ static void go_upstairs() static void go_downstairs() { - if (grid_stair_direction(grd(you.pos())) != CMD_GO_DOWNSTAIRS) + bool shaft = (trap_type_at_xy(you.x_pos, you.y_pos) == TRAP_SHAFT + && grd[you.x_pos][you.y_pos] != DNGN_UNDISCOVERED_TRAP); + + + if (stairs_check_beheld()) + return; + + if (shaft && you.flight_mode() == FL_LEVITATE) + { + mpr("You can't fall through a shaft while levitating."); + return; + } + + if (grid_stair_direction(grd(you.pos())) != CMD_GO_DOWNSTAIRS + && !shaft) { if (grd(you.pos()) == DNGN_STONE_ARCH) mpr("There is nothing on the other side of the stone arch."); @@ -1242,10 +1571,18 @@ static void go_downstairs() mpr("You're held in a net!"); return; } - tag_followers(); // only those beside us right now can follow - start_delay( DELAY_DESCENDING_STAIRS, - 1 + (you.burden_state > BS_UNENCUMBERED), - you.your_level ); + + if (shaft) + { + start_delay( DELAY_DESCENDING_STAIRS, 0, you.your_level ); + } + else + { + tag_followers(); // only those beside us right now can follow + start_delay( DELAY_DESCENDING_STAIRS, + 1 + (you.burden_state > BS_UNENCUMBERED), + you.your_level ); + } } static void experience_check() @@ -1292,8 +1629,6 @@ static void experience_check() void process_command( command_type cmd ) { - - FixedVector < int, 2 > plox; apply_berserk_penalty = true; switch (cmd) @@ -1361,6 +1696,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; @@ -1474,8 +1854,19 @@ void process_command( command_type cmd ) } else if (you.attribute[ATTR_HELD]) { - mpr("You cannot shoot anything while held in a net!"); - break; + const item_def *weapon = you.weapon(); + if (!weapon || !is_range_weapon(*weapon)) + { + mpr("You cannot throw anything while held in a net!"); + break; + } + else if (weapon->sub_type != WPN_BLOWGUN) + { + mprf("You cannot shoot with your %s while held in a net!", + weapon->name(DESC_BASENAME).c_str()); + break; + } + // else shooting is possible } if (Options.tutorial_left) Options.tut_throw_counter++; @@ -1645,6 +2036,10 @@ void process_command( command_type cmd ) mesclr(); break; + case CMD_ANNOTATE_LEVEL: + annotate_level(); + break; + case CMD_EXPLORE: // Start exploring start_explore(Options.explore_greedy); @@ -1660,11 +2055,13 @@ void process_command( command_type cmd ) break; } #endif - plox[0] = 0; - show_map(plox, true); - redraw_screen(); - if (plox[0] > 0) - start_travel(plox[0], plox[1]); + { + coord_def pos; + show_map(pos, true); + redraw_screen(); + if (pos.x > 0) + start_travel(pos.x, pos.y); + } break; case CMD_DISPLAY_KNOWN_OBJECTS: @@ -1700,10 +2097,12 @@ void process_command( command_type cmd ) case CMD_DISPLAY_COMMANDS: if (Options.tutorial_left) + { list_tutorial_help(); + redraw_screen(); + } else - list_commands(false); - redraw_screen(); + list_commands(false, 0, true); break; case CMD_EXPERIENCE_CHECK: @@ -1814,6 +2213,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) @@ -1884,6 +2291,9 @@ static void decrement_durations() if (decrement_a_duration(DUR_BUILDING_RAGE)) go_berserk(false); + if (decrement_a_duration(DUR_SLEEP)) + you.awake(); + // paradox: it both lasts longer & does more damage overall if you're // moving slower. // rationalisation: I guess it gets rubbed off/falls off/etc if you @@ -2110,11 +2520,17 @@ static void decrement_durations() decrement_a_duration( DUR_SURE_BLADE, "The bond with your blade fades away." ); + if ( decrement_a_duration( DUR_BEHELD, "You break out of your daze.", + -1, 0, NULL, MSGCH_RECOVERY )) + { + you.beheld_by.clear(); + } + dec_slow_player(); dec_haste_player(); if (decrement_a_duration(DUR_MIGHT, "You feel a little less mighty now.")) - modify_stat(STAT_STRENGTH, -5, true); + modify_stat(STAT_STRENGTH, -5, true, "might running out"); if (decrement_a_duration(DUR_BERSERKER, "You are no longer berserk.")) { @@ -2327,10 +2743,12 @@ static void check_banished() static void world_reacts() { + crawl_state.clear_god_acting(); + if (you.num_turns != -1) { you.num_turns++; - if (env.turns_on_level + 1 > env.turns_on_level) + if (env.turns_on_level < INT_MAX) env.turns_on_level++; update_turn_count(); } @@ -2338,10 +2756,12 @@ static void world_reacts() run_environment_effects(); - if ( !you.duration[DUR_PARALYSIS] && !you.mutation[MUT_BLURRY_VISION] && + if ( !you.cannot_act() && !you.mutation[MUT_BLURRY_VISION] && (you.mutation[MUT_ACUTE_VISION] >= 2 || random2(50) < you.skills[SK_TRAPS_DOORS]) ) + { search_around(false); // check nonadjacent squares too + } stealth = check_stealth(); @@ -2447,7 +2867,7 @@ static void world_reacts() // food death check: if (you.is_undead != US_UNDEAD && you.hunger <= 500) { - if (!you.duration[DUR_PARALYSIS] && one_chance_in(40)) + if (!you.cannot_act() && one_chance_in(40)) { mpr("You lose consciousness!", MSGCH_FOOD); you.duration[DUR_PARALYSIS] += 5 + random2(8); @@ -2465,7 +2885,7 @@ static void world_reacts() viewwindow(true, false); - if (you.duration[DUR_PARALYSIS] > 0 && any_messages()) + if (you.cannot_act() && any_messages()) more(); spawn_random_monsters(); @@ -2485,8 +2905,6 @@ static void show_message_line(std::string line) std::string sender = line.substr(0, sender_pos); line = line.substr(sender_pos + 1); trim_string(line); - // XXX: Eventually fix mpr so it can do a different colour for - // the sender. formatted_string fs; fs.textcolor(WHITE); fs.cprintf("%s: ", sender.c_str()); @@ -2657,6 +3075,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; @@ -2737,8 +3156,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; @@ -2756,7 +3176,7 @@ command_type keycode_to_command( keycode_type key ) case CONTROL('E'): return CMD_FORGET_STASH; case CONTROL('F'): return CMD_SEARCH_STASHES; case CONTROL('G'): return CMD_INTERLEVEL_TRAVEL; - case CONTROL('I'): return CMD_NO_CMD; + case CONTROL('I'): return CMD_ANNOTATE_LEVEL; case CONTROL('M'): return CMD_NO_CMD; case CONTROL('O'): return CMD_EXPLORE; case CONTROL('P'): return CMD_REPLAY_MESSAGES; @@ -2823,7 +3243,6 @@ static void open_door(int move_x, int move_y, bool check_confused) if (mon != NON_MONSTER && player_can_hit_monster(&menv[mon])) { - if (mons_is_caught(&menv[mon])) { @@ -2848,7 +3267,8 @@ static void open_door(int move_x, int move_y, bool check_confused) return; } - if (grd[dx][dy] >= DNGN_TRAP_MECHANICAL && grd[dx][dy] <= DNGN_TRAP_III) + if (grd[dx][dy] >= DNGN_TRAP_MECHANICAL + && grd[dx][dy] <= DNGN_TRAP_NATURAL) { if (env.cgrid[dx][dy] != EMPTY_CLOUD) { @@ -2997,7 +3417,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) @@ -3145,6 +3564,8 @@ static bool initialise(void) activate_notes(true); + add_key_recorder(&repeat_again_rec); + return (newc); } @@ -3203,8 +3624,49 @@ static void do_berserk_no_combat_penalty(void) } // end do_berserk_no_combat_penalty() -// Called when the player moves by walking/running. Also calls -// attack function and trap function etc when necessary. +void drift_player(int move_x, int move_y) +{ + int drift_dir = -1; + int okay_dirs = 0; + + // don't drift if held in a net + if (you.attribute[ATTR_HELD]) + return; + + for (int i = 0; i < 8; i++) + { + const coord_def drift_delta = Compass[i]; + const coord_def new_pos = you.pos() + drift_delta; + const unsigned short targ_monst = mgrd(new_pos); + + if (you.can_pass_through(new_pos) + && !is_grid_dangerous(grd(new_pos)) + && (targ_monst == NON_MONSTER || + mons_is_submerged(&menv[targ_monst]))) + { + if (one_chance_in(++okay_dirs)) + drift_dir = i; + } + } + + if (okay_dirs > 0) + { + const coord_def drift_delta = Compass[drift_dir]; + const coord_def new_pos = you.pos() + drift_delta; + + if (drift_delta == coord_def(-move_x, -move_y)) + mpr("You drift backwards."); + else if (drift_delta == coord_def(move_x, move_y)) + mpr("You drift forwards."); + else + mpr("You drift."); + + move_player_to_grid(new_pos.x, new_pos.y, true, true, false); + } +} + +// Called when the player moves by walking/running. Also calls attack +// function etc when necessary. static void move_player(int move_x, int move_y) { bool attacking = false; @@ -3229,15 +3691,48 @@ static void move_player(int move_x, int move_y) const int new_targ_x = you.x_pos + move_x; const int new_targ_y = you.y_pos + move_y; if (!in_bounds(new_targ_x, new_targ_y) - || grid_is_solid(grd[new_targ_x][new_targ_y])) + || !you.can_pass_through(new_targ_x, new_targ_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] + const int targ_x = you.x_pos + move_x; + const int targ_y = you.y_pos + move_y; + const dungeon_feature_type targ_grid = grd[ targ_x ][ targ_y ]; + const unsigned short targ_monst = mgrd[ targ_x ][ targ_y ]; + const bool targ_pass = you.can_pass_through(targ_x, targ_y); + + // cannot move away from mermaid but you CAN fight neighbouring squares + if (you.duration[DUR_BEHELD] && !you.duration[DUR_CONF] + && (targ_monst == NON_MONSTER || mons_friendly(&menv[targ_monst]) + || mons_is_submerged(&menv[targ_monst]))) + { + for (unsigned int i = 0; i < you.beheld_by.size(); i++) + { + monsters* mon = &menv[you.beheld_by[i]]; + coord_def pos = mon->pos(); + int olddist = distance(you.x_pos, you.y_pos, pos.x, pos.y); + int newdist = distance(you.x_pos + move_x, you.y_pos + move_y, + pos.x, pos.y); + + if (olddist < newdist) + { + mprf("You cannot move away from %s!", + mon->name(DESC_NOCAP_THE, true).c_str()); + + move_x = 0; + move_y = 0; + return; + } + } + } // end of beholding check + if (you.running.check_stop_running()) { move_x = 0; @@ -3247,12 +3742,6 @@ static void move_player(int move_x, int move_y) return; } - const int targ_x = you.x_pos + move_x; - const int targ_y = you.y_pos + move_y; - const dungeon_feature_type targ_grid = grd[ targ_x ][ targ_y ]; - const unsigned char targ_monst = mgrd[ targ_x ][ targ_y ]; - const bool targ_solid = grid_is_solid(targ_grid); - if (targ_monst != NON_MONSTER && !mons_is_submerged(&menv[targ_monst])) { struct monsters *mon = &menv[targ_monst]; @@ -3267,6 +3756,11 @@ static void move_player(int move_x, int move_y) } else // attack! { + // XXX: Moving into a normal wall does nothing and uses no + // turns or energy, but moving into a wall which contains + // an invisible monster attacks the monster, thus allowing + // the player to figure out which adjacent wall an invis + // monster is in "for free". you_attack( targ_monst, true ); you.turn_is_over = true; @@ -3279,25 +3773,32 @@ static void move_player(int move_x, int move_y) } } - if (!attacking && !targ_solid && moving) + if (!attacking && targ_pass && moving) { you.time_taken *= player_movement_speed(); you.time_taken /= 10; if (!move_player_to_grid(targ_x, targ_y, true, false, swap)) return; + if (you.mutation[MUT_DRIFTING] + && (random2(100) <= you.mutation[MUT_DRIFTING] * 5) ) + { + drift_player(move_x, move_y); + } + move_x = 0; move_y = 0; you.turn_is_over = true; // item_check( false ); request_autopickup(); + } // BCR - Easy doors single move - if (targ_grid == DNGN_CLOSED_DOOR && Options.easy_open) + if (targ_grid == DNGN_CLOSED_DOOR && Options.easy_open && !attacking) open_door(move_x, move_y, false); - else if (targ_solid) + else if (!targ_pass && !attacking) { stop_running(); @@ -3305,6 +3806,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) @@ -3344,3 +3846,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(); +} |