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 &parameters)
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