1 #include "AppHdr.h"
2 
3 #include "stairs.h"
4 
5 #include <sstream>
6 
7 #include "ability.h"
8 #include "abyss.h"
9 #include "act-iter.h"
10 #include "areas.h"
11 #include "artefact.h"
12 #include "bloodspatter.h"
13 #include "branch.h"
14 #include "chardump.h"
15 #include "colour.h"
16 #include "coordit.h"
17 #include "delay.h"
18 #include "dgn-overview.h"
19 #include "directn.h"
20 #include "env.h"
21 #include "files.h"
22 #include "god-passive.h" // passive_t::slow_abyss
23 #include "hints.h"
24 #include "hiscores.h"
25 #include "item-name.h"
26 #include "items.h"
27 #include "level-state-type.h"
28 #include "losglobal.h"
29 #include "mapmark.h"
30 #include "message.h"
31 #include "mon-death.h"
32 #include "movement.h"
33 #include "notes.h"
34 #include "orb-type.h"
35 #include "output.h"
36 #include "prompt.h"
37 #include "religion.h"
38 #include "spl-clouds.h"
39 #include "spl-damage.h"
40 #include "spl-other.h"
41 #include "state.h"
42 #include "stringutil.h"
43 #include "tag-version.h"
44 #include "terrain.h"
45 #ifdef USE_TILE_LOCAL
46  #include "tilepick.h"
47 #endif
48 #include "tiles-build-specific.h"
49 #include "timed-effects.h" // bezotted
50 #include "traps.h"
51 #include "travel.h"
52 #include "view.h"
53 #include "xom.h"
54 
_annotation_exclusion_warning(level_id next_level_id)55 static string _annotation_exclusion_warning(level_id next_level_id)
56 {
57     if (level_annotation_has("!", next_level_id)
58         && next_level_id != level_id::current()
59         && is_connected_branch(next_level_id))
60     {
61         crawl_state.level_annotation_shown = true;
62         return make_stringf("Warning, next level annotated: <yellow>%s</yellow>",
63                             get_level_annotation(next_level_id).c_str());
64     }
65 
66     if (is_exclude_root(you.pos())
67              && feat_is_travelable_stair(env.grid(you.pos()))
68              && !strstr(get_exclusion_desc(you.pos()).c_str(), "cloud"))
69     {
70         return "This staircase is marked as excluded!";
71     }
72 
73     return "";
74 }
75 
_target_exclusion_warning()76 static string _target_exclusion_warning()
77 {
78     if (!feat_is_travelable_stair(env.grid(you.pos())))
79         return "";
80 
81     LevelInfo *li = travel_cache.find_level_info(level_id::current());
82     if (!li)
83         return "";
84 
85     const stair_info *si = li->get_stair(you.pos());
86     if (!si)
87         return "";
88 
89     if (stairs_destination_is_excluded(*si))
90         return "This staircase leads to a travel-excluded area!";
91 
92     return "";
93 }
94 
_bezotting_warning(branch_type branch)95 static string _bezotting_warning(branch_type branch)
96 {
97     if (branch == you.where_are_you || !bezotted_in(branch))
98         return "";
99 
100     const int turns = turns_until_zot_in(branch);
101     return make_stringf("You have just %d turns in %s to find a new floor before Zot consumes you.",
102                         turns, branches[branch].longname);
103 }
104 
check_next_floor_warning()105 bool check_next_floor_warning()
106 {
107     level_id  next_level_id = level_id::get_next_level_id(you.pos());
108 
109     crawl_state.level_annotation_shown = false;
110     const string annotation_warning = _annotation_exclusion_warning(next_level_id);
111 
112     const string target_warning = _target_exclusion_warning();
113     const string bezotting_warning = _bezotting_warning(next_level_id.branch);
114 
115     if (annotation_warning != "")
116         mprf(MSGCH_PROMPT, "%s", annotation_warning.c_str());
117     if (target_warning != "")
118         mprf(MSGCH_PROMPT, "%s", target_warning.c_str());
119     if (bezotting_warning != "")
120         mprf(MSGCH_PROMPT, "%s", bezotting_warning.c_str());
121 
122     const bool might_be_dangerous = annotation_warning != ""
123                                  || target_warning != ""
124                                  || bezotting_warning != "";
125 
126     if (might_be_dangerous
127         && !yesno("Enter next level anyway?", true, 'n', true, false))
128     {
129         canned_msg(MSG_OK);
130         interrupt_activity(activity_interrupt::force);
131         crawl_state.level_annotation_shown = false;
132         return false;
133     }
134     return true;
135 }
136 
_player_change_level_reset()137 static void _player_change_level_reset()
138 {
139     you.prev_targ  = MHITNOT;
140     if (you.pet_target != MHITYOU)
141         you.pet_target = MHITNOT;
142 
143     you.prev_grd_targ.reset();
144 }
145 
_player_change_level(level_id lev)146 static void _player_change_level(level_id lev)
147 {
148     you.depth         = lev.depth;
149     you.where_are_you = lev.branch;
150 }
151 
_maybe_destroy_shaft(const coord_def & p)152 static void _maybe_destroy_shaft(const coord_def &p)
153 {
154     trap_def* trap = trap_at(p);
155     if (trap && trap->type == TRAP_SHAFT)
156         trap->destroy(true);
157 }
158 
_stair_moves_pre(dungeon_feature_type stair)159 static bool _stair_moves_pre(dungeon_feature_type stair)
160 {
161     if (crawl_state.prev_cmd == CMD_WIZARD)
162         return false;
163 
164     if (stair != env.grid(you.pos()))
165         return false;
166 
167     if (feat_stair_direction(stair) == CMD_NO_CMD)
168         return false;
169 
170     if (!you.duration[DUR_REPEL_STAIRS_CLIMB])
171         return false;
172 
173     int pct;
174     if (you.duration[DUR_REPEL_STAIRS_MOVE])
175         pct = 29;
176     else
177         pct = 50;
178 
179     // When the effect is still strong, the chance to actually catch a stair
180     // is smaller. (Assuming the duration starts out at 500.)
181     const int dur = max(0, you.duration[DUR_REPEL_STAIRS_CLIMB] - 200);
182     pct += dur/20;
183 
184     if (!x_chance_in_y(pct, 100))
185         return false;
186 
187     // Get feature name before sliding stair over.
188     string stair_str = feature_description_at(you.pos(), false, DESC_THE);
189 
190     if (!slide_feature_over(you.pos()))
191         return false;
192 
193     string verb = stair_climb_verb(stair);
194 
195     mprf("%s moves away as you attempt to %s it!", stair_str.c_str(),
196          verb.c_str());
197 
198     you.turn_is_over = true;
199 
200     return true;
201 }
202 
_climb_message(dungeon_feature_type stair,bool going_up,branch_type old_branch)203 static void _climb_message(dungeon_feature_type stair, bool going_up,
204                            branch_type old_branch)
205 {
206     if (!is_connected_branch(old_branch))
207         return;
208 
209     if (feat_is_portal(stair))
210         mpr("The world spins around you as you enter the gateway.");
211     else if (feat_is_escape_hatch(stair))
212     {
213         if (going_up)
214             mpr("A mysterious force pulls you upwards.");
215         else
216         {
217             mprf("You %s downwards.",
218                  you.airborne() ? "fly" : "slide");
219         }
220         mpr("The hatch slams shut behind you.");
221     }
222     else if (feat_is_gate(stair))
223     {
224         mprf("You %s %s through the gate.",
225              you.airborne() ? "fly" : "go",
226              going_up ? "up" : "down");
227     }
228     else
229     {
230         mprf("You %s %swards.",
231              you.airborne() ? "fly" : "climb",
232              going_up ? "up" : "down");
233     }
234 }
235 
_clear_golubria_traps()236 static void _clear_golubria_traps()
237 {
238     for (auto c : find_golubria_on_level())
239     {
240         trap_def *trap = trap_at(c);
241         if (trap && trap->type == TRAP_GOLUBRIA)
242             trap->destroy();
243     }
244 }
245 
_clear_prisms()246 static void _clear_prisms()
247 {
248     for (auto &mons : menv_real)
249         if (mons.type == MONS_FULMINANT_PRISM)
250             mons.reset();
251 }
252 
_complete_zig()253 static void _complete_zig()
254 {
255     if (!zot_immune())
256         mpr("You have passed through the Ziggurat. Zot will hunt you nevermore.");
257     you.zigs_completed++;
258 }
259 
leaving_level_now(dungeon_feature_type stair_used)260 void leaving_level_now(dungeon_feature_type stair_used)
261 {
262     if (stair_used == DNGN_EXIT_ZIGGURAT)
263     {
264         if (you.depth == 27)
265             _complete_zig();
266         mark_milestone("zig.exit", make_stringf("left a ziggurat at level %d.",
267                        you.depth));
268     }
269 
270     if (stair_used == DNGN_EXIT_ABYSS)
271     {
272 #ifdef DEBUG
273         auto &vault_list =  you.vault_list[level_id::current()];
274         vault_list.push_back("[exit]");
275 #endif
276     }
277 
278     dungeon_events.fire_position_event(DET_PLAYER_CLIMBS, you.pos());
279     dungeon_events.fire_event(DET_LEAVING_LEVEL);
280 
281     _clear_golubria_traps();
282     _clear_prisms();
283 
284     end_recall();
285 }
286 
_update_travel_cache(const level_id & old_level,const coord_def & stair_pos)287 static void _update_travel_cache(const level_id& old_level,
288                                  const coord_def& stair_pos)
289 {
290     // If the old level is gone, nothing to save.
291     if (!you.save || !you.save->has_chunk(old_level.describe()))
292         return;
293 
294     // Update stair information for the stairs we just ascended, and the
295     // down stairs we're currently on.
296     level_id  new_level_id    = level_id::current();
297 
298     if (can_travel_interlevel())
299     {
300         LevelInfo &old_level_info =
301                     travel_cache.get_level_info(old_level);
302         LevelInfo &new_level_info =
303                     travel_cache.get_level_info(new_level_id);
304         new_level_info.update();
305 
306         // First we update the old level's stair.
307         level_pos lp;
308         lp.id  = new_level_id;
309         lp.pos = you.pos();
310 
311         bool guess = false;
312         // Ugly hack warning:
313         // The stairs in the Vestibule of Hell exhibit special behaviour:
314         // they always lead back to the dungeon level that the player
315         // entered the Vestibule from. This means that we need to pretend
316         // we don't know where the upstairs from the Vestibule go each time
317         // we take it. If we don't, interlevel travel may try to use portals
318         // to Hell as shortcuts between dungeon levels, which won't work,
319         // and will confuse the dickens out of the player (well, it confused
320         // the dickens out of me when it happened).
321         if ((new_level_id == BRANCH_DUNGEON || new_level_id == BRANCH_DEPTHS)
322             && old_level == BRANCH_VESTIBULE)
323         {
324             old_level_info.clear_stairs(DNGN_EXIT_HELL);
325         }
326         else
327             old_level_info.update_stair(stair_pos, lp, guess);
328 
329         // We *guess* that going up a staircase lands us on a downstair,
330         // and that we can descend that downstair and get back to where we
331         // came from. This assumption is guaranteed false when climbing out
332         // of one of the branches of Hell.
333         if (new_level_id != BRANCH_VESTIBULE
334             || !is_hell_subbranch(old_level.branch))
335         {
336             // Set the new level's stair, assuming arbitrarily that going
337             // downstairs will land you on the same upstairs you took to
338             // begin with (not necessarily true).
339             lp.id = old_level;
340             lp.pos = stair_pos;
341             new_level_info.update_stair(you.pos(), lp, true);
342         }
343     }
344 }
345 
346 // These checks are probably unnecessary.
_check_stairs(const dungeon_feature_type ftype,bool going_up)347 static bool _check_stairs(const dungeon_feature_type ftype, bool going_up)
348 {
349     // If it's not bidirectional, check that the player is headed
350     // in the right direction.
351     if (!feat_is_bidirectional_portal(ftype))
352     {
353         if (feat_stair_direction(ftype) != (going_up ? CMD_GO_UPSTAIRS
354                                                      : CMD_GO_DOWNSTAIRS))
355         {
356             if (ftype == DNGN_STONE_ARCH)
357                 mpr("There is nothing on the other side of the stone arch.");
358             else if (ftype == DNGN_ABANDONED_SHOP)
359                 mpr("This shop has been abandoned, nothing of value remains.");
360             else if (going_up)
361                 mpr("You can't go up here!");
362             else
363                 mpr("You can't go down here!");
364             return false;
365         }
366     }
367 
368     return true;
369 }
370 
_check_fall_down_stairs(const dungeon_feature_type ftype,bool going_up)371 static bool _check_fall_down_stairs(const dungeon_feature_type ftype, bool going_up)
372 {
373     if (!you.airborne()
374         && you.confused()
375         && !feat_is_escape_hatch(ftype)
376         && coinflip())
377     {
378         const char* fall_where = "down the stairs";
379         if (!feat_is_staircase(ftype))
380             fall_where = "through the gate";
381 
382         mprf("In your confused state, you trip and fall %s%s.",
383              going_up ? "back " : "", fall_where);
384         if (!feat_is_staircase(ftype))
385             ouch(1, KILLED_BY_FALLING_THROUGH_GATE);
386         else
387             ouch(1, KILLED_BY_FALLING_DOWN_STAIRS);
388 
389         // Note that if going downstairs, this only does damage.
390         // It doesn't cancel the level transition.
391         if (going_up)
392         {
393             you.turn_is_over = true;
394             return true;
395         }
396     }
397 
398     return false;
399 }
400 
_rune_effect(dungeon_feature_type ftype)401 static void _rune_effect(dungeon_feature_type ftype)
402 {
403     // Nothing even remotely flashy for Zig.
404     if (ftype != DNGN_ENTER_ZIGGURAT)
405     {
406         vector<int> runes;
407         for (int i = 0; i < NUM_RUNE_TYPES; i++)
408             if (you.runes[i])
409                 runes.push_back(i);
410 
411         ASSERT(runes.size() >= 1);
412         shuffle_array(runes);
413 
414         // Zot is extra flashy.
415         if (ftype == DNGN_ENTER_ZOT)
416         {
417             ASSERT(runes.size() >= 3);
418 
419             mprf("You insert the %s rune into the lock.", rune_type_name(runes[2]));
420 #ifdef USE_TILE_LOCAL
421             view_add_tile_overlay(you.pos(), tileidx_zap(rune_colour(runes[2])));
422             viewwindow(false);
423             update_screen();
424 #else
425             flash_view(UA_BRANCH_ENTRY, rune_colour(runes[2]));
426 #endif
427             mpr("The lock glows eerily!");
428             // included in default force_more_message
429 
430             mprf("You insert the %s rune into the lock.", rune_type_name(runes[1]));
431             big_cloud(CLOUD_BLUE_SMOKE, &you, you.pos(), 20, 7 + random2(7));
432             viewwindow();
433             update_screen();
434             mpr("Heavy smoke blows from the lock!");
435             // included in default force_more_message
436         }
437 
438         mprf("You insert the %s rune into the lock.", rune_type_name(runes[0]));
439 
440         if (silenced(you.pos()))
441             mpr("The gate opens wide!");
442         else
443             mpr("With a soft hiss the gate opens wide!");
444         // these are included in default force_more_message
445     }
446 }
447 
_gauntlet_effect()448 static void _gauntlet_effect()
449 {
450     // already doomed
451     if (you.stasis())
452         return;
453 
454     mprf(MSGCH_WARN, "The nature of this place prevents you from teleporting.");
455 
456     if (player_teleport(false))
457         mpr("You feel stable on this floor.");
458 }
459 
_new_level_amuses_xom(dungeon_feature_type feat,dungeon_feature_type old_feat,bool shaft,int shaft_depth,bool voluntary)460 static void _new_level_amuses_xom(dungeon_feature_type feat,
461                                   dungeon_feature_type old_feat,
462                                   bool shaft, int shaft_depth, bool voluntary)
463 {
464     switch (you.where_are_you)
465     {
466     default:
467         // Xom thinks it's funny if you enter a new level via shaft
468         // or escape hatch, for shafts it's funnier the deeper you fell.
469         if (shaft || feat_is_escape_hatch(feat))
470             xom_is_stimulated(shaft_depth * 50);
471         else if (!is_connected_branch(you.where_are_you))
472             xom_is_stimulated(25);
473         else
474             xom_is_stimulated(10);
475         break;
476 
477     case BRANCH_ZIGGURAT:
478         // The best way to die currently.
479         xom_is_stimulated(50);
480         break;
481 
482     case BRANCH_PANDEMONIUM:
483         xom_is_stimulated(100);
484         break;
485 
486     case BRANCH_ABYSS:
487         if (voluntary && old_feat == DNGN_ENTER_ABYSS)
488             xom_is_stimulated(100, XM_INTRIGUED);
489         else
490             xom_is_stimulated(200);
491         break;
492     }
493 }
494 
495 /**
496  * Determine destination level.
497  *
498  * @param how         How the player is trying to travel.
499  *                    (e.g. stairs, traps, portals, etc)
500  * @param forced      True if the player is forcing the traveling attempt.
501  *                    (e.g. forcibly exiting the abyss, etc)
502  * @param going_up    True if the player is going upstairs.
503  * @param known_shaft True if the player is intentionally shafting themself.
504  * @return            The destination level, if valid. Note the default value
505  *                    of dest is not valid (since depth = -1) and this is
506  *                    generally what is returned for invalid destinations.
507  *                    But note the special case when failing to climb stairs
508  *                    when attempting to leave the dungeon, depth = 1.
509  */
_travel_destination(const dungeon_feature_type how,bool forced,bool going_up,bool known_shaft)510 static level_id _travel_destination(const dungeon_feature_type how,
511                                     bool forced, bool going_up,
512                                     bool known_shaft)
513 {
514     const bool shaft = known_shaft || how == DNGN_TRAP_SHAFT;
515     level_id shaft_dest;
516     level_id dest;
517     if (shaft)
518     {
519         if (!is_valid_shaft_level())
520         {
521             if (known_shaft)
522                 mpr("The shaft disappears in a puff of logic!");
523             _maybe_destroy_shaft(you.pos());
524             return dest;
525         }
526 
527         shaft_dest = you.shaft_dest();
528     }
529     // How far down you fall via a shaft or hatch.
530     const int shaft_depth = (shaft ? shaft_dest.depth - you.depth : 1);
531 
532     // Only check the current position for a legal stair traverse.
533     // Check that we're going the right way (if we're not falling through
534     // a shaft or being forced).
535     if (!shaft && !forced && !_check_stairs(how, going_up))
536         return dest;
537 
538     // Up and down both work for some portals.
539     // Canonicalize the direction: hell exits into the vestibule are considered
540     // going up; everything else is going down. This mostly affects which way you
541     // fall if confused.
542     if (feat_is_bidirectional_portal(how))
543         going_up = (how == DNGN_ENTER_HELL && player_in_hell(false));
544 
545     if (_stair_moves_pre(how))
546         return dest;
547 
548     // Falling down is checked before the transition if going upstairs, since
549     // it might prevent the transition itself.
550     if (going_up && _check_fall_down_stairs(how, true))
551     {
552         // TODO: This probably causes an obscure bug where confused players
553         // going 'down' into the vestibule are twice as likely to fall, because
554         // they have to pass a check here, and later in floor_transition
555         // Right solution is probably to use the canonicalized direction everywhere
556 
557         // If player falls down the stairs trying to leave the dungeon, we set
558         // the destination depth to 1 (i.e. D:1)
559         if (how == DNGN_EXIT_DUNGEON)
560             dest.depth = 1;
561         return dest;
562     }
563 
564     if (shaft)
565     {
566         if (shaft_dest == level_id::current())
567         {
568             if (known_shaft)
569             {
570                 mpr("Strange, the shaft seems to lead back to this level.");
571                 mpr("The strain on the space-time continuum destroys the "
572                     "shaft!");
573             }
574             _maybe_destroy_shaft(you.pos());
575             return dest;
576         }
577 
578         if (!known_shaft)
579         {
580             mark_milestone("shaft", "fell down a shaft to "
581                                     + shaft_dest.describe() + ".");
582         }
583 
584         mprf("You %s into a shaft and drop %d floor%s!",
585              you.airborne() ? "are sucked" : "fall",
586              shaft_depth,
587              shaft_depth > 1 ? "s" : "");
588 
589         // Shafts are one-time-use.
590         mpr("The shaft crumbles and collapses.");
591         _maybe_destroy_shaft(you.pos());
592     }
593 
594     // Maybe perform the entry sequence (we check that they have enough runes
595     // in main.cc: _can_take_stairs())
596     for (branch_iterator it; it; ++it)
597     {
598         if (how != it->entry_stairs)
599             continue;
600 
601         if (!you.level_visited(level_id(it->id, 1))
602             && runes_for_branch(it->id) > 0)
603         {
604             _rune_effect(how);
605         }
606 
607         break;
608     }
609 
610     // Markers might be deleted when removing portals.
611     const string dst = env.markers.property_at(you.pos(), MAT_ANY, "dst");
612 
613     if (shaft)
614         return shaft_dest;
615     else
616         return stair_destination(how, dst, true);
617 }
618 
619 /**
620  * Check to see if transition will actually move the player.
621  *
622  * @param dest      The destination level (branch and depth).
623  * @param feat      The dungeon feature the player is standing on.
624  * @param going_up  True if the player is trying to go up stairs.
625  * @return          True if the level transition should happen.
626  */
_level_transition_moves_player(level_id dest,dungeon_feature_type feat,bool going_up)627 static bool _level_transition_moves_player(level_id dest,
628                                            dungeon_feature_type feat,
629                                            bool going_up)
630 {
631     bool trying_to_exit = feat == DNGN_EXIT_DUNGEON && going_up;
632 
633     // When exiting the dungeon, dest is not valid (depth = -1)
634     // So the player can transition with an invalid dest ONLY when exiting.
635     // Otherwise (i.e. not exiting) dest must be valid.
636     return dest.is_valid() != trying_to_exit;
637 }
638 
639 /**
640  * Transition to a different level.
641  *
642  * @param how The type of stair/portal tile the player is being conveyed through
643  * @param whence The tile the player was on at the beginning of the transition
644  *               (likely the same as how, unless forced is true)
645  * @param whither The destination level
646  * @param shaft Is the player going down a shaft?
647  */
floor_transition(dungeon_feature_type how,const dungeon_feature_type whence,level_id whither,bool forced,bool going_up,bool shaft,bool update_travel_cache)648 void floor_transition(dungeon_feature_type how,
649                       const dungeon_feature_type whence, level_id whither,
650                       bool forced, bool going_up, bool shaft,
651                       bool update_travel_cache)
652 {
653     const level_id old_level = level_id::current();
654 
655     // Clean up fake blood.
656     heal_flayed_effect(&you, true, true);
657 
658     // We "stepped".
659     if (!forced)
660         apply_barbs_damage();
661 
662     // Magical level changes (which currently only exist "downwards") need this.
663     clear_trapping_net();
664     end_wait_spells();
665     you.stop_constricting_all();
666     you.stop_being_constricted();
667     you.clear_beholders();
668     you.clear_fearmongers();
669     dec_frozen_ramparts(you.duration[DUR_FROZEN_RAMPARTS]);
670 
671     // Fire level-leaving trigger.
672     leaving_level_now(how);
673 
674     // Not entirely accurate - the player could die before
675     // reaching the Abyss.
676     if (!forced && whence == DNGN_ENTER_ABYSS)
677     {
678         mark_milestone("abyss.enter", "entered the Abyss!");
679         take_note(Note(NOTE_MESSAGE, 0, 0, "Voluntarily entered the Abyss."), true);
680     }
681     else if (!forced && whence == DNGN_EXIT_THROUGH_ABYSS)
682     {
683         mark_milestone("abyss.enter", "escaped (hah) into the Abyss!");
684         take_note(Note(NOTE_MESSAGE, 0, 0, "Took an exit into the Abyss."), true);
685     }
686     else if (how == DNGN_EXIT_ABYSS
687              && you.chapter != CHAPTER_POCKET_ABYSS)
688     {
689         mark_milestone("abyss.exit", "escaped from the Abyss!");
690         you.attribute[ATTR_BANISHMENT_IMMUNITY] = you.elapsed_time + 100
691                                                   + random2(100);
692         you.banished_by = "";
693         you.banished_power = 0;
694     }
695 
696     // Interlevel travel data.
697     const bool collect_travel_data = can_travel_interlevel();
698     if (collect_travel_data)
699     {
700         LevelInfo &old_level_info = travel_cache.get_level_info(old_level);
701         old_level_info.update();
702     }
703 
704     const coord_def stair_pos = you.pos();
705 
706     if (how == DNGN_EXIT_DUNGEON)
707     {
708         you.depth = 0;
709         mpr("You have escaped!");
710 
711         if (player_has_orb())
712             ouch(INSTANT_DEATH, KILLED_BY_WINNING);
713 
714         ouch(INSTANT_DEATH, KILLED_BY_LEAVING);
715     }
716 
717     if (how == DNGN_ENTER_ZIGGURAT)
718         dungeon_terrain_changed(you.pos(), DNGN_STONE_ARCH);
719 
720     if (how == DNGN_ENTER_PANDEMONIUM
721         || how == DNGN_ENTER_ABYSS
722         || feat_is_portal_entrance(how))
723     {
724         you.level_stack.push_back(level_pos::current());
725     }
726 
727     // Actually change the player's branch and depth, along with some cleanup.
728     _player_change_level_reset();
729     _player_change_level(whither);
730 
731     // Some branch specific messages.
732     if (old_level.branch == BRANCH_VESTIBULE
733         && !is_hell_subbranch(you.where_are_you))
734     {
735         mpr("Thank you for visiting Hell. Please come again soon.");
736     }
737 
738     if (how == DNGN_EXIT_ABYSS
739         || how == DNGN_EXIT_PANDEMONIUM
740         || how == DNGN_EXIT_THROUGH_ABYSS)
741     {
742         mpr("You pass through the gate.");
743         take_note(Note(NOTE_MESSAGE, 0, 0,
744             how == DNGN_EXIT_ABYSS ? "Escaped the Abyss" :
745             how == DNGN_EXIT_PANDEMONIUM ? "Escaped Pandemonium" :
746             how == DNGN_EXIT_THROUGH_ABYSS ? "Escaped into the Abyss" :
747             "Buggered into bugdom"), true);
748 
749         if (!you.wizard || !crawl_state.is_replaying_keys())
750             more();
751     }
752 
753     // Fixup exits from the Hell branches.
754     if (player_in_branch(BRANCH_VESTIBULE)
755         && is_hell_subbranch(old_level.branch))
756     {
757         how = branches[old_level.branch].entry_stairs;
758     }
759 
760     // Check for falling down the stairs or portal.
761     if (!going_up && !shaft && !forced)
762         _check_fall_down_stairs(how, false);
763 
764     if (shaft)
765         how = DNGN_TRAP_SHAFT;
766 
767     switch (you.where_are_you)
768     {
769     case BRANCH_ABYSS:
770         // There are no abyssal stairs that go up, so this whole case is only
771         // when going down.
772         you.props.erase(ABYSS_SPAWNED_XP_EXIT_KEY);
773         if (old_level.branch == BRANCH_ABYSS)
774         {
775             mprf(MSGCH_BANISHMENT, "You plunge deeper into the Abyss.");
776             if (!you.runes[RUNE_ABYSSAL] && you.depth >= ABYSSAL_RUNE_MIN_LEVEL)
777                 mpr("The abyssal rune of Zot can be found at this depth.");
778             break;
779         }
780         if (!forced)
781             mpr("You enter the Abyss!");
782 
783         mpr("To return, you must find a gate leading back.");
784         mpr("Killing monsters will force the Abyss to allow you passage.");
785         if (have_passive(passive_t::slow_abyss))
786         {
787             mprf(MSGCH_GOD, you.religion,
788                  "You feel %s slowing down the madness of this place.",
789                  god_name(you.religion).c_str());
790         }
791 
792         you.props[ABYSS_STAIR_XP_KEY] = EXIT_XP_COST;
793 
794         // Re-entering the Abyss halves accumulated speed.
795         you.abyss_speed /= 2;
796         learned_something_new(HINT_ABYSS);
797         break;
798 
799     case BRANCH_PANDEMONIUM:
800         if (old_level.branch == BRANCH_PANDEMONIUM)
801             mpr("You pass into a different region of Pandemonium.");
802         break;
803 
804     default:
805         // This hits both cases.
806         if (!shaft)
807             _climb_message(how, going_up, old_level.branch);
808         break;
809     }
810 
811     // Did we enter a different branch?
812     if (!player_in_branch(old_level.branch))
813     {
814         const branch_type branch = you.where_are_you;
815         if (branch_entered(branch))
816             mprf("Welcome back to %s!", branches[branch].longname);
817         else if (how == branches[branch].entry_stairs)
818         {
819             if (branches[branch].entry_message)
820                 mpr(branches[branch].entry_message);
821             else if (branch != BRANCH_ABYSS) // too many messages...
822                 mprf("Welcome to %s!", branches[branch].longname);
823         }
824         const bool was_bezotted = bezotted_in(old_level.branch);
825         if (bezotted())
826         {
827             if (was_bezotted)
828                 mpr("Zot already knows this place too well. Descend or flee this branch!");
829             else
830                 mpr("Zot's attention fixes on you again. Descend or flee this branch!");
831         }
832         else if (was_bezotted)
833         {
834             if (branch == BRANCH_ABYSS)
835                 mpr("Zot has no power in the Abyss.");
836             else
837                 mpr("You feel Zot lose track of you.");
838         }
839 
840         if (branch == BRANCH_GAUNTLET)
841             _gauntlet_effect();
842 
843         const set<branch_type> boring_branch_exits = {
844             BRANCH_TEMPLE,
845             BRANCH_BAZAAR,
846             BRANCH_TROVE
847         };
848 
849         // Did we leave a notable branch for the first time?
850         if (boring_branch_exits.count(old_level.branch) == 0
851             && !you.branches_left[old_level.branch])
852         {
853             string old_branch_string = branches[old_level.branch].longname;
854             if (starts_with(old_branch_string, "The "))
855                 old_branch_string[0] = tolower_safe(old_branch_string[0]);
856             mark_milestone("br.exit", "left " + old_branch_string + ".",
857                            old_level.describe());
858             you.branches_left.set(old_level.branch);
859         }
860         if (how == branches[branch].entry_stairs)
861         {
862             const string noise_desc = branch_noise_desc(branch);
863             if (!noise_desc.empty())
864                 mpr(noise_desc);
865 
866             const string rune_msg = branch_rune_desc(branch, true);
867             if (!rune_msg.empty())
868                 mpr(rune_msg);
869         }
870 
871         // Entered a branch from its parent.
872         if (parent_branch(branch) == old_level.branch)
873             enter_branch(branch, old_level);
874     }
875 
876     // Warn Formicids if they cannot shaft here
877     if (player_has_ability(ABIL_SHAFT_SELF, true)
878                                 && !is_valid_shaft_level())
879     {
880         mpr("Beware, you cannot shaft yourself on this level.");
881     }
882 
883     const bool newlevel = load_level(how, LOAD_ENTER_LEVEL, old_level);
884 
885     if (newlevel)
886     {
887         _new_level_amuses_xom(how, whence, shaft,
888                               (shaft ? whither.depth - old_level.depth : 1),
889                               !forced);
890     }
891 
892     // This should maybe go in load_level?
893     if (you.where_are_you == BRANCH_ABYSS)
894         generate_random_blood_spatter_on_level();
895 
896     you.turn_is_over = true;
897 
898     save_game_state();
899 
900     new_level();
901 
902     moveto_location_effects(whence);
903 
904     trackers_init_new_level();
905 
906     if (update_travel_cache && !shaft)
907         _update_travel_cache(old_level, stair_pos);
908 
909     // Preventing obvious finding of stairs at your position.
910     env.map_seen.set(you.pos());
911 
912     viewwindow();
913     update_screen();
914 
915     // There's probably a reason for this. I don't know it.
916     if (going_up)
917         seen_monsters_react();
918     else
919         maybe_update_stashes();
920 
921     autotoggle_autopickup(false);
922     request_autopickup();
923 }
924 
925 /**
926  * Try to go up or down stairs.
927  *
928  * @param force_stair         The type of stair/portal to take. By default,
929  *      use whatever tile is under the player. But this can be overridden
930  *      (e.g. passing DNGN_EXIT_ABYSS forces the player out of the abyss)
931  * @param going_up            True if the player is going upstairs
932  * @param force_known_shaft   True if player is shafting themselves via ability.
933  * @param update_travel_cache True if travel cache should be updated.
934  */
take_stairs(dungeon_feature_type force_stair,bool going_up,bool force_known_shaft,bool update_travel_cache)935 void take_stairs(dungeon_feature_type force_stair, bool going_up,
936                  bool force_known_shaft, bool update_travel_cache)
937 {
938     const dungeon_feature_type old_feat = orig_terrain(you.pos());
939     dungeon_feature_type how = force_stair ? force_stair : old_feat;
940 
941     // Taking a shaft manually (stepping on a known shaft, or using shaft ability)
942     const bool known_shaft = (!force_stair
943                               && get_trap_type(you.pos()) == TRAP_SHAFT)
944                              || (force_stair == DNGN_TRAP_SHAFT
945                                  && force_known_shaft);
946     // Latter case is falling down a shaft.
947     const bool shaft = known_shaft || force_stair == DNGN_TRAP_SHAFT;
948 
949     level_id whither = _travel_destination(how, bool(force_stair), going_up,
950                                            known_shaft);
951 
952     if (!_level_transition_moves_player(whither, old_feat, going_up))
953         return;
954 
955     // The transition is "forced" for the purpose of floor_transition if
956     // a force_stair feature is specified and force_known_shaft is not set
957     // (in the latter case, the player 'moved').
958     floor_transition(how, old_feat, whither,
959                      bool(force_stair) && !force_known_shaft,
960                      going_up, shaft, update_travel_cache);
961 }
962 
up_stairs(dungeon_feature_type force_stair,bool update_travel_cache)963 void up_stairs(dungeon_feature_type force_stair, bool update_travel_cache)
964 {
965     take_stairs(force_stair, true, false, update_travel_cache);
966 }
967 
968 // Find the other end of the stair or portal at location pos on the current
969 // level. for_real is true if we are actually traversing the feature rather
970 // than merely asking what is on the other side.
stair_destination(coord_def pos,bool for_real)971 level_id stair_destination(coord_def pos, bool for_real)
972 {
973     return stair_destination(orig_terrain(pos),
974                              env.markers.property_at(pos, MAT_ANY, "dst"),
975                              for_real);
976 }
977 
978 // Find the other end of a stair or portal on the current level. feat is the
979 // type of feature (DNGN_EXIT_ABYSS, for example), dst is the target of a
980 // portal vault entrance (and is ignored for other types of features), and
981 // for_real is true if we are actually traversing the feature rather than
982 // merely asking what is on the other side.
stair_destination(dungeon_feature_type feat,const string & dst,bool for_real)983 level_id stair_destination(dungeon_feature_type feat, const string &dst,
984                            bool for_real)
985 {
986 #if TAG_MAJOR_VERSION == 34
987     if (feat == DNGN_ESCAPE_HATCH_UP && player_in_branch(BRANCH_LABYRINTH))
988         feat = DNGN_EXIT_LABYRINTH;
989 #else
990     UNUSED(dst); // see below in the switch
991 #endif
992     if (branches[you.where_are_you].exit_stairs == feat
993         && parent_branch(you.where_are_you) < NUM_BRANCHES
994         && feat != DNGN_EXIT_ZIGGURAT)
995     {
996         level_id lev = brentry[you.where_are_you];
997         if (!lev.is_valid())
998         {
999             // Wizmode, the branch wasn't generated this game.
1000             // Pick the middle of the range instead.
1001             lev = level_id(branches[you.where_are_you].parent_branch,
1002                            (branches[you.where_are_you].mindepth
1003                             + branches[you.where_are_you].maxdepth) / 2);
1004             ASSERT(lev.is_valid());
1005         }
1006 
1007         return lev;
1008     }
1009 
1010     if (feat_is_portal_exit(feat))
1011         feat = DNGN_EXIT_PANDEMONIUM;
1012 
1013     switch (feat)
1014     {
1015     case DNGN_ESCAPE_HATCH_UP:
1016     case DNGN_STONE_STAIRS_UP_I:
1017     case DNGN_STONE_STAIRS_UP_II:
1018     case DNGN_STONE_STAIRS_UP_III:
1019         if (you.depth <= 1)
1020         {
1021             if (you.wizard && !for_real)
1022                 return level_id();
1023             die("upstairs from top of a branch");
1024         }
1025         return level_id(you.where_are_you, you.depth - 1);
1026 
1027     case DNGN_EXIT_HELL:
1028         // If set, it would be found as a branch exit.
1029         if (you.wizard)
1030         {
1031             if (for_real)
1032             {
1033                 mprf(MSGCH_ERROR, "Error: no Hell exit level, how in the "
1034                                   "Vestibule did you get here? Let's go to D:1.");
1035             }
1036             return level_id(BRANCH_DUNGEON, 1);
1037         }
1038         else
1039             die("hell exit without return destination");
1040 
1041     case DNGN_ABYSSAL_STAIR:
1042         ASSERT(player_in_branch(BRANCH_ABYSS));
1043         push_features_to_abyss();
1044     case DNGN_ESCAPE_HATCH_DOWN:
1045     case DNGN_STONE_STAIRS_DOWN_I:
1046     case DNGN_STONE_STAIRS_DOWN_II:
1047     case DNGN_STONE_STAIRS_DOWN_III:
1048     {
1049         ASSERT(!at_branch_bottom());
1050         level_id lev = level_id::current();
1051         lev.depth++;
1052         return lev;
1053     }
1054 
1055     case DNGN_TRANSIT_PANDEMONIUM:
1056         return level_id(BRANCH_PANDEMONIUM);
1057 
1058     case DNGN_EXIT_THROUGH_ABYSS:
1059         return level_id(BRANCH_ABYSS);
1060 
1061 #if TAG_MAJOR_VERSION == 34
1062     case DNGN_ENTER_PORTAL_VAULT:
1063         if (dst.empty())
1064         {
1065             if (for_real)
1066                 die("portal without a destination");
1067             else
1068                 return level_id();
1069         }
1070         try
1071         {
1072             return level_id::parse_level_id(dst);
1073         }
1074         catch (const bad_level_id &err)
1075         {
1076             die("Invalid destination for portal: %s", err.what());
1077         }
1078 #endif
1079 
1080     case DNGN_ENTER_HELL:
1081         if (for_real && !player_in_hell())
1082             brentry[BRANCH_VESTIBULE] = level_id::current();
1083         return level_id(BRANCH_VESTIBULE);
1084 
1085     case DNGN_EXIT_ABYSS:
1086         if (you.chapter == CHAPTER_POCKET_ABYSS)
1087             return level_id(BRANCH_DUNGEON, 1);
1088 #if TAG_MAJOR_VERSION == 34
1089     case DNGN_EXIT_PORTAL_VAULT:
1090 #endif
1091     case DNGN_EXIT_PANDEMONIUM:
1092         if (you.level_stack.empty())
1093         {
1094             if (you.wizard)
1095             {
1096                 if (for_real)
1097                 {
1098                     mprf(MSGCH_ERROR, "Error: no return path. You did create "
1099                          "the exit manually, didn't you? Let's go to D:1.");
1100                 }
1101                 return level_id(BRANCH_DUNGEON, 1);
1102             }
1103             die("no return path from a portal (%s)",
1104                 level_id::current().describe().c_str());
1105         }
1106         return you.level_stack.back().id;
1107     case DNGN_ENTER_ABYSS:
1108         push_features_to_abyss();
1109         break;
1110     default:
1111         break;
1112     }
1113 
1114     // Try to find a branch stair.
1115     for (branch_iterator it; it; ++it)
1116     {
1117         if (it->entry_stairs == feat)
1118             return level_id(it->id);
1119     }
1120 
1121     return level_id();
1122 }
1123 
1124 // TODO(Zannick): Fully merge with up_stairs into take_stairs.
down_stairs(dungeon_feature_type force_stair,bool force_known_shaft,bool update_travel_cache)1125 void down_stairs(dungeon_feature_type force_stair, bool force_known_shaft, bool update_travel_cache)
1126 {
1127     take_stairs(force_stair, false, force_known_shaft, update_travel_cache);
1128 }
1129 
_update_level_state()1130 static void _update_level_state()
1131 {
1132     env.level_state = 0;
1133 
1134     vector<coord_def> golub = find_golubria_on_level();
1135     if (!golub.empty())
1136         env.level_state |= LSTATE_GOLUBRIA;
1137 
1138     for (monster_iterator mon_it; mon_it; ++mon_it)
1139     {
1140         if (mons_allows_beogh(**mon_it))
1141             env.level_state |= LSTATE_BEOGH;
1142         if (mon_it->has_ench(ENCH_STILL_WINDS))
1143             env.level_state |= LSTATE_STILL_WINDS;
1144         if (mon_it->has_ench(ENCH_AWAKEN_FOREST))
1145         {
1146             env.forest_awoken_until
1147                 = you.elapsed_time
1148                   + mon_it->get_ench(ENCH_AWAKEN_FOREST).duration;
1149         }
1150     }
1151 
1152 #if TAG_MAJOR_VERSION == 34
1153     const bool have_ramparts = you.duration[DUR_FROZEN_RAMPARTS];
1154     const auto &ramparts_pos = you.props[FROZEN_RAMPARTS_KEY].get_coord();
1155 #endif
1156     for (rectangle_iterator ri(0); ri; ++ri)
1157     {
1158         if (env.grid(*ri) == DNGN_SLIMY_WALL)
1159             env.level_state |= LSTATE_SLIMY_WALL;
1160 
1161         if (is_icecovered(*ri))
1162 #if TAG_MAJOR_VERSION == 34
1163         {
1164             // Buggy versions of Frozen Ramparts didn't properly clear
1165             // FPROP_ICY from walls in some cases, so we detect invalid walls
1166             // and remove the flag.
1167             if (have_ramparts
1168                 && ramparts_pos.distance_from(*ri) <= 3
1169                 && cell_see_cell(*ri, ramparts_pos, LOS_NO_TRANS))
1170             {
1171 #endif
1172             env.level_state |= LSTATE_ICY_WALL;
1173 #if TAG_MAJOR_VERSION == 34
1174             }
1175             else
1176                 env.pgrid(*ri) &= ~FPROP_ICY;
1177         }
1178 #endif
1179     }
1180 
1181     env.orb_pos = coord_def();
1182     if (item_def* orb = find_floor_item(OBJ_ORBS, ORB_ZOT))
1183         env.orb_pos = orb->pos;
1184     else if (player_has_orb())
1185     {
1186         env.orb_pos = you.pos();
1187         invalidate_agrid(true);
1188     }
1189 }
1190 
new_level(bool restore)1191 void new_level(bool restore)
1192 {
1193     print_stats_level();
1194 #ifdef DGL_WHEREIS
1195     whereis_record();
1196 #endif
1197 
1198     _update_level_state();
1199 
1200     if (restore)
1201         return;
1202 
1203     cancel_polar_vortex();
1204 
1205     if (player_in_branch(BRANCH_ZIGGURAT))
1206         you.zig_max = max(you.zig_max, you.depth);
1207 }
1208