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 ¢re,
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