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 ®ions) 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 ®ions,
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