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.
353 lines
7.6 KiB
C
353 lines
7.6 KiB
C
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2020 by James R.
|
|
// Copyright (C) 2020 by Sonic Team Junior.
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file lua_taglib.c
|
|
/// \brief tag list iterator for Lua scripting
|
|
|
|
#include "doomdef.h"
|
|
#include "taglist.h"
|
|
#include "r_state.h"
|
|
|
|
#include "lua_script.h"
|
|
#include "lua_libs.h"
|
|
|
|
static int tag_iterator(lua_State *L)
|
|
{
|
|
INT32 tag = lua_isnil(L, 2) ? -1 : lua_tonumber(L, 2);
|
|
do
|
|
{
|
|
if (++tag >= MAXTAGS)
|
|
return 0;
|
|
}
|
|
while (! in_bit_array(tags_available, tag)) ;
|
|
lua_pushnumber(L, tag);
|
|
return 1;
|
|
}
|
|
|
|
enum {
|
|
#define UPVALUE lua_upvalueindex
|
|
up_garray = UPVALUE(1),
|
|
up_max_elements = UPVALUE(2),
|
|
up_element_array = UPVALUE(3),
|
|
up_sizeof_element = UPVALUE(4),
|
|
up_meta = UPVALUE(5),
|
|
#undef UPVALUE
|
|
};
|
|
|
|
static INT32 next_element(lua_State *L, const mtag_t tag, const size_t p)
|
|
{
|
|
taggroup_t ** garray = lua_touserdata(L, up_garray);
|
|
const size_t * max_elements = lua_touserdata(L, up_max_elements);
|
|
return Taggroup_Iterate(garray, *max_elements, tag, p);
|
|
}
|
|
|
|
static void push_element(lua_State *L, void *element)
|
|
{
|
|
if (LUA_RawPushUserdata(L, element) == LPUSHED_NEW)
|
|
{
|
|
lua_pushvalue(L, up_meta);
|
|
lua_setmetatable(L, -2);
|
|
}
|
|
}
|
|
|
|
static void push_next_element(lua_State *L, const INT32 element)
|
|
{
|
|
char * element_array = *(char **)lua_touserdata(L, up_element_array);
|
|
const size_t sizeof_element = lua_tonumber(L, up_sizeof_element);
|
|
push_element(L, &element_array[element * sizeof_element]);
|
|
}
|
|
|
|
struct element_iterator_state {
|
|
mtag_t tag;
|
|
size_t p;
|
|
};
|
|
|
|
static int element_iterator(lua_State *L)
|
|
{
|
|
struct element_iterator_state * state = lua_touserdata(L, 1);
|
|
lua_pushnumber(L, ++state->p);
|
|
lua_gettable(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
static int lib_iterateTags(lua_State *L)
|
|
{
|
|
if (lua_gettop(L) < 2)
|
|
{
|
|
lua_pushcfunction(L, tag_iterator);
|
|
return 1;
|
|
}
|
|
else
|
|
return tag_iterator(L);
|
|
}
|
|
|
|
static int lib_numTags(lua_State *L)
|
|
{
|
|
lua_pushnumber(L, num_tags);
|
|
return 1;
|
|
}
|
|
|
|
static int lib_getTaggroup(lua_State *L)
|
|
{
|
|
struct element_iterator_state *state;
|
|
|
|
mtag_t tag;
|
|
|
|
if (lua_gettop(L) > 1)
|
|
return luaL_error(L, "too many arguments");
|
|
|
|
if (lua_isnoneornil(L, 1))
|
|
{
|
|
tag = MTAG_GLOBAL;
|
|
}
|
|
else
|
|
{
|
|
tag = lua_tonumber(L, 1);
|
|
luaL_argcheck(L, tag >= -1, 1, "tag out of range");
|
|
}
|
|
|
|
state = lua_newuserdata(L, sizeof *state);
|
|
state->tag = tag;
|
|
state->p = 0;
|
|
|
|
lua_pushvalue(L, lua_upvalueindex(1));
|
|
lua_setmetatable(L, -2);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int lib_getTaggroupElement(lua_State *L)
|
|
{
|
|
const size_t p = luaL_checknumber(L, 2) - 1;
|
|
const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1);
|
|
const INT32 element = next_element(L, tag, p);
|
|
|
|
if (element == -1)
|
|
return 0;
|
|
else
|
|
{
|
|
push_next_element(L, element);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int lib_numTaggroupElements(lua_State *L)
|
|
{
|
|
const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1);
|
|
if (tag == MTAG_GLOBAL)
|
|
lua_pushnumber(L, *(size_t *)lua_touserdata(L, up_max_elements));
|
|
else
|
|
{
|
|
const taggroup_t ** garray = lua_touserdata(L, up_garray);
|
|
lua_pushnumber(L, garray[tag] ? garray[tag]->count : 0);
|
|
}
|
|
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[],
|
|
size_t * max_elements,
|
|
void * element_array,
|
|
size_t sizeof_element,
|
|
const char * meta)
|
|
{
|
|
lua_createtable(L, 0, 3);
|
|
lua_pushlightuserdata(L, garray);
|
|
lua_pushlightuserdata(L, max_elements);
|
|
|
|
lua_pushvalue(L, -2);
|
|
lua_pushvalue(L, -2);
|
|
lua_pushlightuserdata(L, element_array);
|
|
lua_pushnumber(L, sizeof_element);
|
|
luaL_getmetatable(L, meta);
|
|
lua_pushcclosure(L, lib_getTaggroupElement, 5);
|
|
lua_setfield(L, -4, "__index");
|
|
|
|
lua_pushcclosure(L, lib_numTaggroupElements, 2);
|
|
lua_setfield(L, -2, "__len");
|
|
|
|
lua_pushcfunction(L, element_iterator);
|
|
lua_setfield(L, -2, "__call");
|
|
lua_pushcclosure(L, lib_getTaggroup, 1);
|
|
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);
|
|
lua_createtable(L, 0, 2);
|
|
lua_createtable(L, 0, 1);
|
|
lua_pushcfunction(L, lib_iterateTags);
|
|
lua_setfield(L, -2, "iterate");
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, lib_numTags);
|
|
lua_setfield(L, -2, "__len");
|
|
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;
|
|
}
|