]> granicus.if.org Git - nethack/commitdiff
Add lua selection match method
authorPasi Kallinen <paxed@alt.org>
Mon, 2 Mar 2020 14:15:40 +0000 (16:15 +0200)
committerPasi Kallinen <paxed@alt.org>
Mon, 2 Mar 2020 14:17:53 +0000 (16:17 +0200)
Also improve the replace_terrain command parameters.

include/extern.h
include/sp_lev.h
src/nhlsel.c
src/sp_lev.c
test/test_des.lua
test/test_sel.lua

index bed33c9004a9e9445eb16b0c402c46d74aba529a..425197c13da5357f9a5a2e520728eca043c1654a 100644 (file)
@@ -2468,6 +2468,12 @@ E void FDECL(sysopt_seduce_set, (int));
 
 /* ### sp_lev.c ### */
 #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET)
+E struct mapfragment *FDECL(mapfrag_fromstr, (char *));
+E void FDECL(mapfrag_free, (struct mapfragment **));
+E schar FDECL(mapfrag_get, (struct mapfragment *, int, int));
+E boolean FDECL(mapfrag_canmatch, (struct mapfragment *));
+E const char * FDECL(mapfrag_error, (struct mapfragment *));
+E boolean FDECL(mapfrag_match, (struct mapfragment *, int, int));
 E void FDECL(flip_level, (int, BOOLEAN_P));
 E void FDECL(flip_level_rnd, (int, BOOLEAN_P));
 E boolean FDECL(check_room, (xchar *, xchar *, xchar *, xchar *, BOOLEAN_P));
index 13530c7d1feecd1a312c87650458ddeab4734b2c..ccfd3851324bf096c95e0d4abaa3511d782bd3f8 100644 (file)
@@ -164,12 +164,6 @@ typedef struct {
     xchar ter, tlit;
 } terrain;
 
