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