1 /**
2  * @file
3  * @brief Misc abyss specific functions.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "abyss.h"
9 
10 #include <algorithm>
11 #include <cmath>
12 #include <cstdlib>
13 #include <queue>
14 
15 #include "act-iter.h"
16 #include "areas.h"
17 #include "bloodspatter.h"
18 #include "branch.h"
19 #include "cloud.h"
20 #include "colour.h"
21 #include "coordit.h"
22 #include "dbg-scan.h"
23 #include "dbg-util.h"
24 #include "delay.h"
25 #include "dgn-overview.h"
26 #include "dgn-proclayouts.h"
27 #include "tile-env.h"
28 #include "files.h"
29 #include "god-companions.h" // hep stuff
30 #include "god-passive.h" // passive_t::slow_abyss
31 #include "hiscores.h"
32 #include "item-prop.h"
33 #include "item-status-flag-type.h"
34 #include "items.h"
35 #include "libutil.h"
36 #include "mapmark.h"
37 #include "maps.h"
38 #include "message.h"
39 #include "mon-cast.h"
40 #include "mon-death.h"
41 #include "mon-pathfind.h"
42 #include "mon-pick.h"
43 #include "mon-place.h"
44 #include "mon-transit.h"
45 #include "notes.h"
46 #include "output.h" // redraw_screens
47 #include "religion.h"
48 #include "spl-clouds.h" // big_cloud
49 #include "stash.h"
50 #include "state.h"
51 #include "stairs.h"
52 #include "stringutil.h"
53 #include "terrain.h"
54 #include "rltiles/tiledef-dngn.h"
55 #include "tileview.h"
56 #include "timed-effects.h"
57 #include "traps.h"
58 #include "travel.h"
59 #include "view.h"
60 #include "xom.h"
61 
62 const coord_def ABYSS_CENTRE(GXM / 2, GYM / 2);
63 
64 static const int ABYSSAL_RUNE_MAX_ROLL = 200;
65 
66 abyss_state abyssal_state;
67 
68 static ProceduralLayout *abyssLayout = nullptr, *levelLayout = nullptr;
69 
70 typedef priority_queue<ProceduralSample, vector<ProceduralSample>, ProceduralSamplePQCompare> sample_queue;
71 
72 static sample_queue abyss_sample_queue;
73 static vector<dungeon_feature_type> abyssal_features;
74 static list<monster*> displaced_monsters;
75 
76 static void abyss_area_shift();
77 static void _push_items();
78 static void _push_displaced_monster(monster* mon);
79 
80 // If not_seen is true, don't place the feature where it can be seen from
81 // the centre. Returns the chosen location, or INVALID_COORD if it
82 // could not be placed.
_place_feature_near(const coord_def & centre,int radius,dungeon_feature_type candidate,dungeon_feature_type replacement,int tries,bool not_seen=false)83 static coord_def _place_feature_near(const coord_def &centre,
84                                      int radius,
85                                      dungeon_feature_type candidate,
86                                      dungeon_feature_type replacement,
87                                      int tries, bool not_seen = false)
88 {
89     coord_def cp = INVALID_COORD;
90     for (int i = 0; i < tries; ++i)
91     {
92         coord_def offset;
93         offset.x = random_range(-radius, radius);
94         offset.y = random_range(-radius, radius);
95         cp = centre + offset;
96 
97         if (cp == centre || !in_bounds(cp))
98             continue;
99 
100         if (not_seen && cell_see_cell_nocache(cp, centre))
101             continue;
102 
103         if (env.grid(cp) == candidate)
104         {
105             dprf(DIAG_ABYSS, "Placing %s at (%d,%d)",
106                  dungeon_feature_name(replacement),
107                  cp.x, cp.y);
108             env.grid(cp) = replacement;
109             return cp;
110         }
111     }
112     return INVALID_COORD;
113 }
114 
115 // Returns a feature suitable for use in the proto-Abyss level.
_abyss_proto_feature()116 static dungeon_feature_type _abyss_proto_feature()
117 {
118     return random_choose_weighted(3000, DNGN_FLOOR,
119                                    600, DNGN_ROCK_WALL,
120                                    300, DNGN_STONE_WALL,
121                                    100, DNGN_METAL_WALL,
122                                      1, DNGN_CLOSED_DOOR);
123 }
124 
_write_abyssal_features()125 static void _write_abyssal_features()
126 {
127     if (abyssal_features.empty())
128         return;
129 
130     dprf(DIAG_ABYSS, "Writing a mock-up of old level.");
131     ASSERT((int)abyssal_features.size() == sqr(2 * LOS_RADIUS + 1));
132     const int scalar = 0xFF;
133     int index = 0;
134     for (int x = -LOS_RADIUS; x <= LOS_RADIUS; x++)
135     {
136         for (int y = -LOS_RADIUS; y <= LOS_RADIUS; y++)
137         {
138             coord_def p(x, y);
139             const int dist = p.rdist();
140             p += ABYSS_CENTRE;
141 
142             int chance = pow(0.98, dist * dist) * scalar;
143             if (!map_masked(p, MMT_VAULT))
144             {
145                 if (dist < 2 || x_chance_in_y(chance, scalar))
146                 {
147                     if (abyssal_features[index] != DNGN_UNSEEN)
148                     {
149                         env.grid(p) = abyssal_features[index];
150                         env.level_map_mask(p) = MMT_VAULT;
151                         if (cell_is_solid(p))
152                             delete_cloud(p);
153                         if (monster* mon = monster_at(p))
154                             _push_displaced_monster(mon);
155                     }
156                 }
157                 else
158                 {
159                     //Entombing the player is lame.
160                     env.grid(p) = DNGN_FLOOR;
161                 }
162             }
163 
164             ++index;
165         }
166     }
167 
168     _push_items();
169     abyssal_features.clear();
170 }
171 
172 // Returns the roll to use to check if we want to create an abyssal rune.
_abyssal_rune_roll()173 static int _abyssal_rune_roll()
174 {
175     if (you.runes[RUNE_ABYSSAL] || you.depth < ABYSSAL_RUNE_MIN_LEVEL)
176         return -1;
177     const bool god_favoured = have_passive(passive_t::attract_abyssal_rune);
178 
179     const double depth = you.depth + god_favoured;
180 
181     return (int) pow(100.0, depth/(1 + brdepth[BRANCH_ABYSS]));
182 }
183 
_abyss_fixup_vault(const vault_placement * vp)184 static void _abyss_fixup_vault(const vault_placement *vp)
185 {
186     for (vault_place_iterator vi(*vp); vi; ++vi)
187     {
188         const coord_def p(*vi);
189         const dungeon_feature_type feat(env.grid(p));
190         if (feat_is_stair(feat)
191             && feat != DNGN_EXIT_ABYSS
192             && feat != DNGN_ABYSSAL_STAIR
193             && !feat_is_portal_entrance(feat))
194         {
195             env.grid(p) = DNGN_FLOOR;
196         }
197 
198         tile_init_flavour(p);
199     }
200 }
201 
_abyss_place_map(const map_def * mdef)202 static bool _abyss_place_map(const map_def *mdef)
203 {
204     // This is to prevent the player position from being updated by vaults
205     // until after everything is done.
206     unwind_bool gen(crawl_state.generating_level, true);
207 
208     try
209     {
210         if (dgn_safe_place_map(mdef, true, false, INVALID_COORD))
211         {
212             _abyss_fixup_vault(env.level_vaults.back().get());
213             return true;
214         }
215     }
216     catch (dgn_veto_exception &e)
217     {
218         dprf(DIAG_ABYSS, "Abyss map placement vetoed: %s", e.what());
219     }
220     return false;
221 }
222 
_abyss_place_vault_tagged(const map_bitmask & abyss_genlevel_mask,const string & tag)223 static bool _abyss_place_vault_tagged(const map_bitmask &abyss_genlevel_mask,
224                                       const string &tag)
225 {
226     const map_def *map = random_map_for_tag(tag, true, true, MB_FALSE);
227     if (map)
228     {
229         unwind_vault_placement_mask vaultmask(&abyss_genlevel_mask);
230         return _abyss_place_map(map);
231     }
232     return false;
233 }
234 
_abyss_postvault_fixup()235 static void _abyss_postvault_fixup()
236 {
237     fixup_misplaced_items();
238     link_items();
239     dgn_make_transporters_from_markers();
240     env.markers.activate_all();
241 }
242 
_abyss_place_rune_vault(const map_bitmask & abyss_genlevel_mask)243 static bool _abyss_place_rune_vault(const map_bitmask &abyss_genlevel_mask)
244 {
245     // Make sure we're not about to link bad items.
246     debug_item_scan();
247 
248     bool result = false;
249     int tries = 10;
250     do
251     {
252         result = _abyss_place_vault_tagged(abyss_genlevel_mask, "abyss_rune");
253     }
254     while (!result && --tries);
255 
256     // Make sure the rune is linked.
257     // XXX: I'm fairly sure this does nothing if result == false,
258     //      but leaving it alone for now. -- Grunt
259     _abyss_postvault_fixup();
260     return result;
261 }
262 
_abyss_place_rune(const map_bitmask & abyss_genlevel_mask)263 static bool _abyss_place_rune(const map_bitmask &abyss_genlevel_mask)
264 {
265     // Use a rune vault if there's one.
266     if (_abyss_place_rune_vault(abyss_genlevel_mask))
267         return true;
268 
269     coord_def chosen_spot;
270     int places_found = 0;
271 
272     // Pick a random spot to drop the rune. We specifically do not use
273     // random_in_bounds and similar, because we may be dealing with a
274     // non-rectangular region, and we want to place the rune fairly.
275     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
276     {
277         const coord_def p(*ri);
278         if (abyss_genlevel_mask(p)
279             && env.grid(p) == DNGN_FLOOR && env.igrid(p) == NON_ITEM
280             && one_chance_in(++places_found))
281         {
282             chosen_spot = p;
283         }
284     }
285 
286     if (places_found)
287     {
288         dprf(DIAG_ABYSS, "Placing abyssal rune at (%d,%d)",
289              chosen_spot.x, chosen_spot.y);
290         int item_ind  = items(true, OBJ_RUNES, RUNE_ABYSSAL, 0);
291         if (item_ind != NON_ITEM)
292             item_colour(env.item[item_ind]);
293         move_item_to_grid(&item_ind, chosen_spot);
294         return item_ind != NON_ITEM;
295     }
296 
297     return false;
298 }
299 
300 // Returns true if items can be generated on the given square.
_abyss_square_accepts_items(const map_bitmask & abyss_genlevel_mask,coord_def p)301 static bool _abyss_square_accepts_items(const map_bitmask &abyss_genlevel_mask,
302                                         coord_def p)
303 {
304     return abyss_genlevel_mask(p)
305            && env.grid(p) == DNGN_FLOOR
306            && env.igrid(p) == NON_ITEM
307            && !map_masked(p, MMT_VAULT);
308 }
309 
_abyss_create_items(const map_bitmask & abyss_genlevel_mask,bool placed_abyssal_rune)310 static int _abyss_create_items(const map_bitmask &abyss_genlevel_mask,
311                                bool placed_abyssal_rune)
312 {
313     // During game start, number and level of items mustn't be higher than
314     // that on level 1.
315     int num_items = 150, items_level = 52;
316     int items_placed = 0;
317 
318     if (player_in_starting_abyss())
319     {
320         num_items   = 3 + roll_dice(3, 11);
321         items_level = 0;
322     }
323 
324     const int abyssal_rune_roll = _abyssal_rune_roll();
325     bool should_place_abyssal_rune = false;
326     vector<coord_def> chosen_item_places;
327     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
328     {
329         if (_abyss_square_accepts_items(abyss_genlevel_mask, *ri))
330         {
331             if (items_placed < num_items && one_chance_in(200))
332             {
333                 // [ds] Don't place abyssal rune in this loop to avoid
334                 // biasing rune placement toward the north-west of the
335                 // abyss level. Instead, make a note of whether we
336                 // decided to place the abyssal rune at all, and if we
337                 // did, place it randomly somewhere in the map at the
338                 // end of the item-gen pass. We may as a result create
339                 // (num_items + 1) items instead of num_items, which
340                 // is acceptable.
341                 if (!placed_abyssal_rune && !should_place_abyssal_rune
342                     && abyssal_rune_roll != -1
343                     && x_chance_in_y(abyssal_rune_roll, ABYSSAL_RUNE_MAX_ROLL))
344                 {
345                     should_place_abyssal_rune = true;
346                 }
347 
348                 chosen_item_places.push_back(*ri);
349             }
350         }
351     }
352 
353     if (!placed_abyssal_rune && should_place_abyssal_rune)
354     {
355         if (_abyss_place_rune(abyss_genlevel_mask))
356             ++items_placed;
357     }
358 
359     for (const coord_def &place : chosen_item_places)
360     {
361         if (_abyss_square_accepts_items(abyss_genlevel_mask, place))
362         {
363             int thing_created = items(true, OBJ_RANDOM, OBJ_RANDOM,
364                                       items_level);
365             move_item_to_grid(&thing_created, place);
366             if (thing_created != NON_ITEM)
367                 items_placed++;
368         }
369     }
370 
371     return items_placed;
372 }
373 
_who_banished(const string & who)374 static string _who_banished(const string &who)
375 {
376     return who.empty() ? who : " (" + who + ")";
377 }
378 
_banished_depth(const int power)379 static int _banished_depth(const int power)
380 {
381     // Linear, with the max going from (1,1) to (25,5)
382     // and the min going from (9,1) to (27,5)
383     // Currently using HD for power
384 
385     // This means an orc will send you to A:1, an orc warrior
386     // has a small chance of A:2,
387     // Elves have a good shot at sending you to A:3, but won't
388     // always
389     // Ancient Liches are sending you to A:5 and there's nothing
390     // you can do about that.
391     const int maxdepth = div_rand_round((power + 5), 6);
392     const int mindepth = (4 * power + 7) / 23;
393     return min(5, max(1, random_range(mindepth, maxdepth)));
394 }
395 
banished(const string & who,const int power)396 void banished(const string &who, const int power)
397 {
398     ASSERT(!crawl_state.game_is_arena());
399     if (brdepth[BRANCH_ABYSS] == -1)
400         return;
401 
402     if (player_in_branch(BRANCH_ABYSS))
403     {
404         if (level_id::current().depth < brdepth[BRANCH_ABYSS])
405             down_stairs(DNGN_ABYSSAL_STAIR);
406         else
407         {
408             // On Abyss:5 we can't go deeper; cause a shift to a new area
409             mprf(MSGCH_BANISHMENT, "You are banished to a different region of the Abyss.");
410             abyss_teleport();
411         }
412         return;
413     }
414 
415     const int depth = _banished_depth(power);
416     const string what = make_stringf("Cast into level %d of the Abyss", depth)
417                       + _who_banished(who);
418     take_note(Note(NOTE_MESSAGE, 0, 0, what), true);
419 
420     stop_delay(true);
421     run_animation(ANIMATION_BANISH, UA_BRANCH_ENTRY, false);
422     push_features_to_abyss();
423     floor_transition(DNGN_ENTER_ABYSS, orig_terrain(you.pos()),
424                      level_id(BRANCH_ABYSS, depth), true);
425     // This is an honest abyss entry, mark milestone
426     mark_milestone("abyss.enter",
427         "was cast into the Abyss!" + _who_banished(who), "parent");
428 
429     // Xom just might decide to interfere.
430     if (you_worship(GOD_XOM) && who != "Xom" && who != "wizard command"
431         && who != "a distortion unwield")
432     {
433         xom_maybe_reverts_banishment(false, false);
434     }
435 }
436 
push_features_to_abyss()437 void push_features_to_abyss()
438 {
439     abyssal_features.clear();
440 
441     for (int x = -LOS_RADIUS; x <= LOS_RADIUS; x++)
442     {
443         for (int y = -LOS_RADIUS; y <= LOS_RADIUS; y++)
444         {
445             coord_def p(x, y);
446 
447             p += you.pos();
448 
449             dungeon_feature_type feature = map_bounds(p) ? env.grid(p) : DNGN_UNSEEN;
450             feature = sanitize_feature(feature);
451             abyssal_features.push_back(feature);
452         }
453     }
454 }
455 
_abyss_check_place_feat(coord_def p,const int feat_chance,int * feats_wanted,bool * use_map,dungeon_feature_type which_feat,const map_bitmask & abyss_genlevel_mask)456 static bool _abyss_check_place_feat(coord_def p,
457                                     const int feat_chance,
458                                     int *feats_wanted,
459                                     bool *use_map,
460                                     dungeon_feature_type which_feat,
461                                     const map_bitmask &abyss_genlevel_mask)
462 {
463     if (!which_feat)
464         return false;
465 
466     const bool place_feat = feat_chance && one_chance_in(feat_chance);
467 
468     if (place_feat && feats_wanted)
469         ++*feats_wanted;
470 
471     // Don't place features in bubbles.
472     int wall_count = 0;
473     for (adjacent_iterator ai(p); ai; ++ai)
474         wall_count += cell_is_solid(*ai);
475     if (wall_count > 6)
476         return false;
477 
478     // There's no longer a need to check for features under items,
479     // since we're working on fresh grids that are guaranteed
480     // item-free.
481     if (place_feat || (feats_wanted && *feats_wanted > 0))
482     {
483         dprf(DIAG_ABYSS, "Placing abyss feature: %s.",
484              dungeon_feature_name(which_feat));
485 
486         // When placing Abyss exits, try to use a vault if we have one.
487         if (which_feat == DNGN_EXIT_ABYSS
488             && use_map && *use_map
489             && _abyss_place_vault_tagged(abyss_genlevel_mask, "abyss_exit"))
490         {
491             *use_map = false;
492 
493             // Link the vault-placed items.
494             _abyss_postvault_fixup();
495         }
496         else if (!abyss_genlevel_mask(p))
497             return false;
498         else
499             env.grid(p) = which_feat;
500 
501         if (feats_wanted)
502             --*feats_wanted;
503         return true;
504     }
505     return false;
506 }
507 
_abyss_pick_altar()508 static dungeon_feature_type _abyss_pick_altar()
509 {
510     // Lugonu has a flat 50% chance of corrupting the altar.
511     if (coinflip())
512         return DNGN_ALTAR_LUGONU;
513 
514     god_type god;
515 
516     do
517     {
518         god = random_god();
519     }
520     while (is_good_god(god));
521 
522     return altar_for_god(god);
523 }
524 
_abyssal_rune_at(const coord_def p)525 static bool _abyssal_rune_at(const coord_def p)
526 {
527     for (stack_iterator si(p); si; ++si)
528         if (si->is_type(OBJ_RUNES, RUNE_ABYSSAL))
529             return true;
530     return false;
531 }
532 
533 class xom_abyss_feature_amusement_check
534 {
535 private:
536     bool exit_was_near;
537     bool rune_was_near;
538 
539 private:
abyss_exit_nearness() const540     bool abyss_exit_nearness() const
541     {
542         // env.map_knowledge().known() doesn't work on unmappable levels because
543         // mapping flags are not set on such levels.
544         for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
545             if (env.grid(*ri) == DNGN_EXIT_ABYSS && env.map_knowledge(*ri).seen())
546                 return true;
547 
548         return false;
549     }
550 
abyss_rune_nearness() const551     bool abyss_rune_nearness() const
552     {
553         // See above comment about env.map_knowledge().known().
554         for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
555             if (env.map_knowledge(*ri).seen() && _abyssal_rune_at(*ri))
556                 return true;
557         return false;
558     }
559 
560 public:
xom_abyss_feature_amusement_check()561     xom_abyss_feature_amusement_check()
562     {
563         exit_was_near = abyss_exit_nearness();
564         rune_was_near = abyss_rune_nearness();
565     }
566 
567     // If the player was almost to the exit when it disappeared, Xom
568     // is extremely amused. He's also extremely amused if the player
569     // winds up right next to an exit when there wasn't one there
570     // before. The same applies to Abyssal runes.
~xom_abyss_feature_amusement_check()571     ~xom_abyss_feature_amusement_check()
572     {
573         // Update known terrain
574         viewwindow();
575         update_screen();
576 
577         const bool exit_is_near = abyss_exit_nearness();
578         const bool rune_is_near = abyss_rune_nearness();
579 
580         if (exit_was_near && !exit_is_near || rune_was_near && !rune_is_near)
581             xom_is_stimulated(200, "Xom snickers loudly.", true);
582 
583         if (!rune_was_near && rune_is_near || !exit_was_near && exit_is_near)
584             xom_is_stimulated(200);
585     }
586 };
587 
_abyss_lose_monster(monster & mons)588 static void _abyss_lose_monster(monster& mons)
589 {
590     if (mons.needs_abyss_transit())
591         mons.set_transit(level_id(BRANCH_ABYSS));
592     // make sure we don't end up with an invalid hep ancestor
593     else if (hepliaklqana_ancestor() == mons.mid)
594     {
595         simple_monster_message(mons, " is pulled into the Abyss.",
596                 MSGCH_BANISHMENT);
597         remove_companion(&mons);
598         you.duration[DUR_ANCESTOR_DELAY] = random_range(50, 150); //~5-15 turns
599     }
600 
601     mons.destroy_inventory();
602     monster_cleanup(&mons);
603 }
604 
605 // If a sanctuary exists and is in LOS, moves it to keep it in the
606 // same place relative to the player's location after a shift. If the
607 // sanctuary is not in player LOS, removes it.
_abyss_move_sanctuary(const coord_def abyss_shift_start_centre,const coord_def abyss_shift_end_centre)608 static void _abyss_move_sanctuary(const coord_def abyss_shift_start_centre,
609                                   const coord_def abyss_shift_end_centre)
610 {
611     if (env.sanctuary_time > 0 && in_bounds(env.sanctuary_pos))
612     {
613         if (you.see_cell(env.sanctuary_pos))
614         {
615             env.sanctuary_pos += (abyss_shift_end_centre -
616                                   abyss_shift_start_centre);
617         }
618         else
619             remove_sanctuary();
620     }
621 }
622 
_push_displaced_monster(monster * mon)623 static void _push_displaced_monster(monster* mon)
624 {
625     displaced_monsters.push_back(mon);
626 }
627 
_place_displaced_monsters()628 static void _place_displaced_monsters()
629 {
630     for (monster *mon : displaced_monsters)
631     {
632         if (mon->alive() && !mon->find_home_near_place(mon->pos()))
633         {
634             maybe_bloodify_square(mon->pos());
635             // hep messaging is done in _abyss_lose_monster
636             if (you.can_see(*mon) && hepliaklqana_ancestor() != mon->mid)
637             {
638                 simple_monster_message(*mon, " is pulled into the Abyss.",
639                         MSGCH_BANISHMENT);
640             }
641             _abyss_lose_monster(*mon);
642 
643         }
644     }
645 
646     displaced_monsters.clear();
647 }
648 
_pushy_feature(dungeon_feature_type feat)649 static bool _pushy_feature(dungeon_feature_type feat)
650 {
651     // Only completely impassible features and lava will push items.
652     // In particular, deep water will not push items, because the item
653     // will eventually become accessible again through abyss morphing.
654 
655     // Perhaps this should instead be merged with (the complement of)
656     // _item_safe_square() in terrain.cc. Unlike this function, that
657     // one treats traps as unsafe, but closed doors as safe.
658     return feat_is_solid(feat) || feat == DNGN_LAVA;
659 }
660 
_push_items()661 static void _push_items()
662 {
663     for (int i = 0; i < MAX_ITEMS; i++)
664     {
665         item_def& item(env.item[i]);
666         if (!item.defined() || !in_bounds(item.pos) || item.held_by_monster())
667             continue;
668 
669         if (env.item[i].flags & ISFLAG_SUMMONED)
670         {
671             // this is here because of hep-related crashes that no one has
672             // figured out. Under some circumstances, a hep ancestor can drop
673             // its items in the abyss in a stack of copies(??), and when they
674             // do, on the next abyss morph the call below to move_item_to_grid
675             // will trigger a crash, softlocking the player out of abyss.
676             // Therefore, preemptively clean things up until someone figures
677             // out this bug. See
678             // https://crawl.develz.org/mantis/view.php?id=11756 among others.
679             debug_dump_item(item.name(DESC_PLAIN).c_str(),
680                 i, item, "Cleaning up buggy summoned item!");
681             destroy_item(i);
682             continue;
683         }
684         if (!_pushy_feature(env.grid(item.pos)))
685             continue;
686 
687         for (distance_iterator di(item.pos); di; ++di)
688             if (!_pushy_feature(env.grid(*di)))
689             {
690                 int j = i;
691                 move_item_to_grid(&j, *di, true);
692                 break;
693             }
694     }
695 }
696 
697 // Deletes everything on the level at the given position.
698 // Things that are wiped:
699 // 1. Dungeon terrain (set to DNGN_UNSEEN)
700 // 2. Monsters (the player is unaffected)
701 // 3. Items
702 // 4. Clouds
703 // 5. Terrain properties
704 // 6. Terrain colours
705 // 7. Vault (map) mask
706 // 8. Vault id mask
707 // 9. Map markers
708 
_abyss_wipe_square_at(coord_def p,bool saveMonsters=false)709 static void _abyss_wipe_square_at(coord_def p, bool saveMonsters=false)
710 {
711     // Nuke terrain.
712     destroy_shop_at(p);
713     destroy_trap(p);
714 
715     // Nuke vault flag.
716     if (map_masked(p, MMT_VAULT))
717         env.level_map_mask(p) &= ~MMT_VAULT;
718 
719     env.grid(p) = DNGN_UNSEEN;
720 
721     // Nuke items.
722     if (env.igrid(p) != NON_ITEM)
723         dprf(DIAG_ABYSS, "Nuke item stack at (%d, %d)", p.x, p.y);
724     lose_item_stack(p);
725 
726     // Nuke monster.
727     if (monster* mon = monster_at(p))
728     {
729         ASSERT(mon->alive());
730 
731         if (saveMonsters)
732             _push_displaced_monster(mon);
733         else
734             _abyss_lose_monster(*mon);
735     }
736 
737     // Delete cloud.
738     delete_cloud(p);
739 
740     env.pgrid(p)        = terrain_property_t{};
741     env.grid_colours(p) = 0;
742 #ifdef USE_TILE
743     tile_env.bk_fg(p)   = 0;
744     tile_env.bk_bg(p)   = 0;
745     tile_env.bk_cloud(p)= 0;
746 #endif
747     tile_clear_flavour(p);
748     tile_init_flavour(p);
749 
750     env.level_map_mask(p) = 0;
751     env.level_map_ids(p)  = INVALID_MAP_INDEX;
752 
753     remove_markers_and_listeners_at(p);
754 
755     env.map_knowledge(p).clear();
756     if (env.map_forgotten)
757         (*env.map_forgotten)(p).clear();
758     env.map_seen.set(p, false);
759 #ifdef USE_TILE
760     tile_forget_map(p);
761 #endif
762     StashTrack.update_stash(p);
763 }
764 
765 // Removes monsters, clouds, dungeon features, and items from the
766 // level, torching all squares for which the supplied mask is false.
_abyss_wipe_unmasked_area(const map_bitmask & abyss_preserve_mask)767 static void _abyss_wipe_unmasked_area(const map_bitmask &abyss_preserve_mask)
768 {
769     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
770         if (!abyss_preserve_mask(*ri))
771             _abyss_wipe_square_at(*ri);
772 }
773 
774 // Moves everything at src to dst.
_abyss_move_entities_at(coord_def src,coord_def dst)775 static void _abyss_move_entities_at(coord_def src, coord_def dst)
776 {
777     dgn_move_entities_at(src, dst, true, true, true);
778 }
779 
780 // Move all vaults within the mask by the specified delta.
_abyss_move_masked_vaults_by_delta(const coord_def delta)781 static void _abyss_move_masked_vaults_by_delta(const coord_def delta)
782 {
783     set<int> vault_indexes;
784     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
785     {
786         const int vi = env.level_map_ids(*ri);
787         if (vi != INVALID_MAP_INDEX)
788             vault_indexes.insert(vi);
789     }
790 
791     for (auto i : vault_indexes)
792     {
793         vault_placement &vp(*env.level_vaults[i]);
794 #ifdef DEBUG_DIAGNOSTICS
795         const coord_def oldp = vp.pos;
796 #endif
797         vp.pos += delta;
798         dprf(DIAG_ABYSS, "Moved vault (%s) from (%d,%d)-(%d,%d)",
799              vp.map.name.c_str(), oldp.x, oldp.y, vp.pos.x, vp.pos.y);
800     }
801 }
802 
803 /**
804  * Updates the destination of a transporter that has been shifted to a new
805  * center. Assumes that the transporter itself and its position marker have
806  * already been moved.
807  *
808  * @param pos            Transporter's new (and current) location.
809  * @param source_center  Center of where the transporter's vault area was
810  *                       shifted from.
811  * @param target_center  Center of where the transporter's vault area is being
812  *                       shifted to.
813  * @param shift_area     A map_bitmask of the entire area being shifted.
814 
815  */
_abyss_update_transporter(const coord_def & pos,const coord_def & source_centre,const coord_def & target_centre,const map_bitmask & shift_area)816 static void _abyss_update_transporter(const coord_def &pos,
817                                       const coord_def &source_centre,
818                                       const coord_def &target_centre,
819                                       const map_bitmask &shift_area)
820 {
821     if (env.grid(pos) != DNGN_TRANSPORTER)
822         return;
823 
824     // Get the marker, since we will need to modify it.
825     map_position_marker *marker =
826         get_position_marker_at(pos, DNGN_TRANSPORTER);
827     if (!marker || marker->dest == INVALID_COORD)
828         return;
829 
830     // Original destination is not being preserved, so disable the transporter.
831     if (!shift_area.get(marker->dest))
832     {
833         marker->dest = INVALID_COORD;
834         return;
835     }
836 
837     marker->dest = marker->dest - source_centre + target_centre;
838 }
839 
840 // Moves the player, monsters, terrain and items in the square (circle
841 // in movement distance) around the player with the given radius to
842 // the square centred on target_centre.
843 //
844 // Assumes:
845 // a) target can be truncated if not fully in bounds
846 // b) source and target areas may overlap
847 //
_abyss_move_entities(coord_def target_centre,map_bitmask * shift_area_mask)848 static void _abyss_move_entities(coord_def target_centre,
849                                  map_bitmask *shift_area_mask)
850 {
851     const coord_def source_centre = you.pos();
852     const coord_def delta = (target_centre - source_centre).sgn();
853     const map_bitmask original_area_mask = *shift_area_mask;
854 
855     // When moving a region, walk backward to handle overlapping
856     // ranges correctly.
857     coord_def direction = -delta;
858 
859     if (!direction.x)
860         direction.x = 1;
861     if (!direction.y)
862         direction.y = 1;
863 
864     coord_def start(MAPGEN_BORDER, MAPGEN_BORDER);
865     coord_def end(GXM - 1 - MAPGEN_BORDER, GYM - 1 - MAPGEN_BORDER);
866 
867     if (direction.x == -1)
868         swap(start.x, end.x);
869     if (direction.y == -1)
870         swap(start.y, end.y);
871 
872     end += direction;
873 
874     for (int y = start.y; y != end.y; y += direction.y)
875     {
876         for (int x = start.x; x != end.x; x += direction.x)
877         {
878             const coord_def src(x, y);
879             if (!shift_area_mask->get(src))
880                 continue;
881 
882             shift_area_mask->set(src, false);
883 
884             const coord_def dst = src - source_centre + target_centre;
885             if (map_bounds_with_margin(dst, MAPGEN_BORDER))
886             {
887                 shift_area_mask->set(dst);
888                 // Wipe the dstination clean before dropping things on it.
889                 _abyss_wipe_square_at(dst);
890                 _abyss_move_entities_at(src, dst);
891                 _abyss_update_transporter(dst, source_centre, target_centre,
892                                           original_area_mask);
893             }
894             else
895             {
896                 // Wipe the source clean even if the dst is not in bounds.
897                 _abyss_wipe_square_at(src);
898             }
899         }
900     }
901 
902     _abyss_move_masked_vaults_by_delta(target_centre - source_centre);
903 }
904 
_abyss_expand_mask_to_cover_vault(map_bitmask * mask,int map_index)905 static void _abyss_expand_mask_to_cover_vault(map_bitmask *mask,
906                                               int map_index)
907 {
908     dprf(DIAG_ABYSS, "Expanding mask to cover vault %d (nvaults: %u)",
909          map_index, (unsigned int)env.level_vaults.size());
910     const vault_placement &vp = *env.level_vaults[map_index];
911     for (vault_place_iterator vpi(vp); vpi; ++vpi)
912         mask->set(*vpi);
913 }
914 
915 // Identifies the smallest square around the given source that can be
916 // shifted without breaking up any vaults.
_abyss_identify_area_to_shift(coord_def source,int radius,map_bitmask * mask)917 static void _abyss_identify_area_to_shift(coord_def source, int radius,
918                                           map_bitmask *mask)
919 {
920     mask->reset();
921 
922     set<int> affected_vault_indexes;
923     for (rectangle_iterator ri(source, radius); ri; ++ri)
924     {
925         if (!map_bounds_with_margin(*ri, MAPGEN_BORDER))
926             continue;
927 
928         mask->set(*ri);
929 
930         const int map_index = env.level_map_ids(*ri);
931         if (map_index != INVALID_MAP_INDEX)
932             affected_vault_indexes.insert(map_index);
933     }
934 
935     for (auto i : affected_vault_indexes)
936         _abyss_expand_mask_to_cover_vault(mask, i);
937 }
938 
_abyss_invert_mask(map_bitmask * mask)939 static void _abyss_invert_mask(map_bitmask *mask)
940 {
941     for (rectangle_iterator ri(0); ri; ++ri)
942         mask->set(*ri, !mask->get(*ri));
943 }
944 
945 // Moves everything in the given radius around the player (where radius=0 =>
946 // only the player) to another part of the level, centred on target_centre.
947 // Everything not in the given radius is wiped to DNGN_UNSEEN and the provided
948 // map_bitmask is set to true for all wiped squares.
949 //
950 // Things that are moved:
951 // 1. Dungeon terrain
952 // 2. Actors (player + monsters)
953 // 3. Items
954 // 4. Clouds
955 // 5. Terrain properties
956 // 6. Terrain colours
957 // 7. Vaults
958 // 8. Vault (map) mask
959 // 9. Vault id mask
960 // 10. Map markers
961 //
962 // After the shift, any vaults that are no longer referenced in the id
963 // mask will be discarded. If those vaults had any unique tags or
964 // names, the tag/name will NOT be unregistered.
965 //
966 // Assumes:
967 // a) radius >= LOS_RADIUS
968 // b) All points in the source and target areas are in bounds.
_abyss_shift_level_contents_around_player(int radius,coord_def target_centre,map_bitmask & abyss_destruction_mask)969 static void _abyss_shift_level_contents_around_player(
970     int radius,
971     coord_def target_centre,
972     map_bitmask &abyss_destruction_mask)
973 {
974     const coord_def source_centre = you.pos();
975 
976     abyssal_state.major_coord += (source_centre - ABYSS_CENTRE);
977 
978     ASSERT(radius >= LOS_RADIUS);
979     // This should only really happen due to wizmode blink/movement.
980     // 1KB: ... yet at least Xom "swap with monster" triggers it.
981     //ASSERT(map_bounds_with_margin(source_centre, radius));
982     //ASSERT(map_bounds_with_margin(target_centre, radius));
983 
984     _abyss_identify_area_to_shift(source_centre, radius,
985                                   &abyss_destruction_mask);
986 
987     // Shift sanctuary centre if it's close.
988     _abyss_move_sanctuary(you.pos(), target_centre);
989 
990     // Zap everything except the area we're shifting, so that there's
991     // nothing in the way of moving stuff.
992     _abyss_wipe_unmasked_area(abyss_destruction_mask);
993 
994     // Move stuff to its new home. This will also move the player.
995     _abyss_move_entities(target_centre, &abyss_destruction_mask);
996 
997     // [ds] Rezap everything except the shifted area. NOTE: the old
998     // code did not do this, leaving a repeated swatch of Abyss behind
999     // at the old location for every shift; discussions between Linley
1000     // and dpeg on IRC confirm that this (repeated swatch of terrain left
1001     // behind) was not intentional.
1002     _abyss_wipe_unmasked_area(abyss_destruction_mask);
1003 
1004     // So far we've used the mask to track the portions of the level we're
1005     // preserving. The inverse of the mask represents the area to be filled
1006     // with brand new abyss:
1007     _abyss_invert_mask(&abyss_destruction_mask);
1008 
1009     // Update env.level_vaults to discard any vaults that are no longer in
1010     // the picture.
1011     dgn_erase_unused_vault_placements();
1012 }
1013 
_abyss_generate_monsters(int nmonsters)1014 static void _abyss_generate_monsters(int nmonsters)
1015 {
1016     if (crawl_state.disables[DIS_SPAWNS])
1017         return;
1018 
1019     mgen_data mg;
1020     mg.proximity = PROX_ANYWHERE;
1021 
1022     for (int mcount = 0; mcount < nmonsters; mcount++)
1023     {
1024         mg.cls = pick_random_monster(level_id::current());
1025         if (!invalid_monster_type(mg.cls))
1026             mons_place(mg);
1027     }
1028 }
1029 
1030 
maybe_shift_abyss_around_player()1031 void maybe_shift_abyss_around_player()
1032 {
1033     ASSERT(player_in_branch(BRANCH_ABYSS));
1034     if (map_bounds_with_margin(you.pos(),
1035                                MAPGEN_BORDER + ABYSS_AREA_SHIFT_RADIUS + 1))
1036     {
1037         return;
1038     }
1039 
1040     dprf(DIAG_ABYSS, "Shifting abyss at (%d,%d)", you.pos().x, you.pos().y);
1041 
1042     abyss_area_shift();
1043     if (you.pet_target != MHITYOU)
1044         you.pet_target = MHITNOT;
1045 
1046 #ifdef DEBUG_DIAGNOSTICS
1047     int j = count_if(begin(env.item), end(env.item), mem_fn(&item_def::defined));
1048 
1049     dprf(DIAG_ABYSS, "Number of items present: %d", j);
1050 
1051     j = 0;
1052     for (monster_iterator mi; mi; ++mi)
1053         ++j;
1054 
1055     dprf(DIAG_ABYSS, "Number of monsters present: %d", j);
1056     dprf(DIAG_ABYSS, "Number of clouds present: %d", int(env.cloud.size()));
1057 #endif
1058 }
1059 
save_abyss_uniques()1060 void save_abyss_uniques()
1061 {
1062     for (monster_iterator mi; mi; ++mi)
1063         if (mi->needs_abyss_transit()
1064             && !testbits(mi->flags, MF_TAKING_STAIRS))
1065         {
1066             mi->set_transit(level_id(BRANCH_ABYSS));
1067         }
1068 }
1069 
_in_wastes(const coord_def & p)1070 static bool _in_wastes(const coord_def &p)
1071 {
1072     return p.x > 0 && p.x < 0x7FFFFFF && p.y > 0 && p.y < 0x7FFFFFF;
1073 }
1074 
_get_random_level()1075 static level_id _get_random_level()
1076 {
1077     vector<level_id> levels;
1078     for (branch_iterator it; it; ++it)
1079     {
1080         if (it->id == BRANCH_VESTIBULE || it->id == BRANCH_ABYSS
1081             || it->id == BRANCH_SHOALS)
1082         {
1083             continue;
1084         }
1085         for (int j = 1; j <= brdepth[it->id]; ++j)
1086         {
1087             const level_id id(it->id, j);
1088             // for pregen, this will use levels the player hasn't seen yet
1089             if (is_existing_level(id))
1090                 levels.push_back(id);
1091         }
1092     }
1093     if (levels.empty())
1094     {
1095         // Let this fail later on.
1096         return level_id(static_cast<branch_type>(BRANCH_DUNGEON), 1);
1097     }
1098 
1099     return levels[hash_with_seed(levels.size(), abyssal_state.seed)];
1100 }
1101 
1102 /**************************************************************/
1103 /* Fixed layouts (ie, those that depend only on abyss coords) */
1104 /**************************************************************/
1105 const static WastesLayout wastes;
1106 const static DiamondLayout diamond30(3,0);
1107 const static DiamondLayout diamond21(2,1);
1108 const static ColumnLayout column2(2);
1109 const static ColumnLayout column26(2,6);
1110 const static ProceduralLayout* regularLayouts[] =
1111 {
1112     &diamond30, &diamond21, &column2, &column26,
1113 };
1114 const static vector<const ProceduralLayout*> layout_vec(regularLayouts,
1115     regularLayouts + ARRAYSZ(regularLayouts));
1116 const static WorleyLayout worleyL(123456, layout_vec);
1117 const static RoilingChaosLayout chaosA(8675309, 450);
1118 const static RoilingChaosLayout chaosB(7654321, 400);
1119 const static RoilingChaosLayout chaosC(24324,   380);
1120 const static RoilingChaosLayout chaosD(24816,   500);
1121 const static NewAbyssLayout newAbyssLayout(7629);
1122 const static ProceduralLayout* mixedLayouts[] =
1123 {
1124     &chaosA, &worleyL, &chaosB, &chaosC, &chaosD, &newAbyssLayout,
1125 };
1126 const static vector<const ProceduralLayout*> mixed_vec(mixedLayouts,
1127     mixedLayouts + ARRAYSZ(mixedLayouts));
1128 const static WorleyLayout layout(4321, mixed_vec);
1129 const static ProceduralLayout* baseLayouts[] = { &newAbyssLayout, &layout };
1130 const static vector<const ProceduralLayout*> base_vec(baseLayouts,
1131     baseLayouts + ARRAYSZ(baseLayouts));
1132 const static WorleyLayout baseLayout(314159, base_vec, 5.0);
1133 const static RiverLayout rivers(1800, baseLayout);
1134 // This one is not fixed: [0] is a level pulled from the current game
1135 static vector<const ProceduralLayout*> complex_vec(2);
1136 
_abyss_grid(const coord_def & p)1137 static ProceduralSample _abyss_grid(const coord_def &p)
1138 {
1139     const coord_def pt = p + abyssal_state.major_coord;
1140 
1141     if (_in_wastes(pt))
1142     {
1143         ProceduralSample sample = wastes(pt, abyssal_state.depth);
1144         abyss_sample_queue.push(sample);
1145         return sample;
1146     }
1147 
1148     if (abyssLayout == nullptr)
1149     {
1150         const level_id lid = _get_random_level();
1151         levelLayout = new LevelLayout(lid, 5, rivers);
1152         complex_vec[0] = levelLayout;
1153         complex_vec[1] = &rivers; // const
1154         abyssLayout = new WorleyLayout(23571113, complex_vec, 6.1);
1155         if (is_existing_level(lid))
1156         {
1157             auto &vault_list =  you.vault_list[level_id::current()];
1158             vault_list.push_back("base: " + lid.describe(false));
1159         }
1160     }
1161 
1162     const ProceduralSample sample = (*abyssLayout)(pt, abyssal_state.depth);
1163     ASSERT(sample.feat() > DNGN_UNSEEN);
1164 
1165     abyss_sample_queue.push(sample);
1166     return sample;
1167 }
1168 
_cloud_from_feat(const dungeon_feature_type & ft)1169 static cloud_type _cloud_from_feat(const dungeon_feature_type &ft)
1170 {
1171     switch (ft)
1172     {
1173         case DNGN_CLOSED_DOOR:
1174         case DNGN_OPEN_DOOR:
1175         case DNGN_METAL_WALL:
1176             return CLOUD_GREY_SMOKE;
1177         case DNGN_CRYSTAL_WALL:
1178         case DNGN_ROCK_WALL:
1179         case DNGN_SLIMY_WALL:
1180         case DNGN_STONE_WALL:
1181         case DNGN_PERMAROCK_WALL:
1182             return random_choose(CLOUD_BLUE_SMOKE, CLOUD_PURPLE_SMOKE);
1183         case DNGN_CLEAR_ROCK_WALL:
1184         case DNGN_CLEAR_STONE_WALL:
1185         case DNGN_CLEAR_PERMAROCK_WALL:
1186         case DNGN_GRATE:
1187         case DNGN_CLOSED_CLEAR_DOOR:
1188         case DNGN_OPEN_CLEAR_DOOR:
1189             return CLOUD_MIST;
1190         case DNGN_ORCISH_IDOL:
1191         case DNGN_GRANITE_STATUE:
1192         case DNGN_LAVA:
1193             return CLOUD_BLACK_SMOKE;
1194         case DNGN_DEEP_WATER:
1195         case DNGN_SHALLOW_WATER:
1196         case DNGN_FOUNTAIN_BLUE:
1197             return one_chance_in(5) ? CLOUD_RAIN : CLOUD_BLUE_SMOKE;
1198         case DNGN_FOUNTAIN_SPARKLING:
1199             return CLOUD_RAIN;
1200         default:
1201             return CLOUD_NONE;
1202     }
1203 }
1204 
_veto_dangerous_terrain(dungeon_feature_type feat)1205 static dungeon_feature_type _veto_dangerous_terrain(dungeon_feature_type feat)
1206 {
1207     if (feat == DNGN_DEEP_WATER)
1208         return DNGN_SHALLOW_WATER;
1209     if (feat == DNGN_LAVA)
1210         return DNGN_FLOOR;
1211     if (feat_is_solid(feat))
1212         return DNGN_FLOOR;
1213 
1214     return feat;
1215 }
1216 
_update_abyss_terrain(const coord_def & p,const map_bitmask & abyss_genlevel_mask,bool morph)1217 static void _update_abyss_terrain(const coord_def &p,
1218     const map_bitmask &abyss_genlevel_mask, bool morph)
1219 {
1220     const coord_def rp = p - abyssal_state.major_coord;
1221     // ignore dead coordinates
1222     if (!in_bounds(rp))
1223         return;
1224 
1225     const dungeon_feature_type currfeat = env.grid(rp);
1226 
1227     // Don't decay vaults.
1228     if (map_masked(rp, MMT_VAULT))
1229         return;
1230 
1231     switch (currfeat)
1232     {
1233         case DNGN_EXIT_ABYSS:
1234         case DNGN_ABYSSAL_STAIR:
1235             return;
1236         default:
1237             break;
1238     }
1239 
1240     if (feat_is_altar(currfeat))
1241         return;
1242 
1243     if (!abyss_genlevel_mask(rp))
1244         return;
1245 
1246     if (currfeat != DNGN_UNSEEN && !morph)
1247         return;
1248 
1249     // What should have been there previously?  It might not be because
1250     // of external changes such as digging.
1251     const ProceduralSample sample = _abyss_grid(rp);
1252 
1253     // Enqueue the update, but don't morph.
1254     if (_abyssal_rune_at(rp))
1255         return;
1256 
1257     dungeon_feature_type feat = sample.feat();
1258 
1259     // Don't replace open doors with closed doors!
1260     if (feat_is_door(currfeat) && feat_is_door(feat))
1261         return;
1262 
1263     // Veto dangerous terrain.
1264     if (you.pos() == rp)
1265         feat = _veto_dangerous_terrain(feat);
1266     // Veto morph when there's a submerged monster (or a plant) below you.
1267     if (you.pos() == rp && env.mgrid(rp) != NON_MONSTER)
1268         feat = currfeat;
1269 
1270     // If the selected grid is already there, *or* if we're morphing and
1271     // the selected grid should have been there, do nothing.
1272     if (feat != currfeat)
1273     {
1274         env.grid(rp) = feat;
1275         if (feat == DNGN_FLOOR && in_los_bounds_g(rp))
1276         {
1277             cloud_type cloud = _cloud_from_feat(currfeat);
1278             int cloud_life = _in_wastes(abyssal_state.major_coord) ? 5 : 2;
1279             cloud_life += random2(2); // force a sequence point, just in case
1280             if (cloud != CLOUD_NONE)
1281                 check_place_cloud(_cloud_from_feat(currfeat), rp, cloud_life, 0, 3);
1282         }
1283         else if (feat_is_solid(feat))
1284             delete_cloud(rp);
1285         monster* mon = monster_at(rp);
1286         if (mon && !monster_habitable_grid(mon, feat))
1287             _push_displaced_monster(mon);
1288     }
1289 }
1290 
_destroy_all_terrain(bool vaults)1291 static void _destroy_all_terrain(bool vaults)
1292 {
1293     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
1294     {
1295         if (vaults && env.level_map_mask(*ri) & MMT_VAULT)
1296             continue;
1297         env.level_map_mask(*ri) = MMT_TURNED_TO_FLOOR;
1298     }
1299 }
1300 
_ensure_player_habitable(bool dig_instead)1301 static void _ensure_player_habitable(bool dig_instead)
1302 {
1303     dungeon_feature_type feat = env.grid(you.pos());
1304     if (!you.can_pass_through_feat(feat) || is_feat_dangerous(feat))
1305     {
1306         bool shoved = you.shove();
1307         if (!shoved)
1308         {
1309             // legal only if we just placed a vault
1310             ASSERT(dig_instead);
1311             env.grid(you.pos()) = DNGN_FLOOR;
1312         }
1313     }
1314 }
1315 
_abyss_apply_terrain(const map_bitmask & abyss_genlevel_mask,bool morph=false,bool now=false)1316 static void _abyss_apply_terrain(const map_bitmask &abyss_genlevel_mask,
1317                                  bool morph = false, bool now = false)
1318 {
1319     // The chance is reciprocal to these numbers.
1320     const int exit_chance = you.runes[RUNE_ABYSSAL] ? 1250
1321                             : 7500 - 1250 * (you.depth - 1);
1322 
1323     int exits_wanted  = 0;
1324     int altars_wanted = 0;
1325     bool use_abyss_exit_map = true;
1326     bool used_queue = false;
1327     if (morph && !abyss_sample_queue.empty())
1328     {
1329         int ii = 0;
1330         used_queue = true;
1331         while (!abyss_sample_queue.empty()
1332             && abyss_sample_queue.top().changepoint() < abyssal_state.depth)
1333         {
1334             ++ii;
1335             coord_def p = abyss_sample_queue.top().coord();
1336             _update_abyss_terrain(p, abyss_genlevel_mask, morph);
1337             abyss_sample_queue.pop();
1338         }
1339 /*
1340         if (ii)
1341             dprf(DIAG_ABYSS, "Examined %d features.", ii);
1342 */
1343     }
1344 
1345     int ii = 0;
1346     int delta = you.time_taken * (you.abyss_speed + 40) / 200;
1347     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
1348     {
1349         const coord_def p(*ri);
1350         const coord_def abyss_coord = p + abyssal_state.major_coord;
1351         bool turned_to_floor = map_masked(p, MMT_TURNED_TO_FLOOR);
1352         if (used_queue && !turned_to_floor)
1353             continue;
1354 
1355         if (turned_to_floor && (now || x_chance_in_y(delta, 50))
1356             || !turned_to_floor && !used_queue)
1357         {
1358             ++ii;
1359             _update_abyss_terrain(abyss_coord, abyss_genlevel_mask, morph);
1360             env.level_map_mask(p) &= ~MMT_TURNED_TO_FLOOR;
1361         }
1362         if (morph)
1363             continue;
1364 
1365         // Place abyss exits, stone arches, and altars to liven up the scene
1366         // (only on area creation, not on morphing).
1367         _abyss_check_place_feat(p, exit_chance,
1368                                 &exits_wanted,
1369                                 &use_abyss_exit_map,
1370                                 DNGN_EXIT_ABYSS,
1371                                 abyss_genlevel_mask)
1372         ||
1373         _abyss_check_place_feat(p, 10000,
1374                                 &altars_wanted,
1375                                 nullptr,
1376                                 _abyss_pick_altar(),
1377                                 abyss_genlevel_mask)
1378         ||
1379         level_id::current().depth < brdepth[BRANCH_ABYSS]
1380         && _abyss_check_place_feat(p, 1900, nullptr, nullptr,
1381                                    DNGN_ABYSSAL_STAIR,
1382                                    abyss_genlevel_mask);
1383     }
1384     if (ii)
1385         dprf(DIAG_ABYSS, "Nuked %d features", ii);
1386     _ensure_player_habitable(false);
1387     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
1388         ASSERT_RANGE(env.grid(*ri), DNGN_UNSEEN + 1, NUM_FEATURES);
1389 }
1390 
_abyss_place_vaults(const map_bitmask & abyss_genlevel_mask)1391 static int _abyss_place_vaults(const map_bitmask &abyss_genlevel_mask)
1392 {
1393     unwind_vault_placement_mask vaultmask(&abyss_genlevel_mask);
1394 
1395     int vaults_placed = 0;
1396 
1397     bool extra = false;
1398     const int maxvaults = 6;
1399     int tries = 0;
1400     while (vaults_placed < maxvaults)
1401     {
1402         const map_def *map = random_map_in_depth(level_id::current(), extra);
1403         if (map)
1404         {
1405             if (_abyss_place_map(map) && !map->is_extra_vault())
1406             {
1407                 extra = true;
1408 
1409                 if (!one_chance_in(2 + (++vaults_placed)))
1410                     break;
1411             }
1412             else
1413             {
1414                 if (tries++ >= 100)
1415                     break;
1416 
1417                 continue;
1418             }
1419 
1420         }
1421         else
1422         {
1423             if (extra)
1424                 break;
1425             else
1426             {
1427                 extra = true;
1428                 continue;
1429             }
1430         }
1431     }
1432 
1433     return vaults_placed;
1434 }
1435 
_generate_area(const map_bitmask & abyss_genlevel_mask)1436 static void _generate_area(const map_bitmask &abyss_genlevel_mask)
1437 {
1438     // Any rune on the floor prevents the abyssal rune from being generated.
1439     const bool placed_abyssal_rune = find_floor_item(OBJ_RUNES);
1440 
1441     dprf(DIAG_ABYSS, "_generate_area(). turns_on_level: %d, rune_on_floor: %s",
1442          env.turns_on_level, placed_abyssal_rune? "yes" : "no");
1443 
1444     _abyss_apply_terrain(abyss_genlevel_mask);
1445 
1446     // Make sure we're not about to link bad items.
1447     debug_item_scan();
1448     _abyss_place_vaults(abyss_genlevel_mask);
1449 
1450     // Link the vault-placed items.
1451     _abyss_postvault_fixup();
1452 
1453     _abyss_create_items(abyss_genlevel_mask, placed_abyssal_rune);
1454     setup_environment_effects();
1455 
1456     _ensure_player_habitable(true);
1457 
1458     // Abyss has a constant density.
1459     env.density = 0;
1460 }
1461 
_initialize_abyss_state()1462 static void _initialize_abyss_state()
1463 {
1464     abyssal_state.major_coord.x = rng::get_uint32() & 0x7FFFFFFF;
1465     abyssal_state.major_coord.y = rng::get_uint32() & 0x7FFFFFFF;
1466     abyssal_state.seed = rng::get_uint32() & 0x7FFFFFFF;
1467     abyssal_state.phase = 0.0;
1468     abyssal_state.depth = rng::get_uint32() & 0x7FFFFFFF;
1469     abyssal_state.destroy_all_terrain = false;
1470     abyssal_state.level = _get_random_level();
1471     abyss_sample_queue = sample_queue(ProceduralSamplePQCompare());
1472 }
1473 
set_abyss_state(coord_def coord,uint32_t depth)1474 void set_abyss_state(coord_def coord, uint32_t depth)
1475 {
1476     abyssal_state.major_coord = coord;
1477     abyssal_state.depth = depth;
1478     abyssal_state.seed = rng::get_uint32() & 0x7FFFFFFF;
1479     abyssal_state.phase = 0.0;
1480     abyssal_state.destroy_all_terrain = true;
1481     abyss_sample_queue = sample_queue(ProceduralSamplePQCompare());
1482     you.moveto(ABYSS_CENTRE);
1483     map_bitmask abyss_genlevel_mask(true);
1484     _abyss_apply_terrain(abyss_genlevel_mask, true, true);
1485 }
1486 
abyss_area_shift()1487 static void abyss_area_shift()
1488 {
1489     dprf(DIAG_ABYSS, "area_shift() - player at pos (%d, %d)",
1490          you.pos().x, you.pos().y);
1491 
1492     {
1493         xom_abyss_feature_amusement_check xomcheck;
1494 
1495         // A teleport may move you back to the center, resulting in a (0,0)
1496         // shift. The code can't handle those. We still to forget the map,
1497         // spawn new monsters or allow return from transit, though.
1498         if (you.pos() != ABYSS_CENTRE)
1499         {
1500             // Use a map mask to track the areas that the shift destroys and
1501             // that must be regenerated by _generate_area.
1502             map_bitmask abyss_genlevel_mask;
1503             _abyss_shift_level_contents_around_player(
1504                 ABYSS_AREA_SHIFT_RADIUS, ABYSS_CENTRE, abyss_genlevel_mask);
1505             _generate_area(abyss_genlevel_mask);
1506         }
1507         forget_map(true);
1508 
1509         // Update LOS at player's new abyssal vacation retreat.
1510         los_changed();
1511     }
1512 
1513     // Place some monsters to keep the abyss party going.
1514     int num_monsters = 15 + you.depth * (1 + coinflip());
1515     _abyss_generate_monsters(num_monsters);
1516 
1517     // And allow monsters in transit another chance to return.
1518     place_transiting_monsters();
1519 
1520     auto &vault_list =  you.vault_list[level_id::current()];
1521 #ifdef DEBUG
1522     vault_list.push_back("[shift]");
1523 #endif
1524     const auto &level_vaults = level_vault_names();
1525     vault_list.insert(vault_list.end(),
1526                         level_vaults.begin(), level_vaults.end());
1527 
1528 
1529     check_map_validity();
1530     // TODO: should dactions be rerun at this point instead? That would cover
1531     // this particular case...
1532     gozag_detect_level_gold(false);
1533 }
1534 
destroy_abyss()1535 void destroy_abyss()
1536 {
1537     if (abyssLayout)
1538     {
1539         delete abyssLayout;
1540         abyssLayout = nullptr;
1541         delete levelLayout;
1542         levelLayout = nullptr;
1543     }
1544 }
1545 
_roll_abyss_floor_colour()1546 static colour_t _roll_abyss_floor_colour()
1547 {
1548     return random_choose_weighted<colour_t>(
1549          108, BLUE,
1550          632, GREEN,
1551          // no CYAN (silence)
1552          932, RED,
1553          488, MAGENTA,
1554          433, BROWN,
1555         3438, LIGHTGRAY,
1556          // no DARKGREY (out of LOS)
1557          766, LIGHTBLUE,
1558          587, LIGHTGREEN,
1559          794, LIGHTCYAN,
1560          566, LIGHTRED,
1561          313, LIGHTMAGENTA,
1562          // no YELLOW (halo)
1563          890, WHITE,
1564           50, ETC_FIRE);
1565 }
1566 
_roll_abyss_rock_colour()1567 static colour_t _roll_abyss_rock_colour()
1568 {
1569     return random_choose_weighted<colour_t>(
1570          130, BLUE,
1571          409, GREEN,
1572          // no CYAN (metal)
1573          770, RED,
1574          522, MAGENTA,
1575         1292, BROWN,
1576          // no LIGHTGRAY (stone)
1577          // no DARKGREY (out of LOS)
1578          570, LIGHTBLUE,
1579          705, LIGHTGREEN,
1580          // no LIGHTCYAN (glass)
1581         1456, LIGHTRED,
1582          377, LIGHTMAGENTA,
1583          105, YELLOW,
1584          101, WHITE,
1585           60, ETC_FIRE);
1586 }
1587 
_abyss_generate_new_area()1588 static void _abyss_generate_new_area()
1589 {
1590     _initialize_abyss_state();
1591     dprf(DIAG_ABYSS, "Abyss Coord (%d, %d)",
1592          abyssal_state.major_coord.x, abyssal_state.major_coord.y);
1593     remove_sanctuary();
1594 
1595     env.floor_colour = _roll_abyss_floor_colour();
1596     env.rock_colour = _roll_abyss_rock_colour();
1597     tile_init_flavour();
1598 
1599     map_bitmask abyss_genlevel_mask;
1600     _abyss_wipe_unmasked_area(abyss_genlevel_mask);
1601     dgn_erase_unused_vault_placements();
1602 
1603     you.moveto(ABYSS_CENTRE);
1604     abyss_genlevel_mask.init(true);
1605     _generate_area(abyss_genlevel_mask);
1606     if (one_chance_in(5))
1607     {
1608         _place_feature_near(you.pos(), LOS_RADIUS,
1609                             DNGN_FLOOR, DNGN_ALTAR_LUGONU, 50);
1610     }
1611 
1612     los_changed();
1613     place_transiting_monsters();
1614 }
1615 
1616 // Check if there is a path between the abyss centre and an exit location.
_abyss_has_path(const coord_def & to)1617 static bool _abyss_has_path(const coord_def &to)
1618 {
1619     ASSERT(env.grid(to) == DNGN_EXIT_ABYSS);
1620 
1621     monster_pathfind pf;
1622     return pf.init_pathfind(ABYSS_CENTRE, to);
1623 }
1624 
1625 // Generate the initial (proto) Abyss level. The proto Abyss is where
1626 // the player lands when they arrive in the Abyss from elsewhere.
1627 // _generate_area generates all other Abyss areas.
generate_abyss()1628 void generate_abyss()
1629 {
1630     env.level_build_method += " abyss";
1631     env.level_layout_types.insert("abyss");
1632     destroy_abyss();
1633 
1634 retry:
1635     _initialize_abyss_state();
1636 
1637     dprf(DIAG_ABYSS, "generate_abyss(); turn_on_level: %d",
1638          env.turns_on_level);
1639 
1640     // Generate the initial abyss without vaults. Vaults are horrifying.
1641     _abyss_generate_new_area();
1642     _write_abyssal_features();
1643     map_bitmask abyss_genlevel_mask(true);
1644     _abyss_apply_terrain(abyss_genlevel_mask);
1645 
1646     env.grid(you.pos()) = _veto_dangerous_terrain(env.grid(you.pos()));
1647     _place_displaced_monsters();
1648 
1649     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
1650         ASSERT(env.grid(*ri) > DNGN_UNSEEN);
1651     check_map_validity();
1652 
1653     // If we're starting out in the Abyss, make sure the starting grid is
1654     // an altar to Lugonu and there's an exit near-by.
1655     // Otherwise, we start out on floor and there's a chance there's an
1656     // altar near-by.
1657     if (player_in_starting_abyss())
1658     {
1659         env.grid(ABYSS_CENTRE) = DNGN_ALTAR_LUGONU;
1660         const coord_def eloc = _place_feature_near(ABYSS_CENTRE, LOS_RADIUS + 2,
1661                                                    DNGN_FLOOR, DNGN_EXIT_ABYSS,
1662                                                    50, true);
1663         // Now make sure there is a path from the abyss centre to the exit.
1664         // If for some reason an exit could not be placed, don't bother.
1665         if (eloc == INVALID_COORD || !_abyss_has_path(eloc))
1666             goto retry;
1667     }
1668     else
1669     {
1670         env.grid(ABYSS_CENTRE) = DNGN_FLOOR;
1671         if (one_chance_in(5))
1672         {
1673             _place_feature_near(ABYSS_CENTRE, LOS_RADIUS,
1674                                 DNGN_FLOOR, DNGN_ALTAR_LUGONU, 50);
1675         }
1676     }
1677 
1678     setup_environment_effects();
1679 }
1680 
_increase_depth()1681 static void _increase_depth()
1682 {
1683     int delta = you.time_taken * (you.abyss_speed + 40) / 200;
1684     if (!have_passive(passive_t::slow_abyss))
1685         delta *= 2;
1686     if (you.duration[DUR_TELEPORT])
1687         delta *= 5;
1688     const double theta = abyssal_state.phase;
1689     double depth_change = delta * (0.2 + 2.8 * pow(sin(theta/2), 10.0));
1690     abyssal_state.depth += depth_change;
1691     abyssal_state.phase += delta / 100.0;
1692     if (abyssal_state.phase > PI)
1693         abyssal_state.phase -= PI;
1694 }
1695 
abyss_morph()1696 void abyss_morph()
1697 {
1698     if (abyssal_state.destroy_all_terrain)
1699     {
1700         _destroy_all_terrain(false);
1701         abyssal_state.destroy_all_terrain = false;
1702     }
1703     if (!player_in_branch(BRANCH_ABYSS))
1704         return;
1705     _increase_depth();
1706     map_bitmask abyss_genlevel_mask(true);
1707     dgn_erase_unused_vault_placements();
1708     _abyss_apply_terrain(abyss_genlevel_mask, true);
1709     _place_displaced_monsters();
1710     _push_items();
1711     // TODO: does gozag gold detection need to be here too?
1712     los_changed();
1713 }
1714 
1715 // Force the player one level deeper in the abyss during an abyss teleport with
1716 // probability:
1717 //   (XL + 4 - Depth)^2 / (27^2 * (Depth + 5))
1718 //
1719 // Consequences of this formula:
1720 // - Chance to be pulled deeper increases with XL and decreases with current
1721 //   abyss depth.
1722 // - Characters at XL 1 have about a 0.1% chance of getting pulled from A:1.
1723 // - Characters at XL 13 have chances for getting pulled from A:1/A:2/A:3/A:4
1724 //   of about 5.9%/4.4%/3.3%/2.6%.
1725 // - Characters at XL 27 have chances for getting pulled from A:1/A:2/A:3/A:4
1726 //   of about 20.6%/16.5%/13.4%/11.1%.
_abyss_force_descent()1727 static bool _abyss_force_descent()
1728 {
1729     const int depth = level_id::current().depth;
1730     const int xl_factor = you.experience_level + 4 - depth;
1731     return x_chance_in_y(xl_factor * xl_factor, 729 * (5 + depth));
1732 }
1733 
abyss_teleport()1734 void abyss_teleport()
1735 {
1736     xom_abyss_feature_amusement_check xomcheck;
1737     dprf(DIAG_ABYSS, "New area Abyss teleport.");
1738 
1739     if (level_id::current().depth < brdepth[BRANCH_ABYSS]
1740         && _abyss_force_descent())
1741     {
1742         down_stairs(DNGN_ABYSSAL_STAIR);
1743         more();
1744         return;
1745     }
1746 
1747     mprf(MSGCH_BANISHMENT, "You are suddenly pulled into a different region of"
1748         " the Abyss!");
1749     _abyss_generate_new_area();
1750     _write_abyssal_features();
1751     env.grid(you.pos()) = _veto_dangerous_terrain(env.grid(you.pos()));
1752 
1753     stop_delay(true);
1754     forget_map(false);
1755     clear_excludes();
1756     gozag_detect_level_gold(false);
1757     auto &vault_list =  you.vault_list[level_id::current()];
1758 #ifdef DEBUG
1759     vault_list.push_back("[tele]");
1760 #endif
1761     const auto &level_vaults = level_vault_names();
1762     vault_list.insert(vault_list.end(),
1763                         level_vaults.begin(), level_vaults.end());
1764 
1765     more();
1766 }
1767 
1768 //////////////////////////////////////////////////////////////////////////////
1769 // Abyss effects in other levels, courtesy Lugonu.
1770 
1771 struct corrupt_env
1772 {
1773     int rock_colour, floor_colour, secondary_floor_colour;
corrupt_envcorrupt_env1774     corrupt_env()
1775         : rock_colour(BLACK), floor_colour(BLACK),
1776            secondary_floor_colour(MAGENTA)
1777     { }
1778 
pick_floor_colourcorrupt_env1779     int pick_floor_colour() const
1780     {
1781         return random2(10) < 2 ? secondary_floor_colour : floor_colour;
1782     }
1783 };
1784 
_place_corruption_seed(const coord_def & pos,int duration)1785 static void _place_corruption_seed(const coord_def &pos, int duration)
1786 {
1787     env.markers.add(new map_corruption_marker(pos, duration));
1788     // Corruption markers don't need activation, though we might
1789     // occasionally miss other unactivated markers by clearing.
1790     env.markers.clear_need_activate();
1791 }
1792 
_initialise_level_corrupt_seeds(int power)1793 static void _initialise_level_corrupt_seeds(int power)
1794 {
1795     const int low = power * 40 / 100, high = power * 140 / 100;
1796     const int nseeds = random_range(-1, min(2 + power / 110, 4), 2);
1797 
1798     const int aux_seed_radius = 4;
1799 
1800     dprf("Placing %d corruption seeds (power: %d)", nseeds, power);
1801 
1802     // The corruption centreed on the player is free.
1803     _place_corruption_seed(you.pos(), high + 300);
1804 
1805     for (int i = 0; i < nseeds; ++i)
1806     {
1807         coord_def where;
1808         int tries = 100;
1809         while (tries-- > 0)
1810         {
1811             where = dgn_random_point_from(you.pos(), aux_seed_radius, 2);
1812             if (env.grid(where) == DNGN_FLOOR && !env.markers.find(where, MAT_ANY))
1813                 break;
1814             where.reset();
1815         }
1816 
1817         if (!where.origin())
1818             _place_corruption_seed(where, random_range(low, high, 2) + 300);
1819     }
1820 }
1821 
_incorruptible(monster_type mt)1822 static bool _incorruptible(monster_type mt)
1823 {
1824     return mons_is_abyssal_only(mt) || mons_class_holiness(mt) == MH_HOLY;
1825 }
1826 
1827 // Create a corruption spawn at the given position. Returns false if further
1828 // monsters should not be placed near this spot (overcrowding), true if
1829 // more monsters can fit in.
_spawn_corrupted_servant_near(const coord_def & pos)1830 static bool _spawn_corrupted_servant_near(const coord_def &pos)
1831 {
1832     // Chance to fail to place a monster (but allow continued attempts).
1833     if (x_chance_in_y(100, 200 + you.skill(SK_INVOCATIONS, 25)))
1834         return true;
1835 
1836     // Thirty tries for a place.
1837     for (int i = 0; i < 30; ++i)
1838     {
1839         int offsetX = random2avg(4, 3);
1840         offsetX += random2(3); // force a sequence point between random calls
1841         int offsetY = random2avg(4, 3);
1842         offsetY += random2(3); // ditto
1843         coord_def p;
1844         p.x = pos.x + random_choose(offsetX, -offsetX);
1845         p.y = pos.y + random_choose(offsetY, -offsetY);
1846         if (!in_bounds(p) || actor_at(p))
1847             continue;
1848 
1849         monster_type mons = pick_monster(level_id(BRANCH_ABYSS), _incorruptible);
1850         ASSERT(mons);
1851         if (!monster_habitable_grid(mons, env.grid(p)))
1852             continue;
1853         mgen_data mg(mons, BEH_NEUTRAL, p);
1854         mg.set_summoned(0, 5, 0).set_non_actor_summoner("Lugonu's corruption");
1855         mg.place = BRANCH_ABYSS;
1856         return create_monster(mg);
1857     }
1858 
1859     return false;
1860 }
1861 
_apply_corruption_effect(map_marker * marker,int duration)1862 static void _apply_corruption_effect(map_marker *marker, int duration)
1863 {
1864     if (!duration)
1865         return;
1866 
1867     map_corruption_marker *cmark = dynamic_cast<map_corruption_marker*>(marker);
1868     if (cmark->duration < 1)
1869         return;
1870 
1871     const int neffects = max(div_rand_round(duration, 5), 1);
1872 
1873     for (int i = 0; i < neffects; ++i)
1874     {
1875         if (x_chance_in_y(cmark->duration, 4000)
1876             && !_spawn_corrupted_servant_near(cmark->pos))
1877         {
1878             break;
1879         }
1880     }
1881     cmark->duration -= duration;
1882 }
1883 
run_corruption_effects(int duration)1884 void run_corruption_effects(int duration)
1885 {
1886     for (map_marker *mark : env.markers.get_all(MAT_CORRUPTION_NEXUS))
1887     {
1888         if (mark->get_type() != MAT_CORRUPTION_NEXUS)
1889             continue;
1890 
1891         _apply_corruption_effect(mark, duration);
1892     }
1893 }
1894 
_is_grid_corruptible(const coord_def & c)1895 static bool _is_grid_corruptible(const coord_def &c)
1896 {
1897     if (c == you.pos())
1898         return false;
1899 
1900     const dungeon_feature_type feat = env.grid(c);
1901 
1902     // Stairs and portals cannot be corrupted.
1903     if (feat_stair_direction(feat) != CMD_NO_CMD)
1904         return false;
1905 
1906     switch (feat)
1907     {
1908     case DNGN_PERMAROCK_WALL:
1909     case DNGN_CLEAR_PERMAROCK_WALL:
1910     case DNGN_OPEN_SEA:
1911     case DNGN_LAVA_SEA:
1912     case DNGN_TRANSPORTER_LANDING: // entry already taken care of as stairs
1913         return false;
1914 
1915     case DNGN_METAL_WALL:
1916     case DNGN_CRYSTAL_WALL:
1917         return one_chance_in(4);
1918 
1919     case DNGN_STONE_WALL:
1920     case DNGN_CLEAR_STONE_WALL:
1921         return one_chance_in(3);
1922 
1923     case DNGN_ROCK_WALL:
1924     case DNGN_CLEAR_ROCK_WALL:
1925         return !one_chance_in(3);
1926 
1927     default:
1928         return true;
1929     }
1930 }
1931 
1932 // Returns true if the square has <= 4 traversable neighbours.
_is_crowded_square(const coord_def & c)1933 static bool _is_crowded_square(const coord_def &c)
1934 {
1935     int neighbours = 0;
1936     for (int xi = -1; xi <= 1; ++xi)
1937         for (int yi = -1; yi <= 1; ++yi)
1938         {
1939             if (!xi && !yi)
1940                 continue;
1941 
1942             const coord_def n(c.x + xi, c.y + yi);
1943             if (!in_bounds(n) || !feat_is_traversable(env.grid(n)))
1944                 continue;
1945 
1946             if (++neighbours > 4)
1947                 return false;
1948         }
1949 
1950     return true;
1951 }
1952 
1953 // Returns true if the square has all opaque neighbours.
_is_sealed_square(const coord_def & c)1954 static bool _is_sealed_square(const coord_def &c)
1955 {
1956     for (adjacent_iterator ai(c); ai; ++ai)
1957         if (!feat_is_opaque(env.grid(*ai)))
1958             return false;
1959 
1960     return true;
1961 }
1962 
_corrupt_square_flavor(const corrupt_env & cenv,const coord_def & c)1963 static void _corrupt_square_flavor(const corrupt_env &cenv, const coord_def &c)
1964 {
1965     dungeon_feature_type feat = env.grid(c);
1966     int floor = cenv.pick_floor_colour();
1967 
1968     if (feat == DNGN_ROCK_WALL || feat == DNGN_METAL_WALL
1969         || feat == DNGN_STONE_WALL || feat == DNGN_TREE)
1970     {
1971         env.grid_colours(c) = cenv.rock_colour;
1972     }
1973     else if (feat == DNGN_FLOOR)
1974         env.grid_colours(c) = floor;
1975 
1976     // if you add new features to this, you'll probably need to do some
1977     // hand-tweaking in tileview.cc apply_variations.
1978     // TODO: these tile assignments here seem to get overridden in
1979     // apply_variations, or not used at all...what gives?
1980     if (feat == DNGN_ROCK_WALL)
1981     {
1982         tileidx_t idx = tile_dngn_coloured(TILE_WALL_ABYSS,
1983                                            cenv.rock_colour);
1984         tile_env.flv(c).wall = idx + random2(tile_dngn_count(idx));
1985     }
1986     else if (feat == DNGN_FLOOR)
1987     {
1988         tileidx_t idx = tile_dngn_coloured(TILE_FLOOR_NERVES,
1989                                            floor);
1990         tile_env.flv(c).floor = idx + random2(tile_dngn_count(idx));
1991     }
1992     else if (feat == DNGN_STONE_WALL)
1993     {
1994         // recoloring stone and metal is also impacted heavily by the rolls
1995         // in _is_grid_corruptible
1996         tileidx_t idx = tile_dngn_coloured(TILE_DNGN_STONE_WALL,
1997                                            cenv.rock_colour);
1998         tile_env.flv(c).wall = idx + random2(tile_dngn_count(idx));
1999     }
2000     else if (feat == DNGN_METAL_WALL)
2001     {
2002         tileidx_t idx = tile_dngn_coloured(TILE_DNGN_METAL_WALL,
2003                                            cenv.rock_colour);
2004         tile_env.flv(c).wall = idx + random2(tile_dngn_count(idx));
2005     }
2006     else if (feat == DNGN_TREE)
2007     {
2008         tileidx_t idx = tile_dngn_coloured(TILE_DNGN_TREE,
2009                                            cenv.rock_colour);
2010         // trees only have yellow, lightred, red, and darkgray (dead)
2011         if (idx == TILE_DNGN_TREE)
2012             idx = tile_dngn_coloured(TILE_DNGN_TREE, DARKGREY);
2013         env.grid_colours(c) = DARKGREY;
2014         tile_env.flv(c).wall = idx + random2(tile_dngn_count(idx));
2015     }
2016 }
2017 
_corrupt_square(const corrupt_env & cenv,const coord_def & c)2018 static void _corrupt_square(const corrupt_env &cenv, const coord_def &c)
2019 {
2020     // Ask dungeon_change_terrain to preserve things that are not altars.
2021     // This only actually matters for malign gateways and features that happen
2022     // to be on squares that happen to contain map markers (e.g. sealed doors,
2023     // which come with their own markers). MAT_CORRUPTION_NEXUS is a marker,
2024     // and some of those are certainly nearby, but they only appear on
2025     // DNGN_FLOOR. preserve_features=true would ordinarily cause
2026     // dungeon_terrain_changed to protect stairs/branch entries/portals as
2027     // well, but _corrupt_square is not called on squares containing those
2028     // features.
2029     bool preserve_features = true;
2030     dungeon_feature_type feat = DNGN_UNSEEN;
2031     if (feat_altar_god(env.grid(c)) != GOD_NO_GOD)
2032     {
2033         preserve_features = false;
2034         if (!one_chance_in(3))
2035             feat = DNGN_ALTAR_LUGONU;
2036     }
2037     else
2038         feat = _abyss_proto_feature();
2039 
2040     if (feat_is_trap(feat)
2041         || feat == DNGN_UNSEEN
2042         || (feat_is_traversable(env.grid(c)) && !feat_is_traversable(feat)
2043             && coinflip()))
2044     {
2045         feat = DNGN_FLOOR;
2046     }
2047 
2048     if (feat_is_traversable(env.grid(c)) && !feat_is_traversable(feat)
2049         && _is_crowded_square(c))
2050     {
2051         return;
2052     }
2053 
2054     if (!feat_is_traversable(env.grid(c)) && feat_is_traversable(feat)
2055         && _is_sealed_square(c))
2056     {
2057         return;
2058     }
2059 
2060     if (feat == DNGN_EXIT_ABYSS)
2061         feat = DNGN_ENTER_ABYSS;
2062 
2063     // If we are trying to place a wall on top of a creature or item, try to
2064     // move it aside. If this fails, simply place floor instead.
2065     actor* act = actor_at(c);
2066     if (feat_is_solid(feat) && (env.igrid(c) != NON_ITEM || act))
2067     {
2068         push_items_from(c, nullptr);
2069         push_actor_from(c, nullptr, true);
2070         if (actor_at(c) || env.igrid(c) != NON_ITEM)
2071             feat = DNGN_FLOOR;
2072     }
2073 
2074     dungeon_terrain_changed(c, feat, preserve_features, true);
2075     _corrupt_square_flavor(cenv, c);
2076 }
2077 
_corrupt_level_features(const corrupt_env & cenv)2078 static void _corrupt_level_features(const corrupt_env &cenv)
2079 {
2080     vector<coord_def> corrupt_seeds;
2081 
2082     for (const map_marker *mark : env.markers.get_all(MAT_CORRUPTION_NEXUS))
2083         corrupt_seeds.push_back(mark->pos);
2084 
2085     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
2086     {
2087         int idistance = GXM + GYM;
2088         for (const coord_def &seed : corrupt_seeds)
2089             idistance = min(idistance, (*ri - seed).rdist());
2090 
2091         const int ground_zero_radius = 2;
2092 
2093         // Corruption odds are 100% within 2 squares, decaying to 30%
2094         // at LOS range (radius 7). Even if the corruption roll is made,
2095         // the feature still gets a chance to resist if it's a wall.
2096         const int corrupt_perc_chance =
2097             (idistance <= ground_zero_radius) ? 1000 :
2098             max(0, 1000 - (sqr(idistance) - sqr(ground_zero_radius)) * 700 / 45);
2099 
2100         // linear function that is at 30% at range 7, 15% at radius 20,
2101         // maxed to 1%. Only affects outside of range 7. For cells within los
2102         // that don't make the regular roll, this also gives them a 50%
2103         // chance to get flavor-only corruption.
2104         const int corrupt_flavor_chance =
2105             (idistance <= 7) ? (corrupt_perc_chance + 1000) / 2 :
2106             max(10, 380 - 150 * idistance / 13);
2107 
2108         const int roll = random2(1000);
2109 
2110         if (roll < corrupt_perc_chance && _is_grid_corruptible(*ri))
2111             _corrupt_square(cenv, *ri);
2112         else if (roll < corrupt_flavor_chance && _is_grid_corruptible(*ri))
2113             _corrupt_square_flavor(cenv, *ri);
2114     }
2115 }
2116 
_is_level_corrupted()2117 static bool _is_level_corrupted()
2118 {
2119     if (player_in_branch(BRANCH_ABYSS))
2120         return true;
2121 
2122     return !!env.markers.find(MAT_CORRUPTION_NEXUS);
2123 }
2124 
is_level_incorruptible(bool quiet)2125 bool is_level_incorruptible(bool quiet)
2126 {
2127     if (_is_level_corrupted())
2128     {
2129         if (!quiet)
2130             mpr("This place is already infused with evil and corruption.");
2131         return true;
2132     }
2133 
2134     return false;
2135 }
2136 
_corrupt_choose_colours(corrupt_env * cenv)2137 static void _corrupt_choose_colours(corrupt_env *cenv)
2138 {
2139     colour_t colour = BLACK;
2140     do
2141     {
2142         colour = random_uncommon_colour();
2143     }
2144     while (colour == env.rock_colour || colour == LIGHTGREY || colour == WHITE);
2145     cenv->rock_colour = colour;
2146 
2147     do
2148     {
2149         colour = random_uncommon_colour();
2150     }
2151     while (colour == env.floor_colour || colour == LIGHTGREY
2152            || colour == WHITE);
2153     cenv->floor_colour = colour;
2154 }
2155 
lugonu_corrupt_level(int power)2156 bool lugonu_corrupt_level(int power)
2157 {
2158     if (is_level_incorruptible())
2159         return false;
2160 
2161     simple_god_message("'s Hand of Corruption reaches out!");
2162     take_note(Note(NOTE_MESSAGE, 0, 0, make_stringf("Corrupted %s",
2163               level_id::current().describe().c_str()).c_str()));
2164     mark_corrupted_level(level_id::current());
2165 
2166     flash_view(UA_PLAYER, MAGENTA);
2167 
2168     _initialise_level_corrupt_seeds(power);
2169 
2170     corrupt_env cenv;
2171     _corrupt_choose_colours(&cenv);
2172     _corrupt_level_features(cenv);
2173     run_corruption_effects(300);
2174 
2175 #ifndef USE_TILE_LOCAL
2176     // Allow extra time for the flash to linger.
2177     scaled_delay(1000);
2178 #endif
2179 
2180     return true;
2181 }
2182 
2183 /// If the player has earned enough XP, spawn an exit or stairs down.
abyss_maybe_spawn_xp_exit()2184 void abyss_maybe_spawn_xp_exit()
2185 {
2186     if (!player_in_branch(BRANCH_ABYSS)
2187         || !you.props.exists(ABYSS_STAIR_XP_KEY)
2188         || you.props[ABYSS_STAIR_XP_KEY].get_int() > 0
2189         || !in_bounds(you.pos())
2190         || feat_is_critical(env.grid(you.pos())))
2191     {
2192         return;
2193     }
2194     const bool stairs = !at_branch_bottom()
2195                         && you.props.exists(ABYSS_SPAWNED_XP_EXIT_KEY)
2196                         && coinflip()
2197                         && you.props[ABYSS_SPAWNED_XP_EXIT_KEY].get_bool();
2198 
2199     destroy_wall(you.pos()); // fires listeners etc even if it wasn't a wall
2200     env.grid(you.pos()) = stairs ? DNGN_ABYSSAL_STAIR : DNGN_EXIT_ABYSS;
2201     big_cloud(CLOUD_TLOC_ENERGY, &you, you.pos(), 3 + random2(3), 3, 3);
2202     redraw_screen(); // before the force-more
2203     update_screen();
2204     mprf(MSGCH_BANISHMENT,
2205          "The substance of the Abyss twists violently,"
2206          " and a gateway leading %s appears!", stairs ? "down" : "out");
2207 
2208     you.props[ABYSS_STAIR_XP_KEY] = EXIT_XP_COST;
2209     you.props[ABYSS_SPAWNED_XP_EXIT_KEY] = true;
2210 }
2211