-typedef struct {
-    xchar chance;
-    xchar x1, y1, x2, y2;
-    xchar fromter, toter, tolit;
-} replaceterrain;
-
 typedef struct {
     struct {
         xchar room;
@@ -186,12 +180,9 @@ typedef struct _room {
     xchar rtype, chance, rlit, filled, joined;
 } room;
 
-typedef struct {
-    schar zaligntyp;
-    schar keep_region;
-    schar halign, valign;
-    char xsize, ysize;
-    char **map;
-} mazepart;
+struct mapfragment {
+    int wid, hei;
+    char *data;
+};
 
 #endif /* SP_LEV_H */
index e851ca21a4c77d728ad0508cb3068f0a9ee2266a..9a874efb4e3bfe2b0ad55463787dcc88ae7de376 100644 (file)
@@ -19,9 +19,9 @@ static int FDECL(l_selection_line, (lua_State *));
 static int FDECL(l_selection_randline, (lua_State *));
 static int FDECL(l_selection_rect, (lua_State *));
 static int FDECL(l_selection_fillrect, (lua_State *));
-static int FDECL(l_selection_fillrect, (lua_State *));
 static int FDECL(l_selection_grow, (lua_State *));
 static int FDECL(l_selection_filter_mapchar, (lua_State *));
+static int FDECL(l_selection_match, (lua_State *));
 static int FDECL(l_selection_flood, (lua_State *));
 static int FDECL(l_selection_circle, (lua_State *));
 static int FDECL(l_selection_ellipse, (lua_State *));
@@ -568,6 +568,46 @@ lua_State *L;
     return 1;
 }
 
+/* local s = selection.match([[...]]); */
+static int
+l_selection_match(L)
+lua_State *L;
+{
+    int argc = lua_gettop(L);
+    struct selectionvar *sel = (struct selectionvar *) 0;
+    struct mapfragment *mf;
+    int x, y;
+
+    if (argc == 1) {
+        const char *err;
+        char *mapstr = dupstr(luaL_checkstring(L, 1));
+        lua_pop(L, 1);
+        (void) l_selection_new(L);
+        sel = l_selection_check(L, 1);
+
+        mf = mapfrag_fromstr(mapstr);
+        free(mapstr);
+
+        if ((err = mapfrag_error(mf)) != NULL) {
+            mapfrag_free(&mf);
+            nhl_error(L, err);
+            /*NOTREACHED*/
+        }
+
+    } else {
+        nhl_error(L, "wrong parameters");
+        /*NOTREACHED*/
+    }
+
+    for (y = 0; y <= sel->hei; y++)
+        for (x = 0; x < sel->wid; x++)
+            selection_setpoint(x, y, sel, mapfrag_match(mf, x,y) ? 1 : 0);
+
+    mapfrag_free(&mf);
+
+    return 1;
+}
+
 
 /* local s = selection.floodfill(x,y); */
 static int
@@ -744,6 +784,7 @@ static const struct luaL_Reg l_selection_methods[] = {
     { "area", l_selection_fillrect },
     { "grow", l_selection_grow },
     { "filter_mapchar", l_selection_filter_mapchar },
+    { "match", l_selection_match },
     { "floodfill", l_selection_flood },
     { "circle", l_selection_circle },
     { "ellipse", l_selection_ellipse },
index aed5a08e827d48eb3a15c6f58ebeae2b54cce6d2..079c0747a2f58076a87b2d4fb6cdbf2648fdf1c3 100755 (executable)
@@ -57,7 +57,6 @@ static int FDECL(pm_to_humidity, (struct permonst *));
 static void FDECL(create_monster, (monster *, struct mkroom *));
 static void FDECL(create_object, (object *, struct mkroom *));
 static void FDECL(create_altar, (altar *, struct mkroom *));
-static void FDECL(replace_terrain, (replaceterrain *, struct mkroom *));
 static boolean FDECL(search_door, (struct mkroom *,
                                        xchar *, xchar *, XCHAR_P, int));
 static void NDECL(fix_stair_rooms);
@@ -173,6 +172,97 @@ static struct monst *invent_carrying_monster = (struct monst *) 0;
      * end of no 'g.'
      */
 
+struct mapfragment *
+mapfrag_fromstr(str)
+char *str;
+{
+    struct mapfragment *mf = (struct mapfragment *) alloc(sizeof(struct mapfragment));
+
+    char *tmps;
+
+    mf->data = dupstr(str);
+
+    (void) stripdigits(mf->data);
+    mf->wid = str_lines_maxlen(mf->data);
+    mf->hei = 0;
+    tmps = mf->data;
+    while (tmps && *tmps) {
+        char *s1 = index(tmps, '\n');
+
+        if (mf->hei > MAP_Y_LIM) {
+            free(mf->data);
+            free(mf);
+            return NULL;
+        }
+        if (s1)
+            s1++;
+        tmps = s1;
+        mf->hei++;
+    }
+    return mf;
+}
+
+void
+mapfrag_free(mf)
+struct mapfragment **mf;
+{
+    if (mf && *mf) {
+        free((*mf)->data);
+        free(*mf);
+        mf = NULL;
+    }
+}
+
+schar
+mapfrag_get(mf, x,y)
+struct mapfragment *mf;
+int x,y;
+{
+    if (y < 0 || x < 0 || y > mf->hei-1 || x > mf->wid-1)
+        panic("outside mapfrag (%i,%i), wanted (%i,%i)", mf->wid, mf->hei, x,y);
+    return splev_chr2typ(mf->data[y * (mf->wid + 1) + x]);
+}
+
+boolean
+mapfrag_canmatch(mf)
+struct mapfragment *mf;
+{
+    return ((mf->wid % 2) && (mf->hei % 2));
+}
+
+const char *
+mapfrag_error(mf)
+struct mapfragment *mf;
+{
+    if (!mf)
+        return "mapfragment error";
+    else if (!mapfrag_canmatch(mf)) {
+        mapfrag_free(&mf);
+        return "mapfragment needs to have odd height and width";
+    } else if (mapfrag_get(mf, (mf->wid/2), (mf->hei/2)) >= MAX_TYPE) {
+        mapfrag_free(&mf);
+        return "mapfragment center must be valid terrain";
+    }
+    return NULL;
+}
+
+boolean
+mapfrag_match(mf, x,y)
+struct mapfragment *mf;
+int x,y;
+{
+    int rx, ry;
+
+    for (rx = -(mf->wid / 2); rx <= (mf->wid / 2); rx++)
+        for (ry = -(mf->hei / 2); ry <= (mf->hei / 2); ry++) {
+            schar mapc = mapfrag_get(mf, rx + (mf->wid / 2) , ry + (mf->hei / 2));
+            schar levc = isok(x+rx, y+ry) ? levl[x+rx][y+ry].typ : STONE;
+            if ((mapc < MAX_TYPE) && (mapc != levc))
+                return FALSE;
+        }
+    return TRUE;
+}
+
 static void
 solidify_map()
 {
@@ -2127,31 +2217,6 @@ struct mkroom *croom;
     }
 }
 
-static void
-replace_terrain(terr, croom)
-replaceterrain *terr;
-struct mkroom *croom;
-{
-    schar x, y, x1, y1, x2, y2;
-
-    if (terr->toter >= MAX_TYPE)
-        return;
-
-    x1 = terr->x1;
-    y1 = terr->y1;
-    get_location(&x1, &y1, ANY_LOC, croom);
-
-    x2 = terr->x2;
-    y2 = terr->y2;
-    get_location(&x2, &y2, ANY_LOC, croom);
-
-    for (x = max(x1, 0); x <= min(x2, COLNO - 1); x++)
-        for (y = max(y1, 0); y <= min(y2, ROWNO - 1); y++)
-            if (levl[x][y].typ == terr->fromter && rn2(100) < terr->chance) {
-                SET_TYPLIT(x, y, terr->toter, terr->tolit);
-            }
-}
-
 /*
  * Search for a door in a room on a specified wall.
  */
@@ -4922,17 +4987,22 @@ lua_State *L;
     return 0;
 }
 
