From 314fd2783a2b22c2803b9063994a2dd48ddd98a7 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 4 Dec 2020 13:47:22 -0800 Subject: [PATCH] Lua tag lists Index and take length of tag lists like a table, 1-indexed. There are three methods which may be used on tag lists: list:iterate() - returns an iterator over the tags in the list list:has(tag) - returns a boolean whether the tag is in the list list.shares(list2) - returns whether two lists share a tag "find" is also an alias to "has". Each method may be accessed from the global taglist library too, e.g. taglist.iterate(list) Tag lists may be compared with an equality operator too. This will tell you if the two lists are composed of identical tags. Accessible from sector.taglist, line.taglist and mapthing.taglist. --- src/lua_baselib.c | 3 + src/lua_libs.h | 3 + src/lua_maplib.c | 12 ++++ src/lua_mobjlib.c | 7 +++ src/lua_script.h | 2 + src/lua_taglib.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 183 insertions(+) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 4667fdbf4..1324322a2 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -184,12 +184,15 @@ static const struct { {META_CVAR, "consvar_t"}, {META_SECTORLINES, "sector_t.lines"}, + {META_SECTORTAGLIST, "sector_t.taglist"}, {META_SIDENUM, "line_t.sidenum"}, {META_LINEARGS, "line_t.args"}, {META_LINESTRINGARGS, "line_t.stringargs"}, + {META_LINETAGLIST, "line_t.taglist"}, {META_THINGARGS, "mapthing.args"}, {META_THINGSTRINGARGS, "mapthing.stringargs"}, + {META_THINGTAGLIST, "mapthing_t.taglist"}, #ifdef HAVE_LUA_SEGS {META_NODEBBOX, "node_t.bbox"}, {META_NODECHILDREN, "node_t.children"}, diff --git a/src/lua_libs.h b/src/lua_libs.h index aa0638683..991fae3fd 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -56,11 +56,14 @@ extern lua_State *gL; #define META_CVAR "CONSVAR_T*" #define META_SECTORLINES "SECTOR_T*LINES" +#define META_SECTORTAGLIST "SECTOR_T*TAGLIST" #define META_SIDENUM "LINE_T*SIDENUM" #define META_LINEARGS "LINE_T*ARGS" #define META_LINESTRINGARGS "LINE_T*STRINGARGS" +#define META_LINETAGLIST "LINE_T*TAGLIST" #define META_THINGARGS "MAPTHING_T*ARGS" #define META_THINGSTRINGARGS "MAPTHING_T*STRINGARGS" +#define META_THINGTAGLIST "THING_T*TAGLIST" #define META_POLYOBJVERTICES "POLYOBJ_T*VERTICES" #define META_POLYOBJLINES "POLYOBJ_T*LINES" #ifdef HAVE_LUA_SEGS diff --git a/src/lua_maplib.c b/src/lua_maplib.c index a3df28cca..25edf83d8 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -37,6 +37,7 @@ enum sector_e { sector_lightlevel, sector_special, sector_tag, + sector_taglist, sector_thinglist, sector_heightsec, sector_camsec, @@ -55,6 +56,7 @@ static const char *const sector_opt[] = { "lightlevel", "special", "tag", + "taglist", "thinglist", "heightsec", "camsec", @@ -89,6 +91,7 @@ enum line_e { line_flags, line_special, line_tag, + line_taglist, line_args, line_stringargs, line_sidenum, @@ -113,6 +116,7 @@ static const char *const line_opt[] = { "flags", "special", "tag", + "taglist", "args", "stringargs", "sidenum", @@ -581,6 +585,9 @@ static int sector_get(lua_State *L) case sector_tag: lua_pushinteger(L, Tag_FGet(§or->tags)); return 1; + case sector_taglist: + LUA_PushUserdata(L, §or->tags, META_SECTORTAGLIST); + return 1; case sector_thinglist: // thinglist lua_pushcfunction(L, lib_iterateSectorThinglist); LUA_PushUserdata(L, sector->thinglist, META_MOBJ); @@ -682,6 +689,8 @@ static int sector_set(lua_State *L) case sector_tag: Tag_SectorFSet((UINT32)(sector - sectors), (INT16)luaL_checkinteger(L, 3)); break; + case sector_taglist: + return LUA_ErrSetDirectly(L, "sector_t", "taglist"); } return 0; } @@ -821,6 +830,9 @@ static int line_get(lua_State *L) case line_tag: lua_pushinteger(L, Tag_FGet(&line->tags)); return 1; + case line_taglist: + LUA_PushUserdata(L, &line->tags, META_LINETAGLIST); + return 1; case line_args: LUA_PushUserdata(L, line->args, META_LINEARGS); return 1; diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 134f104ee..8d205780d 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -902,6 +902,11 @@ static int mapthing_get(lua_State *L) number = mt->extrainfo; else if(fastcmp(field,"tag")) number = Tag_FGet(&mt->tags); + else if(fastcmp(field,"taglist")) + { + LUA_PushUserdata(L, &mt->tags, META_THINGTAGLIST); + return 1; + } else if(fastcmp(field,"args")) { LUA_PushUserdata(L, mt->args, META_THINGARGS); @@ -964,6 +969,8 @@ static int mapthing_set(lua_State *L) } else if (fastcmp(field,"tag")) Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3)); + else if (fastcmp(field,"taglist")) + return LUA_ErrSetDirectly(L, "mapthing_t", "taglist"); else if(fastcmp(field,"mobj")) mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); else diff --git a/src/lua_script.h b/src/lua_script.h index 2dc34446a..77fbb7c1d 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -102,6 +102,8 @@ void COM_Lua_f(void); #define LUA_ErrInvalid(L, type) luaL_error(L, "accessed " type " doesn't exist anymore, please check 'valid' before using " type "."); +#define LUA_ErrSetDirectly(L, type, field) luaL_error(L, type " field " LUA_QL(field) " cannot be set directly.") + // Deprecation warnings // Shows once upon use. Then doesn't show again. #define LUA_Deprecated(L,this_func,use_instead)\ diff --git a/src/lua_taglib.c b/src/lua_taglib.c index 2e2a0d277..73f033312 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -150,6 +150,137 @@ static int lib_numTaggroupElements(lua_State *L) return 1; } +static void push_taglist(lua_State *L, int idx) +{ + lua_getmetatable(L, idx); + lua_pushliteral(L, "taglist"); + lua_rawget(L, -2); + lua_remove(L, -2); +} + +static int has_valid_field(lua_State *L) +{ + int equal; + lua_pushliteral(L, "valid"); + equal = lua_rawequal(L, 2, -1); + lua_pop(L, 1); + return equal; +} + +static taglist_t * valid_taglist(lua_State *L, int idx, boolean getting) +{ + taglist_t *list = *(taglist_t **)lua_touserdata(L, idx); + + if (list == NULL) + { + if (getting && has_valid_field(L)) + lua_pushboolean(L, 0); + else + LUA_ErrInvalid(L, "taglist_t");/* doesn't actually return */ + return NULL; + } + else + return list; +} + +static taglist_t * check_taglist(lua_State *L, int idx) +{ + luaL_checktype(L, idx, LUA_TUSERDATA); + push_taglist(L, idx); + luaL_argcheck(L, lua_toboolean(L, -1), idx, "must be a tag list"); + return valid_taglist(L, idx, false); +} + +static int taglist_get(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, true); + + if (list == NULL)/* valid check */ + return 1; + + if (lua_isnumber(L, 2)) + { + const size_t i = lua_tonumber(L, 2); + + if (list && i <= list->count) + { + lua_pushnumber(L, list->tags[i - 1]); + return 1; + } + else + return 0; + } + else if (has_valid_field(L)) + { + lua_pushboolean(L, 1); + return 1; + } + else + { + push_taglist(L, 1); + lua_replace(L, 1); + lua_rawget(L, 1); + return 1; + } +} + +static int taglist_len(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, false); + lua_pushnumber(L, list->count); + return 1; +} + +static int taglist_equal(lua_State *L) +{ + const taglist_t *lhs = check_taglist(L, 1); + const taglist_t *rhs = check_taglist(L, 2); + lua_pushboolean(L, Tag_Compare(lhs, rhs)); + return 1; +} + +static int taglist_iterator(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, false); + const size_t i = 1 + lua_tonumber(L, lua_upvalueindex(1)); + if (i <= list->count) + { + lua_pushnumber(L, list->tags[i - 1]); + /* watch me exploit an upvalue as a control because + I want to use the control as the value */ + lua_pushnumber(L, i); + lua_replace(L, lua_upvalueindex(1)); + return 1; + } + else + return 0; +} + +static int taglist_iterate(lua_State *L) +{ + check_taglist(L, 1); + lua_pushnumber(L, 0); + lua_pushcclosure(L, taglist_iterator, 1); + lua_pushvalue(L, 1); + return 2; +} + +static int taglist_find(lua_State *L) +{ + const taglist_t *list = check_taglist(L, 1); + const mtag_t tag = luaL_checknumber(L, 2); + lua_pushboolean(L, Tag_Find(list, tag)); + return 1; +} + +static int taglist_shares(lua_State *L) +{ + const taglist_t *lhs = check_taglist(L, 1); + const taglist_t *rhs = check_taglist(L, 2); + lua_pushboolean(L, Tag_Share(lhs, rhs)); + return 1; +} + void LUA_InsertTaggroupIterator ( lua_State *L, taggroup_t *garray[], @@ -179,6 +310,13 @@ void LUA_InsertTaggroupIterator lua_setfield(L, -2, "tagged"); } +static luaL_Reg taglist_lib[] = { + {"iterate", taglist_iterate}, + {"find", taglist_find}, + {"shares", taglist_shares}, + {0} +}; + int LUA_TagLib(lua_State *L) { lua_newuserdata(L, 0); @@ -193,5 +331,23 @@ int LUA_TagLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "tags"); + luaL_newmetatable(L, META_THINGTAGLIST); + luaL_register(L, "taglist", taglist_lib); + lua_getfield(L, -1, "find"); + lua_setfield(L, -2, "has"); + lua_setfield(L, -2, "taglist"); + + lua_pushcfunction(L, taglist_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, taglist_len); + lua_setfield(L, -2, "__len"); + + lua_pushcfunction(L, taglist_equal); + lua_setfield(L, -2, "__eq"); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, META_LINETAGLIST); + lua_setfield(L, LUA_REGISTRYINDEX, META_SECTORTAGLIST); + return 0; }