From: Pasi Kallinen Date: Mon, 2 Mar 2020 14:15:40 +0000 (+0200) Subject: Add lua selection match method X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c9b21e36a7096361c8cf34f9e63daaaca456c095;p=nethack Add lua selection match method Also improve the replace_terrain command parameters. --- diff --git a/include/extern.h b/include/extern.h index bed33c900..425197c13 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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)); diff --git a/include/sp_lev.h b/include/sp_lev.h index 13530c7d1..ccfd38513 100644 --- a/include/sp_lev.h +++ b/include/sp_lev.h @@ -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 */ diff --git a/src/nhlsel.c b/src/nhlsel.c index e851ca21a..9a874efb4 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -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 }, diff --git a/src/sp_lev.c b/src/sp_lev.c index aed5a08e8..079c0747a 100755 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -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; } diff --git a/test/test_des.lua b/test/test_des.lua index 62e45ddd3..2a0062ac5 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -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() diff --git a/test/test_sel.lua b/test/test_sel.lua index 387c11260..66b7c30dd 100644 --- a/test/test_sel.lua +++ b/test/test_sel.lua @@ -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();