1 /***
2  * @module dgn
3  */
4 #include "AppHdr.h"
5 
6 #include "l-libs.h"
7 
8 #include <cmath>
9 #include <vector>
10 
11 #include "cluautil.h"
12 #include "coordit.h"
13 #include "dgn-delve.h"
14 #include "dgn-irregular-box.h"
15 #include "dgn-layouts.h"
16 #include "dgn-shoals.h"
17 #include "dgn-swamp.h"
18 #include "dungeon.h"
19 
20 static const char *exit_glyphs = "{}()[]<>@";
21 
22 // Return the integer stored in the table (on the stack) with the key name.
23 // If the key doesn't exist or the value is the wrong type, return defval.
_table_int(lua_State * ls,int idx,const char * name,int defval)24 static int _table_int(lua_State *ls, int idx, const char *name, int defval)
25 {
26     if (!lua_istable(ls, idx))
27         return defval;
28     lua_pushstring(ls, name);
29     lua_gettable(ls, idx < 0 ? idx - 1 : idx);
30     bool nil = lua_isnil(ls, idx);
31     bool valid = lua_isnumber(ls, idx);
32     if (!nil && !valid)
33         luaL_error(ls, "'%s' in table, but not an int.", name);
34     int ret = (!nil && valid ? luaL_safe_checkint(ls, idx) : defval);
35     lua_pop(ls, 1);
36     return ret;
37 }
38 
39 // Return the character stored in the table (on the stack) with the key name.
40 // If the key doesn't exist or the value is the wrong type, return defval.
_table_char(lua_State * ls,int idx,const char * name,char defval)41 static char _table_char(lua_State *ls, int idx, const char *name, char defval)
42 {
43     if (!lua_istable(ls, idx))
44         return defval;
45     lua_pushstring(ls, name);
46     lua_gettable(ls, idx < 0 ? idx - 1 : idx);
47     bool nil = lua_isnil(ls, idx);
48     bool valid = lua_isstring(ls, idx);
49     if (!nil && !valid)
50         luaL_error(ls, "'%s' in table, but not a string.", name);
51 
52     char ret = defval;
53     if (!nil && valid)
54     {
55         const char *str = lua_tostring(ls, idx);
56         if (str[0] && !str[1])
57             ret = str[0];
58         else
59             luaL_error(ls, "'%s' has more than one character.", name);
60     }
61     lua_pop(ls, 1);
62     return ret;
63 }
64 
65 // Return the string stored in the table (on the stack) with the key name.
66 // If the key doesn't exist or the value is the wrong type, return defval.
_table_str(lua_State * ls,int idx,const char * name,const char * defval)67 static const char* _table_str(lua_State *ls, int idx, const char *name, const char *defval)
68 {
69     if (!lua_istable(ls, idx))
70         return defval;
71     lua_pushstring(ls, name);
72     lua_gettable(ls, idx < 0 ? idx - 1 : idx);
73     bool nil = lua_isnil(ls, idx);
74     bool valid = lua_isstring(ls, idx);
75     if (!nil && !valid)
76         luaL_error(ls, "'%s' in table, but not a string.", name);
77     const char *ret = (!nil && valid ? lua_tostring(ls, idx) : defval);
78     lua_pop(ls, 1);
79     return ret;
80 }
81 
82 // Return the boolean stored in the table (on the stack) with the key name.
83 // If the key doesn't exist or the value is the wrong type, return defval.
_table_bool(lua_State * ls,int idx,const char * name,bool defval)84 static bool _table_bool(lua_State *ls, int idx, const char *name, bool defval)
85 {
86     if (!lua_istable(ls, idx))
87         return defval;
88     lua_pushstring(ls, name);
89     lua_gettable(ls, idx < 0 ? idx - 1 : idx);
90     bool nil = lua_isnil(ls, idx);
91     bool valid = lua_isboolean(ls, idx);
92     if (!nil && !valid)
93         luaL_error(ls, "'%s' in table, but not a bool.", name);
94     bool ret = (!nil && valid ? lua_toboolean(ls, idx) : defval);
95     lua_pop(ls, 1);
96     return ret;
97 }
98 
99 // These macros all assume the table is on the top of the lua stack.
100 #define TABLE_INT(ls, val, def) int val = _table_int(ls, -1, #val, def);
101 #define TABLE_CHAR(ls, val, def) char val = _table_char(ls, -1, #val, def);
102 #define TABLE_STR(ls, val, def) const char *val = _table_str(ls, -1, #val, def);
103 #define TABLE_BOOL(ls, val, def) bool val = _table_bool(ls, -1, #val, def);
104 
105 #define ARG_INT(ls, num, val, def) int val = lua_isnone(ls, num) ? \
106                                              def : luaL_safe_tointeger(ls, num)
107 
108 // Read a set of box coords (x1, y1, x2, y2) from the table.
109 // Return true if coords are valid.
_coords(lua_State * ls,map_lines & lines,int & x1,int & y1,int & x2,int & y2,int border=0)110 static bool _coords(lua_State *ls, map_lines &lines,
111                     int &x1, int &y1, int &x2, int &y2, int border = 0)
112 {
113     const int idx = -1;
114     x1 = _table_int(ls, idx, "x1", 0);
115     y1 = _table_int(ls, idx, "y1", 0);
116     x2 = _table_int(ls, idx, "x2", lines.width() - 1);
117     y2 = _table_int(ls, idx, "y2", lines.height() - 1);
118 
119     if (x2 < x1)
120         swap(x1, x2);
121     if (y2 < y1)
122         swap(y1, y2);
123 
124     return x1 + border <= x2 - border && y1 + border <= y2 - border;
125 }
126 
127 // Check if a given coordiante is valid for lines.
_valid_coord(lua_State * ls,map_lines & lines,int x,int y,bool error=true)128 static bool _valid_coord(lua_State *ls, map_lines &lines, int x, int y, bool error = true)
129 {
130     if (x < 0 || x >= lines.width())
131     {
132         if (error)
133             luaL_error(ls, "Invalid x coordinate: %d", x);
134         return false;
135     }
136 
137     if (y < 0 || y >= lines.height())
138     {
139         if (error)
140             luaL_error(ls, "Invalid y coordinate: %d", y);
141         return false;
142     }
143 
144     return true;
145 }
146 
147 // Does what fill_area did, but here, so that it can be used through
148 // multiple functions (including make_box).
_fill_area(lua_State *,map_lines & lines,int x1,int y1,int x2,int y2,char fill)149 static int _fill_area(lua_State */*ls*/, map_lines &lines, int x1, int y1, int x2, int y2, char fill)
150 {
151     for (int y = y1; y <= y2; ++y)
152         for (int x = x1; x <= x2; ++x)
153             lines(x, y) = fill;
154 
155     return 0;
156 }
157 
_border_area(map_lines & lines,int x1,int y1,int x2,int y2,char border)158 static void _border_area(map_lines &lines, int x1, int y1, int x2, int y2, char border)
159 {
160     for (int x = x1 + 1; x < x2; ++x)
161         lines(x, y1) = border, lines(x, y2) = border;
162     for (int y = y1; y <= y2; ++y)
163         lines(x1, y) = border, lines(x2, y) = border;
164 }
165 
166 // Does what count_passable_neighbors does, but in C++ form.
_count_passable_neighbors(lua_State * ls,map_lines & lines,int x,int y,const char * passable=traversable_glyphs)167 static int _count_passable_neighbors(lua_State *ls, map_lines &lines, int x,
168                                      int y, const char *passable = traversable_glyphs)
169 {
170     coord_def tl(x, y);
171     int count = 0;
172 
173     for (adjacent_iterator ai(tl); ai; ++ai)
174     {
175         if (_valid_coord(ls, lines, ai->x, ai->y, false))
176             if (strchr(passable, lines(*ai)))
177                 count++;
178     }
179 
180     return count;
181 }
182 
_get_pool_seed_positions(vector<vector<int>> pool_index,int pool_size,int min_separation)183 static vector<coord_def> _get_pool_seed_positions(
184                                                 vector<vector<int> > pool_index,
185                                                 int pool_size,
186                                                 int min_separation)
187 {
188     const int NO_POOL   = 999997; // must match dgn_add_pools
189 
190     if (pool_size < 1)
191         pool_size = 1;
192 
193     // 1. Find all floor positions
194 
195     vector<coord_def> floor_positions;
196 
197     for (unsigned int x = 0; x < pool_index.size(); x++)
198         for (unsigned int y = 0; y < pool_index[x].size(); y++)
199         {
200             if (pool_index[x][y] == NO_POOL)
201                 floor_positions.emplace_back(x, y);
202         }
203 
204     // 2. Choose the pool seed positions
205 
206     int min_separation_squared = min_separation * min_separation;
207     int pool_count_goal = (floor_positions.size() + random2(pool_size))
208                           / pool_size;
209 
210     vector<coord_def> seeds;
211 
212     for (int i = 0; i < pool_count_goal; i++)
213     {
214         if (floor_positions.empty())
215         {
216             // give up if no more positions
217             break;
218         }
219 
220         // choose a random position
221         int chosen_index = random2(floor_positions.size());
222         coord_def chosen_coord = floor_positions[chosen_index];
223         floor_positions[chosen_index] = floor_positions.back();
224         floor_positions.pop_back();
225 
226         // check if it is too close to another seed
227         bool too_close = false;
228         for (coord_def seed : seeds)
229         {
230             int diff_x = chosen_coord.x - seed.x;
231             int diff_y = chosen_coord.y - seed.y;
232             int distance_squared = diff_x * diff_x + diff_y * diff_y;
233 
234             if (distance_squared < min_separation_squared)
235             {
236                 too_close = true;
237                 break;
238             }
239         }
240 
241         // if not too close, add it to the list
242         if (!too_close)
243             seeds.push_back(chosen_coord);
244     }
245 
246     // 3. Return the pool seeds
247 
248     return seeds;
249 }
250 
251 // This function assumes the table is on the top of the lua stack.
_pool_fill_glyphs_from_table(lua_State * ls,const char * name)252 static vector<char> _pool_fill_glyphs_from_table(lua_State *ls,
253                                                  const char *name)
254 {
255     // We will go through the table and put each possible pool
256     //  fill glyph in a vector once for each weight. This will
257     //  make it easy to select random ones with the correct
258     //  distribution when we need to.
259     vector<char> fill_glyphs;
260 
261     lua_pushstring(ls, name);
262     lua_gettable(ls, -2);
263     if (!lua_isnil(ls, -1) && lua_istable(ls, -1))
264     {
265         // For some reason, LUA requires us to have a dummy
266         //  value to remove from the stack whenever we do a
267         //  table lookup. Here is the first one
268         lua_pushnil(ls);
269 
270         // table is now at -2
271         while (lua_next(ls, -2) != 0)
272         {
273             // uses 'key' (at index -2) and 'value' (at index -1)
274             if (lua_type(ls, -2) == LUA_TSTRING
275                 && lua_type(ls, -1) == LUA_TNUMBER)
276             {
277                 // we use first character of string as glyph
278                 char glyph = (lua_tostring(ls, -2))[0];
279 
280                 int count = luaL_safe_tointeger(ls, -1);
281                 // sanity-check
282                 if (count > 10000)
283                     count = 10000;
284 
285                 if (glyph != '\0')
286                 {
287                     for (int i = 0; i < count; i++)
288                         fill_glyphs.push_back(glyph);
289                 }
290             }
291 
292             // removes 'value'; keeps 'key' for next iteration
293             lua_pop(ls, 1);
294         }
295     }
296     lua_pop(ls, 1);
297 
298     // We might have not got anything, if so, use floor
299     if (fill_glyphs.size() == 0)
300         fill_glyphs.push_back('.');
301     sort(fill_glyphs.begin(), fill_glyphs.end());
302 
303     return fill_glyphs;
304 }
305 
306 // These functions check for irregularities before the first
307 //  corner along a wall in the indicated direction.
_wall_is_empty(map_lines & lines,int x,int y,const char * wall,const char * floor,bool horiz=false,int max_check=9999)308 static bool _wall_is_empty(map_lines &lines,
309                            int x, int y,
310                            const char* wall, const char* floor,
311                            bool horiz = false,
312                            int max_check = 9999)
313 {
314     coord_def normal(horiz ? 0 : 1, horiz ? 1 : 0);
315     for (int d = 1; d >= -1; d-=2)
316     {
317         coord_def length(horiz ? d : 0, horiz ? 0 : d);
318         int n = 1;
319 
320         while (n <= max_check)
321         {
322             coord_def pos(x + length.x*n,y + length.y*n);
323             if (!lines.in_bounds(coord_def(pos.x + normal.x, pos.y + normal.y))
324                 || !strchr(floor, lines(pos.x + normal.x, pos.y + normal.y)))
325             {
326                 break;
327             }
328             if (!lines.in_bounds(coord_def(pos.x - normal.x, pos.y - normal.y))
329                 || !strchr(floor, lines(pos.x - normal.x, pos.y - normal.y)))
330             {
331                 break;
332             }
333 
334             if (!strchr(wall, lines(pos.x, pos.y)))
335                 return false;
336 
337             n++;
338         }
339     }
340 
341     // hit the end of the wall, so this is good
342     return true;
343 }
344 
345 // Only used for the join_the_dots command.
346 struct join_the_dots_path
347 {
348     vector<coord_def> cells;
349     int hit_vault_count;
350     int avoid_vault_count;
351 };
352 
353 /*
354  * Calculates a possible path joining the provided coordinates.
355  *
356  * @param from              The start of the path to be calculated.
357  * @param to                The end of the path to be calculated.
358  * @param force_straight    Whether the path must be a straight line.
359  * @param allow_diagonals   Whether the path can travel diagonally.
360  * @return A data structure containing (1) the path, & (2) the number of times
361  * it hit or almost hit an existing vault.
362  */
_calculate_join_the_dots_path(const coord_def & from,const coord_def & to,bool force_straight,bool allow_diagonals)363 static join_the_dots_path _calculate_join_the_dots_path (const coord_def& from,
364                                                          const coord_def& to,
365                                                          bool force_straight,
366                                                          bool allow_diagonals)
367 {
368     join_the_dots_path path;
369     path.hit_vault_count = 0;
370     path.avoid_vault_count = 0;
371 
372     coord_def at = from;
373     while (true) // loop breaks below
374     {
375         // 1. Handle this position
376 
377         path.cells.push_back(at);
378         if (env.level_map_mask(at) & MMT_VAULT)
379             path.hit_vault_count++;
380 
381         // check done after recording position
382         if (at == to)
383             break;  // exit loop
384 
385         // 2. Identify good moves
386 
387         // possible next positions
388         int x_move = (at.x < to.x) ? 1 : ((at.x > to.x) ? -1 : 0);
389         int y_move = (at.y < to.y) ? 1 : ((at.y > to.y) ? -1 : 0);
390 
391         coord_def next_x  = coord_def(at.x + x_move, at.y);
392         coord_def next_y  = coord_def(at.x,          at.y + y_move);
393         coord_def next_xy = coord_def(at.x + x_move, at.y + y_move);
394 
395         // moves that get you closer
396         bool good_x  = (x_move != 0);
397         bool good_y  = (y_move != 0);
398         bool good_xy = (x_move != 0) && (y_move != 0) && allow_diagonals;
399 
400         // avoid vaults if possible
401         bool vault_x  = env.level_map_mask(next_x)  & MMT_VAULT;
402         bool vault_y  = env.level_map_mask(next_y)  & MMT_VAULT;
403         bool vault_xy = env.level_map_mask(next_xy) & MMT_VAULT;
404         if (   (!vault_x  && good_x)
405             || (!vault_y  && good_y)
406             || (!vault_xy && good_xy))
407         {
408             // if there is a good path that doesn't hit a vault,
409             //  disable the otherwise-good paths that do
410 
411             if (vault_x)  path.avoid_vault_count++;
412             if (vault_y)  path.avoid_vault_count++;
413             if (vault_xy) path.avoid_vault_count++;
414 
415             // There is no &&= operator because short-circuit
416             //  evaluation can do strange and terrible things
417             //  when combined with function calls.
418             good_x  &= !vault_x;
419             good_y  &= !vault_y;
420             good_xy &= !vault_xy;
421         }
422         else
423         {
424             // there is no way to avoid vaults, so hitting one is OK
425             path.avoid_vault_count += 3;
426         }
427 
428         // 3. Choose the next move
429         if (force_straight)
430         {
431             if (good_xy)
432                 at = next_xy;
433             else if (good_x)
434                 at = next_x;
435             else
436                 at = next_y;
437         }
438         else
439         {
440             // allow irregular paths
441 
442             // used for movement ratios; our goal is to make a
443             //  path approximately straight in any direction
444             int x_diff = abs(at.x - to.x);
445             int y_diff = abs(at.y - to.y);
446             int sum_diff = x_diff + y_diff;
447             int min_diff = (x_diff < y_diff) ? x_diff : y_diff;
448             int max_diff = sum_diff - min_diff;
449 
450             // halve chance because a diagonal is worth 2 other moves
451             if (good_xy && (x_chance_in_y(min_diff, max_diff * 2)
452                             || (!good_x && !good_y)))
453             {
454                 at = next_xy;
455             }
456             else if (good_x && (x_chance_in_y(x_diff, sum_diff) || !good_y))
457                 at = next_x;
458             else
459                 at = next_y;
460         }
461     }
462 
463     // path is finished
464     return path;
465 }
466 
467 
468 /*
469  * Calculates a possible path joining the provided coordinates.
470  *
471  * @param from              The start of the path to be calculated.
472  * @param to                The end of the path to be calculated.
473  * @param force_straight    Whether the path must be a straight line.
474  * @param allow_diagonals   Whether the path can travel diagonally.
475  * @return A data structure containing (1) the path, & (2) the number of times
476  * it hit or almost hit an existing vault.
477  */
_draw_join_the_dots_path(map_lines & lines,const join_the_dots_path & path,const char * passable,int thickness,char fill)478 static void _draw_join_the_dots_path (map_lines &lines,
479                                       const join_the_dots_path& path,
480                                       const char* passable,
481                                       int thickness, char fill)
482 {
483     int delta_min = -thickness / 2;
484     int delta_max = delta_min + thickness;
485     for (coord_def center : path.cells)
486     {
487         for (int dx = delta_min; dx < delta_max; dx++)
488             for (int dy = delta_min; dy < delta_max; dy++)
489             {
490                 int x = center.x + dx;
491                 int y = center.y + dy;
492 
493                 // we never change the border
494                 if (x >= 1 && x < lines.width()  - 1 &&
495                     y >= 1 && y < lines.height() - 1 &&
496                     !strchr(passable, lines(x, y)))
497                 {
498                     lines(x, y) = fill;
499                 }
500             }
501     }
502 }
503 
504 
LUAFN(dgn_count_feature_in_box)505 LUAFN(dgn_count_feature_in_box)
506 {
507     LINES(ls, 1, map, lines);
508 
509     int x1, y1, x2, y2;
510     if (!_coords(ls, lines, x1, y1, x2, y2))
511         return 0;
512 
513     TABLE_STR(ls, feat, "");
514 
515     coord_def tl(x1, y1);
516     coord_def br(x2, y2);
517 
518     PLUARET(number, lines.count_feature_in_box(tl, br, feat));
519 }
520 
LUAFN(dgn_count_antifeature_in_box)521 LUAFN(dgn_count_antifeature_in_box)
522 {
523     LINES(ls, 1, map, lines);
524 
525     int x1, y1, x2, y2;
526     if (!_coords(ls, lines, x1, y1, x2, y2))
527         return 0;
528 
529     TABLE_STR(ls, feat, "");
530 
531     coord_def tl(x1, y1);
532     coord_def br(x2, y2);
533 
534     int sum = (br.x - tl.x + 1) * (br.y - tl.y + 1);
535     PLUARET(number, sum - lines.count_feature_in_box(tl, br, feat));
536 }
537 
LUAFN(dgn_count_neighbors)538 LUAFN(dgn_count_neighbors)
539 {
540     LINES(ls, 1, map, lines);
541 
542     TABLE_STR(ls, feat, "");
543     TABLE_INT(ls, x, -1);
544     TABLE_INT(ls, y, -1);
545 
546     if (!_valid_coord(ls, lines, x, y))
547         return 0;
548 
549     coord_def tl(x-1, y-1);
550     coord_def br(x+1, y+1);
551 
552     PLUARET(number, lines.count_feature_in_box(tl, br, feat));
553 }
554 
LUAFN(dgn_count_passable_neighbors)555 LUAFN(dgn_count_passable_neighbors)
556 {
557     LINES(ls, 1, map, lines);
558 
559     TABLE_STR(ls, passable, traversable_glyphs);
560     TABLE_INT(ls, x, -1);
561     TABLE_INT(ls, y, -1);
562 
563     if (!_valid_coord(ls, lines, x, y))
564     {
565         lua_pushnumber(ls, 0);
566         return 1;
567     }
568 
569     lua_pushnumber(ls, _count_passable_neighbors(ls, lines, x, y, passable));
570     return 1;
571 }
572 
LUAFN(dgn_is_valid_coord)573 LUAFN(dgn_is_valid_coord)
574 {
575     LINES(ls, 1, map, lines);
576 
577     TABLE_INT(ls, x, -1);
578     TABLE_INT(ls, y, -1);
579 
580     if (x < 0 || x >= lines.width())
581     {
582         lua_pushboolean(ls, false);
583         return 1;
584     }
585 
586     if (y < 0 || y >= lines.height())
587     {
588         lua_pushboolean(ls, false);
589         return 1;
590     }
591 
592     lua_pushboolean(ls, true);
593     return 1;
594 }
595 
LUAFN(dgn_extend_map)596 LUAFN(dgn_extend_map)
597 {
598     LINES(ls, 1, map, lines);
599 
600     TABLE_INT(ls, height, 1);
601     TABLE_INT(ls, width, 1);
602     TABLE_CHAR(ls, fill, 'x');
603 
604     lines.extend(width, height, fill);
605 
606     return 0;
607 }
608 
LUAFN(dgn_fill_area)609 LUAFN(dgn_fill_area)
610 {
611     LINES(ls, 1, map, lines);
612 
613     int x1, y1, x2, y2;
614     if (!_coords(ls, lines, x1, y1, x2, y2))
615         return 0;
616 
617     TABLE_CHAR(ls, fill, 'x');
618     TABLE_CHAR(ls, border, fill);
619 
620     _fill_area(ls, lines, x1, y1, x2, y2, fill);
621     if (border != fill)
622         _border_area(lines, x1, y1, x2, y2, border);
623 
624     return 0;
625 }
626 
LUAFN(dgn_fill_disconnected)627 LUAFN(dgn_fill_disconnected)
628 {
629     LINES(ls, 1, map, lines);
630 
631     int x1, y1, x2, y2;
632     if (!_coords(ls, lines, x1, y1, x2, y2))
633         return 0;
634 
635     TABLE_CHAR(ls, fill, 'x');
636     TABLE_STR(ls, passable, traversable_glyphs);
637     TABLE_STR(ls, wanted, exit_glyphs);
638 
639     coord_def tl(x1, y1);
640     coord_def br(x2, y2);
641 
642     travel_distance_grid_t tpd;
643     memset(tpd, 0, sizeof(tpd));
644 
645     int nzones = 0;
646     for (rectangle_iterator ri(tl, br); ri; ++ri)
647     {
648         const coord_def c = *ri;
649         if (tpd[c.x][c.y] || passable && !strchr(passable, lines(c)))
650             continue;
651 
652         if (lines.fill_zone(tpd, c, tl, br, ++nzones, wanted, passable))
653             continue;
654 
655         // If wanted wasn't found, fill every passable square that
656         // we just found with the 'fill' glyph.
657         for (rectangle_iterator f(tl, br); f; ++f)
658         {
659             const coord_def fc = *f;
660             if (tpd[fc.x][fc.y] == nzones)
661                 lines(fc) = fill;
662         }
663     }
664 
665     return 0;
666 }
667 
LUAFN(dgn_is_passable_coord)668 LUAFN(dgn_is_passable_coord)
669 {
670     LINES(ls, 1, map, lines);
671 
672     TABLE_INT(ls, x, -1);
673     TABLE_INT(ls, y, -1);
674     TABLE_STR(ls, passable, traversable_glyphs);
675 
676     if (!_valid_coord(ls, lines, x, y))
677         return 0;
678 
679     if (strchr(passable, lines(x, y)))
680         lua_pushboolean(ls, true);
681     else
682         lua_pushboolean(ls, false);
683 
684     return 1;
685 }
686 
LUAFN(dgn_find_in_area)687 LUAFN(dgn_find_in_area)
688 {
689     LINES(ls, 1, map, lines);
690 
691     TABLE_INT(ls, x1, -1);
692     TABLE_INT(ls, y1, -1);
693     TABLE_INT(ls, x2, -1);
694     TABLE_INT(ls, y2, -1);
695     TABLE_BOOL(ls, find_vault, false);
696 
697     if (!_coords(ls, lines, x1, y1, x2, y2))
698         return 0;
699 
700     TABLE_STR(ls, find, "x");
701 
702     int x, y;
703 
704     for (x = x1; x <= x2; x++)
705         for (y = y1; y <= y2; y++)
706             if (strchr(find, lines(x, y))
707                 || (find_vault && (env.level_map_mask(coord_def(x,y))
708                                    & MMT_VAULT)))
709             {
710                 lua_pushboolean(ls, true);
711                 return 1;
712             }
713 
714     lua_pushboolean(ls, false);
715     return 1;
716 }
717 
LUAFN(dgn_height)718 LUAFN(dgn_height)
719 {
720     LINES(ls, 1, map, lines);
721     PLUARET(number, lines.height());
722 }
723 
LUAFN(dgn_primary_vault_dimensions)724 LUAFN(dgn_primary_vault_dimensions)
725 {
726     // we don't need this because this function doesn't use the
727     //  current map
728     // LINES(ls, 1, lines);
729 
730     static const int NO_PRIMARY_VAULT = 99999;
731 
732     int x_min =  NO_PRIMARY_VAULT;
733     int x_max = -NO_PRIMARY_VAULT;
734     int y_min =  NO_PRIMARY_VAULT;
735     int y_max = -NO_PRIMARY_VAULT;
736 
737     for (int y = 0; y < GYM; y++)
738         for (int x = 0; x < GXM; x++)
739         {
740             if (env.level_map_mask(coord_def(x,y)) & MMT_VAULT)
741             {
742                 if (x < x_min)
743                     x_min = x;
744                 if (x > x_max)
745                     x_max = x;
746                 if (y < y_min)
747                     y_min = y;
748                 if (y > y_max)
749                     y_max = y;
750             }
751         }
752 
753     if (x_min != NO_PRIMARY_VAULT)
754     {
755         if (x_max == -NO_PRIMARY_VAULT)
756             return luaL_error(ls, "Primary vault has x_min %d but no x_max", x_min);
757         if (y_min ==  NO_PRIMARY_VAULT)
758             return luaL_error(ls, "Primary vault has x_min %d but no y_min", x_min);
759         if (y_max == -NO_PRIMARY_VAULT)
760             return luaL_error(ls, "Primary vault has x_min %d but no y_max", x_min);
761 
762         lua_pushnumber(ls, x_min);
763         lua_pushnumber(ls, x_max);
764         lua_pushnumber(ls, y_min);
765         lua_pushnumber(ls, y_max);
766     }
767     else  // no primary vault found
768     {
769         if (x_max != -NO_PRIMARY_VAULT)
770             return luaL_error(ls, "Primary vault has x_max %d but no x_min", x_max);
771         if (y_min !=  NO_PRIMARY_VAULT)
772             return luaL_error(ls, "Primary vault has y_min %d but no x_min", y_min);
773         if (y_max != -NO_PRIMARY_VAULT)
774             return luaL_error(ls, "Primary vault has y_max %d but no x_min", y_max);
775 
776         lua_pushnil(ls);
777         lua_pushnil(ls);
778         lua_pushnil(ls);
779         lua_pushnil(ls);
780     }
781 
782     return 4;
783 }
784 
LUAFN(dgn_join_the_dots)785 LUAFN(dgn_join_the_dots)
786 {
787     LINES(ls, 1, map, lines);
788 
789     TABLE_INT(ls, x1, -1);
790     TABLE_INT(ls, y1, -1);
791     TABLE_INT(ls, x2, -1);
792     TABLE_INT(ls, y2, -1);
793     TABLE_STR(ls, passable, traversable_glyphs);
794     TABLE_CHAR(ls, fill, '.');
795     TABLE_BOOL(ls, force_straight, false);
796     TABLE_BOOL(ls, allow_diagonals, false);
797     TABLE_INT(ls, thickness, 1);
798 
799     if (!_valid_coord(ls, lines, x1, y1))
800         return 0;
801     if (!_valid_coord(ls, lines, x2, y2))
802         return 0;
803 
804     coord_def from(x1, y1);
805     coord_def to(x2, y2);
806 
807     if (from == to)
808         return 0;
809 
810     // calculate possible paths
811     join_the_dots_path path1 =
812         _calculate_join_the_dots_path(from, to,
813                                       force_straight, allow_diagonals);
814     join_the_dots_path path2 =
815         _calculate_join_the_dots_path(to, from,
816                                       force_straight, allow_diagonals);
817 
818     // add better path
819     // prefer fewer vaults hit, then fewer vaults avoided, then toss a coin
820     const bool first_path_better =
821         path1.hit_vault_count < path2.hit_vault_count
822         || (path1.hit_vault_count == path2.hit_vault_count
823             && (path1.avoid_vault_count < path2.avoid_vault_count
824                 || path1.avoid_vault_count == path2.avoid_vault_count
825                    && coinflip()
826                 )
827             );
828     _draw_join_the_dots_path(lines, first_path_better ? path1 : path2,
829                              passable, thickness, fill);
830 
831     return 0;
832 }
833 
LUAFN(dgn_make_circle)834 LUAFN(dgn_make_circle)
835 {
836     LINES(ls, 1, map, lines);
837 
838     TABLE_INT(ls, x, -1);
839     TABLE_INT(ls, y, -1);
840     TABLE_INT(ls, radius, 1);
841     TABLE_CHAR(ls, fill, 'x');
842 
843     if (!_valid_coord(ls, lines, x, y))
844         return 0;
845 
846     float radius_squared_max = (radius + 0.5f) * (radius + 0.5f);
847     for (int ry = -radius; ry <= radius; ++ry)
848         for (int rx = -radius; rx <= radius; ++rx)
849             if (rx * rx + ry * ry < radius_squared_max)
850                 lines(x + rx, y + ry) = fill;
851 
852     return 0;
853 }
854 
LUAFN(dgn_make_diamond)855 LUAFN(dgn_make_diamond)
856 {
857     LINES(ls, 1, map, lines);
858 
859     TABLE_INT(ls, x, -1);
860     TABLE_INT(ls, y, -1);
861     TABLE_INT(ls, radius, 1);
862     TABLE_CHAR(ls, fill, 'x');
863 
864     if (!_valid_coord(ls, lines, x, y))
865         return 0;
866 
867     for (int ry = -radius; ry <= radius; ++ry)
868         for (int rx = -radius; rx <= radius; ++rx)
869             if (abs(rx) + abs(ry) <= radius)
870                 lines(x + rx, y + ry) = fill;
871 
872     return 0;
873 }
874 
LUAFN(dgn_make_rounded_square)875 LUAFN(dgn_make_rounded_square)
876 {
877     LINES(ls, 1, map, lines);
878 
879     TABLE_INT(ls, x, -1);
880     TABLE_INT(ls, y, -1);
881     TABLE_INT(ls, radius, 1);
882     TABLE_CHAR(ls, fill, 'x');
883 
884     if (!_valid_coord(ls, lines, x, y))
885         return 0;
886 
887     for (int ry = -radius; ry <= radius; ++ry)
888         for (int rx = -radius; rx <= radius; ++rx)
889             if (abs(rx) != radius || abs(ry) != radius)
890                 lines(x + rx, y + ry) = fill;
891 
892     return 0;
893 }
894 
LUAFN(dgn_make_square)895 LUAFN(dgn_make_square)
896 {
897     LINES(ls, 1, map, lines);
898 
899     TABLE_INT(ls, x, -1);
900     TABLE_INT(ls, y, -1);
901     TABLE_INT(ls, radius, 1);
902     TABLE_CHAR(ls, fill, 'x');
903 
904     if (!_valid_coord(ls, lines, x, y))
905         return 0;
906 
907     for (int ry = -radius; ry <= radius; ++ry)
908         for (int rx = -radius; rx <= radius; ++rx)
909             lines(x + rx, y + ry) = fill;
910 
911     return 0;
912 }
913 
LUAFN(dgn_make_box)914 LUAFN(dgn_make_box)
915 {
916     LINES(ls, 1, map, lines);
917 
918     int x1, y1, x2, y2;
919     if (!_coords(ls, lines, x1, y1, x2, y2))
920         return 0;
921 
922     TABLE_CHAR(ls, floor, '.');
923     TABLE_CHAR(ls, wall, 'x');
924     TABLE_INT(ls, thickness, 1);
925 
926     _fill_area(ls, lines, x1, y1, x2, y2, wall);
927     _fill_area(ls, lines, x1+thickness, y1+thickness,
928                           x2-thickness, y2-thickness, floor);
929 
930     return 0;
931 }
932 
LUAFN(dgn_make_box_doors)933 LUAFN(dgn_make_box_doors)
934 {
935     LINES(ls, 1, map, lines);
936 
937     int x1, y1, x2, y2;
938     if (!_coords(ls, lines, x1, y1, x2, y2))
939         return 0;
940 
941     TABLE_INT(ls, number, 1);
942     TABLE_INT(ls, thickness, 1);
943     TABLE_CHAR(ls, door, '+');
944     TABLE_CHAR(ls, inner_door, '.');
945     TABLE_CHAR(ls, between_doors, '.');
946     TABLE_BOOL(ls, veto_gates, false);
947     TABLE_STR(ls, passable, traversable_glyphs);
948 
949     // size doesn't include corners
950     int size_x = x2 - x1 + 1 - thickness * 2;
951     int size_y = y2 - y1 + 1 - thickness * 2;
952     int position_count = size_x * 2 + size_y * 2;
953 
954     int max_sanity = number * 100;
955     int sanity = 0;
956 
957     int door_count = 0;
958     while (door_count < number)
959     {
960         int position = random2(position_count);
961         int side;
962         int x, y;
963         if (position < size_x)
964         {
965             side = 0;
966             x = x1 + thickness + position;
967             y = y1;
968         }
969         else if (position < size_x * 2)
970         {
971             side = 1;
972             x = x1 + thickness + (position - size_x);
973             y = y2;
974         }
975         else if (position < size_x * 2 + size_y)
976         {
977             side = 2;
978             x = x1;
979             y = y1 + thickness + (position - size_x * 2);
980         }
981         else
982         {
983             side = 3;
984             x = x2;
985             y = y1 + thickness + (position - size_x * 2 - size_y);
986         }
987 
988         // We veto a position if:
989         //  -> The cell outside the box is not passible
990         //  -> The cell (however far) inside the box is not passible
991         //  -> There is a door to the left or right and we are vetoing gates
992         bool good = true;
993         if (side < 2)
994         {
995             if (!strchr(passable, lines(x, y - (side == 0 ? 1 : thickness))))
996                 good = false;
997             if (!strchr(passable, lines(x, y + (side == 1 ? 1 : thickness))))
998                 good = false;
999             if (veto_gates && (lines(x-1, y) == door || lines(x+1, y) == door))
1000                 good = false;
1001         }
1002         else
1003         {
1004             if (!strchr(passable, lines(x - (side == 2 ? 1 : thickness), y)))
1005                 good = false;
1006             if (!strchr(passable, lines(x + (side == 3 ? 1 : thickness), y)))
1007                 good = false;
1008             if (veto_gates && (lines(x, y-1) == door || lines(x, y+1) == door))
1009                 good = false;
1010         }
1011 
1012         if (good)
1013         {
1014             door_count++;
1015             lines(x, y) = door;
1016             for (int i = 1; i < thickness; i++)
1017             {
1018                 switch (side)
1019                 {
1020                 case 0: y++;  break;
1021                 case 1: y--;  break;
1022                 case 2: x++;  break;
1023                 case 3: x--;  break;
1024                 }
1025                 lines(x, y) = between_doors;
1026             }
1027             if (thickness > 1)
1028                 lines(x, y) = inner_door;
1029         }
1030         else
1031         {
1032             sanity++;
1033             if (sanity >= max_sanity)
1034                 break;
1035         }
1036     }
1037 
1038     lua_pushnumber(ls, door_count);
1039     return 1;
1040 }
1041 
LUAFN(dgn_make_irregular_box)1042 LUAFN(dgn_make_irregular_box)
1043 {
1044     LINES(ls, 1, map, lines);
1045 
1046     int x1, y1, x2, y2;
1047     if (!_coords(ls, lines, x1, y1, x2, y2))
1048         return 0;
1049 
1050     TABLE_CHAR(ls, floor, '.');
1051     TABLE_CHAR(ls, wall, 'x');
1052     TABLE_CHAR(ls, door, '+');
1053     TABLE_INT(ls, door_count, 1);
1054     TABLE_INT(ls, div_x, 1);
1055     TABLE_INT(ls, div_y, 1);
1056     TABLE_INT(ls, in_x, 10000);
1057     TABLE_INT(ls, in_y, 10000);
1058 
1059     make_irregular_box(lines, x1, y1, x2, y2,
1060                        div_x, div_y, in_x, in_y,
1061                        floor, wall, door, door_count);
1062     return 0;
1063 }
1064 
LUAFN(dgn_make_round_box)1065 LUAFN(dgn_make_round_box)
1066 {
1067     LINES(ls, 1, map, lines);
1068 
1069     int x1, y1, x2, y2;
1070     if (!_coords(ls, lines, x1, y1, x2, y2))
1071         return 0;
1072 
1073     TABLE_CHAR(ls, floor, '.');
1074     TABLE_CHAR(ls, wall, 'x');
1075     TABLE_CHAR(ls, door, '+');
1076     TABLE_INT(ls, door_count, 1);
1077     TABLE_STR(ls, passable, traversable_glyphs);
1078     TABLE_BOOL(ls, veto_gates, false);
1079     TABLE_BOOL(ls, veto_if_no_doors, false);
1080 
1081     const int OUTSIDE = 0;
1082     const int WALL    = 1;
1083     const int FLOOR   = 2;
1084     const int DOOR    = 3;
1085 
1086     int size_x = x2 - x1 + 1;
1087     int size_y = y2 - y1 + 1;
1088 
1089     //
1090     //  The basic idea here is we draw a filled circle, hollow
1091     //    out the middle, and then place doors on straight walls
1092     //    until we have enough.
1093     //
1094     //  We do not know for sure whether we want to actually draw
1095     //    anything until the end, so we will draw out tower onto
1096     //    our own separate array (actually a vector of vectors
1097     //    so we can set the size at runtime). Then, if
1098     //    everything goes well, we will copy it to the world with
1099     //    the appropriate glyphs.
1100     //
1101     //  Note that each of these steps has to be completed before
1102     //    we can do the next one, so all the loops over the same
1103     //    area are required.
1104     //
1105 
1106     //  1. Fill with OUTSIDE glyphs.
1107     vector<vector<int> > new_glyphs(size_x, vector<int>(size_y, OUTSIDE));
1108 
1109     //  2. Draw wall glyphs for filled circle.
1110     //    -> This is a formula for an ellipse in case we get a
1111     //       non-circular room to make
1112     //    -> we add an extra 0.5 to the radius so that we don't
1113     //       get wall isolated cells on the outside
1114     float radius_x = (size_x - 1.0f) * 0.5f;
1115     float radius_y = (size_y - 1.0f) * 0.5f;
1116     for (int x = 0; x < size_x; x++)
1117         for (int y = 0; y < size_y; y++)
1118         {
1119             float fraction_x = (x - radius_x) / (radius_x + 0.5f);
1120             float fraction_y = (y - radius_y) / (radius_y + 0.5f);
1121             if (fraction_x * fraction_x + fraction_y * fraction_y <= 1.0f)
1122                 new_glyphs[x][y] = WALL;
1123         }
1124 
1125     //  3. Replace all wall glypyhs that don't touch outside the
1126     //     circle with floor glyphs.
1127     for (int x = 0; x < size_x; x++)
1128         for (int y = 0; y < size_y; y++)
1129         {
1130             // we can't use adjacent_iterator it doesn't
1131             //  report neighbours with negative coordinates
1132             if (new_glyphs[x][y] == WALL
1133                 && x > 0 && x < size_x - 1
1134                 && y > 0 && y < size_y - 1
1135                 && new_glyphs[x - 1][y - 1] != OUTSIDE
1136                 && new_glyphs[x    ][y - 1] != OUTSIDE
1137                 && new_glyphs[x + 1][y - 1] != OUTSIDE
1138                 && new_glyphs[x - 1][y    ] != OUTSIDE
1139                 && new_glyphs[x + 1][y    ] != OUTSIDE
1140                 && new_glyphs[x - 1][y + 1] != OUTSIDE
1141                 && new_glyphs[x    ][y + 1] != OUTSIDE
1142                 && new_glyphs[x + 1][y + 1] != OUTSIDE)
1143             {
1144                 new_glyphs[x][y] = FLOOR;
1145             }
1146         }
1147 
1148     //  4. Find all potential door positions.
1149     vector<coord_def> door_positions;
1150     for (int x = 0; x < size_x; x++)
1151         for (int y = 0; y < size_y; y++)
1152             if (new_glyphs[x][y] == WALL)
1153             {
1154                 // check for wall in each direction
1155                 bool xm = (x - 1 >= 0      && new_glyphs[x - 1][y] == WALL);
1156                 bool xp = (x + 1 <  size_x && new_glyphs[x + 1][y] == WALL);
1157                 bool ym = (y - 1 >= 0      && new_glyphs[x][y - 1] == WALL);
1158                 bool yp = (y + 1 <  size_y && new_glyphs[x][y + 1] == WALL);
1159 
1160                 int real_x = x1 + x;
1161                 int real_y = y1 + y;
1162 
1163                 // We are on an X-aligned wall
1164                 if (xm && xp && !ym && !yp)
1165                 {
1166                     //
1167                     //  Check for passable glyphs in real map
1168                     //    and outside the tower. The check
1169                     //    order is:
1170                     //    -> in real map
1171                     //    -> passable
1172                     //    -> outside temporary array
1173                     //       or array has OUTSIDE glyph
1174                     //
1175                     //  If we can find one on at least one side,
1176                     //    we can put a door here.
1177                     //    -> we will only get two on rooms only
1178                     //       1 cell wide including walls
1179                     //
1180 
1181                     if (real_y - 1 >= 0
1182                         && strchr(passable, lines(real_x, real_y - 1))
1183                         && (y - 1 < 0
1184                             || new_glyphs[x][y - 1] == OUTSIDE))
1185                     {
1186                         door_positions.emplace_back(x, y);
1187                     }
1188                     else if (real_y + 1 < lines.height()
1189                              && strchr(passable, lines(real_x, real_y + 1))
1190                              && (y + 1 >= size_y
1191                                  || new_glyphs[x][y + 1] == OUTSIDE))
1192                     {
1193                         door_positions.emplace_back(x, y);
1194                     }
1195                 }
1196 
1197                 // We are on an Y-aligned wall
1198                 if (!xm && !xp && ym && yp)
1199                 {
1200                     // Same checks as above, but the other axis
1201                     if (real_x - 1 >= 0
1202                         && strchr(passable, lines(real_x - 1, real_y))
1203                         && (x - 1 < 0
1204                             || new_glyphs[x - 1][y] == OUTSIDE))
1205                     {
1206                         door_positions.emplace_back(x, y);
1207                     }
1208                     else if (real_x + 1 < lines.width()
1209                              && strchr(passable, lines(real_x + 1, real_y))
1210                              && (x + 1 >= size_x
1211                                  || new_glyphs[x + 1][y] == OUTSIDE))
1212                     {
1213                         door_positions.emplace_back(x, y);
1214                     }
1215                 }
1216             }
1217 
1218     //  5. Add doors
1219     int doors_placed = 0;
1220     while (doors_placed < door_count && !door_positions.empty())
1221     {
1222         int index = random2(door_positions.size());
1223         coord_def pos = door_positions[index];
1224         door_positions[index] = door_positions[door_positions.size() - 1];
1225         door_positions.pop_back();
1226 
1227         bool good_spot = true;
1228         if (veto_gates)
1229         {
1230             if (pos.x - 1 >= 0     && new_glyphs[pos.x - 1][pos.y] == DOOR)
1231                 good_spot = false;
1232             if (pos.x + 1 < size_x && new_glyphs[pos.x + 1][pos.y] == DOOR)
1233                 good_spot = false;
1234             if (pos.y - 1 >= 0     && new_glyphs[pos.x][pos.y - 1] == DOOR)
1235                 good_spot = false;
1236             if (pos.y + 1 < size_y && new_glyphs[pos.x][pos.y + 1] == DOOR)
1237                 good_spot = false;
1238         }
1239 
1240         if (good_spot)
1241         {
1242             new_glyphs[pos.x][pos.y] = DOOR;
1243             doors_placed++;
1244         }
1245     }
1246 
1247     //  6. Add tower to map (if not vetoed)
1248     if (doors_placed > 0 || !veto_if_no_doors)
1249     {
1250         for (int x = 0; x < size_x; x++)
1251             for (int y = 0; y < size_y; y++)
1252             {
1253                 switch (new_glyphs[x][y])
1254                 {
1255                 // leave existing glyphs on OUTSIDE
1256                 case WALL:  lines(x1 + x, y1 + y) = wall;  break;
1257                 case FLOOR: lines(x1 + x, y1 + y) = floor; break;
1258                 case DOOR:  lines(x1 + x, y1 + y) = door; break;
1259                 }
1260             }
1261 
1262         lua_pushboolean(ls, true);
1263     }
1264     else
1265         lua_pushboolean(ls, false);
1266 
1267     return 1;
1268 }
1269 
1270 // Return a metatable for a point on the map_lines grid.
LUAFN(dgn_mapgrd_table)1271 LUAFN(dgn_mapgrd_table)
1272 {
1273     MAP(ls, 1, map);
1274 
1275     map_def **mapref = clua_new_userdata<map_def *>(ls, MAPGRD_METATABLE);
1276     *mapref = map;
1277 
1278     return 1;
1279 }
1280 
LUAFN(dgn_octa_room)1281 LUAFN(dgn_octa_room)
1282 {
1283     LINES(ls, 1, map, lines);
1284 
1285     int default_oblique = min(lines.width(), lines.height()) / 2 - 1;
1286     TABLE_INT(ls, oblique, default_oblique);
1287     TABLE_CHAR(ls, outside, 'x');
1288     TABLE_CHAR(ls, inside, '.');
1289     TABLE_STR(ls, replace, "");
1290 
1291     int x1, y1, x2, y2;
1292     if (!_coords(ls, lines, x1, y1, x2, y2))
1293         return 0;
1294 
1295     coord_def tl(x1, y1);
1296     coord_def br(x2, y2);
1297 
1298     for (rectangle_iterator ri(tl, br); ri; ++ri)
1299     {
1300         const coord_def mc = *ri;
1301         char glyph = lines(mc);
1302         if (replace[0] && !strchr(replace, glyph))
1303             continue;
1304 
1305         int ob = 0;
1306         ob += max(oblique + tl.x - mc.x, 0);
1307         ob += max(oblique + mc.x - br.x, 0);
1308 
1309         bool is_inside = (mc.y >= tl.y + ob && mc.y <= br.y - ob);
1310         lines(mc) = is_inside ? inside : outside;
1311     }
1312 
1313     return 0;
1314 }
1315 
LUAFN(dgn_remove_isolated_glyphs)1316 LUAFN(dgn_remove_isolated_glyphs)
1317 {
1318     LINES(ls, 1, map, lines);
1319 
1320     TABLE_STR(ls, find, "");
1321     TABLE_CHAR(ls, replace, '.');
1322     TABLE_INT(ls, percent, 100);
1323     TABLE_BOOL(ls, boxy, false);
1324 
1325     int x1, y1, x2, y2;
1326     if (!_coords(ls, lines, x1, y1, x2, y2))
1327         return 0;
1328 
1329     // we never change the border
1330     if (x1 < 1)
1331         x1 = 1;
1332     if (x2 >= lines.width() - 1)
1333         x2 = lines.width() - 2;
1334     if (y1 < 1)
1335         y1 = 1;
1336     if (y2 >= lines.height() - 1)
1337         y2 = lines.height() - 2;
1338 
1339     for (int y = y1; y <= y2; ++y)
1340         for (int x = x1; x <= x2; ++x)
1341             if (strchr(find, lines(x, y)) && x_chance_in_y(percent, 100))
1342             {
1343                 bool do_replace = true;
1344                 for (radius_iterator ri(coord_def(x, y), 1,
1345                                         (boxy ? C_ROUND : C_POINTY),
1346                                         true); ri; ++ri)
1347                 {
1348                     if (_valid_coord(ls, lines, ri->x, ri->y, false))
1349                         if (strchr(find, lines(*ri)))
1350                         {
1351                             do_replace = false;
1352                             break;
1353                         }
1354                 }
1355                 if (do_replace)
1356                     lines(x, y) = replace;
1357             }
1358 
1359     return 0;
1360 }
1361 
LUAFN(dgn_widen_paths)1362 LUAFN(dgn_widen_paths)
1363 {
1364     LINES(ls, 1, map, lines);
1365 
1366     TABLE_STR(ls, find, "");
1367     TABLE_CHAR(ls, replace, '.');
1368     TABLE_STR(ls, passable, traversable_glyphs);
1369     TABLE_INT(ls, percent, 100);
1370     TABLE_BOOL(ls, boxy, false);
1371 
1372     int x1, y1, x2, y2;
1373     if (!_coords(ls, lines, x1, y1, x2, y2))
1374         return 0;
1375 
1376     // we never change the border
1377     if (x1 < 1)
1378         x1 = 1;
1379     if (x2 >= lines.width() - 1)
1380         x2 = lines.width() - 2;
1381     if (y1 < 1)
1382         y1 = 1;
1383     if (y2 >= lines.height() - 1)
1384         y2 = lines.height() - 2;
1385 
1386     vector<int> percent_for_neighbours;
1387     // these cases are temporary, to keep a particular seed stable for a bit...
1388     // They mimic some floating point quirks of the previous version.
1389     // there's one more case in gehenna that I didn't bother with.
1390     if (percent == 30)
1391         percent_for_neighbours = {0, 30, 52, 66, 76, 84, 89, 92, 95};
1392     else if (percent == 50)
1393         percent_for_neighbours = {0, 50, 75, 88, 94, 97, 99, 100, 100};
1394     else
1395     {
1396         const long antifraction_each = 10 - percent / 10; // truncates...
1397         long antifraction_current = 10 * antifraction_each;
1398         long divisor = 1;
1399         percent_for_neighbours.push_back(0);
1400         for (int i = 1; i < 9; i++)
1401         {
1402             percent_for_neighbours.push_back(
1403                 100 - antifraction_current / divisor);
1404             antifraction_current *= antifraction_each;
1405             divisor *= 10;
1406         }
1407     }
1408 
1409     // We do not replace this as we go to avoid favouring some directions.
1410     vector<coord_def> coord_to_replace;
1411 
1412     for (int y = y1; y <= y2; ++y)
1413         for (int x = x1; x <= x2; ++x)
1414             if (strchr(find, lines(x, y)))
1415             {
1416                 int neighbour_count = 0;
1417                 for (radius_iterator ri(coord_def(x, y), 1,
1418                                         (boxy ? C_ROUND : C_POINTY),
1419                                         true); ri; ++ri)
1420                 {
1421                     if (_valid_coord(ls, lines, ri->x, ri->y, false))
1422                         if (strchr(passable, lines(*ri)))
1423                             neighbour_count++;
1424                 }
1425 
1426                 // store this coordinate if needed
1427                 if (x_chance_in_y(percent_for_neighbours[neighbour_count], 100))
1428                     coord_to_replace.emplace_back(x, y);
1429             }
1430 
1431     // now go through and actually replace the positions
1432     for (coord_def c : coord_to_replace)
1433         lines(c) = replace;
1434 
1435     return 0;
1436 }
1437 
LUAFN(dgn_connect_adjacent_rooms)1438 LUAFN(dgn_connect_adjacent_rooms)
1439 {
1440     LINES(ls, 1, map, lines);
1441 
1442     TABLE_STR(ls, wall, "xcvbmn");
1443     TABLE_STR(ls, floor, ".");
1444     TABLE_CHAR(ls, replace, '.');
1445     TABLE_INT(ls, max, 1);
1446     TABLE_INT(ls, min, max);
1447     TABLE_INT(ls, check_distance, 9999);
1448 
1449     int x1, y1, x2, y2;
1450     if (!_coords(ls, lines, x1, y1, x2, y2))
1451         return 0;
1452 
1453     // we never go right up to the border to avoid looking off the map edge
1454     if (x1 < 1)
1455         x1 = 1;
1456     if (x2 >= lines.width() - 1)
1457         x2 = lines.width() - 2;
1458     if (y1 < 1)
1459         y1 = 1;
1460     if (y2 >= lines.height() - 1)
1461         y2 = lines.height() - 2;
1462 
1463     if (min < 0)
1464         return luaL_error(ls, "Invalid min connections: %d", min);
1465     if (max < min)
1466     {
1467         return luaL_error(ls, "Invalid max connections: %d (min is %d)",
1468                           max, min);
1469     }
1470 
1471     int count = random_range(min, max);
1472     for (random_rectangle_iterator ri(coord_def(x1, y1),
1473                                       coord_def(x2, y2)); ri; ++ri)
1474     {
1475         if (count <= 0)
1476         {
1477             // stop when have checked enough spots
1478             return 0;
1479         }
1480 
1481         int x = ri->x;
1482         int y = ri->y;
1483 
1484         if (strchr(wall, lines(*ri)))
1485         {
1486             if (strchr(floor, lines(x, y - 1))
1487                 && strchr(floor, lines(x, y + 1))
1488                 && (_wall_is_empty(lines, x, y, wall, floor,
1489                                    true, check_distance)))
1490             {
1491                 lines(*ri) = replace;
1492             }
1493             else if (strchr(floor, lines(x - 1, y))
1494                      && strchr(floor, lines(x + 1, y))
1495                      && (_wall_is_empty(lines, x, y, wall, floor,
1496                                         false, check_distance)))
1497             {
1498                 lines(*ri) = replace;
1499             }
1500         }
1501         count--;
1502     }
1503 
1504     return 0;
1505 }
1506 
LUAFN(dgn_remove_disconnected_doors)1507 LUAFN(dgn_remove_disconnected_doors)
1508 {
1509     LINES(ls, 1, map, lines);
1510 
1511     TABLE_STR(ls, door, "+");
1512     TABLE_STR(ls, open, traversable_glyphs);
1513     TABLE_CHAR(ls, replace, '.');
1514 
1515     int x1, y1, x2, y2;
1516     if (!_coords(ls, lines, x1, y1, x2, y2))
1517         return 0;
1518 
1519     // we never go right up to the border to avoid looking off the map edge
1520     if (x1 < 1)
1521         x1 = 1;
1522     if (x2 >= lines.width() - 1)
1523         x2 = lines.width() - 2;
1524     if (y1 < 1)
1525         y1 = 1;
1526     if (y2 >= lines.height() - 1)
1527         y2 = lines.height() - 2;
1528 
1529     // TODO: Improve this to handle gates correctly.
1530     //  -> Right now it just removes them.
1531     //  -> I (infiniplex) tried to find formulas for this and there were
1532     //     too many weird cases.
1533     //    -> 2-long hallway with doors at both end should not be a gate
1534     //    -> end(s) of gate surround by wall should become wall
1535     //    -> door glyphs in a shape other than a straight line
1536     //    -> walls of either side of the middle of a gate when the ends
1537     //       are open may connect otherwise-disconnected regions
1538     //    -> results must be direction-independent
1539 
1540     for (int y = y1; y <= y2; ++y)
1541         for (int x = x1; x <= x2; ++x)
1542             if (strchr(door, lines(x, y)))
1543             {
1544                 //
1545                 // This door is not part of a gate
1546                 //  -> There are a lot of cases here because doors
1547                 //     can be at corners
1548                 //  -> We will choose the doors we want to keep
1549                 //     and remove the rest of them
1550                 //
1551 
1552                 // which directions are open
1553                 bool south     = strchr(open, lines(x,     y + 1));
1554                 bool north     = strchr(open, lines(x,     y - 1));
1555                 bool east      = strchr(open, lines(x + 1, y));
1556                 bool west      = strchr(open, lines(x - 1, y));
1557                 bool southeast = strchr(open, lines(x + 1, y + 1));
1558                 bool northwest = strchr(open, lines(x - 1, y - 1));
1559                 bool southwest = strchr(open, lines(x - 1, y + 1));
1560                 bool northeast = strchr(open, lines(x + 1, y - 1));
1561 
1562 
1563                 //
1564                 // A door in an S-N straight wall
1565                 //
1566                 //     x      x     .x      x      x.
1567                 //    .+.     +.     +.    .+     .+
1568                 //     x     .x      x      x.     x
1569                 //
1570                 if (!south && !north
1571                     && (east || west)
1572                     && (east || southeast || northeast)
1573                     && (west || southwest || northwest))
1574                 {
1575                     continue;
1576                 }
1577 
1578                 //
1579                 // A door in an E-W straight wall
1580                 //
1581                 //     .     .        .     .      .
1582                 //    x+x    x+x    x+x    x+x    x+x
1583                 //     .      .      .     .        .
1584                 //
1585                 if (!east && !west
1586                     && (south || north)
1587                     && (south || northeast || northwest)
1588                     && (north || southeast || southwest))
1589                 {
1590                     continue;
1591                 }
1592 
1593                 //
1594                 // A door in a SE-NW diagonal
1595                 //
1596                 //    .x     ..     .xx
1597                 //    x+.    .+x    x+x
1598                 //     ..     x.    xx.
1599                 //
1600                 if (southeast && northwest && south == east && north == west)
1601                 {
1602                     if (south != west)
1603                         continue;
1604                     else if (!south && !west && !southwest && !northeast)
1605                         continue;
1606                 }
1607 
1608                 //
1609                 // A door in a SW-NE diagonal
1610                 //
1611                 //     ..     x.    xx.
1612                 //    x+.    .+x    x+x
1613                 //    .x     ..     .xx
1614                 //
1615                 if (southwest && northeast && south == west && north == east)
1616                 {
1617                     if (south != east)
1618                         continue;
1619                     else if (!south && !east && !southeast && !northwest)
1620                         continue;
1621                 }
1622 
1623                 // if we get her, the door is invalid
1624                 lines(x, y) = replace;
1625             }
1626 
1627     return 0;
1628 }
1629 
LUAFN(dgn_add_windows)1630 LUAFN(dgn_add_windows)
1631 {
1632     LINES(ls, 1, map, lines);
1633 
1634     TABLE_STR(ls, wall, "xcvbmn");
1635     TABLE_STR(ls, open, traversable_glyphs);
1636     TABLE_CHAR(ls, window, 'm');
1637 
1638     int x1, y1, x2, y2;
1639     if (!_coords(ls, lines, x1, y1, x2, y2))
1640         return 0;
1641 
1642     // we never go right up to the border to avoid looking off the map edge
1643     if (x1 < 1)
1644         x1 = 1;
1645     if (x2 >= lines.width() - 1)
1646         x2 = lines.width() - 2;
1647     if (y1 < 1)
1648         y1 = 1;
1649     if (y2 >= lines.height() - 1)
1650         y2 = lines.height() - 2;
1651 
1652     for (int y = y1; y <= y2; ++y)
1653         for (int x = x1; x <= x2; ++x)
1654             if (strchr(wall, lines(x, y)))
1655             {
1656                 // which directions are open
1657                 bool south_open     = strchr(open, lines(x,     y + 1));
1658                 bool north_open     = strchr(open, lines(x,     y - 1));
1659                 bool east_open      = strchr(open, lines(x + 1, y));
1660                 bool west_open      = strchr(open, lines(x - 1, y));
1661                 bool southeast_open = strchr(open, lines(x + 1, y + 1));
1662                 bool northwest_open = strchr(open, lines(x - 1, y - 1));
1663                 bool southwest_open = strchr(open, lines(x - 1, y + 1));
1664                 bool northeast_open = strchr(open, lines(x + 1, y - 1));
1665 
1666                 // which directions are blocked by walls
1667                 bool south_blocked     = strchr(wall, lines(x,     y + 1));
1668                 bool north_blocked     = strchr(wall, lines(x,     y - 1));
1669                 bool east_blocked      = strchr(wall, lines(x + 1, y));
1670                 bool west_blocked      = strchr(wall, lines(x - 1, y));
1671                 bool southeast_blocked = strchr(wall, lines(x + 1, y + 1));
1672                 bool northwest_blocked = strchr(wall, lines(x - 1, y - 1));
1673                 bool southwest_blocked = strchr(wall, lines(x - 1, y + 1));
1674                 bool northeast_blocked = strchr(wall, lines(x + 1, y - 1));
1675 
1676                 // a simple window in a straight wall
1677                 //
1678                 //   .    x
1679                 //  xmx  .m.
1680                 //   .    x
1681                 //
1682                 if (south_open && north_open && east_blocked && west_blocked)
1683                     lines(x, y) = window;
1684                 if (east_open && west_open && south_blocked && north_blocked)
1685                     lines(x, y) = window;
1686 
1687                 // a diagonal window in a straight segment of a bent wall
1688                 //
1689                 //  . x  x .   x    x
1690                 //  xmx  xmx  .m.  .m.
1691                 //  x .  . x   x    x
1692                 //
1693                 if (east_blocked && west_blocked)
1694                 {
1695                     if (southeast_open && northwest_open
1696                         && southwest_blocked && northeast_blocked)
1697                     {
1698                         lines(x, y) = window;
1699                     }
1700                     if (southwest_open && northeast_open
1701                         && southeast_blocked && northwest_blocked)
1702                     {
1703                         lines(x, y) = window;
1704                     }
1705                 }
1706 
1707                 // a window in a 3-wide diagonal wall
1708                 //
1709                 //  .x    x.
1710                 //  xmx  xmx
1711                 //   x.  .x
1712                 //
1713                 if (   south_blocked && north_blocked
1714                     && east_blocked  && west_blocked)
1715                 {
1716                     if (southeast_open && northwest_open)
1717                         lines(x, y) = window;
1718                     if (southwest_open && northeast_open)
1719                         lines(x, y) = window;
1720                 }
1721 
1722                 // a window in a 2-wide diagonal wall
1723                 //  -> this will change the whole wall
1724                 //
1725                 //   .    .   .x    x.
1726                 //  .mx  xm.  xm.  .mx
1727                 //   x.  .x    .    .
1728                 //
1729                 if (south_blocked && east_blocked
1730                     && north_open && west_open && southeast_open)
1731                 {
1732                     lines(x, y) = window;
1733                 }
1734                 if (south_blocked && west_blocked
1735                     && north_open && east_open && southwest_open)
1736                 {
1737                     lines(x, y) = window;
1738                 }
1739                 if (north_blocked && west_blocked
1740                     && south_open && east_open && northwest_open)
1741                 {
1742                     lines(x, y) = window;
1743                 }
1744                 if (north_blocked && east_blocked
1745                     && south_open && west_open && northeast_open)
1746                 {
1747                     lines(x, y) = window;
1748                 }
1749             }
1750 
1751     return 0;
1752 }
1753 
LUAFN(dgn_replace_area)1754 LUAFN(dgn_replace_area)
1755 {
1756     LINES(ls, 1, map, lines);
1757 
1758     TABLE_STR(ls, find, 0);
1759     TABLE_CHAR(ls, replace, '\0');
1760 
1761     int x1, y1, x2, y2;
1762     if (!_coords(ls, lines, x1, y1, x2, y2))
1763         return 0;
1764 
1765     for (int y = y1; y <= y2; ++y)
1766         for (int x = x1; x <= x2; ++x)
1767             if (strchr(find, lines(x, y)))
1768                 lines(x, y) = replace;
1769 
1770     return 0;
1771 }
1772 
LUAFN(dgn_replace_first)1773 LUAFN(dgn_replace_first)
1774 {
1775     LINES(ls, 1, map, lines);
1776 
1777     TABLE_INT(ls, x, 0);
1778     TABLE_INT(ls, y, 0);
1779     TABLE_INT(ls, xdir, 2);
1780     TABLE_INT(ls, ydir, 2);
1781     TABLE_CHAR(ls, find, '\0');
1782     TABLE_CHAR(ls, replace, '\0');
1783     TABLE_BOOL(ls, required, false);
1784 
1785     if (!_valid_coord(ls, lines, x, y))
1786         return 0;
1787 
1788     if (xdir < -1 || xdir > 1)
1789         return luaL_error(ls, "Invalid xdir: %d", xdir);
1790 
1791     if (ydir < -1 || ydir > 1)
1792         return luaL_error(ls, "Invalid ydir: %d", ydir);
1793 
1794     while (lines.in_bounds(coord_def(x, y)))
1795     {
1796         if (lines(x, y) == find)
1797         {
1798             lines(x, y) = replace;
1799             return 0;
1800         }
1801 
1802         x += xdir;
1803         y += ydir;
1804     }
1805 
1806     if (required)
1807         return luaL_error(ls, "Could not find feature '%c' to replace", find);
1808 
1809     return 0;
1810 }
1811 
LUAFN(dgn_replace_random)1812 LUAFN(dgn_replace_random)
1813 {
1814     LINES(ls, 1, map, lines);
1815 
1816     TABLE_CHAR(ls, find, '\0');
1817     TABLE_CHAR(ls, replace, '\0');
1818     TABLE_BOOL(ls, required, false);
1819 
1820     int x1, y1, x2, y2;
1821     if (!_coords(ls, lines, x1, y1, x2, y2))
1822         return 0;
1823 
1824     int count = (x2 - x1 + 1) * (y2 - y1 + 1);
1825     if (!count)
1826     {
1827         if (required)
1828             luaL_error(ls, "%s", "No elements to replace");
1829         return 0;
1830     }
1831 
1832     vector<coord_def> loc;
1833     loc.reserve(count);
1834 
1835     for (int y = y1; y <= y2; ++y)
1836         for (int x = x1; x <= x2; ++x)
1837             if (lines(x, y) == find)
1838                 loc.emplace_back(x, y);
1839 
1840     if (loc.empty())
1841     {
1842         if (required)
1843             luaL_error(ls, "Could not find '%c'", find);
1844         return 0;
1845     }
1846 
1847     int idx = random2(loc.size());
1848     lines(loc[idx]) = replace;
1849 
1850     return 0;
1851 }
1852 
LUAFN(dgn_replace_closest)1853 LUAFN(dgn_replace_closest)
1854 {
1855     LINES(ls, 1, map, lines);
1856 
1857     TABLE_INT(ls, x, 0);
1858     TABLE_INT(ls, y, 0);
1859     TABLE_CHAR(ls, find, '\0');
1860     TABLE_CHAR(ls, replace, '\0');
1861     TABLE_BOOL(ls, required, false);
1862 
1863     coord_def center(x, y);
1864 
1865     int x1, y1, x2, y2;
1866     if (!_coords(ls, lines, x1, y1, x2, y2))
1867         return 0;
1868 
1869     int count = (x2 - x1 + 1) * (y2 - y1 + 1);
1870     if (!count)
1871     {
1872         if (required)
1873             luaL_error(ls, "%s", "No elements to replace");
1874         return 0;
1875     }
1876 
1877     vector<coord_def> loc;
1878     loc.reserve(count);
1879 
1880     int best_distance = 10000;
1881     unsigned int best_count = 0;
1882     coord_def best_coord;
1883 
1884     for (int this_y = y1; this_y <= y2; ++this_y)
1885         for (int this_x = x1; this_x <= x2; ++this_x)
1886             if (lines(this_x, this_y) == find)
1887             {
1888                 coord_def here(this_x, this_y);
1889                 int distance = here.distance_from(center);
1890                 if (distance < best_distance)
1891                 {
1892                     best_distance = distance;
1893                     best_count = 1;
1894                     best_coord = here;
1895                 }
1896                 else if (distance == best_distance)
1897                 {
1898                     best_count++;
1899                     if (one_chance_in(best_count))
1900                         best_coord = here;
1901                 }
1902             }
1903 
1904     if (best_count == 0)
1905     {
1906         if (required)
1907             return luaL_error(ls, "Could not find '%c'", find);
1908         return 0;
1909     }
1910 
1911     lines(best_coord) = replace;
1912 
1913     return 0;
1914 }
1915 
LUAFN(dgn_smear_map)1916 LUAFN(dgn_smear_map)
1917 {
1918     LINES(ls, 1, map, lines);
1919 
1920     TABLE_INT(ls, iterations, 1);
1921     TABLE_CHAR(ls, smear, 'x');
1922     TABLE_STR(ls, onto, ".");
1923     TABLE_BOOL(ls, boxy, false);
1924 
1925     const int border = 1;
1926     int x1, y1, x2, y2;
1927     if (!_coords(ls, lines, x1, y1, x2, y2, border))
1928         return 0;
1929 
1930     const int max_test_per_iteration = 10;
1931     int sanity = 0;
1932     int max_sanity = iterations * max_test_per_iteration;
1933 
1934     for (int i = 0; i < iterations; i++)
1935     {
1936         bool diagonals, straights;
1937         coord_def mc;
1938 
1939         do
1940         {
1941             do
1942             {
1943                 if (sanity++ > max_sanity)
1944                     return 0;
1945 
1946                 mc.x = random_range(x1+border, y2-border);
1947                 mc.y = random_range(y1+border, y2-border);
1948             }
1949             while (onto[0] && !strchr(onto, lines(mc)));
1950 
1951             // Is there a "smear" feature along the diagonal from mc?
1952             diagonals = lines(mc.x + 1, mc.y + 1) == smear
1953                         || lines(mc.x - 1, mc.y + 1) == smear
1954                         || lines(mc.x - 1, mc.y - 1) == smear
1955                         || lines(mc.x + 1, mc.y - 1) == smear;
1956 
1957             // Is there a "smear" feature up, down, left, or right from mc?
1958             straights = lines(mc.x + 1, mc.y) == smear
1959                         || lines(mc.x - 1, mc.y) == smear
1960                         || lines(mc.x, mc.y + 1) == smear
1961                         || lines(mc.x, mc.y - 1) == smear;
1962         }
1963         while (!straights && (boxy || !diagonals));
1964 
1965         lines(mc) = smear;
1966     }
1967 
1968     return 0;
1969 }
1970 
LUAFN(dgn_spotty_map)1971 LUAFN(dgn_spotty_map)
1972 {
1973     LINES(ls, 1, map, lines);
1974 
1975     TABLE_STR(ls, replace, "x");
1976     TABLE_CHAR(ls, fill, '.');
1977     TABLE_BOOL(ls, boxy, true);
1978     TABLE_INT(ls, iterations, random2(boxy ? 750 : 1500));
1979 
1980     const int border = 4;
1981     int x1, y1, x2, y2;
1982     if (!_coords(ls, lines, x1, y1, x2, y2, border))
1983         return 0;
1984 
1985     const int max_test_per_iteration = 10;
1986     int sanity = 0;
1987     int max_sanity = iterations * max_test_per_iteration;
1988 
1989     for (int i = 0; i < iterations; i++)
1990     {
1991         int x, y;
1992         do
1993         {
1994             if (sanity++ > max_sanity)
1995                 return 0;
1996 
1997             x = random_range(x1 + border, x2 - border);
1998             y = random_range(y1 + border, y2 - border);
1999         }
2000         while (strchr(replace, lines(x, y))
2001                && strchr(replace, lines(x-1, y))
2002                && strchr(replace, lines(x+1, y))
2003                && strchr(replace, lines(x, y-1))
2004                && strchr(replace, lines(x, y+1))
2005                && strchr(replace, lines(x-2, y))
2006                && strchr(replace, lines(x+2, y))
2007                && strchr(replace, lines(x, y-2))
2008                && strchr(replace, lines(x, y+2)));
2009 
2010         for (radius_iterator ai(coord_def(x, y), boxy ? 2 : 1, C_CIRCLE,
2011                                 false); ai; ++ai)
2012         {
2013             if (strchr(replace, lines(*ai)))
2014                 lines(*ai) = fill;
2015         }
2016     }
2017 
2018     return 0;
2019 }
2020 
LUAFN(dgn_add_pools)2021 LUAFN(dgn_add_pools)
2022 {
2023     LINES(ls, 1, map, lines);
2024 
2025     TABLE_STR(ls, replace, ".");
2026     TABLE_CHAR(ls, border, '.');
2027     TABLE_INT(ls, pool_size, 100);
2028     TABLE_INT(ls, seed_separation, 2);
2029 
2030     vector<char> fill_glyphs = _pool_fill_glyphs_from_table(ls, "contents");
2031 
2032     int x1, y1, x2, y2;
2033     if (!_coords(ls, lines, x1, y1, x2, y2))
2034         return 0;
2035 
2036     int size_x = x2 - x1 + 1;
2037     int size_y = y2 - y1 + 1;
2038 
2039     //
2040     //  The basic ideas here is that we place a number of
2041     //    pool "seeds" on the map and spread them outward
2042     //    randomly until they run into each other. We never
2043     //    fill in the last cell, so they never actually
2044     //    touch and we get paths between them.
2045     //
2046     //  The algorithm we use to spread the pools is like a
2047     //    depth-/breadth-first search, except that:
2048     //      1. We choose a random element from the open list
2049     //      2. We have multiple starting locations, each
2050     //         with its own "closed" value
2051     //      3. We mark all cells bordered by 2 (or more)
2052     //         distinct non-BORDER closed values with special
2053     //         closed value BORDER
2054     //
2055     //  In the end, we used the "closed" values to determine
2056     //    which pool we are in. The BORDER value indicates
2057     //    a path between pools.
2058     //
2059 
2060     // NO_POOL
2061     //  -> must be lowest constant
2062     //  -> must match _get_pool_seed_positions
2063     const int NO_POOL   = 999997;
2064     const int IN_LIST   = 999998;
2065     const int BORDER    = 999999;
2066     const int FORBIDDEN = 1000000;
2067 
2068     // Step 1: Make a 2D array to store which pool each cell is part of
2069     //    -> We use nested vectors to store this. We cannot use
2070     //       a fixedarray because we don't know the size at
2071     //       compile time.
2072 
2073     vector<vector<int> > pool_index(size_x, vector<int>(size_y, FORBIDDEN));
2074     for (int x = 0; x < size_x; x++)
2075         for (int y = 0; y < size_y; y++)
2076         {
2077             if (strchr(replace, lines(x + x1, y + y1)))
2078                 pool_index[x][y] = NO_POOL;
2079         }
2080 
2081     // Step 2: Place the pool seeds and add their neighbours to the open list
2082 
2083     vector<coord_def> pool_seeds = _get_pool_seed_positions(pool_index,
2084                                                             pool_size,
2085                                                             seed_separation);
2086     vector<coord_def> open_list;
2087 
2088     for (unsigned int pool = 0; pool < pool_seeds.size(); pool++)
2089     {
2090         int x = pool_seeds[pool].x;
2091         int y = pool_seeds[pool].y;
2092 
2093         pool_index[x][y] = pool;
2094 
2095         // add neighbours to open list
2096         for (orth_adjacent_iterator ai(pool_seeds[pool]); ai; ++ai)
2097             if (_valid_coord(ls, lines, ai->x, ai->y, false))
2098             {
2099                 pool_index[ai->x][ai->y] = IN_LIST;
2100                 open_list.push_back(*ai);
2101             }
2102     }
2103 
2104     // Step 3: Spread out pools as far as possible
2105 
2106     while (!open_list.empty())
2107     {
2108         // remove a random position from the open list
2109         int index_chosen = random2(open_list.size());
2110         coord_def chosen_coord = open_list[index_chosen];
2111         open_list[index_chosen] = open_list.back();
2112         open_list.pop_back();
2113 
2114         // choose which neighbouring pool to join
2115         int chosen_pool = NO_POOL;
2116         for (adjacent_iterator ai(chosen_coord); ai; ++ai)
2117             if (_valid_coord(ls, lines, ai->x, ai->y, false))
2118             {
2119                 int neighbour_pool = pool_index[ai->x][ai->y];
2120                 if (neighbour_pool < NO_POOL)
2121                 {
2122                     // this is a valid pool, consider it
2123                     if (chosen_pool == NO_POOL)
2124                         chosen_pool = neighbour_pool;
2125                     else if (chosen_pool == neighbour_pool)
2126                         ; // already correct
2127                     else
2128                     {
2129                         // this is the path between pools
2130                         chosen_pool = BORDER;
2131                         break;
2132                     }
2133                 }
2134                 else if (neighbour_pool == FORBIDDEN)
2135                 {
2136                     // next to a wall
2137                     chosen_pool = BORDER;
2138                     break;
2139                 }
2140             }
2141 
2142         if (chosen_pool != NO_POOL)
2143         {
2144             // add this cell to the appropriate pool
2145             pool_index[chosen_coord.x][chosen_coord.y] = chosen_pool;
2146 
2147             // add neighbours to open list
2148             for (orth_adjacent_iterator ai(chosen_coord); ai; ++ai)
2149                 if (_valid_coord(ls, lines, ai->x, ai->y, false)
2150                     && pool_index[ai->x][ai->y] == NO_POOL)
2151                 {
2152                     pool_index[ai->x][ai->y] = IN_LIST;
2153                     open_list.push_back(*ai);
2154                 }
2155         }
2156         else
2157         {
2158             // a default, although I do not know why we ever get here
2159             pool_index[chosen_coord.x][chosen_coord.y] = NO_POOL;
2160         }
2161     }
2162 
2163     // Step 4: Add the pools to the map
2164 
2165     vector<char> pool_glyphs(pool_seeds.size(), '\0');
2166     for (char &gly : pool_glyphs)
2167         gly = fill_glyphs[random2(fill_glyphs.size())];
2168 
2169     for (int x = 0; x < size_x; x++)
2170         for (int y = 0; y < size_y; y++)
2171             {
2172             int index = pool_index[x][y];
2173             if (index < (int)(pool_glyphs.size()))
2174                 lines(x + x1, y + y1) = pool_glyphs[index];
2175             else if (index == NO_POOL || index == BORDER)
2176                 lines(x + x1, y + y1) = border;
2177             else if (index == FORBIDDEN)
2178                 ; // leave it alone
2179             else
2180             {
2181                 return luaL_error(ls, "Invalid pool index %i/%i at (%i, %i)",
2182                                   index, pool_glyphs.size(), x + x1, y + y1);
2183             }
2184         }
2185 
2186     return 0;
2187 }
2188 
dgn_width(lua_State * ls)2189 static int dgn_width(lua_State *ls)
2190 {
2191     LINES(ls, 1, map, lines);
2192     PLUARET(number, lines.width());
2193 }
2194 
LUAFN(dgn_delve)2195 LUAFN(dgn_delve)
2196 {
2197     LINES(ls, 1, map, lines);
2198 
2199     ARG_INT(ls, 2, ngb_min, 2);
2200     ARG_INT(ls, 3, ngb_max, 3);
2201     ARG_INT(ls, 4, connchance, 0);
2202     ARG_INT(ls, 5, cellnum, -1);
2203     ARG_INT(ls, 6, top, 125);
2204 
2205     delve(&lines, ngb_min, ngb_max, connchance, cellnum, top);
2206     return 0;
2207 }
2208 
LUAFN(dgn_farthest_from)2209 LUAFN(dgn_farthest_from)
2210 {
2211     LINES(ls, 1, map, lines);
2212     const char *beacons = luaL_checkstring(ls, 2);
2213 
2214     ASSERT(lines.width() <= GXM);
2215     ASSERT(lines.height() <= GYM);
2216     FixedArray<bool, GXM, GYM> visited;
2217     visited.init(false);
2218     vector<coord_def> queue;
2219     unsigned int dc_prev = 0, dc_next; // indices where dist changes to the next value
2220 
2221     for (int x = lines.width(); x >= 0; x--)
2222         for (int y = lines.height(); y >= 0; y--)
2223         {
2224             coord_def c(x, y);
2225             if (lines.in_map(c) && strchr(beacons, lines(c)))
2226             {
2227                 queue.push_back(c);
2228                 visited(c) = true;
2229             }
2230         }
2231 
2232     dc_next = queue.size();
2233     if (!dc_next)
2234     {
2235         // Not a single beacon, nowhere to go.
2236         lua_pushnil(ls);
2237         lua_pushnil(ls);
2238         return 2;
2239     }
2240 
2241     for (unsigned int dc = 0; dc < queue.size(); dc++)
2242     {
2243         if (dc >= dc_next)
2244         {
2245             dc_prev = dc_next;
2246             dc_next = dc;
2247         }
2248 
2249         coord_def c = queue[dc];
2250         for (adjacent_iterator ai(c); ai; ++ai)
2251             if (lines.in_map(*ai) && !visited(*ai)
2252                 && strchr(traversable_glyphs, lines(*ai)))
2253             {
2254                 queue.push_back(*ai);
2255                 visited(*ai) = true;
2256             }
2257     }
2258 
2259     ASSERT(dc_next > dc_prev);
2260     // There may be multiple farthest cells, pick one at random.
2261     coord_def loc = queue[random_range(dc_prev, dc_next - 1)];
2262     lua_pushnumber(ls, loc.x);
2263     lua_pushnumber(ls, loc.y);
2264     return 2;
2265 }
2266 
2267 /* Wrappers for C++ layouts, to facilitate choosing of layouts by weight and
2268  * depth */
2269 
LUAFN(dgn_layout_basic)2270 LUAFN(dgn_layout_basic)
2271 {
2272     UNUSED(ls);
2273     dgn_build_basic_level();
2274     return 0;
2275 }
2276 
LUAFN(dgn_layout_bigger_room)2277 LUAFN(dgn_layout_bigger_room)
2278 {
2279     UNUSED(ls);
2280     dgn_build_bigger_room_level();
2281     return 0;
2282 }
2283 
LUAFN(dgn_layout_chaotic_city)2284 LUAFN(dgn_layout_chaotic_city)
2285 {
2286     const dungeon_feature_type feature = check_lua_feature(ls, 2, true);
2287     dgn_build_chaotic_city_level(feature == DNGN_UNSEEN ? NUM_FEATURES : feature);
2288     return 0;
2289 }
2290 
LUAFN(dgn_layout_shoals)2291 LUAFN(dgn_layout_shoals)
2292 {
2293     UNUSED(ls);
2294     dgn_build_shoals_level();
2295     return 0;
2296 }
2297 
LUAFN(dgn_layout_swamp)2298 LUAFN(dgn_layout_swamp)
2299 {
2300     UNUSED(ls);
2301     dgn_build_swamp_level();
2302     return 0;
2303 }
2304 
2305 const struct luaL_reg dgn_build_dlib[] =
2306 {
2307     { "count_feature_in_box", &dgn_count_feature_in_box },
2308     { "count_antifeature_in_box", &dgn_count_antifeature_in_box },
2309     { "count_neighbors", &dgn_count_neighbors },
2310     { "count_passable_neighbors", &dgn_count_passable_neighbors },
2311     { "is_valid_coord", &dgn_is_valid_coord },
2312     { "is_passable_coord", &dgn_is_passable_coord },
2313     { "extend_map", &dgn_extend_map },
2314     { "fill_area", &dgn_fill_area },
2315     { "fill_disconnected", &dgn_fill_disconnected },
2316     { "find_in_area", &dgn_find_in_area },
2317     { "height", dgn_height },
2318     { "primary_vault_dimensions", &dgn_primary_vault_dimensions },
2319     { "join_the_dots", &dgn_join_the_dots },
2320     { "make_circle", &dgn_make_circle },
2321     { "make_diamond", &dgn_make_diamond },
2322     { "make_rounded_square", &dgn_make_rounded_square },
2323     { "make_square", &dgn_make_square },
2324     { "make_box", &dgn_make_box },
2325     { "make_box_doors", &dgn_make_box_doors },
2326     { "make_irregular_box", &dgn_make_irregular_box },
2327     { "make_round_box", &dgn_make_round_box },
2328     { "mapgrd_table", dgn_mapgrd_table },
2329     { "octa_room", &dgn_octa_room },
2330     { "remove_isolated_glyphs", &dgn_remove_isolated_glyphs },
2331     { "widen_paths", &dgn_widen_paths },
2332     { "connect_adjacent_rooms", &dgn_connect_adjacent_rooms },
2333     { "remove_disconnected_doors", &dgn_remove_disconnected_doors },
2334     { "add_windows", &dgn_add_windows },
2335     { "replace_area", &dgn_replace_area },
2336     { "replace_first", &dgn_replace_first },
2337     { "replace_random", &dgn_replace_random },
2338     { "replace_closest", &dgn_replace_closest },
2339     { "smear_map", &dgn_smear_map },
2340     { "spotty_map", &dgn_spotty_map },
2341     { "add_pools", &dgn_add_pools },
2342     { "delve", &dgn_delve },
2343     { "width", dgn_width },
2344     { "farthest_from", &dgn_farthest_from },
2345 
2346     { "layout_basic", &dgn_layout_basic },
2347     { "layout_bigger_room", &dgn_layout_bigger_room },
2348     { "layout_chaotic_city", &dgn_layout_chaotic_city },
2349     { "layout_shoals", &dgn_layout_shoals },
2350     { "layout_swamp", &dgn_layout_swamp },
2351 
2352     { nullptr, nullptr }
2353 };
2354