-/* TODO: better parameters, allow selection instead of x1,y1,x2,y2 nonsense.
-   TODO: or remove, if terrain + selection can do this better?
-*/
 /* replace_terrain({ x1=NN,y1=NN, x2=NN,y2=NN, fromterrain=MAPCHAR, toterrain=MAPCHAR, lit=N, chance=NN }); */
 /* replace_terrain({ region={x1,y1, x2,y2}, fromterrain=MAPCHAR, toterrain=MAPCHAR, lit=N, chance=NN }); */
+/* replace_terrain({ selection=selection.area(2,5, 40,10), fromterrain=MAPCHAR, toterrain=MAPCHAR }); */
+/* replace_terrain({ selection=SEL, mapfragment=[[...]], toterrain=MAPCHAR }); */
 int
 lspo_replace_terrain(L)
 lua_State *L;
 {
-    replaceterrain rt;
     xchar totyp, fromtyp;
+    struct mapfragment *mf = NULL;
+    struct selectionvar *sel = NULL;
+    boolean freesel = FALSE;
+    int x, y;
+    int x1, y1, x2, y2;
+    int chance;
+    int tolit;
 
     create_des_coder();
 
@@ -4940,25 +5010,75 @@ lua_State *L;
 
     totyp = get_table_mapchr(L, "toterrain");
 
-    fromtyp = get_table_mapchr(L, "fromterrain");
+    if (totyp >= MAX_TYPE)
+        return 0;
 
-    rt.chance = get_table_int_opt(L, "chance", 100);
-    rt.tolit = get_table_int_opt(L, "lit", 1);
-    rt.toter = totyp;
-    rt.fromter = fromtyp;
-    rt.x1 = get_table_int_opt(L, "x1", -1);
-    rt.y1 = get_table_int_opt(L, "y1", -1);
-    rt.x2 = get_table_int_opt(L, "x2", -1);
-    rt.y2 = get_table_int_opt(L, "y2", -1);
+    fromtyp = get_table_mapchr_opt(L, "fromterrain", INVALID_TYPE);
 
-    if (rt.x1 == -1 && rt.y1 == -1 && rt.x2 == -1 && rt.y2 == -1) {
-        int rx1, ry1, rx2, ry2;
-        get_table_region(L, "region", &rx1, &ry1, &rx2, &ry2, FALSE);
-        rt.x1 = rx1; rt.y1 = ry1;
-        rt.x2 = rx2; rt.y2 = ry2;
+    if (fromtyp == INVALID_TYPE) {
+        const char *err;
+        char *tmpstr = get_table_str(L, "mapfragment");
+        mf = mapfrag_fromstr(tmpstr);
+        free(tmpstr);
+
+        if ((err = mapfrag_error(mf)) != NULL) {
+            mapfrag_free(&mf);
+            nhl_error(L, err);
+            /*NOTREACHED*/
+        }
+    }
+
+    chance = get_table_int_opt(L, "chance", 100);
+    tolit = get_table_int_opt(L, "lit", -2);
+    x1 = get_table_int_opt(L, "x1", -1);
+    y1 = get_table_int_opt(L, "y1", -1);
+    x2 = get_table_int_opt(L, "x2", -1);
+    y2 = get_table_int_opt(L, "y2", -1);
+
+    if (x1 == -1 && y1 == -1 && x2 == -1 && y2 == -1) {
+        get_table_region(L, "region", &x1, &y1, &x2, &y2, TRUE);
+    }
+
+    if (x1 == -1 && y1 == -1 && x2 == -1 && y2 == -1) {
+        lua_getfield(L, 1, "selection");
+        if (lua_type(L, -1) != LUA_TNIL)
+            sel = l_selection_check(L, -1);
+        lua_pop(L, 1);
     }
 
-    replace_terrain(&rt, g.coder->croom);
+    if (!sel) {
+        sel = selection_new();
+        freesel = TRUE;
+
+        if (x1 == -1 && y1 == -1 && x2 == -1 && y2 == -1) {
+            (void) selection_not(sel);
+        } else {
+            schar rx1, ry1, rx2, ry2;
+            rx1 = x1, ry1 = y1, rx2 = x2, ry2 = x2;
+            get_location(&rx1, &ry1, ANY_LOC, g.coder->croom);
+            get_location(&rx2, &ry2, ANY_LOC, g.coder->croom);
+            for (x = max(rx1, 0); x <= min(rx2, COLNO - 1); x++)
+                for (y = max(ry1, 0); y <= min(ry2, ROWNO - 1); y++)
+                    selection_setpoint(x,y, sel, 1);
+        }
+    }
+
+    for (y = 0; y <= sel->hei; y++)
+        for (x = 0; x < sel->wid; x++)
+            if (selection_getpoint(x,y,sel)) {
+                if (mf) {
+                    if (mapfrag_match(mf, x,y) && (rn2(100)) < chance)
+                        SET_TYPLIT(x, y, totyp, tolit);
+                } else {
+                    if (levl[x][y].typ == fromtyp && rn2(100) < chance)
+                        SET_TYPLIT(x, y, totyp, tolit);
+                }
+            }
+
+    if (freesel)
+        selection_free(sel, TRUE);
+
+    mapfrag_free(&mf);
 
     return 0;
 }
@@ -5736,7 +5856,6 @@ int
 lspo_map(L)
 lua_State *L;
 {
-    mazepart tmpmazepart;
     xchar tmpxstart, tmpystart, tmpxsize, tmpysize;
 
     /*
@@ -5756,36 +5875,31 @@ TODO: g.coder->croom needs to be updated
     };
     static const int t_or_b2i[] = { TOP, CENTER, BOTTOM, -1, -1 };
     int lr, tb, keepregion = 1, x = -1, y = -1;
-    char *tmps, *mapdata;
-    int mapwid, maphei = 0;
+    struct mapfragment *mf;
     int argc = lua_gettop(L);
 
     create_des_coder();
 
     if (argc == 1 && lua_type(L, 1) == LUA_TSTRING) {
+        char *tmpstr = dupstr(luaL_checkstring(L, 1));
         lr = tb = CENTER;
-        mapdata = dupstr(luaL_checkstring(L, 1));
+        mf = mapfrag_fromstr(tmpstr);
+        free(tmpstr);
     } else {
+        char *tmpstr;
         lcheck_param_table(L);
         lr = l_or_r2i[get_table_option(L, "halign", "none", left_or_right)];
         tb = t_or_b2i[get_table_option(L, "valign", "none", top_or_bot)];
         keepregion = get_table_boolean_opt(L, "keepregion", 1); /* TODO: maybe rename? */
         get_table_xy_or_coord(L, &x, &y);
-        mapdata = get_table_str(L, "map");
+        tmpstr = get_table_str(L, "map");
+        mf = mapfrag_fromstr(tmpstr);
+        free(tmpstr);
     }
 
-    (void) stripdigits(mapdata);
-    mapwid = str_lines_maxlen(mapdata);
-    tmps = mapdata;
-    while (tmps && *tmps) {
-        char *s1 = index(tmps, '\n');
-
-        if (maphei > MAP_Y_LIM)
-            break;
-        if (s1)
-            s1++;
-        tmps = s1;
-        maphei++;
+    if (!mf) {
+        nhl_error(L, "Map data error");
+        return 0;
     }
 
     /* keepregion restricts the coordinates of the commands coming after
@@ -5797,10 +5911,8 @@ TODO: g.coder->croom needs to be updated
     tmpystart = g.ystart;
 
 
-    g.xsize = tmpmazepart.xsize = mapwid;
-    g.ysize = tmpmazepart.ysize = maphei;
-    tmpmazepart.halign = lr;
-    tmpmazepart.valign = tb;
+    g.xsize = mf->wid;
+    g.ysize = mf->hei;
 
     if (lr == -1 && tb == -1) {
         if (isok(x,y)) {
@@ -5809,17 +5921,18 @@ TODO: g.coder->croom needs to be updated
                 /* in a room? adjust to room relative coords */
                 g.xstart = x + g.coder->croom->lx;
                 g.ystart = y + g.coder->croom->ly;
-                g.xsize = min(tmpmazepart.xsize,
+                g.xsize = min(mf->wid,
                               (g.coder->croom->hx - g.coder->croom->lx));
-                g.ysize = min(tmpmazepart.ysize,
+                g.ysize = min(mf->hei,
                               (g.coder->croom->hy - g.coder->croom->ly));
             } else {
-                g.xsize = tmpmazepart.xsize;
-                g.ysize = tmpmazepart.ysize;
+                g.xsize = mf->wid;
+                g.ysize = mf->hei;
                 g.xstart = x;
                 g.ystart = y;
             }
         } else {
+            mapfrag_free(&mf);
             nhl_error(L, "Map requires either x,y or halign,valign params");
             return 0;
         }
@@ -5873,15 +5986,12 @@ TODO: g.coder->croom needs to be updated
         g.xsize = COLNO - 1;
         g.ysize = ROWNO;
     } else {
-        char mpchr;
         xchar mptyp;
 
         /* Load the map */
         for (y = g.ystart; y < min(ROWNO, g.ystart + g.ysize); y++)
             for (x = g.xstart; x < min(COLNO, g.xstart + g.xsize); x++) {
-                mpchr = mapdata[(y - g.ystart) * (mapwid + 1)
-                                + (x - g.xstart)];
-                mptyp = splev_chr2typ(mpchr);
+                mptyp = mapfrag_get(mf, (x - g.xstart), (y - g.ystart));
                 if (mptyp == INVALID_TYPE) {
                     /* TODO: warn about illegal map char */
                     continue;
@@ -5930,7 +6040,7 @@ TODO: g.coder->croom needs to be updated
         g.ysize = tmpysize;
     }
 
-    Free(mapdata);
+    mapfrag_free(&mf);
 
     return 0;
 }
index 62e45ddd3cd57b2e2d06eba21e9649b4bc197ba4..2a0062ac51656902b1edd08e38db1833ca76128f 100644 (file)
@@ -390,6 +390,8 @@ function test_replace_terrain()
    des.replace_terrain({ x1=1, y1=1, x2=70,y2=19, fromterrain=".", toterrain="I", lit=1 });
    des.replace_terrain({ x1=1, y1=1, x2=70,y2=19, fromterrain=".", toterrain="I", chance=50 });
    des.replace_terrain({ region={1,1, 70,19}, fromterrain=".", toterrain="L", chance=25 });
+   des.replace_terrain({ selection=selection.area(2,5, 10,15), fromterrain="L", toterrain="." });
+   des.replace_terrain({ mapfragment=[[...]], toterrain="T" });
 end
 
 function test_corridor()
index 387c112605d839d8f2811e966122b5c13236f75e..66b7c30ddcf5fc03bf2b1819fcd8e2bf1c77a00d 100644 (file)
@@ -352,9 +352,12 @@ function test_sel_flood()
    sel_pt_ne(selb, 5,5, 1, __func__);
    sel_pt_ne(selb, 6,5, 1, __func__);
    sel_pt_ne(selb, 7,5, 1, __func__);
-
 end -- test_sel_flood
 
+function test_sel_match()
+   local sel = selection.match([[...]]);
+end -- test_sel_match
+
 test_selection_params();
 test_sel_negate();
 test_sel_logical_and();
@@ -368,3 +371,4 @@ test_sel_randline();
 test_sel_grow();
 test_sel_filter_mapchar();
 test_sel_flood();
+test_sel_match();