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.
This commit is contained in:
James R 2020-12-04 13:47:22 -08:00
parent c2217bb426
commit 314fd2783a
6 changed files with 183 additions and 0 deletions

View File

@ -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"},

View File

@ -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

View File

@ -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(&sector->tags));
return 1;
case sector_taglist:
LUA_PushUserdata(L, &sector->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;

View File

@ -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

View File

@ -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)\

View File

@ -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;
}