make rank() static again
[NetHack.git] / src / nhlsel.c
blobf700a9ea37f5ba5d3d627a85b7634bb8ff88215c
1 /* NetHack 3.7 nhlua.c $NHDT-Date: 1737545957 2025/01/22 03:39:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.64 $ */
2 /* Copyright (c) 2018 by Pasi Kallinen */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "sp_lev.h"
9 struct selectionvar *l_selection_check(lua_State *, int);
10 staticfn struct selectionvar *l_selection_push_new(lua_State *);
12 /* lua_CFunction prototypes */
13 staticfn int l_selection_new(lua_State *);
14 staticfn int l_selection_clone(lua_State *);
15 staticfn int l_selection_numpoints(lua_State *);
16 staticfn int l_selection_getpoint(lua_State *);
17 staticfn int l_selection_setpoint(lua_State *);
18 staticfn int l_selection_filter_percent(lua_State *);
19 staticfn int l_selection_rndcoord(lua_State *);
20 staticfn int l_selection_room(lua_State *);
21 staticfn int l_selection_getbounds(lua_State *);
22 staticfn boolean params_sel_2coords(lua_State *, struct selectionvar **,
23 coordxy *, coordxy *, coordxy *, coordxy *);
24 staticfn int l_selection_line(lua_State *);
25 staticfn int l_selection_randline(lua_State *);
26 staticfn int l_selection_rect(lua_State *);
27 staticfn int l_selection_fillrect(lua_State *);
28 staticfn int l_selection_grow(lua_State *);
29 staticfn int l_selection_filter_mapchar(lua_State *);
30 staticfn int l_selection_match(lua_State *);
31 staticfn int l_selection_flood(lua_State *);
32 staticfn int l_selection_circle(lua_State *);
33 staticfn int l_selection_ellipse(lua_State *);
34 staticfn int l_selection_gradient(lua_State *);
35 staticfn int l_selection_iterate(lua_State *);
36 staticfn int l_selection_gc(lua_State *);
37 staticfn int l_selection_not(lua_State *);
38 staticfn int l_selection_and(lua_State *);
39 staticfn int l_selection_or(lua_State *);
40 staticfn int l_selection_xor(lua_State *);
41 /* There doesn't seem to be a point in having a l_selection_add since it would
42 * do the same thing as l_selection_or. The addition operator is mapped to
43 * l_selection_or. */
44 staticfn int l_selection_sub(lua_State *);
45 #if 0
46 /* the following do not appear to currently be
47 used and because they are static, the OSX
48 compiler is complaining about them. I've
49 if ifdef'd out the prototype here and the
50 function body below.
52 staticfn int l_selection_ipairs(lua_State *);
53 staticfn struct selectionvar *l_selection_to(lua_State *, int);
54 #endif
56 struct selectionvar *
57 l_selection_check(lua_State *L, int index)
59 struct selectionvar *sel;
61 luaL_checktype(L, index, LUA_TUSERDATA);
62 sel = (struct selectionvar *) luaL_checkudata(L, index, "selection");
63 if (!sel)
64 nhl_error(L, "Selection error");
65 return sel;
68 staticfn int
69 l_selection_gc(lua_State *L)
71 struct selectionvar *sel = l_selection_check(L, 1);
73 if (sel)
74 selection_free(sel, FALSE);
75 return 0;
78 #if 0
79 staticfn struct selectionvar *
80 l_selection_to(lua_State *L, int index)
82 struct selectionvar *sel
83 = (struct selectionvar *) lua_touserdata(L, index);
85 if (!sel)
86 nhl_error(L, "Selection error");
87 return sel;
89 #endif
91 /* push a new selection into lua stack, return the selectionvar */
92 staticfn struct selectionvar *
93 l_selection_push_new(lua_State *L)
95 struct selectionvar *tmp = selection_new();
96 struct selectionvar *sel
97 = (struct selectionvar *) lua_newuserdata(L, sizeof (struct selectionvar));
99 luaL_getmetatable(L, "selection");
100 lua_setmetatable(L, -2);
102 *sel = *tmp;
103 sel->map = dupstr(tmp->map);
104 selection_free(tmp, TRUE);
106 return sel;
109 /* push a copy of selectionvar tmp to lua stack */
110 void
111 l_selection_push_copy(lua_State *L, struct selectionvar *tmp)
113 struct selectionvar *sel
114 = (struct selectionvar *) lua_newuserdata(L, sizeof (struct selectionvar));
116 luaL_getmetatable(L, "selection");
117 lua_setmetatable(L, -2);
119 *sel = *tmp;
120 sel->map = dupstr(tmp->map);
124 /* local sel = selection.new(); */
125 staticfn int
126 l_selection_new(lua_State *L)
128 (void) l_selection_push_new(L);
129 return 1;
132 /* Replace the topmost selection in the stack with a clone of it. */
133 /* local sel = selection.clone(sel); */
134 staticfn int
135 l_selection_clone(lua_State *L)
137 struct selectionvar *sel = l_selection_check(L, 1);
138 struct selectionvar *tmp;
140 (void) l_selection_new(L);
141 tmp = l_selection_check(L, 2);
142 if (tmp->map)
143 free(tmp->map);
144 *tmp = *sel;
145 tmp->map = dupstr(sel->map);
146 return 1;
149 DISABLE_WARNING_UNREACHABLE_CODE
151 /* selection.set(sel, x, y); */
152 /* selection.set(sel, x, y, value); */
153 /* local sel = selection.set(); */
154 /* local sel = sel:set(); */
155 /* local sel = selection.set(sel); */
156 /* TODO: allow setting multiple coords at once: set({x1,y1},{x2,y2},...); */
157 staticfn int
158 l_selection_setpoint(lua_State *L)
160 struct selectionvar *sel = (struct selectionvar *) 0;
161 coordxy x = -1, y = -1;
162 int val = 1;
163 int argc = lua_gettop(L);
164 long crd = 0L;
166 if (argc == 0) {
167 (void) l_selection_new(L);
168 } else if (argc == 1) {
169 sel = l_selection_check(L, 1);
170 } else if (argc == 2) {
171 x = (coordxy) luaL_checkinteger(L, 1);
172 y = (coordxy) luaL_checkinteger(L, 2);
173 lua_pop(L, 2);
174 (void) l_selection_new(L);
175 sel = l_selection_check(L, 1);
176 } else {
177 sel = l_selection_check(L, 1);
178 x = (coordxy) luaL_checkinteger(L, 2);
179 y = (coordxy) luaL_checkinteger(L, 3);
180 val = (int) luaL_optinteger(L, 4, 1);
183 if (!sel || !sel->map) {
184 nhl_error(L, "Selection setpoint error");
185 /*NOTREACHED*/
186 return 0;
189 if (x == -1 && y == -1)
190 crd = SP_COORD_PACK_RANDOM(0);
191 else
192 crd = SP_COORD_PACK(x,y);
193 get_location_coord(&x, &y, ANY_LOC,
194 gc.coder ? gc.coder->croom : NULL, crd);
195 selection_setpoint(x, y, sel, val);
196 lua_settop(L, 1);
197 return 1;
200 /* local numpoints = selection.numpoints(sel); */
201 staticfn int
202 l_selection_numpoints(lua_State *L)
204 struct selectionvar *sel = l_selection_check(L, 1);
205 coordxy x, y;
206 int ret = 0;
207 NhRect rect = cg.zeroNhRect;
209 selection_getbounds(sel, &rect);
211 for (x = rect.lx; x <= rect.hx; x++)
212 for (y = rect.ly; y <= rect.hy; y++)
213 if (selection_getpoint(x, y, sel))
214 ret++;
216 lua_settop(L, 0);
217 lua_pushinteger(L, ret);
218 return 1;
221 /* local value = selection.get(sel, x, y); */
222 staticfn int
223 l_selection_getpoint(lua_State *L)
225 struct selectionvar *sel = l_selection_check(L, 1);
226 coordxy x, y;
227 lua_Integer ix, iy;
228 int val;
229 long crd;
231 lua_remove(L, 1); /* sel */
232 if (!nhl_get_xy_params(L, &ix, &iy)) {
233 nhl_error(L, "l_selection_getpoint: Incorrect params");
234 /*NOTREACHED*/
235 return 0;
237 x = (coordxy) ix;
238 y = (coordxy) iy;
240 if (x == -1 && y == -1)
241 crd = SP_COORD_PACK_RANDOM(0);
242 else
243 crd = SP_COORD_PACK(x,y);
244 get_location_coord(&x, &y, ANY_LOC,
245 gc.coder ? gc.coder->croom : NULL, crd);
247 val = selection_getpoint(x, y, sel);
248 lua_settop(L, 0);
249 lua_pushnumber(L, val);
250 return 1;
253 RESTORE_WARNING_UNREACHABLE_CODE
255 /* local s = selection.negate(sel); */
256 /* local s = selection.negate(); */
257 /* local s = sel:negate(); */
258 staticfn int
259 l_selection_not(lua_State *L)
261 int argc = lua_gettop(L);
262 struct selectionvar *sel, *sel2;
264 if (argc == 0) {
265 (void) l_selection_new(L);
266 sel = l_selection_check(L, 1);
267 selection_clear(sel, 1);
268 } else {
269 (void) l_selection_check(L, 1);
270 (void) l_selection_clone(L);
271 sel2 = l_selection_check(L, 2);
272 selection_not(sel2);
273 lua_remove(L, 1);
275 return 1;
278 /* local sel = selection.area(4,5, 40,10) & selection.rect(7,8, 60,14); */
279 staticfn int
280 l_selection_and(lua_State *L)
282 int x, y;
283 struct selectionvar *sela = l_selection_check(L, 1);
284 struct selectionvar *selb = l_selection_check(L, 2);
285 struct selectionvar *selr = l_selection_push_new(L);
286 NhRect rect = cg.zeroNhRect;
288 rect_bounds(sela->bounds, selb->bounds, &rect);
290 for (x = rect.lx; x <= rect.hx; x++)
291 for (y = rect.ly; y <= rect.hy; y++) {
292 int val = (selection_getpoint(x, y, sela)
293 & selection_getpoint(x, y, selb));
295 selection_setpoint(x, y, selr, val);
298 lua_remove(L, 1);
299 lua_remove(L, 1);
300 return 1;
303 /* local sel = selection.area(4,5, 40,10) | selection.rect(7,8, 60,14); */
304 staticfn int
305 l_selection_or(lua_State *L)
307 int x,y;
308 struct selectionvar *sela = l_selection_check(L, 1);
309 struct selectionvar *selb = l_selection_check(L, 2);
310 struct selectionvar *selr = l_selection_push_new(L);
311 NhRect rect = cg.zeroNhRect;
313 rect_bounds(sela->bounds, selb->bounds, &rect);
315 for (x = rect.lx; x <= rect.hx; x++)
316 for (y = rect.ly; y <= rect.hy; y++) {
317 int val = (selection_getpoint(x, y, sela)
318 | selection_getpoint(x, y, selb));
320 selection_setpoint(x, y, selr, val);
322 selr->bounds = rect;
324 lua_remove(L, 1);
325 lua_remove(L, 1);
326 return 1;
329 /* local sel = selection.area(4,5, 40,10) ~ selection.rect(7,8, 60,14); */
330 staticfn int
331 l_selection_xor(lua_State *L)
333 int x,y;
334 struct selectionvar *sela = l_selection_check(L, 1);
335 struct selectionvar *selb = l_selection_check(L, 2);
336 struct selectionvar *selr = l_selection_push_new(L);
337 NhRect rect = cg.zeroNhRect;
339 rect_bounds(sela->bounds, selb->bounds, &rect);
341 for (x = rect.lx; x <= rect.hx; x++)
342 for (y = rect.ly; y <= rect.hy; y++) {
343 int val = (selection_getpoint(x, y, sela)
344 ^ selection_getpoint(x, y, selb));
346 selection_setpoint(x, y, selr, val);
348 /* this may have created a smaller or irregular selection with
349 * bounds_dirty set to true - update its boundaries */
350 selection_recalc_bounds(selr);
352 lua_remove(L, 1);
353 lua_remove(L, 1);
354 return 1;
357 /* local sel = selection.area(10,10, 20,20) - selection.area(14,14, 17,17)
358 * - i.e. points that are in A but not in B */
359 staticfn int
360 l_selection_sub(lua_State *L)
362 int x,y;
363 struct selectionvar *sela = l_selection_check(L, 1);
364 struct selectionvar *selb = l_selection_check(L, 2);
365 struct selectionvar *selr = l_selection_push_new(L);
366 NhRect rect = cg.zeroNhRect;
368 rect_bounds(sela->bounds, selb->bounds, &rect);
370 for (x = rect.lx; x <= rect.hx; x++)
371 for (y = rect.ly; y <= rect.hy; y++) {
372 coordxy a_pt = selection_getpoint(x, y, sela);
373 coordxy b_pt = selection_getpoint(x, y, selb);
374 int val = (a_pt ^ b_pt) & a_pt;
375 selection_setpoint(x, y, selr, val);
377 /* this may have created a smaller or irregular selection with
378 * bounds_dirty set to true - update its boundaries */
379 selection_recalc_bounds(selr);
381 lua_remove(L, 1);
382 lua_remove(L, 1);
383 return 1;
386 /* local s = selection.percentage(sel, 50); */
387 staticfn int
388 l_selection_filter_percent(lua_State *L)
390 int argc = lua_gettop(L);
391 struct selectionvar *sel = l_selection_check(L, 1);
392 int p = (int) luaL_checkinteger(L, 2);
393 struct selectionvar *tmp;
395 tmp = selection_filter_percent(sel, p);
396 lua_pop(L, argc);
397 l_selection_push_copy(L, tmp);
398 selection_free(tmp, TRUE);
400 return 1;
403 /* local pt = selection.rndcoord(sel); */
404 /* local pt = selection.rndcoord(sel, 1); */
405 staticfn int
406 l_selection_rndcoord(lua_State *L)
408 struct selectionvar *sel = l_selection_check(L, 1);
409 int removeit = (int) luaL_optinteger(L, 2, 0);
410 coordxy x = -1, y = -1;
411 selection_rndcoord(sel, &x, &y, removeit);
412 if (!(x == -1 && y == -1)) {
413 update_croom();
414 if (gc.coder && gc.coder->croom) {
415 x -= gc.coder->croom->lx;
416 y -= gc.coder->croom->ly;
417 } else {
418 x -= gx.xstart;
419 y -= gy.ystart;
422 lua_settop(L, 0);
423 lua_newtable(L);
424 nhl_add_table_entry_int(L, "x", x);
425 nhl_add_table_entry_int(L, "y", y);
426 return 1;
429 /* local s = selection.room(); */
430 staticfn int
431 l_selection_room(lua_State *L)
433 struct selectionvar *sel;
434 int argc = lua_gettop(L);
435 struct mkroom *croom = NULL;
437 if (argc == 1) {
438 int i = luaL_checkinteger(L, -1);
440 croom = (i >= 0 && i < svn.nroom) ? &svr.rooms[i] : NULL;
443 sel = selection_from_mkroom(croom);
445 l_selection_push_copy(L, sel);
446 selection_free(sel, TRUE);
448 return 1;
451 /* local rect = sel:bounds(); */
452 staticfn int
453 l_selection_getbounds(lua_State *L)
455 struct selectionvar *sel = l_selection_check(L, 1);
456 NhRect rect = cg.zeroNhRect;
458 selection_getbounds(sel, &rect);
459 lua_settop(L, 0);
460 lua_newtable(L);
461 nhl_add_table_entry_int(L, "lx", rect.lx);
462 nhl_add_table_entry_int(L, "ly", rect.ly);
463 nhl_add_table_entry_int(L, "hx", rect.hx);
464 nhl_add_table_entry_int(L, "hy", rect.hy);
465 return 1;
468 /* internal function to get a selection and 4 integer values from lua stack.
469 removes the integers from the stack.
470 returns TRUE if params are good.
472 /* function(selection, x1,y1, x2,y2) */
473 /* selection:function(x1,y1, x2,y2) */
474 staticfn boolean
475 params_sel_2coords(lua_State *L, struct selectionvar **sel,
476 coordxy *x1, coordxy *y1, coordxy *x2, coordxy *y2)
478 int argc = lua_gettop(L);
480 if (argc == 4) {
481 (void) l_selection_new(L);
482 *x1 = (coordxy) luaL_checkinteger(L, 1);
483 *y1 = (coordxy) luaL_checkinteger(L, 2);
484 *x2 = (coordxy) luaL_checkinteger(L, 3);
485 *y2 = (coordxy) luaL_checkinteger(L, 4);
486 *sel = l_selection_check(L, 5);
487 lua_remove(L, 1);
488 lua_remove(L, 1);
489 lua_remove(L, 1);
490 lua_remove(L, 1);
491 return TRUE;
492 } else if (argc == 5) {
493 *sel = l_selection_check(L, 1);
494 *x1 = (coordxy) luaL_checkinteger(L, 2);
495 *y1 = (coordxy) luaL_checkinteger(L, 3);
496 *x2 = (coordxy) luaL_checkinteger(L, 4);
497 *y2 = (coordxy) luaL_checkinteger(L, 5);
498 lua_pop(L, 4);
499 return TRUE;
501 return FALSE;
504 /* local s = selection.line(sel, x1,y1, x2,y2); */
505 /* local s = selection.line(x1,y1, x2,y2); */
506 /* s:line(x1,y1, x2,y2); */
507 staticfn int
508 l_selection_line(lua_State *L)
510 struct selectionvar *sel = NULL;
511 coordxy x1, y1, x2, y2;
513 if (!params_sel_2coords(L, &sel, &x1, &y1, &x2, &y2)) {
514 nhl_error(L, "selection.line: illegal arguments");
517 get_location_coord(&x1, &y1, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
518 SP_COORD_PACK(x1, y1));
519 get_location_coord(&x2, &y2, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
520 SP_COORD_PACK(x2, y2));
522 (void) l_selection_clone(L);
523 sel = l_selection_check(L, 2);
524 selection_do_line(x1,y1,x2,y2, sel);
525 return 1;
528 /* local s = selection.rect(sel, x1,y1, x2,y2); */
529 staticfn int
530 l_selection_rect(lua_State *L)
532 struct selectionvar *sel = NULL;
533 coordxy x1, y1, x2, y2;
535 if (!params_sel_2coords(L, &sel, &x1, &y1, &x2, &y2)) {
536 nhl_error(L, "selection.rect: illegal arguments");
539 get_location_coord(&x1, &y1, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
540 SP_COORD_PACK(x1, y1));
541 get_location_coord(&x2, &y2, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
542 SP_COORD_PACK(x2, y2));
544 (void) l_selection_clone(L);
545 sel = l_selection_check(L, 2);
546 selection_do_line(x1, y1, x2, y1, sel);
547 selection_do_line(x1, y1, x1, y2, sel);
548 selection_do_line(x2, y1, x2, y2, sel);
549 selection_do_line(x1, y2, x2, y2, sel);
550 return 1;
553 /* local s = selection.fillrect(sel, x1,y1, x2,y2); */
554 /* local s = selection.fillrect(x1,y1, x2,y2); */
555 /* s:fillrect(x1,y1, x2,y2); */
556 /* selection.area(x1,y1, x2,y2); */
557 staticfn int
558 l_selection_fillrect(lua_State *L)
560 struct selectionvar *sel = NULL;
561 int y;
562 coordxy x1, y1, x2, y2;
564 if (!params_sel_2coords(L, &sel, &x1, &y1, &x2, &y2)) {
565 nhl_error(L, "selection.fillrect: illegal arguments");
568 get_location_coord(&x1, &y1, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
569 SP_COORD_PACK(x1, y1));
570 get_location_coord(&x2, &y2, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
571 SP_COORD_PACK(x2, y2));
573 (void) l_selection_clone(L);
574 sel = l_selection_check(L, 2);
575 if (x1 == x2) {
576 for (y = y1; y <= y2; y++)
577 selection_setpoint(x1, y, sel, 1);
578 } else {
579 for (y = y1; y <= y2; y++)
580 selection_do_line(x1, y, x2, y, sel);
582 return 1;
585 /* local s = selection.randline(sel, x1,y1, x2,y2, roughness); */
586 /* local s = selection.randline(x1,y1, x2,y2, roughness); */
587 /* TODO: selection.randline(x1,y1, x2,y2, roughness); */
588 /* TODO: selection.randline({x1,y1}, {x2,y2}, roughness); */
589 staticfn int
590 l_selection_randline(lua_State *L)
592 int argc = lua_gettop(L);
593 struct selectionvar *sel;
594 coordxy x1 = 0, y1 = 0, x2 = 0, y2 = 0;
595 int roughness = 7;
597 if (argc == 6) {
598 (void) l_selection_check(L, 1);
599 x1 = (coordxy) luaL_checkinteger(L, 2);
600 y1 = (coordxy) luaL_checkinteger(L, 3);
601 x2 = (coordxy) luaL_checkinteger(L, 4);
602 y2 = (coordxy) luaL_checkinteger(L, 5);
603 roughness = (int) luaL_checkinteger(L, 6);
604 lua_pop(L, 5);
605 } else if (argc == 5 && lua_type(L, 1) == LUA_TNUMBER) {
606 x1 = (coordxy) luaL_checkinteger(L, 1);
607 y1 = (coordxy) luaL_checkinteger(L, 2);
608 x2 = (coordxy) luaL_checkinteger(L, 3);
609 y2 = (coordxy) luaL_checkinteger(L, 4);
610 roughness = (int) luaL_checkinteger(L, 5);
611 lua_pop(L, 5);
612 (void) l_selection_new(L);
613 (void) l_selection_check(L, 1);
616 get_location_coord(&x1, &y1, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
617 SP_COORD_PACK(x1, y1));
618 get_location_coord(&x2, &y2, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
619 SP_COORD_PACK(x2, y2));
621 (void) l_selection_clone(L);
622 sel = l_selection_check(L, 2);
623 selection_do_randline(x1, y1, x2, y2, roughness, 12, sel);
624 return 1;
627 /* local s = selection.grow(sel); */
628 /* local s = selection.grow(sel, "north"); */
629 staticfn int
630 l_selection_grow(lua_State *L)
632 static const char *const growdirs[] = {
633 "all", "random", "north", "west", "east", "south", NULL
635 static const int growdirs2i[] = {
636 W_ANY, W_RANDOM, W_NORTH, W_WEST, W_EAST, W_SOUTH, 0
638 struct selectionvar *sel;
639 int dir, argc = lua_gettop(L);
641 (void) l_selection_check(L, 1);
642 dir = growdirs2i[luaL_checkoption(L, 2, "all", growdirs)];
644 if (argc == 2)
645 lua_pop(L, 1); /* get rid of growdir */
647 (void) l_selection_clone(L);
648 sel = l_selection_check(L, 2);
649 selection_do_grow(sel, dir);
650 return 1;
654 /* local s = selection.filter_mapchar(sel, mapchar, lit); */
655 staticfn int
656 l_selection_filter_mapchar(lua_State *L)
658 int argc = lua_gettop(L);
659 struct selectionvar *sel = l_selection_check(L, 1);
660 char *mapchr = dupstr(luaL_checkstring(L, 2));
661 coordxy typ = check_mapchr(mapchr);
662 int lit = (int) luaL_optinteger(L, 3, -2); /* TODO: special lit values */
663 struct selectionvar *tmp;
665 if (typ == INVALID_TYPE)
666 nhl_error(L, "Erroneous map char");
668 tmp = selection_filter_mapchar(sel, typ, lit);
669 lua_pop(L, argc);
670 l_selection_push_copy(L, tmp);
671 selection_free(tmp, TRUE);
673 if (mapchr)
674 free(mapchr);
676 return 1;
679 /* local s = selection.match([[...]]); */
680 staticfn int
681 l_selection_match(lua_State *L)
683 int argc = lua_gettop(L);
684 struct selectionvar *sel = (struct selectionvar *) 0;
685 struct mapfragment *mf = (struct mapfragment *) 0;
686 int x, y;
688 if (argc == 1) {
689 const char *err;
690 char *mapstr = dupstr(luaL_checkstring(L, 1));
691 lua_pop(L, 1);
692 (void) l_selection_new(L);
693 sel = l_selection_check(L, 1);
695 mf = mapfrag_fromstr(mapstr);
696 free(mapstr);
698 if ((err = mapfrag_error(mf)) != NULL) {
699 nhl_error(L, err);
700 /*NOTREACHED*/
703 } else {
704 nhl_error(L, "wrong parameters");
705 /*NOTREACHED*/
708 for (y = 0; y <= sel->hei; y++)
709 for (x = 1; x < sel->wid; x++)
710 selection_setpoint(x, y, sel, mapfrag_match(mf, x,y) ? 1 : 0);
712 mapfrag_free(&mf);
714 return 1;
718 /* local s = selection.floodfill(x,y); */
719 /* local s = selection.floodfill(x,y, diagonals); */
720 staticfn int
721 l_selection_flood(lua_State *L)
723 int argc = lua_gettop(L);
724 struct selectionvar *sel = (struct selectionvar *) 0;
725 coordxy x = 0, y = 0;
726 boolean diagonals = FALSE;
728 if (argc == 2 || argc == 3) {
729 x = (coordxy) luaL_checkinteger(L, 1);
730 y = (coordxy) luaL_checkinteger(L, 2);
731 if (argc == 3)
732 diagonals = lua_toboolean(L, 3);
733 lua_pop(L, argc);
734 (void) l_selection_new(L);
735 sel = l_selection_check(L, 1);
736 } else {
737 nhl_error(L, "wrong parameters");
738 /*NOTREACHED*/
741 get_location_coord(&x, &y, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
742 SP_COORD_PACK(x, y));
744 if (isok(x, y)) {
745 set_floodfillchk_match_under(levl[x][y].typ);
746 selection_floodfill(sel, x, y, diagonals);
748 return 1;
752 /* local s = selection.circle(x,y, radius); */
753 /* local s = selection.circle(x, y, radius, filled); */
754 /* local s = selection.circle(sel, x, y, radius); */
755 /* local s = selection.circle(sel, x, y, radius, filled); */
756 staticfn int
757 l_selection_circle(lua_State *L)
759 int argc = lua_gettop(L);
760 struct selectionvar *sel = (struct selectionvar *) 0;
761 coordxy x = 0, y = 0;
762 int r = 0, filled = 0;
764 if (argc == 3) {
765 x = (coordxy) luaL_checkinteger(L, 1);
766 y = (coordxy) luaL_checkinteger(L, 2);
767 r = (int) luaL_checkinteger(L, 3);
768 lua_pop(L, 3);
769 (void) l_selection_new(L);
770 sel = l_selection_check(L, 1);
771 filled = 0;
772 } else if (argc == 4 && lua_type(L, 1) == LUA_TNUMBER) {
773 x = (coordxy) luaL_checkinteger(L, 1);
774 y = (coordxy) luaL_checkinteger(L, 2);
775 r = (int) luaL_checkinteger(L, 3);
776 filled = (int) luaL_checkinteger(L, 4); /* TODO: boolean*/
777 lua_pop(L, 4);
778 (void) l_selection_new(L);
779 sel = l_selection_check(L, 1);
780 } else if (argc == 4 || argc == 5) {
781 sel = l_selection_check(L, 1);
782 x = (coordxy) luaL_checkinteger(L, 2);
783 y = (coordxy) luaL_checkinteger(L, 3);
784 r = (int) luaL_checkinteger(L, 4);
785 filled = (int) luaL_optinteger(L, 5, 0); /* TODO: boolean */
786 } else {
787 nhl_error(L, "wrong parameters");
788 /*NOTREACHED*/
791 get_location_coord(&x, &y, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
792 SP_COORD_PACK(x, y));
794 selection_do_ellipse(sel, x, y, r, r, !filled);
796 lua_settop(L, 1);
797 return 1;
800 /* local s = selection.ellipse(x, y, radius1, radius2); */
801 /* local s = selection.ellipse(x, y, radius1, radius2, filled); */
802 /* local s = selection.ellipse(sel, x, y, radius1, radius2); */
803 /* local s = selection.ellipse(sel, x, y, radius1, radius2, filled); */
804 staticfn int
805 l_selection_ellipse(lua_State *L)
807 int argc = lua_gettop(L);
808 struct selectionvar *sel = (struct selectionvar *) 0;
809 coordxy x = 0, y = 0;
810 int r1 = 0, r2 = 0, filled = 0;
812 if (argc == 4) {
813 x = (coordxy) luaL_checkinteger(L, 1);
814 y = (coordxy) luaL_checkinteger(L, 2);
815 r1 = (int) luaL_checkinteger(L, 3);
816 r2 = (int) luaL_checkinteger(L, 4);
817 lua_pop(L, 4);
818 (void) l_selection_new(L);
819 sel = l_selection_check(L, 1);
820 filled = 0;
821 } else if (argc == 5 && lua_type(L, 1) == LUA_TNUMBER) {
822 x = (coordxy) luaL_checkinteger(L, 1);
823 y = (coordxy) luaL_checkinteger(L, 2);
824 r1 = (int) luaL_checkinteger(L, 3);
825 r2 = (int) luaL_checkinteger(L, 4);
826 filled = (int) luaL_optinteger(L, 5, 0); /* TODO: boolean */
827 lua_pop(L, 5);
828 (void) l_selection_new(L);
829 sel = l_selection_check(L, 1);
830 } else if (argc == 5 || argc == 6) {
831 sel = l_selection_check(L, 1);
832 x = (coordxy) luaL_checkinteger(L, 2);
833 y = (coordxy) luaL_checkinteger(L, 3);
834 r1 = (int) luaL_checkinteger(L, 4);
835 r2 = (int) luaL_checkinteger(L, 5);
836 filled = (int) luaL_optinteger(L, 6, 0); /* TODO: boolean */
837 } else {
838 nhl_error(L, "wrong parameters");
839 /*NOTREACHED*/
842 get_location_coord(&x, &y, ANY_LOC, gc.coder ? gc.coder->croom : NULL,
843 SP_COORD_PACK(x, y));
845 selection_do_ellipse(sel, x, y, r1, r2, !filled);
847 lua_settop(L, 1);
848 return 1;
851 /* Gradients are versatile enough, with so many independently optional
852 * arguments, that it doesn't seem helpful to provide a non-table form with
853 * non-obvious argument order. */
854 /* selection.gradient({ type = "radial", x = 3, y = 5, x2 = 10, y2 = 12,
855 * mindist = 4, maxdist = 10, limited = false }); */
856 staticfn int
857 l_selection_gradient(lua_State *L)
859 int argc = lua_gettop(L);
860 struct selectionvar *sel = (struct selectionvar *) 0;
861 /* if x2 and y2 aren't set, the gradient has a single center point of x,y;
862 * if they are set, the gradient is centered on a (x,y) to (x2,y2) line */
863 coordxy x = 0, y = 0, x2 = -1, y2 = -1;
864 /* points are always added within mindist of the center; the chance for a
865 * point between mindist and maxdist to be added to the selection starts
866 * at 100% at mindist and decreases linearly to 0% at maxdist */
867 coordxy mindist = 0, maxdist = 0;
868 long type = 0;
869 static const char *const gradtypes[] = {
870 "radial", "square", NULL
872 static const int gradtypes2i[] = {
873 SEL_GRADIENT_RADIAL, SEL_GRADIENT_SQUARE, -1
876 if (argc == 1 && lua_type(L, 1) == LUA_TTABLE) {
877 lcheck_param_table(L);
878 type = gradtypes2i[get_table_option(L, "type", "radial", gradtypes)];
879 x = (coordxy) get_table_int(L, "x");
880 y = (coordxy) get_table_int(L, "y");
881 x2 = (coordxy) get_table_int_opt(L, "x2", -1);
882 y2 = (coordxy) get_table_int_opt(L, "y2", -1);
883 cvt_to_abscoord(&x, &y);
884 cvt_to_abscoord(&x2, &y2);
885 /* maxdist is required because there's no obvious default value for
886 * it, whereas mindist has an obvious default of 0 */
887 maxdist = get_table_int(L, "maxdist");
888 mindist = get_table_int_opt(L, "mindist", 0);
890 lua_pop(L, 1);
891 (void) l_selection_new(L);
892 sel = l_selection_check(L, 1);
893 } else {
894 nhl_error(L, "selection.gradient requires table argument");
895 /* NOTREACHED */
898 /* someone might conceivably want to draw a gradient somewhere off-map. So
899 * the only coordinate that's "illegal" for that is (-1,-1).
900 * If a level designer really needs to draw a gradient line using that
901 * coordinate, they can do so by setting regular x and y to -1. */
902 if (x2 == -1 && y2 == -1) {
903 x2 = x;
904 y2 = y;
907 selection_do_gradient(sel, x, y, x2, y2, type, mindist, maxdist);
908 lua_settop(L, 1);
909 return 1;
912 /* sel:iterate(function(x,y) ... end);
913 * The x, y coordinates passed to the function are map- or room-relative
914 * rather than absolute, unless there has been no previous map or room
915 * defined.
917 staticfn int
918 l_selection_iterate(lua_State *L)
920 int argc = lua_gettop(L);
921 struct selectionvar *sel = (struct selectionvar *) 0;
922 int x, y;
923 NhRect rect = cg.zeroNhRect;
925 if (argc == 2 && lua_type(L, 2) == LUA_TFUNCTION) {
926 sel = l_selection_check(L, 1);
927 selection_getbounds(sel, &rect);
928 for (y = rect.ly; y <= rect.hy; y++)
929 for (x = max(1,rect.lx); x <= rect.hx; x++)
930 if (selection_getpoint(x, y, sel)) {
931 coordxy tmpx = x, tmpy = y;
932 cvt_to_relcoord(&tmpx, &tmpy);
933 lua_pushvalue(L, 2);
934 lua_pushinteger(L, tmpx);
935 lua_pushinteger(L, tmpy);
936 if (nhl_pcall_handle(L, 2, 0, "l_selection_iterate",
937 NHLpa_impossible)) {
938 /* abort loops to prevent possible error cascade */
939 goto out;
942 } else {
943 nhl_error(L, "wrong parameters");
944 /*NOTREACHED*/
946 out:
947 return 0;
951 static const struct luaL_Reg l_selection_methods[] = {
952 { "new", l_selection_new },
953 { "clone", l_selection_clone },
954 { "get", l_selection_getpoint },
955 { "set", l_selection_setpoint },
956 { "numpoints", l_selection_numpoints },
957 { "negate", l_selection_not },
958 { "percentage", l_selection_filter_percent },
959 { "rndcoord", l_selection_rndcoord },
960 { "line", l_selection_line },
961 { "randline", l_selection_randline },
962 { "rect", l_selection_rect },
963 { "fillrect", l_selection_fillrect },
964 { "area", l_selection_fillrect },
965 { "grow", l_selection_grow },
966 { "filter_mapchar", l_selection_filter_mapchar },
967 { "match", l_selection_match },
968 { "floodfill", l_selection_flood },
969 { "circle", l_selection_circle },
970 { "ellipse", l_selection_ellipse },
971 { "gradient", l_selection_gradient },
972 { "iterate", l_selection_iterate },
973 { "bounds", l_selection_getbounds },
974 { "room", l_selection_room },
975 { NULL, NULL }
978 static const luaL_Reg l_selection_meta[] = {
979 { "__gc", l_selection_gc },
980 { "__unm", l_selection_not },
981 { "__band", l_selection_and },
982 { "__bor", l_selection_or },
983 { "__bxor", l_selection_xor },
984 { "__bnot", l_selection_not },
985 { "__add", l_selection_or }, /* this aliases + to be the same as | */
986 { "__sub", l_selection_sub },
987 /* TODO: http://lua-users.org/wiki/MetatableEvents
988 { "__ipairs", l_selection_ipairs },
990 { NULL, NULL }
994 l_selection_register(lua_State *L)
996 /* Table of instance methods and static methods. */
997 luaL_newlib(L, l_selection_methods);
999 /* metatable = { __name = "selection", __gc = l_selection_gc } */
1000 luaL_newmetatable(L, "selection");
1001 luaL_setfuncs(L, l_selection_meta, 0);
1003 /* metatable.__index points at the selection method table. */
1004 lua_pushvalue(L, -2);
1005 lua_setfield(L, -2, "__index");
1007 /* Don't let lua code mess with the real metatable.
1008 Instead offer a fake one that only contains __gc. */
1009 luaL_newlib(L, l_selection_meta);
1010 lua_setfield(L, -2, "__metatable");
1012 /* We don't need the metatable anymore. It's safe in the
1013 Lua registry for use by luaL_setmetatable. */
1014 lua_pop(L, 1);
1016 /* global selection = the method table we created at the start */
1017 lua_setglobal(L, "selection");
1019 return 0;