1 /**
2  * @file
3  * @brief Functions used when building new levels.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "dungeon.h"
9 
10 #include <algorithm>
11 #include <cmath>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <ctime>
15 #include <list>
16 #include <map>
17 #include <set>
18 #include <sstream>
19 
20 #include "abyss.h"
21 #include "acquire.h"
22 #include "artefact.h"
23 #include "branch.h"
24 #include "chardump.h"
25 #include "cloud.h"
26 #include "coordit.h"
27 #include "corpse.h"
28 #include "describe.h"
29 #include "directn.h"
30 #include "dbg-maps.h"
31 #include "dbg-scan.h"
32 #include "dgn-delve.h"
33 #include "dgn-height.h"
34 #include "dgn-overview.h"
35 #include "dgn-shoals.h"
36 #include "end.h"
37 #include "fight.h"
38 #include "files.h"
39 #include "flood-find.h"
40 #include "ghost.h"
41 #include "god-passive.h"
42 #include "item-name.h"
43 #include "item-prop.h"
44 #include "item-status-flag-type.h"
45 #include "items.h"
46 #include "lev-pand.h"
47 #include "libutil.h"
48 #include "mapmark.h"
49 #include "maps.h"
50 #include "message.h"
51 #include "mon-death.h"
52 #include "mon-gear.h"
53 #include "mon-pick.h"
54 #include "mon-place.h"
55 #include "mon-poly.h"
56 #include "nearby-danger.h"
57 #include "notes.h"
58 #include "place.h"
59 #include "randbook.h"
60 #include "random.h"
61 #include "religion.h"
62 #include "show.h"
63 #include "spl-transloc.h"
64 #include "stairs.h"
65 #include "state.h"
66 #include "stringutil.h"
67 #include "rltiles/tiledef-dngn.h"
68 #include "tag-version.h"
69 #include "tile-env.h"
70 #include "tilepick.h"
71 #include "tileview.h"
72 #include "timed-effects.h"
73 #include "traps.h"
74 #include "unique-creature-list-type.h"
75 #ifdef WIZARD
76 #include "wiz-dgn.h"
77 #endif
78 
79 #ifdef DEBUG_DIAGNOSTICS
80 #define DEBUG_TEMPLES
81 #endif
82 
83 #ifdef WIZARD
84 #include "cio.h" // for cancellable_get_line()
85 #endif
86 
87 // DUNGEON BUILDERS
88 static bool _build_level_vetoable(bool enable_random_maps);
89 static void _build_dungeon_level();
90 static bool _valid_dungeon_level();
91 
92 static bool _builder_by_type();
93 static bool _builder_normal();
94 static void _builder_items();
95 static void _builder_monsters();
96 static coord_def _place_specific_feature(dungeon_feature_type feat);
97 static void _place_specific_trap(const coord_def& where, trap_spec* spec,
98                                  int charges = 0);
99 static void _place_branch_entrances(bool use_vaults);
100 static void _place_extra_vaults();
101 static void _place_chance_vaults();
102 static void _place_minivaults();
103 static int _place_uniques();
104 static void _place_traps();
105 static void _prepare_water();
106 static void _check_doors();
107 
108 static void _add_plant_clumps(int rarity, int clump_sparseness,
109                               int clump_radius);
110 
111 static void _pick_float_exits(vault_placement &place,
112                               vector<coord_def> &targets);
113 static bool _feat_is_wall_floor_liquid(dungeon_feature_type);
114 static bool _connect_spotty(const coord_def& from,
115                             bool (*overwriteable)(dungeon_feature_type) = nullptr);
116 static bool _connect_vault_exit(const coord_def& exit);
117 
118 // VAULT FUNCTIONS
119 static const vault_placement *
120 _build_secondary_vault(const map_def *vault,
121                        bool check_collisions = true,
122                        bool make_no_exits = false,
123                        const coord_def &where = coord_def(-1, -1));
124 
125 static const vault_placement *_build_primary_vault(const map_def *vault);
126 
127 static void _build_postvault_level(vault_placement &place);
128 static const vault_placement *
129 _build_vault_impl(const map_def *vault,
130                   bool build_only = false,
131                   bool check_collisions = false,
132                   bool make_no_exits = false,
133                   const coord_def &where = coord_def(-1, -1));
134 
135 static void _vault_grid(vault_placement &,
136                         int vgrid,
137                         const coord_def& where,
138                         keyed_mapspec *mapsp);
139 static void _vault_grid_mons(vault_placement &,
140                         int vgrid,
141                         const coord_def& where,
142                         keyed_mapspec *mapsp);
143 static void _vault_grid_glyph(vault_placement &place, const coord_def& where,
144                               int vgrid);
145 static void _vault_grid_mapspec(vault_placement &place, const coord_def& where,
146                                 keyed_mapspec& mapsp);
147 static dungeon_feature_type _vault_inspect(vault_placement &place,
148                                            int vgrid, keyed_mapspec *mapsp);
149 static dungeon_feature_type _vault_inspect_mapspec(vault_placement &place,
150                                                    keyed_mapspec& mapsp);
151 static dungeon_feature_type _vault_inspect_glyph(int vgrid);
152 
153 static const map_def *_dgn_random_map_for_place(bool minivault);
154 static void _dgn_load_colour_grid();
155 static void _dgn_map_colour_fixup();
156 
157 static void _dgn_unregister_vault(const map_def &map);
158 static void _remember_vault_placement(const vault_placement &place);
159 
160 // Returns true if the given square is okay for use by any character,
161 // but always false for squares in non-transparent vaults.
162 static bool _dgn_square_is_passable(const coord_def &c);
163 
164 static coord_def _dgn_random_point_in_bounds(
165     dungeon_feature_type searchfeat,
166     uint32_t mapmask = MMT_VAULT,
167     dungeon_feature_type adjacent = DNGN_UNSEEN,
168     bool monster_free = false,
169     int tries = 1500);
170 
171 // ALTAR FUNCTIONS
172 static int                  _setup_temple_altars(CrawlHashTable &temple);
173 static dungeon_feature_type _pick_temple_altar();
174 static dungeon_feature_type _pick_an_altar();
175 
176 static vector<god_type> _temple_altar_list;
177 static CrawlHashTable*       _current_temple_hash = nullptr; // XXX: hack!
178 
179 // MISC FUNCTIONS
180 static void _dgn_set_floor_colours();
181 static bool _fixup_interlevel_connectivity();
182 static void _slime_connectivity_fixup();
183 
184 static void _dgn_postprocess_level();
185 static void _calc_density();
186 static void _mark_solid_squares();
187 
188 //////////////////////////////////////////////////////////////////////////
189 // Static data
190 
191 // A mask of vaults and vault-specific flags.
192 vector<vault_placement> Temp_Vaults;
193 static unique_creature_list temp_unique_creatures;
194 static FixedVector<unique_item_status_type, MAX_UNRANDARTS> temp_unique_items;
195 
196 const map_bitmask *Vault_Placement_Mask = nullptr;
197 
198 static bool use_random_maps = true;
199 static bool dgn_check_connectivity = false;
200 static int  dgn_zones = 0;
201 
202 #ifdef DEBUG_STATISTICS
203 static vector<string> _you_all_vault_list;
204 #endif
205 
206 struct coloured_feature
207 {
208     dungeon_feature_type feature;
209     int                  colour;
210 
coloured_featurecoloured_feature211     coloured_feature() : feature(DNGN_UNSEEN), colour(BLACK) { }
coloured_featurecoloured_feature212     coloured_feature(dungeon_feature_type f, int c)
213         : feature(f), colour(c)
214     {
215     }
216 };
217 
218 struct dgn_colour_override_manager
219 {
dgn_colour_override_managerdgn_colour_override_manager220     dgn_colour_override_manager()
221     {
222         _dgn_load_colour_grid();
223     }
224 
~dgn_colour_override_managerdgn_colour_override_manager225     ~dgn_colour_override_manager()
226     {
227         _dgn_map_colour_fixup();
228     }
229 };
230 
231 typedef FixedArray< coloured_feature, GXM, GYM > dungeon_colour_grid;
232 static unique_ptr<dungeon_colour_grid> dgn_colour_grid;
233 
234 static string branch_epilogues[NUM_BRANCHES];
235 
get_uniq_map_tags()236 set<string> &get_uniq_map_tags()
237 {
238     if (you.where_are_you == BRANCH_ABYSS)
239         return you.uniq_map_tags_abyss;
240     else
241         return you.uniq_map_tags;
242 }
243 
get_uniq_map_names()244 set<string> &get_uniq_map_names()
245 {
246     if (you.where_are_you == BRANCH_ABYSS)
247         return you.uniq_map_names_abyss;
248     else
249         return you.uniq_map_names;
250 }
251 
252 /**********************************************************************
253  * builder() - kickoff for the dungeon generator.
254  *********************************************************************/
builder(bool enable_random_maps)255 bool builder(bool enable_random_maps)
256 {
257 #ifndef DEBUG_FULL_DUNGEON_SPAM
258     // hide builder debug spam by default -- this is still collected by a tee
259     // and accessible via &ctrl-l without this #define.
260     msg::suppress quiet(MSGCH_DIAGNOSTICS);
261 #endif
262 
263     // Re-check whether we're in a valid place, it leads to obscure errors
264     // otherwise.
265     ASSERT_RANGE(you.where_are_you, 0, NUM_BRANCHES);
266     ASSERT_RANGE(you.depth, 0 + 1, brdepth[you.where_are_you] + 1);
267 
268     const set<string> uniq_tags = get_uniq_map_tags();
269     const set<string> uniq_names = get_uniq_map_names();
270 
271     // For normal cases, this should already be taken care of by enter_level.
272     // However, we want to be really sure that the builder isn't ever run with
273     // the player at a real position on the level, e.g. during debug code or
274     // tests, because that can impact levelgen (somehow) and cause seed
275     // divergence. (I think it's because actor position can impact item gen,
276     // but it's a bit hard to track down.)
277     unwind_var<coord_def> saved_position(you.position);
278     you.position.reset();
279 
280     // TODO: why are these globals?
281     // Save a copy of unique creatures for vetoes.
282     temp_unique_creatures = you.unique_creatures;
283     // And unrands
284     temp_unique_items = you.unique_items;
285 
286     unwind_bool levelgen(crawl_state.generating_level, true);
287     rng::generator levelgen_rng(you.where_are_you);
288 
289 #ifdef DEBUG_DIAGNOSTICS // no point in enabling unless dprf works
290     CrawlHashTable &debug_logs = you.props["debug_builder_logs"].get_table();
291     string &cur_level_log = debug_logs[level_id::current().describe()].get_string();
292     msg::tee debug_messages(cur_level_log);
293     debug_messages.append_line(make_stringf("Builder log for %s:",
294         level_id::current().describe().c_str()));
295 #endif
296 
297     // N tries to build the level, after which we bail with a capital B.
298     int tries = 50;
299     while (tries-- > 0)
300     {
301         // If we're getting low on available retries, disable random vaults
302         // and minivaults (special levels will still be placed).
303         if (tries < 5)
304             enable_random_maps = false;
305 
306         try
307         {
308             // clean slate
309             crawl_state.last_builder_error_fatal = false;
310 
311             if (_build_level_vetoable(enable_random_maps))
312                 return true;
313 #if defined(DEBUG_VETO_RESUME) && defined(WIZARD)
314             else if (is_wizard_travel_target(level_id::current()))
315             {
316                 // TODO: as-is, this has no way to look at vetos on D:1. You
317                 // can manually force this by adding `|| env.absdepth0 == 0` to
318                 // the condition above.
319                 mprf(MSGCH_ERROR, "Builder paused after veto; use &ctrl-r to resume.");
320                 // reset global state preemptively; otherwise wizard reload will
321                 // quickly deviate from the seed
322                 get_uniq_map_tags() = uniq_tags;
323                 get_uniq_map_names() = uniq_names;
324                 you.unique_creatures = temp_unique_creatures;
325                 you.unique_items = temp_unique_items;
326 
327                 // Because vault generation can be interrupted, this potentially
328                 // leaves unlinked items kicking around, which triggers a lot
329                 // of debug messaging. For the sanity of the user, clean them
330                 // up.
331                 for (int i = 0; i < MAX_ITEMS; i++)
332                     if (env.item[i].defined()
333                         && !env.item[i].holding_monster()
334                         && (!in_bounds(env.item[i].pos)
335                             || env.igrid(env.item[i].pos) == NON_ITEM))
336                     {
337                         dprf("    Cleaning up unfinished item '%s'",
338                                         env.item[i].name(DESC_PLAIN).c_str());
339                         init_item(i);
340                     }
341 
342                 // Remove any portal entrances after a veto; otherwise they
343                 // will generate in the pregen code immediately, and can mess
344                 // up continuing on with the seed. Leave something behind so
345                 // that you can see where it was supposed to go.
346                 // This will still usually trigger the announcements, etc, and
347                 // possibly the flavor tile for the entrance.
348                 for (rectangle_iterator ri(0); ri; ++ri)
349                 {
350                     dungeon_feature_type feat = env.grid(*ri);
351                     if (feat_is_portal_entrance(feat) && !feature_mimic_at(*ri))
352                     {
353                         level_id whither = stair_destination(feat, "", false);
354                         dprf("    Removing portal entrance to %s at %d,%d",
355                                 whither.describe().c_str(), (*ri).x, (*ri).y);
356                         env.grid(*ri) = DNGN_STONE_ARCH;
357                     }
358                 }
359                 update_portal_entrances();
360 
361                 return true;
362             }
363 #endif
364 
365         }
366         catch (map_load_exception &mload)
367         {
368             mprf(MSGCH_ERROR, "Failed to load map, reloading all maps (%s).",
369                  mload.what());
370             reread_maps();
371         }
372 
373         get_uniq_map_tags() = uniq_tags;
374         get_uniq_map_names() = uniq_names;
375         if (crawl_state.last_builder_error_fatal &&
376             (you.props.exists("force_map") || you.props.exists("force_minivault")))
377         {
378             // if there was a fatal lua error and this is a forced levelgen,
379             // it's most likely that the same thing will keep happening over
380             // and over again (and the dev should want to fix the error
381             // anyways.)
382             mprf(MSGCH_ERROR, "Aborting &P builder on fatal lua error");
383             break;
384         }
385         else if (crawl_state.last_builder_error_fatal)
386         {
387             mprf(MSGCH_ERROR,
388                 "Builder <white>VETO</white> on fatal lua error: %s",
389                 crawl_state.last_builder_error.c_str());
390         }
391     }
392 
393     if (!crawl_state.map_stat_gen && !crawl_state.obj_stat_gen)
394     {
395         // Failed to build level, bail out.
396         // Don't crash at this point -- this needs to be handled in pregen code
397         // in case the builder fails in the middle of a pregen sequence.
398 
399         // A failure here indicates that the builder vetoed or otherwise failed
400         // 50 times in a row. This can occasionally happen in depths due to the
401         // complexity of the placement constraints. In order to not break the
402         // player's save, what should happen is that the rng state for the
403         // relevant level get saved -- this will allow resuming the builder
404         // for another 50 tries with a different level seed. This is handled
405         // in `load_level`.
406 
407         // TODO: this doesn't show up in the player's log for some reason. But
408         // it does show up in the builder log / crashlog at least.
409         mprf(MSGCH_ERROR, "Unable to generate level for '%s'!",
410             level_id::current().describe().c_str());
411     }
412 
413     env.level_layout_types.clear(); // is this necessary?
414 
415     return false;
416 }
417 
dgn_record_veto(const dgn_veto_exception & e)418 void dgn_record_veto(const dgn_veto_exception &e)
419 {
420     string error = make_stringf("%s: %s",
421          level_id::current().describe().c_str(), e.what());
422 
423     crawl_state.last_builder_error = error;
424 
425     dprf(DIAG_DNGN, "<white>VETO</white>: %s", error.c_str());
426 
427 #ifdef DEBUG_STATISTICS
428     mapstat_report_map_veto(e.what());
429 #endif
430 
431 }
432 
_build_level_vetoable(bool enable_random_maps)433 static bool _build_level_vetoable(bool enable_random_maps)
434 {
435 #ifdef DEBUG_STATISTICS
436     mapstat_report_map_build_start();
437 #endif
438 
439     dgn_reset_level(enable_random_maps);
440 
441     if (player_in_branch(BRANCH_TEMPLE))
442         _setup_temple_altars(you.props);
443 
444     crawl_state.last_builder_error = "";
445 
446     try
447     {
448         _build_dungeon_level();
449     }
450     catch (dgn_veto_exception& e)
451     {
452         dgn_record_veto(e);
453 
454         // try not to lose any ghosts that have been placed
455         save_ghosts(ghost_demon::find_ghosts(false), false);
456         return false;
457     }
458 
459     _dgn_set_floor_colours();
460 
461     if (crawl_state.game_standard_levelgen()
462         && !_valid_dungeon_level())
463     {
464         return false;
465     }
466 
467 #ifdef DEBUG_MONS_SCAN
468     // If debug_mons_scan() finds a problem while crawl_state.generating_level is
469     // still true then it will announce that a problem was caused
470     // during level generation.
471     debug_mons_scan();
472 #endif
473 
474     if (!env.level_build_method.empty()
475         && env.level_build_method[0] == ' ')
476     {
477         env.level_build_method = env.level_build_method.substr(1);
478     }
479 
480     string level_layout_type = comma_separated_line(
481         env.level_layout_types.begin(),
482         env.level_layout_types.end(), ", ");
483 
484     // Save information in the level's properties hash table
485     // so we can include it in crash reports.
486     env.properties[BUILD_METHOD_KEY] = env.level_build_method;
487     env.properties[LAYOUT_TYPE_KEY]  = level_layout_type;
488 
489     _dgn_postprocess_level();
490 
491     env.level_layout_types.clear();
492     env.level_uniq_maps.clear();
493     env.level_uniq_map_tags.clear();
494     _dgn_map_colour_fixup();
495 
496     // Call the branch epilogue, if any.
497     if (!branch_epilogues[you.where_are_you].empty())
498         if (!dlua.callfn(branch_epilogues[you.where_are_you].c_str(), 0, 0))
499         {
500             mprf(MSGCH_ERROR, "branch epilogue for %s failed: %s",
501                               level_id::current().describe().c_str(),
502                               dlua.error.c_str());
503             return false;
504         }
505 
506     // Discard any Lua chunks we loaded.
507     strip_all_maps();
508 
509     check_map_validity();
510 
511 #ifdef DEBUG_STATISTICS
512     for (auto vault : _you_all_vault_list)
513         mapstat_report_map_success(vault);
514 #endif
515 
516     return true;
517 }
518 
519 // Things that are bugs where we want to assert rather than to sweep it under
520 // the rug with a veto.
_builder_assertions()521 static void _builder_assertions()
522 {
523 #ifdef ASSERTS
524     for (rectangle_iterator ri(0); ri; ++ri)
525         if (!in_bounds(*ri))
526             if (!feat_is_valid_border(env.grid(*ri)))
527             {
528                 die("invalid map border at (%d,%d): %s", ri->x, ri->y,
529                     dungeon_feature_name(env.grid(*ri)));
530             }
531 #endif
532 }
533 
534 /**
535  * Place a transporter and set its destination.
536  *
537  * @param pos     The position of the transporter
538  * @param dest    The position the transporter destination.
539  **/
dgn_place_transporter(const coord_def & pos,const coord_def & dest)540 void dgn_place_transporter(const coord_def &pos, const coord_def &dest)
541 {
542     ASSERT(pos != dest);
543 
544     env.markers.add(new map_position_marker(pos, DNGN_TRANSPORTER, dest));
545     env.markers.clear_need_activate();
546     dungeon_terrain_changed(pos, DNGN_TRANSPORTER, false, true);
547     dungeon_terrain_changed(dest, DNGN_TRANSPORTER_LANDING, false, true);
548 }
549 
550 /**
551  * Create transporters on the current level based on transporter markers. This
552  * does checks for duplicate transporter destinations, transporters with no
553  * destinations, and unused transporter destinations.
554  *
555  * @returns True if no transporter placement errors were found, false
556  *          otherwise.
557  **/
dgn_make_transporters_from_markers()558 bool dgn_make_transporters_from_markers()
559 {
560     bool no_errors = true;
561 
562     // Find transporter destination markers and ensure no duplicates.
563     const vector<map_marker*> dest_markers =
564         find_markers_by_prop(TRANSPORTER_DEST_NAME_PROP);
565     map<string, map_marker *> dest_map;
566     for (auto dm : dest_markers)
567     {
568         const string name = dm->property(TRANSPORTER_DEST_NAME_PROP);
569         if (dest_map.find(name) != dest_map.end())
570         {
571             mprf(MSGCH_ERROR, "Multiple locations with transporter "
572                  "destination name %s.", name.c_str());
573             no_errors = false;
574             continue;
575         }
576         dest_map[name] = dm;
577     }
578 
579     // Place transporters.
580     const vector<map_marker*> trans_markers =
581         find_markers_by_prop(TRANSPORTER_NAME_PROP);
582     map<string, bool> used_dest_map;
583     for (auto tm : trans_markers)
584     {
585         const string name = tm->property(TRANSPORTER_NAME_PROP);
586         if (dest_map.find(name) == dest_map.end())
587         {
588             mprf(MSGCH_ERROR, "Transporter with name %s has no corresponding "
589                  "destination marker.", name.c_str());
590             no_errors = false;
591             continue;
592         }
593         dgn_place_transporter(tm->pos, dest_map[name]->pos);
594         env.markers.remove(tm);
595         used_dest_map[name] = true;
596     }
597 
598     // Clean up any destination markers.
599     for (auto dm : dest_markers)
600     {
601         const string name = dm->property(TRANSPORTER_DEST_NAME_PROP);
602         if (used_dest_map[name])
603             env.markers.remove(dm);
604         else
605         {
606             mprf(MSGCH_ERROR, "Unused transporter destination with name %s.",
607                  name.c_str());
608             no_errors = false;
609         }
610     }
611 
612     return no_errors;
613 }
614 
615 // Should be called after a level is constructed to perform any final
616 // fixups.
_dgn_postprocess_level()617 static void _dgn_postprocess_level()
618 {
619     shoals_postprocess_level();
620     _builder_assertions();
621     _calc_density();
622     _mark_solid_squares();
623 }
624 
dgn_clear_vault_placements()625 void dgn_clear_vault_placements()
626 {
627     env.level_vaults.clear();
628 }
629 
630 // Removes vaults that are not referenced in the map index mask from
631 // the level_vaults array.
dgn_erase_unused_vault_placements()632 void dgn_erase_unused_vault_placements()
633 {
634     set<int> referenced_vault_indexes;
635     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
636     {
637         const int map_index = env.level_map_ids(*ri);
638         if (map_index != INVALID_MAP_INDEX)
639             referenced_vault_indexes.insert(map_index);
640     }
641 
642     // Walk backwards and toss unused vaults.
643     map<int, int> new_vault_index_map;
644     const int nvaults = env.level_vaults.size();
645     for (int i = nvaults - 1; i >= 0; --i)
646     {
647         if (!referenced_vault_indexes.count(i))
648         {
649             {
650                 auto &vp = env.level_vaults[i];
651                 // Unreferenced vault, blow it away
652                 dprf(DIAG_DNGN, "Removing references to unused map #%d)"
653                         " '%s' (%d,%d) (%d,%d)",
654                         i, vp->map.name.c_str(), vp->pos.x, vp->pos.y,
655                         vp->size.x, vp->size.y);
656 
657                 if (!vp->seen)
658                 {
659                     dprf(DIAG_DNGN, "Unregistering unseen vault: %s",
660                             vp->map.name.c_str());
661                     _dgn_unregister_vault(vp->map);
662                 }
663             }
664 
665             env.level_vaults.erase(env.level_vaults.begin() + i);
666 
667             // Fix new indexes for all higher indexed vaults that are
668             // still referenced.
669             for (int j = i + 1; j < nvaults; ++j)
670                 if (int *newidx = map_find(new_vault_index_map, j))
671                     --*newidx;
672         }
673         else
674         {
675             // Vault is still referenced, make a note of this index.
676             new_vault_index_map[i] = i;
677         }
678     }
679 
680     // Finally, update the index map.
681     for (rectangle_iterator ri(MAPGEN_BORDER); ri; ++ri)
682     {
683         const int map_index = env.level_map_ids(*ri);
684         if (map_index != INVALID_MAP_INDEX)
685         {
686             if (int *newidx = map_find(new_vault_index_map, map_index))
687                 env.level_map_ids(*ri) = *newidx;
688         }
689     }
690 
691 #ifdef DEBUG_ABYSS
692     dprf(DIAG_DNGN, "Extant vaults on level: %d",
693          (int) env.level_vaults.size());
694     int i = 0;
695     for (auto &vp : env.level_vaults)
696     {
697         dprf(DIAG_DNGN, "%d) %s (%d,%d) size (%d,%d)",
698              i++, vp->map.name.c_str(), vp->pos.x, vp->pos.y,
699              vp->size.x, vp->size.y);
700     }
701 #endif
702 }
703 
level_clear_vault_memory()704 void level_clear_vault_memory()
705 {
706     dgn_clear_vault_placements();
707     Temp_Vaults.clear();
708     env.level_map_mask.init(0);
709     env.level_map_ids.init(INVALID_MAP_INDEX);
710 }
711 
dgn_flush_map_memory()712 void dgn_flush_map_memory()
713 {
714     // it's probably better in general to just reset `you`. But that's not so
715     // convenient for lua tests, who are the only user of this function.
716     // This leaves some state uninitialized, and probably should be immediately
717     // followed by a call to `initial_dungeon_setup` and something that moves
718     // the player to a level or regenerates a level.
719 
720     // vaults and map stuff
721     you.uniq_map_tags.clear();
722     you.uniq_map_names.clear();
723     you.uniq_map_tags_abyss.clear();
724     you.uniq_map_names_abyss.clear();
725     you.vault_list.clear();
726     you.branches_left.reset();
727     you.zigs_completed = 0;
728     you.zig_max = 0;
729     you.exploration = 0;
730     you.seen_portals = 0; // should be just cosmetic
731     reset_portal_entrances();
732     // would it be safe to just clear you.props?
733     you.props.erase(TEMPLE_SIZE_KEY);
734     you.props.erase(TEMPLE_MAP_KEY);
735     you.props.erase(OVERFLOW_TEMPLES_KEY);
736     you.props.erase(TEMPLE_GODS_KEY);
737     you.clear_place_info();
738     // the following is supposed to clear any persistent lua state related to
739     // the builder. However, it's susceptible to custom dlua doing its own
740     // thing...
741     dlua.callfn("dgn_clear_data", "");
742 
743     // monsters
744     you.unique_creatures.reset();
745 
746     // item stuff that can interact with the builder
747     you.runes.reset();
748     you.obtainable_runes = 15;
749     you.unique_items.init(UNIQ_NOT_EXISTS);
750     you.octopus_king_rings = 0x00;
751     you.item_description.init(255); // random names need reset after this, e.g.
752                                     // via debug.dungeon_setup()
753     you.attribute[ATTR_GOLD_GENERATED] = 0;
754     // potentially relevant for item placement in e.g. troves:
755     you.seen_weapon.init(0);
756     you.seen_armour.init(0);
757     you.seen_misc.reset();
758 }
759 
_dgn_load_colour_grid()760 static void _dgn_load_colour_grid()
761 {
762     dgn_colour_grid.reset(new dungeon_colour_grid);
763     dungeon_colour_grid &dcgrid(*dgn_colour_grid);
764     for (int y = Y_BOUND_1; y <= Y_BOUND_2; ++y)
765         for (int x = X_BOUND_1; x <= X_BOUND_2; ++x)
766             if (env.grid_colours[x][y] != BLACK)
767             {
768                 dcgrid[x][y]
769                     = coloured_feature(env.grid[x][y], env.grid_colours[x][y]);
770             }
771 }
772 
_dgn_map_colour_fixup()773 static void _dgn_map_colour_fixup()
774 {
775     if (!dgn_colour_grid)
776         return;
777 
778     // If the original coloured feature has been changed, reset the colour.
779     const dungeon_colour_grid &dcgrid(*dgn_colour_grid);
780     for (int y = Y_BOUND_1; y <= Y_BOUND_2; ++y)
781         for (int x = X_BOUND_1; x <= X_BOUND_2; ++x)
782             if (dcgrid[x][y].colour != BLACK
783                 && env.grid[x][y] != dcgrid[x][y].feature
784                 && dcgrid[x][y].feature != DNGN_FLOOR)
785             {
786                 env.grid_colours[x][y] = BLACK;
787             }
788 
789     dgn_colour_grid.reset(nullptr);
790 }
791 
dgn_set_grid_colour_at(const coord_def & c,int colour)792 void dgn_set_grid_colour_at(const coord_def &c, int colour)
793 {
794     if (colour != BLACK)
795     {
796         env.grid_colours(c) = colour;
797         if (!dgn_colour_grid)
798             dgn_colour_grid.reset(new dungeon_colour_grid);
799 
800         (*dgn_colour_grid)(c) = coloured_feature(env.grid(c), colour);
801     }
802 }
803 
_set_grd(const coord_def & c,dungeon_feature_type feat)804 static void _set_grd(const coord_def &c, dungeon_feature_type feat)
805 {
806     // It might be good to clear some pgrid flags as well.
807     tile_env.flv(c).feat    = 0;
808     tile_env.flv(c).special = 0;
809     env.grid_colours(c) = 0;
810     env.grid(c) = feat;
811 }
812 
_dgn_register_vault(const string & name,const unordered_set<string> & tags)813 static void _dgn_register_vault(const string &name, const unordered_set<string> &tags)
814 {
815     if (!tags.count("allow_dup"))
816         get_uniq_map_names().insert(name);
817 
818     if (tags.count("luniq"))
819         env.level_uniq_maps.insert(name);
820 
821     vector<string> sorted_tags(tags.begin(), tags.end());
822     sort(sorted_tags.begin(), sorted_tags.end());
823 
824     for (const string &tag : sorted_tags)
825     {
826         if (starts_with(tag, "uniq_"))
827             get_uniq_map_tags().insert(tag);
828         else if (starts_with(tag, "luniq_"))
829             env.level_uniq_map_tags.insert(tag);
830     }
831 }
832 
_dgn_register_vault(const map_def & map)833 static void _dgn_register_vault(const map_def &map)
834 {
835     _dgn_register_vault(map.name, map.get_tags_unsorted());
836 }
837 
_dgn_register_vault(const string & name,string & spaced_tags)838 static void _dgn_register_vault(const string &name, string &spaced_tags)
839 {
840     _dgn_register_vault(name, parse_tags(spaced_tags));
841 }
842 
_dgn_unregister_vault(const map_def & map)843 static void _dgn_unregister_vault(const map_def &map)
844 {
845     get_uniq_map_names().erase(map.name);
846     env.level_uniq_maps.erase(map.name);
847 
848     for (const string &tag : map.get_tags_unsorted())
849     {
850         if (starts_with(tag, "uniq_"))
851             get_uniq_map_tags().erase(tag);
852         else if (starts_with(tag, "luniq_"))
853             env.level_uniq_map_tags.erase(tag);
854     }
855 
856     for (const subvault_place &sub : map.subvault_places)
857         _dgn_unregister_vault(*sub.subvault);
858 }
859 
dgn_square_travel_ok(const coord_def & c)860 bool dgn_square_travel_ok(const coord_def &c)
861 {
862     const dungeon_feature_type feat = env.grid(c);
863     if (feat_is_trap(feat))
864     {
865         const trap_def * const trap = trap_at(c);
866         return !(trap && (trap->type == TRAP_TELEPORT_PERMANENT
867                           || trap->type == TRAP_DISPERSAL));
868     }
869     else // the mimic check here relies on full placement operating, e.g. not &L
870         return feat_is_traversable(feat) || feature_mimic_at(c);
871 }
872 
_dgn_square_is_tele_connected(const coord_def & c)873 static bool _dgn_square_is_tele_connected(const coord_def &c)
874 {
875     // all solid features get marked with no_tele_into, including doors, so
876     // undo this more or less so that this function can be used for connectivity
877     // checking; except when the entire vault is notele
878     const bool vault_notele = dgn_vault_at(c)
879                     && dgn_vault_at(c)->map.has_tag("no_tele_into");
880     return (!(env.pgrid(c) & FPROP_NO_TELE_INTO)
881                             || !vault_notele && feat_is_solid(env.grid(c)))
882         && dgn_square_travel_ok(c);
883 }
884 
_dgn_square_is_passable(const coord_def & c)885 static bool _dgn_square_is_passable(const coord_def &c)
886 {
887     // [enne] Why does this function check these flags?
888     //
889     // Don't peek inside MMT_OPAQUE vaults (all vaults are opaque by
890     // default) because vaults may choose to create isolated regions,
891     // or otherwise cause connectivity issues even if the map terrain
892     // is travel-passable.
893     //
894     // For vaults place by Vaults layouts, however, we set MMT_PASSABLE
895     // and always assume they are properly passable from one entrance
896     // to the other.
897     // The V layout code already guarantees that the level is connected
898     // up validly and room entrances connected up to the doors.
899     return env.level_map_mask(c) & MMT_PASSABLE
900         || !(env.level_map_mask(c) & MMT_OPAQUE) && dgn_square_travel_ok(c);
901 }
902 
_dgn_square_is_boring(const coord_def & c)903 static bool _dgn_square_is_boring(const coord_def &c)
904 {
905     // ignore vault squares by the same algorithm as _dgn_square_is_passable,
906     // but in this case find only floor squares.
907     const dungeon_feature_type feat = env.grid(c);
908     return (feat_has_solid_floor(feat) || feat_is_door(feat))
909         && (env.mgrid(c) == NON_MONSTER
910             || mons_is_firewood(env.mons[env.mgrid(c)]))
911         && (env.level_map_mask(c) & MMT_PASSABLE
912             || !(env.level_map_mask(c) & MMT_OPAQUE));
913 }
914 
_dgn_square_is_ever_passable(const coord_def & c)915 static bool _dgn_square_is_ever_passable(const coord_def &c)
916 {
917     if (!(env.level_map_mask(c) & MMT_OPAQUE))
918     {
919         const dungeon_feature_type feat = env.grid(c);
920         if (feat == DNGN_DEEP_WATER || feat == DNGN_LAVA)
921             return true;
922     }
923     return _dgn_square_is_passable(c);
924 }
925 
_dgn_point_record_stub(const coord_def &)926 static inline void _dgn_point_record_stub(const coord_def &) { }
927 
928 template <class point_record>
_dgn_fill_zone(const coord_def & start,int zone,point_record & record_point,bool (* passable)(const coord_def &)=_dgn_square_is_passable,bool (* iswanted)(const coord_def &)=nullptr)929 static bool _dgn_fill_zone(
930     const coord_def &start, int zone,
931     point_record &record_point,
932     bool (*passable)(const coord_def &) = _dgn_square_is_passable,
933     bool (*iswanted)(const coord_def &) = nullptr)
934 {
935     bool ret = false;
936     list<coord_def> points[2];
937     int cur = 0;
938     int found_points = 0;
939 
940     // No bounds checks, assuming the level has at least one layer of
941     // rock border.
942 
943     for (points[cur].push_back(start); !points[cur].empty();)
944     {
945         for (const auto &c : points[cur])
946         {
947             travel_point_distance[c.x][c.y] = zone;
948             found_points++;
949 
950             if (iswanted && iswanted(c))
951                 ret = true;
952 
953             for (adjacent_iterator ai(c); ai; ++ai)
954             {
955                 const coord_def& cp = *ai;
956                 if (!map_bounds(cp)
957                     || travel_point_distance[cp.x][cp.y] || !passable(cp))
958                 {
959                     continue;
960                 }
961 
962                 travel_point_distance[cp.x][cp.y] = zone;
963                 record_point(cp);
964                 points[!cur].push_back(cp);
965             }
966         }
967 
968         points[cur].clear();
969         cur = !cur;
970     }
971     dprf("Zone %d contains %d points from seed %d,%d", zone, found_points,
972         start.x, start.y);
973     return ret;
974 }
975 
_is_perm_down_stair(const coord_def & c)976 static bool _is_perm_down_stair(const coord_def &c)
977 {
978     switch (env.grid(c))
979     {
980     case DNGN_STONE_STAIRS_DOWN_I:
981     case DNGN_STONE_STAIRS_DOWN_II:
982     case DNGN_STONE_STAIRS_DOWN_III:
983     case DNGN_EXIT_HELL:
984     case DNGN_EXIT_PANDEMONIUM:
985     case DNGN_TRANSIT_PANDEMONIUM:
986     case DNGN_EXIT_ABYSS:
987     case DNGN_ABYSSAL_STAIR:
988         return true;
989     default:
990         return false;
991     }
992 }
993 
_is_upwards_exit_stair(const coord_def & c)994 static bool _is_upwards_exit_stair(const coord_def &c)
995 {
996     // Is this a valid upwards or exit stair out of a branch? In general,
997     // ensure that each region has a stone stair up.
998 
999     if (feature_mimic_at(c))
1000         return false;
1001 
1002     if (feat_is_stone_stair_up(env.grid(c))
1003         || feat_is_branch_exit(env.grid(c)))
1004     {
1005         return true;
1006     }
1007 
1008     switch (env.grid(c))
1009     {
1010     case DNGN_EXIT_PANDEMONIUM:
1011     case DNGN_TRANSIT_PANDEMONIUM:
1012     case DNGN_EXIT_ABYSS:
1013         return true;
1014     case DNGN_ENTER_HELL:
1015         return parent_branch(you.where_are_you) == BRANCH_VESTIBULE;
1016     default:
1017         return false;
1018     }
1019 }
1020 
_is_exit_stair(const coord_def & c)1021 static bool _is_exit_stair(const coord_def &c)
1022 {
1023     if (feature_mimic_at(c))
1024         return false;
1025 
1026     // Branch entries, portals, and abyss entries are not considered exit
1027     // stairs here, as they do not provide an exit (in a transitive sense) from
1028     // the current level.
1029     if (feat_is_stone_stair(env.grid(c))
1030         || feat_is_escape_hatch(env.grid(c))
1031         || feat_is_branch_exit(env.grid(c)))
1032     {
1033         return true;
1034     }
1035 
1036     switch (env.grid(c))
1037     {
1038     case DNGN_EXIT_PANDEMONIUM:
1039     case DNGN_TRANSIT_PANDEMONIUM:
1040     case DNGN_EXIT_ABYSS:
1041         return true;
1042     case DNGN_ENTER_HELL:
1043         return parent_branch(you.where_are_you) == BRANCH_VESTIBULE;
1044     default:
1045         return false;
1046     }
1047 }
1048 
1049 // Counts the number of mutually unreachable areas in the map,
1050 // excluding isolated zones within vaults (we assume the vault author
1051 // knows what she's doing). This is an easy way to check whether a map
1052 // has isolated parts of the level that were not formerly isolated.
1053 //
1054 // All squares within vaults are treated as non-reachable, to simplify
1055 // life, because vaults may change the level layout and isolate
1056 // different areas without changing the number of isolated areas.
1057 // Here's a before and after example of such a vault that would cause
1058 // problems if we considered floor in the vault as non-isolating (the
1059 // vault is represented as V for walls and o for floor squares in the
1060 // vault).
1061 //
1062 // Before:
1063 //
1064 //   xxxxx    xxxxx
1065 //   x<..x    x.2.x
1066 //   x.1.x    xxxxx  3 isolated zones
1067 //   x>..x    x.3.x
1068 //   xxxxx    xxxxx
1069 //
1070 // After:
1071 //
1072 //   xxxxx    xxxxx
1073 //   x<1.x    x.2.x
1074 //   VVVVVVVVVVoooV  3 isolated zones, but the isolated zones are different.
1075 //   x>3.x    x...x
1076 //   xxxxx    xxxxx
1077 //
1078 // If count_stairless is true, returns the number of regions that have no
1079 // stairs in them.
1080 //
1081 // If fill is non-zero, it fills any disconnected regions with fill.
1082 //
1083 // TODO: refactor this to something more usable
_process_disconnected_zones(int x1,int y1,int x2,int y2,bool choose_stairless,dungeon_feature_type fill,bool (* passable)(const coord_def &)=_dgn_square_is_passable,bool (* fill_check)(const coord_def &)=nullptr,int fill_small_zones=0)1084 static int _process_disconnected_zones(int x1, int y1, int x2, int y2,
1085                 bool choose_stairless,
1086                 dungeon_feature_type fill,
1087                 bool (*passable)(const coord_def &) = _dgn_square_is_passable,
1088                 bool (*fill_check)(const coord_def &) = nullptr,
1089                 int fill_small_zones = 0)
1090 {
1091     memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
1092     int nzones = 0;
1093     int ngood = 0;
1094     for (int y = y1; y <= y2 ; ++y)
1095     {
1096         for (int x = x1; x <= x2; ++x)
1097         {
1098             if (!map_bounds(x, y)
1099                 || travel_point_distance[x][y]
1100                 || !passable(coord_def(x, y)))
1101             {
1102                 continue;
1103             }
1104 
1105             int zone_size = 0;
1106             auto inc_zone_size = [&zone_size](const coord_def &) { zone_size++; };
1107 
1108             const bool found_exit_stair =
1109                 _dgn_fill_zone(coord_def(x, y), ++nzones,
1110                                inc_zone_size,
1111                                passable,
1112                                choose_stairless ? (at_branch_bottom() ?
1113                                                    _is_upwards_exit_stair :
1114                                                    _is_exit_stair) : nullptr);
1115 
1116             // If we want only stairless zones, screen out zones that did
1117             // have stairs.
1118             if (choose_stairless && found_exit_stair)
1119                 ++ngood;
1120             else if (fill
1121                 && (fill_small_zones <= 0 || zone_size <= fill_small_zones))
1122             {
1123                 // Don't fill in areas connected to vaults.
1124                 // We want vaults to be accessible; if the area is disconneted
1125                 // from the rest of the level, this will cause the level to be
1126                 // vetoed later on.
1127                 bool veto = false;
1128                 vector<coord_def> coords;
1129                 dprf("Filling zone %d", nzones);
1130                 for (int fy = y1; fy <= y2 ; ++fy)
1131                 {
1132                     for (int fx = x1; fx <= x2; ++fx)
1133                     {
1134                         if (travel_point_distance[fx][fy] == nzones)
1135                         {
1136                             if (map_masked(coord_def(fx, fy), MMT_VAULT))
1137                             {
1138                                 veto = true;
1139                                 break;
1140                             }
1141                             else if (!fill_check || fill_check(coord_def(fx, fy)))
1142                                 coords.emplace_back(fx, fy);
1143                         }
1144                     }
1145                     if (veto)
1146                         break;
1147                 }
1148                 if (!veto)
1149                 {
1150                     for (auto c : coords)
1151                     {
1152                         // For normal builder scenarios items shouldn't be
1153                         // placed yet, but it could (if not careful) happen
1154                         // in weirder cases, such as the abyss.
1155                         if (env.igrid(c) != NON_ITEM
1156                             && (!feat_is_traversable(fill)
1157                                 || feat_destroys_items(fill)))
1158                         {
1159                             // Alternatively, could place floor instead?
1160                             dprf("Nuke item stack at (%d, %d)", c.x, c.y);
1161                             lose_item_stack(c);
1162                         }
1163                         _set_grd(c, fill);
1164                         if (env.mgrid(c) != NON_MONSTER
1165                             && !env.mons[env.mgrid(c)].is_habitable_feat(fill))
1166                         {
1167                             monster_die(env.mons[env.mgrid(c)],
1168                                         KILL_RESET, NON_MONSTER, false, true);
1169                         }
1170                     }
1171                 }
1172             }
1173         }
1174     }
1175 
1176     return nzones - ngood;
1177 }
1178 
dgn_count_tele_zones(bool choose_stairless)1179 int dgn_count_tele_zones(bool choose_stairless)
1180 {
1181     dprf("Counting teleport zones");
1182     return _process_disconnected_zones(0, 0, GXM-1, GYM-1, choose_stairless,
1183                                     DNGN_UNSEEN, _dgn_square_is_tele_connected);
1184 }
1185 
1186 // Count number of mutually isolated zones. If choose_stairless, only count
1187 // zones with no stairs in them. If fill is set to anything other than
1188 // DNGN_UNSEEN, chosen zones will be filled with the provided feature.
dgn_count_disconnected_zones(bool choose_stairless,dungeon_feature_type fill)1189 int dgn_count_disconnected_zones(bool choose_stairless,
1190                                  dungeon_feature_type fill)
1191 {
1192     return _process_disconnected_zones(0, 0, GXM-1, GYM-1, choose_stairless,
1193                                        fill);
1194 }
1195 
_fill_small_disconnected_zones()1196 static void _fill_small_disconnected_zones()
1197 {
1198     // debugging tip: change the feature to something like lava that will be
1199     // very noticeable.
1200     // TODO: make even more agressive, up to ~25?
1201     _process_disconnected_zones(0, 0, GXM-1, GYM-1, true, DNGN_ROCK_WALL,
1202                                        _dgn_square_is_passable,
1203                                        _dgn_square_is_boring,
1204                                        10);
1205 }
1206 
_fixup_hell_stairs()1207 static void _fixup_hell_stairs()
1208 {
1209     for (rectangle_iterator ri(1); ri; ++ri)
1210     {
1211         if (feat_is_stone_stair_up(env.grid(*ri))
1212             || env.grid(*ri) == DNGN_ESCAPE_HATCH_UP)
1213         {
1214             _set_grd(*ri, DNGN_ENTER_HELL);
1215         }
1216     }
1217 }
1218 
_fixup_pandemonium_stairs()1219 static void _fixup_pandemonium_stairs()
1220 {
1221     for (rectangle_iterator ri(1); ri; ++ri)
1222     {
1223         if (feat_is_stone_stair_up(env.grid(*ri))
1224             || env.grid(*ri) == DNGN_ESCAPE_HATCH_UP)
1225         {
1226             _set_grd(*ri, DNGN_TRANSIT_PANDEMONIUM);
1227         }
1228     }
1229 }
1230 
_mask_vault(const vault_placement & place,unsigned mask,function<bool (const coord_def &)> skip_fun=nullptr)1231 static void _mask_vault(const vault_placement &place, unsigned mask,
1232                         function<bool(const coord_def &)> skip_fun = nullptr)
1233 {
1234     for (vault_place_iterator vi(place); vi; ++vi)
1235         if (!skip_fun || !skip_fun(*vi))
1236             env.level_map_mask(*vi) |= mask;
1237 }
1238 
_dgn_apply_map_index(const vault_placement & place,int map_index)1239 static void _dgn_apply_map_index(const vault_placement &place, int map_index)
1240 {
1241     for (vault_place_iterator vi(place); vi; ++vi)
1242         env.level_map_ids(*vi) = map_index;
1243 }
1244 
1245 const vault_placement *
dgn_register_place(const vault_placement & place,bool register_vault)1246 dgn_register_place(const vault_placement &place, bool register_vault)
1247 {
1248     const int  map_index    = env.level_vaults.size();
1249     const bool overwritable = place.map.is_overwritable_layout();
1250     const bool transparent  = place.map.has_tag("transparent");
1251 
1252     if (register_vault)
1253     {
1254         _dgn_register_vault(place.map);
1255         for (int i = env.new_subvault_names.size() - 1; i >= 0; i--)
1256         {
1257             _dgn_register_vault(env.new_subvault_names[i],
1258                                 env.new_subvault_tags[i]);
1259         }
1260         clear_subvault_stack();
1261 
1262         // Identify each square in the map with its map_index.
1263         if (!overwritable)
1264             _dgn_apply_map_index(place, map_index);
1265     }
1266 
1267     if (!overwritable)
1268     {
1269         if (place.map.orient == MAP_ENCOMPASS)
1270         {
1271             for (rectangle_iterator ri(0); ri; ++ri)
1272                 env.level_map_mask(*ri) |= MMT_VAULT;
1273         }
1274         else
1275             _mask_vault(place, MMT_VAULT);
1276 
1277         if (place.map.has_tag("passable"))
1278         {
1279             // Ignore outside of Vaults -- creates too many bugs otherwise.
1280             // This tag is mainly to allow transporter vaults to work with
1281             // Vaults layout code.
1282             if (player_in_branch(BRANCH_VAULTS))
1283                 _mask_vault(place, MMT_PASSABLE);
1284         }
1285         else if (!transparent)
1286         {
1287             // mask everything except for any marked vault exits as opaque.
1288             // This uses vault exits marked by @, +, = on the vault edge, and
1289             // in a pinch, spaces in floats that are necessary for exits.
1290             // (This is overridden by explicit masking, or no_exits.)
1291             _mask_vault(place, MMT_OPAQUE,
1292                 [&place](const coord_def &c)
1293                 {
1294                     return !place.map.has_tag("no_exits")
1295                         && count(place.exits.begin(), place.exits.end(), c);
1296                 });
1297         }
1298     }
1299 
1300     // Find tags matching properties.
1301     for (const auto &tag : place.map.get_tags_unsorted())
1302     {
1303         const feature_property_type prop = str_to_fprop(tag);
1304         if (prop == FPROP_NONE)
1305             continue;
1306 
1307         for (vault_place_iterator vi(place); vi; ++vi)
1308             env.pgrid(*vi) |= prop;
1309 
1310     }
1311 
1312     if (place.map.has_tag("no_monster_gen"))
1313         _mask_vault(place, MMT_NO_MONS);
1314 
1315     if (place.map.has_tag("no_item_gen"))
1316         _mask_vault(place, MMT_NO_ITEM);
1317 
1318     if (place.map.has_tag("no_pool_fixup"))
1319         _mask_vault(place, MMT_NO_POOL);
1320 
1321     if (place.map.has_tag("no_wall_fixup"))
1322         _mask_vault(place, MMT_NO_WALL);
1323 
1324     if (place.map.has_tag("no_trap_gen"))
1325         _mask_vault(place, MMT_NO_TRAP);
1326 
1327     // Now do per-square by-symbol masking.
1328     for (vault_place_iterator vi(place); vi; ++vi)
1329     {
1330         const keyed_mapspec *spec = place.map.mapspec_at(*vi - place.pos);
1331 
1332         if (spec != nullptr)
1333         {
1334             env.level_map_mask(*vi) |= (short)spec->map_mask.flags_set;
1335             env.level_map_mask(*vi) &= ~((short)spec->map_mask.flags_unset);
1336         }
1337     }
1338 
1339     if (place.map.floor_colour != BLACK)
1340         env.floor_colour = place.map.floor_colour;
1341 
1342     if (place.map.rock_colour != BLACK)
1343         env.rock_colour = place.map.rock_colour;
1344 
1345     if (!place.map.rock_tile.empty())
1346     {
1347         tileidx_t rock;
1348         if (tile_dngn_index(place.map.rock_tile.c_str(), &rock))
1349         {
1350             tile_env.default_flavour.wall_idx =
1351                 store_tilename_get_index(place.map.rock_tile);
1352 
1353             tile_env.default_flavour.wall = rock;
1354         }
1355     }
1356 
1357     if (!place.map.floor_tile.empty())
1358     {
1359         tileidx_t floor;
1360         if (tile_dngn_index(place.map.floor_tile.c_str(), &floor))
1361         {
1362             tile_env.default_flavour.floor_idx =
1363                 store_tilename_get_index(place.map.floor_tile);
1364 
1365             tile_env.default_flavour.floor = floor;
1366         }
1367     }
1368 
1369     vault_placement *new_vault_place = new vault_placement(place);
1370     env.level_vaults.emplace_back(new_vault_place);
1371     if (register_vault)
1372         _remember_vault_placement(place);
1373     return new_vault_place;
1374 }
1375 
_dgn_ensure_vault_placed(bool vault_success,bool disable_further_vaults,string desc)1376 static bool _dgn_ensure_vault_placed(bool vault_success,
1377                                      bool disable_further_vaults,
1378                                      string desc)
1379 {
1380     if (!vault_success)
1381     {
1382         // the exception message will overwrite last_builder_error, so package
1383         // this info in here
1384         string err = make_stringf("Vault placement failure for '%s'.", desc.c_str());
1385         if (!crawl_state.last_builder_error.empty())
1386             err += " " + crawl_state.last_builder_error;
1387         throw dgn_veto_exception(err);
1388     }
1389     else if (disable_further_vaults)
1390         use_random_maps = false;
1391     return vault_success;
1392 }
1393 
_ensure_vault_placed_ex(bool vault_success,const map_def * vault)1394 static bool _ensure_vault_placed_ex(bool vault_success, const map_def *vault)
1395 {
1396     return _dgn_ensure_vault_placed(vault_success,
1397                                     (!vault->is_extra_vault()
1398                                      && vault->orient == MAP_ENCOMPASS),
1399                                     vault->name);
1400 }
1401 
_find_level_feature(int feat)1402 static coord_def _find_level_feature(int feat)
1403 {
1404     for (rectangle_iterator ri(1); ri; ++ri)
1405     {
1406         if (env.grid(*ri) == feat)
1407             return *ri;
1408     }
1409 
1410     return coord_def(0, 0);
1411 }
1412 
_has_connected_stone_stairs_from(const coord_def & c)1413 static bool _has_connected_stone_stairs_from(const coord_def &c)
1414 {
1415     flood_find<feature_grid, coord_predicate> ff(env.grid, in_bounds);
1416     ff.add_feat(DNGN_STONE_STAIRS_DOWN_I);
1417     ff.add_feat(DNGN_STONE_STAIRS_DOWN_II);
1418     ff.add_feat(DNGN_STONE_STAIRS_DOWN_III);
1419     ff.add_feat(DNGN_STONE_STAIRS_UP_I);
1420     ff.add_feat(DNGN_STONE_STAIRS_UP_II);
1421     ff.add_feat(DNGN_STONE_STAIRS_UP_III);
1422 
1423     coord_def where = ff.find_first_from(c, env.level_map_mask);
1424     return where.x || !ff.did_leave_vault();
1425 }
1426 
_has_connected_downstairs_from(const coord_def & c)1427 static bool _has_connected_downstairs_from(const coord_def &c)
1428 {
1429     flood_find<feature_grid, coord_predicate> ff(env.grid, in_bounds);
1430     ff.add_feat(DNGN_STONE_STAIRS_DOWN_I);
1431     ff.add_feat(DNGN_STONE_STAIRS_DOWN_II);
1432     ff.add_feat(DNGN_STONE_STAIRS_DOWN_III);
1433     ff.add_feat(DNGN_ESCAPE_HATCH_DOWN);
1434 
1435     coord_def where = ff.find_first_from(c, env.level_map_mask);
1436     return where.x || !ff.did_leave_vault();
1437 }
1438 
_is_level_stair_connected(dungeon_feature_type feat)1439 static bool _is_level_stair_connected(dungeon_feature_type feat)
1440 {
1441     coord_def up = _find_level_feature(feat);
1442     if (up.x && up.y)
1443         return _has_connected_downstairs_from(up);
1444 
1445     return false;
1446 }
1447 
_valid_dungeon_level()1448 static bool _valid_dungeon_level()
1449 {
1450     // D:1 only.
1451     // Also, what's the point of this check?  Regular connectivity should
1452     // do that already.
1453     if (player_in_branch(BRANCH_DUNGEON) && you.depth == 1)
1454         return _is_level_stair_connected(branches[BRANCH_DUNGEON].exit_stairs);
1455 
1456     return true;
1457 }
1458 
dgn_reset_level(bool enable_random_maps)1459 void dgn_reset_level(bool enable_random_maps)
1460 {
1461     env.level_uniq_maps.clear();
1462     env.level_uniq_map_tags.clear();
1463     clear_subvault_stack();
1464 
1465     you.unique_creatures = temp_unique_creatures;
1466     you.unique_items = temp_unique_items;
1467 
1468 #ifdef DEBUG_STATISTICS
1469     _you_all_vault_list.clear();
1470 #endif
1471     env.level_build_method.clear();
1472     env.level_layout_types.clear();
1473     level_clear_vault_memory();
1474     dgn_colour_grid.reset(nullptr);
1475 
1476     use_random_maps = enable_random_maps;
1477     dgn_check_connectivity = false;
1478     dgn_zones        = 0;
1479 
1480     _temple_altar_list.clear();
1481     _current_temple_hash = nullptr;
1482 
1483     // Forget level properties.
1484     env.properties.clear();
1485     env.heightmap.reset(nullptr);
1486 
1487     env.absdepth0 = absdungeon_depth(you.where_are_you, you.depth);
1488 
1489     if (!crawl_state.test)
1490         dprf(DIAG_DNGN, "absdepth0 = %d", env.absdepth0);
1491 
1492     // Blank level with DNGN_ROCK_WALL.
1493     env.grid.init(DNGN_ROCK_WALL);
1494     env.pgrid.init(terrain_property_t{});
1495     env.grid_colours.init(BLACK);
1496     env.map_knowledge.init(map_cell());
1497     env.map_forgotten.reset();
1498     env.map_seen.reset();
1499 
1500     // Delete all traps.
1501     env.trap.clear();
1502 
1503     // Initialise all items.
1504     for (int i = 0; i < MAX_ITEMS; i++)
1505         init_item(i);
1506 
1507     // Reset all monsters.
1508     reset_all_monsters();
1509     init_anon();
1510 
1511     // ... and Pan/regular spawn lists.
1512     env.mons_alloc.init(MONS_NO_MONSTER);
1513     setup_vault_mon_list();
1514 
1515     env.cloud.clear();
1516 
1517     env.mgrid.init(NON_MONSTER);
1518     env.igrid.init(NON_ITEM);
1519 
1520     // Reset all shops.
1521     env.shop.clear();
1522 
1523     // Clear all markers.
1524     env.markers.clear();
1525 
1526     // Lose all listeners.
1527     dungeon_events.clear();
1528 
1529     // Set default random monster generation rate (smaller is more often,
1530     // except that 0 == no random monsters).
1531     if (player_in_branch(BRANCH_TEMPLE)
1532         && !player_on_orb_run() // except for the Orb run
1533         || crawl_state.game_is_tutorial())
1534     {
1535         // No random monsters in tutorial or ecu temple
1536         env.spawn_random_rate = 0;
1537     }
1538     else if (player_in_connected_branch()
1539              || (player_on_orb_run() && !player_in_branch(BRANCH_ABYSS)))
1540         env.spawn_random_rate = 240;
1541     else if (player_in_branch(BRANCH_ABYSS)
1542              || player_in_branch(BRANCH_PANDEMONIUM))
1543     {
1544         // Abyss spawn rate is set for those characters that start out in the
1545         // Abyss; otherwise the number is ignored in the Abyss.
1546         env.spawn_random_rate = 50;
1547     }
1548     else
1549         // No random monsters in portal vaults if we don't have the orb.
1550         env.spawn_random_rate = 0;
1551     env.density = 0;
1552     env.forest_awoken_until = 0;
1553 
1554     env.floor_colour = BLACK;
1555     env.rock_colour  = BLACK;
1556 
1557     // Clear exclusions
1558     clear_excludes();
1559 
1560     // Clear custom tile settings from vaults
1561     tile_init_default_flavour();
1562     tile_clear_flavour();
1563     tile_env.names.clear();
1564 
1565     update_portal_entrances();
1566 }
1567 
_num_items_wanted(int absdepth0)1568 static int _num_items_wanted(int absdepth0)
1569 {
1570     if (branches[you.where_are_you].branch_flags & brflag::no_items)
1571         return 0;
1572     else if (absdepth0 > 5 && one_chance_in(500 - 5 * absdepth0))
1573         return 10 + random2avg(85, 2); // rich level!
1574     else if (absdepth0 < 3)
1575         return 3 + roll_dice(3, 7); // thin loot on early floors
1576     else
1577         return 3 + roll_dice(3, 10);
1578 }
1579 
_mon_die_size()1580 static int _mon_die_size()
1581 {
1582     const int size = branches[you.where_are_you].mon_die_size;
1583     if (you.where_are_you != BRANCH_DUNGEON)
1584         return size;
1585 
1586     // Dungeon is a very special place and needs a lot of hand-holding.
1587     // Historically we used weird mysterious MONS_NO_MONSTER weights for
1588     // this balancing, but now we have technology.
1589     switch (you.depth)
1590     {
1591         case 1:
1592             return 12;
1593         case 2:
1594             return 10;
1595         case 3:
1596         case 4:
1597             return 9;
1598         case 5:
1599         case 6:
1600             return 7;
1601         case 7:
1602             return 6;
1603         case 8:
1604         case 9:
1605             return 5;
1606         case 10:
1607             return 4;
1608         case 11:
1609             return 5;
1610         case 12:
1611         case 13:
1612         case 14:
1613         case 15:
1614             return 6;
1615         default:
1616             return 12;
1617     }
1618 }
1619 
1620 // Return how many level monster are wanted for level generation.
_num_mons_wanted()1621 static int _num_mons_wanted()
1622 {
1623     const bool in_pan = player_in_branch(BRANCH_PANDEMONIUM);
1624     // No disconnected branches aside from Pan have level monsters.
1625     if ((!player_in_connected_branch() && !in_pan)
1626         // Temple is connected but has no monsters.
1627         || !branch_has_monsters(you.where_are_you))
1628     {
1629         return 0;
1630     }
1631 
1632     int mon_wanted = roll_dice(3, _mon_die_size());
1633     if (mon_wanted > 60)
1634         mon_wanted = 60;
1635     return mon_wanted;
1636 }
1637 
_fixup_walls()1638 static void _fixup_walls()
1639 {
1640     // If level part of Dis -> all walls metal.
1641     // If Vaults:$ -> all walls metal or crystal.
1642     // If part of crypt -> all walls stone.
1643 
1644     dungeon_feature_type wall_type = DNGN_ROCK_WALL;
1645 
1646     if (!player_in_connected_branch())
1647         return;
1648 
1649     switch (you.where_are_you)
1650     {
1651     case BRANCH_DIS:
1652         wall_type = DNGN_METAL_WALL;
1653         break;
1654 
1655     case BRANCH_VAULTS:
1656     {
1657         // Everything but the branch end is handled in Lua.
1658         if (you.depth == branches[BRANCH_VAULTS].numlevels)
1659         {
1660             wall_type = random_choose_weighted(1, DNGN_CRYSTAL_WALL,
1661                                                9, DNGN_METAL_WALL);
1662         }
1663         break;
1664     }
1665 
1666     case BRANCH_CRYPT:
1667         wall_type = DNGN_STONE_WALL;
1668         break;
1669 
1670     case BRANCH_SLIME:
1671         wall_type = DNGN_SLIMY_WALL;
1672         break;
1673 
1674     default:
1675         return;
1676     }
1677 
1678     dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, wall_type,
1679                      MMT_NO_WALL);
1680 }
1681 
1682 // Remove any items that are on squares that items should not be on.
1683 // link_items() must be called after this function.
fixup_misplaced_items()1684 void fixup_misplaced_items()
1685 {
1686     for (auto &item : env.item)
1687     {
1688         if (!item.defined() || item.held_by_monster())
1689             continue;
1690 
1691         if (in_bounds(item.pos))
1692         {
1693             dungeon_feature_type feat = env.grid(item.pos);
1694             if (feat_has_solid_floor(feat))
1695                 continue;
1696 
1697             // We accept items in deep water in the Abyss---they are likely to
1698             // be revealed eventually by morphing, and having deep water push
1699             // items away leads to strange results.
1700             if (feat == DNGN_DEEP_WATER && player_in_branch(BRANCH_ABYSS))
1701                 continue;
1702 
1703             mprf(MSGCH_ERROR, "Item %s buggily placed in feature %s at (%d, %d).",
1704                  item.name(DESC_PLAIN).c_str(),
1705                  feature_description_at(item.pos, false, DESC_PLAIN).c_str(),
1706                  item.pos.x, item.pos.y);
1707         }
1708         else
1709         {
1710             mprf(MSGCH_ERROR, "Item buggily placed out of bounds at (%d, %d).",
1711                  item.pos.x, item.pos.y);
1712         }
1713 
1714         // Can't just unlink item because it might not have been linked yet.
1715         item.base_type = OBJ_UNASSIGNED;
1716         item.quantity = 0;
1717         item.pos.reset();
1718     }
1719 }
1720 
1721 /*
1722  * At the top or bottom of a branch, adjust or remove illegal stairs:
1723  *
1724  * - non-vault escape hatches pointing outside the branch are removed
1725  * - non-vault stone stairs down from X:$ are removed
1726  * - stone stairs up from X:1 are replaced with branch exit feature
1727  *   - if there is more than one such stair, an error is logged
1728  * - vault-specified hatches or stairs are replaced with appropriate features
1729  *   - hatches down from X:$ are pointed up instead, and vice versa on X:1
1730  *   - for single-level branches, all hatches turn into the branch exit feature
1731  *   - if the branch has an "escape feature", it is used instead of hatches up
1732  */
_fixup_branch_stairs()1733 static void _fixup_branch_stairs()
1734 {
1735     const auto& branch = your_branch();
1736     const bool root = player_in_branch(root_branch);
1737     const bool top = you.depth == 1;
1738     const bool bottom = at_branch_bottom();
1739 
1740     const dungeon_feature_type exit =
1741         root ? DNGN_EXIT_DUNGEON
1742              : branch.exit_stairs;
1743     const dungeon_feature_type escape =
1744         branch.escape_feature == NUM_FEATURES ? DNGN_ESCAPE_HATCH_UP :
1745                                                 branch.escape_feature;
1746     const dungeon_feature_type up_hatch =
1747         top && bottom ? exit :
1748                   top ? DNGN_ESCAPE_HATCH_DOWN :
1749                         escape;
1750 
1751 #ifdef DEBUG_DIAGNOSTICS
1752     int count = 0;
1753 #endif
1754     // Just in case we somehow get here with more than one stair placed.
1755     // Prefer stairs that are placed in vaults for picking an exit at
1756     // random.
1757     vector<coord_def> vault_stairs, normal_stairs;
1758     for (rectangle_iterator ri(1); ri; ++ri)
1759     {
1760         const bool vault = map_masked(*ri, MMT_VAULT);
1761         const auto escape_replacement = vault ? up_hatch : DNGN_FLOOR;
1762         if (bottom && (feat_is_stone_stair_down(env.grid(*ri))
1763                        || env.grid(*ri) == DNGN_ESCAPE_HATCH_DOWN))
1764         {
1765             _set_grd(*ri, escape_replacement);
1766         }
1767 
1768         if (top)
1769         {
1770             if (env.grid(*ri) == DNGN_ESCAPE_HATCH_UP)
1771                 _set_grd(*ri, escape_replacement);
1772             else if (feat_is_stone_stair_up(env.grid(*ri)))
1773             {
1774 #ifdef DEBUG_DIAGNOSTICS
1775                 if (count++ && !root)
1776                 {
1777                     mprf(MSGCH_ERROR, "Multiple branch exits on %s",
1778                          level_id::current().describe().c_str());
1779                 }
1780 #endif
1781                 if (root)
1782                 {
1783                     env.markers.add(new map_feature_marker(*ri, env.grid(*ri)));
1784                     _set_grd(*ri, exit);
1785                 }
1786                 else
1787                 {
1788                     if (vault)
1789                         vault_stairs.push_back(*ri);
1790                     else
1791                         normal_stairs.push_back(*ri);
1792                 }
1793             }
1794         }
1795     }
1796     if (!root)
1797     {
1798         vector<coord_def> stairs;
1799         if (!vault_stairs.empty())
1800             stairs = vault_stairs;
1801         else
1802             stairs = normal_stairs;
1803 
1804         if (!stairs.empty())
1805         {
1806             shuffle_array(stairs);
1807             coord_def coord = *(stairs.begin());
1808             env.markers.add(new map_feature_marker(coord, env.grid(coord)));
1809             _set_grd(coord, exit);
1810             for (auto it = stairs.begin() + 1; it != stairs.end(); it++)
1811                 _set_grd(*it, DNGN_FLOOR);
1812         }
1813     }
1814 }
1815 
1816 /// List all stone stairs of the indicated type.
_find_stone_stairs(bool up_stairs)1817 static list<coord_def> _find_stone_stairs(bool up_stairs)
1818 {
1819     list<coord_def> stairs;
1820 
1821     for (rectangle_iterator ri(1); ri; ++ri)
1822     {
1823         const coord_def& c = *ri;
1824         if (feature_mimic_at(c))
1825             continue;
1826 
1827         const dungeon_feature_type feat = env.grid(c);
1828         if (feat_is_stone_stair(feat)
1829             && up_stairs == feat_is_stone_stair_up(feat))
1830         {
1831             stairs.push_back(c);
1832         }
1833     }
1834 
1835     return stairs;
1836 }
1837 
1838 /**
1839  * Try to turn excess stairs into hatches.
1840  *
1841  * @param stairs[in,out]    The list of stairs to be trimmed; any stairs that
1842  *                          are turned into hatches will be removed.
1843  * @param needed_stairs     The desired number of stairs.
1844  * @param preserve_vault_stairs    Don't trapdoorify stairs that are in vaults.
1845  * @param hatch_type        What sort of hatch to turn excess stairs into.
1846  */
_cull_redundant_stairs(list<coord_def> & stairs,unsigned int needed_stairs,bool preserve_vault_stairs,dungeon_feature_type hatch_type)1847 static void _cull_redundant_stairs(list<coord_def> &stairs,
1848                                    unsigned int needed_stairs,
1849                                    bool preserve_vault_stairs,
1850                                    dungeon_feature_type hatch_type)
1851 {
1852     // we're going to iterate over the list, looking for redundant stairs.
1853     // (redundant = can walk from one to the other.) For each of
1854     // those iterations, we'll iterate over the remaining list checking for
1855     // stairs redundant with the outer iteration, and hatchify + remove from
1856     // the stair list any redundant stairs we find.
1857 
1858     for (auto iter1 = stairs.begin();
1859          iter1 != stairs.end() && stairs.size() <= needed_stairs;
1860          ++iter1)
1861     {
1862         const coord_def s1_loc = *iter1;
1863         // Ensure we don't search for the feature at s1. XXX: unwind_var?
1864         const dungeon_feature_type saved_feat = env.grid(s1_loc);
1865         env.grid(s1_loc) = DNGN_FLOOR;
1866 
1867         auto iter2 = iter1;
1868         ++iter2;
1869         while (iter2 != stairs.end() && stairs.size() > needed_stairs)
1870         {
1871             const coord_def s2_loc = *iter2;
1872             auto being_examined = iter2;
1873             ++iter2;
1874 
1875             if (preserve_vault_stairs && map_masked(s2_loc, MMT_VAULT))
1876                 continue;
1877 
1878             flood_find<feature_grid, coord_predicate> ff(env.grid,
1879                                                          in_bounds);
1880             ff.add_feat(env.grid(s2_loc));
1881             const coord_def where =
1882                 ff.find_first_from(s1_loc, env.level_map_mask);
1883             if (!where.x) // these stairs aren't in the same zone
1884                 continue;
1885 
1886             dprf(DIAG_DNGN,
1887                  "Too many stairs -- removing one of a connected pair.");
1888             env.grid(s2_loc) = hatch_type;
1889             stairs.erase(being_examined);
1890         }
1891 
1892         env.grid(s1_loc) = saved_feat;
1893     }
1894 }
1895 
1896 /**
1897  * Trapdoorify stairs at random, until we reach the specified number.
1898  * @param stairs[in,out]    The list of stairs to be trimmed; any stairs that
1899  *                          are turned into hatches will be removed. Order not
1900  *                          preserved.
1901  * @param needed_stairs     The desired number of stairs.
1902  * @param preserve_vault_stairs    Don't remove stairs that are in vaults.
1903  * @param hatch_type        What sort of hatch to turn excess stairs into.
1904  */
_cull_random_stairs(list<coord_def> & stairs,unsigned int needed_stairs,bool preserve_vault_stairs,dungeon_feature_type hatch_type)1905 static void _cull_random_stairs(list<coord_def> &stairs,
1906                                 unsigned int needed_stairs,
1907                                 bool preserve_vault_stairs,
1908                                 dungeon_feature_type hatch_type)
1909 {
1910     while (stairs.size() > needed_stairs)
1911     {
1912         const int remove_index = random2(stairs.size());
1913         // rotate the list until the desired element is in front
1914         for (int i = 0; i < remove_index; ++i)
1915         {
1916             stairs.push_back(stairs.front());
1917             stairs.pop_front();
1918         }
1919 
1920         if (preserve_vault_stairs)
1921         {
1922             // try to rotate a non-vault stair to the front, if possible.
1923             for (size_t i = 0; i < stairs.size(); ++i)
1924             {
1925                 if (map_masked(stairs.front(), MMT_VAULT))
1926                 {
1927                     stairs.push_back(stairs.front());
1928                     stairs.pop_front();
1929                 }
1930             }
1931 
1932             // If we looped through all possibilities, then it
1933             // means that there are more than 3 stairs in vaults and
1934             // we can't preserve vault stairs.
1935             if (map_masked(stairs.front(), MMT_VAULT))
1936             {
1937                 dprf(DIAG_DNGN, "Too many stairs inside vaults!");
1938                 return;
1939             }
1940         }
1941 
1942         dprf(DIAG_DNGN, "Too many stairs -- removing one blindly.");
1943         _set_grd(stairs.front(), hatch_type);
1944         stairs.pop_front();
1945     }
1946 }
1947 
1948 /**
1949  * Ensure that there is only the correct number of each type of 'stone'
1950  * (permanent, intra-branch, non-trapdoor) stair on the current level.
1951  *
1952  * @param preserve_vault_stairs     Don't delete or trapdoorify stairs that are
1953  *                                  in vaults.
1954  * @param checking_up_stairs        Whether we're looking at stairs that lead
1955  *                                  up. If false, we're looking at down-stairs.
1956  * @return                          Whether we successfully set the right # of
1957  *                                  stairs for the level.
1958  */
_fixup_stone_stairs(bool preserve_vault_stairs,bool checking_up_stairs)1959 static bool _fixup_stone_stairs(bool preserve_vault_stairs,
1960                                 bool checking_up_stairs)
1961 {
1962     list<coord_def> stairs = _find_stone_stairs(checking_up_stairs);
1963 
1964     unsigned int needed_stairs;
1965     dungeon_feature_type base;
1966     dungeon_feature_type replace;
1967     if (checking_up_stairs)
1968     {
1969         replace = DNGN_FLOOR;
1970         base = DNGN_STONE_STAIRS_UP_I;
1971         // Pan abuses stair placement for transits, as we want connectivity
1972         // checks.
1973         needed_stairs = you.depth == 1
1974                         && !player_in_branch(BRANCH_PANDEMONIUM)
1975                         ? 1 : 3;
1976     }
1977     else
1978     {
1979         replace = DNGN_FLOOR;
1980         base = DNGN_STONE_STAIRS_DOWN_I;
1981 
1982         if (at_branch_bottom())
1983             needed_stairs = 0;
1984         else
1985             needed_stairs = 3;
1986     }
1987 
1988     // In Zot, don't create extra escape hatches, in order to force
1989     // the player through vaults that use all three down stone stairs.
1990     if (player_in_branch(BRANCH_ZOT))
1991     {
1992         replace = random_choose(DNGN_FOUNTAIN_BLUE,
1993                                 DNGN_FOUNTAIN_SPARKLING,
1994                                 DNGN_FOUNTAIN_BLOOD);
1995     }
1996 
1997     dprf(DIAG_DNGN, "Before culling: %d/%d %s stairs",
1998          (int)stairs.size(), needed_stairs, checking_up_stairs ? "up" : "down");
1999 
2000     // Find pairwise stairs that are connected and turn one of them
2001     // into an escape hatch of the appropriate type.
2002     if (stairs.size() > needed_stairs)
2003     {
2004         _cull_redundant_stairs(stairs, needed_stairs,
2005                                preserve_vault_stairs, replace);
2006     }
2007 
2008     // If that doesn't work, remove random stairs.
2009     if (stairs.size() > needed_stairs)
2010     {
2011         _cull_random_stairs(stairs, needed_stairs,
2012                             preserve_vault_stairs, replace);
2013     }
2014 
2015     // FIXME: stairs that generate inside random vaults are still
2016     // protected, resulting in superfluous ones.
2017     dprf(DIAG_DNGN, "After culling: %d/%d %s stairs",
2018          (int)stairs.size(), needed_stairs, checking_up_stairs ? "up" : "down");
2019 
2020     // XXX: this logic is exceptionally shady & should be reviewed
2021     if (stairs.size() > needed_stairs && preserve_vault_stairs
2022         && (!checking_up_stairs || you.depth != 1
2023             || !player_in_branch(root_branch)))
2024     {
2025         return false;
2026     }
2027 
2028     // If there are no stairs, it's either a branch entrance or exit.
2029     // If we somehow have ended up in a catastrophic "no stairs" state,
2030     // the level will not be validated, so we do not need to catch it here.
2031     if (stairs.size() == 0)
2032         return true;
2033 
2034     // Add extra stairs to get to exactly three.
2035     for (unsigned int s = stairs.size(); s < needed_stairs; s++)
2036     {
2037         const uint32_t mask = preserve_vault_stairs ? MMT_VAULT : 0;
2038         const coord_def gc
2039             = _dgn_random_point_in_bounds(DNGN_FLOOR, mask, DNGN_UNSEEN);
2040 
2041         if (gc.origin())
2042             return false;
2043 
2044         dprf(DIAG_DNGN, "Adding stair %d at (%d,%d)", s, gc.x, gc.y);
2045         // base gets fixed up to be the right stone stair below...
2046         _set_grd(gc, base);
2047         stairs.push_back(gc);
2048     }
2049 
2050     // If we only need one stone stair, make sure it's _I.
2051     if (needed_stairs != 3)
2052     {
2053         ASSERT(checking_up_stairs);
2054         ASSERT(needed_stairs == 1);
2055         ASSERT(stairs.size() == 1 || player_in_branch(root_branch));
2056         if (stairs.size() == 1)
2057             env.grid(stairs.front()) = DNGN_STONE_STAIRS_UP_I;
2058 
2059         return true;
2060     }
2061 
2062     // Ensure uniqueness of three stairs.
2063     ASSERT(needed_stairs == 3);
2064     for (size_t s = 0; s < needed_stairs + 1; s++)
2065     {
2066         const coord_def s1_loc = stairs.front();
2067         const coord_def s2_loc = stairs.back();
2068         if (env.grid(s1_loc) == env.grid(s2_loc))
2069         {
2070             _set_grd(s2_loc, (dungeon_feature_type)
2071                      (base + (env.grid(s2_loc)-base+1) % needed_stairs));
2072         }
2073 
2074         stairs.push_back(stairs.front());
2075         stairs.pop_front();
2076     }
2077 
2078     return true;
2079 }
2080 
_fixup_stone_stairs(bool preserve_vault_stairs)2081 static bool _fixup_stone_stairs(bool preserve_vault_stairs)
2082 {
2083     // This function ensures that there is exactly one each up and down
2084     // stone stairs I, II, and III. More than three stairs will result in
2085     // turning additional stairs into escape hatches (with an attempt to keep
2086     // level connectivity). Fewer than three stone stairs will result in
2087     // random placement of new stairs.
2088     const bool upstairs_fixed = _fixup_stone_stairs(preserve_vault_stairs,
2089                                                     true);
2090     const bool downstairs_fixed = _fixup_stone_stairs(preserve_vault_stairs,
2091                                                       false);
2092     return upstairs_fixed && downstairs_fixed;
2093 }
2094 
_add_feat_if_missing(bool (* iswanted)(const coord_def &),dungeon_feature_type feat)2095 static bool _add_feat_if_missing(bool (*iswanted)(const coord_def &),
2096                                  dungeon_feature_type feat)
2097 {
2098     memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
2099     int nzones = 0;
2100     for (int y = 0; y < GYM; ++y)
2101         for (int x = 0; x < GXM; ++x)
2102         {
2103             // [ds] Use dgn_square_is_passable instead of
2104             // dgn_square_travel_ok here, for we'll otherwise
2105             // fail on floorless isolated pocket in vaults (like the
2106             // altar surrounded by deep water), and trigger the assert
2107             // downstairs.
2108             const coord_def gc(x, y);
2109             if (!map_bounds(x, y)
2110                 || travel_point_distance[x][y] // already covered previously
2111                 || !_dgn_square_is_passable(gc))
2112             {
2113                 continue;
2114             }
2115 
2116             if (_dgn_fill_zone(gc, ++nzones, _dgn_point_record_stub,
2117                                _dgn_square_is_passable, iswanted))
2118             {
2119                 continue;
2120             }
2121 
2122             bool found_feature = false;
2123             for (rectangle_iterator ri(0); ri; ++ri)
2124             {
2125                 if (env.grid(*ri) == feat
2126                     && travel_point_distance[ri->x][ri->y] == nzones)
2127                 {
2128                     found_feature = true;
2129                     break;
2130                 }
2131             }
2132 
2133             if (found_feature)
2134                 continue;
2135 
2136             int i = 0;
2137             while (i++ < 2000)
2138             {
2139                 coord_def rnd;
2140                 rnd.x = random2(GXM);
2141                 rnd.y = random2(GYM);
2142                 if (env.grid(rnd) != DNGN_FLOOR)
2143                     continue;
2144 
2145                 if (travel_point_distance[rnd.x][rnd.y] != nzones)
2146                     continue;
2147 
2148                 _set_grd(rnd, feat);
2149                 found_feature = true;
2150                 break;
2151             }
2152 
2153             if (found_feature)
2154                 continue;
2155 
2156             for (rectangle_iterator ri(0); ri; ++ri)
2157             {
2158                 if (env.grid(*ri) != DNGN_FLOOR)
2159                     continue;
2160 
2161                 if (travel_point_distance[ri->x][ri->y] != nzones)
2162                     continue;
2163 
2164                 _set_grd(*ri, feat);
2165                 found_feature = true;
2166                 break;
2167             }
2168 
2169             if (found_feature)
2170                 continue;
2171 
2172 #ifdef DEBUG_DIAGNOSTICS
2173             dump_map("debug.map", true, true);
2174 #endif
2175             // [ds] Too many normal cases trigger this ASSERT, including
2176             // rivers that surround a stair with deep water.
2177             // die("Couldn't find region.");
2178             return false;
2179         }
2180 
2181     return true;
2182 }
2183 
_add_connecting_escape_hatches()2184 static bool _add_connecting_escape_hatches()
2185 {
2186     // For any regions without a down stone stair case, add an
2187     // escape hatch. This will always allow (downward) progress.
2188 
2189     if (branches[you.where_are_you].branch_flags & brflag::islanded)
2190         return true;
2191 
2192     // Veto D:1 or Pan if there are disconnected areas.
2193     if (player_in_branch(BRANCH_PANDEMONIUM)
2194         || (player_in_branch(BRANCH_DUNGEON) && you.depth == 1))
2195     {
2196         // Allow == 0 in case the entire level is one opaque vault.
2197         return dgn_count_disconnected_zones(false) <= 1;
2198     }
2199 
2200     if (!player_in_connected_branch())
2201         return true;
2202 
2203     if (at_branch_bottom())
2204         return dgn_count_disconnected_zones(true) == 0;
2205 
2206     if (!_add_feat_if_missing(_is_perm_down_stair, DNGN_ESCAPE_HATCH_DOWN))
2207         return false;
2208 
2209     // FIXME: shouldn't depend on branch.
2210     if (!player_in_branch(BRANCH_ORC))
2211         return true;
2212 
2213     return _add_feat_if_missing(_is_upwards_exit_stair, DNGN_ESCAPE_HATCH_UP);
2214 }
2215 
_branch_entrances_are_connected()2216 static bool _branch_entrances_are_connected()
2217 {
2218     // Returns true if all branch entrances on the level are connected to
2219     // stone stairs.
2220     for (rectangle_iterator ri(0); ri; ++ri)
2221     {
2222         if (!feat_is_branch_entrance(env.grid(*ri)))
2223             continue;
2224         if (!_has_connected_stone_stairs_from(*ri))
2225             return false;
2226     }
2227 
2228     return true;
2229 }
2230 
_branch_needs_stairs()2231 static bool _branch_needs_stairs()
2232 {
2233     // Irrelevant for branches with a single level and all encompass maps.
2234     return !player_in_branch(BRANCH_ZIGGURAT)
2235         && !player_in_branch(BRANCH_TOMB);
2236 }
2237 
_dgn_verify_connectivity(unsigned nvaults)2238 static void _dgn_verify_connectivity(unsigned nvaults)
2239 {
2240     // After placing vaults, make sure parts of the level have not been
2241     // disconnected.
2242     if (dgn_zones && nvaults != env.level_vaults.size())
2243     {
2244         if (!player_in_branch(BRANCH_ABYSS))
2245             _fill_small_disconnected_zones();
2246 
2247         const int newzones = dgn_count_disconnected_zones(false);
2248 
2249 #ifdef DEBUG_STATISTICS
2250         ostringstream vlist;
2251         for (unsigned i = nvaults; i < env.level_vaults.size(); ++i)
2252         {
2253             if (i > nvaults)
2254                 vlist << ", ";
2255             vlist << env.level_vaults[i]->map.name;
2256         }
2257         dprf(DIAG_DNGN, "Dungeon has %d zones after placing %s.",
2258              newzones, vlist.str().c_str());
2259 #endif
2260         if (newzones > dgn_zones)
2261         {
2262             throw dgn_veto_exception(make_stringf(
2263                  "Had %d zones, now has %d%s%s.", dgn_zones, newzones,
2264 #ifdef DEBUG_STATISTICS
2265                  "; broken by ", vlist.str().c_str()
2266 #else
2267                  "", ""
2268 #endif
2269             ));
2270         }
2271     }
2272 
2273     // Also check for isolated regions that have no stairs.
2274     if (player_in_connected_branch()
2275         && !(branches[you.where_are_you].branch_flags & brflag::islanded)
2276         && dgn_count_disconnected_zones(true) > 0)
2277     {
2278         throw dgn_veto_exception("Isolated areas with no stairs.");
2279     }
2280 
2281     if (_branch_needs_stairs() && !_fixup_stone_stairs(true))
2282     {
2283         dprf(DIAG_DNGN, "Warning: failed to preserve vault stairs.");
2284         if (!_fixup_stone_stairs(false))
2285             throw dgn_veto_exception("Failed to fix stone stairs.");
2286     }
2287 
2288     if (!_branch_entrances_are_connected())
2289         throw dgn_veto_exception("A disconnected branch entrance.");
2290 
2291     if (!_add_connecting_escape_hatches())
2292         throw dgn_veto_exception("Failed to add connecting escape hatches.");
2293 
2294     // XXX: Interlevel connectivity fixup relies on being the last
2295     //      point at which a level may be vetoed.
2296     if (!_fixup_interlevel_connectivity())
2297         throw dgn_veto_exception("Failed to ensure interlevel connectivity.");
2298 }
2299 
2300 // Structure of OVERFLOW_TEMPLES:
2301 //
2302 // * A vector, with one cell per dungeon level (unset if there's no
2303 //   overflow temples on that level).
2304 //
2305 // * The cell of the previous vector is a vector, with one overflow
2306 //   temple definition per cell.
2307 //
2308 // * The cell of the previous vector is a hash table, containing the
2309 //   list of gods for the overflow temple and (optionally) the name of
2310 //   the vault to use for the temple. If no map name is supplied,
2311 //   it will randomly pick from vaults tagged "temple_overflow_num",
2312 //   where "num" is the number of gods in the temple. Gods are listed
2313 //   in the order their altars are placed.
_build_overflow_temples()2314 static void _build_overflow_temples()
2315 {
2316     // Levels built while in testing mode.
2317     if (!you.props.exists(OVERFLOW_TEMPLES_KEY))
2318         return;
2319 
2320     CrawlVector &levels = you.props[OVERFLOW_TEMPLES_KEY].get_vector();
2321 
2322     // Are we deeper than the last overflow temple?
2323     if (you.depth >= levels.size() + 1)
2324         return;
2325 
2326     CrawlStoreValue &val = levels[you.depth - 1];
2327 
2328     // Does this level have an overflow temple?
2329     if (val.get_flags() & SFLAG_UNSET)
2330         return;
2331 
2332     CrawlVector &temples = val.get_vector();
2333 
2334     if (temples.empty())
2335         return;
2336 
2337     for (unsigned int i = 0; i < temples.size(); i++)
2338     {
2339         CrawlHashTable &temple = temples[i].get_table();
2340 
2341         const int num_gods = _setup_temple_altars(temple);
2342 
2343         const map_def *vault = nullptr;
2344         string vault_tag = "";
2345         string name = "";
2346 
2347         if (temple.exists(TEMPLE_MAP_KEY))
2348         {
2349             name = temple[TEMPLE_MAP_KEY].get_string();
2350 
2351             vault = find_map_by_name(name);
2352             if (vault == nullptr)
2353             {
2354                 mprf(MSGCH_ERROR,
2355                      "Couldn't find overflow temple map '%s'!",
2356                      name.c_str());
2357             }
2358         }
2359         else
2360         {
2361             // First try to find a temple specialized for this combination of
2362             // gods.
2363             if (num_gods > 1 || coinflip())
2364             {
2365                 vault_tag = make_stringf("temple_overflow_%d", num_gods);
2366 
2367                 CrawlVector &god_vec = temple[TEMPLE_GODS_KEY];
2368 
2369                 for (int j = 0; j < num_gods; j++)
2370                 {
2371                     god_type god = (god_type) god_vec[j].get_byte();
2372 
2373                     name = god_name(god);
2374                     name = replace_all(name, " ", "_");
2375                     lowercase(name);
2376 
2377                     vault_tag = vault_tag + " temple_overflow_" + name;
2378                 }
2379 
2380                 if (num_gods == 1
2381                     && get_uniq_map_tags().find("uniq_altar_" + name)
2382                        != get_uniq_map_tags().end())
2383                 {
2384                     // We've already placed a specialized temple for this
2385                     // god, so do nothing.
2386 #ifdef DEBUG_TEMPLES
2387                     mprf(MSGCH_DIAGNOSTICS, "Already placed specialized "
2388                          "single-altar temple for %s", name.c_str());
2389 #endif
2390                     continue;
2391                 }
2392 
2393                 vault = random_map_for_tag(vault_tag, true);
2394 #ifdef DEBUG_TEMPLES
2395                 if (vault == nullptr)
2396                 {
2397                     mprf(MSGCH_DIAGNOSTICS, "Couldn't find overflow temple "
2398                          "for combination of tags %s", vault_tag.c_str());
2399                 }
2400 #endif
2401             }
2402 
2403             if (vault == nullptr)
2404             {
2405                 vault_tag = make_stringf("temple_overflow_generic_%d",
2406                                          num_gods);
2407 
2408                 vault = random_map_for_tag(vault_tag, true);
2409                 if (vault == nullptr)
2410                 {
2411                     mprf(MSGCH_ERROR,
2412                          "Couldn't find overflow temple tag '%s'!",
2413                          vault_tag.c_str());
2414                 }
2415             }
2416         }
2417 
2418         if (vault == nullptr)
2419             // Might as well build the rest of the level if we couldn't
2420             // find the overflow temple map, so don't veto the level.
2421             return;
2422 
2423         {
2424             dgn_map_parameters mp(vault_tag);
2425             if (!_dgn_ensure_vault_placed(
2426                     _build_secondary_vault(vault),
2427                     false, vault->name))
2428             {
2429 #ifdef DEBUG_TEMPLES
2430                 mprf(MSGCH_DIAGNOSTICS, "Couldn't place overflow temple '%s', "
2431                      "vetoing level.", vault->name.c_str());
2432 #endif
2433                 return;
2434             }
2435         }
2436 #ifdef DEBUG_TEMPLES
2437         mprf(MSGCH_DIAGNOSTICS, "Placed overflow temple %s",
2438              vault->name.c_str());
2439 #endif
2440     }
2441     _current_temple_hash = nullptr; // XXX: hack!
2442 }
2443 
2444 struct coord_feat
2445 {
2446     coord_def pos;
2447     dungeon_feature_type feat;
2448     terrain_property_t prop;
2449     unsigned int mask;
2450 
coord_featcoord_feat2451     coord_feat(const coord_def &c, dungeon_feature_type f)
2452         : pos(c), feat(f), prop(), mask(0)
2453     {
2454     }
2455 
set_fromcoord_feat2456     void set_from(const coord_def &c)
2457     {
2458         feat = env.grid(c);
2459         // Don't copy mimic-ness.
2460         mask = env.level_map_mask(c) & ~(MMT_MIMIC);
2461         // Only copy "static" properties.
2462         prop = env.pgrid(c) & (FPROP_NO_CLOUD_GEN | FPROP_NO_TELE_INTO
2463                                | FPROP_NO_TIDE);
2464     }
2465 };
2466 
2467 template <typename Iterator>
_ruin_level(Iterator iter,unsigned vault_mask=MMT_VAULT,int ruination=10,int plant_density=5,int iterations=1)2468 static void _ruin_level(Iterator iter,
2469                         unsigned vault_mask = MMT_VAULT,
2470                         int ruination = 10,
2471                         int plant_density = 5,
2472                         int iterations = 1)
2473 {
2474     vector<coord_def> replaced;
2475 
2476     for (int i = 0; i < iterations; ++i)
2477     {
2478         typedef vector<coord_feat> coord_feats;
2479         coord_feats to_replace;
2480         for (Iterator ri = iter; ri; ++ri)
2481         {
2482             // don't alter map boundary
2483             if (!in_bounds(*ri))
2484                 continue;
2485 
2486             // only try to replace wall and door tiles
2487             if (!feat_is_wall(env.grid(*ri)) && !feat_is_door(env.grid(*ri)))
2488                 continue;
2489 
2490             // don't mess with permarock
2491             if (env.grid(*ri) == DNGN_PERMAROCK_WALL)
2492                 continue;
2493 
2494             // or vaults
2495             if (map_masked(*ri, vault_mask))
2496                 continue;
2497 
2498             // Pick a random adjacent non-wall, non-door, non-statue
2499             // feature, and count the number of such features.
2500             coord_feat replacement(*ri, DNGN_UNSEEN);
2501             int floor_count = 0;
2502             for (adjacent_iterator ai(*ri); ai; ++ai)
2503             {
2504                 if (!feat_is_wall(env.grid(*ai)) && !feat_is_door(env.grid(*ai))
2505                     && !feat_is_statuelike(env.grid(*ai))
2506                     // Shouldn't happen, but just in case.
2507                     && env.grid(*ai) != DNGN_MALIGN_GATEWAY)
2508                 {
2509                     if (one_chance_in(++floor_count))
2510                         replacement.set_from(*ai);
2511                 }
2512             }
2513 
2514             // chance of removing the tile is dependent on the number of adjacent
2515             // floor(ish) tiles
2516             if (x_chance_in_y(floor_count, ruination))
2517                 to_replace.push_back(replacement);
2518         }
2519 
2520         for (const auto &cfeat : to_replace)
2521         {
2522             const coord_def &p(cfeat.pos);
2523             replaced.push_back(p);
2524             dungeon_feature_type replacement = cfeat.feat;
2525             ASSERT(replacement != DNGN_UNSEEN);
2526 
2527             // Don't replace doors with impassable features.
2528             if (feat_is_door(env.grid(p)))
2529             {
2530                 if (feat_is_water(replacement))
2531                     replacement = DNGN_SHALLOW_WATER;
2532                 else
2533                     replacement = DNGN_FLOOR;
2534             }
2535             else if (feat_has_solid_floor(replacement)
2536                     && replacement != DNGN_SHALLOW_WATER)
2537             {
2538                 // Exclude traps, shops, stairs, portals, altars, fountains.
2539                 // The first four, especially, are a big deal.
2540                 replacement = DNGN_FLOOR;
2541             }
2542 
2543             // only remove some doors, to preserve tactical options
2544             if (feat_is_wall(env.grid(p)) || coinflip() && feat_is_door(env.grid(p)))
2545             {
2546                 // Copy the mask and properties too, so that we don't make an
2547                 // isolated transparent or rtele_into square.
2548                 env.level_map_mask(p) |= cfeat.mask;
2549                 env.pgrid(p) |= cfeat.prop;
2550                 _set_grd(p, replacement);
2551             }
2552 
2553             // but remove doors if we've removed all adjacent walls
2554             for (adjacent_iterator wai(p); wai; ++wai)
2555             {
2556                 if (feat_is_door(env.grid(*wai)))
2557                 {
2558                     bool remove = true;
2559                     for (adjacent_iterator dai(*wai); dai; ++dai)
2560                     {
2561                         if (feat_is_wall(env.grid(*dai)))
2562                             remove = false;
2563                     }
2564                     // It's always safe to replace a door with floor.
2565                     if (remove)
2566                     {
2567                         env.level_map_mask(p) |= cfeat.mask;
2568                         env.pgrid(p) |= cfeat.prop;
2569                         _set_grd(*wai, DNGN_FLOOR);
2570                     }
2571                 }
2572             }
2573         }
2574     }
2575 
2576     for (const auto &p : replaced)
2577     {
2578         // replace some ruined walls with plants/fungi/bushes
2579         if (plant_density && one_chance_in(plant_density)
2580             && feat_has_solid_floor(env.grid(p))
2581             && !plant_forbidden_at(p))
2582         {
2583             mgen_data mg;
2584             mg.cls = random_choose_weighted( 2, MONS_BUSH,
2585                                             19, MONS_PLANT,
2586                                             19, MONS_FUNGUS);
2587             mg.pos = p;
2588             mg.flags = MG_FORCE_PLACE;
2589             mons_place(mgen_data(mg));
2590         }
2591     }
2592 }
2593 
_mimic_at_level()2594 static bool _mimic_at_level()
2595 {
2596     return !player_in_branch(BRANCH_TEMPLE)
2597            && !player_in_branch(BRANCH_VESTIBULE)
2598            && !player_in_branch(BRANCH_SLIME)
2599            && !player_in_branch(BRANCH_TOMB)
2600            && !player_in_branch(BRANCH_PANDEMONIUM)
2601            && !player_in_hell()
2602            && !crawl_state.game_is_tutorial();
2603 }
2604 
_place_feature_mimics()2605 static void _place_feature_mimics()
2606 {
2607     for (rectangle_iterator ri(1); ri; ++ri)
2608     {
2609         const coord_def pos = *ri;
2610         const dungeon_feature_type feat = env.grid(pos);
2611 
2612         // Vault tag prevents mimic.
2613         if (map_masked(pos, MMT_NO_MIMIC))
2614             continue;
2615 
2616         // Only features valid for mimicing.
2617         if (!feat_is_mimicable(feat))
2618             continue;
2619 
2620         if (one_chance_in(FEATURE_MIMIC_CHANCE))
2621         {
2622             dprf(DIAG_DNGN, "Placed %s mimic at (%d,%d).",
2623                  feat_type_name(feat), ri->x, ri->y);
2624             env.level_map_mask(*ri) |= MMT_MIMIC;
2625 
2626             // If we're mimicing a unique portal vault, give a chance for
2627             // another one to spawn.
2628             const char* dst = branches[stair_destination(pos).branch].abbrevname;
2629             const string tag = "uniq_" + lowercase_string(dst);
2630             if (get_uniq_map_tags().count(tag))
2631                 get_uniq_map_tags().erase(tag);
2632         }
2633     }
2634 }
2635 
2636 // Apply modifications (ruination, plant clumps) that should happen
2637 // regardless of game mode.
_post_vault_build()2638 static void _post_vault_build()
2639 {
2640     if (player_in_branch(BRANCH_LAIR))
2641     {
2642         int depth = you.depth + 1;
2643         _ruin_level(rectangle_iterator(1), MMT_VAULT,
2644                     20 - depth, depth / 2 + 4, 1 + (depth / 3));
2645         do
2646         {
2647             _add_plant_clumps(12 - depth, 18 - depth / 4, depth / 4 + 2);
2648             depth -= 3;
2649         } while (depth > 0);
2650     }
2651 }
2652 
_build_dungeon_level()2653 static void _build_dungeon_level()
2654 {
2655     bool place_vaults = _builder_by_type();
2656 
2657     if (player_in_branch(BRANCH_SLIME))
2658         _slime_connectivity_fixup();
2659 
2660     // Now place items, mons, gates, etc.
2661     // Stairs must exist by this point (except in Shoals where they are
2662     // yet to be placed). Some items and monsters already exist.
2663 
2664     _check_doors();
2665 
2666     const unsigned nvaults = env.level_vaults.size();
2667 
2668     // Any further vaults must make sure not to disrupt level layout.
2669     dgn_check_connectivity = true;
2670 
2671     if (player_in_branch(BRANCH_DUNGEON)
2672         && !crawl_state.game_is_tutorial())
2673     {
2674         _build_overflow_temples();
2675     }
2676 
2677     // Try to place minivaults that really badly want to be placed. Still
2678     // no guarantees, seeing this is a minivault.
2679     if (crawl_state.game_standard_levelgen())
2680     {
2681         if (place_vaults)
2682         {
2683             // Moved branch entries to place first so there's a good
2684             // chance of having room for a vault
2685             _place_branch_entrances(true);
2686             _place_chance_vaults();
2687             _place_minivaults();
2688             _place_extra_vaults();
2689         }
2690         else
2691         {
2692             // Place any branch entries vaultlessly
2693             _place_branch_entrances(false);
2694             // Still place chance vaults - important things like Abyss,
2695             // Hell, Pan entries are placed this way
2696             _place_chance_vaults();
2697         }
2698 
2699         // Ruination and plant clumps.
2700         _post_vault_build();
2701 
2702         // XXX: Moved this here from builder_monsters so that
2703         //      connectivity can be ensured
2704         _place_uniques();
2705 
2706         if (_mimic_at_level())
2707             _place_feature_mimics();
2708 
2709         _place_traps();
2710 
2711         // Any vault-placement activity must happen before this check.
2712         _dgn_verify_connectivity(nvaults);
2713 
2714         _builder_monsters();
2715 
2716         // Place items.
2717         _builder_items();
2718 
2719         _fixup_walls();
2720     }
2721     else
2722     {
2723         // Do ruination and plant clumps even in funny game modes, if
2724         // they happen to have the relevant branch.
2725         _post_vault_build();
2726     }
2727 
2728     // Translate stairs for pandemonium levels.
2729     if (player_in_branch(BRANCH_PANDEMONIUM))
2730         _fixup_pandemonium_stairs();
2731 
2732     _fixup_branch_stairs();
2733 
2734     if (!dgn_make_transporters_from_markers())
2735         throw dgn_veto_exception("Transporter placement failed.");
2736 
2737     fixup_misplaced_items();
2738     link_items();
2739 
2740     if (!player_in_branch(BRANCH_COCYTUS)
2741         && !player_in_branch(BRANCH_SWAMP)
2742         && !player_in_branch(BRANCH_SHOALS))
2743     {
2744         _prepare_water();
2745     }
2746 
2747     if (player_in_hell())
2748         _fixup_hell_stairs();
2749 }
2750 
_dgn_set_floor_colours()2751 static void _dgn_set_floor_colours()
2752 {
2753     colour_t old_floor_colour = env.floor_colour;
2754     colour_t old_rock_colour  = env.rock_colour;
2755 
2756     const int youbranch = you.where_are_you;
2757     env.floor_colour    = branches[youbranch].floor_colour;
2758     env.rock_colour     = branches[youbranch].rock_colour;
2759 
2760     if (old_floor_colour != BLACK)
2761         env.floor_colour = old_floor_colour;
2762     if (old_rock_colour != BLACK)
2763         env.rock_colour = old_rock_colour;
2764 
2765     if (env.floor_colour == BLACK)
2766         env.floor_colour = LIGHTGREY;
2767     if (env.rock_colour == BLACK)
2768         env.rock_colour  = BROWN;
2769 }
2770 
_check_doors()2771 static void _check_doors()
2772 {
2773     for (rectangle_iterator ri(1); ri; ++ri)
2774     {
2775         if (!feat_is_closed_door(env.grid(*ri)))
2776             continue;
2777 
2778         int solid_count = 0;
2779 
2780         for (orth_adjacent_iterator rai(*ri); rai; ++rai)
2781             if (cell_is_solid(*rai))
2782                 solid_count++;
2783 
2784         if (solid_count < 2)
2785             _set_grd(*ri, DNGN_FLOOR);
2786     }
2787 }
2788 
count_feature_in_box(int x0,int y0,int x1,int y1,dungeon_feature_type feat)2789 int count_feature_in_box(int x0, int y0, int x1, int y1,
2790                          dungeon_feature_type feat)
2791 {
2792     int result = 0;
2793     for (int i = x0; i < x1; ++i)
2794         for (int j = y0; j < y1; ++j)
2795         {
2796             if (env.grid[i][j] == feat)
2797                 ++result;
2798         }
2799 
2800     return result;
2801 }
2802 
2803 // Count how many neighbours of env.grid[x][y] are the feature feat.
count_neighbours(int x,int y,dungeon_feature_type feat)2804 int count_neighbours(int x, int y, dungeon_feature_type feat)
2805 {
2806     return count_feature_in_box(x-1, y-1, x+2, y+2, feat);
2807 }
2808 
2809 // Gives water which is next to ground/shallow water a chance of being
2810 // shallow. Checks each water space.
_prepare_water()2811 static void _prepare_water()
2812 {
2813     for (rectangle_iterator ri(1); ri; ++ri)
2814     {
2815         if (map_masked(*ri, MMT_NO_POOL) || env.grid(*ri) != DNGN_DEEP_WATER)
2816             continue;
2817 
2818         for (adjacent_iterator ai(*ri); ai; ++ai)
2819         {
2820             const dungeon_feature_type which_grid = env.grid(*ai);
2821 
2822             if (which_grid == DNGN_SHALLOW_WATER && one_chance_in(20)
2823                 || feat_has_dry_floor(which_grid) && x_chance_in_y(2, 5))
2824             {
2825                 _set_grd(*ri, DNGN_SHALLOW_WATER);
2826                 break;
2827             }
2828         }
2829     }
2830 }
2831 
_vault_can_use_layout(const map_def * vault,const map_def * layout)2832 static bool _vault_can_use_layout(const map_def *vault, const map_def *layout)
2833 {
2834     bool permissive = false;
2835     if (!vault->has_tag_prefix("layout_")
2836         && !(permissive = vault->has_tag_prefix("nolayout_")))
2837     {
2838         return true;
2839     }
2840 
2841     ASSERT(layout->has_tag_prefix("layout_type_"));
2842 
2843     // in principle, tag order can matter here, even though that is probably
2844     // a vault designer error
2845     for (const auto &tag : layout->get_tags())
2846     {
2847         if (starts_with(tag, "layout_type_"))
2848         {
2849             string type = tag_without_prefix(tag, "layout_type_");
2850             if (vault->has_tag("layout_" + type))
2851                 return true;
2852             else if (vault->has_tag("nolayout_" + type))
2853                 return false;
2854         }
2855     }
2856 
2857     return permissive;
2858 }
2859 
_pick_layout(const map_def * vault)2860 static const map_def *_pick_layout(const map_def *vault)
2861 {
2862     const map_def *layout = nullptr;
2863 
2864     // This is intended for use with primary vaults, so...
2865     ASSERT(vault);
2866 
2867     // For centred maps, try to pick a central-type layout first.
2868     if (vault->orient == MAP_CENTRE)
2869         layout = random_map_for_tag("central", true, true);
2870 
2871     int tries = 100;
2872 
2873     if (!layout)
2874     {
2875         do
2876         {
2877             if (!tries--)
2878             {
2879                 die("Couldn't find a layout for %s",
2880                     level_id::current().describe().c_str());
2881             }
2882             layout = random_map_for_tag("layout", true, true);
2883         }
2884         while (layout->has_tag("no_primary_vault")
2885                || (tries > 10 && !_vault_can_use_layout(vault, layout)));
2886     }
2887 
2888     return layout;
2889 }
2890 
_pan_level()2891 static bool _pan_level()
2892 {
2893     const char *pandemon_level_names[] =
2894         { "mnoleg", "lom_lobon", "cerebov", "gloorx_vloq", };
2895     int which_demon = -1;
2896     const PlaceInfo &place_info = you.get_place_info();
2897     bool all_demons_generated = true;
2898 
2899     if (you.props.exists("force_map"))
2900     {
2901         const map_def *vault =
2902             find_map_by_name(you.props["force_map"].get_string());
2903         ASSERT(vault);
2904 
2905         _dgn_ensure_vault_placed(_build_primary_vault(vault), false, vault->name);
2906         return vault->orient != MAP_ENCOMPASS;
2907     }
2908 
2909     for (int i = 0; i < 4; i++)
2910     {
2911         if (!get_uniq_map_tags().count(string("uniq_") + pandemon_level_names[i]))
2912         {
2913             all_demons_generated = false;
2914             break;
2915         }
2916     }
2917 
2918     // Unique pan lords become more common as you travel through pandemonium.
2919     // On average it takes about 14 levels to see all four, and on average
2920     // about 5 levels to see your first.
2921     if (x_chance_in_y(1 + place_info.levels_seen, 20 + place_info.levels_seen)
2922         && !all_demons_generated)
2923     {
2924         do
2925         {
2926             which_demon = random2(4);
2927         }
2928         while (get_uniq_map_tags().count(string("uniq_")
2929                                        + pandemon_level_names[which_demon]));
2930     }
2931 
2932     const map_def *vault = nullptr;
2933 
2934     if (which_demon >= 0)
2935     {
2936         vault = random_map_for_tag(pandemon_level_names[which_demon], false,
2937                                    false, MB_FALSE);
2938     }
2939     else
2940         vault = random_map_in_depth(level_id::current(), false, MB_FALSE);
2941 
2942     // Every Pan level should have a primary vault.
2943     ASSERT(vault);
2944     _dgn_ensure_vault_placed(_build_primary_vault(vault), false, vault->name);
2945     return vault->orient != MAP_ENCOMPASS;
2946 }
2947 
2948 // Returns true if we want the dungeon builder
2949 // to place more vaults after this
_builder_by_type()2950 static bool _builder_by_type()
2951 {
2952     if (player_in_branch(BRANCH_ABYSS))
2953     {
2954         generate_abyss();
2955         // Should place some vaults in abyss because
2956         // there's never an encompass vault
2957         return true;
2958     }
2959     else if (player_in_branch(BRANCH_PANDEMONIUM))
2960     {
2961         // Generate a random monster table for Pan.
2962         init_pandemonium();
2963         setup_vault_mon_list();
2964         return _pan_level();
2965     }
2966     else
2967         return _builder_normal();
2968 }
2969 
_dgn_random_map_for_place(bool minivault)2970 static const map_def *_dgn_random_map_for_place(bool minivault)
2971 {
2972     if (!minivault && player_in_branch(BRANCH_TEMPLE))
2973     {
2974         // Temple vault determined at new game time.
2975         const string name = you.props[TEMPLE_MAP_KEY];
2976 
2977         // Tolerate this for a little while, for old games.
2978         if (!name.empty())
2979         {
2980             const map_def *vault = find_map_by_name(name);
2981 
2982             if (vault)
2983                 return vault;
2984 
2985             mprf(MSGCH_ERROR, "Unable to find Temple vault '%s'",
2986                  name.c_str());
2987 
2988             // Fall through and use a different Temple map instead.
2989         }
2990     }
2991 
2992     const level_id lid = level_id::current();
2993 
2994 #if TAG_MAJOR_VERSION == 34
2995     if (!minivault
2996         && player_in_branch(BRANCH_TOMB)
2997         && you.props[TOMB_STONE_STAIRS_KEY])
2998     {
2999         const map_def *vault = random_map_for_tag("tomb_stone_stairs", true);
3000 
3001         if (vault)
3002             return vault;
3003 
3004         end(1, false, "Couldn't find map with tag tomb_stone_stairs for level "
3005             "%s.", lid.describe().c_str());
3006     }
3007 #endif
3008 
3009     const map_def *vault = 0;
3010 
3011     if (you.props.exists("force_map"))
3012         vault = find_map_by_name(you.props["force_map"].get_string());
3013     else if (lid.branch == root_branch && lid.depth == 1
3014         && (crawl_state.game_is_sprint()
3015             || crawl_state.game_is_tutorial()))
3016     {
3017         vault = find_map_by_name(crawl_state.map);
3018         if (vault == nullptr)
3019         {
3020             end(1, false, "Couldn't find selected map '%s'.",
3021                 crawl_state.map.c_str());
3022         }
3023     }
3024 
3025     if (!vault)
3026         // Pick a normal map
3027         vault = random_map_for_place(lid, minivault, MB_FALSE);
3028 
3029     if (!vault && lid.branch == root_branch && lid.depth == 1)
3030         vault = random_map_for_tag("arrival", false, false, MB_FALSE);
3031 
3032     return vault;
3033 }
3034 
_setup_temple_altars(CrawlHashTable & temple)3035 static int _setup_temple_altars(CrawlHashTable &temple)
3036 {
3037     _current_temple_hash = &temple; // XXX: hack!
3038 
3039     CrawlVector god_list = temple[TEMPLE_GODS_KEY].get_vector();
3040 
3041     _temple_altar_list.clear();
3042 
3043     for (unsigned int i = 0; i < god_list.size(); i++)
3044         _temple_altar_list.push_back((god_type) god_list[i].get_byte());
3045 
3046     return (int)god_list.size();
3047 }
3048 
3049 struct map_component
3050 {
3051     int label;
3052 
map_componentmap_component3053     map_component()
3054     {
3055         min_equivalent = nullptr;
3056     }
3057     map_component * min_equivalent;
3058 
3059     coord_def min_coord;
3060     coord_def max_coord;
3061 
3062     coord_def seed_position;
3063 
start_componentmap_component3064     void start_component(const coord_def & pos, int in_label)
3065     {
3066         seed_position = pos;
3067         min_coord = pos;
3068         max_coord = pos;
3069 
3070         label = in_label;
3071         min_equivalent = nullptr;
3072     }
3073 
add_coordmap_component3074     void add_coord(const coord_def & pos)
3075     {
3076         if (pos.x < min_coord.x)
3077             min_coord.x = pos.x;
3078         if (pos.x > max_coord.x)
3079             max_coord.x = pos.x;
3080         if (pos.y < min_coord.y)
3081             min_coord.y = pos.y;
3082         if (pos.y > max_coord.y)
3083             max_coord.y = pos.y;
3084     }
3085 
merge_regionmap_component3086     void merge_region(const map_component & existing)
3087     {
3088         add_coord(existing.min_coord);
3089         add_coord(existing.max_coord);
3090     }
3091 };
3092 
_min_transitive_label(map_component & component)3093 static int _min_transitive_label(map_component & component)
3094 {
3095     map_component * current = &component;
3096 
3097     int label;
3098     do
3099     {
3100         label = current->label;
3101 
3102         current = current->min_equivalent;
3103     } while (current);
3104 
3105     return label;
3106 }
3107 
3108 // 8-way connected component analysis on the current level map.
3109 template<typename comp>
_ccomps_8(FixedArray<int,GXM,GYM> & connectivity_map,vector<map_component> & components,comp & connected)3110 static void _ccomps_8(FixedArray<int, GXM, GYM > & connectivity_map,
3111                       vector<map_component> & components, comp & connected)
3112 {
3113     map<int, map_component> intermediate_components;
3114 
3115     connectivity_map.init(0);
3116     components.clear();
3117 
3118     unsigned adjacent_size = 4;
3119     coord_def offsets[4] = {coord_def(-1, 0), coord_def(-1, -1), coord_def(0, -1), coord_def(1, -1)};
3120 
3121     int next_label = 1;
3122 
3123     // Pass 1, for each point, check the upper/left adjacent squares for labels
3124     // if a labels are found, use the min connected label, else assign a new
3125     // label and start a new component
3126     for (rectangle_iterator pos(1); pos; ++pos)
3127     {
3128         if (connected(*pos))
3129         {
3130             int absolute_min_label = INT_MAX;
3131             set<int> neighbor_labels;
3132             for (unsigned i = 0; i < adjacent_size; i++)
3133             {
3134                 coord_def test = *pos + offsets[i];
3135                 if (in_bounds(test) && connectivity_map(test) != 0)
3136                 {
3137                     int neighbor_label = connectivity_map(test);
3138                     if (neighbor_labels.insert(neighbor_label).second)
3139                     {
3140                         int trans = _min_transitive_label(intermediate_components[neighbor_label]);
3141 
3142                         if (trans < absolute_min_label)
3143                             absolute_min_label = trans;
3144                     }
3145                 }
3146             }
3147 
3148             int label;
3149             if (neighbor_labels.empty())
3150             {
3151                 intermediate_components[next_label].start_component(*pos, next_label);
3152                 label = next_label;
3153                 next_label++;
3154             }
3155             else
3156             {
3157                 label = absolute_min_label;
3158                 map_component * absolute_min = &intermediate_components[absolute_min_label];
3159 
3160                 absolute_min->add_coord(*pos);
3161                 for (auto i : neighbor_labels)
3162                 {
3163                     map_component * current = &intermediate_components[i];
3164 
3165                     while (current && current != absolute_min)
3166                     {
3167                         absolute_min->merge_region(*current);
3168                         map_component * next = current->min_equivalent;
3169                         current->min_equivalent = absolute_min;
3170                         current = next;
3171                     }
3172                 }
3173             }
3174             connectivity_map(*pos) = label;
3175         }
3176     }
3177 
3178     int reindexed_label = 1;
3179     // Reindex root labels, and move them to output
3180     for (auto &entry : intermediate_components)
3181     {
3182         if (entry.second.min_equivalent == nullptr)
3183         {
3184             entry.second.label = reindexed_label++;
3185             components.push_back(entry.second);
3186         }
3187     }
3188 
3189     // Pass 2, mark new labels on the grid
3190     for (rectangle_iterator pos(1); pos; ++pos)
3191     {
3192         int label = connectivity_map(*pos);
3193         if (label  != 0)
3194             connectivity_map(*pos) = _min_transitive_label(intermediate_components[label]);
3195     }
3196 }
3197 
3198 // Is this square a wall, or does it belong to a vault? both are considered to
3199 // block connectivity.
_passable_square(const coord_def & pos)3200 static bool _passable_square(const coord_def & pos)
3201 {
3202     return !feat_is_wall(env.grid(pos)) && !(env.level_map_mask(pos) & MMT_VAULT);
3203 }
3204 
3205 struct adjacency_test
3206 {
adjacency_testadjacency_test3207     adjacency_test()
3208     {
3209         adjacency.init(0);
3210     }
3211     FixedArray<int, GXM, GYM> adjacency;
operator ()adjacency_test3212     bool operator()(const coord_def & pos)
3213     {
3214         return _passable_square(pos) && adjacency(pos) == 0;
3215     }
3216 };
3217 
3218 struct dummy_estimate
3219 {
operator ()dummy_estimate3220     bool operator() (const coord_def &)
3221     {
3222         return 0;
3223     }
3224 };
3225 
3226 struct adjacent_costs
3227 {
3228     FixedArray<int, GXM, GYM> * adjacency;
operator ()adjacent_costs3229     int operator()(const coord_def & pos)
3230     {
3231         return (*adjacency)(pos);
3232     }
3233 };
3234 
3235 struct label_match
3236 {
3237     FixedArray<int, GXM, GYM> * labels;
3238     int target_label;
operator ()label_match3239     bool operator()(const coord_def & pos)
3240     {
3241         return (*labels)(pos) == target_label;
3242     }
3243 };
3244 
3245 // Connectivity checks to make sure that the parts of a bubble are not
3246 // obstructed by slime wall adjacent squares
_slime_connectivity_fixup()3247 static void _slime_connectivity_fixup()
3248 {
3249     // Generate a connectivity map considering any non wall, non vault square
3250     // passable
3251     FixedArray<int, GXM, GYM> connectivity_map;
3252     vector<map_component> components;
3253     _ccomps_8(connectivity_map, components, _passable_square);
3254 
3255     // Next we will generate a connectivity map with the above restrictions,
3256     // and also considering wall adjacent squares unpassable. But first we
3257     // build a map of how many walls are adjacent to each square in the level.
3258     // Walls/vault squares are flagged with DISCONNECT_DIST in this map.
3259     // This will be used to build the connectivity map, then later the adjacent
3260     // counts will define the costs of a search used to connect components in
3261     // the basic connectivity map that are broken apart in the restricted map
3262     FixedArray<int, GXM, GYM> non_adjacent_connectivity;
3263     vector<map_component> non_adj_components;
3264     adjacency_test adjacent_check;
3265 
3266     for (rectangle_iterator ri(1); ri; ++ri)
3267     {
3268         int count = 0;
3269         if (!_passable_square(*ri))
3270             count = DISCONNECT_DIST;
3271         else
3272         {
3273             for (adjacent_iterator adj(*ri); adj; ++adj)
3274             {
3275                 if (feat_is_wall(env.grid(*adj)))
3276                 {
3277                     // Not allowed to damage vault squares, so mark them
3278                     // inaccessible
3279                     if (env.level_map_mask(*adj) & MMT_VAULT)
3280                     {
3281                         count = DISCONNECT_DIST;
3282                         break;
3283                     }
3284                     else
3285                         count++;
3286                 }
3287 
3288             }
3289         }
3290         adjacent_check.adjacency(*ri) = count;
3291     }
3292 
3293     _ccomps_8(non_adjacent_connectivity, non_adj_components, adjacent_check);
3294 
3295     // Now that we have both connectivity maps, go over each component in the
3296     // unrestricted map and connect any separate components in the restricted
3297     // map that it was broken up into.
3298     for (map_component &comp : components)
3299     {
3300         // Collect the components in the restricted connectivity map that
3301         // occupy part of the current component
3302         map<int, map_component *> present;
3303         for (rectangle_iterator ri(comp.min_coord, comp.max_coord); ri; ++ri)
3304         {
3305             int new_label = non_adjacent_connectivity(*ri);
3306             if (comp.label == connectivity_map(*ri) && new_label != 0)
3307             {
3308                 // the bit with new_label - 1 is foolish.
3309                 present[new_label] = &non_adj_components[new_label-1];
3310             }
3311         }
3312 
3313         // Set one restricted component as the base point, and search to all
3314         // other restricted components
3315         auto target_components = present.begin();
3316 
3317         // No non-wall adjacent squares in this component? This probably
3318         // shouldn't happen, but just move on.
3319         if (target_components == present.end())
3320             continue;
3321 
3322         map_component * base_component = target_components->second;
3323         ++target_components;
3324 
3325         adjacent_costs connection_costs;
3326         connection_costs.adjacency = &adjacent_check.adjacency;
3327 
3328         label_match valid_label;
3329         valid_label.labels = &non_adjacent_connectivity;
3330 
3331         dummy_estimate dummy;
3332 
3333         // Now search from our base component to the other components, and
3334         // clear out the path found
3335         for ( ; target_components != present.end(); ++target_components)
3336         {
3337             valid_label.target_label = target_components->second->label;
3338 
3339             vector<set<position_node>::iterator >path;
3340             set<position_node> visited;
3341             search_astar(base_component->seed_position, valid_label,
3342                          connection_costs, dummy, visited, path);
3343 
3344             // Did the search, now remove any walls adjacent to squares in
3345             // the path.
3346             if (!path.size())
3347                 continue;
3348             const position_node * current = &(*path[0]);
3349 
3350             while (current)
3351             {
3352                 if (adjacent_check.adjacency(current->pos) > 0)
3353                 {
3354                     for (adjacent_iterator adj_it(current->pos); adj_it; ++adj_it)
3355                     {
3356                         if (feat_is_wall(env.grid(*adj_it)))
3357                         {
3358                             // This shouldn't happen since vault adjacent
3359                             // squares should have adjacency of DISCONNECT_DIST
3360                             // but oh well
3361                             if (env.level_map_mask(*adj_it) & MMT_VAULT)
3362                                 mpr("Whoops, nicked a vault in slime connectivity fixup");
3363                             env.grid(*adj_it) = DNGN_FLOOR;
3364                         }
3365                     }
3366                     adjacent_check.adjacency(current->pos) = 0;
3367                 }
3368                 current = current->last;
3369             }
3370 
3371         }
3372 
3373     }
3374 }
3375 
3376 // Place vaults with CHANCE: that want to be placed on this level.
_place_chance_vaults()3377 static void _place_chance_vaults()
3378 {
3379     const level_id &lid(level_id::current());
3380     mapref_vector maps = random_chance_maps_in_depth(lid);
3381     // [ds] If there are multiple CHANCE maps that share an luniq_ or
3382     // uniq_ tag, only the first such map will be placed. Shuffle the
3383     // order of chosen maps so we don't have a first-map bias.
3384     shuffle_array(maps);
3385     for (const map_def *map : maps)
3386     {
3387         bool check_fallback = true;
3388         if (!map->map_already_used())
3389         {
3390             dprf(DIAG_DNGN, "Placing CHANCE vault: %s (%s)",
3391                  map->name.c_str(), map->chance(lid).describe().c_str());
3392             check_fallback = !_build_secondary_vault(map);
3393         }
3394         if (check_fallback)
3395         {
3396             const string chance_tag = vault_chance_tag(*map);
3397             if (!chance_tag.empty())
3398             {
3399                 const string fallback_tag =
3400                     "fallback_" + chance_tag.substr(7); // "chance_"
3401                 const map_def *fallback =
3402                     random_map_for_tag(fallback_tag, true, false, MB_FALSE);
3403                 if (fallback)
3404                 {
3405                     dprf(DIAG_DNGN, "Found fallback vault %s for chance tag %s",
3406                          fallback->name.c_str(), chance_tag.c_str());
3407                     _build_secondary_vault(fallback);
3408                 }
3409             }
3410         }
3411     }
3412 }
3413 
_place_minivaults()3414 static void _place_minivaults()
3415 {
3416     const map_def *vault = nullptr;
3417     // First place the vault requested with &P
3418     if (you.props.exists("force_minivault")
3419         && (vault = find_map_by_name(you.props["force_minivault"])))
3420     {
3421         _dgn_ensure_vault_placed(_build_secondary_vault(vault), false, vault->name);
3422     }
3423     // Always try to place PLACE:X minivaults.
3424     if ((vault = random_map_for_place(level_id::current(), true)))
3425         _build_secondary_vault(vault);
3426 
3427     if (use_random_maps)
3428     {
3429         int tries = 0;
3430         do
3431         {
3432             vault = random_map_in_depth(level_id::current(), true);
3433             if (vault)
3434                 _build_secondary_vault(vault);
3435         } // if ALL maps eligible are "extra" but fail to place, we'd be screwed
3436         while (vault && vault->is_extra_vault() && tries++ < 10000);
3437     }
3438 }
3439 
_builder_normal()3440 static bool _builder_normal()
3441 {
3442     const map_def *vault = _dgn_random_map_for_place(false);
3443 
3444     if (vault)
3445     {
3446         // TODO: figure out a good way to do this only in Temple
3447         dgn_map_parameters mp(
3448             you.props.exists(TEMPLE_SIZE_KEY)
3449             ? make_stringf("temple_altars_%d",
3450                            you.props[TEMPLE_SIZE_KEY].get_int())
3451             : "");
3452         env.level_build_method += " random_map_for_place";
3453         _ensure_vault_placed_ex(_build_primary_vault(vault), vault);
3454         // Only place subsequent random vaults on non-encompass maps
3455         // and not at the branch end
3456         return vault->orient != MAP_ENCOMPASS;
3457     }
3458 
3459     if (use_random_maps)
3460         vault = random_map_in_depth(level_id::current(), false, MB_FALSE);
3461 
3462     if (vault)
3463     {
3464         env.level_build_method += " random_map_in_depth";
3465         _ensure_vault_placed_ex(_build_primary_vault(vault), vault);
3466         // Only place subsequent random vaults on non-encompass maps
3467         // and not at the branch end
3468         return vault->orient != MAP_ENCOMPASS;
3469     }
3470 
3471     vault = random_map_for_tag("layout", true, true);
3472 
3473     if (!vault)
3474         die("Couldn't pick a layout.");
3475 
3476     _dgn_ensure_vault_placed(_build_primary_vault(vault), false, vault->name);
3477     return true;
3478 }
3479 
_place_traps()3480 static void _place_traps()
3481 {
3482     const int num_traps = random2avg(2 * trap_rate_for_place(), 2);
3483 
3484     ASSERT(num_traps >= 0);
3485     dprf("attempting to place %d traps", num_traps);
3486 
3487     for (int i = 0; i < num_traps; i++)
3488     {
3489         trap_def ts;
3490 
3491         int tries;
3492         for (tries = 0; tries < 200; ++tries)
3493         {
3494             ts.pos.x = random2(GXM);
3495             ts.pos.y = random2(GYM);
3496             // Don't place random traps under vault monsters; if a vault
3497             // wants this they have to request it specifically.
3498             if (in_bounds(ts.pos)
3499                 && env.grid(ts.pos) == DNGN_FLOOR
3500                 && !map_masked(ts.pos, MMT_NO_TRAP)
3501                 && env.mgrid(ts.pos) == NON_MONSTER)
3502             {
3503                 break;
3504             }
3505         }
3506 
3507         if (tries == 200)
3508         {
3509             dprf("tried %d times to place a trap & gave up", tries);
3510             break;
3511         }
3512 
3513         // Don't place dispersal traps in opaque vaults, they won't
3514         // be later checked for connectivity and we might break them.
3515         const trap_type type = random_trap_for_place(
3516                                    !map_masked(ts.pos, MMT_OPAQUE));
3517         if (type == NUM_TRAPS)
3518         {
3519             dprf("failed to find a trap type to place");
3520             continue;
3521         }
3522 
3523         ts.type = type;
3524         env.grid(ts.pos) = ts.feature();
3525         ts.prepare_ammo();
3526         env.trap[ts.pos] = ts;
3527         dprf("placed a %s trap", trap_name(type).c_str());
3528     }
3529 
3530     if (player_in_branch(BRANCH_SPIDER))
3531     {
3532         // Max webs ranges from around 35 (Spider:1) to 220 (Spider:5), actual
3533         // amount will be much lower.
3534         int max_webs = 35 * pow(2, (you.depth - 1) / 1.5) - num_traps;
3535         max_webs /= 2;
3536         place_webs(max_webs + random2(max_webs));
3537     }
3538 }
3539 
_dgn_place_feature_at_random_floor_square(dungeon_feature_type feat,unsigned mask=MMT_VAULT)3540 static void _dgn_place_feature_at_random_floor_square(dungeon_feature_type feat,
3541                                                       unsigned mask = MMT_VAULT)
3542 {
3543     coord_def place = _dgn_random_point_in_bounds(DNGN_FLOOR, mask, DNGN_FLOOR);
3544     if (player_in_branch(BRANCH_SLIME))
3545     {
3546         int tries = 100;
3547         while (!place.origin()  // stop if we fail to find floor.
3548                && (dgn_has_adjacent_feat(place, DNGN_ROCK_WALL)
3549                    || dgn_has_adjacent_feat(place, DNGN_SLIMY_WALL))
3550                && tries-- > 0)
3551         {
3552             place = _dgn_random_point_in_bounds(DNGN_FLOOR, mask, DNGN_FLOOR);
3553         }
3554 
3555         if (tries < 0)  // tries == 0 means we succeeded on the last attempt
3556             place.reset();
3557     }
3558     if (place.origin())
3559         throw dgn_veto_exception("Cannot place feature at random floor square.");
3560     else
3561         _set_grd(place, feat);
3562 }
3563 
3564 // Create randomly-placed stone stairs.
dgn_place_stone_stairs(bool maybe_place_hatches)3565 void dgn_place_stone_stairs(bool maybe_place_hatches)
3566 {
3567     const int stair_start = DNGN_STONE_STAIRS_DOWN_I;
3568     const int stair_count = DNGN_ESCAPE_HATCH_UP - stair_start + 1;
3569 
3570     FixedVector < bool, stair_count > existing;
3571 
3572     existing.init(false);
3573 
3574     for (rectangle_iterator ri(0); ri; ++ri)
3575         if (env.grid(*ri) >= stair_start && env.grid(*ri) < stair_start + stair_count)
3576             existing[env.grid(*ri) - stair_start] = true;
3577 
3578     int pair_count = 3;
3579 
3580     if (maybe_place_hatches && coinflip())
3581         pair_count++;
3582 
3583     for (int i = 0; i < pair_count; ++i)
3584     {
3585         if (!existing[i])
3586         {
3587             _dgn_place_feature_at_random_floor_square(
3588                 static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_DOWN_I + i));
3589         }
3590 
3591         if (!existing[DNGN_STONE_STAIRS_UP_I - stair_start + i])
3592         {
3593             _dgn_place_feature_at_random_floor_square(
3594                 static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_UP_I + i));
3595         }
3596     }
3597 }
3598 
dgn_has_adjacent_feat(coord_def c,dungeon_feature_type feat)3599 bool dgn_has_adjacent_feat(coord_def c, dungeon_feature_type feat)
3600 {
3601     for (adjacent_iterator ai(c); ai; ++ai)
3602         if (env.grid(*ai) == feat)
3603             return true;
3604     return false;
3605 }
3606 
dgn_random_point_in_margin(int margin)3607 coord_def dgn_random_point_in_margin(int margin)
3608 {
3609     coord_def res;
3610     res.x = random_range(margin, GXM - margin - 1);
3611     res.y = random_range(margin, GYM - margin - 1);
3612     return res;
3613 }
3614 
_point_matches_feat(coord_def c,dungeon_feature_type searchfeat,uint32_t mapmask,dungeon_feature_type adjacent_feat,bool monster_free)3615 static inline bool _point_matches_feat(coord_def c,
3616                                        dungeon_feature_type searchfeat,
3617                                        uint32_t mapmask,
3618                                        dungeon_feature_type adjacent_feat,
3619                                        bool monster_free)
3620 {
3621     return env.grid(c) == searchfeat
3622            && (!monster_free || !monster_at(c))
3623            && !map_masked(c, mapmask)
3624            && (adjacent_feat == DNGN_UNSEEN ||
3625                dgn_has_adjacent_feat(c, adjacent_feat));
3626 }
3627 
3628 // Returns a random point in map bounds matching the given search feature,
3629 // and respecting the map mask (a map mask of MMT_VAULT ensures that
3630 // positions inside vaults will not be returned).
3631 //
3632 // If adjacent_feat is not DNGN_UNSEEN, the chosen square will be
3633 // adjacent to a square containing adjacent_feat.
3634 //
3635 // If monster_free is true, the chosen square will never be occupied by
3636 // a monster.
3637 //
3638 // If tries is set to anything other than -1, this function will make tries
3639 // attempts to find a suitable square, and may fail if the map is crowded.
3640 // If tries is set to -1, this function will examine the entire map and
3641 // guarantees to find a suitable point if available.
3642 //
3643 // If a suitable point is not available (or was not found in X tries),
3644 // returns coord_def(0,0)
3645 //
_dgn_random_point_in_bounds(dungeon_feature_type searchfeat,uint32_t mapmask,dungeon_feature_type adjacent_feat,bool monster_free,int tries)3646 static coord_def _dgn_random_point_in_bounds(dungeon_feature_type searchfeat,
3647                                      uint32_t mapmask,
3648                                      dungeon_feature_type adjacent_feat,
3649                                      bool monster_free,
3650                                      int tries)
3651 {
3652     if (tries == -1)
3653     {
3654         // Try a quick and dirty random search:
3655         int n = 10;
3656         if (searchfeat == DNGN_FLOOR)
3657             n = 500;
3658         coord_def chosen = _dgn_random_point_in_bounds(searchfeat,
3659                                                        mapmask,
3660                                                        adjacent_feat,
3661                                                        monster_free,
3662                                                        n);
3663         if (!chosen.origin())
3664             return chosen;
3665 
3666         // Exhaustive search; will never fail if a suitable place is
3667         // available, but is also far more expensive.
3668         int nfound = 0;
3669         for (rectangle_iterator ri(1); ri; ++ri)
3670         {
3671             const coord_def c(*ri);
3672             if (_point_matches_feat(c, searchfeat, mapmask, adjacent_feat,
3673                                     monster_free)
3674                 && one_chance_in(++nfound))
3675             {
3676                 chosen = c;
3677             }
3678         }
3679         return chosen;
3680     }
3681     else
3682     {
3683         // Random search.
3684         while (--tries >= 0)
3685         {
3686             const coord_def c = random_in_bounds();
3687             if (_point_matches_feat(c, searchfeat, mapmask, adjacent_feat,
3688                                     monster_free))
3689             {
3690                 return c;
3691             }
3692         }
3693         return coord_def(0, 0);
3694     }
3695 }
3696 
_place_specific_feature(dungeon_feature_type feat)3697 static coord_def _place_specific_feature(dungeon_feature_type feat)
3698 {
3699     /* Only overwrite vaults when absolutely necessary. */
3700     coord_def c = _dgn_random_point_in_bounds(DNGN_FLOOR, MMT_VAULT, DNGN_UNSEEN, true);
3701     if (!in_bounds(c))
3702         c = _dgn_random_point_in_bounds(DNGN_FLOOR, 0, DNGN_UNSEEN, true);
3703 
3704     if (in_bounds(c))
3705         env.grid(c) = feat;
3706     else
3707         throw dgn_veto_exception("Cannot place specific feature.");
3708 
3709     return c;
3710 }
3711 
_place_vault_by_tag(const string & tag)3712 static bool _place_vault_by_tag(const string &tag)
3713 {
3714     const map_def *vault = random_map_for_tag(tag, true);
3715     if (!vault)
3716         return false;
3717     return _build_secondary_vault(vault);
3718 }
3719 
_place_branch_entrances(bool use_vaults)3720 static void _place_branch_entrances(bool use_vaults)
3721 {
3722     // Find what branch entrances are already placed, and what branch
3723     // entrances could be placed here.
3724     bool branch_entrance_placed[NUM_BRANCHES];
3725     bool could_be_placed = false;
3726     for (branch_iterator it; it; ++it)
3727     {
3728         branch_entrance_placed[it->id] = false;
3729         if (!could_be_placed
3730             && !branch_is_unfinished(it->id)
3731             && !is_hell_subbranch(it->id)
3732             && ((you.depth >= it->mindepth
3733                  && you.depth <= it->maxdepth)
3734                 || level_id::current() == brentry[it->id]))
3735         {
3736             could_be_placed = true;
3737         }
3738     }
3739 
3740     // If there's nothing to be placed, don't bother.
3741     if (!could_be_placed)
3742         return;
3743 
3744     for (rectangle_iterator ri(0); ri; ++ri)
3745     {
3746         if (!feat_is_branch_entrance(env.grid(*ri)))
3747             continue;
3748 
3749         for (branch_iterator it; it; ++it)
3750             if (it->entry_stairs == env.grid(*ri)
3751                 && !feature_mimic_at(*ri))
3752             {
3753                 branch_entrance_placed[it->id] = true;
3754                 break;
3755             }
3756     }
3757 
3758     // Place actual branch entrances.
3759     for (branch_iterator it; it; ++it)
3760     {
3761         // Vestibule and hells are placed by other means.
3762         // Likewise, if we already have an entrance, keep going.
3763         if (is_hell_branch(it->id) || branch_entrance_placed[it->id])
3764             continue;
3765 
3766         if (it->entry_stairs != NUM_FEATURES
3767             && player_in_branch(parent_branch(it->id))
3768             && level_id::current() == brentry[it->id])
3769         {
3770             // Placing a stair.
3771             dprf(DIAG_DNGN, "Placing stair to %s", it->shortname);
3772 
3773             // Attempt to place an entry vault if allowed
3774             if (use_vaults)
3775             {
3776                 string entry_tag = string(it->abbrevname);
3777                 entry_tag += "_entry";
3778                 lowercase(entry_tag);
3779 
3780                 if (_place_vault_by_tag(entry_tag))
3781                     // Placed this entrance, carry on to subsequent branches
3782                     continue;
3783             }
3784 
3785             // Otherwise place a single stair feature.
3786             // Try to use designated locations for entrances if possible.
3787             const coord_def portal_pos = find_portal_place(nullptr, false);
3788             if (!portal_pos.origin())
3789             {
3790                 env.grid(portal_pos) = it->entry_stairs;
3791                 env.level_map_mask(portal_pos) |= MMT_VAULT;
3792                 continue;
3793             }
3794 
3795             const coord_def stair_pos = _place_specific_feature(it->entry_stairs);
3796             // Don't allow subsequent vaults to overwrite the branch stair
3797             env.level_map_mask(stair_pos) |= MMT_VAULT;
3798         }
3799     }
3800 }
3801 
_place_extra_vaults()3802 static void _place_extra_vaults()
3803 {
3804     int tries = 0;
3805     while (true)
3806     {
3807         if (!player_in_branch(BRANCH_DUNGEON) && use_random_maps)
3808         {
3809             const map_def *vault = random_map_in_depth(level_id::current(),
3810                                                        false, MB_TRUE);
3811 
3812             // Encompass vaults can't be used as secondaries.
3813             if (!vault || vault->orient == MAP_ENCOMPASS)
3814                 break;
3815 
3816             if (_build_secondary_vault(vault))
3817             {
3818                 const map_def &map(*vault);
3819                 if (map.is_extra_vault())
3820                     continue;
3821                 use_random_maps = false;
3822             }
3823             else if (++tries >= 3)
3824                 break;
3825         }
3826         break;
3827     }
3828 }
3829 
3830 // Place uniques on the level.
3831 // Return the number of uniques placed.
_place_uniques()3832 static int _place_uniques()
3833 {
3834 #ifdef DEBUG_UNIQUE_PLACEMENT
3835     FILE *ostat = fopen("unique_placement.log", "a");
3836     fprintf(ostat, "--- Looking to place uniques on %s\n",
3837                    level_id::current().describe().c_str());
3838 #endif
3839 
3840     int num_placed = 0;
3841 
3842     // Magic numbers for dpeg's unique system.
3843     int A = 2;
3844     const int B = 5;
3845     while (one_chance_in(A))
3846     {
3847         // In dpeg's unique placement system, chances is always 1 in A of even
3848         // starting to place a unique; reduced if there are less uniques to be
3849         // placed or available. Then there is a chance of uniques_available /
3850         // B; this only triggers on levels that have less than B uniques to be
3851         // placed.
3852         const mapref_vector uniques_available =
3853             find_maps_for_tag("place_unique", true, true);
3854 
3855         if (!x_chance_in_y((int)uniques_available.size(), B))
3856             break;
3857 
3858         const map_def *uniq_map = random_map_for_tag("place_unique", true);
3859         if (!uniq_map)
3860         {
3861 #ifdef DEBUG_UNIQUE_PLACEMENT
3862             fprintf(ostat, "Dummy balance or no uniques left.\n");
3863 #endif
3864             break;
3865         }
3866 
3867         const bool map_placed = _build_secondary_vault(uniq_map);
3868         if (map_placed)
3869         {
3870             num_placed++;
3871             // Make the placement chance drop steeply after
3872             // some have been placed, to reduce chance of
3873             // many uniques per level.
3874             if (num_placed >= 3)
3875                 A++;
3876 #ifdef DEBUG_UNIQUE_PLACEMENT
3877             fprintf(ostat, "Placed valid unique map: %s.\n",
3878                     uniq_map->name.c_str());
3879 #endif
3880             dprf(DIAG_DNGN, "Placed %s.", uniq_map->name.c_str());
3881         }
3882 #ifdef DEBUG_UNIQUE_PLACEMENT
3883         else
3884         {
3885             fprintf(ostat, "Didn't place valid map: %s\n",
3886                     uniq_map->name.c_str());
3887         }
3888 #endif
3889     }
3890 
3891 #ifdef DEBUG_UNIQUE_PLACEMENT
3892     fprintf(ostat, "--- Finished this set, placed %d uniques.\n", num_placed);
3893     fclose(ostat);
3894 #endif
3895     return num_placed;
3896 }
3897 
_place_aquatic_in(vector<coord_def> & places,const vector<pop_entry> & pop,int level,bool allow_zombies)3898 static void _place_aquatic_in(vector<coord_def> &places, const vector<pop_entry>& pop,
3899                               int level, bool allow_zombies)
3900 {
3901     if (places.size() < 35)
3902         return;
3903     int num = min(random_range(places.size() / 35, places.size() / 18), 15);
3904     shuffle_array(places);
3905 
3906     for (int i = 0; i < num; i++)
3907     {
3908         monster_type mon = pick_monster_from(pop, level);
3909         if (mon == MONS_0)
3910             break;
3911 
3912         mgen_data mg;
3913         mg.behaviour = BEH_SLEEP;
3914         mg.flags    |= MG_PERMIT_BANDS | MG_FORCE_PLACE;
3915         mg.map_mask |= MMT_NO_MONS;
3916         mg.cls = mon;
3917         mg.pos = places[i];
3918 
3919         // Amphibious creatures placed with water should hang around it
3920         if (mons_class_primary_habitat(mon) == HT_LAND)
3921             mg.flags |= MG_PATROLLING;
3922 
3923         if (allow_zombies
3924             && player_in_hell()
3925             && mons_class_can_be_zombified(mg.cls))
3926         {
3927             mg.base_type = mg.cls;
3928             const int skel_chance = mons_skeleton(mg.cls) ? 2 : 0;
3929             mg.cls = random_choose_weighted(skel_chance, MONS_SKELETON,
3930                                             8,           MONS_ZOMBIE,
3931                                             1,           MONS_SIMULACRUM);
3932         }
3933 
3934         place_monster(mg);
3935     }
3936 }
3937 
_place_aquatic_monsters()3938 static void _place_aquatic_monsters()
3939 {
3940     // Shoals relies on normal monster generation to place its monsters.
3941     // Abyss's nature discourages random movement-inhibited monsters.
3942     // Default liquid creatures are harmless in Pan or Zot, and
3943     // threatening ones are distracting from their sets.
3944     // Random liquid monster placement is too vicious before D:6.
3945     //
3946     if (player_in_branch(BRANCH_SHOALS)
3947         || player_in_branch(BRANCH_ABYSS)
3948         || player_in_branch(BRANCH_PANDEMONIUM)
3949         || player_in_branch(BRANCH_ZOT)
3950         || player_in_branch(BRANCH_DUNGEON) && you.depth < 6)
3951     {
3952         return;
3953     }
3954 
3955     int level = level_id::current().depth;
3956 
3957     vector<coord_def> water;
3958     vector<coord_def> lava;
3959 
3960     for (rectangle_iterator ri(0); ri; ++ri)
3961     {
3962         if (actor_at(*ri) || env.level_map_mask(*ri) & MMT_NO_MONS)
3963             continue;
3964 
3965         dungeon_feature_type feat = env.grid(*ri);
3966         if (feat == DNGN_SHALLOW_WATER || feat == DNGN_DEEP_WATER)
3967             water.push_back(*ri);
3968         else if (feat == DNGN_LAVA)
3969             lava.push_back(*ri);
3970     }
3971 
3972     _place_aquatic_in(water, fish_population(you.where_are_you, false), level,
3973                       true);
3974     _place_aquatic_in(lava, fish_population(you.where_are_you, true), level,
3975                       false);
3976 }
3977 
_zombifiables()3978 static vector<monster_type> _zombifiables()
3979 {
3980     vector<monster_type> z;
3981     for (monster_type mcls = MONS_0; mcls < NUM_MONSTERS; ++mcls)
3982     {
3983         if (mons_species(mcls) != mcls
3984             || !mons_zombie_size(mcls)
3985             || mons_is_unique(mcls)
3986             || !(mons_class_holiness(mcls) & MH_NATURAL)
3987             || mons_class_flag(mcls, M_NO_GEN_DERIVED))
3988         {
3989             continue;
3990         }
3991 
3992         z.push_back(mcls);
3993     }
3994     return z;
3995 }
3996 
3997 // For Crypt, adds a bunch of skeletons and zombies that do not respect
3998 // absdepth (and thus tend to be varied and include several types that
3999 // would not otherwise spawn there).
_place_assorted_zombies()4000 static void _place_assorted_zombies()
4001 {
4002     static const vector<monster_type> zombifiable = _zombifiables();
4003     int num_zombies = random_range(6, 12, 3);
4004     for (int i = 0; i < num_zombies; ++i)
4005     {
4006         bool skel = coinflip();
4007         monster_type z_base;
4008         do
4009         {
4010             z_base = zombifiable[random2(zombifiable.size())];
4011         }
4012         while (skel && !mons_skeleton(z_base));
4013 
4014         mgen_data mg;
4015         mg.cls = (skel ? MONS_SKELETON : MONS_ZOMBIE);
4016         mg.base_type = z_base;
4017         mg.behaviour = BEH_SLEEP;
4018         mg.map_mask |= MMT_NO_MONS;
4019         mg.preferred_grid_feature = DNGN_FLOOR;
4020 
4021         place_monster(mg);
4022     }
4023 }
4024 
door_vetoed(const coord_def pos)4025 bool door_vetoed(const coord_def pos)
4026 {
4027     return env.markers.property_at(pos, MAT_ANY, "veto_open") == "veto";
4028 }
4029 
_builder_monsters()4030 static void _builder_monsters()
4031 {
4032     if (player_in_branch(BRANCH_TEMPLE))
4033         return;
4034 
4035     int mon_wanted = _num_mons_wanted();
4036 
4037     const bool in_shoals = player_in_branch(BRANCH_SHOALS);
4038     if (in_shoals)
4039         dgn_shoals_generate_flora();
4040 
4041     // Try to place Shoals monsters on floor where possible instead of
4042     // letting all the merfolk be generated in the middle of the
4043     // water.
4044     const dungeon_feature_type preferred_grid_feature =
4045         in_shoals ? DNGN_FLOOR : DNGN_UNSEEN;
4046 
4047     dprf(DIAG_DNGN, "_builder_monsters: Generating %d monsters", mon_wanted);
4048     for (int i = 0; i < mon_wanted; i++)
4049     {
4050         mgen_data mg;
4051 
4052         // On D:1, we want monsters out of LOS distance from the player's
4053         // starting position, and we don't generate them awake.
4054         if (env.absdepth0 == 0)
4055         {
4056             mg.behaviour = BEH_SLEEP;
4057             mg.proximity = PROX_AWAY_FROM_ENTRANCE;
4058         }
4059         else if (player_in_connected_branch() && one_chance_in(8))
4060         {
4061             // Chance to generate the monster awake, but away from all level
4062             // stairs (including the delver start stairs).
4063             mg.proximity = PROX_AWAY_FROM_STAIRS;
4064         }
4065         else if (!player_in_branch(BRANCH_PANDEMONIUM))
4066         {
4067             // Pan monsters always generate awake.
4068 
4069             // For delvers, waking monsters can generate on D:5, but they can't
4070             // be near the entrance.
4071             if (env.absdepth0 == starting_absdepth())
4072                 mg.proximity = PROX_AWAY_FROM_ENTRANCE;
4073             mg.behaviour = BEH_SLEEP;
4074         }
4075 
4076         mg.flags    |= MG_PERMIT_BANDS;
4077         mg.map_mask |= MMT_NO_MONS;
4078         mg.preferred_grid_feature = preferred_grid_feature;
4079         place_monster(mg);
4080     }
4081 
4082     if (!player_in_branch(BRANCH_CRYPT)) // No water creatures in the Crypt.
4083         _place_aquatic_monsters();
4084     else
4085         _place_assorted_zombies();
4086 }
4087 
4088 /**
4089  * Randomly place a single item
4090  *
4091  * @param item   The item slot of the item being randomly placed
4092  */
_randomly_place_item(int item)4093 static void _randomly_place_item(int item)
4094 {
4095     coord_def itempos;
4096     bool found = false;
4097     for (int i = 0; i < 500 && !found; ++i)
4098     {
4099         itempos = random_in_bounds();
4100         const monster* mon = monster_at(itempos);
4101         found = env.grid(itempos) == DNGN_FLOOR
4102                 && !map_masked(itempos, MMT_NO_ITEM)
4103                 // oklobs or statues are ok
4104                 && (!mon || !mons_is_firewood(*mon));
4105     }
4106     if (!found)
4107     {
4108         dprf(DIAG_DNGN, "Builder failed to place %s",
4109             env.item[item].name(DESC_PLAIN, false, true).c_str());
4110         // Couldn't find a single good spot!
4111         destroy_item(item);
4112     }
4113     else
4114     {
4115         dprf(DIAG_DNGN, "Builder placing %s at %d,%d",
4116             env.item[item].name(DESC_PLAIN, false, true).c_str(),
4117             itempos.x, itempos.y);
4118         move_item_to_grid(&item, itempos);
4119     }
4120 }
4121 
4122 /**
4123  * Randomly place items on a level. Does not place items in vaults,
4124  * on monsters, etc. Only normal floor generated items.
4125  */
_builder_items()4126 static void _builder_items()
4127 {
4128     int i = 0;
4129     object_class_type specif_type = OBJ_RANDOM;
4130     int items_levels = env.absdepth0;
4131     int items_wanted = _num_items_wanted(items_levels);
4132 
4133     if (player_in_branch(BRANCH_VAULTS))
4134     {
4135         items_levels *= 15;
4136         items_levels /= 10;
4137     }
4138     else if (player_in_branch(BRANCH_ORC))
4139     {
4140         specif_type = OBJ_GOLD;  // Lots of gold in the orcish mines.
4141         items_levels *= 2;       // Four levels' worth, in fact.
4142     }
4143 
4144     for (i = 0; i < items_wanted; i++)
4145     {
4146         int item = items(true, specif_type, OBJ_RANDOM, items_levels);
4147 
4148         _randomly_place_item(item);
4149     }
4150 
4151 }
4152 
_connect_vault_exit(const coord_def & exit)4153 static bool _connect_vault_exit(const coord_def& exit)
4154 {
4155     flood_find<feature_grid, coord_predicate> ff(env.grid, in_bounds, true,
4156                                                  false);
4157     ff.add_feat(DNGN_FLOOR);
4158 
4159     coord_def target = ff.find_first_from(exit, env.level_map_mask);
4160 
4161     if (in_bounds(target))
4162         return join_the_dots(exit, target, MMT_VAULT);
4163 
4164     return false;
4165 }
4166 
_grid_needs_exit(const coord_def & c)4167 static bool _grid_needs_exit(const coord_def& c)
4168 {
4169     return !cell_is_solid(c)
4170            || feat_is_closed_door(env.grid(c));
4171 }
4172 
_map_feat_is_on_edge(const vault_placement & place,const coord_def & c)4173 static bool _map_feat_is_on_edge(const vault_placement &place,
4174                                  const coord_def &c)
4175 {
4176     if (!place.map.in_map(c - place.pos))
4177         return false;
4178 
4179     for (orth_adjacent_iterator ai(c); ai; ++ai)
4180         if (!place.map.in_map(*ai - place.pos))
4181             return true;
4182 
4183     return false;
4184 }
4185 
_pick_float_exits(vault_placement & place,vector<coord_def> & targets)4186 static void _pick_float_exits(vault_placement &place, vector<coord_def> &targets)
4187 {
4188     vector<coord_def> possible_exits;
4189 
4190     for (rectangle_iterator ri(place.pos, place.pos + place.size - 1); ri; ++ri)
4191         if (_grid_needs_exit(*ri) && _map_feat_is_on_edge(place, *ri))
4192             possible_exits.push_back(*ri);
4193 
4194     if (possible_exits.empty())
4195     {
4196         // Empty map (a serial vault master, etc).
4197         if (place.size.origin())
4198             return;
4199 
4200         // The vault is disconnected, does it have a stair inside?
4201         for (rectangle_iterator ri(place.pos, place.pos + place.size - 1); ri; ++ri)
4202             if (feat_is_stair(env.grid(*ri)))
4203                 return;
4204 
4205         mprf(MSGCH_ERROR, "Unable to find exit from %s",
4206              place.map.name.c_str());
4207         return;
4208     }
4209 
4210     const int npoints = possible_exits.size();
4211     int nexits = npoints < 6? npoints : npoints / 8 + 1;
4212 
4213     if (nexits > 10)
4214         nexits = 10;
4215 
4216     while (nexits-- > 0)
4217     {
4218         int which_exit = random2(possible_exits.size());
4219         targets.push_back(possible_exits[which_exit]);
4220         possible_exits.erase(possible_exits.begin() + which_exit);
4221     }
4222 }
4223 
_fixup_after_vault()4224 static void _fixup_after_vault()
4225 {
4226     _dgn_set_floor_colours();
4227 
4228     link_items();
4229     env.markers.activate_all();
4230 
4231     // Force teleport to place the player somewhere sane.
4232     you_teleport_now();
4233 
4234     setup_environment_effects();
4235 }
4236 
4237 // Places a map on the current level (minivault or regular vault).
4238 //
4239 // You can specify the centre of the map using "where" for floating vaults
4240 // and minivaults. "where" is ignored for other vaults. XXX: it might be
4241 // nice to specify a square that is not the centre, but is identified by
4242 // a marker in the vault to be placed.
4243 //
4244 // NOTE: encompass maps will destroy the existing level!
4245 //
4246 // check_collision: If true, the newly placed vault cannot clobber existing
4247 //          items and monsters (otherwise, items may be destroyed, monsters may
4248 //          be teleported).
4249 //
4250 // Non-dungeon code should generally use dgn_safe_place_map instead of
4251 // this function to recover from map_load_exceptions.
dgn_place_map(const map_def * mdef,bool check_collision,bool make_no_exits,const coord_def & where)4252 const vault_placement *dgn_place_map(const map_def *mdef,
4253                                      bool check_collision,
4254                                      bool make_no_exits,
4255                                      const coord_def &where)
4256 {
4257     if (!mdef)
4258         return nullptr;
4259 
4260     const dgn_colour_override_manager colour_man;
4261 
4262     if (mdef->orient == MAP_ENCOMPASS && !crawl_state.generating_level)
4263     {
4264         if (check_collision)
4265         {
4266             mprf(MSGCH_DIAGNOSTICS,
4267                  "Cannot generate encompass map '%s' with check_collision=true",
4268                  mdef->name.c_str());
4269 
4270             return nullptr;
4271         }
4272 
4273         // For encompass maps, clear the entire level.
4274         unwind_bool levgen(crawl_state.generating_level, true);
4275         dgn_reset_level();
4276         dungeon_events.clear();
4277         const vault_placement *vault_place =
4278             dgn_place_map(mdef, check_collision, make_no_exits, where);
4279         if (vault_place)
4280             _fixup_after_vault();
4281         return vault_place;
4282     }
4283 
4284     const vault_placement *vault_place =
4285         _build_secondary_vault(mdef, check_collision,
4286                                make_no_exits, where);
4287 
4288     // Activate any markers within the map.
4289     if (vault_place && !crawl_state.generating_level)
4290     {
4291 #ifdef ASSERTS
4292         if (mdef->name != vault_place->map.name)
4293         {
4294             die("Placed map '%s', yet vault_placement is '%s'",
4295                 mdef->name.c_str(), vault_place->map.name.c_str());
4296         }
4297 #endif
4298 
4299         for (vault_place_iterator vpi(*vault_place); vpi; ++vpi)
4300         {
4301             const coord_def p = *vpi;
4302             env.markers.activate_markers_at(p);
4303             if (!you.see_cell(p))
4304                 set_terrain_changed(p);
4305         }
4306         env.markers.clear_need_activate();
4307 
4308         setup_environment_effects();
4309         _dgn_postprocess_level();
4310     }
4311 
4312     return vault_place;
4313 }
4314 
4315 // Identical to dgn_place_map, but recovers gracefully from
4316 // map_load_exceptions. Prefer this function if placing maps *not*
4317 // during level generation time.
4318 //
4319 // Returns the map actually placed if the map was placed successfully.
4320 // This is usually the same as the map passed in, unless map load
4321 // failed and maps had to be reloaded.
dgn_safe_place_map(const map_def * mdef,bool check_collision,bool make_no_exits,const coord_def & where)4322 const vault_placement *dgn_safe_place_map(const map_def *mdef,
4323                                           bool check_collision,
4324                                           bool make_no_exits,
4325                                           const coord_def &where)
4326 {
4327     const string mapname(mdef->name);
4328     int retries = 10;
4329     while (true)
4330     {
4331         try
4332         {
4333             return dgn_place_map(mdef, check_collision, make_no_exits, where);
4334         }
4335         catch (map_load_exception &mload)
4336         {
4337             if (retries-- > 0)
4338             {
4339                 mprf(MSGCH_ERROR,
4340                      "Failed to load map %s in dgn_safe_place_map, "
4341                      "reloading all maps",
4342                      mload.what());
4343                 reread_maps();
4344 
4345                 mdef = find_map_by_name(mapname);
4346             }
4347             else
4348                 return nullptr;
4349         }
4350     }
4351 }
4352 
dgn_vault_at(coord_def p)4353 vault_placement *dgn_vault_at(coord_def p)
4354 {
4355     const int map_index = env.level_map_ids(p);
4356     return map_index == INVALID_MAP_INDEX ? nullptr
4357                                           : env.level_vaults[map_index].get();
4358 }
4359 
dgn_seen_vault_at(coord_def p)4360 void dgn_seen_vault_at(coord_def p)
4361 {
4362     if (vault_placement *vp = dgn_vault_at(p))
4363     {
4364         if (!vp->seen)
4365         {
4366             dprf(DIAG_DNGN, "Vault %s (%d,%d)-(%d,%d) seen",
4367                  vp->map.name.c_str(), vp->pos.x, vp->pos.y,
4368                  vp->size.x, vp->size.y);
4369             vp->seen = true;
4370         }
4371     }
4372 }
4373 
_vault_wants_damage(const vault_placement & vp)4374 static bool _vault_wants_damage(const vault_placement &vp)
4375 {
4376     const map_def &map = vp.map;
4377     if (map.has_tag("ruin"))
4378         return true;
4379 
4380     // Some vaults may want to be ruined only in certain places with
4381     // tags like "ruin_abyss" or "ruin_lair"
4382     string place_desc = level_id::current().describe(false, false);
4383     lowercase(place_desc);
4384     return map.has_tag("ruin_" + place_desc);
4385 }
4386 
_ruin_vault(const vault_placement & vp)4387 static void _ruin_vault(const vault_placement &vp)
4388 {
4389     _ruin_level(vault_place_iterator(vp), 0, 12, 0);
4390 }
4391 
4392 // Places a vault somewhere in an already built level if possible.
4393 // Returns true if the vault was successfully placed.
_build_secondary_vault(const map_def * vault,bool check_collision,bool no_exits,const coord_def & where)4394 static const vault_placement *_build_secondary_vault(const map_def *vault,
4395                        bool check_collision, bool no_exits,
4396                        const coord_def &where)
4397 {
4398     return _build_vault_impl(vault, true, check_collision, no_exits, where);
4399 }
4400 
4401 // Builds a primary vault - i.e. a vault that is built before anything
4402 // else on the level. After placing the vault, rooms and corridors
4403 // will be constructed on the level and the vault exits will be
4404 // connected to corridors.
4405 //
4406 // If portions of the level are already generated at this point, use
4407 // _build_secondary_vault or dgn_place_map instead.
4408 //
4409 // NOTE: minivaults can never be placed as primary vaults.
4410 //
_build_primary_vault(const map_def * vault)4411 static const vault_placement *_build_primary_vault(const map_def *vault)
4412 {
4413     return _build_vault_impl(vault);
4414 }
4415 
4416 // Builds a vault or minivault. Do not use this function directly: always
4417 // prefer _build_secondary_vault or _build_primary_vault.
_build_vault_impl(const map_def * vault,bool build_only,bool check_collisions,bool make_no_exits,const coord_def & where)4418 static const vault_placement *_build_vault_impl(const map_def *vault,
4419                   bool build_only, bool check_collisions,
4420                   bool make_no_exits, const coord_def &where)
4421 {
4422     if (dgn_check_connectivity && !dgn_zones)
4423     {
4424         dgn_zones = dgn_count_disconnected_zones(false);
4425         if (player_in_branch(BRANCH_PANDEMONIUM) && dgn_zones > 1)
4426             throw dgn_veto_exception("Pan map with disconnected zones");
4427     }
4428 
4429     unwind_var<string> placing(env.placing_vault, vault->name);
4430 
4431     vault_placement place;
4432 
4433     if (map_bounds(where))
4434         place.pos = where;
4435 
4436     const map_section_type placed_vault_orientation =
4437         vault_main(place, vault, check_collisions);
4438 
4439     dprf(DIAG_DNGN, "Map: %s; placed: %s; place: (%d,%d), size: (%d,%d)",
4440          vault->name.c_str(),
4441          placed_vault_orientation != MAP_NONE ? "yes" : "no",
4442          place.pos.x, place.pos.y, place.size.x, place.size.y);
4443 
4444     // veto if this was a fatal error. (Vetoing in general here would be
4445     // extremely costly.)
4446     // TODO: I'm not sure this is necessary, but it makes the error more
4447     // prominent
4448     _dgn_ensure_vault_placed(placed_vault_orientation != MAP_NONE
4449         || !crawl_state.last_builder_error_fatal, false,
4450         vault->name);
4451 
4452     if (placed_vault_orientation == MAP_NONE)
4453         return nullptr;
4454 
4455     const bool is_layout = place.map.is_overwritable_layout();
4456 
4457     if (placed_vault_orientation == MAP_ENCOMPASS && !is_layout)
4458         env.level_layout_types.insert("encompass");
4459 
4460     if (!build_only
4461         && (placed_vault_orientation == MAP_ENCOMPASS || is_layout)
4462         && place.map.border_fill_type != DNGN_ROCK_WALL)
4463     {
4464         dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL,
4465                          place.map.border_fill_type);
4466     }
4467 
4468     // XXX: Moved this out of dgn_register_place so that vault-set monsters can
4469     // be accessed with the '9' and '8' glyphs. (due)
4470     if (!place.map.random_mons.empty())
4471     {
4472         dprf(DIAG_DNGN, "Setting the custom random mons list.");
4473         set_vault_mon_list(place.map.random_mons);
4474     }
4475 
4476     place.apply_grid();
4477 
4478     if (_vault_wants_damage(place))
4479         _ruin_vault(place);
4480 
4481     if (place.exits.empty() && placed_vault_orientation != MAP_ENCOMPASS
4482         && (!place.map.is_minivault() || !place.map.has_tag("no_exits")))
4483     {
4484         _pick_float_exits(place, place.exits);
4485     }
4486 
4487     if (make_no_exits)
4488         place.exits.clear();
4489 
4490     // Must do this only after target_connections is finalised, or the vault
4491     // exits will not be correctly set.
4492     const vault_placement *saved_place = dgn_register_place(place, true);
4493 
4494 #ifdef DEBUG_STATISTICS
4495     if (crawl_state.map_stat_gen)
4496         mapstat_report_map_use(place.map);
4497 #endif
4498 
4499     if (is_layout && place.map.has_tag_prefix("layout_type_"))
4500     {
4501         for (auto &tag : place.map.get_tags())
4502         {
4503             if (starts_with(tag, "layout_type_"))
4504             {
4505                 env.level_layout_types.insert(
4506                     tag_without_prefix(tag, "layout_type_"));
4507             }
4508         }
4509     }
4510 
4511     // If the map takes the whole screen or we were only requested to
4512     // build the vault, our work is done.
4513     if (!build_only && (placed_vault_orientation != MAP_ENCOMPASS || is_layout))
4514     {
4515         if (!is_layout)
4516             _build_postvault_level(place);
4517 
4518         dgn_place_stone_stairs(true);
4519     }
4520 
4521     if (!build_only && (placed_vault_orientation != MAP_ENCOMPASS || is_layout)
4522         && player_in_branch(BRANCH_SWAMP))
4523     {
4524         _process_disconnected_zones(0, 0, GXM-1, GYM-1, true, DNGN_MANGROVE);
4525         // do a second pass to remove tele closets consisting of deep water
4526         // created by the first pass -- which will not fill in deep water
4527         // because it is treated as impassable.
4528         // TODO: get zonify to prevent these?
4529         // TODO: does this come up anywhere outside of swamp?
4530         _process_disconnected_zones(0, 0, GXM-1, GYM-1, true, DNGN_MANGROVE,
4531                 _dgn_square_is_ever_passable);
4532     }
4533 
4534     if (!make_no_exits)
4535     {
4536         const bool spotty =
4537             testbits(branches[you.where_are_you].branch_flags, brflag::spotty);
4538         if (place.connect(spotty) == 0 && place.exits.size() > 0
4539             && !player_in_branch(BRANCH_ABYSS))
4540         {
4541             throw dgn_veto_exception("Failed to connect exits for: "
4542                                      + place.map.name);
4543         }
4544     }
4545 
4546     // Fire any post-place hooks defined for this map; any failure
4547     // here is an automatic veto. Note that the post-place hook must
4548     // be run only after _build_postvault_level.
4549     if (!place.map.run_postplace_hook())
4550     {
4551         throw dgn_veto_exception("Post-place hook failed for: "
4552                                  + place.map.name);
4553     }
4554 
4555     return saved_place;
4556 }
4557 
_build_postvault_level(vault_placement & place)4558 static void _build_postvault_level(vault_placement &place)
4559 {
4560     if (player_in_branch(BRANCH_SPIDER))
4561     {
4562         int ngb_min = 2;
4563         int ngb_max = random_range(3, 8);
4564         if (one_chance_in(10))
4565             ngb_min = 1, ngb_max = random_range(5, 7);
4566         if (one_chance_in(20))
4567             ngb_min = 3, ngb_max = 4;
4568         const int connchance = random_choose(0, 5, 20, 50, 100);
4569         const int top = random_choose(1, 20, 125, 500, 999999);
4570         delve(0, ngb_min, ngb_max, connchance, -1, top);
4571     }
4572     else
4573     {
4574         const map_def* layout = _pick_layout(&place.map);
4575         ASSERT(layout);
4576         {
4577             dgn_map_parameters mp(place.orient == MAP_CENTRE
4578                                   ? "central" : "layout");
4579             _build_secondary_vault(layout, false);
4580         }
4581     }
4582 }
4583 
_dgn_item_corpse(const item_spec & ispec,const coord_def where)4584 static int _dgn_item_corpse(const item_spec &ispec, const coord_def where)
4585 {
4586     rng::subgenerator corpse_rng;
4587 
4588     mons_spec mspec(ispec.corpse_monster_spec());
4589     item_def* corpse = nullptr;
4590 
4591     for (int tries = 0; !corpse; tries++)
4592     {
4593         if (tries > 200)
4594             return NON_ITEM;
4595         monster *mon = dgn_place_monster(mspec, coord_def(), true);
4596         if (!mon)
4597             continue;
4598         mon->position = where;
4599         corpse = place_monster_corpse(*mon, true);
4600         // Dismiss the monster we used to place the corpse.
4601         mon->flags |= MF_HARD_RESET;
4602         monster_die(*mon, KILL_DISMISSED, NON_MONSTER, false, true);
4603     }
4604 
4605     if (ispec.props.exists(CORPSE_NEVER_DECAYS))
4606     {
4607         corpse->props[CORPSE_NEVER_DECAYS].get_bool() =
4608             ispec.props[CORPSE_NEVER_DECAYS].get_bool();
4609     }
4610 
4611     if (ispec.base_type == OBJ_CORPSES && ispec.sub_type == CORPSE_SKELETON)
4612         turn_corpse_into_skeleton(*corpse);
4613 
4614     if (ispec.props.exists(MONSTER_HIT_DICE))
4615     {
4616         corpse->props[MONSTER_HIT_DICE].get_short() =
4617             ispec.props[MONSTER_HIT_DICE].get_short();
4618     }
4619 
4620     return corpse->index();
4621 }
4622 
_apply_item_props(item_def & item,const item_spec & spec,bool allow_useless,bool monster)4623 static bool _apply_item_props(item_def &item, const item_spec &spec,
4624                               bool allow_useless, bool monster)
4625 {
4626     const CrawlHashTable props = spec.props;
4627 
4628     if (props.exists("build_themed_book"))
4629     {
4630         string owner = props[RANDBK_OWNER_KEY].get_string();
4631         if (owner == "player")
4632             owner = you.your_name;
4633         const string title = props[RANDBK_TITLE_KEY].get_string();
4634 
4635         vector<spell_type> spells;
4636         CrawlVector spell_list = props[RANDBK_SPELLS_KEY].get_vector();
4637         for (unsigned int i = 0; i < spell_list.size(); ++i)
4638             spells.push_back((spell_type) spell_list[i].get_int());
4639 
4640         spschool disc1 = (spschool)props[RANDBK_DISC1_KEY].get_short();
4641         spschool disc2 = (spschool)props[RANDBK_DISC2_KEY].get_short();
4642         if (disc1 == spschool::none && disc2 == spschool::none)
4643         {
4644             if (spells.size())
4645                 disc1 = matching_book_theme(spells);
4646             else
4647                 disc1 = random_book_theme();
4648             disc2 = random_book_theme();
4649         }
4650         else if (disc2 == spschool::none)
4651             disc2 = disc1;
4652         else
4653             ASSERT(disc1 != spschool::none); // mapdef should've handled this
4654 
4655         int num_spells = props[RANDBK_NSPELLS_KEY].get_short();
4656         if (num_spells < 1)
4657             num_spells = theme_book_size();
4658         const int max_levels = props[RANDBK_SLVLS_KEY].get_short();
4659 
4660         vector<spell_type> chosen_spells;
4661         theme_book_spells(disc1, disc2,
4662                           forced_spell_filter(spells,
4663                                                capped_spell_filter(max_levels)),
4664                           origin_as_god_gift(item), num_spells, chosen_spells);
4665         fixup_randbook_disciplines(disc1, disc2, chosen_spells);
4666         init_book_theme_randart(item, chosen_spells);
4667         name_book_theme_randart(item, disc1, disc2, owner, title);
4668         // XXX: changing the signature of build_themed_book()'s get_discipline
4669         // would allow us to roll much of this ^ into that. possibly clever
4670         // lambdas could let us do it without even changing the signature?
4671     }
4672 
4673     // Wipe item origin to remove "this is a god gift!" from there,
4674     // unless we're dealing with a corpse.
4675     if (!spec.corpselike())
4676         origin_reset(item);
4677     if (is_stackable_item(item) && spec.qty > 0)
4678         item.quantity = spec.qty;
4679 
4680     if (spec.item_special)
4681         item.special = spec.item_special;
4682 
4683     if (spec.plus >= 0 && item.is_type(OBJ_BOOKS, BOOK_MANUAL))
4684     {
4685         item.plus = spec.plus;
4686         item_colour(item);
4687     }
4688 
4689     if (item.base_type == OBJ_RUNES)
4690     {
4691         if (you.runes[item.sub_type])
4692         {
4693             destroy_item(item, true);
4694             return false;
4695         }
4696         item_colour(item);
4697     }
4698 
4699     if (props.exists("useful") && is_useless_item(item, false)
4700         && !allow_useless)
4701     {
4702         destroy_item(item, true);
4703         return false;
4704     }
4705     if (item.base_type == OBJ_WANDS && props.exists("charges"))
4706         item.charges = props["charges"].get_int();
4707     if ((item.base_type == OBJ_WEAPONS || item.base_type == OBJ_ARMOUR
4708          || item.base_type == OBJ_JEWELLERY || item.base_type == OBJ_MISSILES)
4709         && props.exists("plus") && !is_unrandom_artefact(item))
4710     {
4711         item.plus = props["plus"].get_int();
4712         item_set_appearance(item);
4713     }
4714     if (props.exists("ident"))
4715         item.flags |= props["ident"].get_int();
4716     if (props.exists("unobtainable"))
4717         item.flags |= ISFLAG_UNOBTAINABLE;
4718 
4719     if (props.exists("no_pickup"))
4720         item.flags |= ISFLAG_NO_PICKUP;
4721 
4722     if (props.exists("item_tile_name"))
4723         item.props["item_tile_name"] = props["item_tile_name"].get_string();
4724     if (props.exists("worn_tile_name"))
4725         item.props["worn_tile_name"] = props["worn_tile_name"].get_string();
4726     bind_item_tile(item);
4727 
4728     if (!monster)
4729     {
4730         if (props.exists("mimic"))
4731         {
4732             const int chance = props["mimic"];
4733             if (chance > 0 && one_chance_in(chance))
4734                 item.flags |= ISFLAG_MIMIC;
4735         }
4736     }
4737 
4738     return true;
4739 }
4740 
_superb_object_class()4741 static object_class_type _superb_object_class()
4742 {
4743     return random_choose_weighted(
4744             20, OBJ_WEAPONS,
4745             10, OBJ_ARMOUR,
4746             10, OBJ_JEWELLERY,
4747             10, OBJ_BOOKS,
4748             10, OBJ_STAVES,
4749             10, OBJ_MISCELLANY);
4750 }
4751 
dgn_place_item(const item_spec & spec,const coord_def & where,int level)4752 int dgn_place_item(const item_spec &spec,
4753                    const coord_def &where,
4754                    int level)
4755 {
4756     // Dummy object?
4757     if (spec.base_type == OBJ_UNASSIGNED)
4758         return NON_ITEM;
4759 
4760     if (level == INVALID_ABSDEPTH)
4761         level = env.absdepth0;
4762 
4763     object_class_type base_type = spec.base_type;
4764     bool acquire = false;
4765 
4766     if (spec.level >= 0)
4767         level = spec.level;
4768     else
4769     {
4770         bool adjust_type = false;
4771         switch (spec.level)
4772         {
4773         case ISPEC_DAMAGED:
4774         case ISPEC_BAD:
4775         case ISPEC_RANDART:
4776             level = spec.level;
4777             break;
4778         case ISPEC_STAR:
4779             level = 5 + level * 2;
4780             break;
4781         case ISPEC_SUPERB:
4782             adjust_type = true;
4783             level = ISPEC_GOOD_ITEM;
4784             break;
4785         case ISPEC_ACQUIREMENT:
4786             adjust_type = true;
4787             acquire = true;
4788             break;
4789         default:
4790             break;
4791         }
4792 
4793         if (spec.props.exists("mimic") && base_type == OBJ_RANDOM)
4794             base_type = get_random_item_mimic_type();
4795         else if (adjust_type && base_type == OBJ_RANDOM)
4796         {
4797             base_type = acquire ? shuffled_acquirement_classes(false)[0]
4798                                 : _superb_object_class();
4799         }
4800     }
4801 
4802     int useless_tries = 0;
4803 
4804     while (true)
4805     {
4806         int item_made = NON_ITEM;
4807 
4808         if (acquire)
4809         {
4810             item_made = acquirement_create_item(base_type,
4811                                                 spec.acquirement_source,
4812                                                 true, where);
4813         }
4814 
4815         // Both normal item generation and the failed "acquire foo" fallback.
4816         if (item_made == NON_ITEM)
4817         {
4818             if (spec.corpselike())
4819                 item_made = _dgn_item_corpse(spec, where);
4820             else
4821             {
4822                 item_made = items(spec.allow_uniques, base_type,
4823                                   spec.sub_type, level, spec.ego);
4824 
4825                 if (spec.level == ISPEC_MUNDANE)
4826                     squash_plusses(item_made);
4827             }
4828         }
4829 
4830         if (item_made == NON_ITEM || item_made == -1)
4831             return NON_ITEM;
4832         else
4833         {
4834             item_def &item(env.item[item_made]);
4835             item.pos = where;
4836 
4837             if (_apply_item_props(item, spec, (useless_tries >= 10), false))
4838             {
4839                 dprf(DIAG_DNGN, "vault spec: placing %s at %d,%d",
4840                     env.item[item_made].name(DESC_INVENTORY, false, true).c_str(),
4841                     where.x, where.y);
4842                 env.level_map_mask(where) |= MMT_NO_TRAP;
4843                 return item_made;
4844             }
4845             else
4846             {
4847                 // _apply_item_props will not generate a rune you already have,
4848                 // so don't bother looping.
4849                 if (base_type == OBJ_RUNES)
4850                     return NON_ITEM;
4851                 useless_tries++;
4852             }
4853         }
4854 
4855     }
4856 
4857 }
4858 
dgn_place_multiple_items(item_list & list,const coord_def & where)4859 void dgn_place_multiple_items(item_list &list, const coord_def& where)
4860 {
4861     const int size = list.size();
4862     for (int i = 0; i < size; ++i)
4863         dgn_place_item(list.get_item(i), where);
4864 }
4865 
_dgn_place_item_explicit(int index,const coord_def & where,vault_placement & place)4866 static void _dgn_place_item_explicit(int index, const coord_def& where,
4867                                      vault_placement &place)
4868 {
4869     item_list &sitems = place.map.items;
4870 
4871     if ((index < 0 || index >= static_cast<int>(sitems.size())) &&
4872         !crawl_state.game_is_sprint())
4873     {
4874         return;
4875     }
4876 
4877     const item_spec spec = sitems.get_item(index);
4878     dgn_place_item(spec, where);
4879 }
4880 
_give_animated_weapon_ammo(monster & mon)4881 static void _give_animated_weapon_ammo(monster &mon)
4882 {
4883     const item_def *launcher = mon.launcher();
4884     if (!launcher)
4885         return;
4886 
4887     const item_def *missiles = mon.missiles();
4888     if (missiles && missiles->launched_by(*launcher))
4889         return;
4890 
4891     const int ammo_type = fires_ammo_type(*launcher);
4892     const int thing_created = items(false, OBJ_MISSILES, ammo_type, 1);
4893     if (thing_created == NON_ITEM)
4894         return;
4895 
4896     give_specific_item(&mon, thing_created);
4897 }
4898 
_dgn_give_mon_spec_items(mons_spec & mspec,monster * mon)4899 static void _dgn_give_mon_spec_items(mons_spec &mspec, monster *mon)
4900 {
4901     ASSERT(mspec.place.is_valid());
4902 
4903     unwind_var<int> save_speedinc(mon->speed_increment);
4904 
4905     // Get rid of existing equipment.
4906     for (mon_inv_iterator ii(*mon); ii; ++ii)
4907     {
4908         mon->unequip(*ii, false, true);
4909         destroy_item(ii->index(), true);
4910     }
4911 
4912     item_list &list = mspec.items;
4913 
4914     const int size = list.size();
4915     for (int i = 0; i < size; ++i)
4916     {
4917         item_spec spec = list.get_item(i);
4918 
4919         if (spec.base_type == OBJ_UNASSIGNED)
4920             continue;
4921 
4922         // Don't give monster a randart, and don't randomly give
4923         // monster an ego item.
4924         if (spec.base_type == OBJ_ARMOUR || spec.base_type == OBJ_WEAPONS
4925             || spec.base_type == OBJ_MISSILES)
4926         {
4927             spec.allow_uniques = 0;
4928             if (spec.ego == 0)
4929                 spec.ego = SP_FORBID_EGO;
4930         }
4931 
4932         int item_level = mspec.place.absdepth();
4933 
4934         if (spec.level >= 0)
4935             item_level = spec.level;
4936         else
4937         {
4938             // TODO: merge this with the equivalent switch in dgn_place_item,
4939             // and maybe even handle ISPEC_ACQUIREMENT.
4940             switch (spec.level)
4941             {
4942             case ISPEC_STAR:
4943                 item_level = 5 + item_level * 2;
4944                 break;
4945             case ISPEC_SUPERB:
4946                 item_level = ISPEC_GOOD_ITEM;
4947                 break;
4948             case ISPEC_DAMAGED:
4949             case ISPEC_BAD:
4950             case ISPEC_RANDART:
4951                 item_level = spec.level;
4952                 break;
4953             }
4954         }
4955 
4956         for (int useless_tries = 0; true; useless_tries++)
4957         {
4958             int item_made;
4959 
4960             if (spec.corpselike())
4961                 item_made = _dgn_item_corpse(spec, mon->pos());
4962             else
4963             {
4964                 item_made = items(spec.allow_uniques, spec.base_type,
4965                                   spec.sub_type, item_level,
4966                                   spec.ego);
4967 
4968                 if (spec.level == ISPEC_MUNDANE)
4969                     squash_plusses(item_made);
4970             }
4971 
4972             if (!(item_made == NON_ITEM || item_made == -1))
4973             {
4974                 item_def &item(env.item[item_made]);
4975 
4976                 if (_apply_item_props(item, spec, (useless_tries >= 10), true))
4977                 {
4978                     // Mark items on summoned monsters as such.
4979                     if (mspec.abjuration_duration != 0)
4980                         item.flags |= ISFLAG_SUMMONED;
4981 
4982                     if (!mon->pickup_item(item, false, true))
4983                         destroy_item(item_made, true);
4984                     break;
4985                 }
4986             }
4987         }
4988     }
4989 
4990     // Pre-wield ranged weapons.
4991     if (mon->inv[MSLOT_WEAPON] == NON_ITEM
4992         && mon->inv[MSLOT_ALT_WEAPON] != NON_ITEM)
4993     {
4994         mon->swap_weapons(MB_FALSE);
4995     }
4996 
4997     // Make dancing launchers less pathetic.
4998     // XX this should be done on creation, not placement?
4999     if (mons_class_is_animated_weapon(mon->type))
5000         _give_animated_weapon_ammo(*mon);
5001 }
5002 
_should_veto_unique(monster_type type)5003 static bool _should_veto_unique(monster_type type)
5004 {
5005     // Already generated.
5006     return mons_is_unique(type) && you.unique_creatures[type];
5007 }
5008 
dgn_place_monster(mons_spec & mspec,coord_def where,bool force_pos,bool generate_awake,bool patrolling)5009 monster* dgn_place_monster(mons_spec &mspec, coord_def where,
5010                            bool force_pos, bool generate_awake, bool patrolling)
5011 {
5012 #if TAG_MAJOR_VERSION == 34
5013     if ((int)mspec.type == -1) // or rebuild the des cache
5014         return 0;
5015 #endif
5016     if (mspec.type == MONS_NO_MONSTER)
5017         return 0;
5018 
5019     monster_type type = mspec.type;
5020     const bool m_generate_awake = (generate_awake || mspec.generate_awake);
5021     const bool m_patrolling     = (patrolling || mspec.patrolling);
5022     const bool m_band           = mspec.band;
5023 
5024     if (!mspec.place.is_valid())
5025         mspec.place = level_id::current();
5026     bool chose_ood = false;
5027     bool fuzz_ood  = true;
5028     const int starting_depth = mspec.place.depth;
5029 
5030     if (type == RANDOM_SUPER_OOD || type == RANDOM_MODERATE_OOD)
5031     {
5032         // don't do OOD depth adjustment for portal branches
5033         if (brdepth[mspec.place.branch] > 1)
5034         {
5035             if (type == RANDOM_SUPER_OOD)
5036                 mspec.place.depth = mspec.place.depth * 2 + 4; // TODO: why?
5037             else if (type == RANDOM_MODERATE_OOD)
5038                 mspec.place.depth += 5;
5039 
5040             if (mspec.place.branch == BRANCH_DUNGEON && starting_depth <= 8)
5041                 mspec.place.depth = min(mspec.place.depth, starting_depth * 2);
5042         }
5043         fuzz_ood = false;
5044         type = RANDOM_MONSTER;
5045     }
5046 
5047     if (type < NUM_MONSTERS)
5048     {
5049         // Don't place a unique monster a second time.
5050         // (Boris is handled specially.)
5051         if (_should_veto_unique(type)
5052             && !crawl_state.game_is_arena())
5053         {
5054             return 0;
5055         }
5056 
5057         const monster_type montype = mons_class_is_zombified(type)
5058                                                          ? mspec.monbase
5059                                                          : type;
5060 
5061         const habitat_type habitat = mons_class_primary_habitat(montype);
5062 
5063         if (in_bounds(where) && !monster_habitable_grid(montype, env.grid(where)))
5064             dungeon_terrain_changed(where, habitat2grid(habitat));
5065     }
5066 
5067     if (type == RANDOM_MONSTER)
5068     {
5069         if (mons_class_is_zombified(mspec.monbase))
5070             type = pick_local_zombifiable_monster(mspec.place, mspec.monbase, coord_def());
5071         else
5072         {
5073             level_id place = mspec.place;
5074             type = pick_random_monster(mspec.place, mspec.monbase, &place,
5075                                        fuzz_ood);
5076             if (place.depth > starting_depth + 5)
5077                 chose_ood = true;
5078         }
5079         if (!type)
5080             type = RANDOM_MONSTER;
5081     }
5082 
5083     mgen_data mg(type);
5084 
5085     mg.behaviour = (m_generate_awake) ? BEH_WANDER : BEH_SLEEP;
5086     switch (mspec.attitude)
5087     {
5088     case ATT_FRIENDLY:
5089         mg.behaviour = BEH_FRIENDLY;
5090         break;
5091     case ATT_NEUTRAL:
5092         mg.behaviour = BEH_NEUTRAL;
5093         break;
5094     case ATT_GOOD_NEUTRAL:
5095         mg.behaviour = BEH_GOOD_NEUTRAL;
5096         break;
5097     case ATT_STRICT_NEUTRAL:
5098         mg.behaviour = BEH_STRICT_NEUTRAL;
5099         break;
5100     default:
5101         break;
5102     }
5103     mg.base_type = mspec.monbase;
5104     mg.colour    = mspec.colour;
5105 
5106     if (mspec.god != GOD_NO_GOD)
5107         mg.god   = mspec.god;
5108 
5109     mg.mname     = mspec.monname;
5110     mg.hd        = mspec.hd;
5111     mg.hp        = mspec.hp;
5112     mg.props     = mspec.props;
5113 
5114     if (mg.props.exists(MAP_KEY))
5115         mg.xp_tracking = XP_VAULT;
5116 
5117     // Marking monsters as summoned
5118     mg.abjuration_duration = mspec.abjuration_duration;
5119     mg.summon_type         = mspec.summon_type;
5120     mg.non_actor_summoner  = mspec.non_actor_summoner;
5121 
5122     if (mg.colour == COLOUR_UNDEF)
5123         mg.colour = random_monster_colour();
5124 
5125     if (!force_pos && actor_at(where)
5126         && (mg.cls < NUM_MONSTERS || needs_resolution(mg.cls)))
5127     {
5128         const monster_type habitat_target =
5129             mg.cls == RANDOM_MONSTER ? MONS_BAT : mg.cls;
5130         where = find_newmons_square_contiguous(habitat_target, where, 0);
5131     }
5132 
5133     mg.pos = where;
5134 
5135     if (mons_class_is_zombified(mg.base_type))
5136     {
5137         if (mons_class_is_zombified(mg.cls))
5138             mg.base_type = MONS_NO_MONSTER;
5139         else
5140             swap(mg.base_type, mg.cls);
5141     }
5142 
5143     if (m_patrolling)
5144         mg.flags |= MG_PATROLLING;
5145 
5146     if (m_band)
5147         mg.flags |= MG_PERMIT_BANDS;
5148 
5149     // Store any extra flags here.
5150     mg.extra_flags |= mspec.extra_monster_flags;
5151 
5152     monster *mons = place_monster(mg, true, force_pos && where.origin());
5153     if (!mons)
5154         return 0;
5155 
5156     // Spells before items, so e.g. simulacrum casters can be given chunks.
5157     // add custom spell prop for spell display
5158     // XXX limited to the actual spellbook, won't display all
5159     // spellbooks available for vault monsters
5160     if (mspec.explicit_spells)
5161     {
5162         mons->spells = mspec.spells[random2(mspec.spells.size())];
5163         mons->props[CUSTOM_SPELLS_KEY] = true;
5164     }
5165 
5166     // this prop is mainly for the seed explorer.
5167     // not a great or complete measure of monster danger: the builder can roll
5168     // on the low side of the ood range, and vaults don't get this set.
5169     if (chose_ood)
5170         mons->props[MON_OOD_KEY].get_bool() = true;
5171 
5172     if (!mspec.items.empty())
5173         _dgn_give_mon_spec_items(mspec, mons);
5174 
5175     if (mspec.props.exists("monster_tile"))
5176     {
5177         mons->props["monster_tile"] =
5178             mspec.props["monster_tile"].get_short();
5179     }
5180     if (mspec.props.exists("monster_tile_name"))
5181     {
5182         mons->props["monster_tile_name"].get_string() =
5183             mspec.props["monster_tile_name"].get_string();
5184     }
5185 
5186     if (mspec.props.exists("always_corpse"))
5187         mons->props["always_corpse"] = true;
5188 
5189     if (mspec.props.exists(NEVER_CORPSE_KEY))
5190         mons->props[NEVER_CORPSE_KEY] = true;
5191 
5192     if (mspec.props.exists("dbname"))
5193         mons->props["dbname"].get_string() = mspec.props["dbname"].get_string();
5194 
5195     // These are applied earlier to prevent issues with renamed monsters
5196     // and "<monster> comes into view" (see delay.cc:_monster_warning).
5197     //mons->flags |= mspec.extra_monster_flags;
5198 
5199     // Monsters with gods set by the spec aren't god gifts
5200     // unless they have the "god_gift" tag. place_monster(),
5201     // by default, marks any monsters with gods as god gifts,
5202     // so unmark them here.
5203     if (mspec.god != GOD_NO_GOD && !mspec.god_gift)
5204         mons->flags &= ~MF_GOD_GIFT;
5205 
5206     if (mons->is_priest() && mons->god == GOD_NO_GOD)
5207         mons->god = GOD_NAMELESS;
5208 
5209     if (mons_class_is_animated_weapon(mons->type))
5210     {
5211         item_def *wpn = mons->mslot_item(MSLOT_WEAPON);
5212         ASSERT(wpn);
5213         if (mons->type == MONS_DANCING_WEAPON)
5214             mons->ghost->init_dancing_weapon(*wpn, 100);
5215         else if (mons->type == MONS_SPECTRAL_WEAPON)
5216             mons->ghost->init_spectral_weapon(*wpn);
5217         mons->ghost_demon_init();
5218     }
5219 
5220     for (const mon_enchant &ench : mspec.ench)
5221         mons->add_ench(ench);
5222 
5223     return mons;
5224 }
5225 
_dgn_place_monster(const vault_placement & place,mons_spec & mspec,const coord_def & where)5226 static bool _dgn_place_monster(const vault_placement &place, mons_spec &mspec,
5227                                const coord_def& where)
5228 {
5229     const bool generate_awake
5230         = mspec.generate_awake
5231           || place.map.has_tag("generate_awake")
5232           || player_in_branch(BRANCH_GAUNTLET);
5233 
5234     const bool patrolling
5235         = mspec.patrolling || place.map.has_tag("patrolling");
5236 
5237     mspec.props[MAP_KEY].get_string() = place.map_name_at(where);
5238     return dgn_place_monster(mspec, where, false, generate_awake, patrolling);
5239 }
5240 
_dgn_place_one_monster(const vault_placement & place,mons_list & mons,const coord_def & where)5241 static bool _dgn_place_one_monster(const vault_placement &place,
5242                                    mons_list &mons, const coord_def& where)
5243 {
5244     for (int i = 0, size = mons.size(); i < size; ++i)
5245     {
5246         mons_spec spec = mons.get_monster(i);
5247         if (_dgn_place_monster(place, spec, where))
5248             return true;
5249     }
5250     return false;
5251 }
5252 
5253 /* "Oddball grids" are handled in _vault_grid. */
_glyph_to_feat(int glyph)5254 static dungeon_feature_type _glyph_to_feat(int glyph)
5255 {
5256     return (glyph == 'x') ? DNGN_ROCK_WALL :
5257            (glyph == 'X') ? DNGN_PERMAROCK_WALL :
5258            (glyph == 'c') ? DNGN_STONE_WALL :
5259            (glyph == 'v') ? DNGN_METAL_WALL :
5260            (glyph == 'b') ? DNGN_CRYSTAL_WALL :
5261            (glyph == 'm') ? DNGN_CLEAR_ROCK_WALL :
5262            (glyph == 'n') ? DNGN_CLEAR_STONE_WALL :
5263            (glyph == 'o') ? DNGN_CLEAR_PERMAROCK_WALL :
5264            // We make 't' correspond to the right tree type by branch.
5265            (glyph == 't') ?
5266                player_in_branch(BRANCH_SWAMP)          ? DNGN_MANGROVE :
5267                player_in_branch(BRANCH_ABYSS)
5268                || player_in_branch(BRANCH_PANDEMONIUM) ? DNGN_DEMONIC_TREE
5269                                                        : DNGN_TREE :
5270            (glyph == '+') ? DNGN_CLOSED_DOOR :
5271            (glyph == '=') ? DNGN_RUNED_CLEAR_DOOR :
5272            (glyph == 'w') ? DNGN_DEEP_WATER :
5273            (glyph == 'W') ? DNGN_SHALLOW_WATER :
5274            (glyph == 'l') ? DNGN_LAVA :
5275            (glyph == '>') ? DNGN_ESCAPE_HATCH_DOWN :
5276            (glyph == '<') ? DNGN_ESCAPE_HATCH_UP :
5277            (glyph == '}') ? DNGN_STONE_STAIRS_DOWN_I :
5278            (glyph == '{') ? DNGN_STONE_STAIRS_UP_I :
5279            (glyph == ')') ? DNGN_STONE_STAIRS_DOWN_II :
5280            (glyph == '(') ? DNGN_STONE_STAIRS_UP_II :
5281            (glyph == ']') ? DNGN_STONE_STAIRS_DOWN_III :
5282            (glyph == '[') ? DNGN_STONE_STAIRS_UP_III :
5283            (glyph == 'A') ? DNGN_STONE_ARCH :
5284            (glyph == 'C') ? _pick_an_altar() :   // f(x) elsewhere {dlb}
5285            (glyph == 'I') ? DNGN_ORCISH_IDOL :
5286            (glyph == 'G') ? DNGN_GRANITE_STATUE :
5287            (glyph == 'T') ? DNGN_FOUNTAIN_BLUE :
5288            (glyph == 'U') ? DNGN_FOUNTAIN_SPARKLING :
5289            (glyph == 'V') ? DNGN_DRY_FOUNTAIN :
5290            (glyph == 'Y') ? DNGN_FOUNTAIN_BLOOD :
5291            (glyph == '\0')? DNGN_ROCK_WALL
5292                           : DNGN_FLOOR; // includes everything else
5293 }
5294 
map_feature_at(map_def * map,const coord_def & c,int rawfeat)5295 dungeon_feature_type map_feature_at(map_def *map, const coord_def &c,
5296                                     int rawfeat)
5297 {
5298     if (rawfeat == -1)
5299         rawfeat = map->glyph_at(c);
5300 
5301     if (rawfeat == ' ')
5302         return NUM_FEATURES;
5303 
5304     keyed_mapspec *mapsp = map? map->mapspec_at(c) : nullptr;
5305     if (mapsp)
5306     {
5307         feature_spec f = mapsp->get_feat();
5308         if (f.trap)
5309         {
5310             if (f.trap->tr_type >= NUM_TRAPS)
5311                 return DNGN_FLOOR;
5312             else
5313                 return trap_feature(f.trap->tr_type);
5314         }
5315         else if (f.feat >= 0)
5316             return static_cast<dungeon_feature_type>(f.feat);
5317         else if (f.glyph >= 0)
5318             return map_feature_at(nullptr, c, f.glyph);
5319         else if (f.shop)
5320             return DNGN_ENTER_SHOP;
5321 
5322         return DNGN_FLOOR;
5323     }
5324 
5325     return _glyph_to_feat(rawfeat);
5326 }
5327 
_vault_grid_mapspec(vault_placement & place,const coord_def & where,keyed_mapspec & mapsp)5328 static void _vault_grid_mapspec(vault_placement &place, const coord_def &where,
5329                                 keyed_mapspec& mapsp)
5330 {
5331     const feature_spec f = mapsp.get_feat();
5332     if (f.trap)
5333         _place_specific_trap(where, f.trap.get(), 0);
5334     else if (f.feat >= 0)
5335         env.grid(where) = static_cast<dungeon_feature_type>(f.feat);
5336     else if (f.glyph >= 0)
5337         _vault_grid_glyph(place, where, f.glyph);
5338     else if (f.shop)
5339         place_spec_shop(where, *f.shop);
5340     else
5341         env.grid(where) = DNGN_FLOOR;
5342 
5343     if (f.mimic > 0 && one_chance_in(f.mimic))
5344     {
5345         ASSERT(feat_is_mimicable(env.grid(where), false));
5346         env.level_map_mask(where) |= MMT_MIMIC;
5347     }
5348     else if (f.no_mimic)
5349         env.level_map_mask(where) |= MMT_NO_MIMIC;
5350 
5351     item_list &items = mapsp.get_items();
5352     dgn_place_multiple_items(items, where);
5353 }
5354 
_vault_grid_glyph(vault_placement & place,const coord_def & where,int vgrid)5355 static void _vault_grid_glyph(vault_placement &place, const coord_def& where,
5356                               int vgrid)
5357 {
5358     // First, set base tile for grids {dlb}:
5359     if (vgrid != -1)
5360         env.grid(where) = _glyph_to_feat(vgrid);
5361 
5362     if (feat_is_altar(env.grid(where))
5363         && is_unavailable_god(feat_altar_god(env.grid(where))))
5364     {
5365         env.grid(where) = DNGN_FLOOR;
5366     }
5367 
5368     // then, handle oddball grids {dlb}:
5369     switch (vgrid)
5370     {
5371     case '@':
5372     case '=':
5373     case '+':
5374         if (_map_feat_is_on_edge(place, where))
5375             place.exits.push_back(where);
5376         break;
5377     case '^':
5378         place_specific_trap(where, TRAP_RANDOM);
5379         break;
5380     case '~':
5381         place_specific_trap(where, random_vault_trap());
5382         break;
5383     case 'B':
5384         env.grid(where) = _pick_temple_altar();
5385         break;
5386     }
5387 
5388     // Then, handle grids that place "stuff" {dlb}:
5389     if (vgrid == '$' || vgrid == '%' || vgrid == '*' || vgrid == '|')
5390     {
5391         int item_made = NON_ITEM;
5392         object_class_type which_class = OBJ_RANDOM;
5393         uint8_t which_type = OBJ_RANDOM;
5394         int which_depth = env.absdepth0;
5395 
5396         if (vgrid == '$')
5397             which_class = OBJ_GOLD;
5398         else if (vgrid == '|')
5399         {
5400             which_class = _superb_object_class();
5401             which_depth = ISPEC_GOOD_ITEM;
5402         }
5403         else if (vgrid == '*')
5404             which_depth = 5 + which_depth * 2;
5405 
5406         item_made = items(true, which_class, which_type, which_depth);
5407         if (item_made != NON_ITEM)
5408         {
5409             env.item[item_made].pos = where;
5410             env.level_map_mask(where) |= MMT_NO_TRAP;
5411             dprf(DIAG_DNGN, "vault grid: placing %s at %d,%d",
5412                 env.item[item_made].name(DESC_PLAIN, false, true).c_str(),
5413                 env.item[item_made].pos.x, env.item[item_made].pos.y);
5414         }
5415     }
5416 
5417     // defghijk - items
5418     if (map_def::valid_item_array_glyph(vgrid))
5419     {
5420         int slot = map_def::item_array_glyph_to_slot(vgrid);
5421         _dgn_place_item_explicit(slot, where, place);
5422     }
5423 }
5424 
_vault_grid(vault_placement & place,int vgrid,const coord_def & where,keyed_mapspec * mapsp)5425 static void _vault_grid(vault_placement &place,
5426                         int vgrid,
5427                         const coord_def& where,
5428                         keyed_mapspec *mapsp)
5429 {
5430     if (mapsp && mapsp->replaces_glyph())
5431         _vault_grid_mapspec(place, where, *mapsp);
5432     else
5433         _vault_grid_glyph(place, where, vgrid);
5434 
5435     if (cell_is_solid(where))
5436         delete_cloud(where);
5437 }
5438 
_vault_grid_glyph_mons(vault_placement & place,const coord_def & where,int vgrid)5439 static void _vault_grid_glyph_mons(vault_placement &place,
5440                                    const coord_def &where,
5441                                    int vgrid)
5442 {
5443     // Handle grids that place monsters {dlb}:
5444     if (map_def::valid_monster_glyph(vgrid))
5445     {
5446         mons_spec ms(RANDOM_MONSTER);
5447 
5448         if (vgrid == '8')
5449             ms.type = RANDOM_SUPER_OOD;
5450         else if (vgrid == '9')
5451             ms.type = RANDOM_MODERATE_OOD;
5452         else if (vgrid != '0')
5453         {
5454             int slot = map_def::monster_array_glyph_to_slot(vgrid);
5455             ms = place.map.mons.get_monster(slot);
5456             monster_type mt = ms.type;
5457             // Is a map for a specific place trying to place a unique which
5458             // somehow already got created?
5459             if (!place.map.place.empty()
5460                 && !invalid_monster_type(mt)
5461                 && mons_is_unique(mt)
5462                 && you.unique_creatures[mt])
5463             {
5464                 mprf(MSGCH_ERROR, "ERROR: %s already generated somewhere "
5465                      "else; please file a bug report.",
5466                      mons_type_name(mt, DESC_THE).c_str());
5467                 // Force it to be generated anyway.
5468                 you.unique_creatures.set(mt, false);
5469             }
5470         }
5471 
5472         _dgn_place_monster(place, ms, where);
5473     }
5474 }
5475 
_vault_grid_mapspec_mons(vault_placement & place,const coord_def & where,keyed_mapspec & mapsp)5476 static void _vault_grid_mapspec_mons(vault_placement &place,
5477                                      const coord_def &where,
5478                                      keyed_mapspec& mapsp)
5479 {
5480     const feature_spec f = mapsp.get_feat();
5481     if (f.glyph >= 0)
5482         _vault_grid_glyph_mons(place, where, f.glyph);
5483     mons_list &mons = mapsp.get_monsters();
5484     _dgn_place_one_monster(place, mons, where);
5485 }
5486 
_vault_grid_mons(vault_placement & place,int vgrid,const coord_def & where,keyed_mapspec * mapsp)5487 static void _vault_grid_mons(vault_placement &place,
5488                         int vgrid,
5489                         const coord_def& where,
5490                         keyed_mapspec *mapsp)
5491 {
5492     if (mapsp && mapsp->replaces_glyph())
5493         _vault_grid_mapspec_mons(place, where, *mapsp);
5494     else
5495         _vault_grid_glyph_mons(place, where, vgrid);
5496 }
5497 
5498 // Only used for Slime:$ where it will turn the stone walls into floor once the
5499 // Royal Jelly has been killed, or at 6* Jiyva piety.
seen_destroy_feat(dungeon_feature_type old_feat)5500 bool seen_destroy_feat(dungeon_feature_type old_feat)
5501 {
5502     coord_def p1(0, 0);
5503     coord_def p2(GXM - 1, GYM - 1);
5504 
5505     bool seen = false;
5506     for (rectangle_iterator ri(p1, p2); ri; ++ri)
5507     {
5508         if (orig_terrain(*ri) == old_feat)
5509         {
5510             destroy_wall(*ri);
5511             if (you.see_cell(*ri))
5512                 seen = true;
5513         }
5514     }
5515 
5516     return seen;
5517 }
5518 
dgn_replace_area(int sx,int sy,int ex,int ey,dungeon_feature_type replace,dungeon_feature_type feature,unsigned mmask,bool needs_update)5519 void dgn_replace_area(int sx, int sy, int ex, int ey,
5520                       dungeon_feature_type replace,
5521                       dungeon_feature_type feature,
5522                       unsigned mmask, bool needs_update)
5523 {
5524     dgn_replace_area(coord_def(sx, sy), coord_def(ex, ey),
5525                       replace, feature, mmask, needs_update);
5526 }
5527 
dgn_replace_area(const coord_def & p1,const coord_def & p2,dungeon_feature_type replace,dungeon_feature_type feature,uint32_t mapmask,bool needs_update)5528 void dgn_replace_area(const coord_def& p1, const coord_def& p2,
5529                        dungeon_feature_type replace,
5530                        dungeon_feature_type feature, uint32_t mapmask,
5531                        bool needs_update)
5532 {
5533     for (rectangle_iterator ri(p1, p2); ri; ++ri)
5534     {
5535         if (env.grid(*ri) == replace && !map_masked(*ri, mapmask))
5536         {
5537             env.grid(*ri) = feature;
5538             if (needs_update && env.map_knowledge(*ri).seen())
5539             {
5540                 env.map_knowledge(*ri).set_feature(feature, 0,
5541                                                    get_trap_type(*ri));
5542 #ifdef USE_TILE
5543                 tile_env.bk_bg(*ri) = feature;
5544 #endif
5545             }
5546         }
5547     }
5548 }
5549 
map_masked(const coord_def & c,unsigned mask)5550 bool map_masked(const coord_def &c, unsigned mask)
5551 {
5552     return mask && (env.level_map_mask(c) & mask);
5553 }
5554 
5555 struct coord_comparator
5556 {
5557     coord_def target;
coord_comparatorcoord_comparator5558     coord_comparator(const coord_def &t) : target(t) { }
5559 
distcoord_comparator5560     static int dist(const coord_def &a, const coord_def &b)
5561     {
5562         const coord_def del = a - b;
5563         return abs(del.x) + abs(del.y);
5564     }
5565 
operator ()coord_comparator5566     bool operator () (const coord_def &a, const coord_def &b) const
5567     {
5568         return dist(a, target) < dist(b, target);
5569     }
5570 };
5571 
5572 typedef set<coord_def, coord_comparator> coord_set;
5573 
_jtd_init_surrounds(coord_set & coords,uint32_t mapmask,const coord_def & c)5574 static void _jtd_init_surrounds(coord_set &coords, uint32_t mapmask,
5575                                 const coord_def &c)
5576 {
5577     vector<coord_def> cur;
5578     for (orth_adjacent_iterator ai(c); ai; ++ai)
5579     {
5580         if (!in_bounds(*ai) || travel_point_distance[ai->x][ai->y]
5581             || map_masked(*ai, mapmask))
5582         {
5583             continue;
5584         }
5585         cur.insert(cur.begin() + random2(cur.size()), *ai);
5586     }
5587     for (auto cc : cur)
5588     {
5589         coords.insert(cc);
5590 
5591         const coord_def dp = cc - c;
5592         travel_point_distance[cc.x][cc.y] = (-dp.x + 2) * 4 + (-dp.y + 2);
5593     }
5594 }
5595 
5596 // Resets travel_point_distance
dgn_join_the_dots_pathfind(const coord_def & from,const coord_def & to,uint32_t mapmask)5597 vector<coord_def> dgn_join_the_dots_pathfind(const coord_def &from,
5598                                              const coord_def &to,
5599                                              uint32_t mapmask)
5600 {
5601     memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
5602     const coord_comparator comp(to);
5603     coord_set coords(comp);
5604 
5605     vector<coord_def> path;
5606     coord_def curr = from;
5607     while (true)
5608     {
5609         int &tpd = travel_point_distance[curr.x][curr.y];
5610         tpd = !tpd? -1000 : -tpd;
5611 
5612         if (curr == to)
5613             break;
5614 
5615         _jtd_init_surrounds(coords, mapmask, curr);
5616 
5617         if (coords.empty())
5618             break;
5619 
5620         curr = *coords.begin();
5621         coords.erase(coords.begin());
5622     }
5623 
5624     if (curr != to)
5625         return path;
5626 
5627     while (curr != from)
5628     {
5629         if (!map_masked(curr, mapmask))
5630             path.push_back(curr);
5631 
5632         const int dist = travel_point_distance[curr.x][curr.y];
5633         ASSERT(dist < 0);
5634         ASSERT(dist != -1000);
5635         curr += coord_def(-dist / 4 - 2, (-dist % 4) - 2);
5636     }
5637     if (!map_masked(curr, mapmask))
5638         path.push_back(curr);
5639 
5640     return path;
5641 }
5642 
join_the_dots(const coord_def & from,const coord_def & to,uint32_t mapmask,bool (* overwriteable)(dungeon_feature_type))5643 bool join_the_dots(const coord_def &from, const coord_def &to,
5644                    uint32_t mapmask,
5645                    bool (*overwriteable)(dungeon_feature_type))
5646 {
5647     if (!overwriteable)
5648         overwriteable = _feat_is_wall_floor_liquid;
5649 
5650     const vector<coord_def> path =
5651         dgn_join_the_dots_pathfind(from, to, mapmask);
5652 
5653     for (auto c : path)
5654     {
5655         auto feat = env.grid(c);
5656         if (!map_masked(c, mapmask) && overwriteable(feat))
5657         {
5658             env.grid(c) = DNGN_FLOOR;
5659             dgn_height_set_at(c);
5660         }
5661         else
5662         {
5663             dprf(DIAG_DNGN, "Failed to path through %s at (%d;%d) for connectivity",
5664                  get_feature_def(feat).name, c.x, c.y);
5665         }
5666     }
5667 
5668     return !path.empty() || from == to;
5669 }
5670 
_pick_temple_altar()5671 static dungeon_feature_type _pick_temple_altar()
5672 {
5673     if (_temple_altar_list.empty())
5674     {
5675         if (_current_temple_hash != nullptr)
5676         {
5677             // Altar god doesn't matter, setting up the whole machinery would
5678             // be too much work.
5679             if (crawl_state.map_stat_gen || crawl_state.obj_stat_gen)
5680                 return DNGN_ALTAR_XOM;
5681 
5682             mprf(MSGCH_ERROR, "Ran out of altars for temple!");
5683             return DNGN_FLOOR;
5684         }
5685         // Randomized altar list for mini-temples.
5686         _temple_altar_list = temple_god_list();
5687         shuffle_array(_temple_altar_list);
5688     }
5689 
5690     const god_type god = _temple_altar_list.back();
5691 
5692     _temple_altar_list.pop_back();
5693 
5694     return altar_for_god(god);
5695 }
5696 
5697 //jmf: Generate altar based on where you are, or possibly randomly.
_pick_an_altar()5698 static dungeon_feature_type _pick_an_altar()
5699 {
5700     god_type god;
5701 
5702     // No extra altars in Temple.
5703     if (player_in_branch(BRANCH_TEMPLE))
5704         god = GOD_NO_GOD;
5705 
5706     // Xom can turn up anywhere
5707     else if (one_chance_in(27))
5708         god = GOD_XOM;
5709     else
5710     {
5711         switch (you.where_are_you)
5712         {
5713         case BRANCH_CRYPT:
5714             god = random_choose(GOD_KIKUBAAQUDGHA, GOD_YREDELEMNUL);
5715             break;
5716 
5717         case BRANCH_ORC: // There are a few heretics
5718             if (one_chance_in(5))
5719                 god = random_choose(GOD_TROG, GOD_MAKHLEB, GOD_VEHUMET);
5720             else
5721                 god = GOD_BEOGH;
5722             break;
5723 
5724         case BRANCH_ELF: // magic gods
5725             god = random_choose(GOD_VEHUMET, GOD_SIF_MUNA, GOD_KIKUBAAQUDGHA);
5726             break;
5727 
5728         case BRANCH_SLIME:
5729             god = GOD_JIYVA;
5730             break;
5731 
5732         case BRANCH_TOMB:
5733             god = GOD_KIKUBAAQUDGHA;
5734             break;
5735 
5736         case BRANCH_VESTIBULE:
5737         case BRANCH_DIS:
5738         case BRANCH_GEHENNA:
5739         case BRANCH_COCYTUS:
5740         case BRANCH_TARTARUS:
5741         case BRANCH_PANDEMONIUM: // particularly destructive / elemental gods
5742             if (one_chance_in(3))
5743             {
5744                 god = random_choose(GOD_KIKUBAAQUDGHA, GOD_NEMELEX_XOBEH,
5745                                     GOD_QAZLAL, GOD_VEHUMET);
5746             }
5747             else
5748                 god = GOD_MAKHLEB;
5749             break;
5750 
5751         default: // Any god (with exceptions).
5752             do
5753             {
5754                 god = random_god();
5755             }
5756             while (god == GOD_LUGONU || god == GOD_BEOGH || god == GOD_JIYVA);
5757             break;
5758         }
5759     }
5760 
5761     if (is_unavailable_god(god))
5762         god = GOD_NO_GOD;
5763 
5764     return altar_for_god(god);
5765 }
5766 
_shop_sells_antiques(shop_type type)5767 static bool _shop_sells_antiques(shop_type type)
5768 {
5769     return type == SHOP_WEAPON_ANTIQUE
5770             || type == SHOP_ARMOUR_ANTIQUE
5771             || type == SHOP_GENERAL_ANTIQUE;
5772 }
5773 
place_spec_shop(const coord_def & where,shop_type force_type)5774 void place_spec_shop(const coord_def& where, shop_type force_type)
5775 {
5776     shop_spec spec(force_type);
5777     place_spec_shop(where, spec);
5778 }
5779 
greed_for_shop_type(shop_type shop,int level_number)5780 int greed_for_shop_type(shop_type shop, int level_number)
5781 {
5782     if (_shop_sells_antiques(shop))
5783     {
5784         const int rand = random2avg(19, 2);
5785         return 15 + rand + random2(level_number);
5786     }
5787     const int rand = random2(5);
5788     return 10 + rand + random2(level_number / 2);
5789 }
5790 
5791 /**
5792  * How greedy should a given shop be? (Applies a multiplier to prices.)
5793  *
5794  * @param type              The type of the shop. (E.g. SHOP_WEAPON_ANTIQUE.)
5795  * @param level_number      The depth in which the shop is placed.
5796  * @param spec_greed        An override for the greed, based on a vault
5797  *                          specification; if not -1, will override other
5798  *                          calculations & give a debug message.
5799  * @return                  The greed for the shop.
5800  */
_shop_greed(shop_type type,int level_number,int spec_greed)5801 static int _shop_greed(shop_type type, int level_number, int spec_greed)
5802 {
5803     const int base_greed = greed_for_shop_type(type, level_number);
5804     int adj_greed = base_greed;
5805 
5806     // Allow bargains in bazaars, prices randomly between 60% and 95%.
5807     if (player_in_branch(BRANCH_BAZAAR))
5808     {
5809         // divided by 20, so each is 5% of original price
5810         // 12-19 = 60-95%, per above
5811         const int factor = random2(8) + 12;
5812 
5813         dprf(DIAG_DNGN, "Shop type %d: original greed = %d, factor = %d,"
5814              " discount = %d%%.",
5815              type, base_greed, factor, (20-factor)*5);
5816 
5817         adj_greed = factor * adj_greed / 20;
5818     }
5819 
5820     if (spec_greed != -1)
5821     {
5822         dprf(DIAG_DNGN, "Shop spec overrides greed: %d becomes %d.",
5823              adj_greed, spec_greed);
5824         return spec_greed;
5825     }
5826 
5827     return adj_greed;
5828 }
5829 
5830 /**
5831  * How many items should be placed in a given shop?
5832  *
5833  * @param spec              A vault shop spec; may override default results.
5834  * @return                  The number of items the shop should be generated
5835  *                          to hold.
5836  */
_shop_num_items(const shop_spec & spec)5837 static int _shop_num_items(const shop_spec &spec)
5838 {
5839     if (spec.num_items != -1)
5840     {
5841         dprf(DIAG_DNGN, "Shop spec overrides number of items to %d.",
5842              spec.num_items);
5843         return spec.num_items;
5844     }
5845 
5846     if (spec.use_all && !spec.items.empty())
5847     {
5848         dprf(DIAG_DNGN, "Shop spec wants all items placed: %u of them.",
5849              (unsigned int)spec.items.size());
5850         return (int) spec.items.size();
5851     }
5852 
5853     return 5 + random2avg(12, 3);
5854 }
5855 
5856 /**
5857  * What 'level' should an item from the given shop type be generated at?
5858  *
5859  * @param shop_type_        The type of shop the item is to be sold from.
5860  * @param level_number      The depth of the level the shop is on.
5861  * @return                  An "item level" to generate an item at.
5862  */
_choose_shop_item_level(shop_type shop_type_,int level_number)5863 static int _choose_shop_item_level(shop_type shop_type_, int level_number)
5864 {
5865     const int shop_multiplier = _shop_sells_antiques(shop_type_) ? 3 : 2;
5866     const int base_level = level_number
5867                             + random2((level_number + 1) * shop_multiplier);
5868 
5869     // Make bazaar items more valuable (up to double value).
5870     if (!player_in_branch(BRANCH_BAZAAR))
5871         return base_level;
5872 
5873     const int bazaar_bonus = random2(base_level) + 1;
5874     return min(base_level + bazaar_bonus, level_number * 5);
5875 }
5876 
5877 /**
5878  * Is the given item valid for placement in the given shop?
5879  *
5880  * @param item_index    An index into env.item; may be NON_ITEM.
5881  * @param shop_type_    The type of shop being generated.
5882  * @param spec          The specification for the shop.
5883  * @return              Whether the item is valid.
5884  */
_valid_item_for_shop(int item_index,shop_type shop_type_,shop_spec & spec)5885 static bool _valid_item_for_shop(int item_index, shop_type shop_type_,
5886                                  shop_spec &spec)
5887 {
5888     if (item_index == NON_ITEM)
5889         return false;
5890 
5891     const item_def &item = env.item[item_index];
5892     ASSERT(item.defined());
5893 
5894     // Don't generate gold in shops! This used to be possible with
5895     // general stores (GDL)
5896     if (item.base_type == OBJ_GOLD)
5897         return false;
5898 
5899     // Don't place missiles or books in general antique shops...
5900     if (shop_type_ == SHOP_GENERAL_ANTIQUE
5901             && (item.base_type == OBJ_MISSILES
5902                 || item.base_type == OBJ_BOOKS))
5903     {
5904         // ...unless they're specified by the item spec.
5905         return !spec.items.empty();
5906     }
5907 
5908     return true;
5909 }
5910 
5911 /**
5912  * Create an item and place it in a shop.
5913  *
5914  * FIXME: I'm pretty sure this will go into an infinite loop if env.item is full.
5915  * items() uses get_mitm_slot with culling, so i think this is ok --wheals
5916  *
5917  * @param j                 The index of the item being created in the shop's
5918  *                          inventory.
5919  * @param shop_type_        The type of shop. (E.g. SHOP_WEAPON_ANTIQUE.)
5920  * @param stocked[in,out]   An array mapping book types to the # in the shop.
5921  * @param spec              The specification of the shop.
5922  * @param shop              The shop.
5923  * @param shop_level        The effective depth to use for the shop.
5924  */
_stock_shop_item(int j,shop_type shop_type_,int stocked[NUM_BOOKS],shop_spec & spec,shop_struct & shop,int shop_level)5925 static void _stock_shop_item(int j, shop_type shop_type_,
5926                              int stocked[NUM_BOOKS],
5927                              shop_spec &spec, shop_struct &shop,
5928                              int shop_level)
5929 {
5930     const int level_number = shop_level ? shop_level : env.absdepth0;
5931     const int item_level = _choose_shop_item_level(shop_type_, level_number);
5932 
5933     int item_index; // index into env.item (global item array)
5934                     // where the generated item will be stored
5935 
5936     // XXX: this scares the hell out of me. should it be a for (...1000)?
5937     // also, it'd be nice if it was just a function that returned an
5938     // item index, maybe
5939     while (true)
5940     {
5941         object_class_type basetype = item_in_shop(shop_type_);
5942         int subtype = OBJ_RANDOM;
5943 
5944         if (!spec.items.empty() && !spec.use_all)
5945         {
5946             // shop spec lists a random set of items; choose one
5947             item_index = dgn_place_item(spec.items.random_item_weighted(),
5948                                         coord_def(), item_level);
5949         }
5950         else if (!spec.items.empty() && spec.use_all
5951                  && j < (int)spec.items.size())
5952         {
5953             // shop lists ordered items; take the one at the right index
5954             item_index = dgn_place_item(spec.items.get_item(j), coord_def(),
5955                                         item_level);
5956         }
5957         else
5958         {
5959             // make an item randomly
5960             // gozag shop items are better
5961             const bool good_item = spec.gozag || one_chance_in(4);
5962             const int level = good_item ? ISPEC_GOOD_ITEM : item_level;
5963             item_index = items(true, basetype, subtype, level);
5964         }
5965 
5966         // Try for a better selection for bookshops.
5967         if (item_index != NON_ITEM && shop_type_ == SHOP_BOOK)
5968         {
5969             // if this book type is already in the shop, maybe discard it
5970             if (!one_chance_in(stocked[env.item[item_index].sub_type] + 1))
5971             {
5972                 env.item[item_index].clear();
5973                 item_index = NON_ITEM; // try again
5974             }
5975         }
5976 
5977         if (_valid_item_for_shop(item_index, shop_type_, spec))
5978             break;
5979 
5980         // Reset object and try again.
5981         if (item_index != NON_ITEM)
5982             env.item[item_index].clear();
5983     }
5984 
5985     ASSERT(item_index != NON_ITEM);
5986 
5987     item_def item = env.item[item_index];
5988 
5989     // If this is a book, note it down in the stocked books array
5990     // (unless it's a randbook)
5991     if (shop_type_ == SHOP_BOOK && !is_artefact(item))
5992         stocked[item.sub_type]++;
5993 
5994     // Identify the item, unless we don't do that.
5995     if (!_shop_sells_antiques(shop_type_))
5996         set_ident_flags(item, ISFLAG_IDENT_MASK);
5997 
5998     // Now move it into the shop!
5999     dec_mitm_item_quantity(item_index, item.quantity);
6000     item.pos = shop.pos;
6001     item.link = ITEM_IN_SHOP;
6002     shop.stock.push_back(item);
6003     dprf(DIAG_DNGN, "shop spec: placing %s",
6004                     item.name(DESC_PLAIN, false, true).c_str());
6005 }
6006 
_random_shop()6007 static shop_type _random_shop()
6008 {
6009     return random_choose(SHOP_WEAPON, SHOP_ARMOUR, SHOP_WEAPON_ANTIQUE,
6010                          SHOP_ARMOUR_ANTIQUE, SHOP_GENERAL_ANTIQUE,
6011                          SHOP_JEWELLERY, SHOP_BOOK,
6012                          SHOP_DISTILLERY, SHOP_SCROLL, SHOP_GENERAL);
6013 }
6014 
6015 
6016 /**
6017  * Attempt to place a shop in a given location.
6018  *
6019  * @param where             The location to place the shop.
6020  * @param spec              The details of the shop.
6021  *                          Would be const if not for list method nonsense.
6022  * @param shop_level        The effective depth to use for the shop.
6023 
6024  */
place_spec_shop(const coord_def & where,shop_spec & spec,int shop_level)6025 void place_spec_shop(const coord_def& where, shop_spec &spec, int shop_level)
6026 {
6027     rng::subgenerator shop_rng; // isolate shop rolls from levelgen
6028     no_notes nx;
6029 
6030     shop_struct& shop = env.shop[where];
6031 
6032     const int level_number = shop_level ? shop_level : env.absdepth0;
6033 
6034     for (int j = 0; j < 3; j++)
6035         shop.keeper_name[j] = 1 + random2(200);
6036     shop.shop_name = spec.name;
6037     shop.shop_type_name = spec.type;
6038     shop.shop_suffix_name = spec.suffix;
6039     shop.level = level_number * 2;
6040     shop.type = spec.sh_type;
6041     if (shop.type == SHOP_RANDOM)
6042         shop.type = _random_shop();
6043     shop.greed = _shop_greed(shop.type, level_number, spec.greed);
6044     shop.pos = where;
6045 
6046     _set_grd(where, DNGN_ENTER_SHOP);
6047 
6048     const int num_items = _shop_num_items(spec);
6049 
6050     // For books shops, store how many copies of a given book are on display.
6051     // This increases the diversity of books in a shop.
6052     int stocked[NUM_BOOKS] = { 0 };
6053 
6054     shop.stock.clear();
6055     for (int j = 0; j < num_items; j++)
6056         _stock_shop_item(j, shop.type, stocked, spec, shop, shop_level);
6057 }
6058 
item_in_shop(shop_type shop_type)6059 object_class_type item_in_shop(shop_type shop_type)
6060 {
6061     switch (shop_type)
6062     {
6063     case SHOP_WEAPON:
6064         if (one_chance_in(5))
6065             return OBJ_MISSILES;
6066         // *** deliberate fall through here  {dlb} ***
6067     case SHOP_WEAPON_ANTIQUE:
6068         return OBJ_WEAPONS;
6069 
6070     case SHOP_ARMOUR:
6071     case SHOP_ARMOUR_ANTIQUE:
6072         return OBJ_ARMOUR;
6073 
6074     case SHOP_GENERAL:
6075     case SHOP_GENERAL_ANTIQUE:
6076         return OBJ_RANDOM;
6077 
6078     case SHOP_JEWELLERY:
6079         return OBJ_JEWELLERY;
6080 
6081     case SHOP_BOOK:
6082         return OBJ_BOOKS;
6083 
6084     case SHOP_DISTILLERY:
6085         return OBJ_POTIONS;
6086 
6087     case SHOP_SCROLL:
6088         return OBJ_SCROLLS;
6089 
6090     default:
6091         die("unknown shop type %d", shop_type);
6092     }
6093 
6094     return OBJ_RANDOM;
6095 }
6096 
6097 // Keep seeds away from the borders so we don't end up with a
6098 // straight wall.
_spotty_seed_ok(const coord_def & p)6099 static bool _spotty_seed_ok(const coord_def& p)
6100 {
6101     const int margin = 4;
6102     return p.x >= margin && p.y >= margin
6103            && p.x < GXM - margin && p.y < GYM - margin;
6104 }
6105 
_feat_is_wall_floor_liquid(dungeon_feature_type feat)6106 static bool _feat_is_wall_floor_liquid(dungeon_feature_type feat)
6107 {
6108     return feat_is_water(feat)
6109            || feat_is_tree(feat)
6110            || feat_is_lava(feat)
6111            || feat_is_wall(feat)
6112            || feat == DNGN_FLOOR;
6113 }
6114 
6115 // Connect vault exit "from" to dungeon floor by growing a spotty chamber.
6116 // This tries to be like _spotty_level, but probably isn't quite.
6117 // It might be better to aim for a more open connection -- currently
6118 // it stops pretty much as soon as connectivity is attained.
_dgn_spotty_connect_path(const coord_def & from,bool (* overwriteable)(dungeon_feature_type))6119 static set<coord_def> _dgn_spotty_connect_path(const coord_def& from,
6120             bool (*overwriteable)(dungeon_feature_type))
6121 {
6122     set<coord_def> flatten;
6123     set<coord_def> border;
6124     bool success = false;
6125 
6126     if (!overwriteable)
6127         overwriteable = _feat_is_wall_floor_liquid;
6128 
6129     for (adjacent_iterator ai(from); ai; ++ai)
6130         if (!map_masked(*ai, MMT_VAULT) && _spotty_seed_ok(*ai))
6131             border.insert(*ai);
6132 
6133     while (!success && !border.empty())
6134     {
6135         auto it = random_iterator(border);
6136         coord_def cur = *it;
6137         border.erase(it);
6138 
6139         // Flatten orthogonal neighbours, and add new neighbours to border.
6140         flatten.insert(cur);
6141         for (orth_adjacent_iterator ai(cur); ai; ++ai)
6142         {
6143             if (map_masked(*ai, MMT_VAULT))
6144                 continue;
6145 
6146             if (env.grid(*ai) == DNGN_FLOOR)
6147                 success = true; // Through, but let's remove the others, too.
6148 
6149             if (!overwriteable(env.grid(*ai)) || flatten.count(*ai))
6150                 continue;
6151 
6152             flatten.insert(*ai);
6153             for (adjacent_iterator bi(*ai); bi; ++bi)
6154             {
6155                 if (!map_masked(*bi, MMT_VAULT)
6156                     && _spotty_seed_ok(*bi)
6157                     && !flatten.count(*bi))
6158                 {
6159                     border.insert(*bi);
6160                 }
6161             }
6162         }
6163     }
6164 
6165     if (!success)
6166         flatten.clear();
6167 
6168     return flatten;
6169 }
6170 
_connect_spotty(const coord_def & from,bool (* overwriteable)(dungeon_feature_type))6171 static bool _connect_spotty(const coord_def& from,
6172                             bool (*overwriteable)(dungeon_feature_type))
6173 {
6174     const set<coord_def> spotty_path =
6175         _dgn_spotty_connect_path(from, overwriteable);
6176 
6177     if (!spotty_path.empty())
6178     {
6179         for (auto c : spotty_path)
6180         {
6181             env.grid(c) = (player_in_branch(BRANCH_SWAMP) && one_chance_in(3))
6182                    ? DNGN_SHALLOW_WATER
6183                    : DNGN_FLOOR;
6184             dgn_height_set_at(c);
6185         }
6186     }
6187 
6188     return !spotty_path.empty();
6189 }
6190 
place_specific_trap(const coord_def & where,trap_type spec_type,int charges)6191 void place_specific_trap(const coord_def& where, trap_type spec_type, int charges)
6192 {
6193     trap_spec spec(spec_type);
6194 
6195     _place_specific_trap(where, &spec, charges);
6196 }
6197 
_place_specific_trap(const coord_def & where,trap_spec * spec,int charges)6198 static void _place_specific_trap(const coord_def& where, trap_spec* spec,
6199                                  int charges)
6200 {
6201     trap_type spec_type = spec->tr_type;
6202 
6203     if (spec_type == TRAP_SHAFT && !is_valid_shaft_level())
6204     {
6205         mprf(MSGCH_ERROR, "Vault %s tried to place a shaft at a branch end",
6206                 env.placing_vault.c_str());
6207     }
6208 
6209     // find an appropriate trap for TRAP_RANDOM
6210     if (spec_type == TRAP_RANDOM)
6211     {
6212         do
6213         {
6214             spec_type = static_cast<trap_type>(random2(NUM_TRAPS));
6215         }
6216         while (!is_regular_trap(spec_type)
6217 #if TAG_MAJOR_VERSION == 34
6218                || spec_type == TRAP_NEEDLE || spec_type == TRAP_GAS
6219                || spec_type == TRAP_SHADOW || spec_type == TRAP_SHADOW_DORMANT
6220 #endif
6221                || !is_valid_shaft_level() && spec_type == TRAP_SHAFT);
6222     }
6223 
6224     trap_def t;
6225     t.type = spec_type;
6226     t.pos = where;
6227     env.grid(where) = trap_feature(spec_type);
6228     t.prepare_ammo(charges);
6229     env.trap[where] = t;
6230     dprf("placed a %s trap", trap_name(spec_type).c_str());
6231 }
6232 
6233 /**
6234  * Sprinkle plants around the level.
6235  *
6236  * @param rarity            1/chance of placing clumps in any given place.
6237  * @param clump_density     1/chance of placing more plants within a clump.
6238  * @param clump_raidus      Radius of plant clumps.
6239  */
_add_plant_clumps(int rarity,int clump_sparseness,int clump_radius)6240 static void _add_plant_clumps(int rarity,
6241                               int clump_sparseness,
6242                               int clump_radius)
6243 {
6244     for (rectangle_iterator ri(1); ri; ++ri)
6245     {
6246         mgen_data mg;
6247         mg.flags = MG_FORCE_PLACE;
6248         if (env.mgrid(*ri) != NON_MONSTER && !map_masked(*ri, MMT_VAULT))
6249         {
6250             // clump plants around things that already exist
6251             monster_type type = env.mons[env.mgrid(*ri)].type;
6252             if ((type == MONS_PLANT
6253                      || type == MONS_FUNGUS
6254                      || type == MONS_BUSH)
6255                  && one_chance_in(rarity))
6256             {
6257                 mg.cls = type;
6258             }
6259             else
6260                 continue;
6261         }
6262         else
6263             continue;
6264 
6265         vector<coord_def> to_place;
6266         to_place.push_back(*ri);
6267         for (int i = 1; i < clump_radius; ++i)
6268         {
6269             for (radius_iterator rad(*ri, i, C_ROUND); rad; ++rad)
6270             {
6271                 if (env.grid(*rad) != DNGN_FLOOR)
6272                     continue;
6273 
6274                 // make sure the iterator stays valid
6275                 vector<coord_def> more_to_place;
6276                 for (auto c : to_place)
6277                 {
6278                     if (*rad == c)
6279                         continue;
6280                     // only place plants next to previously placed plants
6281                     if (abs(rad->x - c.x) <= 1 && abs(rad->y - c.y) <= 1)
6282                     {
6283                         if (one_chance_in(clump_sparseness))
6284                             more_to_place.push_back(*rad);
6285                     }
6286                 }
6287                 to_place.insert(to_place.end(), more_to_place.begin(),
6288                                 more_to_place.end());
6289             }
6290         }
6291 
6292         for (auto c : to_place)
6293         {
6294             if (c == *ri)
6295                 continue;
6296             if (plant_forbidden_at(c))
6297                 continue;
6298             mg.pos = c;
6299             mons_place(mgen_data(mg));
6300         }
6301     }
6302 }
6303 
_find_named_hatch_dest(string hatch_name)6304 static coord_def _find_named_hatch_dest(string hatch_name)
6305 {
6306     vector <map_marker *> markers;
6307     markers = find_markers_by_prop(HATCH_DEST_NAME_PROP, hatch_name);
6308     ASSERT(markers.size() == 1);
6309     return markers[0]->pos;
6310 }
6311 
_get_feat_dest(coord_def base_pos,dungeon_feature_type feat,const string & hatch_name)6312 static coord_def _get_feat_dest(coord_def base_pos, dungeon_feature_type feat,
6313                                 const string &hatch_name)
6314 {
6315     const bool shaft = feat == DNGN_TRAP_SHAFT;
6316     map_position_marker *marker = nullptr;
6317 
6318     if (!shaft)
6319         marker = get_position_marker_at(base_pos, feat);
6320 
6321     if (!marker)
6322     {
6323         coord_def dest_pos;
6324 
6325         if (feat_is_escape_hatch(feat) && !hatch_name.empty())
6326             dest_pos = _find_named_hatch_dest(hatch_name);
6327         else
6328         {
6329             do
6330             {
6331                 dest_pos = random_in_bounds();
6332             }
6333             while (env.grid(dest_pos) != DNGN_FLOOR
6334                    || env.pgrid(dest_pos) & FPROP_NO_TELE_INTO
6335                    || count_adjacent_slime_walls(dest_pos) != 0);
6336         }
6337 
6338         if (!shaft)
6339         {
6340             env.markers.add(new map_position_marker(base_pos, feat, dest_pos));
6341             env.markers.clear_need_activate();
6342         }
6343         return dest_pos;
6344     }
6345     else
6346         return marker->dest;
6347 }
6348 
dgn_degrees_to_radians(int degrees)6349 double dgn_degrees_to_radians(int degrees)
6350 {
6351     return degrees * PI / 180;
6352 }
6353 
dgn_random_point_from(const coord_def & c,int radius,int margin)6354 coord_def dgn_random_point_from(const coord_def &c, int radius, int margin)
6355 {
6356     int attempts = 70;
6357     while (attempts-- > 0)
6358     {
6359         const double angle = dgn_degrees_to_radians(random2(360));
6360         const coord_def res =
6361             c + coord_def(static_cast<int>(radius * cos(angle)),
6362                           static_cast<int>(radius * sin(angle)));
6363         if (map_bounds_with_margin(res, margin))
6364             return res;
6365     }
6366     return coord_def();
6367 }
6368 
dgn_find_feature_marker(dungeon_feature_type feat)6369 coord_def dgn_find_feature_marker(dungeon_feature_type feat)
6370 {
6371     for (map_marker *mark : env.markers.get_all(MAT_FEATURE))
6372         if (dynamic_cast<map_feature_marker*>(mark)->feat == feat)
6373             return mark->pos;
6374     return coord_def();
6375 }
6376 
6377 // Make hatches and shafts land the player a bit away from the wall.
6378 // Specifically, the adjacent cell with least slime walls next to it.
6379 // XXX: This can still give bad situations if the layout is not bubbly,
6380 //      e.g. when a vault is placed with connecting corridors.
_fixup_slime_hatch_dest(coord_def * pos)6381 static void _fixup_slime_hatch_dest(coord_def* pos)
6382 {
6383     int max_walls = 9;
6384     for (adjacent_iterator ai(*pos, false); ai; ++ai)
6385     {
6386         if (!feat_is_traversable(env.grid(*ai)))
6387             continue;
6388         const int walls = count_adjacent_slime_walls(*ai);
6389         if (walls < max_walls)
6390         {
6391             *pos = *ai;
6392             max_walls = walls;
6393         }
6394     }
6395     ASSERT(max_walls < 9);
6396 }
6397 
dgn_find_nearby_stair(dungeon_feature_type stair_to_find,coord_def base_pos,bool find_closest,string hatch_name)6398 coord_def dgn_find_nearby_stair(dungeon_feature_type stair_to_find,
6399                                 coord_def base_pos, bool find_closest,
6400                                 string hatch_name)
6401 {
6402     dprf(DIAG_DNGN, "Level entry point on %sstair: %d (%s)",
6403          find_closest ? "closest " : "",
6404          stair_to_find, dungeon_feature_name(stair_to_find));
6405 
6406     // Shafts and hatches.
6407     if (stair_to_find == DNGN_ESCAPE_HATCH_UP
6408         || stair_to_find == DNGN_ESCAPE_HATCH_DOWN
6409         || stair_to_find == DNGN_TRAP_SHAFT)
6410     {
6411         coord_def pos(_get_feat_dest(base_pos, stair_to_find, hatch_name));
6412         if (player_in_branch(BRANCH_SLIME))
6413             _fixup_slime_hatch_dest(&pos);
6414         if (in_bounds(pos))
6415             return pos;
6416     }
6417 
6418     if (stair_to_find == DNGN_STONE_ARCH)
6419     {
6420         const coord_def pos(dgn_find_feature_marker(stair_to_find));
6421         if (in_bounds(pos) && env.grid(pos) == stair_to_find)
6422             return pos;
6423     }
6424 
6425     if (stair_to_find == your_branch().exit_stairs)
6426     {
6427         const coord_def pos(dgn_find_feature_marker(DNGN_STONE_STAIRS_UP_I));
6428         if (in_bounds(pos) && env.grid(pos) == stair_to_find)
6429             return pos;
6430     }
6431 
6432     // Scan around the player's position first.
6433     int basex = base_pos.x;
6434     int basey = base_pos.y;
6435 
6436     // Check for illegal starting point.
6437     if (!in_bounds(basex, basey))
6438     {
6439         basex = 0;
6440         basey = 0;
6441     }
6442 
6443     coord_def result;
6444 
6445     int found = 0;
6446     int best_dist = 1 + GXM*GXM + GYM*GYM;
6447 
6448     // XXX These passes should be rewritten to use an iterator of STL
6449     // algorithm of some kind.
6450 
6451     // First pass: look for an exact match.
6452     for (int xcode = 0; xcode < GXM; ++xcode)
6453     {
6454         if (stair_to_find == DNGN_FLOOR)
6455             break;
6456 
6457         const int xsign = ((xcode % 2) ? 1 : -1);
6458         const int xdiff = xsign * (xcode + 1)/2;
6459         const int xpos  = (basex + xdiff + GXM) % GXM;
6460 
6461         for (int ycode = 0; ycode < GYM; ++ycode)
6462         {
6463             const int ysign = ((ycode % 2) ? 1 : -1);
6464             const int ydiff = ysign * (ycode + 1)/2;
6465             const int ypos  = (basey + ydiff + GYM) % GYM;
6466 
6467             // Note that due to the wrapping above, we can't just use
6468             // xdiff*xdiff + ydiff*ydiff.
6469             const int dist = (xpos-basex)*(xpos-basex)
6470                              + (ypos-basey)*(ypos-basey);
6471 
6472             if (orig_terrain(coord_def(xpos, ypos)) == stair_to_find
6473                 && !feature_mimic_at(coord_def(xpos, ypos)))
6474             {
6475                 found++;
6476                 if (find_closest)
6477                 {
6478                     if (dist < best_dist)
6479                     {
6480                         best_dist = dist;
6481                         result.x = xpos;
6482                         result.y = ypos;
6483                     }
6484                 }
6485                 else if (one_chance_in(found))
6486                 {
6487                     result.x = xpos;
6488                     result.y = ypos;
6489                 }
6490             }
6491         }
6492     }
6493 
6494     if (found)
6495         return result;
6496 
6497     best_dist = 1 + GXM*GXM + GYM*GYM;
6498 
6499     // Second pass: find a staircase in the proper direction.
6500     for (int xcode = 0; xcode < GXM; ++xcode)
6501     {
6502         if (stair_to_find == DNGN_FLOOR)
6503             break;
6504 
6505         const int xsign = ((xcode % 2) ? 1 : -1);
6506         const int xdiff = xsign * (xcode + 1)/2;
6507         const int xpos  = (basex + xdiff + GXM) % GXM;
6508 
6509         for (int ycode = 0; ycode < GYM; ++ycode)
6510         {
6511             const int ysign = ((ycode % 2) ? 1 : -1);
6512             const int ydiff = ysign * (ycode + 1)/2;
6513             const int ypos  = (basey + ydiff + GYM) % GYM;
6514 
6515             bool good_stair;
6516             const int looking_at = orig_terrain(coord_def(xpos, ypos));
6517 
6518             if (feat_is_stone_stair_down(stair_to_find)
6519                 || stair_to_find == DNGN_ESCAPE_HATCH_DOWN
6520                 || stair_to_find == DNGN_FLOOR)
6521             {
6522                 good_stair = feat_is_stone_stair_down((dungeon_feature_type)looking_at)
6523                              || looking_at == DNGN_ESCAPE_HATCH_DOWN;
6524             }
6525             else
6526             {
6527                 good_stair = feat_is_stone_stair_up((dungeon_feature_type)looking_at)
6528                               || looking_at == DNGN_ESCAPE_HATCH_UP;
6529             }
6530 
6531             const int dist = (xpos-basex)*(xpos-basex)
6532                              + (ypos-basey)*(ypos-basey);
6533 
6534             if (good_stair && !feature_mimic_at(coord_def(xpos, ypos)))
6535             {
6536                 found++;
6537                 if (find_closest && dist < best_dist)
6538                 {
6539                     best_dist = dist;
6540                     result.x = xpos;
6541                     result.y = ypos;
6542                 }
6543                 else if (one_chance_in(found))
6544                 {
6545                     result.x = xpos;
6546                     result.y = ypos;
6547                 }
6548             }
6549         }
6550     }
6551 
6552     if (found)
6553         return result;
6554 
6555     const coord_def pos(dgn_find_feature_marker(stair_to_find));
6556     if (in_bounds(pos))
6557         return pos;
6558 
6559     // Look for any clear terrain and abandon the idea of looking nearby now.
6560     // This is used when taking transit Pandemonium gates. Currently the player
6561     // can land in vaults, which is considered acceptable.
6562     for (rectangle_iterator ri(0); ri; ++ri)
6563     {
6564         if (feat_has_dry_floor(env.grid(*ri))
6565             && !(env.pgrid(*ri) & FPROP_NO_TELE_INTO))
6566         {
6567             found++;
6568             if (one_chance_in(found))
6569                 result = *ri;
6570         }
6571     }
6572     if (found)
6573         return result;
6574 
6575     // FAIL
6576     die("Can't find any floor to put the player on.");
6577 }
6578 
dgn_set_branch_epilogue(branch_type branch,string func_name)6579 void dgn_set_branch_epilogue(branch_type branch, string func_name)
6580 {
6581     branch_epilogues[branch] = func_name;
6582 }
6583 
6584 ////////////////////////////////////////////////////////////////////
6585 // dgn_region
6586 
overlaps(const dgn_region & other) const6587 bool dgn_region::overlaps(const dgn_region &other) const
6588 {
6589     // The old overlap check checked only two corners - top-left and
6590     // bottom-right. I'm hoping nothing actually *relied* on that stupid bug.
6591 
6592     return (between(pos.x, other.pos.x, other.pos.x + other.size.x - 1)
6593                || between(pos.x + size.x - 1, other.pos.x,
6594                           other.pos.x + other.size.x - 1))
6595            && (between(pos.y, other.pos.y, other.pos.y + other.size.y - 1)
6596                || between(pos.y + size.y - 1, other.pos.y,
6597                           other.pos.y + other.size.y - 1));
6598 }
6599 
overlaps_any(const dgn_region_list & regions) const6600 bool dgn_region::overlaps_any(const dgn_region_list &regions) const
6601 {
6602     for (auto reg : regions)
6603         if (overlaps(reg))
6604             return true;
6605 
6606     return false;
6607 }
6608 
overlaps(const dgn_region_list & regions,const map_mask & mask) const6609 bool dgn_region::overlaps(const dgn_region_list &regions,
6610                           const map_mask &mask) const
6611 {
6612     return overlaps_any(regions) && overlaps(mask);
6613 }
6614 
overlaps(const map_mask & mask) const6615 bool dgn_region::overlaps(const map_mask &mask) const
6616 {
6617     const coord_def endp = pos + size;
6618     for (int y = pos.y; y < endp.y; ++y)
6619         for (int x = pos.x; x < endp.x; ++x)
6620         {
6621             if (mask[x][y])
6622                 return true;
6623         }
6624 
6625     return false;
6626 }
6627 
random_edge_point() const6628 coord_def dgn_region::random_edge_point() const
6629 {
6630     coord_def res;
6631     if (x_chance_in_y(size.x, size.x + size.y))
6632     {
6633         res.x = pos.x + random2(size.x);
6634         res.y = random_choose(pos.y, pos.y + size.y - 1);
6635     }
6636     else
6637     {
6638         res.x = random_choose(pos.x, pos.x + size.x - 1);
6639         res.y = pos.y + random2(size.y);
6640     }
6641     return res;
6642 }
6643 
random_point() const6644 coord_def dgn_region::random_point() const
6645 {
6646     coord_def res;
6647     res.x = pos.x + random2(size.x);
6648     res.y = pos.y + random2(size.y);
6649     return res;
6650 }
6651 
6652 struct StairConnectivity
6653 {
StairConnectivityStairConnectivity6654     StairConnectivity()
6655     {
6656         region[0] = region[1] = region[2] = 0;
6657         connected[0] = connected[1] = connected[2] = true;
6658     }
6659 
connect_regionStairConnectivity6660     void connect_region(int idx)
6661     {
6662         for (int i = 0; i < 3; i++)
6663             connected[i] |= (region[i] == idx);
6664     }
6665 
readStairConnectivity6666     void read(reader &th)
6667     {
6668         region[0] = unmarshallByte(th);
6669         region[1] = unmarshallByte(th);
6670         region[2] = unmarshallByte(th);
6671         connected[0] = unmarshallBoolean(th);
6672         connected[1] = unmarshallBoolean(th);
6673         connected[2] = unmarshallBoolean(th);
6674     }
6675 
writeStairConnectivity6676     void write(writer &th)
6677     {
6678         marshallByte(th, region[0]);
6679         marshallByte(th, region[1]);
6680         marshallByte(th, region[2]);
6681         marshallBoolean(th, connected[0]);
6682         marshallBoolean(th, connected[1]);
6683         marshallBoolean(th, connected[2]);
6684     }
6685 
6686     int8_t region[3];
6687     bool connected[3];
6688 };
6689 
6690 FixedVector<vector<StairConnectivity>, NUM_BRANCHES> connectivity;
6691 
init_level_connectivity()6692 void init_level_connectivity()
6693 {
6694     for (branch_iterator it; it; ++it)
6695     {
6696         int depth = brdepth[it->id] > 0 ? brdepth[it->id] : 0;
6697         connectivity[it->id].resize(depth);
6698     }
6699 }
6700 
read_level_connectivity(reader & th)6701 void read_level_connectivity(reader &th)
6702 {
6703     int nb = unmarshallInt(th);
6704     ASSERT(nb <= NUM_BRANCHES);
6705     for (int i = 0; i < nb; i++)
6706     {
6707         unsigned int depth = brdepth[i] > 0 ? brdepth[i] : 0;
6708         unsigned int num_entries = unmarshallInt(th);
6709         connectivity[i].resize(max(depth, num_entries));
6710 
6711         for (unsigned int e = 0; e < num_entries; e++)
6712             connectivity[i][e].read(th);
6713     }
6714 }
6715 
write_level_connectivity(writer & th)6716 void write_level_connectivity(writer &th)
6717 {
6718     marshallInt(th, NUM_BRANCHES);
6719     for (int i = 0; i < NUM_BRANCHES; i++)
6720     {
6721         marshallInt(th, connectivity[i].size());
6722         for (unsigned int e = 0; e < connectivity[i].size(); e++)
6723             connectivity[i][e].write(th);
6724     }
6725 }
6726 
_fixup_interlevel_connectivity()6727 static bool _fixup_interlevel_connectivity()
6728 {
6729     // Rotate the stairs on this level to attempt to preserve connectivity
6730     // as much as possible. At a minimum, it ensures a path from the bottom
6731     // of a branch to the top of a branch. If this is not possible, it
6732     // returns false.
6733     //
6734     // Note: this check is undirectional and assumes that levels below this
6735     // one have not been created yet. If this is not the case, it will not
6736     // guarantee or preserve connectivity.
6737     //
6738     // XXX: If successful, the previous level's connectedness information
6739     //      is updated, so we rely on the level not being vetoed after
6740     //      this check.
6741 
6742     if (!player_in_connected_branch() || brdepth[you.where_are_you] == -1)
6743         return true;
6744     if (branches[you.where_are_you].branch_flags & brflag::islanded)
6745         return true;
6746 
6747     StairConnectivity prev_con;
6748     if (you.depth > 1)
6749         prev_con = connectivity[your_branch().id][you.depth - 2];
6750     StairConnectivity this_con;
6751 
6752     FixedVector<coord_def, 3> up_gc;
6753     FixedVector<coord_def, 3> down_gc;
6754     FixedVector<int, 3> up_region;
6755     FixedVector<int, 3> down_region;
6756     FixedVector<bool, 3> has_down;
6757     vector<bool> region_connected;
6758 
6759     up_region[0] = up_region[1] = up_region[2] = -1;
6760     down_region[0] = down_region[1] = down_region[2] = -1;
6761     has_down[0] = has_down[1] = has_down[2] = false;
6762 
6763     // Find up stairs and down stairs on the current level.
6764     memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
6765     int nzones = 0;
6766     for (rectangle_iterator ri(0); ri; ++ri)
6767     {
6768         if (!map_bounds(ri->x, ri->y)
6769             || travel_point_distance[ri->x][ri->y]
6770             || !dgn_square_travel_ok(*ri))
6771         {
6772             continue;
6773         }
6774 
6775         _dgn_fill_zone(*ri, ++nzones, _dgn_point_record_stub,
6776                        dgn_square_travel_ok, nullptr);
6777     }
6778 
6779     int max_region = 0;
6780     for (rectangle_iterator ri(0); ri; ++ri)
6781     {
6782         if (feature_mimic_at(*ri))
6783             continue;
6784 
6785         dungeon_feature_type feat = env.grid(*ri);
6786         switch (feat)
6787         {
6788         case DNGN_STONE_STAIRS_DOWN_I:
6789         case DNGN_STONE_STAIRS_DOWN_II:
6790         case DNGN_STONE_STAIRS_DOWN_III:
6791         {
6792             int idx = feat - DNGN_STONE_STAIRS_DOWN_I;
6793             if (down_region[idx] == -1)
6794             {
6795                 down_region[idx] = travel_point_distance[ri->x][ri->y];
6796                 down_gc[idx] = *ri;
6797                 max_region = max(down_region[idx], max_region);
6798             }
6799             else
6800             {
6801                 // Too many stairs!
6802                 return false;
6803             }
6804             break;
6805         }
6806         case DNGN_STONE_STAIRS_UP_I:
6807         case DNGN_STONE_STAIRS_UP_II:
6808         case DNGN_STONE_STAIRS_UP_III:
6809         {
6810             int idx = feat - DNGN_STONE_STAIRS_UP_I;
6811             if (up_region[idx] == -1)
6812             {
6813                 up_region[idx] = travel_point_distance[ri->x][ri->y];
6814                 up_gc[idx] = *ri;
6815                 max_region = max(up_region[idx], max_region);
6816             }
6817             else
6818             {
6819                 // Too many stairs!
6820                 return false;
6821             }
6822             break;
6823         }
6824         default:
6825             break;
6826         }
6827     }
6828 
6829     const int up_region_max = you.depth == 1 ? 1 : 3;
6830 
6831     // Ensure all up stairs were found.
6832     for (int i = 0; i < up_region_max; i++)
6833         if (up_region[i] == -1)
6834             return false;
6835 
6836     region_connected.resize(max_region + 1);
6837     fill(begin(region_connected), end(region_connected), false);
6838 
6839     // Which up stairs have a down stair? (These are potentially connected.)
6840     if (!at_branch_bottom())
6841     {
6842         for (int i = 0; i < up_region_max; i++)
6843             for (int j = 0; j < 3; j++)
6844             {
6845                 if (down_region[j] == up_region[i])
6846                     has_down[i] = true;
6847             }
6848     }
6849 
6850     bool any_connected = has_down[0] || has_down[1] || has_down[2];
6851     if (!any_connected && !at_branch_bottom())
6852         return false;
6853 
6854     // Keep track of what stairs we've assigned.
6855     int assign_prev[3] = {-1, -1, -1};
6856     int assign_cur[3] = {-1, -1, -1};
6857 
6858     // Assign one connected down stair from the previous level to an
6859     // upstair on the current level with a downstair in the same region.
6860     // This ensures at least a single valid path to the top.
6861     bool minimal_connectivity = false;
6862     for (int i = 0; i < 3 && !minimal_connectivity; i++)
6863     {
6864         if (!prev_con.connected[i])
6865             continue;
6866         for (int j = 0; j < up_region_max; j++)
6867         {
6868             if (!has_down[j] && !at_branch_bottom())
6869                 continue;
6870 
6871             minimal_connectivity = true;
6872             assign_prev[i] = j;
6873             assign_cur[j] = i;
6874             region_connected[up_region[j]] = true;
6875             break;
6876         }
6877     }
6878     if (!minimal_connectivity)
6879         return false;
6880 
6881     // For each disconnected stair (in a unique region) on the previous level,
6882     // try to reconnect to a connected up stair on the current level.
6883     for (int i = 0; i < 3; i++)
6884     {
6885         if (assign_prev[i] != -1 || prev_con.connected[i])
6886             continue;
6887 
6888         bool unique_region = true;
6889         for (int j = 0; j < i; j++)
6890         {
6891             if (prev_con.region[j] == prev_con.region[i])
6892                 unique_region = false;
6893         }
6894         if (!unique_region)
6895             continue;
6896 
6897         // Try first to assign to any connected regions.
6898         for (int j = 0; j < up_region_max; j++)
6899         {
6900             if (assign_cur[j] != -1 || !region_connected[up_region[j]])
6901                 continue;
6902 
6903             assign_prev[i] = j;
6904             assign_cur[j] = i;
6905             prev_con.connect_region(prev_con.region[i]);
6906             break;
6907         }
6908         if (assign_prev[i] != -1)
6909             continue;
6910 
6911         // If we fail, then assign to any up stair with a down, and we'll
6912         // try to reconnect this section on the next level.
6913         for (int j = 0; j < 3; j++)
6914         {
6915             if (assign_cur[j] != -1 || !has_down[j])
6916                 continue;
6917 
6918             assign_prev[i] = j;
6919             assign_cur[j] = i;
6920             break;
6921         }
6922     }
6923 
6924     // Assign any connected down stairs from the previous level to any
6925     // disconnected stairs on the current level.
6926     for (int i = 0; i < 3; i++)
6927     {
6928         if (!prev_con.connected[i] || assign_prev[i] != -1)
6929             continue;
6930 
6931         for (int j = 0; j < up_region_max; j++)
6932         {
6933             if (has_down[j] || assign_cur[j] != -1)
6934                 continue;
6935             if (region_connected[up_region[j]])
6936                 continue;
6937 
6938             assign_prev[i] = j;
6939             assign_cur[j] = i;
6940             region_connected[up_region[j]] = true;
6941             break;
6942         }
6943     }
6944 
6945     // If there are any remaining stairs, assign those.
6946     for (int i = 0; i < 3; i++)
6947     {
6948         if (assign_prev[i] != -1)
6949             continue;
6950         for (int j = 0; j < up_region_max; j++)
6951         {
6952             if (assign_cur[j] != -1)
6953                 continue;
6954             assign_prev[i] = j;
6955             assign_cur[j] = i;
6956 
6957             if (region_connected[up_region[j]])
6958                 prev_con.connect_region(prev_con.region[i]);
6959             else if (prev_con.connected[i])
6960                 region_connected[up_region[j]] = true;
6961             break;
6962         }
6963     }
6964 
6965     // At the branch bottom, all up stairs must be connected.
6966     if (at_branch_bottom())
6967     {
6968         for (int i = 0; i < up_region_max; i++)
6969             if (!region_connected[up_region[i]])
6970                 return false;
6971     }
6972 
6973     // Sanity check that we're not duplicating stairs.
6974     if (up_region_max > 1)
6975     {
6976         bool stairs_unique = (assign_cur[0] != assign_cur[1]
6977                               && assign_cur[1] != assign_cur[2]);
6978         ASSERT(stairs_unique);
6979         if (!stairs_unique)
6980             return false;
6981     }
6982 
6983     // Reassign up stair numbers as needed.
6984     for (int i = 0; i < up_region_max; i++)
6985     {
6986         _set_grd(up_gc[i],
6987             (dungeon_feature_type)(DNGN_STONE_STAIRS_UP_I + assign_cur[i]));
6988     }
6989 
6990     // Fill in connectivity and regions.
6991     for (int i = 0; i < 3; i++)
6992     {
6993         this_con.region[i] = down_region[i];
6994         if (down_region[i] != -1)
6995             this_con.connected[i] = region_connected[down_region[i]];
6996         else
6997             this_con.connected[i] = false;
6998 
6999     }
7000 
7001     // Save the connectivity.
7002     if (you.depth > 1)
7003         connectivity[your_branch().id][you.depth - 2] = prev_con;
7004     connectivity[your_branch().id][you.depth - 1] = this_con;
7005 
7006     return true;
7007 }
7008 
run_map_epilogues()7009 void run_map_epilogues()
7010 {
7011     // Iterate over level vaults and run each map's epilogue.
7012     for (auto &vault : env.level_vaults)
7013         vault->map.run_lua_epilogue();
7014 }
7015 
7016 //////////////////////////////////////////////////////////////////////////
7017 // vault_placement
7018 
vault_placement()7019 vault_placement::vault_placement()
7020     : pos(-1, -1), size(0, 0), orient(MAP_NONE), map(), exits(), seen(false)
7021 {
7022 }
7023 
map_name_at(const coord_def & where) const7024 string vault_placement::map_name_at(const coord_def &where) const
7025 {
7026     const coord_def offset = where - pos;
7027     return map.name_at(offset);
7028 }
7029 
reset()7030 void vault_placement::reset()
7031 {
7032     if (_current_temple_hash != nullptr)
7033         _setup_temple_altars(*_current_temple_hash);
7034     else
7035         _temple_altar_list.clear();
7036 }
7037 
apply_grid()7038 void vault_placement::apply_grid()
7039 {
7040     if (!size.zero())
7041     {
7042         bool clear = !map.has_tag("overwrite_floor_cell");
7043 
7044         // NOTE: assumes *no* previous item (I think) or monster (definitely)
7045         // placement.
7046         for (rectangle_iterator ri(pos, pos + size - 1); ri; ++ri)
7047         {
7048             if (map.is_overwritable_layout() && map_masked(*ri, MMT_VAULT))
7049                 continue;
7050 
7051             const coord_def &rp(*ri);
7052             const coord_def dp = rp - pos;
7053 
7054             const int feat = map.map.glyph(dp);
7055 
7056             if (feat == ' ')
7057                 continue;
7058 
7059             const dungeon_feature_type oldgrid = env.grid(*ri);
7060 
7061             if (clear)
7062             {
7063                 env.grid_colours(*ri) = 0;
7064                 env.pgrid(*ri) = terrain_property_t{};
7065                 // what about heightmap?
7066                 tile_clear_flavour(*ri);
7067             }
7068 
7069             keyed_mapspec *mapsp = map.mapspec_at(dp);
7070             _vault_grid(*this, feat, *ri, mapsp);
7071 
7072             if (!crawl_state.generating_level)
7073             {
7074                 // Have to link items each square at a time, or
7075                 // dungeon_terrain_changed could blow up.
7076                 link_items();
7077                 // Init tile flavour -- dungeon_terrain_changed does
7078                 // this too, but only if oldgrid != newgrid, so we
7079                 // make sure here.
7080                 tile_init_flavour(*ri);
7081                 const dungeon_feature_type newgrid = env.grid(*ri);
7082                 env.grid(*ri) = oldgrid;
7083                 dungeon_terrain_changed(*ri, newgrid, true);
7084                 remove_markers_and_listeners_at(*ri);
7085             }
7086         }
7087 
7088         // Place monsters in a second pass. Otherwise band followers
7089         // could be overwritten with subsequent walls.
7090         for (rectangle_iterator ri(pos, pos + size - 1); ri; ++ri)
7091         {
7092             if (map.is_overwritable_layout() && map_masked(*ri, MMT_VAULT))
7093                 continue;
7094 
7095             const coord_def dp = *ri - pos;
7096 
7097             const int feat = map.map.glyph(dp);
7098             keyed_mapspec *mapsp = map.mapspec_at(dp);
7099 
7100             _vault_grid_mons(*this, feat, *ri, mapsp);
7101         }
7102 
7103         map.map.apply_overlays(pos, map.is_overwritable_layout());
7104     }
7105 }
7106 
draw_at(const coord_def & c)7107 void vault_placement::draw_at(const coord_def &c)
7108 {
7109     pos = c;
7110     apply_grid();
7111 }
7112 
connect(bool spotty) const7113 int vault_placement::connect(bool spotty) const
7114 {
7115     int exits_placed = 0;
7116 
7117     for (auto c : exits)
7118     {
7119         if (spotty && _connect_spotty(c, _feat_is_wall_floor_liquid)
7120             || player_in_branch(BRANCH_SHOALS) && dgn_shoals_connect_point(c)
7121             || _connect_vault_exit(c))
7122         {
7123             exits_placed++;
7124         }
7125         else
7126         {
7127             dprf(DIAG_DNGN, "Warning: failed to connect vault exit (%d;%d).",
7128                  c.x, c.y);
7129         }
7130     }
7131 
7132     return exits_placed;
7133 }
7134 
7135 // Checks the resultant feature type of the map glyph, after applying KFEAT
7136 // and so forth. Unfortunately there is a certain amount of duplication of
7137 // the code path in apply_grid; but actual modifications to the level
7138 // are so intertwined with that code path it would be actually quite messy
7139 // to try and avoid the duplication.
7140 // TODO: this should be const, but a lot of mapdef stuff is not properly
7141 // marked for this
feature_at(const coord_def & c)7142 dungeon_feature_type vault_placement::feature_at(const coord_def &c)
7143 {
7144     // Can't check outside bounds of vault
7145     if (size.zero() || c.x > size.x || c.y > size.y)
7146         return NUM_FEATURES;
7147 
7148     const int feat = map.map.glyph(c);
7149 
7150     //XXX: perhaps this should really be NUM_FEATURES, but there are crashes.
7151     if (feat == ' ')
7152         return DNGN_FLOOR;
7153 
7154     keyed_mapspec *mapsp = map.mapspec_at(c);
7155     return _vault_inspect(*this, feat, mapsp);
7156 }
7157 
is_space(const coord_def & c) const7158 bool vault_placement::is_space(const coord_def &c) const
7159 {
7160     // Can't check outside bounds of vault
7161     if (size.zero() || c.x > size.x || c.y > size.y)
7162         return false;
7163 
7164     const int feat = map.map.glyph(c);
7165     return feat == ' ';
7166 }
is_exit(const coord_def & c) const7167 bool vault_placement::is_exit(const coord_def &c) const
7168 {
7169     // Can't check outside bounds of vault
7170     if (size.zero() || c.x > size.x || c.y > size.y)
7171         return false;
7172 
7173     const int feat = map.map.glyph(c);
7174     return feat == '@';
7175 }
_vault_inspect(vault_placement & place,int vgrid,keyed_mapspec * mapsp)7176 static dungeon_feature_type _vault_inspect(vault_placement &place,
7177                         int vgrid, keyed_mapspec *mapsp)
7178 {
7179     // The two functions called are
7180     if (mapsp && mapsp->replaces_glyph())
7181         return _vault_inspect_mapspec(place, *mapsp);
7182     else
7183         return _vault_inspect_glyph(vgrid);
7184 }
7185 
_vault_inspect_mapspec(vault_placement & place,keyed_mapspec & mapsp)7186 static dungeon_feature_type _vault_inspect_mapspec(vault_placement &place,
7187                                                    keyed_mapspec& mapsp)
7188 {
7189     UNUSED(place);
7190     dungeon_feature_type found = NUM_FEATURES;
7191     const feature_spec f = mapsp.get_feat();
7192     if (f.trap)
7193         found = trap_feature(f.trap->tr_type);
7194     else if (f.feat >= 0)
7195         found = static_cast<dungeon_feature_type>(f.feat);
7196     else if (f.glyph >= 0)
7197         found = _vault_inspect_glyph(f.glyph);
7198     else if (f.shop)
7199         found = DNGN_ENTER_SHOP;
7200     else
7201         found = DNGN_FLOOR;
7202 
7203     return found;
7204 }
7205 
_vault_inspect_glyph(int vgrid)7206 static dungeon_feature_type _vault_inspect_glyph(int vgrid)
7207 {
7208     // Get the base feature according to the glyph
7209     dungeon_feature_type found = NUM_FEATURES;
7210     if (vgrid != -1)
7211         found = _glyph_to_feat(vgrid);
7212 
7213     // If it's an altar for an unavailable god then it will get turned into floor by _vault_grid_glyph
7214     if (feat_is_altar(found)
7215         && is_unavailable_god(feat_altar_god(found)))
7216     {
7217         found = DNGN_FLOOR;
7218     }
7219 
7220     return found;
7221 }
7222 
_remember_vault_placement(const vault_placement & place)7223 static void _remember_vault_placement(const vault_placement &place)
7224 {
7225     UNUSED(place);
7226 #ifdef DEBUG_STATISTICS
7227     _you_all_vault_list.push_back(place.map.name);
7228 #endif
7229 }
7230 
dump_vault_maps()7231 string dump_vault_maps()
7232 {
7233     string out = "";
7234 
7235     vector<level_id> levels = all_dungeon_ids();
7236 
7237     for (const level_id &lid : levels)
7238     {
7239         // n.b. portal vaults get cleared from here, so won't show up.
7240         // kind of spammy in wizmode. To test non-wizmode, use &ctrl-y
7241         if (!you.wizard && (!you.level_visited(lid)
7242                             || !you.vault_list.count(lid))
7243             || branch_is_unfinished(lid.branch))
7244         {
7245             continue;
7246         }
7247 
7248         if (you.wizard)
7249         {
7250             // because the save is already gone at the point where we are
7251             // printing a morgue, this check isn't reliable. Ignore it.
7252             if (!is_existing_level(lid) && you.save)
7253             {
7254                 out += "[-gen]      ";
7255                 if (!you.vault_list.count(lid))
7256                 {
7257                     out +=  lid.describe() + "\n";
7258                     continue;
7259                 }
7260             }
7261             else
7262                 out += you.level_visited(lid) ? "[+gen,+vis] " : "[+gen,-vis] ";
7263         }
7264         out += lid.describe();
7265         vector<string> &maps(you.vault_list[lid]);
7266         if (maps.size() == 0)
7267         {
7268             out += "\n";
7269             continue;
7270         }
7271 
7272         out += ": " + string(max(8 - int(lid.describe().length()), 0), ' ');
7273 
7274         // TODO: some way of showing no_dump maps in wizmode?
7275 
7276         string vaults = comma_separated_line(maps.begin(), maps.end(), ", ");
7277         out += wordwrap_line(vaults, you.wizard ? 58 : 70) + "\n";
7278         while (!vaults.empty())
7279         {
7280             out += string(you.wizard ? 22 : 10, ' ')
7281                     + wordwrap_line(vaults, you.wizard ? 58 : 70, false) + "\n";
7282         }
7283 
7284     }
7285     return out;
7286 }
7287 
7288 ///////////////////////////////////////////////////////////////////////////
7289 // vault_place_iterator
7290 
vault_place_iterator(const vault_placement & vp)7291 vault_place_iterator::vault_place_iterator(const vault_placement &vp)
7292     : vault_place(vp), pos(vp.pos), tl(vp.pos), br(vp.pos + vp.size - 1)
7293 {
7294     --pos.x;
7295     ++*this;
7296 }
7297 
operator bool() const7298 vault_place_iterator::operator bool () const
7299 {
7300     return pos.y <= br.y && pos.x <= br.x;
7301 }
7302 
operator *() const7303 coord_def vault_place_iterator::operator * () const
7304 {
7305     return pos;
7306 }
7307 
operator ->() const7308 const coord_def *vault_place_iterator::operator -> () const
7309 {
7310     return &pos;
7311 }
7312 
vault_pos() const7313 coord_def vault_place_iterator::vault_pos() const
7314 {
7315     return pos - tl;
7316 }
7317 
operator ++()7318 vault_place_iterator &vault_place_iterator::operator ++ ()
7319 {
7320     while (pos.y <= br.y)
7321     {
7322         if (++pos.x > br.x)
7323         {
7324             pos.x = tl.x;
7325             ++pos.y;
7326         }
7327         if (pos.y <= br.y && vault_place.map.in_map(pos - tl))
7328             break;
7329     }
7330     return *this;
7331 }
7332 
operator ++(int)7333 vault_place_iterator vault_place_iterator::operator ++ (int)
7334 {
7335     const vault_place_iterator copy = *this;
7336     ++*this;
7337     return copy;
7338 }
7339 
7340 //////////////////////////////////////////////////////////////////////////
7341 // unwind_vault_placement_mask
7342 
unwind_vault_placement_mask(const map_bitmask * mask)7343 unwind_vault_placement_mask::unwind_vault_placement_mask(const map_bitmask *mask)
7344     : oldmask(Vault_Placement_Mask)
7345 {
7346     Vault_Placement_Mask = mask;
7347 }
7348 
~unwind_vault_placement_mask()7349 unwind_vault_placement_mask::~unwind_vault_placement_mask()
7350 {
7351     Vault_Placement_Mask = oldmask;
7352 }
7353 
7354 // mark all unexplorable squares, count the rest
_calc_density()7355 static void _calc_density()
7356 {
7357     int open = 0;
7358     for (rectangle_iterator ri(0); ri; ++ri)
7359     {
7360         // If for some reason a level gets modified afterwards, dug-out
7361         // places in unmodified parts should not suddenly become explorable.
7362         if (!testbits(env.pgrid(*ri), FPROP_SEEN_OR_NOEXP))
7363             for (adjacent_iterator ai(*ri, false); ai; ++ai)
7364                 if (feat_has_solid_floor(env.grid(*ai)))
7365                 {
7366                     open++;
7367                     goto out;
7368                 }
7369         env.pgrid(*ri) |= FPROP_SEEN_OR_NOEXP;
7370     out:;
7371     }
7372 
7373     dprf(DIAG_DNGN, "Level density: %d", open);
7374     env.density = open;
7375 }
7376 
7377 // Mark all solid squares as no_tele so that digging doesn't influence
7378 // random teleportation.
_mark_solid_squares()7379 static void _mark_solid_squares()
7380 {
7381     for (rectangle_iterator ri(0); ri; ++ri)
7382         if (feat_is_solid(env.grid(*ri)))
7383             env.pgrid(*ri) |= FPROP_NO_TELE_INTO;
7384 }
7385 
7386 // Based on their starting class, where does the player start?
starting_absdepth()7387 int starting_absdepth()
7388 {
7389     if (you.char_class == JOB_DELVER)
7390         return 4;
7391     return 0; // (absdepth is 0-indexed)
7392 }
7393