#include "AppHdr.h" #include "stairs.h" #include #include "abyss.h" #include "act-iter.h" #include "areas.h" #include "bloodspatter.h" #include "branch.h" #include "chardump.h" #include "colour.h" #include "coordit.h" #include "delay.h" #include "dgn-overview.h" #include "directn.h" #include "env.h" #include "files.h" #include "fprop.h" #include "godabil.h" #include "hints.h" #include "hiscores.h" #include "itemname.h" #include "items.h" #include "libutil.h" #include "mapmark.h" #include "message.h" #include "misc.h" #include "notes.h" #include "options.h" #include "ouch.h" #include "output.h" #include "place.h" #include "prompt.h" #include "random.h" #include "religion.h" #include "spl-clouds.h" #include "spl-damage.h" #include "spl-other.h" #include "spl-transloc.h" #include "stash.h" #include "state.h" #include "strings.h" #include "terrain.h" #ifdef USE_TILE_LOCAL #include "tilepick.h" #endif #include "traps.h" #include "travel.h" #include "view.h" #include "xom.h" bool check_annotation_exclusion_warning() { // Players might not realise the implications of teleport mutations // in the labyrinth. if (grd(you.pos()) == DNGN_ENTER_LABYRINTH && player_mutation_level(MUT_TELEPORT)) { mpr("Within the labyrinth you'll only be able to teleport away from " "the exit!"); if (!yesno("Continue anyway?", false, 'N', true, false)) { canned_msg(MSG_OK); interrupt_activity(AI_FORCE_INTERRUPT); return false; } return true; } level_id next_level_id = level_id::get_next_level_id(you.pos()); crawl_state.level_annotation_shown = false; bool might_be_dangerous = false; if (level_annotation_has("!", next_level_id) && next_level_id != level_id::current() && is_connected_branch(next_level_id)) { mprf(MSGCH_PROMPT, "Warning, next level annotated: %s", get_level_annotation(next_level_id).c_str()); might_be_dangerous = true; crawl_state.level_annotation_shown = true; } else if (is_exclude_root(you.pos()) && feat_is_travelable_stair(grd(you.pos())) && !strstr(get_exclusion_desc(you.pos()).c_str(), "cloud")) { mprf(MSGCH_WARN, "This staircase is marked as excluded!"); might_be_dangerous = true; } if (might_be_dangerous && !yesno("Enter next level anyway?", true, 'n', true, false)) { canned_msg(MSG_OK); interrupt_activity(AI_FORCE_INTERRUPT); crawl_state.level_annotation_shown = false; return false; } return true; } static void _player_change_level_reset() { you.prev_targ = MHITNOT; if (you.pet_target != MHITYOU) you.pet_target = MHITNOT; you.prev_grd_targ.reset(); } static void _player_change_level_upstairs(dungeon_feature_type feat) { level_id lev = feat ? stair_destination(feat, "", true) : stair_destination(you.pos(), true); you.depth = lev.depth; you.where_are_you = lev.branch; } static bool _marker_vetoes_level_change() { return marker_vetoes_operation("veto_level_change"); } static bool _stair_moves_pre(dungeon_feature_type stair) { if (crawl_state.prev_cmd == CMD_WIZARD) return false; if (stair != grd(you.pos())) return false; if (feat_stair_direction(stair) == CMD_NO_CMD) return false; if (!you.duration[DUR_REPEL_STAIRS_CLIMB]) return false; int pct; if (you.duration[DUR_REPEL_STAIRS_MOVE]) pct = 29; else pct = 50; // When the effect is still strong, the chance to actually catch a stair // is smaller. (Assuming the duration starts out at 500.) const int dur = max(0, you.duration[DUR_REPEL_STAIRS_CLIMB] - 200); pct += dur/20; if (!x_chance_in_y(pct, 100)) return false; // Get feature name before sliding stair over. string stair_str = feature_description_at(you.pos(), false, DESC_THE, false); if (!slide_feature_over(you.pos(), coord_def(-1, -1), false)) return false; string verb = stair_climb_verb(stair); mprf("%s moves away as you attempt to %s it!", stair_str.c_str(), verb.c_str()); you.turn_is_over = true; return true; } static void _exit_stair_message(dungeon_feature_type stair) { if (feat_is_escape_hatch(stair)) mpr("The hatch slams shut behind you."); } static void _climb_message(dungeon_feature_type stair, bool going_up, branch_type old_branch) { if (!is_connected_branch(old_branch)) return; if (feat_is_portal(stair)) mpr("The world spins around you as you enter the gateway."); else if (feat_is_escape_hatch(stair)) { if (going_up) mpr("A mysterious force pulls you upwards."); else { mprf("You %s downwards.", you.flight_mode() ? "fly" : "slide"); } } else if (feat_is_gate(stair)) { mprf("You %s %s through the gate.", you.flight_mode() ? "fly" : "go", going_up ? "up" : "down"); } else { mprf("You %s %swards.", you.flight_mode() ? "fly" : "climb", going_up ? "up" : "down"); } } static void _clear_golubria_traps() { vector traps = find_golubria_on_level(); for (vector::const_iterator it = traps.begin(); it != traps.end(); ++it) { trap_def *trap = find_trap(*it); if (trap && trap->type == TRAP_GOLUBRIA) trap->destroy(); } } static void _clear_prisms() { for (int i = 0; i < MAX_MONSTERS; ++i) { monster* mons = &menv[i]; if (mons->type == MONS_FULMINANT_PRISM) mons->reset(); } } void leaving_level_now(dungeon_feature_type stair_used) { process_sunlights(true); if (stair_used == DNGN_EXIT_ZIGGURAT) { if (you.depth == 27) you.zigs_completed++; mark_milestone("zig.exit", make_stringf("left a Ziggurat at level %d.", you.depth)); } // Note the name ahead of time because the events may cause markers // to be discarded. const string newtype = env.markers.property_at(you.pos(), MAT_ANY, "dst"); dungeon_events.fire_position_event(DET_PLAYER_CLIMBS, you.pos()); dungeon_events.fire_event(DET_LEAVING_LEVEL); _clear_golubria_traps(); _clear_prisms(); end_recall(); } static void _update_travel_cache(const level_id& old_level, const coord_def& stair_pos) { // If the old level is gone, nothing to save. if (!you.save || !you.save->has_chunk(old_level.describe())) return; // Update stair information for the stairs we just ascended, and the // down stairs we're currently on. level_id new_level_id = level_id::current(); if (can_travel_interlevel()) { LevelInfo &old_level_info = travel_cache.get_level_info(old_level); LevelInfo &new_level_info = travel_cache.get_level_info(new_level_id); new_level_info.update(); // First we update the old level's stair. level_pos lp; lp.id = new_level_id; lp.pos = you.pos(); bool guess = false; // Ugly hack warning: // The stairs in the Vestibule of Hell exhibit special behaviour: // they always lead back to the dungeon level that the player // entered the Vestibule from. This means that we need to pretend // we don't know where the upstairs from the Vestibule go each time // we take it. If we don't, interlevel travel may try to use portals // to Hell as shortcuts between dungeon levels, which won't work, // and will confuse the dickens out of the player (well, it confused // the dickens out of me when it happened). if ((new_level_id == BRANCH_DUNGEON || new_level_id == BRANCH_DEPTHS) && old_level == BRANCH_VESTIBULE) { old_level_info.clear_stairs(DNGN_EXIT_HELL); } else old_level_info.update_stair(stair_pos, lp, guess); // We *guess* that going up a staircase lands us on a downstair, // and that we can descend that downstair and get back to where we // came from. This assumption is guaranteed false when climbing out // of one of the branches of Hell. if (new_level_id != BRANCH_VESTIBULE || !is_hell_subbranch(old_level.branch)) { // Set the new level's stair, assuming arbitrarily that going // downstairs will land you on the same upstairs you took to // begin with (not necessarily true). lp.id = old_level; lp.pos = stair_pos; new_level_info.update_stair(you.pos(), lp, true); } } } // These checks are probably unnecessary. static bool _check_stairs(const dungeon_feature_type ftype, bool down = false) { // If it's not bidirectional, check that the player is headed // in the right direction. if (!feat_is_bidirectional_portal(ftype)) { if (feat_stair_direction(ftype) != (down ? CMD_GO_DOWNSTAIRS : CMD_GO_UPSTAIRS)) { if (ftype == DNGN_STONE_ARCH) mpr("There is nothing on the other side of the stone arch."); else if (ftype == DNGN_ABANDONED_SHOP) mpr("This shop appears to be closed."); else if (down) mpr("You can't go down here!"); else mpr("You can't go up here!"); return false; } } return true; } void up_stairs(dungeon_feature_type force_stair) { dungeon_feature_type stair_find = (force_stair ? force_stair : orig_terrain(you.pos())); const level_id old_level = level_id::current(); // Up and down both work for portals. // Canonicalize the direction; hell exits into the vestibule are handled // by up_stairs; everything else by down_stairs. if (feat_is_bidirectional_portal(stair_find)) { if (!(stair_find == DNGN_ENTER_HELL && player_in_hell())) return down_stairs(force_stair); } // Only check the current position for a legal stair traverse. if (!force_stair && !_check_stairs(stair_find)) return; if (_stair_moves_pre(stair_find)) return; if (!you.airborne() && you.confused() && !feat_is_escape_hatch(stair_find) && coinflip()) { const char* fall_where = "down the stairs"; if (!feat_is_staircase(stair_find)) fall_where = "through the gate"; mprf("In your confused state, you trip and fall back %s.", fall_where); if (!feat_is_staircase(stair_find)) ouch(1, NON_MONSTER, KILLED_BY_FALLING_THROUGH_GATE); else ouch(1, NON_MONSTER, KILLED_BY_FALLING_DOWN_STAIRS); you.turn_is_over = true; return; } // Bail if any markers veto the move. if (_marker_vetoes_level_change()) return; // Magical level changes (don't exist yet in this direction) // need this. clear_trapping_net(); // Checks are done, the character is committed to moving between levels. leaving_level_now(stair_find); // Interlevel travel data. const bool collect_travel_data = can_travel_interlevel(); if (collect_travel_data) { LevelInfo &old_level_info = travel_cache.get_level_info(old_level); old_level_info.update(); } if (stair_find == DNGN_EXIT_DUNGEON) { you.depth = 0; mpr("You have escaped!"); if (player_has_orb()) ouch(INSTANT_DEATH, NON_MONSTER, KILLED_BY_WINNING); ouch(INSTANT_DEATH, NON_MONSTER, KILLED_BY_LEAVING); } _player_change_level_reset(); _player_change_level_upstairs(force_stair); if (old_level.branch == BRANCH_VESTIBULE && !player_in_branch(BRANCH_VESTIBULE)) { mpr("Thank you for visiting Hell. Please come again soon."); } // Fixup exits from the Hell branches. if (player_in_branch(BRANCH_VESTIBULE)) { switch (old_level.branch) { case BRANCH_COCYTUS: stair_find = DNGN_ENTER_COCYTUS; break; case BRANCH_DIS: stair_find = DNGN_ENTER_DIS; break; case BRANCH_GEHENNA: stair_find = DNGN_ENTER_GEHENNA; break; case BRANCH_TARTARUS: stair_find = DNGN_ENTER_TARTARUS; break; default: break; } } const dungeon_feature_type stair_taken = stair_find; if (you.flight_mode() && !feat_is_gate(stair_find)) mpr("You fly upwards."); else _climb_message(stair_find, true, old_level.branch); _exit_stair_message(stair_find); if (old_level.branch != you.where_are_you) { mprf("Welcome back to %s!", branches[you.where_are_you].longname); if ((brdepth[old_level.branch] > 1 || old_level.branch == BRANCH_VESTIBULE) && !you.branches_left[old_level.branch]) { string old_branch_string = branches[old_level.branch].longname; if (old_branch_string.find("The ") == 0) old_branch_string[0] = tolower(old_branch_string[0]); mark_milestone("br.exit", "left " + old_branch_string + ".", old_level.describe()); you.branches_left.set(old_level.branch); } } const coord_def stair_pos = you.pos(); load_level(stair_taken, LOAD_ENTER_LEVEL, old_level); you.turn_is_over = true; save_game_state(); new_level(); _update_travel_cache(old_level, stair_pos); // Preventing obvious finding of stairs at your position. env.map_seen.set(you.pos()); viewwindow(); seen_monsters_react(); if (!allow_control_teleport(true)) mprf(MSGCH_WARN, "You sense a powerful magical force warping space."); request_autopickup(); } // Find the other end of the stair or portal at location pos on the current // level. for_real is true if we are actually traversing the feature rather // than merely asking what is on the other side. level_id stair_destination(coord_def pos, bool for_real) { return stair_destination(orig_terrain(pos), env.markers.property_at(pos, MAT_ANY, "dst"), for_real); } // Find the other end of a stair or portal on the current level. feat is the // type of feature (DNGN_EXIT_ABYSS, for example), dst is the target of a // portal vault entrance (and is ignored for other types of features), and // for_real is true if we are actually traversing the feature rather than // merely asking what is on the other side. level_id stair_destination(dungeon_feature_type feat, const string &dst, bool for_real) { #if TAG_MAJOR_VERSION == 34 if (feat == DNGN_ESCAPE_HATCH_UP && player_in_branch(BRANCH_LABYRINTH)) feat = DNGN_EXIT_LABYRINTH; #endif if (branches[you.where_are_you].exit_stairs == feat && parent_branch(you.where_are_you) < NUM_BRANCHES && feat != DNGN_EXIT_ZIGGURAT) { level_id lev = brentry[you.where_are_you]; if (!lev.is_valid()) { // Wizmode, the branch wasn't generated this game. // Pick the middle of the range instead. lev = level_id(branches[you.where_are_you].parent_branch, (branches[you.where_are_you].mindepth + branches[you.where_are_you].maxdepth) / 2); ASSERT(lev.is_valid()); } return lev; } if (feat >= DNGN_EXIT_FIRST_PORTAL && feat <= DNGN_EXIT_LAST_PORTAL) feat = DNGN_EXIT_PANDEMONIUM; switch (feat) { case DNGN_ESCAPE_HATCH_UP: case DNGN_STONE_STAIRS_UP_I: case DNGN_STONE_STAIRS_UP_II: case DNGN_STONE_STAIRS_UP_III: if (you.depth <= 1) { if (you.wizard && !for_real) return level_id(); die("upstairs from top of a branch"); } return level_id(you.where_are_you, you.depth - 1); case DNGN_EXIT_HELL: // If set, it would be found as a branch exit. if (you.wizard) { if (for_real) { mprf(MSGCH_ERROR, "Error: no Hell exit level, how in the " "Vestibule did you get here? Let's go to D:1."); } return level_id(BRANCH_DUNGEON, 1); } else die("hell exit without return destination"); case DNGN_ABYSSAL_STAIR: ASSERT(you.where_are_you == BRANCH_ABYSS); push_features_to_abyss(); case DNGN_ESCAPE_HATCH_DOWN: case DNGN_STONE_STAIRS_DOWN_I: case DNGN_STONE_STAIRS_DOWN_II: case DNGN_STONE_STAIRS_DOWN_III: { ASSERT(!at_branch_bottom()); level_id lev = level_id::current(); lev.depth++; return lev; } case DNGN_TRANSIT_PANDEMONIUM: return level_id(BRANCH_PANDEMONIUM); case DNGN_EXIT_THROUGH_ABYSS: return level_id(BRANCH_ABYSS); #if TAG_MAJOR_VERSION == 34 case DNGN_ENTER_PORTAL_VAULT: if (dst.empty()) { if (for_real) die("portal without a destination"); else return level_id(); } return level_id::parse_level_id(dst); #endif case DNGN_ENTER_HELL: if (for_real && !player_in_hell()) brentry[BRANCH_VESTIBULE] = level_id::current(); return level_id(BRANCH_VESTIBULE); case DNGN_EXIT_ABYSS: if (you.char_direction == GDT_GAME_START) return level_id(BRANCH_DUNGEON, 1); #if TAG_MAJOR_VERSION == 34 case DNGN_EXIT_PORTAL_VAULT: #endif case DNGN_EXIT_PANDEMONIUM: if (you.level_stack.empty()) { if (you.wizard) { if (for_real) { mprf(MSGCH_ERROR, "Error: no return path. You did create " "the exit manually, didn't you? Let's go to D:1."); } return level_id(BRANCH_DUNGEON, 1); } die("no return path from a portal (%s)", level_id::current().describe().c_str()); } return you.level_stack.back().id; case DNGN_ENTER_ABYSS: push_features_to_abyss(); break; default: break; } // Try to find a branch stair. for (branch_iterator it; it; ++it) { if (it->entry_stairs == feat) return level_id(it->id); } return level_id(); } static void _player_change_level_downstairs(dungeon_feature_type stair_find, const string &dst) { level_id lev = stair_destination(stair_find, dst, true); if (!lev.is_valid()) die("Unknown down stair: %s", dungeon_feature_name(stair_find)); you.depth = lev.depth; you.where_are_you = lev.branch; } static void _maybe_destroy_trap(const coord_def &p) { trap_def* trap = find_trap(p); if (trap) trap->destroy(true); } // TODO(Zannick): Fully merge with up_stairs into take_stairs. void down_stairs(dungeon_feature_type force_stair, bool force_known_shaft) { const level_id old_level = level_id::current(); const dungeon_feature_type old_feat = orig_terrain(you.pos()); const dungeon_feature_type stair_find = force_stair ? force_stair : old_feat; // Taking a shaft manually const bool known_shaft = (!force_stair && get_trap_type(you.pos()) == TRAP_SHAFT && stair_find != DNGN_UNDISCOVERED_TRAP) || (force_stair == DNGN_TRAP_SHAFT && force_known_shaft); // Latter case is falling down a shaft. const bool shaft = known_shaft || force_stair == DNGN_TRAP_SHAFT; level_id shaft_dest; // Up and down both work for portals. // Canonicalize the direction; hell exits into the vestibule are handled // by up_stairs; everything else by down_stairs. if (feat_is_bidirectional_portal(stair_find)) { if (stair_find == DNGN_ENTER_HELL && player_in_hell()) return up_stairs(force_stair); } // Only check the current position for a legal stair traverse. // If it's a shaft that we're taking, then we're already good. if (!shaft && !_check_stairs(stair_find, true)) return; if (_stair_moves_pre(stair_find)) return; if (shaft) { if (!is_valid_shaft_level()) { if (known_shaft) mpr("The shaft disappears in a puff of logic!"); _maybe_destroy_trap(you.pos()); return; } shaft_dest = you.shaft_dest(known_shaft); } const int shaft_depth = (shaft ? shaft_dest.depth - you.depth : 1); if (shaft) { if (shaft_dest == level_id::current()) { if (known_shaft) { mpr("Strange, the shaft seems to lead back to this level."); mpr("The strain on the space-time continuum destroys the " "shaft!"); } _maybe_destroy_trap(you.pos()); return; } if (!known_shaft) { mark_milestone("shaft", "fell down a shaft to " + short_place_name(shaft_dest) + "."); } handle_items_on_shaft(you.pos(), false); string howfar; if (force_stair && shaft_depth > 1) howfar = make_stringf(" for %d floors", shaft_depth); if (!you.flight_mode() || force_stair) mprf("You fall through a shaft%s!", howfar.c_str()); else mpr("You dive down through the shaft."); // Shafts are one-time-use. mpr("The shaft crumbles and collapses."); _maybe_destroy_trap(you.pos()); } if (stair_find == DNGN_ENTER_VAULTS && !is_existing_level(level_id(BRANCH_VAULTS, 1))) { bool has_rune = false; int i = 0; for (; i < NUM_RUNE_TYPES; i++) if (you.runes[i]) { has_rune = true; break; } if (!has_rune) { mpr("You need a rune to enter this place."); return; } mprf("You insert the %s rune into the lock.", rune_type_name(i)); if (silenced(you.pos())) mpr("The gate opens wide!"); else mpr("With a loud hiss the gate opens wide!"); more(); } if (stair_find == DNGN_ENTER_ZOT && !you.opened_zot) { vector runes; for (int i = 0; i < NUM_RUNE_TYPES; i++) if (you.runes[i]) runes.push_back(i); if (runes.size() < NUMBER_OF_RUNES_NEEDED) { switch (NUMBER_OF_RUNES_NEEDED) { case 1: mpr("You need a rune to enter this place."); break; default: mprf("You need at least %d runes to enter this place.", NUMBER_OF_RUNES_NEEDED); } return; } ASSERT(runes.size() >= 3); shuffle_array(runes); mprf("You insert the %s rune into the lock.", rune_type_name(runes[0])); #ifdef USE_TILE_LOCAL tiles.add_overlay(you.pos(), tileidx_zap(rune_colour(runes[0]))); update_screen(); #else flash_view(rune_colour(runes[0])); #endif mpr("The lock glows eerily!"); more(); mprf("You insert the %s rune into the lock.", rune_type_name(runes[1])); big_cloud(CLOUD_BLUE_SMOKE, &you, you.pos(), 20, 7 + random2(7)); viewwindow(); mpr("Heavy smoke blows from the lock!"); more(); mprf("You insert the %s rune into the lock.", rune_type_name(runes[2])); if (silenced(you.pos())) mpr("The gate opens wide!"); else mpr("With a loud hiss the gate opens wide!"); more(); you.opened_zot = true; } if (stair_find == DNGN_ENTER_ZIGGURAT) { #define ZIG_RUNES 3 if (runes_in_pack() < ZIG_RUNES) { mprf("You need at least %d runes to enter this place.", ZIG_RUNES); return; } } // Bail if any markers veto the move. if (_marker_vetoes_level_change()) return; // All checks are done, the player is on the move now. // Magical level changes (Portal, Banishment) need this. clear_trapping_net(); end_searing_ray(); // Markers might be deleted when removing portals. const string dst = env.markers.property_at(you.pos(), MAT_ANY, "dst"); // Fire level-leaving trigger. leaving_level_now(stair_find); // Not entirely accurate - the player could die before // reaching the Abyss. if (!force_stair && old_feat == DNGN_ENTER_ABYSS) { mark_milestone("abyss.enter", "entered the Abyss!"); take_note(Note(NOTE_MESSAGE, 0, 0, "Voluntarily entered the Abyss."), true); } else if (old_feat == DNGN_EXIT_THROUGH_ABYSS) { mark_milestone("abyss.enter", "escaped (hah) into the Abyss!"); take_note(Note(NOTE_MESSAGE, 0, 0, "Took an exit into the Abyss."), true); } else if (stair_find == DNGN_EXIT_ABYSS && you.char_direction != GDT_GAME_START) { mark_milestone("abyss.exit", "escaped from the Abyss!"); you.attribute[ATTR_BANISHMENT_IMMUNITY] = you.elapsed_time + 100 + random2(100); } // Interlevel travel data. const bool collect_travel_data = can_travel_interlevel(); if (collect_travel_data) { LevelInfo &old_level_info = travel_cache.get_level_info(old_level); old_level_info.update(); } const coord_def stair_pos = you.pos(); if (stair_find == DNGN_ENTER_LABYRINTH || stair_find == DNGN_ENTER_ZIGGURAT) dungeon_terrain_changed(you.pos(), DNGN_STONE_ARCH); if (stair_find == DNGN_ENTER_LABYRINTH #if TAG_MAJOR_VERSION == 34 || stair_find == DNGN_ENTER_PORTAL_VAULT #endif || stair_find == DNGN_ENTER_PANDEMONIUM || stair_find == DNGN_ENTER_ABYSS || stair_find >= DNGN_ENTER_FIRST_PORTAL && stair_find <= DNGN_ENTER_LAST_PORTAL) { you.level_stack.push_back(level_pos::current()); } _player_change_level_reset(); if (shaft) you.depth = shaft_dest.depth; else _player_change_level_downstairs(stair_find, dst); // Did we enter a new branch. const bool entered_branch( you.where_are_you != old_level.branch && parent_branch(you.where_are_you) == old_level.branch); if (stair_find == DNGN_EXIT_ABYSS || stair_find == DNGN_EXIT_PANDEMONIUM || stair_find == DNGN_EXIT_THROUGH_ABYSS) { mpr("You pass through the gate."); take_note(Note(NOTE_MESSAGE, 0, 0, stair_find == DNGN_EXIT_ABYSS ? "Escaped the Abyss" : stair_find == DNGN_EXIT_PANDEMONIUM ? "Escaped Pandemonium" : stair_find == DNGN_EXIT_THROUGH_ABYSS ? "Escaped into the Abyss" : "Buggered into bugdom"), true); if (!you.wizard || !crawl_state.is_replaying_keys()) more(); } if (!is_connected_branch(old_level.branch) && player_in_connected_branch() && old_level.branch != you.where_are_you) { mprf("Welcome %sto %s!", you.char_direction == GDT_GAME_START ? "" : "back ", branches[you.where_are_you].longname); } if (!you.airborne() && you.confused() && !feat_is_escape_hatch(stair_find) && force_stair != DNGN_ENTER_ABYSS && force_stair != DNGN_ABYSSAL_STAIR && force_stair != DNGN_EXIT_ABYSS && coinflip()) { const char* fall_where = "down the stairs"; if (!feat_is_staircase(stair_find)) fall_where = "through the gate"; mprf("In your confused state, you trip and fall %s.", fall_where); // Note that this only does damage; it doesn't cancel the level // transition. if (!feat_is_staircase(stair_find)) ouch(1, NON_MONSTER, KILLED_BY_FALLING_THROUGH_GATE); else ouch(1, NON_MONSTER, KILLED_BY_FALLING_DOWN_STAIRS); } dungeon_feature_type stair_taken = stair_find; if (shaft) stair_taken = DNGN_TRAP_SHAFT; switch (you.where_are_you) { case BRANCH_LABYRINTH: // XXX: Ideally, we want to hint at the wall rule (rock > metal), // and that the walls can shift occasionally. // Are these too long? mpr("As you enter the labyrinth, previously moving walls settle noisily into place."); mpr("You hear the metallic echo of a distant snort before it fades into the rock."); break; case BRANCH_ABYSS: if (old_level.branch == BRANCH_ABYSS) { mprf(MSGCH_BANISHMENT, "You plunge deeper into the Abyss."); break; } if (!force_stair) mpr("You enter the Abyss!"); mpr("To return, you must find a gate leading back."); if (you_worship(GOD_CHEIBRIADOS)) { mprf(MSGCH_GOD, GOD_CHEIBRIADOS, "You feel Cheibriados slowing down the madness of this place."); } // Re-entering the Abyss halves accumulated speed. you.abyss_speed /= 2; learned_something_new(HINT_ABYSS); break; case BRANCH_PANDEMONIUM: if (old_level.branch == BRANCH_PANDEMONIUM) mpr("You pass into a different region of Pandemonium."); else { mpr("You enter the halls of Pandemonium!"); mpr("To return, you must find a gate leading back."); } break; default: if (!shaft) _climb_message(stair_find, false, old_level.branch); break; } if (!shaft) _exit_stair_message(stair_find); if (entered_branch) { const branch_type branch = you.where_are_you; if (branches[branch].entry_message) mpr(branches[branch].entry_message); else mprf("Welcome to %s!", branches[branch].longname); enter_branch(branch, old_level); } if (stair_find == DNGN_ENTER_HELL) { mpr("Welcome to Hell!"); mpr("Please enjoy your stay."); // Kill -more- prompt if we're traveling. if (!you.running && !force_stair) more(); } const bool newlevel = load_level(stair_taken, LOAD_ENTER_LEVEL, old_level); if (newlevel) { switch (you.where_are_you) { default: // Xom thinks it's funny if you enter a new level via shaft // or escape hatch, for shafts it's funnier the deeper you fell. if (shaft || feat_is_escape_hatch(stair_find)) xom_is_stimulated(shaft_depth * 50); else if (!is_connected_branch(you.where_are_you)) xom_is_stimulated(25); else xom_is_stimulated(10); break; case BRANCH_ZIGGURAT: // The best way to die currently. xom_is_stimulated(50); break; case BRANCH_LABYRINTH: // Finding the way out of a labyrinth interests Xom, // but less so for Minotaurs. (though not now, as they cannot // map the labyrinth any more {due}) xom_is_stimulated(75); break; case BRANCH_PANDEMONIUM: xom_is_stimulated(100); break; case BRANCH_ABYSS: generate_random_blood_spatter_on_level(); if (!force_stair && old_feat == DNGN_ENTER_ABYSS) xom_is_stimulated(100, XM_INTRIGUED); else xom_is_stimulated(200); break; } } you.turn_is_over = true; save_game_state(); new_level(); moveto_location_effects(old_feat); // Clear list of beholding and constricting/constricted monsters. you.clear_beholders(); you.clear_fearmongers(); you.stop_constricting_all(); you.stop_being_constricted(); if (!allow_control_teleport(true)) mprf(MSGCH_WARN, "You sense a powerful magical force warping space."); trackers_init_new_level(true); _update_travel_cache(old_level, stair_pos); // Preventing obvious finding of stairs at your position. env.map_seen.set(you.pos()); viewwindow(); maybe_update_stashes(); request_autopickup(); // Zotdef: returning from portals (e.g. bazaar) paralyses the player in // place for 5 moves. Nasty, but punishes players for using portals as // quick-healing stopovers. if (crawl_state.game_is_zotdef()) start_delay(DELAY_UNINTERRUPTIBLE, 5); } static bool _any_glowing_mold() { for (rectangle_iterator ri(0); ri; ++ri) if (glowing_mold(*ri)) return true; for (monster_iterator mon_it; mon_it; ++mon_it) if (mon_it->type == MONS_HYPERACTIVE_BALLISTOMYCETE) return true; return false; } static void _update_level_state() { env.level_state = 0; vector golub = find_golubria_on_level(); if (!golub.empty()) env.level_state |= LSTATE_GOLUBRIA; if (_any_glowing_mold()) env.level_state |= LSTATE_GLOW_MOLD; for (monster_iterator mon_it; mon_it; ++mon_it) if (mons_allows_beogh(*mon_it)) env.level_state |= LSTATE_BEOGH; for (rectangle_iterator ri(0); ri; ++ri) if (grd(*ri) == DNGN_SLIMY_WALL) env.level_state |= LSTATE_SLIMY_WALL; env.orb_pos = orb_position(); if (player_has_orb()) { env.orb_pos = you.pos(); invalidate_agrid(true); } } void new_level(bool restore) { print_stats_level(); #ifdef DGL_WHEREIS whereis_record(); #endif _update_level_state(); if (restore) return; cancel_tornado(); if (player_in_branch(BRANCH_ZIGGURAT)) you.zig_max = max(you.zig_max, you.depth); }