1 /**
2 * @file
3 * @brief Functions used to create vaults.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "maps.h"
9
10 #include <algorithm>
11 #include <cstdlib>
12 #include <cstring>
13 #include <sys/param.h>
14 #include <sys/types.h>
15 #if defined(UNIX) || defined(TARGET_COMPILER_MINGW)
16 #include <unistd.h>
17 #endif
18
19 #include "branch.h"
20 #include "coord.h"
21 #include "coordit.h"
22 #include "dbg-maps.h"
23 #include "dungeon.h"
24 #include "end.h"
25 #include "endianness.h"
26 #include "files.h"
27 #include "mapmark.h"
28 #include "message.h"
29 #include "state.h"
30 #include "stringutil.h"
31 #include "syscalls.h"
32 #include "tag-version.h"
33 #include "terrain.h"
34
35 #ifndef BYTE_ORDER
36 # error BYTE_ORDER is not defined
37 #endif
38 #if BYTE_ORDER == LITTLE_ENDIAN
39 # define WORD_LEN (int8_t)sizeof(long)
40 #else
41 # define WORD_LEN -(int8_t)sizeof(long)
42 #endif
43
44 static map_section_type _write_vault(map_def &mdef,
45 vault_placement &,
46 bool check_place);
47 static map_section_type _apply_vault_definition(
48 map_def &def,
49 vault_placement &,
50 bool check_place);
51
52 static bool _resolve_map(map_def &def);
53
54 static bool _map_safe_vault_place(const map_def &map,
55 const coord_def &c,
56 const coord_def &size);
57
58 // Globals: Use unwind_var to modify!
59
60 // Checks whether a map place is valid.
61 map_place_check_t map_place_valid = _map_safe_vault_place;
62
63 // If non-empty, any floating vault's @ exit must land on these point.
64 point_vector map_anchor_points;
65
66 //////////////////////////////////////////////////////////////////////////
67 // New style vault definitions
68
69 static map_vector vdefs;
70
71 // Parameter array that vault code can use.
72 string_vector map_parameters;
73
dgn_map_parameters(const string & astring)74 dgn_map_parameters::dgn_map_parameters(const string &astring)
75 : mpar(map_parameters)
76 {
77 map_parameters.push_back(astring);
78 }
79
dgn_map_parameters(const string_vector & parameters)80 dgn_map_parameters::dgn_map_parameters(const string_vector ¶meters)
81 : mpar(map_parameters)
82 {
83 map_parameters = parameters;
84 }
85
86 static bool _debug_ignore_depth = false;
87
88 // This is useful only in very unusual debugging cases; see e.g. the
89 // placement.lua placement testing script
dgn_ignore_depth(bool b)90 void dgn_ignore_depth(bool b)
91 {
92 _debug_ignore_depth = b;
93 }
94
95 /* ******************** BEGIN PUBLIC FUNCTIONS ******************* */
96
97 // Remember (!!!) - if a member of the monster array isn't specified
98 // within a vault subroutine, assume that it holds a random monster
99 // -- only in cases of explicit monsters assignment have I listed
100 // out random monster insertion {dlb}
101
102 // Make sure that vault_n, where n is a number, is a vault which can be put
103 // anywhere, while other vault names are for specific level ranges, etc.
vault_main(vault_placement & place,const map_def * vault,bool check_place)104 map_section_type vault_main(vault_placement &place, const map_def *vault,
105 bool check_place)
106 {
107 #ifdef DEBUG_STATISTICS
108 if (crawl_state.map_stat_gen)
109 mapstat_report_map_try(*vault);
110 #endif
111
112 // Return value of MAP_NONE forces dungeon.cc to regenerate the
113 // level, except for branch entry vaults where dungeon.cc just
114 // rejects the vault and places a vanilla entry.
115
116 return _write_vault(const_cast<map_def&>(*vault), place, check_place);
117 }
118
_write_vault(map_def & mdef,vault_placement & place,bool check_place)119 static map_section_type _write_vault(map_def &mdef,
120 vault_placement &place,
121 bool check_place)
122 {
123 mdef.load();
124
125 // Copy the map so we can monkey with it.
126 place.map = mdef;
127 place.map.original = &mdef;
128
129 // Try so many times to place the map. This will always succeed
130 // unless there are conflicting map placements in 'avoid', or there
131 // is a map validate Lua hook that keeps rejecting the map.
132 int tries = 25;
133
134 while (tries-- > 0)
135 {
136 // We're a regular vault, so clear the subvault stack.
137 clear_subvault_stack();
138
139 if (place.map.test_lua_veto())
140 break;
141
142 if (!_resolve_map(place.map))
143 {
144 // for most fatal errors, there's no point in trying again. (This
145 // isn't 100% true, e.g. an error in an itemspec that's hidden
146 // behind a random roll may fix itself. But it's true enough that
147 // it's better on average to consider this map bugged and move
148 // on to another one in normal generation.)
149 if (crawl_state.last_builder_error_fatal)
150 break;
151 else
152 continue;
153 }
154
155 // Must set size here, or minivaults will not be placed correctly.
156 place.size = place.map.size();
157 place.orient = _apply_vault_definition(place.map,
158 place, check_place);
159
160 if (place.orient != MAP_NONE)
161 return place.orient;
162 }
163 return MAP_NONE;
164 }
165
_dgn_flush_map_environments()166 static void _dgn_flush_map_environments()
167 {
168 // Clean up cached environments.
169 dlua.callfn("dgn_flush_map_environments", 0, 0);
170 }
171
_dgn_flush_map_environment_for(const string & mapname)172 static void _dgn_flush_map_environment_for(const string &mapname)
173 {
174 dlua.callfn("dgn_flush_map_environment_for", "s", mapname.c_str());
175 }
176
177 // Execute the map's Lua, perform substitutions and other transformations,
178 // and validate the map
_resolve_map_lua(map_def & map)179 static bool _resolve_map_lua(map_def &map)
180 {
181 _dgn_flush_map_environment_for(map.name);
182 map.reinit();
183
184 string err = map.run_lua(true);
185 if (!err.empty())
186 {
187 #ifdef DEBUG_STATISTICS
188 if (crawl_state.map_stat_gen)
189 mapstat_report_error(map, err);
190 #endif
191 crawl_state.last_builder_error = err;
192 crawl_state.last_builder_error_fatal = true;
193 mprf(MSGCH_ERROR, "Fatal lua error: %s", err.c_str());
194 return false;
195 }
196
197 map.fixup();
198 err = map.resolve();
199 if (!err.empty())
200 {
201 crawl_state.last_builder_error = err;
202 crawl_state.last_builder_error_fatal = true;
203 mprf(MSGCH_ERROR, "Map resolution error: %s", err.c_str());
204 return false;
205 }
206
207 // this is non-fatal: maps use validation to enforce basic placement
208 // constraints, and a failure here should mean retrying
209 if (!map.test_lua_validate(false))
210 {
211 crawl_state.last_builder_error = make_stringf(
212 "Lua validation for map %s failed.", map.name.c_str());
213 dprf("%s", crawl_state.last_builder_error.c_str());
214 return false;
215 }
216
217 return true;
218 }
219
220 // Resolve Lua and transformation directives, then mirror and rotate the
221 // map if allowed
_resolve_map(map_def & map)222 static bool _resolve_map(map_def &map)
223 {
224 if (!_resolve_map_lua(map))
225 return false;
226
227 // Don't bother flipping or rotating 1x1 subvaults.
228 // This just cuts down on level generation message spam.
229 if (map.map.width() == map.map.height() && map.map.width() == 1)
230 return true;
231
232 // Mirroring is possible for any map that does not explicitly forbid it.
233 // Note that mirroring also flips the orientation.
234 if (coinflip())
235 map.hmirror();
236
237 if (coinflip())
238 map.vmirror();
239
240 // The map may also refuse to be rotated.
241 if (coinflip())
242 map.rotate(coinflip());
243
244 return true;
245 }
246
resolve_subvault(map_def & map)247 bool resolve_subvault(map_def &map)
248 {
249 ASSERT(map.is_subvault());
250 if (!map.is_subvault())
251 return false;
252
253 if (map.test_lua_veto())
254 return false;
255
256 if (!_resolve_map_lua(map))
257 return false;
258
259 int width = map.subvault_width();
260 int height = map.subvault_height();
261
262 bool can_hmirror = !map.has_tag("no_hmirror");
263 bool can_vmirror = !map.has_tag("no_vmirror");
264
265 bool can_rot = (map.map.width() <= height && map.map.height() <= width)
266 && !map.has_tag("no_rotate");
267 bool must_rot = (map.map.width() > width || map.map.height() > height);
268
269 // Too big, whether or not it is rotated.
270 if (must_rot && !can_rot)
271 return false;
272
273 if (must_rot || can_rot && coinflip())
274 {
275 bool dir = coinflip();
276 map.rotate(dir);
277
278 // Update post-rotation dimensions.
279 width = map.subvault_width();
280 height = map.subvault_height();
281 }
282
283 // Inexact fits with dimensions flipped will get rotated above.
284 bool exact_fit = (map.map.height() == height && map.map.width() == width);
285 if (!exact_fit)
286 {
287 if (can_hmirror && coinflip())
288 map.hmirror();
289
290 if (can_vmirror && coinflip())
291 map.vmirror();
292
293 // The map may have refused to have been rotated, so verify dimensions.
294 bool valid = (map.map.width() <= width && map.map.height() <= height);
295 return valid;
296 }
297
298 // Don't bother flipping or rotating 1x1 subvaults.
299 // This just cuts down on level generation message spam.
300 if (exact_fit && width == height && width == 1)
301 return true;
302
303 // Count original mismatches. If mirroring the map causes more cells to
304 // not be written, then don't mirror. This allows oddly shaped subvaults
305 // to be fit correctly into a parent vault that specifies the exact same
306 // shape.
307 const coord_def svplace(0, 0);
308
309 // 0 - normal
310 // 1 - flip horz
311 // 2 - flip vert + horz
312 // 3 - flip vert
313 int mismatch[4];
314
315 // Mirror map in all directions to find best fit.
316 mismatch[0] = map.subvault_mismatch_count(svplace);
317 map.hmirror();
318 mismatch[1] = map.subvault_mismatch_count(svplace);
319 map.vmirror();
320 mismatch[2] = map.subvault_mismatch_count(svplace);
321 map.hmirror();
322 mismatch[3] = map.subvault_mismatch_count(svplace);
323
324 int min_mismatch = mismatch[0];
325 if (can_hmirror)
326 min_mismatch = min(min_mismatch, mismatch[1]);
327 if (can_hmirror && can_vmirror)
328 min_mismatch = min(min_mismatch, mismatch[2]);
329 if (can_vmirror)
330 min_mismatch = min(min_mismatch, mismatch[3]);
331
332 // Pick a mirror combination with the minimum number of mismatches.
333 min_mismatch = min(min_mismatch, mismatch[2]);
334 if (!map.has_tag("no_vmirror"))
335 min_mismatch = min(min_mismatch, mismatch[3]);
336
337 // Pick a mirror combination with the minimum number of mismatches.
338 int idx = random2(4);
339 while (mismatch[idx] != min_mismatch)
340 idx = (idx + 1) % 4;
341
342 // Flip the map (currently vmirror'd) to the correct orientation.
343 if (idx == 0)
344 map.vmirror();
345 else if (idx == 1)
346 {
347 map.vmirror();
348 map.hmirror();
349 }
350 else if (idx == 2)
351 map.hmirror();
352
353 ASSERT(map.subvault_mismatch_count(svplace) == min_mismatch);
354
355 // We already know this is an exact fit, so this is a success.
356 return true;
357 }
358
359 // Given a rectangular region, slides it to fit into the map. size must be
360 // smaller than (GXM,GYM).
_fit_region_into_map_bounds(coord_def & pos,const coord_def & size,int margin)361 static void _fit_region_into_map_bounds(coord_def &pos, const coord_def &size,
362 int margin)
363 {
364 const int X_1(X_BOUND_1 + margin);
365 const int X_2(X_BOUND_2 - margin);
366 const int Y_1(Y_BOUND_1 + margin);
367 const int Y_2(Y_BOUND_2 - margin);
368
369 ASSERT(size.x <= (X_2 - X_1 + 1));
370 ASSERT(size.y <= (Y_2 - Y_1 + 1));
371
372 if (pos.x < X_1)
373 pos.x = X_1;
374 if (pos.y < Y_1)
375 pos.y = Y_1;
376 if (pos.x + size.x - 1 > X_2)
377 pos.x = X_2 - size.x + 1;
378 if (pos.y + size.y - 1 > Y_2)
379 pos.y = Y_2 - size.y + 1;
380 }
381
382 // Used for placement of vaults.
_may_overwrite_feature(const coord_def p,bool water_ok,bool wall_ok=true)383 static bool _may_overwrite_feature(const coord_def p,
384 bool water_ok, bool wall_ok = true)
385 {
386 // If there's a mask specifying where vaults can be placed, don't
387 // allow stepping outside it.
388 if (Vault_Placement_Mask && !(*Vault_Placement_Mask)(p))
389 return false;
390
391 // If in the abyss, the placement mask is the only check necessary
392 // for terrain.
393 if (Vault_Placement_Mask && player_in_branch(BRANCH_ABYSS))
394 return true;
395
396 const dungeon_feature_type grid = env.grid(p);
397
398 // Deep water grids may be overwritten if water_ok == true.
399 if (grid == DNGN_DEEP_WATER)
400 return water_ok;
401
402 // Handle all other non-LOS blocking grids here.
403 if (!feat_is_opaque(grid)
404 && grid != DNGN_FLOOR
405 && grid != DNGN_SHALLOW_WATER
406 && !feat_is_door(grid))
407 {
408 return false;
409 }
410
411 if (feat_is_wall(grid) || feat_is_tree(grid))
412 return wall_ok;
413
414 // Otherwise, feel free to clobber this feature.
415 return true;
416 }
417
_marker_is_portal(map_marker * marker)418 static bool _marker_is_portal(map_marker* marker)
419 {
420 return marker && !marker->property("portal").empty();
421 }
422
_is_portal_place(const coord_def & c)423 static bool _is_portal_place(const coord_def &c)
424 {
425 return _marker_is_portal(env.markers.find(c, MAT_LUA_MARKER));
426 }
427
_is_transporter_place(const coord_def & c)428 static bool _is_transporter_place(const coord_def &c)
429 {
430 auto m = env.markers.find(c, MAT_LUA_MARKER);
431 return m && (!m->property(TRANSPORTER_NAME_PROP).empty()
432 || !m->property(TRANSPORTER_DEST_NAME_PROP).empty());
433 }
434
_map_safe_vault_place(const map_def & map,const coord_def & c,const coord_def & size)435 static bool _map_safe_vault_place(const map_def &map,
436 const coord_def &c,
437 const coord_def &size)
438 {
439 if (size.zero())
440 return true;
441
442 // Processing for layouts is handled elsewhere.
443 if (map.is_overwritable_layout())
444 return true;
445
446 const bool water_ok =
447 map.has_tag("water_ok") || player_in_branch(BRANCH_SWAMP);
448
449 const bool vault_can_overwrite_other_vaults =
450 map.has_tag("overwrite_floor_cell");
451
452 const bool vault_can_replace_portals =
453 map.has_tag("replace_portal");
454
455 const vector<string> &lines = map.map.get_lines();
456 for (rectangle_iterator ri(c, c + size - 1); ri; ++ri)
457 {
458 const coord_def cp(*ri);
459 const coord_def dp(cp - c);
460
461 if (lines[dp.y][dp.x] == ' ')
462 continue;
463
464 // Unconditionally allow portal placements to work.
465 if (vault_can_replace_portals && _is_portal_place(cp))
466 continue;
467
468 if (!vault_can_overwrite_other_vaults)
469 {
470 // Also check adjacent squares for collisions, because being next
471 // to another vault may block off one of this vault's exits.
472 for (adjacent_iterator ai(cp); ai; ++ai)
473 {
474 if (map_bounds(*ai) && (env.level_map_mask(*ai) & MMT_VAULT))
475 return false;
476 }
477 }
478 else if (env.grid(cp) != DNGN_FLOOR || env.pgrid(cp) & FPROP_NO_TELE_INTO
479 || _is_transporter_place(cp))
480 {
481 // Don't place overwrite_floor_cell vaults on anything but floor or
482 // on squares that can't be teleported into, because
483 // overwrite_floor_cell is used for things that are expected to be
484 // connected. Don't place on transporter markers, because these will
485 // later themselves overwrite whatever feature this vault places.
486 return false;
487 }
488
489 // Don't overwrite features other than floor, rock wall, doors,
490 // nor water, if !water_ok.
491 if (!_may_overwrite_feature(cp, water_ok))
492 return false;
493
494 // Don't overwrite monsters or items, either!
495 if (monster_at(cp) || env.igrid(cp) != NON_ITEM)
496 return false;
497
498 // If in Slime, don't let stairs end up next to minivaults,
499 // so that they don't possibly end up next to unsafe walls.
500 if (player_in_branch(BRANCH_SLIME))
501 {
502 for (adjacent_iterator ai(cp); ai; ++ai)
503 {
504 if (map_bounds(*ai) && feat_is_stair(env.grid(*ai)))
505 return false;
506 }
507 }
508 }
509
510 return true;
511 }
512
_connected_minivault_place(const coord_def & c,const vault_placement & place)513 static bool _connected_minivault_place(const coord_def &c,
514 const vault_placement &place)
515 {
516 if (place.size.zero())
517 return true;
518
519 // Must not be completely isolated.
520 const vector<string> &lines = place.map.map.get_lines();
521
522 for (rectangle_iterator ri(c, c + place.size - 1); ri; ++ri)
523 {
524 const coord_def &ci(*ri);
525
526 if (lines[ci.y - c.y][ci.x - c.x] == ' ')
527 continue;
528
529 if (_may_overwrite_feature(ci, false, false)
530 || (place.map.has_tag("replace_portal")
531 && _is_portal_place(ci)))
532 {
533 return true;
534 }
535 }
536
537 return false;
538 }
539
find_portal_place(const vault_placement * place,bool check_place)540 coord_def find_portal_place(const vault_placement *place, bool check_place)
541 {
542 vector<coord_def> candidates;
543 for (auto marker : env.markers.get_all(MAT_LUA_MARKER))
544 {
545 if (_marker_is_portal(marker))
546 {
547 coord_def v1(marker->pos);
548 if ((!check_place
549 || place && map_place_valid(place->map, v1, place->size))
550 && (!place || _connected_minivault_place(v1, *place))
551 && !feat_is_gate(env.grid(v1))
552 && !feat_is_branch_entrance(env.grid(v1)))
553 {
554 candidates.push_back(v1);
555 }
556 }
557 }
558
559 if (!candidates.empty())
560 return candidates[random2(candidates.size())];
561
562 return coord_def();
563 }
564
_find_minivault_place(const vault_placement & place,bool check_place)565 static coord_def _find_minivault_place(
566 const vault_placement &place,
567 bool check_place)
568 {
569 if (place.map.has_tag("replace_portal"))
570 {
571 coord_def portal_place = find_portal_place(&place, check_place);
572 if (!portal_place.origin())
573 return portal_place;
574 }
575
576 // [ds] The margin around the edges of the map where the minivault
577 // won't be placed. Purely arbitrary as far as I can see.
578 // The spotty connector in the Shoals needs one more space to work.
579 const int margin = MAPGEN_BORDER * 2 + player_in_branch(BRANCH_SHOALS);
580
581 // Find a target area which can be safely overwritten.
582 for (int tries = 0; tries < 600; ++tries)
583 {
584 coord_def v1;
585 v1.x = random_range(margin, GXM - margin - place.size.x);
586 v1.y = random_range(margin, GYM - margin - place.size.y);
587
588 if (check_place && !map_place_valid(place.map, v1, place.size))
589 {
590 #ifdef DEBUG_MINIVAULT_PLACEMENT
591 mprf(MSGCH_DIAGNOSTICS,
592 "Skipping (%d,%d): not safe to place map",
593 v1.x, v1.y);
594 #endif
595 continue;
596 }
597
598 if (!_connected_minivault_place(v1, place))
599 {
600 #ifdef DEBUG_MINIVAULT_PLACEMENT
601 mprf(MSGCH_DIAGNOSTICS,
602 "Skipping (%d,%d): not a good minivault place (tags: %s)",
603 v1.x, v1.y, place.map.tags.c_str());
604 #endif
605 continue;
606 }
607 return v1;
608 }
609 return coord_def(-1, -1);
610 }
611
_apply_vault_grid(map_def & def,vault_placement & place,bool check_place)612 static bool _apply_vault_grid(map_def &def,
613 vault_placement &place,
614 bool check_place)
615 {
616 const map_lines &ml = def.map;
617 const int orient = def.orient;
618
619 const coord_def size(ml.size());
620
621 coord_def start(0, 0);
622
623 if (orient == MAP_SOUTH || orient == MAP_SOUTHEAST
624 || orient == MAP_SOUTHWEST)
625 {
626 start.y = GYM - size.y;
627 }
628 if (orient == MAP_EAST || orient == MAP_NORTHEAST
629 || orient == MAP_SOUTHEAST)
630 {
631 start.x = GXM - size.x;
632 }
633
634 // Handle maps aligned along cardinals that are smaller than
635 // the corresponding map dimension.
636 if ((orient == MAP_NORTH || orient == MAP_SOUTH
637 || orient == MAP_ENCOMPASS || orient == MAP_CENTRE)
638 && size.x < GXM)
639 {
640 start.x = (GXM - size.x) / 2;
641 }
642 if ((orient == MAP_EAST || orient == MAP_WEST
643 || orient == MAP_ENCOMPASS || orient == MAP_CENTRE)
644 && size.y < GYM)
645 {
646 start.y = (GYM - size.y) / 2;
647 }
648
649 // Floating maps can go anywhere, ask the map_def to suggest a place.
650 if (orient == MAP_FLOAT)
651 {
652 const bool minivault = def.is_minivault();
653 if (map_bounds(place.pos))
654 {
655 start = place.pos - size / 2;
656 _fit_region_into_map_bounds(start, size, minivault ? MAPGEN_BORDER : 0);
657 }
658 else if (minivault)
659 {
660 start = _find_minivault_place(place, check_place);
661 if (map_bounds(start))
662 _fit_region_into_map_bounds(start, size, MAPGEN_BORDER);
663 }
664 else
665 start = def.float_place();
666 }
667
668 if (!map_bounds(start))
669 return false;
670
671 if (check_place && !map_place_valid(def, start, size))
672 {
673 dprf(DIAG_DNGN, "Bad vault place: (%d,%d) dim (%d,%d)",
674 start.x, start.y, size.x, size.y);
675 return false;
676 }
677
678 place.pos = start;
679 place.size = size;
680
681 return true;
682 }
683
_apply_vault_definition(map_def & def,vault_placement & place,bool check_place)684 static map_section_type _apply_vault_definition(
685 map_def &def,
686 vault_placement &place,
687 bool check_place)
688 {
689 if (!_apply_vault_grid(def, place, check_place))
690 return MAP_NONE;
691
692 const map_section_type orient = def.orient;
693 return orient == MAP_NONE ? MAP_NORTH : orient;
694 }
695
696 ///////////////////////////////////////////////////////////////////////////
697 // Map lookups
698
_map_matches_layout_type(const map_def & map)699 static bool _map_matches_layout_type(const map_def &map)
700 {
701 bool permissive = false;
702 if (env.level_layout_types.empty()
703 || (!map.has_tag_prefix("layout_")
704 && !(permissive = map.has_tag_prefix("nolayout_"))))
705 {
706 return true;
707 }
708
709 for (const auto &layout : env.level_layout_types)
710 {
711 if (map.has_tag("layout_" + layout))
712 return true;
713 else if (map.has_tag("nolayout_" + layout))
714 return false;
715 }
716
717 return permissive;
718 }
719
_map_matches_species(const map_def & map)720 static bool _map_matches_species(const map_def &map)
721 {
722 if (!species::is_valid(you.species))
723 return true;
724 return !map.has_tag("no_species_"
725 + lowercase_string(species::get_abbrev(you.species)));
726 }
727
find_map_by_name(const string & name)728 const map_def *find_map_by_name(const string &name)
729 {
730 for (const map_def &mapdef : vdefs)
731 if (mapdef.name == name)
732 return &mapdef;
733
734 return nullptr;
735 }
736
737 // Discards Lua code loaded by all maps to reduce memory use. If any stripped
738 // map is reused, its data will be reloaded from the .dsc
strip_all_maps()739 void strip_all_maps()
740 {
741 for (map_def &mapdef : vdefs)
742 mapdef.strip();
743 }
744
find_map_matches(const string & name)745 vector<string> find_map_matches(const string &name)
746 {
747 vector<string> matches;
748
749 for (const map_def &mapdef : vdefs)
750 if (mapdef.name.find(name) != string::npos)
751 matches.push_back(mapdef.name);
752 return matches;
753 }
754
find_maps_for_tag(const string & tag,bool check_depth,bool check_used)755 mapref_vector find_maps_for_tag(const string &tag,
756 bool check_depth,
757 bool check_used)
758 {
759 mapref_vector maps;
760 level_id place = level_id::current();
761 unordered_set<string> tag_set = parse_tags(tag);
762
763 for (const map_def &mapdef : vdefs)
764 {
765 if (mapdef.has_all_tags(tag_set.begin(), tag_set.end())
766 && !mapdef.has_tag("dummy")
767 && (!check_depth || _debug_ignore_depth
768 || !mapdef.has_depth()
769 || mapdef.is_usable_in(place))
770 && (!check_used || !mapdef.map_already_used()))
771 {
772 maps.push_back(&mapdef);
773 }
774 }
775 return maps;
776 }
777
778 struct map_selector
779 {
780 private:
781 enum select_type
782 {
783 PLACE,
784 DEPTH,
785 DEPTH_AND_CHANCE,
786 TAG,
787 };
788
789 public:
790 bool accept(const map_def &md) const;
791 void announce(const map_def *map) const;
792
validmap_selector793 bool valid() const
794 {
795 return sel == TAG || place.is_valid();
796 }
797
by_placemap_selector798 static map_selector by_place(const level_id &_place, bool _mini,
799 maybe_bool _extra)
800 {
801 return map_selector(map_selector::PLACE, _place, "", _mini, _extra,
802 false);
803 }
804
by_depthmap_selector805 static map_selector by_depth(const level_id &_place, bool _mini,
806 maybe_bool _extra)
807 {
808 return map_selector(map_selector::DEPTH, _place, "", _mini, _extra,
809 true);
810 }
811
by_depth_chancemap_selector812 static map_selector by_depth_chance(const level_id &_place,
813 maybe_bool _extra)
814 {
815 return map_selector(map_selector::DEPTH_AND_CHANCE, _place, "", false,
816 _extra, true);
817 }
818
by_tagmap_selector819 static map_selector by_tag(const string &_tag,
820 bool _check_depth,
821 bool _check_chance,
822 maybe_bool _extra,
823 const level_id &_place = level_id::current())
824 {
825 map_selector msel = map_selector(map_selector::TAG, _place, _tag,
826 false, _extra, _check_depth);
827 msel.ignore_chance = !_check_chance;
828 return msel;
829 }
830
831 private:
map_selectormap_selector832 map_selector(select_type _typ, const level_id &_pl,
833 const string &_tag,
834 bool _mini, maybe_bool _extra, bool _check_depth)
835 : ignore_chance(false), preserve_dummy(false),
836 sel(_typ), place(_pl), tag(_tag),
837 mini(_mini), extra(_extra),
838 check_depth(_check_depth),
839 check_layout((sel == DEPTH || sel == DEPTH_AND_CHANCE)
840 && place == level_id::current())
841 {
842 if (_typ == PLACE)
843 ignore_chance = true;
844 }
845
846 bool depth_selectable(const map_def &) const;
847
848 public:
849 bool ignore_chance;
850 bool preserve_dummy;
851 const select_type sel;
852 const level_id place;
853 const string tag;
854 const bool mini;
855 const maybe_bool extra;
856 const bool check_depth;
857 const bool check_layout;
858 };
859
_overflow_range(level_id place)860 static bool _overflow_range(level_id place)
861 {
862 // Intentionally not checked for the minimum, this is to exclude
863 // depth selection for overflow temples as mini vaults before the
864 // MAX_OVERFLOW_LEVEL
865 return place.branch == BRANCH_DUNGEON && place.depth <= MAX_OVERFLOW_LEVEL;
866 }
867
depth_selectable(const map_def & mapdef) const868 bool map_selector::depth_selectable(const map_def &mapdef) const
869 {
870 return mapdef.is_usable_in(place)
871 // Some tagged levels cannot be selected as random
872 // maps in a specific depth:
873 && !mapdef.has_tag_suffix("entry")
874 && !mapdef.has_tag("unrand")
875 && !mapdef.has_tag("place_unique")
876 && !mapdef.has_tag("tutorial")
877 && (!mapdef.has_tag_prefix("temple_")
878 || !_overflow_range(place)
879 && mapdef.has_tag_prefix("uniq_altar_"))
880 && _map_matches_species(mapdef)
881 && (!check_layout || _map_matches_layout_type(mapdef));
882 }
883
_is_extra_compatible(maybe_bool want_extra,bool have_extra)884 static bool _is_extra_compatible(maybe_bool want_extra, bool have_extra)
885 {
886 return want_extra == MB_MAYBE
887 || (want_extra == MB_TRUE && have_extra)
888 || (want_extra == MB_FALSE && !have_extra);
889 }
890
accept(const map_def & mapdef) const891 bool map_selector::accept(const map_def &mapdef) const
892 {
893 switch (sel)
894 {
895 case PLACE:
896 if (mapdef.has_tag_prefix("tutorial")
897 && (!crawl_state.game_is_tutorial()
898 || !mapdef.has_tag(crawl_state.map)))
899 {
900 return false;
901 }
902 return mapdef.is_minivault() == mini
903 && _is_extra_compatible(extra, mapdef.is_extra_vault())
904 && mapdef.place.is_usable_in(place)
905 && _map_matches_layout_type(mapdef)
906 && !mapdef.map_already_used();
907
908 case DEPTH:
909 {
910 const map_chance chance(mapdef.chance(place));
911 return mapdef.is_minivault() == mini
912 && _is_extra_compatible(extra, mapdef.is_extra_vault())
913 && (!chance.valid() || mapdef.has_tag("dummy"))
914 && depth_selectable(mapdef)
915 && !mapdef.map_already_used();
916 }
917
918 case DEPTH_AND_CHANCE:
919 {
920 const map_chance chance(mapdef.chance(place));
921 // Only vaults with valid chance
922 return chance.valid()
923 && !mapdef.has_tag("dummy")
924 && depth_selectable(mapdef)
925 && _is_extra_compatible(extra, mapdef.is_extra_vault())
926 && !mapdef.map_already_used();
927 }
928
929 case TAG:
930 return mapdef.has_all_tags(tag) // allow multiple tags, for temple overflow vaults
931 && (!check_depth || _debug_ignore_depth
932 || !mapdef.has_depth()
933 || mapdef.is_usable_in(place))
934 && _map_matches_species(mapdef)
935 && _map_matches_layout_type(mapdef)
936 && !mapdef.map_already_used();
937
938 default:
939 return false;
940 }
941 }
942
announce(const map_def * vault) const943 void map_selector::announce(const map_def *vault) const
944 {
945 #ifdef DEBUG_DIAGNOSTICS
946 if (vault)
947 {
948 if (sel == DEPTH_AND_CHANCE)
949 {
950 dprf(DIAG_DNGN,
951 "[CHANCE+DEPTH] Found map %s for %s (%s)",
952 vault->name.c_str(), place.describe().c_str(),
953 vault->chance(place).describe().c_str());
954 }
955 else
956 {
957 const char *format =
958 sel == PLACE ? "[PLACE] Found map %s for %s" :
959 sel == DEPTH ? "[DEPTH] Found random map %s for %s" :
960 "[TAG] Found map %s tagged '%s'";
961
962 dprf(DIAG_DNGN, format,
963 vault->name.c_str(),
964 sel == TAG ? tag.c_str() : place.describe().c_str());
965 }
966 }
967 #else
968 UNUSED(vault);
969 #endif
970 }
971
vault_chance_tag(const map_def & map)972 string vault_chance_tag(const map_def &map)
973 {
974 if (map.has_tag_prefix("chance_"))
975 {
976 for (const string &tag : map.get_tags())
977 if (tag.find("chance_") == 0)
978 return tag;
979 }
980 return "";
981 }
982
983 typedef vector<unsigned> vault_indices;
984
_eligible_maps_for_selector(const map_selector & sel)985 static vault_indices _eligible_maps_for_selector(const map_selector &sel)
986 {
987 vault_indices eligible;
988
989 if (sel.valid())
990 {
991 for (unsigned i = 0, size = vdefs.size(); i < size; ++i)
992 if (sel.accept(vdefs[i]))
993 eligible.push_back(i);
994 }
995
996 return eligible;
997 }
998
999 static const map_def *_random_map_by_selector(const map_selector &sel);
1000
_vault_chance_new(const map_def & map,const level_id & place,set<string> & chance_tags)1001 static bool _vault_chance_new(const map_def &map,
1002 const level_id &place,
1003 set<string> &chance_tags)
1004 {
1005 if (map.chance(place).valid())
1006 {
1007 // There may be several alternatives for a portal
1008 // vault that want to be governed by one common
1009 // CHANCE. In this case each vault will use a
1010 // CHANCE, and a common chance_xxx tag. Pick the
1011 // first such vault for the chance roll.
1012 const string tag = vault_chance_tag(map);
1013 if (!chance_tags.count(tag))
1014 {
1015 if (!tag.empty())
1016 chance_tags.insert(tag);
1017 return true;
1018 }
1019 }
1020 return false;
1021 }
1022
1023 class vault_chance_roll_iterator
1024 {
1025 public:
vault_chance_roll_iterator(const mapref_vector & _maps)1026 vault_chance_roll_iterator(const mapref_vector &_maps)
1027 : place(level_id::current()),
1028 current(_maps.begin()), end(_maps.end())
1029 {
1030 find_valid();
1031 }
1032
operator bool() const1033 operator bool () const { return current != end; }
operator *() const1034 const map_def *operator * () const { return *current; }
operator ->() const1035 const map_def *operator -> () const { return *current; }
1036
operator ++()1037 vault_chance_roll_iterator &operator ++ ()
1038 {
1039 ++current;
1040 find_valid();
1041 return *this;
1042 }
1043
operator ++(int)1044 vault_chance_roll_iterator operator ++ (int)
1045 {
1046 vault_chance_roll_iterator copy(*this);
1047 operator ++ ();
1048 return copy;
1049 }
1050
1051 private:
find_valid()1052 void find_valid()
1053 {
1054 while (current != end && !(*current)->chance(place).roll())
1055 ++current;
1056 }
1057
1058 private:
1059 level_id place;
1060 mapref_vector::const_iterator current;
1061 mapref_vector::const_iterator end;
1062 };
1063
_resolve_chance_vault(const map_selector & sel,const map_def * map)1064 static const map_def *_resolve_chance_vault(const map_selector &sel,
1065 const map_def *map)
1066 {
1067 const string chance_tag = vault_chance_tag(*map);
1068 // If this map has a chance_ tag, convert the search into
1069 // a lookup for that tag.
1070 if (!chance_tag.empty())
1071 {
1072 map_selector msel = map_selector::by_tag(chance_tag,
1073 sel.check_depth,
1074 false,
1075 sel.extra,
1076 sel.place);
1077 return _random_map_by_selector(msel);
1078 }
1079 return map;
1080 }
1081
1082 static mapref_vector
_random_chance_maps_in_list(const map_selector & sel,const vault_indices & filtered)1083 _random_chance_maps_in_list(const map_selector &sel,
1084 const vault_indices &filtered)
1085 {
1086 // Vaults that are eligible and have >0 chance.
1087 mapref_vector chance;
1088 mapref_vector chosen_chances;
1089
1090 typedef set<string> tag_set;
1091 tag_set chance_tags;
1092
1093 for (const int i : filtered)
1094 if (!sel.ignore_chance
1095 && _vault_chance_new(vdefs[i], sel.place, chance_tags))
1096 {
1097 chance.push_back(&vdefs[i]);
1098 }
1099
1100 for (vault_chance_roll_iterator vc(chance); vc; ++vc)
1101 if (const map_def *chosen = _resolve_chance_vault(sel, *vc))
1102 {
1103 chosen_chances.push_back(chosen);
1104 sel.announce(chosen);
1105 }
1106
1107 return chosen_chances;
1108 }
1109
1110 static const map_def *
_random_map_in_list(const map_selector & sel,const vault_indices & filtered)1111 _random_map_in_list(const map_selector &sel,
1112 const vault_indices &filtered)
1113 {
1114 const map_def *chosen_map = nullptr;
1115 int rollsize = 0;
1116
1117 // First build a list of vaults that could be used:
1118 mapref_vector eligible;
1119
1120 // Vaults that are eligible and have >0 chance.
1121 mapref_vector chance;
1122
1123 typedef set<string> tag_set;
1124 tag_set chance_tags;
1125
1126 for (auto i : filtered)
1127 {
1128 if (!sel.ignore_chance && vdefs[i].chance(sel.place).valid())
1129 {
1130 if (_vault_chance_new(vdefs[i], sel.place, chance_tags))
1131 chance.push_back(&vdefs[i]);
1132 }
1133 else
1134 eligible.push_back(&vdefs[i]);
1135 }
1136
1137 for (vault_chance_roll_iterator vc(chance); vc; ++vc)
1138 if (const map_def *chosen = _resolve_chance_vault(sel, *vc))
1139 {
1140 chosen_map = chosen;
1141 break;
1142 }
1143
1144 if (!chosen_map)
1145 {
1146 const level_id &here(level_id::current());
1147 for (auto map : eligible)
1148 {
1149 const int weight = map->weight(here);
1150
1151 if (weight <= 0)
1152 continue;
1153
1154 rollsize += weight;
1155
1156 if (rollsize && x_chance_in_y(weight, rollsize))
1157 chosen_map = map;
1158 }
1159 }
1160
1161 if (!sel.preserve_dummy && chosen_map
1162 && chosen_map->has_tag("dummy"))
1163 {
1164 chosen_map = nullptr;
1165 }
1166
1167 sel.announce(chosen_map);
1168 return chosen_map;
1169 }
1170
_random_map_by_selector(const map_selector & sel)1171 static const map_def *_random_map_by_selector(const map_selector &sel)
1172 {
1173 const vault_indices filtered = _eligible_maps_for_selector(sel);
1174 return _random_map_in_list(sel, filtered);
1175 }
1176
1177 // Returns a map for which PLACE: matches the given place.
random_map_for_place(const level_id & place,bool minivault,maybe_bool extra)1178 const map_def *random_map_for_place(const level_id &place, bool minivault,
1179 maybe_bool extra)
1180 {
1181 return _random_map_by_selector(
1182 map_selector::by_place(place, minivault, extra));
1183 }
1184
random_map_in_depth(const level_id & place,bool want_minivault,maybe_bool extra)1185 const map_def *random_map_in_depth(const level_id &place, bool want_minivault,
1186 maybe_bool extra)
1187 {
1188 return _random_map_by_selector(
1189 map_selector::by_depth(place, want_minivault, extra));
1190 }
1191
random_chance_maps_in_depth(const level_id & place,maybe_bool extra)1192 mapref_vector random_chance_maps_in_depth(const level_id &place,
1193 maybe_bool extra)
1194 {
1195 map_selector sel = map_selector::by_depth_chance(place, extra);
1196 const vault_indices eligible = _eligible_maps_for_selector(sel);
1197 return _random_chance_maps_in_list(sel, eligible);
1198 }
1199
random_map_for_tag(const string & tag,bool check_depth,bool check_chance,maybe_bool extra)1200 const map_def *random_map_for_tag(const string &tag,
1201 bool check_depth,
1202 bool check_chance,
1203 maybe_bool extra)
1204 {
1205 return _random_map_by_selector(
1206 map_selector::by_tag(tag, check_depth, check_chance, extra));
1207 }
1208
map_count()1209 int map_count()
1210 {
1211 return vdefs.size();
1212 }
1213
1214 /////////////////////////////////////////////////////////////////////////////
1215 // Reading maps from .des files.
1216
1217 // All global preludes.
1218 static vector<dlua_chunk> global_preludes;
1219
1220 // Map-specific prelude.
1221 dlua_chunk lc_global_prelude("global_prelude");
1222 string lc_desfile;
1223 map_def lc_map;
1224 depth_ranges lc_default_depths;
1225 bool lc_run_global_prelude = true;
1226 map_load_info_t lc_loaded_maps;
1227
1228 static set<string> map_files_read;
1229
1230 extern int yylineno;
1231
_reset_map_parser()1232 static void _reset_map_parser()
1233 {
1234 lc_map.init();
1235 lc_default_depths.clear();
1236 lc_global_prelude.clear();
1237
1238 yylineno = 1;
1239 lc_run_global_prelude = true;
1240 }
1241
1242 ////////////////////////////////////////////////////////////////////////////
1243
1244 static bool checked_des_index_dir = false;
1245
_des_cache_dir(const string & relpath="")1246 static string _des_cache_dir(const string &relpath = "")
1247 {
1248 return catpath(savedir_versioned_path("des"), relpath);
1249 }
1250
_check_des_index_dir()1251 static void _check_des_index_dir()
1252 {
1253 if (checked_des_index_dir)
1254 return;
1255
1256 string desdir = _des_cache_dir();
1257 if (!check_mkdir("Data file cache", &desdir, true))
1258 end(1, true, "Can't create data file cache: %s", desdir.c_str());
1259
1260 checked_des_index_dir = true;
1261 }
1262
get_descache_path(const string & file,const string & ext)1263 string get_descache_path(const string &file, const string &ext)
1264 {
1265 const string basename = change_file_extension(get_base_filename(file), ext);
1266 return _des_cache_dir(basename);
1267 }
1268
verify_file_version(const string & file,time_t mtime)1269 static bool verify_file_version(const string &file, time_t mtime)
1270 {
1271 FILE *fp = fopen_u(file.c_str(), "rb");
1272 if (!fp)
1273 return false;
1274 try
1275 {
1276 reader inf(fp);
1277 const auto version = get_save_version(inf);
1278 const auto major = version.major, minor = version.minor;
1279 const int8_t word = unmarshallByte(inf);
1280 const int64_t t = unmarshallSigned(inf);
1281 fclose(fp);
1282 return major == TAG_MAJOR_VERSION
1283 && minor <= TAG_MINOR_VERSION
1284 && word == WORD_LEN
1285 && t == mtime;
1286 }
1287 catch (short_read_exception &E)
1288 {
1289 fclose(fp);
1290 return false;
1291 }
1292 }
1293
_verify_map_index(const string & base,time_t mtime)1294 static bool _verify_map_index(const string &base, time_t mtime)
1295 {
1296 return verify_file_version(base + ".idx", mtime);
1297 }
1298
_verify_map_full(const string & base,time_t mtime)1299 static bool _verify_map_full(const string &base, time_t mtime)
1300 {
1301 return verify_file_version(base + ".dsc", mtime);
1302 }
1303
_load_map_index(const string & cache,const string & base,time_t mtime)1304 static bool _load_map_index(const string& cache, const string &base,
1305 time_t mtime)
1306 {
1307 // If there's a global prelude, load that first.
1308 if (FILE *fp = fopen_u((base + ".lux").c_str(), "rb"))
1309 {
1310 reader inf(fp, TAG_MINOR_VERSION);
1311 const auto version = get_save_version(inf);
1312 const auto major = version.major, minor = version.minor;
1313 int8_t word = unmarshallByte(inf);
1314 int64_t t = unmarshallSigned(inf);
1315 if (major != TAG_MAJOR_VERSION || minor > TAG_MINOR_VERSION
1316 || word != WORD_LEN || t != mtime)
1317 {
1318 return false;
1319 }
1320
1321 lc_global_prelude.read(inf);
1322 fclose(fp);
1323
1324 global_preludes.push_back(lc_global_prelude);
1325 }
1326
1327 FILE* fp = fopen_u((base + ".idx").c_str(), "rb");
1328 if (!fp)
1329 end(1, true, "Unable to read %s", (base + ".idx").c_str());
1330
1331 reader inf(fp, TAG_MINOR_VERSION);
1332 // Re-check version, might have been modified in the meantime.
1333 const auto version = get_save_version(inf);
1334 const auto major = version.major, minor = version.minor;
1335 int8_t word = unmarshallByte(inf);
1336 int64_t t = unmarshallSigned(inf);
1337 if (major != TAG_MAJOR_VERSION || minor > TAG_MINOR_VERSION
1338 || word != WORD_LEN || t != mtime)
1339 {
1340 return false;
1341 }
1342
1343 #if TAG_MAJOR_VERSION == 34
1344 // Throw out indices that could have CHANCE priority entirely.
1345 if (minor < TAG_MINOR_NO_PRIORITY)
1346 return false;
1347 #endif
1348
1349 const int nmaps = unmarshallShort(inf);
1350 const int nexist = vdefs.size();
1351 vdefs.resize(nexist + nmaps, map_def());
1352 for (int i = 0; i < nmaps; ++i)
1353 {
1354 map_def &vdef(vdefs[nexist + i]);
1355 vdef.read_index(inf);
1356 vdef.description = unmarshallString(inf);
1357 vdef.order = unmarshallInt(inf);
1358
1359 vdef.set_file(cache);
1360 lc_loaded_maps[vdef.name] = vdef.place_loaded_from;
1361 vdef.place_loaded_from.clear();
1362 }
1363 fclose(fp);
1364
1365 return true;
1366 }
1367
_load_map_cache(const string & filename,const string & cachename)1368 static bool _load_map_cache(const string &filename, const string &cachename)
1369 {
1370 _check_des_index_dir();
1371 const string descache_base = get_descache_path(cachename, "");
1372
1373 file_lock deslock(descache_base + ".lk", "rb", false);
1374
1375 time_t mtime = file_modtime(filename);
1376 string file_idx = descache_base + ".idx";
1377 string file_dsc = descache_base + ".dsc";
1378
1379 // What's the point in checking these twice (here and in load_ma_index)?
1380 if (!_verify_map_index(descache_base, mtime)
1381 || !_verify_map_full(descache_base, mtime))
1382 {
1383 return false;
1384 }
1385
1386 return _load_map_index(cachename, descache_base, mtime);
1387 }
1388
_write_map_prelude(const string & filebase,time_t mtime)1389 static void _write_map_prelude(const string &filebase, time_t mtime)
1390 {
1391 const string luafile = filebase + ".lux";
1392 if (lc_global_prelude.empty())
1393 {
1394 unlink_u(luafile.c_str());
1395 return;
1396 }
1397
1398 FILE *fp = fopen_u(luafile.c_str(), "wb");
1399 writer outf(luafile, fp);
1400 write_save_version(outf, save_version::current());
1401 marshallByte(outf, WORD_LEN);
1402 marshallSigned(outf, mtime);
1403 lc_global_prelude.write(outf);
1404 fclose(fp);
1405 }
1406
_write_map_full(const string & filebase,size_t vs,size_t ve,time_t mtime)1407 static void _write_map_full(const string &filebase, size_t vs, size_t ve,
1408 time_t mtime)
1409 {
1410 const string cfile = filebase + ".dsc";
1411 FILE *fp = fopen_u(cfile.c_str(), "wb");
1412 if (!fp)
1413 end(1, true, "Unable to open %s for writing", cfile.c_str());
1414
1415 writer outf(cfile, fp);
1416 write_save_version(outf, save_version::current());
1417 marshallByte(outf, WORD_LEN);
1418 marshallSigned(outf, mtime);
1419 for (size_t i = vs; i < ve; ++i)
1420 vdefs[i].write_full(outf);
1421 fclose(fp);
1422 }
1423
_write_map_index(const string & filebase,size_t vs,size_t ve,time_t mtime)1424 static void _write_map_index(const string &filebase, size_t vs, size_t ve,
1425 time_t mtime)
1426 {
1427 const string cfile = filebase + ".idx";
1428 FILE *fp = fopen_u(cfile.c_str(), "wb");
1429 if (!fp)
1430 end(1, true, "Unable to open %s for writing", cfile.c_str());
1431
1432 writer outf(cfile, fp);
1433 write_save_version(outf, save_version::current());
1434 marshallByte(outf, WORD_LEN);
1435 marshallSigned(outf, mtime);
1436 marshallShort(outf, ve > vs? ve - vs : 0);
1437 for (size_t i = vs; i < ve; ++i)
1438 {
1439 vdefs[i].write_index(outf);
1440 marshallString(outf, vdefs[i].description);
1441 marshallInt(outf, vdefs[i].order);
1442 vdefs[i].place_loaded_from.clear();
1443 vdefs[i].strip();
1444 }
1445 fclose(fp);
1446 }
1447
_write_map_cache(const string & filename,size_t vs,size_t ve,time_t mtime)1448 static void _write_map_cache(const string &filename, size_t vs, size_t ve,
1449 time_t mtime)
1450 {
1451 _check_des_index_dir();
1452
1453 const string descache_base = get_descache_path(filename, "");
1454
1455 file_lock deslock(descache_base + ".lk", "wb");
1456
1457 _write_map_prelude(descache_base, mtime);
1458 _write_map_full(descache_base, vs, ve, mtime);
1459 _write_map_index(descache_base, vs, ve, mtime);
1460 }
1461
_parse_maps(const string & s)1462 static void _parse_maps(const string &s)
1463 {
1464 string cache_name = get_cache_name(s);
1465 if (map_files_read.count(cache_name))
1466 return;
1467
1468 map_files_read.insert(cache_name);
1469
1470 if (_load_map_cache(s, cache_name))
1471 return;
1472
1473 FILE *dat = fopen_u(s.c_str(), "r");
1474 if (!dat)
1475 end(1, true, "Failed to open %s for reading", s.c_str());
1476
1477 #ifdef DEBUG_DIAGNOSTICS
1478 printf("Regenerating des: %s\n", s.c_str());
1479 #endif
1480 // won't be seen by the user unless they look for it
1481 mprf(MSGCH_PLAIN, "Regenerating des: %s", s.c_str());
1482
1483 time_t mtime = file_modtime(dat);
1484 _reset_map_parser();
1485
1486 extern int yyparse();
1487 extern FILE *yyin;
1488 yyin = dat;
1489
1490 const size_t file_start = vdefs.size();
1491 yyparse();
1492 fclose(dat);
1493
1494 global_preludes.push_back(lc_global_prelude);
1495
1496 _write_map_cache(cache_name, file_start, vdefs.size(), mtime);
1497 }
1498
read_map(const string & file)1499 void read_map(const string &file)
1500 {
1501 _parse_maps(lc_desfile = datafile_path(file));
1502 _dgn_flush_map_environments();
1503 // Force GC to prevent heap from swelling unnecessarily.
1504 dlua.gc();
1505 }
1506
read_maps()1507 void read_maps()
1508 {
1509 if (dlua.execfile("dlua/loadmaps.lua", true, true, true))
1510 end(1, false, "Lua error: %s", dlua.error.c_str());
1511
1512 lc_loaded_maps.clear();
1513
1514 {
1515 unwind_var<FixedVector<int, NUM_BRANCHES> > depths(brdepth);
1516 // let the sanity check place maps
1517 for (branch_iterator it; it; ++it)
1518 brdepth[it->id] = it->numlevels;
1519 dlua.execfile("dlua/sanity.lua", true, true);
1520 }
1521 }
1522
1523 // If a .dsc file has been changed under the running Crawl, discard
1524 // all map knowledge and reload maps. This will not affect maps that
1525 // have already been used, but it might trigger exciting happenings if
1526 // the new maps fail sanity checks or remove maps that the game
1527 // expects to be present.
reread_maps()1528 void reread_maps()
1529 {
1530 dprf("reread_maps:: discarding %u existing maps",
1531 (unsigned int)vdefs.size());
1532
1533 // BOOM!
1534 vdefs.clear();
1535 map_files_read.clear();
1536 read_maps();
1537 }
1538
dump_map(const map_def & map)1539 void dump_map(const map_def &map)
1540 {
1541 if (crawl_state.dump_maps)
1542 {
1543 fprintf(stderr, "\n----------------------------------------\n%s\n",
1544 map.describe().c_str());
1545 }
1546 }
1547
add_parsed_map(const map_def & md)1548 void add_parsed_map(const map_def &md)
1549 {
1550 map_def map = md;
1551
1552 map.fixup();
1553 vdefs.push_back(map);
1554 }
1555
run_map_global_preludes()1556 void run_map_global_preludes()
1557 {
1558 for (dlua_chunk &chunk : global_preludes)
1559 {
1560 if (!chunk.empty())
1561 {
1562 if (chunk.load_call(dlua, nullptr))
1563 mprf(MSGCH_ERROR, "Lua error: %s", chunk.orig_error().c_str());
1564 }
1565 }
1566 }
1567
run_map_local_preludes()1568 void run_map_local_preludes()
1569 {
1570 for (map_def &vdef : vdefs)
1571 {
1572 if (!vdef.prelude.empty())
1573 {
1574 string err = vdef.run_lua(true);
1575 if (!err.empty())
1576 {
1577 mprf(MSGCH_ERROR, "Lua error (map %s): %s",
1578 vdef.name.c_str(), err.c_str());
1579 }
1580 }
1581 }
1582 }
1583
map_by_index(int index)1584 const map_def *map_by_index(int index)
1585 {
1586 return &vdefs[index];
1587 }
1588
1589 // Supporting map code for mapstat
1590 #ifdef DEBUG_STATISTICS
1591
1592 typedef pair<string, int> weighted_map_name;
1593 typedef vector<weighted_map_name> weighted_map_names;
1594
_find_random_vaults(const level_id & place,bool wantmini)1595 static weighted_map_names _find_random_vaults(
1596 const level_id &place, bool wantmini)
1597 {
1598 weighted_map_names wms;
1599
1600 if (!place.is_valid())
1601 return wms;
1602
1603 typedef map<string, int> map_count_t;
1604
1605 map_count_t map_counts;
1606
1607 map_selector sel = map_selector::by_depth(place, wantmini, MB_MAYBE);
1608 sel.preserve_dummy = true;
1609
1610 msg::suppress mx;
1611 vault_indices filtered = _eligible_maps_for_selector(sel);
1612
1613 for (int i = 0; i < 10000; ++i)
1614 {
1615 const map_def *map(_random_map_in_list(sel, filtered));
1616 if (!map)
1617 map_counts["(none)"]++;
1618 else
1619 map_counts[map->name]++;
1620 }
1621
1622 wms.insert(wms.end(), map_counts.begin(), map_counts.end());
1623 return wms;
1624 }
1625
_weighted_map_more_likely(const weighted_map_name & a,const weighted_map_name & b)1626 static bool _weighted_map_more_likely(
1627 const weighted_map_name &a,
1628 const weighted_map_name &b)
1629 {
1630 return a.second > b.second;
1631 }
1632
_report_random_vaults(FILE * outf,const level_id & place,bool wantmini)1633 static void _report_random_vaults(
1634 FILE *outf, const level_id &place, bool wantmini)
1635 {
1636 weighted_map_names wms = _find_random_vaults(place, wantmini);
1637 sort(wms.begin(), wms.end(), _weighted_map_more_likely);
1638 int weightsum = 0;
1639 for (const auto& weighted_name : wms)
1640 weightsum += weighted_name.second;
1641
1642 string line;
1643 for (int i = 0, size = wms.size(); i < size; ++i)
1644 {
1645 string curr = make_stringf("%s (%.2f%%)", wms[i].first.c_str(),
1646 100.0 * wms[i].second / weightsum);
1647 if (i < size - 1)
1648 curr += ", ";
1649 if (line.length() + curr.length() > 80u)
1650 {
1651 fprintf(outf, "%s\n", line.c_str());
1652 line.clear();
1653 }
1654
1655 line += curr;
1656 }
1657 if (!line.empty())
1658 fprintf(outf, "%s\n", line.c_str());
1659 }
1660
mapstat_report_random_maps(FILE * outf,const level_id & place)1661 void mapstat_report_random_maps(FILE *outf, const level_id &place)
1662 {
1663 fprintf(outf, "---------------- Mini\n");
1664 _report_random_vaults(outf, place, true);
1665 fprintf(outf, "------------- Regular\n");
1666 _report_random_vaults(outf, place, false);
1667 }
1668
1669 #endif //DEBUG_STATISTICS
1670