#include "AppHdr.h"
#include <string>
#include <algorithm>
#if !defined(__IBMCPP__)
#include <limits.h>
#endif
#ifdef DEBUG
#include <float.h>
#endif
#include <errno.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <sstream>
#include <iostream>
#ifdef TARGET_OS_DOS
#include <dos.h>
#include <conio.h>
#include <file.h>
#endif
#ifdef USE_UNIX_SIGNALS
#include <signal.h>
#endif
#include "externs.h"
#include "species.h"
#include "abl-show.h"
#include "abyss.h"
#include "artefact.h"
#include "arena.h"
#include "branch.h"
#include "chardump.h"
#include "cio.h"
#include "cloud.h"
#include "clua.h"
#include "command.h"
#include "ctest.h"
#include "crash.h"
#include "database.h"
#include "debug.h"
#include "delay.h"
#include "describe.h"
#include "dlua.h"
#include "directn.h"
#include "dungeon.h"
#include "effects.h"
#include "fight.h"
#include "files.h"
#include "food.h"
#include "godabil.h"
#include "hiscores.h"
#include "initfile.h"
#include "invent.h"
#include "item_use.h"
#include "it_use3.h"
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
#include "lev-pand.h"
#include "los.h"
#include "luaterp.h"
#include "macro.h"
#include "makeitem.h"
#include "mapmark.h"
#include "maps.h"
#include "message.h"
#include "misc.h"
#include "monplace.h"
#include "monstuff.h"
#include "mon-util.h"
#include "mutation.h"
#include "newgame.h"
#include "ng-init.h"
#include "notes.h"
#include "ouch.h"
#include "output.h"
#include "overmap.h"
#include "player.h"
#include "quiver.h"
#include "random.h"
#include "religion.h"
#include "shopping.h"
#include "skills.h"
#include "skills2.h"
#include "spells1.h"
#include "spells2.h"
#include "spells3.h"
#include "spells4.h"
#include "spl-book.h"
#include "spl-cast.h"
#include "spl-util.h"
#include "stash.h"
#include "state.h"
#include "stuff.h"
#include "tags.h"
#include "terrain.h"
#include "transfor.h"
#include "traps.h"
#include "travel.h"
#include "tutorial.h"
#include "view.h"
#include "stash.h"
#include "xom.h"
#ifdef USE_TILE
#include "tiles.h"
#include "tiledef-dngn.h"
#endif
CLua clua(true);
CLua dlua(false); crawl_environment env; player you;
system_environment SysEnv;
game_state crawl_state;
std::string init_file_error;
char info[ INFO_SIZE ];
int stealth;
void world_reacts();
static key_recorder repeat_again_rec;
const struct coord_def Compass[8] =
{
coord_def(0, -1), coord_def(1, -1), coord_def(1, 0), coord_def(1, 1),
coord_def(0, 1), coord_def(-1, 1), coord_def(-1, 0), coord_def(-1, -1),
};
static void _do_berserk_no_combat_penalty(void);
static bool _initialise(void);
static void _input(void);
static void _move_player(int move_x, int move_y);
static void _move_player(coord_def move);
static int _check_adjacent(dungeon_feature_type feat, coord_def& delta);
static void _open_door(coord_def move, bool check_confused = true);
static void _open_door(int x, int y, bool check_confused = true)
{
_open_door(coord_def(x, y), check_confused);
}
static void _close_door(coord_def move);
static void _start_running( int dir, int mode );
static void _prep_input();
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();
#endif
static void _compile_time_asserts();
#ifdef USE_TILE
#include <SDL_main.h>
#endif
int main( int argc, char *argv[] )
{
_compile_time_asserts();
init_crash_handler();
init_keybindings();
get_system_environment();
if (!parse_args(argc, argv, true))
{
_show_commandline_options_help();
return 1;
}
init_monsters();
init_properties();
init_item_name_cache();
init_file_error = read_init_file();
parse_args( argc, argv, false );
if (Options.sc_entries != 0 || !SysEnv.scorefile.empty())
{
hiscores_print_all( Options.sc_entries, Options.sc_format );
return 0;
}
else
{
SysEnv.scorefile.clear();
}
#ifdef USE_TILE
if (!tiles.initialise())
return -1;
#endif
const bool game_start = _initialise();
init_tutorial_options();
msg::stream << "Welcome" << (game_start? "" : " back") << ", "
<< you.your_name << " the "
<< species_name( you.species,you.experience_level )
<< " " << you.class_name << "."
<< std::endl;
env.markers.activate_all();
if (game_start && you.char_class == JOB_WANDERER)
_wanderer_startup_message();
_god_greeting_message( game_start );
wield_warning(false);
_prep_input();
if (game_start)
{
if (Options.tutorial_left)
_startup_tutorial();
_take_starting_note();
}
else
tutorial_load_game();
level_change();
while (true)
_input();
clear_globals_on_exit();
return 0;
}
static void _show_commandline_options_help()
{
puts("Command line options:");
puts(" -help prints this list of options");
puts(" -name <string> character name");
puts(" -species <arg> preselect race (by letter, abbreviation, or name)");
puts(" -job <arg> preselect class (by letter, abbreviation, or name)");
puts(" -plain don't use IBM extended characters");
puts(" -dir <path> crawl directory");
puts(" -rc <file> init file name");
puts(" -rcdir <dir> directory that contains (included) rc files");
puts(" -morgue <dir> directory to save character dumps");
puts(" -macro <dir> directory to save/find macro.txt");
puts(" -version Crawl version (and compilation info)");
puts("");
puts("Command line options override init file options, which override");
puts("environment options (CRAWL_NAME, CRAWL_DIR, CRAWL_RC).");
puts("");
puts(" -extra-opt-first optname=optval");
puts(" -extra-opt-last optname=optval");
puts("");
puts("Acts as if 'optname=optval' was at the top or bottom of the init");
puts("file. Can be used multiple times.");
puts("");
puts("Highscore list options: (Can 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");
puts("");
puts("Arena options: (Stage a tournament between various monsters.)");
puts(" -arena \"<monster list> v <monster list> arena:<arena map>\"");
#if DEBUG_DIAGNOSTICS
puts("");
puts(" -test run test cases in ./test");
#endif
}
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)
{
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: Lead the forces of light to victory!");
break;
case GOD_KIKUBAAQUDGHA:
simple_god_message(" says: Welcome...");
break;
case GOD_YREDELEMNUL:
simple_god_message(" says: Carry the black torch! Rouse the idle dead!");
break;
case GOD_NEMELEX_XOBEH:
simple_god_message(" says: It's all in the cards!");
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_JIYVA:
god_speaks(you.religion, "Slime for the Slime God!");
break;
case GOD_FEAWN:
simple_god_message(" says: Spread life and death.");
break;
case GOD_CHEIBRIADOS:
simple_god_message(" says: Take it easy.");
break;
case GOD_NO_GOD:
case NUM_GODS:
case GOD_RANDOM:
case GOD_NAMELESS:
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();
#ifdef WIZARD
if (you.wizard)
{
notestr << "You started the game in wizard mode.";
take_note(Note(NOTE_MESSAGE, 0, 0, notestr.str().c_str()));
notestr.str("");
notestr.clear();
}
#endif
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()
{
Options.tut_just_triggered = true;
msg::streams(MSGCH_TUTORIAL)
<< "Press any key to start the tutorial intro, or Escape to skip it."
<< std::endl;
flush_prev_message();
const int ch = c_getch();
if (ch != ESCAPE)
tut_starting_screen();
}
#ifdef WIZARD
static void _do_wizard_command(int wiz_command, bool silent_fail)
{
ASSERT(you.wizard);
switch (wiz_command)
{
case '?':
{
const int key = list_wizard_commands(true);
_do_wizard_command(key, true);
return;
}
case CONTROL('D'): wizard_edit_durations(); break;
case CONTROL('F'): debug_fight_statistics(false, true); break;
case CONTROL('G'): save_ghost(true); break;
case CONTROL('H'): wizard_set_hunger_state(); break;
case CONTROL('I'): debug_item_statistics(); break;
case CONTROL('L'): wizard_set_xl(); break;
case CONTROL('T'): debug_terp_dlua(); break;
case 'O': debug_test_explore(); break;
case 'S': wizard_set_skill_level(); break;
case 'A': wizard_set_all_skills(); break;
case 'a': acquirement(OBJ_RANDOM, AQ_WIZMODE); break;
case 'v': wizard_value_artefact(); break;
case '+': wizard_make_object_randart(); break;
case '|': wizard_create_all_artefacts(); break;
case 'C': wizard_uncurse_item(); break;
case 'g': wizard_exercise_skill(); break;
case 'G': wizard_dismiss_all_monsters(); break;
case 'c': wizard_draw_card(); break;
case 'H': wizard_heal(true); break;
case 'h': wizard_heal(false); break;
case 'b': blink(1000, true, true); break;
case '~': wizard_interlevel_travel(); break;
case '"': debug_list_monsters(); break;
case 't': wizard_tweak_object(); break;
case 'T': debug_make_trap(); break;
case '\\': debug_make_shop(); break;
case 'f': debug_fight_statistics(false); break;
case 'F': debug_fight_statistics(true); break;
case 'm': wizard_create_spec_monster(); break;
case 'M': wizard_create_spec_monster_name(); break;
case 'R': wizard_spawn_control(); break;
case 'r': wizard_change_species(); break;
case '>': wizard_place_stairs(true); break;
case '<': wizard_place_stairs(false); break;
case 'P': wizard_create_portal(); break;
case 'L': debug_place_map(); break;
case 'i': wizard_identify_pack(); break;
case 'I': wizard_unidentify_pack(); break;
case 'z': wizard_cast_spec_spell(); break;
case 'Z': wizard_cast_spec_spell_name(); break;
case '(': wizard_create_feature_number(); break;
case ')': wizard_create_feature_name(); break;
case ':': wizard_list_branches(); break;
case '{': wizard_map_level(); break;
case '@': wizard_set_stats(); break;
case '^': wizard_gain_piety(); break;
case '_': wizard_get_religion(); break;
case '\'': wizard_list_items(); break;
case 'd': wizard_level_travel(true); break;
case 'D': wizard_detect_creatures(); break;
case 'u': case 'U': wizard_level_travel(false); break;
case '%': case 'o': wizard_create_spec_object(); break;
case 'x':
you.experience = 1 + exp_needed( 2 + you.experience_level );
level_change();
break;
case 's':
you.exp_available = 20000;
you.redraw_experience = true;
break;
case '$':
you.add_gold(1000);
if (!Options.show_gold_turns)
{
mprf("You now have %d gold piece%s.",
you.gold, you.gold != 1 ? "s" : "");
}
break;
case 'B':
if (you.level_type != LEVEL_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 ']':
if (!wizard_add_mutation())
mpr( "Failure to give mutation." );
break;
case '=':
mprf( "Cost level: %d Skill points: %d Next cost level: %d",
you.skill_cost_level, you.total_skill_points,
skill_cost_needed( you.skill_cost_level + 1 ) );
break;
case 'X':
if (you.religion == GOD_XOM)
xom_acts(abs(you.piety - HALF_MAX_PIETY));
else
xom_acts(coinflip(), random_range(0, HALF_MAX_PIETY));
break;
case 'p':
dungeon_terrain_changed(you.pos(), DNGN_ENTER_PANDEMONIUM, false);
break;
case 'l':
dungeon_terrain_changed(you.pos(), DNGN_ENTER_LABYRINTH, false);
break;
case 'k':
if (you.level_type == LEVEL_LABYRINTH)
change_labyrinth(true);
else
mpr("This only makes sense in a labyrinth!");
break;
default:
if (!silent_fail)
{
formatted_mpr(formatted_string::parse_string(
"Not a <magenta>Wizard</magenta> Command."));
}
break;
}
you.turn_is_over = true;
religion_turn_end();
you.turn_is_over = false;
}
static void _handle_wizard_command( void )
{
int wiz_command;
if (Options.wiz_mode == WIZ_NEVER)
return;
if (!you.wizard)
{
mpr("WARNING: ABOUT TO ENTER WIZARD MODE!", MSGCH_WARN);
#ifndef SCORE_WIZARD_MODE
mpr("If you continue, your game will not be scored!", MSGCH_WARN);
#endif
if (!yesno( "Do you really want to enter wizard mode?", false, 'n' ))
return;
take_note(Note(NOTE_MESSAGE, 0, 0, "Entered wizard mode."));
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)
{
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;
}
}
_do_wizard_command(wiz_command, false);
}
#endif
static void _start_running( int dir, int mode )
{
if (Options.tutorial_events[TUT_SHIFT_RUN] && mode == RMODE_START)
Options.tutorial_events[TUT_SHIFT_RUN] = false;
if (i_feel_safe(true))
you.running.initialise(dir, mode);
}
static bool _recharge_rod( item_def &rod, bool wielded )
{
if (!item_is_rod(rod) || rod.plus >= rod.plus2 || !enough_mp(1, true))
return (false);
const int charge = rod.plus / ROD_CHARGE_MULT;
int rate = ((charge + 1) * ROD_CHARGE_MULT) / 10;
rate *= (10 + skill_bump( SK_EVOCATIONS ));
rate = div_rand_round( rate, 100 );
if (rate < 5)
rate = 5;
else if (rate > ROD_CHARGE_MULT / 2)
rate = ROD_CHARGE_MULT / 2;
if (!wielded)
rate /= 5;
else if (player_shield())
rate /= 2;
if (rod.plus / ROD_CHARGE_MULT != (rod.plus + rate) / ROD_CHARGE_MULT)
{
dec_mp(1);
if (wielded)
you.wield_change = true;
}
rod.plus += rate;
if (rod.plus > rod.plus2)
rod.plus = rod.plus2;
if (wielded && rod.plus == rod.plus2)
{
mpr("Your rod has recharged.");
if (is_resting())
stop_running();
}
return (true);
}
static void _recharge_rods()
{
const int wielded = you.equip[EQ_WEAPON];
if (wielded != -1 && _recharge_rod( you.inv[wielded], true ))
return;
for (int i = 0; i < ENDOFPACK; ++i)
{
if (i != wielded && is_valid_item(you.inv[i])
&& one_chance_in(3)
&& _recharge_rod( you.inv[i], false ))
{
return;
}
}
}
static bool _cmd_is_repeatable(command_type cmd, bool is_again = false)
{
switch (cmd)
{
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_LIST_GOLD:
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_RESISTS_SCREEN:
case CMD_READ_MESSAGES:
case CMD_SEARCH_STASHES:
mpr("You can't repeat informational commands.");
return (false);
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);
case CMD_TOGGLE_AUTOPICKUP:
case CMD_TOGGLE_FRIENDLY_PICKUP:
case CMD_ADJUST_INVENTORY:
case CMD_QUIVER_ITEM:
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_FORGET_STASH:
case CMD_FIX_WAYPOINT:
case CMD_CLEAR_MAP:
case CMD_INSCRIBE_ITEM:
case CMD_MAKE_NOTE:
case CMD_CYCLE_QUIVER_FORWARD:
#ifdef USE_TILE
case CMD_EDIT_PLAYER_TILE:
#endif
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?", false, 'n');
}
return (true);
case CMD_NO_CMD:
mpr("Unknown command, not repeating.");
return (false);
default:
return (true);
}
return (false);
}
bool apply_berserk_penalty = false;
static void _center_cursor()
{
#ifndef USE_TILE
const coord_def cwhere = grid2view(you.pos());
cgotoxy(cwhere.x, cwhere.y);
#endif
}
static void _input()
{
#if defined(USE_UNIX_SIGNALS) && defined(SIGHUP_SAVE) && defined(USE_CURSES)
if (crawl_state.seen_hups)
sighup_save_and_exit();
#endif
crawl_state.clear_mon_acting();
religion_turn_start();
check_beholders();
you.reset_escaped_death();
flush_prev_message();
if (crawl_state.is_replaying_keys() && crawl_state.is_repeating_cmd()
&& kbhit())
{
crawl_state.cancel_cmd_repeat("Key pressed, interrupting command "
"repetition.");
crawl_state.prev_cmd = CMD_NO_CMD;
getchm();
return;
}
update_monsters_in_view();
you.turn_is_over = false;
_prep_input();
const bool player_feels_safe = i_feel_safe();
if (Options.tutorial_left)
{
Options.tut_just_triggered = false;
if (you.attribute[ATTR_HELD])
learned_something_new(TUT_CAUGHT_IN_NET);
else if (player_feels_safe && you.level_type != LEVEL_ABYSS)
{
if (2 * you.hp < you.hp_max
|| 2 * you.magic_points < you.max_magic_points)
{
tutorial_healing_reminder();
}
else if (!you.running
&& Options.tutorial_events[TUT_SHIFT_RUN]
&& you.num_turns >= 200
&& you.hp == you.hp_max
&& you.magic_points == you.max_magic_points)
{
learned_something_new(TUT_SHIFT_RUN);
}
else if (!you.running
&& Options.tutorial_events[TUT_MAP_VIEW]
&& you.num_turns >= 500
&& you.hp == you.hp_max
&& you.magic_points == you.max_magic_points)
{
learned_something_new(TUT_MAP_VIEW);
}
else if (!you.running
&& Options.tutorial_events[TUT_AUTO_EXPLORE]
&& you.num_turns >= 700
&& you.hp == you.hp_max
&& you.magic_points == you.max_magic_points)
{
learned_something_new(TUT_AUTO_EXPLORE);
}
}
else
{
if (2*you.hp < you.hp_max)
learned_something_new(TUT_RUN_AWAY);
if (Options.tutorial_type == TUT_MAGIC_CHAR && you.magic_points < 1)
learned_something_new(TUT_RETREAT_CASTER);
}
}
if (you.cannot_act())
{
if (crawl_state.repeat_cmd != CMD_WIZARD)
{
crawl_state.cancel_cmd_repeat("Cannot move, cancelling command "
"repetition.");
}
world_reacts();
return;
}
if (!you_are_delayed())
reset_more_autoclear();
if (need_to_autopickup())
autopickup();
if (need_to_autoinscribe())
autoinscribe();
handle_delay();
if (you_are_delayed() && current_delay_action() != DELAY_MACRO_PROCESS_KEY)
{
if (you.time_taken)
world_reacts();
return;
}
if (you.turn_is_over)
{
world_reacts();
return;
}
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;
{
flush_prev_message();
clear_macro_process_key_delay();
crawl_state.waiting_for_command = true;
c_input_reset(true);
_center_cursor();
#ifdef USE_TILE
cursor_control con(false);
#else
cursor_control con(true);
#endif
const command_type cmd = _get_next_cmd();
#if defined(USE_UNIX_SIGNALS) && defined(SIGHUP_SAVE) && defined(USE_CURSES)
if (crawl_state.seen_hups)
sighup_save_and_exit();
#endif
crawl_state.waiting_for_command = false;
if (cmd != CMD_PREV_CMD_AGAIN && cmd != CMD_REPEAT_CMD
&& cmd != CMD_NO_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);
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 (cmd == CMD_REPEAT_CMD)
return;
if (cmd == CMD_PREV_CMD_AGAIN)
return;
}
if (need_to_autoinscribe())
autoinscribe();
if (you.turn_is_over)
{
if (apply_berserk_penalty)
_do_berserk_no_combat_penalty();
world_reacts();
}
else
viewwindow(true, false);
_update_replay_state();
if (you.num_turns != -1)
{
PlaceInfo& curr_PlaceInfo = you.get_place_info();
PlaceInfo delta;
delta.turns_total++;
delta.elapsed_total += you.time_taken;
switch (you.running)
{
case RMODE_INTERLEVEL:
delta.turns_interlevel++;
delta.elapsed_interlevel += you.time_taken;
break;
case RMODE_EXPLORE_GREEDY:
case RMODE_EXPLORE:
delta.turns_explore++;
delta.elapsed_explore += you.time_taken;
break;
case RMODE_TRAVEL:
delta.turns_travel++;
delta.elapsed_travel += you.time_taken;
break;
default:
static bool prev_was_rest = false;
if (!you.delay_queue.empty()
&& you.delay_queue.front().type == DELAY_REST)
{
prev_was_rest = true;
}
if (prev_was_rest)
{
delta.turns_resting++;
delta.elapsed_resting += you.time_taken;
}
else
{
delta.turns_other++;
delta.elapsed_other += you.time_taken;
}
if (you.delay_queue.empty()
|| you.delay_queue.front().type != DELAY_REST)
{
prev_was_rest = false;
}
break;
}
you.global_info += delta;
you.global_info.assert_validity();
curr_PlaceInfo += delta;
curr_PlaceInfo.assert_validity();
}
crawl_state.clear_god_acting();
}
static bool _stairs_check_mesmerised()
{
if (you.duration[DUR_MESMERISED] && !you.confused())
{
mprf("You cannot move away from %s!",
menv[you.mesmerised_by[0]].name(DESC_NOCAP_THE, true).c_str());
return (true);
}
return (false);
}
static bool _marker_vetoes_stair()
{
return marker_vetoes_operation("veto_stair");
}
static void _go_downstairs();
static void _go_upstairs()
{
ASSERT(!crawl_state.arena && !crawl_state.arena_suspended);
const dungeon_feature_type ygrd = grd(you.pos());
if (_stairs_check_mesmerised())
return;
if (you.attribute[ATTR_HELD])
{
mpr("You're held in a net!");
return;
}
if (ygrd == DNGN_ENTER_SHOP)
{
if (you.duration[DUR_BERSERKER])
canned_msg(MSG_TOO_BERSERK);
else
shop();
return;
}
else if (get_feature_dchar(ygrd) == DCHAR_ARCH
&& feat_stair_direction(ygrd) != CMD_NO_CMD
&& ygrd != DNGN_ENTER_ZOT)
{
;
}
else if (feat_stair_direction(ygrd) != CMD_GO_UPSTAIRS)
{
if (ygrd == DNGN_STONE_ARCH)
mpr("There is nothing on the other side of the stone arch.");
else if (ygrd == DNGN_ABANDONED_SHOP)
mpr("This shop appears to be closed.");
else
mpr("You can't go up here!");
return;
}
if (!check_annotation_exclusion_warning())
return;
if (_marker_vetoes_stair())
return;
tag_followers(); start_delay(DELAY_ASCENDING_STAIRS,
1 + (you.burden_state > BS_UNENCUMBERED));
}
static void _go_downstairs()
{
ASSERT(!crawl_state.arena && !crawl_state.arena_suspended);
const dungeon_feature_type ygrd = grd(you.pos());
const bool shaft = (get_trap_type(you.pos()) == TRAP_SHAFT
&& ygrd != DNGN_UNDISCOVERED_TRAP);
if (_stairs_check_mesmerised())
return;
if (shaft && you.flight_mode() == FL_LEVITATE)
{
mpr("You can't fall through a shaft while levitating.");
return;
}
if (ygrd == DNGN_ENTER_SHOP)
{
if (you.duration[DUR_BERSERKER])
canned_msg(MSG_TOO_BERSERK);
else
shop();
return;
}
else if (get_feature_dchar(ygrd) == DCHAR_ARCH
&& feat_stair_direction(ygrd) != CMD_NO_CMD
&& ygrd != DNGN_ENTER_ZOT)
{
;
}
else if (feat_stair_direction(ygrd) != CMD_GO_DOWNSTAIRS
&& !shaft)
{
if (ygrd == DNGN_STONE_ARCH)
mpr("There is nothing on the other side of the stone arch.");
else if (ygrd == DNGN_ABANDONED_SHOP)
mpr("This shop appears to be closed.");
else
mpr( "You can't go down here!" );
return;
}
if (you.attribute[ATTR_HELD])
{
mpr("You're held in a net!");
return;
}
if (!check_annotation_exclusion_warning())
return;
if (shaft)
{
start_delay( DELAY_DESCENDING_STAIRS, 0, you.your_level );
}
else
{
if (_marker_vetoes_stair())
return;
tag_followers(); start_delay(DELAY_DESCENDING_STAIRS,
1 + (you.burden_state > BS_UNENCUMBERED),
you.your_level);
}
}
static void _experience_check()
{
mprf("You are a level %d %s %s.",
you.experience_level,
species_name(you.species,you.experience_level).c_str(),
you.class_name);
if (you.experience_level < 27)
{
int xp_needed = (exp_needed(you.experience_level+2)-you.experience)+1;
mprf( "Level %d requires %ld experience (%d point%s to go!)",
you.experience_level + 1,
exp_needed(you.experience_level + 2) + 1,
xp_needed,
(xp_needed > 1) ? "s" : "");
}
else
{
mpr( "I'm sorry, level 27 is as high as you can go." );
mpr( "With the way you've been playing, I'm surprised you got this far." );
}
if (you.real_time != -1)
{
const time_t curr = you.real_time + (time(NULL) - you.start_time);
msg::stream << "Play time: " << make_time_string(curr)
<< " (" << you.num_turns << " turns)"
<< std::endl;
}
#ifdef DEBUG_DIAGNOSTICS
if (wearing_amulet(AMU_THE_GOURMAND))
mprf(MSGCH_DIAGNOSTICS, "Gourmand charge: %d",
you.duration[DUR_GOURMAND]);
mprf(MSGCH_DIAGNOSTICS, "Turns spent on this level: %d",
env.turns_on_level);
#endif
}
static void _print_friendly_pickup_setting(bool was_changed)
{
std::string now = (was_changed? "now " : "");
if (you.friendly_pickup == FRIENDLY_PICKUP_NONE)
{
mprf("Your intelligent allies are %sforbidden to pick up anything at all.",
now.c_str());
}
else if (you.friendly_pickup == FRIENDLY_PICKUP_FRIEND)
{
mprf("Your intelligent allies may %sonly pick up items dropped by allies.",
now.c_str());
}
else if (you.friendly_pickup == FRIENDLY_PICKUP_PLAYER)
{
mprf("Your intelligent allies may %sonly pick up items dropped by you "
"and your allies.", now.c_str());
}
else if (you.friendly_pickup == FRIENDLY_PICKUP_ALL)
{
mprf("Your intelligent allies may %spick up anything they need.",
now.c_str());
}
else
mprf(MSGCH_ERROR, "Your allies%s are collecting bugs!", now.c_str());
}
void process_command( command_type cmd )
{
apply_berserk_penalty = true;
switch (cmd)
{
#ifdef USE_TILE
case CMD_EDIT_PLAYER_TILE:
tiles.draw_doll_edit();
break;
case CMD_TOGGLE_SPELL_DISPLAY:
if (Options.tile_display == TDSP_SPELLS)
Options.tile_display = TDSP_INVENT;
else
Options.tile_display = TDSP_SPELLS;
tiles.update_inventory();
break;
#endif
case CMD_OPEN_DOOR_UP_RIGHT: _open_door( 1, -1); break;
case CMD_OPEN_DOOR_UP: _open_door( 0, -1); break;
case CMD_OPEN_DOOR_UP_LEFT: _open_door(-1, -1); break;
case CMD_OPEN_DOOR_RIGHT: _open_door( 1, 0); break;
case CMD_OPEN_DOOR_DOWN_RIGHT: _open_door( 1, 1); break;
case CMD_OPEN_DOOR_DOWN: _open_door( 0, 1); break;
case CMD_OPEN_DOOR_DOWN_LEFT: _open_door(-1, 1); break;
case CMD_OPEN_DOOR_LEFT: _open_door(-1, 0); break;
case CMD_MOVE_DOWN_LEFT: _move_player(-1, 1); break;
case CMD_MOVE_DOWN: _move_player( 0, 1); break;
case CMD_MOVE_UP_RIGHT: _move_player( 1, -1); break;
case CMD_MOVE_UP: _move_player( 0, -1); break;
case CMD_MOVE_UP_LEFT: _move_player(-1, -1); break;
case CMD_MOVE_LEFT: _move_player(-1, 0); break;
case CMD_MOVE_DOWN_RIGHT: _move_player( 1, 1); break;
case CMD_MOVE_RIGHT: _move_player( 1, 0); break;
case CMD_REST:
if (you.hunger_state == HS_STARVING && !you_min_hunger())
{
mpr("You are too hungry to rest.");
break;
}
if (i_feel_safe())
{
if ((you.hp == you.hp_max || you.species == SP_VAMPIRE
&& you.hunger_state == HS_STARVING)
&& you.magic_points == you.max_magic_points )
{
mpr("You start searching.");
}
else
mpr("You start resting.");
}
_start_running( RDIR_REST, RMODE_REST_DURATION );
break;
case CMD_RUN_DOWN_LEFT:
_start_running( RDIR_DOWN_LEFT, RMODE_START );
break;
case CMD_RUN_DOWN:
_start_running( RDIR_DOWN, RMODE_START );
break;
case CMD_RUN_UP_RIGHT:
_start_running( RDIR_UP_RIGHT, RMODE_START );
break;
case CMD_RUN_UP:
_start_running( RDIR_UP, RMODE_START );
break;
case CMD_RUN_UP_LEFT:
_start_running( RDIR_UP_LEFT, RMODE_START );
break;
case CMD_RUN_LEFT:
_start_running( RDIR_LEFT, RMODE_START );
break;
case CMD_RUN_DOWN_RIGHT:
_start_running( RDIR_DOWN_RIGHT, RMODE_START );
break;
case CMD_RUN_RIGHT:
_start_running( RDIR_RIGHT, RMODE_START );
break;
case CMD_DISABLE_MORE:
Options.show_more_prompt = false;
break;
case CMD_ENABLE_MORE:
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))
{
#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:
if (Options.autopickup_on < 1)
Options.autopickup_on = 1;
else
Options.autopickup_on = 0;
mprf("Autopickup is now %s.", Options.autopickup_on > 0 ? "on" : "off");
break;
case CMD_TOGGLE_FRIENDLY_PICKUP:
{
_print_friendly_pickup_setting(false);
mpr("Change to (d)efault, (n)othing, (f)riend-dropped, (p)layer, "
"or (a)ll? ", MSGCH_PROMPT);
char type = (char) getchm(KMC_DEFAULT);
type = tolower(type);
if (type == 'd')
you.friendly_pickup = Options.default_friendly_pickup;
else if (type == 'n')
you.friendly_pickup = FRIENDLY_PICKUP_NONE;
else if (type == 'f')
you.friendly_pickup = FRIENDLY_PICKUP_FRIEND;
else if (type == 'p')
you.friendly_pickup = FRIENDLY_PICKUP_PLAYER;
else if (type == 'a')
you.friendly_pickup = FRIENDLY_PICKUP_ALL;
else
{
canned_msg( MSG_OK );
break;
}
_print_friendly_pickup_setting(true);
break;
}
case CMD_MAKE_NOTE:
make_user_note();
break;
case CMD_READ_MESSAGES:
#ifdef DGL_SIMPLE_MESSAGING
if (SysEnv.have_messages)
_read_messages();
#endif
break;
case CMD_CLEAR_MAP:
if (player_in_mappable_area())
{
mpr("Clearing level map.");
clear_map();
crawl_view.set_player_at(you.pos());
}
break;
case CMD_DISPLAY_OVERMAP: display_overmap(); break;
case CMD_GO_UPSTAIRS: _go_upstairs(); break;
case CMD_GO_DOWNSTAIRS: _go_downstairs(); break;
case CMD_OPEN_DOOR: _open_door(0, 0); break;
case CMD_CLOSE_DOOR: _close_door(coord_def(0, 0)); break;
case CMD_DROP:
drop();
if (Options.stash_tracking >= STM_DROPPED)
StashTrack.add_stash();
break;
case CMD_SEARCH_STASHES:
if (Options.tut_stashes)
Options.tut_stashes = 0;
StashTrack.search_stashes();
break;
case CMD_FORGET_STASH:
if (Options.stash_tracking >= STM_EXPLICIT)
StashTrack.no_stash();
break;
case CMD_BUTCHER:
butchery();
break;
case CMD_DISPLAY_INVENTORY:
get_invent(OSEL_ANY);
break;
case CMD_EVOKE:
if (!evoke_item())
flush_input_buffer( FLUSH_ON_FAILURE );
break;
case CMD_EVOKE_WIELDED:
if (!evoke_item(you.equip[EQ_WEAPON]))
flush_input_buffer( FLUSH_ON_FAILURE );
break;
case CMD_PICKUP:
pickup();
break;
case CMD_INSPECT_FLOOR:
request_autopickup();
break;
case CMD_FULL_VIEW:
full_describe_view();
break;
case CMD_WIELD_WEAPON:
wield_weapon(false);
break;
case CMD_FIRE:
fire_thing();
break;
case CMD_QUIVER_ITEM:
choose_item_for_quiver();
break;
case CMD_THROW_ITEM_NO_QUIVER:
throw_item_no_quiver();
break;
case CMD_WEAR_ARMOUR:
wear_armour();
break;
case CMD_REMOVE_ARMOUR:
{
if (player_in_bat_form())
{
mpr("You can't wear or remove anything in your present form.");
break;
}
int index = 0;
if (armour_prompt("Take off which item?", &index, OPER_TAKEOFF))
takeoff_armour(index);
}
break;
case CMD_REMOVE_JEWELLERY:
remove_ring();
break;
case CMD_WEAR_JEWELLERY:
puton_ring(-1);
break;
case CMD_ADJUST_INVENTORY:
adjust();
break;
case CMD_MEMORISE_SPELL:
if (!learn_spell())
flush_input_buffer( FLUSH_ON_FAILURE );
break;
case CMD_ZAP_WAND:
zap_wand();
break;
case CMD_EAT:
eat_food();
break;
case CMD_USE_ABILITY:
if (!activate_ability())
flush_input_buffer( FLUSH_ON_FAILURE );
break;
case CMD_DISPLAY_MUTATIONS:
display_mutations();
redraw_screen();
break;
case CMD_EXAMINE_OBJECT:
examine_object();
break;
case CMD_PRAY:
pray();
break;
case CMD_DISPLAY_RELIGION:
describe_god( you.religion, true );
redraw_screen();
break;
case CMD_MOVE_NOWHERE:
case CMD_SEARCH:
search_around();
you.turn_is_over = true;
break;
case CMD_QUAFF:
drink();
break;
case CMD_READ:
read_scroll();
break;
case CMD_LOOK_AROUND:
{
mpr("Move the cursor around to observe a square "
"(v - describe square, ? - help)", MSGCH_PROMPT);
struct dist lmove; direction(lmove, DIR_TARGET, TARG_ANY, -1, true);
if (lmove.isValid && lmove.isTarget && !lmove.isCancel
&& !crawl_state.arena_suspended)
{
start_travel( lmove.target );
}
break;
}
case CMD_CAST_SPELL:
case CMD_FORCE_CAST_SPELL:
if (player_in_bat_form()
|| you.attribute[ATTR_TRANSFORMATION] == TRAN_PIG)
{
canned_msg(MSG_PRESENT_FORM);
break;
}
if (scan_artefacts(ARTP_PREVENT_SPELLCASTING))
{
mpr("Something interferes with your magic!");
flush_input_buffer( FLUSH_ON_FAILURE );
break;
}
if (Options.tutorial_left)
Options.tut_spell_counter++;
if (!cast_a_spell(cmd == CMD_CAST_SPELL))
flush_input_buffer( FLUSH_ON_FAILURE );
break;
case CMD_DISPLAY_SPELLS:
inspect_spells();
break;
case CMD_WEAPON_SWAP:
wield_weapon(true);
break;
case CMD_FIX_WAYPOINT:
travel_cache.add_waypoint();
break;
case CMD_INTERLEVEL_TRAVEL:
if (Options.tut_travel)
Options.tut_travel = 0;
if (!can_travel_interlevel())
{
if (you.running.pos == you.pos())
{
mpr("You're already here.");
break;
}
else if (!you.running.pos.x || !you.running.pos.y)
{
mpr("Sorry, you can't auto-travel out of here.");
break;
}
start_travel(you.running.pos);
}
else
start_translevel_travel();
if (you.running)
mesclr();
break;
case CMD_ANNOTATE_LEVEL:
annotate_level();
break;
case CMD_EXPLORE:
if (you.hunger_state == HS_STARVING && !you_min_hunger())
{
mpr("You need to eat something NOW!");
break;
}
start_explore(Options.explore_greedy);
break;
case CMD_DISPLAY_MAP:
if (Options.tutorial_events[TUT_MAP_VIEW])
Options.tutorial_events[TUT_MAP_VIEW] = false;
#if (!DEBUG_DIAGNOSTICS)
if (!player_in_mappable_area())
{
mpr("It would help if you knew where you were, first.");
break;
}
#endif
{
coord_def pos;
#ifdef USE_TILE
std::string str = "Move the cursor to view the level map, or "
"type <w>?</w> for a list of commands.";
print_formatted_paragraph(str);
#endif
show_map(pos, true);
redraw_screen();
#ifdef USE_TILE
mpr("Returning to the game...");
#endif
if (pos.x > 0)
start_travel(pos);
}
break;
case CMD_DISPLAY_KNOWN_OBJECTS:
check_item_knowledge();
break;
case CMD_REPLAY_MESSAGES:
replay_messages();
redraw_screen();
break;
case CMD_REDRAW_SCREEN:
redraw_screen();
break;
case CMD_SAVE_GAME_NOW:
mpr("Saving game... please wait.");
save_game(true);
break;
#ifdef USE_UNIX_SIGNALS
case CMD_SUSPEND_GAME:
clrscr();
#ifndef USE_TILE
unixcurses_shutdown();
kill(0, SIGTSTP);
unixcurses_startup();
#endif
redraw_screen();
break;
#endif
case CMD_DISPLAY_COMMANDS:
list_commands(0, true);
break;
case CMD_EXPERIENCE_CHECK:
_experience_check();
break;
case CMD_SHOUT:
yell();
break;
case CMD_MOUSE_MOVE:
{
const coord_def dest = view2grid(crawl_view.mousep);
if (in_bounds(dest))
terse_describe_square(dest);
break;
}
case CMD_MOUSE_CLICK:
{
c_mouse_event cme = get_mouse_event();
if (cme && crawl_view.in_view_viewport(cme.pos))
{
const coord_def dest = view2grid(cme.pos);
if (cme.left_clicked())
{
if (in_bounds(dest))
start_travel(dest);
}
else if (cme.right_clicked())
{
if (see_cell(dest))
full_describe_square(dest);
else
mpr("You can't see that place.");
}
}
break;
}
case CMD_DISPLAY_CHARACTER_STATUS:
display_char_status();
break;
case CMD_RESISTS_SCREEN:
print_overview_screen();
break;
case CMD_DISPLAY_SKILLS:
show_skills();
redraw_screen();
break;
case CMD_CHARACTER_DUMP:
if (dump_char(you.your_name, false))
mpr("Char dumped successfully.");
else
mpr("Char dump unsuccessful! Sorry about that.");
break;
case CMD_MACRO_ADD:
macro_add_query();
break;
case CMD_CYCLE_QUIVER_FORWARD:
case CMD_CYCLE_QUIVER_BACKWARD:
{
int dir = (cmd == CMD_CYCLE_QUIVER_FORWARD ? +1 : -1);
int cur = you.m_quiver->get_fire_item();
const int next = get_next_fire_item(cur, dir);
#ifdef DEBUG_QUIVER
mprf(MSGCH_DIAGNOSTICS, "next slot: %d, item: %s", next,
next == -1 ? "none" : you.inv[next].name(DESC_PLAIN).c_str());
#endif
if (next != -1)
{
you.m_quiver->on_item_fired(you.inv[next], true);
if (next == cur)
mpr("No other missiles available. Use F to throw any item.");
}
else if (cur == -1)
mpr("No missiles available. Use F to throw any item.");
break;
}
case CMD_LIST_WEAPONS:
list_weapons();
break;
case CMD_LIST_ARMOUR:
list_armour();
break;
case CMD_LIST_JEWELLERY:
list_jewellery();
break;
case CMD_LIST_EQUIPMENT:
get_invent(OSEL_EQUIP);
break;
case CMD_LIST_GOLD:
mprf("You have %d gold piece%s.",
you.gold, you.gold != 1 ? "s" : "");
break;
case CMD_INSCRIBE_ITEM:
prompt_inscribe_item();
break;
#ifdef WIZARD
case CMD_WIZARD:
_handle_wizard_command();
break;
#endif
case CMD_SAVE_GAME:
if (yesno("Save game and exit?", true, 'n'))
save_game(true);
break;
case CMD_QUIT:
if (yes_or_no("Are you sure you want to quit"))
ouch(INSTANT_DEATH, NON_MONSTER, KILLED_BY_QUITTING);
else
canned_msg(MSG_OK);
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)
{
std::string msg = "Unknown command. (For a list of commands type "
"<w>?\?<lightgrey>.)";
print_formatted_paragraph(msg);
}
else mpr("Unknown command.", MSGCH_EXAMINE_FILTER);
break;
}
flush_prev_message();
}
static void _prep_input()
{
you.time_taken = player_speed();
you.shield_blocks = 0;
textcolor(LIGHTGREY);
set_redraw_status( REDRAW_LINE_2_MASK | REDRAW_LINE_3_MASK );
print_stats();
}
static bool _decrement_a_duration(duration_type dur, const char* endmsg = NULL,
int midloss = 0, const char* midmsg = NULL,
msg_channel_type chan = MSGCH_DURATION)
{
if (you.duration[dur] < 1)
return (false);
const int midpoint = get_expiration_threshold(dur);
you.duration[dur]--;
if (you.duration[dur] == midpoint)
{
if (midmsg)
mpr(midmsg, chan);
you.duration[dur] -= midloss;
}
if (you.duration[dur] == 0)
{
if (endmsg)
mpr(endmsg, chan);
return true;
}
return false;
}
static void _decrement_durations()
{
if (wearing_amulet(AMU_THE_GOURMAND))
{
if (you.duration[DUR_GOURMAND] < GOURMAND_MAX && coinflip())
you.duration[DUR_GOURMAND]++;
}
else
you.duration[DUR_GOURMAND] = 0;
if (_decrement_a_duration(DUR_BUILDING_RAGE))
go_berserk(false);
if (_decrement_a_duration(DUR_SLEEP))
you.awake();
dec_napalm_player();
if (_decrement_a_duration(DUR_ICY_ARMOUR,
"Your icy armour evaporates.", coinflip(),
"Your icy armour starts to melt."))
{
you.redraw_armour_class = true;
}
if (_decrement_a_duration(DUR_SILENCE, "Your hearing returns."))
you.attribute[ATTR_WAS_SILENCED] = 0;
_decrement_a_duration(DUR_REPEL_MISSILES,
"You feel less protected from missiles.",
coinflip(),
"Your repel missiles spell is about to expire...");
_decrement_a_duration(DUR_DEFLECT_MISSILES,
"You feel less protected from missiles.",
coinflip(),
"Your deflect missiles spell is about to expire...");
if (_decrement_a_duration(DUR_REGENERATION,
NULL, coinflip(),
"Your skin is crawling a little less now."))
{
remove_regen(you.attribute[ATTR_DIVINE_REGENERATION]);
}
if (you.duration[DUR_PRAYER] > 1)
you.duration[DUR_PRAYER]--;
else if (you.duration[DUR_PRAYER] == 1)
end_prayer();
if (you.duration[DUR_DIVINE_SHIELD] > 0)
{
if (you.duration[DUR_DIVINE_SHIELD] > 1)
{
if (--you.duration[DUR_DIVINE_SHIELD] == 1)
mpr("Your divine shield starts to fade.", MSGCH_DURATION);
}
if (you.duration[DUR_DIVINE_SHIELD] == 1 && !one_chance_in(3))
{
you.redraw_armour_class = true;
if (--you.attribute[ATTR_DIVINE_SHIELD] == 0)
{
you.duration[DUR_DIVINE_SHIELD] = 0;
mpr("Your divine shield fades away.", MSGCH_DURATION);
}
}
}
if (you.duration[DUR_WEAPON_BRAND] > 1)
you.duration[DUR_WEAPON_BRAND]--;
else if (you.duration[DUR_WEAPON_BRAND] == 1)
{
item_def& weapon = *you.weapon();
const int temp_effect = get_weapon_brand(weapon);
you.duration[DUR_WEAPON_BRAND] = 0;
set_item_ego_type(weapon, OBJ_WEAPONS, SPWPN_NORMAL);
std::string msg = weapon.name(DESC_CAP_YOUR);
switch (temp_effect)
{
case SPWPN_VORPAL:
if (get_vorpal_type(weapon) == DVORP_SLICING)
msg += " seems blunter.";
else
msg += " feels lighter.";
break;
case SPWPN_FLAMING:
msg += " goes out.";
break;
case SPWPN_FREEZING:
msg += " stops glowing.";
break;
case SPWPN_VENOM:
msg += " stops dripping with poison.";
break;
case SPWPN_DRAINING:
msg += " stops crackling.";
break;
case SPWPN_DISTORTION:
msg += " seems straighter.";
break;
case SPWPN_PAIN:
msg += " seems less painful.";
break;
default:
msg += " seems inexplicably less special.";
break;
}
mpr(msg.c_str(), MSGCH_DURATION);
you.wield_change = true;
}
if (you.species != SP_VAMPIRE || !player_in_bat_form()
|| you.duration[DUR_TRANSFORMATION] <= 5)
{
if (_decrement_a_duration(DUR_TRANSFORMATION, NULL, random2(3),
"Your transformation is almost over."))
{
untransform();
you.duration[DUR_BREATH_WEAPON] = 0;
}
}
_decrement_a_duration(DUR_BREATH_WEAPON, "You have got your breath back.",
0, NULL, MSGCH_RECOVERY);
_decrement_a_duration(DUR_SWIFTNESS,
"You feel sluggish.", coinflip(),
"You start to feel a little slower.");
_decrement_a_duration(DUR_INSULATION,
"You feel conductive.", coinflip(),
"You start to feel a little less insulated.");
if (_decrement_a_duration(DUR_STONEMAIL,
"Your scaly stone armour disappears.",
coinflip(),
"Your scaly stone armour is starting "
"to flake away."))
{
you.redraw_armour_class = true;
burden_change();
}
if (_decrement_a_duration(DUR_PHASE_SHIFT,
"You are firmly grounded in the material plane once more.",
coinflip(),
"You feel closer to the material plane."))
{
you.redraw_evasion = true;
}
if (_decrement_a_duration(DUR_SEE_INVISIBLE) && !you.can_see_invisible())
mpr("Your eyesight blurs momentarily.", MSGCH_DURATION);
_decrement_a_duration(DUR_TELEPATHY, "You feel less empathic.");
if (_decrement_a_duration(DUR_CONDENSATION_SHIELD))
remove_condensation_shield();
if (you.duration[DUR_CONDENSATION_SHIELD] && player_res_cold() < 0)
{
mpr("You feel very cold.");
ouch(2 + random2avg(13, 2), NON_MONSTER, KILLED_BY_FREEZING);
}
if (_decrement_a_duration(DUR_MAGIC_SHIELD,
"Your magical shield disappears."))
{
you.redraw_armour_class = true;
}
if (_decrement_a_duration(DUR_STONESKIN, "Your skin feels tender."))
you.redraw_armour_class = true;
if (_decrement_a_duration(DUR_TELEPORT))
{
you_teleport_now(true, one_chance_in(5));
untag_followers();
}
_decrement_a_duration(DUR_CONTROL_TELEPORT,
"You feel uncertain.", coinflip(),
"You start to feel a little uncertain.");
if (_decrement_a_duration(DUR_DEATH_CHANNEL,
"Your unholy channel expires.", coinflip(),
"Your unholy channel is weakening."))
{
you.attribute[ATTR_DIVINE_DEATH_CHANNEL] = 0;
}
_decrement_a_duration(DUR_SAGE, "You feel less studious.");
_decrement_a_duration(DUR_STEALTH, "You feel less stealthy.");
_decrement_a_duration(DUR_RESIST_FIRE, "Your fire resistance expires.");
_decrement_a_duration(DUR_RESIST_COLD, "Your cold resistance expires.");
_decrement_a_duration(DUR_RESIST_POISON, "Your poison resistance expires.");
_decrement_a_duration(DUR_SLAYING, "You feel less lethal.");
_decrement_a_duration(DUR_INVIS, "You flicker back into view.",
coinflip(), "You flicker for a moment.");
_decrement_a_duration(DUR_BARGAIN, "You feel less charismatic.");
_decrement_a_duration(DUR_CONF, "You feel less confused.");
_decrement_a_duration(DUR_LOWERED_MR, "You feel more resistant to magic.");
_decrement_a_duration(DUR_SLIMIFY, "You feel less slimy.",
coinflip(), "Your slime is starting to congeal.");
if (you.duration[DUR_PARALYSIS] || you.duration[DUR_PETRIFIED])
{
_decrement_a_duration(DUR_PARALYSIS);
_decrement_a_duration(DUR_PETRIFIED);
if (!you.duration[DUR_PARALYSIS] && !you.duration[DUR_PETRIFIED])
{
mpr("You can move again.", MSGCH_DURATION);
you.redraw_evasion = true;
}
}
_decrement_a_duration(DUR_EXHAUSTED, "You feel less fatigued.");
_decrement_a_duration(DUR_CONFUSING_TOUCH,
((std::string("Your ") + your_hand(true)) +
" stop glowing.").c_str());
_decrement_a_duration(DUR_SURE_BLADE,
"The bond with your blade fades away.");
if (_decrement_a_duration(DUR_MESMERISED, "You break out of your daze.",
0, NULL, MSGCH_RECOVERY))
{
you.mesmerised_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, "might running out");
if (_decrement_a_duration(DUR_AGILITY, "You feel a little less agile now."))
modify_stat(STAT_DEXTERITY, -5, true, "agility running out");
if (_decrement_a_duration(DUR_BRILLIANCE, "You feel a little less clever now."))
modify_stat(STAT_INTELLIGENCE, -5, true, "brilliance running out");
if (_decrement_a_duration(DUR_BERSERKER, "You are no longer berserk."))
{
did_god_conduct(DID_STIMULANTS, 6 + random2(6));
if (you.berserk_penalty != NO_BERSERK_PENALTY)
{
const int chance =
10 + player_mutation_level(MUT_BERSERK) * 25
+ (wearing_amulet(AMU_RAGE) ? 10 : 0)
+ (player_has_spell(SPELL_BERSERKER_RAGE) ? 5 : 0);
if (you.religion == GOD_TROG && x_chance_in_y(you.piety, 150)
&& !player_under_penance())
{
mpr("Trog's vigour flows through your veins.");
}
else if (one_chance_in(chance))
{
mpr("You pass out from exhaustion.", MSGCH_WARN);
you.duration[DUR_PARALYSIS] += roll_dice(1, 4);
}
}
if (!you.duration[DUR_PARALYSIS] && !you.duration[DUR_PETRIFIED])
mpr("You are exhausted.", MSGCH_WARN);
you.berserk_penalty = 0;
int dur = 12 + roll_dice(2, 12);
you.duration[DUR_EXHAUSTED] += dur;
const bool tut_slow = Options.tutorial_events[TUT_YOU_ENCHANTED];
Options.tutorial_events[TUT_YOU_ENCHANTED] = false;
{
no_messages nm;
if (you.duration[DUR_HASTE] > 0)
{
if (wearing_amulet(AMU_RESIST_SLOW))
{
if (you.duration[DUR_HASTE > 6])
{
you.duration[DUR_HASTE] = 2 + coinflip();
mpr("Your extra speed is starting to run out.",
MSGCH_DURATION);
}
else
{
mpr("You feel yourself slow down.", MSGCH_DURATION);
you.duration[DUR_HASTE] = 0;
}
did_god_conduct(DID_HASTY, 3, true);
}
else
{
you.duration[DUR_HASTE] = 0;
}
}
slow_player(dur);
}
make_hungry(700, true);
you.hunger = std::max(50, you.hunger);
you.hp = (you.hp + 1) / 2;
calc_hp();
learned_something_new(TUT_POSTBERSERK);
Options.tutorial_events[TUT_YOU_ENCHANTED] = tut_slow;
}
if (you.duration[DUR_BACKLIGHT] && !--you.duration[DUR_BACKLIGHT]
&& !you.backlit())
{
mpr("You are no longer glowing.", MSGCH_DURATION);
}
if (you.piety < MAX_PIETY && you.duration[DUR_PIETY_POOL] > 0
&& one_chance_in(5))
{
you.duration[DUR_PIETY_POOL]--;
gain_piety(1);
#if DEBUG_DIAGNOSTICS || DEBUG_SACRIFICE || DEBUG_PIETY
mpr("Piety increases by 1 due to piety pool.", MSGCH_DIAGNOSTICS);
if (you.duration[DUR_PIETY_POOL] == 0)
mpr("Piety pool is now empty.", MSGCH_DIAGNOSTICS);
#endif
}
if (!you.permanent_levitation() && !you.permanent_flight())
{
if (_decrement_a_duration(DUR_LEVITATION,
"You float gracefully downwards.",
random2(6),
"You are starting to lose your buoyancy!"))
{
burden_change();
you.duration[DUR_CONTROLLED_FLIGHT] = 0;
move_player_to_grid(you.pos(), false, true, true);
}
}
if (!you.permanent_flight())
{
if (_decrement_a_duration(DUR_CONTROLLED_FLIGHT) && you.airborne())
mpr("You lose control over your flight.", MSGCH_DURATION);
}
if (you.rotting > 0)
{
if (you.species == SP_MUMMY)
you.rotting = 0;
else if (x_chance_in_y(you.rotting, 20))
{
mpr("You feel your flesh rotting away.", MSGCH_WARN);
ouch(1, NON_MONSTER, KILLED_BY_ROTTING);
rot_hp(1);
you.rotting--;
}
}
if (you.species == SP_GHOUL)
{
if (one_chance_in(400))
{
mpr("You feel your flesh rotting away.", MSGCH_WARN);
ouch(1, NON_MONSTER, KILLED_BY_ROTTING);
rot_hp(1);
if (you.rotting > 0)
you.rotting--;
}
}
dec_disease_player();
dec_poison_player();
if (you.duration[DUR_DEATHS_DOOR])
{
if (you.hp > allowed_deaths_door_hp())
{
mpr("Your life is in your own hands once again.", MSGCH_DURATION);
you.duration[DUR_PARALYSIS] += 5 + random2(5);
confuse_player(10 + random2(10));
you.hp_max--;
deflate_hp(you.hp_max, false);
you.duration[DUR_DEATHS_DOOR] = 0;
}
else
{
_decrement_a_duration(DUR_DEATHS_DOOR,
"Your life is in your own hands again!",
random2(6),
"Your time is quickly running out!");
}
}
if (_decrement_a_duration(DUR_DIVINE_VIGOUR))
remove_divine_vigour();
if (_decrement_a_duration(DUR_DIVINE_STAMINA))
remove_divine_stamina();
_decrement_a_duration(DUR_REPEL_STAIRS_MOVE);
_decrement_a_duration(DUR_REPEL_STAIRS_CLIMB);
}
static void _check_banished()
{
if (you.banished)
{
you.banished = false;
if (you.level_type != LEVEL_ABYSS)
{
mpr("You are cast into the Abyss!");
more();
banished(DNGN_ENTER_ABYSS, you.banished_by);
}
you.banished_by.clear();
}
}
static void _check_shafts()
{
for (int i = 0; i < MAX_TRAPS; ++i)
{
trap_def &trap = env.trap[i];
if (trap.type != TRAP_SHAFT)
continue;
ASSERT(in_bounds(trap.pos));
handle_items_on_shaft(trap.pos, true);
}
}
static void _check_sanctuary()
{
if (env.sanctuary_time <= 0)
return;
decrease_sanctuary_radius();
}
static void _regenerate_hp_and_mp()
{
int tmp = you.hit_points_regeneration;
if (you.hp < you.hp_max && !you.disease && !you.duration[DUR_DEATHS_DOOR])
tmp += player_regen();
while (tmp >= 100)
{
inc_hp(1, false);
tmp -= 100;
}
ASSERT( tmp >= 0 && tmp < 100 );
you.hit_points_regeneration = static_cast<unsigned char>(tmp);
tmp = you.magic_points_regeneration;
if (you.magic_points < you.max_magic_points)
tmp += 7 + you.max_magic_points / 2;
while (tmp >= 100)
{
inc_mp(1, false);
tmp -= 100;
}
ASSERT( tmp >= 0 && tmp < 100 );
you.magic_points_regeneration = static_cast<unsigned char>(tmp);
}
void world_reacts()
{
crawl_state.clear_mon_acting();
if (!crawl_state.arena)
{
you.turn_is_over = true;
religion_turn_end();
crawl_state.clear_god_acting();
}
#ifdef USE_TILE
if (Options.tutorial_left)
{
tiles.clear_text_tags(TAG_TUTORIAL);
tiles.place_cursor(CURSOR_TUTORIAL, Region::NO_CURSOR);
}
#endif
if (you.num_turns != -1)
{
if (you.num_turns < LONG_MAX)
you.num_turns++;
if (env.turns_on_level < INT_MAX)
env.turns_on_level++;
update_turn_count();
}
_check_banished();
_check_shafts();
_check_sanctuary();
run_environment_effects();
if (!you.cannot_act() && !player_mutation_level(MUT_BLURRY_VISION)
&& x_chance_in_y(you.skills[SK_TRAPS_DOORS], 50))
{
search_around(false); }
if (!crawl_state.arena)
stealth = check_stealth();
#ifdef DEBUG_STEALTH
mprf(MSGCH_DIAGNOSTICS, "stealth: %d", stealth );
#endif
if (you.attribute[ATTR_NOISES])
noisy_equipment();
if (you.attribute[ATTR_SHADOWS])
shadow_lantern_effect();
if (you.unrand_reacts != 0)
unrand_reacts();
if (!crawl_state.arena && one_chance_in(10))
{
if (player_teleport() > 0 && one_chance_in(100 / player_teleport()))
you_teleport_now( true );
else if (you.level_type == LEVEL_ABYSS && one_chance_in(30))
you_teleport_now( false, true ); }
if (!crawl_state.arena && env.cgrid(you.pos()) != EMPTY_CLOUD)
in_a_cloud();
if (you.level_type == LEVEL_DUNGEON && you.duration[DUR_TELEPATHY])
detect_creatures( 1 + you.duration[DUR_TELEPATHY] / 2, true );
_decrement_durations();
const int food_use = player_hunger_rate();
if (food_use > 0 && you.hunger >= 40)
make_hungry(food_use, true);
_regenerate_hp_and_mp();
_recharge_rods();
viewwindow(true, true);
maybe_update_stashes();
handle_monsters();
_check_banished();
ASSERT(you.time_taken >= 0);
ASSERT(DBL_MAX - you.elapsed_time > you.time_taken);
you.elapsed_time += you.time_taken;
if (you.synch_time <= you.time_taken)
{
handle_time(200 + (you.time_taken - you.synch_time));
you.synch_time = 200;
_check_banished();
}
else
{
const long old_synch_time = you.synch_time;
you.synch_time -= you.time_taken;
if (old_synch_time >= 150 && you.synch_time < 150
|| old_synch_time >= 100 && you.synch_time < 100
|| old_synch_time >= 50 && you.synch_time < 50)
{
spawn_random_monsters();
}
}
manage_clouds();
if (you.duration[DUR_FIRE_SHIELD] > 0)
manage_fire_shield();
if (you.is_undead != US_UNDEAD && you.hunger <= 500)
{
if (!you.cannot_act() && one_chance_in(40))
{
mpr("You lose consciousness!", MSGCH_FOOD);
stop_running();
you.duration[DUR_PARALYSIS] += 5 + random2(8);
if (you.duration[DUR_PARALYSIS] > 13)
you.duration[DUR_PARALYSIS] = 13;
}
if (you.hunger <= 100)
{
mpr("You have starved to death.", MSGCH_FOOD);
ouch(INSTANT_DEATH, NON_MONSTER, KILLED_BY_STARVATION);
}
}
viewwindow(true, false);
if (you.cannot_act() && any_messages()
&& crawl_state.repeat_cmd != CMD_WIZARD)
{
more();
}
#if defined(DEBUG_TENSION) || defined(DEBUG_RELIGION)
if (you.religion != GOD_NO_GOD)
mprf(MSGCH_DIAGNOSTICS, "TENSION = %d", get_tension());
#endif
}
#ifdef DGL_SIMPLE_MESSAGING
static struct stat mfilestat;
static void _show_message_line(std::string line)
{
const std::string::size_type sender_pos = line.find(":");
if (sender_pos == std::string::npos)
mpr(line.c_str());
else
{
std::string sender = line.substr(0, sender_pos);
line = line.substr(sender_pos + 1);
trim_string(line);
formatted_string fs;
fs.textcolor(WHITE);
fs.cprintf("%s: ", sender.c_str());
fs.textcolor(LIGHTGREY);
fs.cprintf("%s", line.c_str());
formatted_mpr(fs, MSGCH_PLAIN, 0);
take_note(Note( NOTE_MESSAGE, MSGCH_PLAIN, 0,
(sender + ": " + line).c_str() ));
}
}
static void _kill_messaging(FILE *mf)
{
if (mf)
fclose(mf);
SysEnv.have_messages = false;
Options.messaging = false;
}
static void _read_each_message()
{
bool say_got_msg = true;
FILE *mf = fopen(SysEnv.messagefile.c_str(), "r+");
if (!mf)
{
mprf(MSGCH_ERROR, "Couldn't read %s: %s", SysEnv.messagefile.c_str(),
strerror(errno));
_kill_messaging(mf);
return;
}
char line[120];
if (!lock_file_handle(mf, F_RDLCK))
{
mprf(MSGCH_ERROR, "Failed to lock %s: %s", SysEnv.messagefile.c_str(),
strerror(errno));
_kill_messaging(mf);
return;
}
while (fgets(line, sizeof line, mf))
{
unlock_file_handle(mf);
const int len = strlen(line);
if (len)
{
if (line[len - 1] == '\n')
line[len - 1] = 0;
if (say_got_msg)
{
mprf(MSGCH_PROMPT, "Your messages:");
say_got_msg = false;
}
_show_message_line(line);
}
if (!lock_file_handle(mf, F_RDLCK))
{
mprf(MSGCH_ERROR, "Failed to lock %s: %s",
SysEnv.messagefile.c_str(),
strerror(errno));
_kill_messaging(mf);
return;
}
}
if (!lock_file_handle(mf, F_WRLCK))
{
mprf(MSGCH_ERROR, "Unable to write lock %s: %s",
SysEnv.messagefile.c_str(),
strerror(errno));
}
if (!ftruncate(fileno(mf), 0))
mfilestat.st_mtime = 0;
unlock_file_handle(mf);
fclose(mf);
SysEnv.have_messages = false;
}
static void _read_messages()
{
_read_each_message();
update_message_status();
}
static void _announce_messages()
{
mprf("Beep! Your pager goes off! Use _ to check your messages.");
}
static void _check_messages()
{
if (!Options.messaging
|| SysEnv.have_messages
|| SysEnv.messagefile.empty()
|| kbhit()
|| (SysEnv.message_check_tick++ % DGL_MESSAGE_CHECK_INTERVAL))
{
return;
}
const bool had_messages = SysEnv.have_messages;
struct stat st;
if (stat(SysEnv.messagefile.c_str(), &st))
{
mfilestat.st_mtime = 0;
return;
}
if (st.st_mtime > mfilestat.st_mtime)
{
if (st.st_size)
SysEnv.have_messages = true;
mfilestat.st_mtime = st.st_mtime;
}
if (SysEnv.have_messages && !had_messages)
{
_announce_messages();
update_message_status();
_center_cursor();
}
}
#endif
static command_type _get_next_cmd()
{
#ifdef DGL_SIMPLE_MESSAGING
_check_messages();
#endif
#if DEBUG_DIAGNOSTICS
you.old_hunger = you.hunger;
#endif
#if DEBUG_ITEM_SCAN
debug_item_scan();
#endif
#if DEBUG_MONS_SCAN
debug_mons_scan();
#endif
const time_t before = time(NULL);
keycode_type keyin = _get_next_keycode();
const time_t after = time(NULL);
if (after - before > IDLE_TIME_CLAMP)
{
you.real_time += int(before - you.start_time) + IDLE_TIME_CLAMP;
you.start_time = after;
}
if (is_userfunction(keyin))
{
run_macro(get_userfunction(keyin).c_str());
return (CMD_NEXT_CMD);
}
return _keycode_to_command(keyin);
}
static command_type _keycode_to_command( keycode_type key )
{
switch ( key )
{
#ifdef USE_TILE
case CK_MOUSE_CMD: return CMD_NEXT_CMD;
#endif
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;
default:
return key_to_command(key, KMC_DEFAULT);
}
}
static keycode_type _get_next_keycode()
{
keycode_type keyin;
flush_input_buffer( FLUSH_BEFORE_COMMAND );
mouse_control mc(MOUSE_MODE_COMMAND);
keyin = unmangle_direction_keys(getch_with_command_macros());
if (!is_synthetic_key(keyin))
mesclr();
return (keyin);
}
static int _check_adjacent(dungeon_feature_type feat, coord_def& delta)
{
int num = 0;
for (adjacent_iterator ai(you.pos(), false); ai; ++ai)
{
if (grd(*ai) == feat)
{
num++;
delta = *ai - you.pos();
}
}
return num;
}
static bool _untrap_target(const coord_def move, bool check_confused)
{
const coord_def target = you.pos() + move;
monsters* mon = monster_at(target);
if (mon && player_can_hit_monster(mon))
{
if (mons_is_caught(mon) && mons_friendly(mon)
&& player_can_open_doors() && !you.confused())
{
const std::string prompt =
make_stringf("Do you want to try to take the net off %s?",
mon->name(DESC_NOCAP_THE).c_str());
if (yesno(prompt.c_str(), true, 'n'))
{
remove_net_from(mon);
return (true);
}
}
you.turn_is_over = true;
you_attack(mon->mindex(), true);
if (you.berserk_penalty != NO_BERSERK_PENALTY)
you.berserk_penalty = 0;
return (true);
}
if (find_trap(target) && grd(target) != DNGN_UNDISCOVERED_TRAP)
{
if (!you.confused())
{
if (!player_can_open_doors())
{
mpr("You can't disarm traps in your present form.");
return (true);
}
if (env.cgrid(target) != EMPTY_CLOUD)
{
mpr("You can't get to that trap right now.");
return (true);
}
}
disarm_trap(target);
return (true);
}
const dungeon_feature_type feat = grd(target);
if (!feat_is_closed_door(feat) || you.confused())
{
switch (feat)
{
case DNGN_OPEN_DOOR:
_close_door(move); return (true);
default:
{
bool do_msg = true;
if (feat_is_solid(feat))
{
dgn_event event(DET_WALL_HIT, target);
event.arg1 = NON_MONSTER;
do_msg =
dungeon_events.fire_vetoable_position_event(event,
target);
}
if (do_msg)
mpr("You swing at nothing.");
make_hungry(3, true);
you.turn_is_over = true;
return (true);
}
}
}
return (false);
}
static void _open_door(coord_def move, bool check_confused)
{
ASSERT(!crawl_state.arena && !crawl_state.arena_suspended);
if (you.attribute[ATTR_HELD])
{
free_self_from_net();
you.turn_is_over = true;
return;
}
if (!move.origin())
{
if (check_confused && you.confused() && !one_chance_in(3))
{
do
{
move.x = random2(3) - 1;
move.y = random2(3) - 1;
}
while (move.origin());
}
if (_untrap_target(move, check_confused))
return;
}
if (!player_can_open_doors())
{
mpr("You can't open doors in your present form.");
return;
}
dist door_move;
if (move.origin())
{
const int num = _check_adjacent(DNGN_CLOSED_DOOR, move)
+ _check_adjacent(DNGN_DETECTED_SECRET_DOOR, move);
if (num == 0)
{
mpr("There's nothing to open nearby.");
return;
}
if (num == 1)
door_move.delta = move;
else
{
mpr("Which direction? ", MSGCH_PROMPT);
direction(door_move, DIR_DIR);
if (!door_move.isValid)
return;
}
}
else
door_move.delta = move;
if (check_confused && you.confused() && !one_chance_in(3))
{
do
{
door_move.delta.x = random2(3) - 1;
door_move.delta.y = random2(3) - 1;
}
while (door_move.delta.origin());
}
const coord_def doorpos = you.pos() + door_move.delta;
const dungeon_feature_type feat = (in_bounds(doorpos) ? grd(doorpos)
: DNGN_UNSEEN);
if (!feat_is_closed_door(feat))
{
if (you.confused())
{
mpr("You swing at nothing.");
make_hungry(3, true);
you.turn_is_over = true;
return;
}
switch (feat)
{
case DNGN_OPEN_DOOR:
mpr("It's already open!");
break;
default:
mpr("There isn't anything that you can open there!");
break;
}
return;
}
std::set<coord_def> all_door;
find_connected_range(doorpos, DNGN_CLOSED_DOOR, DNGN_SECRET_DOOR, all_door);
const char *adj, *noun;
get_door_description(all_door.size(), &adj, &noun);
int skill = you.dex
+ (you.skills[SK_TRAPS_DOORS] + you.skills[SK_STEALTH]) / 2;
if (is_exclude_root(doorpos) && !(check_confused && you.confused()))
{
std::string prompt =
make_stringf("This %s%s is marked as excluded! Open it "
"anyway?", adj, noun);
if (!yesno(prompt.c_str(), true, 'n', true, false))
{
canned_msg(MSG_OK);
interrupt_activity(AI_FORCE_INTERRUPT);
return;
}
}
if (you.duration[DUR_BERSERKER])
{
if (silenced(you.pos()))
mprf("The %s%s flies open!", adj, noun);
else
{
mprf(MSGCH_SOUND, "The %s%s flies open with a bang!", adj, noun);
noisy(15, you.pos());
}
}
else if (one_chance_in(skill) && !silenced(you.pos()))
{
mprf(MSGCH_SOUND, "As you open the %s%s, it creaks loudly!",
adj, noun);
noisy(10, you.pos());
}
else
{
const char* verb = (player_is_airborne() ? "reach down and open"
: "open");
mprf("You %s the %s%s.", verb, adj, noun);
}
bool seen_secret = false;
std::vector<coord_def> excludes;
for (std::set<coord_def>::iterator i = all_door.begin();
i != all_door.end(); ++i)
{
const coord_def& dc = *i;
if (is_terrain_seen(dc))
{
set_envmap_obj(dc, DNGN_OPEN_DOOR);
#ifdef USE_TILE
env.tile_bk_bg(dc) = TILE_DNGN_OPEN_DOOR;
#endif
if (!seen_secret && grd(dc) == DNGN_SECRET_DOOR)
{
seen_secret = true;
dungeon_feature_type secret
= grid_secret_door_appearance(dc);
mprf("That %s was a secret door!",
feature_description(secret, NUM_TRAPS, false,
DESC_PLAIN, false).c_str());
}
}
grd(dc) = DNGN_OPEN_DOOR;
if (is_excluded(dc))
excludes.push_back(dc);
}
update_exclusion_los(excludes);
you.turn_is_over = true;
}
static void _close_door(coord_def move)
{
if (!player_can_open_doors())
{
mpr("You can't close doors in your present form.");
return;
}
if (you.attribute[ATTR_HELD])
{
mpr("You can't close doors while held in a net.");
return;
}
dist door_move;
if (move.origin())
{
int num = _check_adjacent(DNGN_OPEN_DOOR, move);
if (num == 0)
{
mpr("There's nothing to close nearby.");
return;
}
else if (num == 1)
door_move.delta = move;
else
{
mpr("Which direction? ", MSGCH_PROMPT);
direction(door_move, DIR_DIR);
if (!door_move.isValid)
return;
}
}
else
door_move.delta = move;
if (you.confused() && !one_chance_in(3))
{
do
{
door_move.delta.x = random2(3) - 1;
door_move.delta.y = random2(3) - 1;
}
while (door_move.delta.origin());
}
if (door_move.delta.origin())
{
mpr("You can't close doors on yourself!");
return;
}
const coord_def doorpos = you.pos() + door_move.delta;
const dungeon_feature_type feat = (in_bounds(doorpos) ? grd(doorpos)
: DNGN_UNSEEN);
if (feat == DNGN_OPEN_DOOR)
{
std::set<coord_def> all_door;
find_connected_identical(doorpos, grd(doorpos), all_door);
const char *adj, *noun;
get_door_description(all_door.size(), &adj, &noun);
for (std::set<coord_def>::const_iterator i = all_door.begin();
i != all_door.end(); ++i)
{
const coord_def& dc = *i;
if (monsters* mon = monster_at(dc))
{
if (!you.can_see(mon))
{
mprf("Something is blocking the %sway!", noun);
you.turn_is_over = true;
}
else
mprf("There's a creature in the %sway!", noun);
return;
}
if (igrd(dc) != NON_ITEM)
{
mprf("There's something blocking the %sway.", noun);
return;
}
if (you.pos() == dc)
{
mprf("There's a thick-headed creature in the %sway!", noun);
return;
}
}
int skill = you.dex
+ (you.skills[SK_TRAPS_DOORS] + you.skills[SK_STEALTH]) / 2;
if (you.duration[DUR_BERSERKER])
{
if (silenced(you.pos()))
{
mprf("You slam the %s%s shut!", adj, noun);
}
else
{
mprf(MSGCH_SOUND,
"You slam the %s%s shut with an echoing bang!",
adj, noun);
noisy(25, you.pos());
}
}
else if (one_chance_in(skill) && !silenced(you.pos()))
{
mprf(MSGCH_SOUND, "As you close the %s%s, it creaks loudly!",
adj, noun);
noisy(10, you.pos());
}
else
{
const char* verb = player_is_airborne() ? "reach down and close"
: "close";
mprf("You %s the %s%s.", verb, adj, noun);
}
std::vector<coord_def> excludes;
for (std::set<coord_def>::const_iterator i = all_door.begin();
i != all_door.end(); ++i)
{
const coord_def& dc = *i;
grd(dc) = DNGN_CLOSED_DOOR;
if (is_terrain_seen(dc))
{
set_envmap_obj(dc, DNGN_CLOSED_DOOR);
#ifdef USE_TILE
env.tile_bk_bg(dc) = TILE_DNGN_CLOSED_DOOR;
#endif
}
if (is_excluded(dc))
excludes.push_back(dc);
}
update_exclusion_los(excludes);
you.turn_is_over = true;
}
else if (you.confused())
_open_door(door_move.delta);
else
{
switch (feat)
{
case DNGN_CLOSED_DOOR:
case DNGN_DETECTED_SECRET_DOOR:
mpr("It's already closed!");
break;
default:
mpr("There isn't anything that you can close there!");
break;
}
}
}
static bool _initialise(void)
{
Options.fixup_options();
read_startup_prefs();
you.symbol = '@';
you.colour = LIGHTGREY;
seed_rng();
get_typeid_array().init(ID_UNKNOWN_TYPE);
init_char_table(Options.char_set);
init_feature_table();
init_monster_symbols();
init_spell_descs(); init_mon_name_cache();
init_item_name_cache();
msg::initialise_mpr_streams();
for (int i = 0; i < MAX_ITEMS; ++i)
init_item(i);
info[0] = 0;
for (int i = 0; i < MAX_MONSTERS; ++i)
menv[i].reset();
igrd.init(NON_ITEM);
mgrd.init(NON_MONSTER);
env.map.init(map_cell());
you.unique_creatures.init(false);
you.unique_items.init(UNIQ_NOT_EXISTS);
init_dungeon_lua();
read_maps();
databaseSystemInit();
init_feat_desc_cache();
init_spell_name_cache();
init_spell_rarities();
cio_init();
textbackground(0);
clrscr();
#ifdef DEBUG_DIAGNOSTICS
if (crawl_state.map_stat_gen)
{
generate_map_stats();
end(0, false);
}
#endif
if (crawl_state.test)
{
#ifdef DEBUG_DIAGNOSTICS
#ifdef USE_TILE
init_player_doll();
tiles.initialise_items();
#endif
Options.show_more_prompt = false;
crawl_tests::run_tests(true);
end(0, false);
#else
end(1, false, "Non-debug Crawl cannot run tests. "
"Please use a debug build");
#endif
}
if (crawl_state.arena)
{
run_map_preludes();
initialise_item_descriptions();
#ifdef USE_TILE
tiles.initialise_items();
#endif
run_arena();
end(0, false);
}
const bool newc = new_game();
if (!newc)
restore_game();
fixup_mutations();
macro_init();
crawl_state.need_save = true;
calc_hp();
calc_mp();
run_map_preludes();
if (newc && you.char_direction == GDT_GAME_START)
{
you.level_type = LEVEL_ABYSS;
you.entry_cause = EC_UNKNOWN;
}
load(you.entering_level ? you.transit_stair : DNGN_STONE_STAIRS_DOWN_I,
you.entering_level ? LOAD_ENTER_LEVEL :
newc ? LOAD_START_GAME : LOAD_RESTART_GAME,
NUM_LEVEL_AREA_TYPES, -1, you.where_are_you);
if (newc && you.char_direction == GDT_GAME_START)
{
init_pandemonium();
}
#if DEBUG_DIAGNOSTICS
you.wizard = true;
#endif
init_properties();
burden_change();
make_hungry(0, true);
you.redraw_strength = true;
you.redraw_intelligence = true;
you.redraw_dexterity = true;
you.redraw_armour_class = true;
you.redraw_evasion = true;
you.redraw_experience = true;
you.redraw_quiver = true;
you.wield_change = true;
you.start_time = time( NULL );
#ifdef CLUA_BINDINGS
clua.runhook("chk_startgame", "b", newc);
std::string yname = you.your_name; read_init_file(true);
Options.fixup_options();
you.your_name = yname;
init_char_table(Options.char_set);
init_feature_table();
init_monster_symbols();
#endif
#ifdef USE_TILE
if (Options.tile_menu_icons && Options.show_inventory_weights)
Options.show_inventory_weights = false;
init_player_doll();
tiles.resize();
#endif
draw_border();
new_level();
update_turn_count();
trackers_init_new_level(false);
you.lava_in_sight = you.water_in_sight = -1;
set_los_radius(you.current_vision);
init_exclusion_los();
if (newc) {
you.friendly_pickup = Options.default_friendly_pickup;
origin_set_inventory(origin_set_unknown);
zap_los_monsters();
if (Options.tutorial_left)
tutorial_zap_secret_doors();
}
#ifdef USE_TILE
tiles.initialise_items();
TileNewLevel(newc);
#endif
set_cursor_enabled(false);
maybe_update_stashes();
viewwindow(true, false);
activate_notes(true);
add_key_recorder(&repeat_again_rec);
return (newc);
}
static void _do_berserk_no_combat_penalty(void)
{
const int delay = current_delay_action();
if (delay == DELAY_BUTCHER || delay == DELAY_EAT)
return;
if (you.berserk_penalty == NO_BERSERK_PENALTY)
return;
if (you.duration[DUR_BERSERKER])
{
you.berserk_penalty++;
switch (you.berserk_penalty)
{
case 2:
mpr("You feel a strong urge to attack something.", MSGCH_DURATION);
break;
case 4:
mpr("You feel your anger subside.", MSGCH_DURATION);
break;
case 6:
mpr("Your blood rage is quickly leaving you.", MSGCH_DURATION);
break;
}
you.duration[DUR_BERSERKER] -= you.berserk_penalty;
if (you.duration[DUR_BERSERKER] < 1)
you.duration[DUR_BERSERKER] = 1;
you.duration[DUR_MIGHT] -= you.berserk_penalty;
if (you.duration[DUR_MIGHT] < 1)
you.duration[DUR_MIGHT] = 1;
you.duration[DUR_HASTE] -= you.berserk_penalty;
if (you.duration[DUR_HASTE] < 1)
you.duration[DUR_HASTE] = 1;
}
return;
}
static void _move_player(int move_x, int move_y)
{
_move_player( coord_def(move_x, move_y) );
}
static void _move_player(coord_def move)
{
ASSERT(!crawl_state.arena && !crawl_state.arena_suspended);
bool attacking = false;
bool moving = true; bool swap = false;
if (you.attribute[ATTR_HELD])
{
free_self_from_net();
you.turn_is_over = true;
return;
}
if (you.confused())
{
dungeon_feature_type dangerous = DNGN_FLOOR;
for (adjacent_iterator ai(you.pos(), false); ai; ++ai)
{
if (is_feat_dangerous(grd(*ai))
&& (dangerous == DNGN_FLOOR || grd(*ai) == DNGN_LAVA))
{
dangerous = grd(*ai);
}
}
if (dangerous != DNGN_FLOOR)
{
std::string prompt = "Are you sure you want to move while confused "
"and next to ";
prompt += (dangerous == DNGN_LAVA ? "lava"
: "deep water");
prompt += "? ";
if (!yesno(prompt.c_str(), false, 'n'))
{
canned_msg(MSG_OK);
return;
}
}
if (!one_chance_in(3))
{
move.x = random2(3) - 1;
move.y = random2(3) - 1;
you.reset_prev_move();
}
const coord_def& new_targ = you.pos() + move;
if (!in_bounds(new_targ) || !you.can_pass_through(new_targ))
{
you.turn_is_over = true;
mpr("Ouch!");
apply_berserk_penalty = true;
crawl_state.cancel_cmd_repeat();
return;
}
}
const coord_def& targ = you.pos() + move;
const dungeon_feature_type targ_grid = grd(targ);
monsters* targ_monst = monster_at(targ);
if (feawn_passthrough(targ_monst))
targ_monst = NULL;
const bool targ_pass = you.can_pass_through(targ);
const bool can_swap_places = targ_monst
&& !mons_is_stationary(targ_monst)
&& (mons_wont_attack(targ_monst)
&& !you.confused()
|| is_sanctuary(you.pos())
&& is_sanctuary(targ));
monsters *beholder = NULL;
if (you.duration[DUR_MESMERISED] && !you.confused())
{
for (unsigned int i = 0; i < you.mesmerised_by.size(); ++i)
{
monsters& mon = menv[you.mesmerised_by[i]];
int olddist = grid_distance(you.pos(), mon.pos());
int newdist = grid_distance(targ, mon.pos());
if (olddist < newdist)
{
beholder = &mon;
break;
}
}
}
if (you.running.check_stop_running())
{
you.turn_is_over = false;
return;
}
coord_def mon_swap_dest;
if (targ_monst && !targ_monst->submerged())
{
if (can_swap_places && !beholder)
{
if (swap_check(targ_monst, mon_swap_dest))
swap = true;
else
moving = false;
}
else if (!can_swap_places) {
you.turn_is_over = true;
you_attack(targ_monst->mindex(), true);
if (you.berserk_penalty != NO_BERSERK_PENALTY)
you.berserk_penalty = 0;
attacking = true;
}
}
if (!attacking && targ_pass && moving && !beholder)
{
you.time_taken *= player_movement_speed();
you.time_taken /= 10;
if (!move_player_to_grid(targ, true, false, false, swap))
return;
if (swap)
swap_places(targ_monst, mon_swap_dest);
you.prev_move = move;
move.reset();
you.turn_is_over = true;
request_autopickup();
}
if (Options.easy_open && !attacking && feat_is_closed_door(targ_grid))
{
_open_door(move.x, move.y, false);
you.prev_move = move;
}
else if (!targ_pass && !attacking)
{
if (grd(targ) == DNGN_OPEN_SEA)
mpr("You can't go out to sea!");
stop_running();
move.reset();
you.turn_is_over = false;
crawl_state.cancel_cmd_repeat();
return;
}
else if (beholder && !attacking)
{
mprf("You cannot move away from %s!",
beholder->name(DESC_NOCAP_THE, true).c_str());
return;
}
if (you.running == RMODE_START)
you.running = RMODE_CONTINUE;
if (you.level_type == LEVEL_ABYSS
&& (you.pos().x <= 15 || you.pos().x >= (GXM - 16)
|| you.pos().y <= 15 || you.pos().y >= (GYM - 16)))
{
area_shift();
if (you.pet_target != MHITYOU)
you.pet_target = MHITNOT;
#if DEBUG_DIAGNOSTICS
mpr("Shifting.", MSGCH_DIAGNOSTICS);
int j = 0;
for (int i = 0; i < MAX_ITEMS; ++i)
if (is_valid_item( mitm[i] ))
++j;
mprf(MSGCH_DIAGNOSTICS, "Number of items present: %d", j);
j = 0;
for (int i = 0; i < MAX_MONSTERS; ++i)
if (menv[i].type != -1)
++j;
mprf(MSGCH_DIAGNOSTICS, "Number of monsters present: %d", j);
mprf(MSGCH_DIAGNOSTICS, "Number of clouds present: %d", env.cloud_no);
#endif
}
apply_berserk_penalty = !attacking;
if (!attacking && you.religion == GOD_CHEIBRIADOS && one_chance_in(10)
&& player_equip_ego_type(EQ_BOOTS, SPARM_RUNNING))
{
did_god_conduct(DID_HASTY, 1, true);
}
}
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, MSGCH_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];
int ch = _get_num_and_char("Number of times to repeat, then command key: ",
buf, 80);
if (ch == ESCAPE)
{
keyseq trigger;
trigger.push_back(ch);
if (get_macro_buf_size() == 0)
{
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);
repeat_again_rec.clear();
insert_macro_into_buff(trigger);
ch = getchm();
if (ch == ESCAPE)
{
if (get_macro_buf_size() > 0)
mpr("That key isn't bound to a macro.");
else
canned_msg( MSG_OK );
crawl_state.cancel_cmd_again();
crawl_state.cancel_cmd_repeat();
flush_input_buffer(FLUSH_REPLAY_SETUP_FAILURE);
return;
}
}
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)
{
do
{
repeat_again_rec.keys.pop_front();
}
while (repeat_again_rec.keys.size() > 0
&& repeat_again_rec.keys[0] != ch);
repeat_again_rec.keys.pop_front();
}
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: ");
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())
{
repeat_again_rec.paused = true;
macro_buf_add(ch, true);
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;
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();
repeat_again_rec.clear();
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);
macro_buf_add(keys, true);
bool was_doing_repeats = crawl_state.is_repeating_cmd();
_input();
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())
{
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;
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
{
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();
}
static void _compile_time_asserts()
{
COMPILE_CHECK(SK_UNARMED_COMBAT == 19 , c1);
COMPILE_CHECK(SK_EVOCATIONS == 39 , c2);
COMPILE_CHECK(SP_VAMPIRE == 30 , c3);
COMPILE_CHECK(SPELL_DEBUGGING_RAY == 103 , c4);
COMPILE_CHECK(SPELL_PETRIFY == 156 , c5);
COMPILE_CHECK(NUM_SPELLS == 201 , c6);
COMPILE_CHECK(NUM_SPECIES < SP_UNKNOWN , c7);
COMPILE_CHECK(NUM_JOBS < JOB_UNKNOWN , c8);
COMPILE_CHECK(NO_UNRANDARTS < MAX_UNRANDARTS, c9);
COMPILE_CHECK((int) NUM_SPECIAL_WEAPONS < (int) UNRAND_START, c10);
for (int i = 0; i < NUM_BRANCHES; ++i)
ASSERT(branches[i].id == i);
}