Files
SRB2Kart-Saturn/src/lua_infolib.c
T
Alug 80b35cdefb properly fix mobj action stuff for c23
thx hanicef!
hope i didnt screw anything up in info.c
2025-05-03 20:27:08 +02:00

1181 lines
31 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2018 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_infolib.c
/// \brief infotable editing library for Lua scripting
#include "doomdef.h"
#include "fastcmp.h"
#include "info.h"
#include "dehacked.h"
#include "p_mobj.h"
#include "p_local.h"
#include "z_zone.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
#include "lua_hook.h" // cmd errors
boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor);
state_t *astate;
boolean actionsoverridden[NUMACTIONS] = {false};
//
// Sprite Names
//
// push sprite name
static int lib_getSprname(lua_State *L)
{
UINT32 i;
lua_remove(L, 1); // don't care about sprnames[] dummy userdata.
if (lua_isnumber(L, 1))
{
i = lua_tonumber(L, 1);
if (i > NUMSPRITES)
return 0;
lua_pushlstring(L, sprnames[i], 4);
return 1;
}
else if (lua_isstring(L, 1))
{
const char *name = lua_tostring(L, 1);
for (i = 0; i < NUMSPRITES; i++)
if (fastcmp(name, sprnames[i]))
{
lua_pushinteger(L, i);
return 1;
}
}
return 0;
}
/// \todo Maybe make it tally up the used_spr from dehacked?
FUNCINLINE static ATTRINLINE int lib_sprnamelen(lua_State *L)
{
lua_pushinteger(L, NUMSPRITES);
return 1;
}
////////////////
// STATE INFO //
////////////////
// Uses astate to determine which state is calling it
// Then looks up which Lua action is assigned to that state and calls it
static void A_Lua(mobj_t *actor)
{
boolean found = false;
I_Assert(actor != NULL);
lua_settop(gL, 0); // Just in case...
lua_pushcfunction(gL, LUA_GetErrorMessage);
// get the action for this state
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_STATEACTION);
I_Assert(lua_istable(gL, -1));
lua_pushlightuserdata(gL, astate);
lua_rawget(gL, -2);
I_Assert(lua_isfunction(gL, -1));
lua_remove(gL, -2); // pop LREG_STATEACTION
// get the name for this action, if possible.
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
lua_pushnil(gL);
while (lua_next(gL, -2))
{
if (lua_rawequal(gL, -1, -4))
{
found = true;
superactions[superstack] = lua_tostring(gL, -2); // "A_ACTION"
++superstack;
lua_pop(gL, 2); // pop the name and function
break;
}
lua_pop(gL, 1);
}
lua_pop(gL, 1); // pop LREG_ACTION
LUA_PushUserdata(gL, actor, META_MOBJ);
lua_pushinteger(gL, var1);
lua_pushinteger(gL, var2);
LUA_Call(gL, 3, 0, 1);
lua_pop(gL, 1); // pop error handler
if (found)
{
--superstack;
superactions[superstack] = NULL;
}
}
// Arbitrary states[] table index -> state_t *
FUNCINLINE static ATTRINLINE int lib_getState(lua_State *L)
{
UINT32 i;
lua_remove(L, 1);
i = luaL_checkinteger(L, 1);
if (i >= NUMSTATES)
return luaL_error(L, "states[] index %d out of range (0 - %d)", i, NUMSTATES-1);
LUA_PushUserdata(L, &states[i], META_STATE);
return 1;
}
// Lua table full of data -> states[] (set the values all at once! :D :D)
static int lib_setState(lua_State *L)
{
state_t *state;
lua_remove(L, 1); // don't care about states[] userdata.
{
UINT32 i = luaL_checkinteger(L, 1);
if (i >= NUMSTATES)
return luaL_error(L, "states[] index %d out of range (0 - %d)", i, NUMSTATES-1);
state = &states[i]; // get the state to assign to.
}
luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
lua_remove(L, 1); // pop state num, don't need it any more.
lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the state.
if (hud_running)
return luaL_error(L, "Do not alter states in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter states in BuildCMD code!");
// clear the state to start with, in case of missing table elements
memset(state, 0, sizeof(state_t));
state->tics = -1;
lua_pushnil(L);
while (lua_next(L, 1)) {
lua_Integer i = 0;
const char *str = NULL;
lua_Integer value;
if (lua_isnumber(L, 2))
i = lua_tointeger(L, 2);
else
str = luaL_checkstring(L, 2);
if (i == 1 || (str && fastcmp(str, "sprite"))) {
value = luaL_checkinteger(L, 3);
if (value < SPR_NULL || value >= NUMSPRITES)
return luaL_error(L, "sprite number %d is invalid.", value);
state->sprite = (spritenum_t)value;
} else if (i == 2 || (str && fastcmp(str, "frame"))) {
state->frame = (UINT32)luaL_checkinteger(L, 3);
} else if (i == 3 || (str && fastcmp(str, "tics"))) {
state->tics = (INT32)luaL_checkinteger(L, 3);
} else if (i == 4 || (str && fastcmp(str, "action"))) {
switch(lua_type(L, 3))
{
case LUA_TNIL: // Null? Set the action to nothing, then.
state->action = NULL;
break;
case LUA_TSTRING: // It's a string, expect the name of a built-in action
LUA_SetActionByName(state, lua_tostring(L, 3));
break;
case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!)
lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, state); // We'll store this function by the state's pointer in the registry.
lua_pushvalue(L, 3); // Bring it to the top of the stack
lua_rawset(L, -3); // Set it in the registry
lua_pop(L, 1); // pop LREG_STATEACTION
state->action = (actionf_p1)A_Lua; // Set the action for the userdata.
break;
default: // ?!
return luaL_typerror(L, 3, "function");
}
} else if (i == 5 || (str && fastcmp(str, "var1"))) {
state->var1 = (INT32)luaL_checkinteger(L, 3);
} else if (i == 6 || (str && fastcmp(str, "var2"))) {
state->var2 = (INT32)luaL_checkinteger(L, 3);
} else if (i == 7 || (str && fastcmp(str, "nextstate"))) {
value = luaL_checkinteger(L, 3);
if (value < S_NULL || value >= NUMSTATES)
return luaL_error(L, "nextstate number %d is invalid.", value);
state->nextstate = (statenum_t)value;
}
lua_pop(L, 1);
}
return 0;
}
// #states -> NUMSTATES
FUNCINLINE static ATTRINLINE int lib_statelen(lua_State *L)
{
lua_pushinteger(L, NUMSTATES);
return 1;
}
boolean LUA_SetLuaAction(void *stv, const char *action)
{
state_t *st = (state_t *)stv;
I_Assert(st != NULL);
//I_Assert(st >= states && st < states+NUMSTATES); // if you REALLY want to be paranoid...
I_Assert(action != NULL);
if (!gL) // Lua isn't loaded,
return false; // action not set.
// action is assumed to be in all-caps already !!
// the registry is case-sensitive, so we strupr everything that enters it.
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
lua_getfield(gL, -1, action);
if (lua_isnil(gL, -1)) // no match
{
lua_pop(gL, 2); // pop nil and LREG_ACTIONS
return false; // action not set.
}
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_STATEACTION);
I_Assert(lua_istable(gL, -1));
lua_pushlightuserdata(gL, stv); // We'll store this function by the state's pointer in the registry.
lua_pushvalue(gL, -3); // Bring it to the top of the stack
lua_rawset(gL, -3); // Set it in the registry
lua_pop(gL, 1); // pop LREG_STATEACTION
lua_pop(gL, 2); // pop the function and LREG_ACTIONS
st->action = (actionf_p1)A_Lua; // Set the action for the userdata.
return true; // action successfully set.
}
boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
{
I_Assert(actor != NULL);
if (!actionsoverridden[actionnum]) // The action is not overriden,
return false; // action not called.
if (superstack && fasticmp(actionpointers[actionnum].name, superactions[superstack-1])) // the action is calling itself,
return false; // let it call the hardcoded function
lua_pushcfunction(gL, LUA_GetErrorMessage);
// grab function by uppercase name.
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
lua_getfield(gL, -1, actionpointers[actionnum].name);
lua_remove(gL, -2); // pop LREG_ACTIONS
if (lua_isnil(gL, -1)) // no match
{
lua_pop(gL, 2); // pop nil and error handler
return false; // action not called.
}
if (superstack == MAXRECURSION)
{
CONS_Alert(CONS_WARNING, "Max Lua Action recursion reached! Cool it on the calling A_Action functions from inside A_Action functions!\n");
lua_pop(gL, 2); // pop function and error handler
return true;
}
// Found a function.
// Call it with (actor, var1, var2)
I_Assert(lua_isfunction(gL, -1));
LUA_PushUserdata(gL, actor, META_MOBJ);
lua_pushinteger(gL, var1);
lua_pushinteger(gL, var2);
superactions[superstack] = actionpointers[actionnum].name;
++superstack;
LUA_Call(gL, 3, 0, -(2 + 3));
lua_pop(gL, -1); // Error handler
--superstack;
superactions[superstack] = NULL;
return true; // action successfully called.
}
enum state_e {
state_sprite = 0,
state_frame,
state_tics,
state_action,
state_var1,
state_var2,
state_nextstate,
};
const char *const state_opt[] = {
"sprite",
"frame",
"tics",
"action",
"var1",
"var2",
"nextstate",
NULL
};
static int state_fields_ref = LUA_NOREF;
// state_t *, field -> number
static int state_get(lua_State *L)
{
state_t *st = *((state_t **)luaL_checkudata(L, 1, META_STATE));
enum state_e field = Lua_optoption(L, 2, -1, state_fields_ref);
lua_Integer number;
switch (field)
{
case state_sprite:
number = st->sprite;
break;
case state_frame:
number = st->frame;
break;
case state_tics:
number = st->tics;
break;
case state_action:
{
const char *name;
if (!st->action) // Action is NULL.
return 0; // return nil.
if (st->action == (actionf_p1)A_Lua) { // This is a Lua function?
lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, st); // Push the state pointer and
lua_rawget(L, -2); // use it to get the actual Lua function.
lua_remove(L, -2); // pop LREG_STATEACTION
return 1; // Return the Lua function.
}
name = LUA_GetActionName(&st->action); // find a hardcoded function name
if (!name) // If it's not a hardcoded function and it's not a Lua function...
return 0; // Just what is this??
// get the function from the global
// because the metatable will trigger.
lua_getglobal(L, name); // actually gets from LREG_ACTIONS if applicable, and pushes a new C closure if not.
lua_pushstring(L, name); // push the name we found.
return 2; // return both the function and its name, in case somebody wanted to do a comparison by name or something?
}
break;
case state_var1:
number = st->var1;
break;
case state_var2:
number = st->var2;
break;
case state_nextstate:
number = st->nextstate;
break;
default:
{
if (devparm)
return luaL_error(L, LUA_QL("state_t") " has no field named " LUA_QS, field);
return 0;
}
}
lua_pushinteger(L, number);
return 1;
}
// state_t *, field, number -> states[]
static int state_set(lua_State *L)
{
state_t *st = *((state_t **)luaL_checkudata(L, 1, META_STATE));
enum state_e field = Lua_optoption(L, 2, -1, state_fields_ref);
lua_Integer value;
if (hud_running)
return luaL_error(L, "Do not alter states in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter states in BuildCMD code!");
switch (field)
{
case state_sprite:
value = luaL_checknumber(L, 3);
if (value < SPR_NULL || value >= NUMSPRITES)
return luaL_error(L, "sprite number %d is invalid.", value);
st->sprite = (spritenum_t)value;
break;
case state_frame:
st->frame = (UINT32)luaL_checknumber(L, 3);
break;
case state_tics:
st->tics = (INT32)luaL_checknumber(L, 3);
break;
case state_action:
switch(lua_type(L, 3))
{
case LUA_TNIL: // Null? Set the action to nothing, then.
st->action = NULL;
break;
case LUA_TSTRING: // It's a string, expect the name of a built-in action
LUA_SetActionByName(st, lua_tostring(L, 3));
break;
case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!)
lua_getfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, st); // We'll store this function by the state's pointer in the registry.
lua_pushvalue(L, 3); // Bring it to the top of the stack
lua_rawset(L, -3); // Set it in the registry
lua_pop(L, 1); // pop LREG_STATEACTION
st->action = (actionf_p1)A_Lua; // Set the action for the userdata.
break;
default: // ?!
return luaL_typerror(L, 3, "function");
}
break;
case state_var1:
st->var1 = (INT32)luaL_checknumber(L, 3);
break;
case state_var2:
st->var2 = (INT32)luaL_checknumber(L, 3);
break;
case state_nextstate:
value = luaL_checkinteger(L, 3);
if (value < S_NULL || value >= NUMSTATES)
return luaL_error(L, "nextstate number %d is invalid.", value);
st->nextstate = (statenum_t)value;
break;
default:
return luaL_error(L, LUA_QL("state_t") " has no field named " LUA_QS, field);
}
return 0;
}
// state_t * -> S_*
static int state_num(lua_State *L)
{
state_t *state = *((state_t **)luaL_checkudata(L, 1, META_STATE));
lua_pushinteger(L, state-states);
return 1;
}
///////////////
// MOBJ INFO //
///////////////
// Arbitrary mobjinfo[] table index -> mobjinfo_t *
FUNCINLINE static ATTRINLINE int lib_getMobjInfo(lua_State *L)
{
UINT32 i;
lua_remove(L, 1);
i = luaL_checkinteger(L, 1);
if (i >= NUMMOBJTYPES)
return luaL_error(L, "mobjinfo[] index %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
LUA_PushUserdata(L, &mobjinfo[i], META_MOBJINFO);
return 1;
}
// Lua table full of data -> mobjinfo[]
static int lib_setMobjInfo(lua_State *L)
{
mobjinfo_t *info;
lua_remove(L, 1); // don't care about mobjinfo[] userdata.
{
UINT32 i = luaL_checkinteger(L, 1);
if (i >= NUMMOBJTYPES)
return luaL_error(L, "mobjinfo[] index %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
info = &mobjinfo[i]; // get the mobjinfo to assign to.
}
luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
lua_remove(L, 1); // pop mobjtype num, don't need it any more.
lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the mobjinfo.
if (hud_running)
return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter mobjinfo in BuildCMD code!");
// clear the mobjinfo to start with, in case of missing table elements
memset(info, 0, sizeof(mobjinfo_t));
info->doomednum = -1; // default to no editor value
info->spawnhealth = 1; // avoid 'dead' noclip behaviors
lua_pushnil(L);
while (lua_next(L, 1)) {
lua_Integer i = 0;
const char *str = NULL;
lua_Integer value;
if (lua_isnumber(L, 2))
i = lua_tointeger(L, 2);
else
str = luaL_checkstring(L, 2);
if (i == 1 || (str && fastcmp(str,"doomednum")))
info->doomednum = (INT32)luaL_checkinteger(L, 3);
else if (i == 2 || (str && fastcmp(str,"spawnstate"))) {
value = luaL_checkinteger(L, 3);
if (value < S_NULL || value >= NUMSTATES)
return luaL_error(L, "spawnstate number %d is invalid.", value);
info->spawnstate = (statenum_t)value;
} else if (i == 3 || (str && fastcmp(str,"spawnhealth")))
info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
else if (i == 4 || (str && fastcmp(str,"seestate"))) {
value = luaL_checkinteger(L, 3);
if (value < S_NULL || value >= NUMSTATES)
return luaL_error(L, "seestate number %d is invalid.", value);
info->seestate = (statenum_t)value;
} else if (i == 5 || (str && fastcmp(str,"seesound"))) {
value = luaL_checkinteger(L, 3);
if (value < sfx_None || value >= NUMSFX)
return luaL_error(L, "seesound number %d is invalid.", value);
info->seesound = (sfxenum_t)value;
} else if (i == 6 || (str && fastcmp(str,"reactiontime")))
info->reactiontime = (INT32)luaL_checkinteger(L, 3);
else if (i == 7 || (str && fastcmp(str,"attacksound")))
info->attacksound = luaL_checkinteger(L, 3);
else if (i == 8 || (str && fastcmp(str,"painstate")))
info->painstate = luaL_checkinteger(L, 3);
else if (i == 9 || (str && fastcmp(str,"painchance")))
info->painchance = (INT32)luaL_checkinteger(L, 3);
else if (i == 10 || (str && fastcmp(str,"painsound")))
info->painsound = luaL_checkinteger(L, 3);
else if (i == 11 || (str && fastcmp(str,"meleestate")))
info->meleestate = luaL_checkinteger(L, 3);
else if (i == 12 || (str && fastcmp(str,"missilestate")))
info->missilestate = luaL_checkinteger(L, 3);
else if (i == 13 || (str && fastcmp(str,"deathstate")))
info->deathstate = luaL_checkinteger(L, 3);
else if (i == 14 || (str && fastcmp(str,"xdeathstate")))
info->xdeathstate = luaL_checkinteger(L, 3);
else if (i == 15 || (str && fastcmp(str,"deathsound")))
info->deathsound = luaL_checkinteger(L, 3);
else if (i == 16 || (str && fastcmp(str,"speed")))
info->speed = luaL_checkfixed(L, 3);
else if (i == 17 || (str && fastcmp(str,"radius")))
info->radius = luaL_checkfixed(L, 3);
else if (i == 18 || (str && fastcmp(str,"height")))
info->height = luaL_checkfixed(L, 3);
else if (i == 19 || (str && fastcmp(str,"dispoffset")))
info->dispoffset = (INT32)luaL_checkinteger(L, 3);
else if (i == 20 || (str && fastcmp(str,"mass")))
info->mass = (INT32)luaL_checkinteger(L, 3);
else if (i == 21 || (str && fastcmp(str,"damage")))
info->damage = (INT32)luaL_checkinteger(L, 3);
else if (i == 22 || (str && fastcmp(str,"activesound")))
info->activesound = luaL_checkinteger(L, 3);
else if (i == 23 || (str && fastcmp(str,"flags")))
info->flags = (INT32)luaL_checkinteger(L, 3);
else if (i == 24 || (str && fastcmp(str,"raisestate"))) {
info->raisestate = luaL_checkinteger(L, 3);
}
lua_pop(L, 1);
}
return 0;
}
// #mobjinfo -> NUMMOBJTYPES
FUNCINLINE static ATTRINLINE int lib_mobjinfolen(lua_State *L)
{
lua_pushinteger(L, NUMMOBJTYPES);
return 1;
}
enum mobjinfo_e
{
mobjinfo_doomednum,
mobjinfo_spawnstate,
mobjinfo_spawnhealth,
mobjinfo_seestate,
mobjinfo_seesound,
mobjinfo_reactiontime,
mobjinfo_attacksound,
mobjinfo_painstate,
mobjinfo_painchance,
mobjinfo_painsound,
mobjinfo_meleestate,
mobjinfo_missilestate,
mobjinfo_deathstate,
mobjinfo_xdeathstate,
mobjinfo_deathsound,
mobjinfo_speed,
mobjinfo_radius,
mobjinfo_height,
mobjinfo_dispoffset,
mobjinfo_mass,
mobjinfo_damage,
mobjinfo_activesound,
mobjinfo_flags,
mobjinfo_raisestate,
};
const char *const mobjinfo_opt[] = {
"doomednum",
"spawnstate",
"spawnhealth",
"seestate",
"seesound",
"reactiontime",
"attacksound",
"painstate",
"painchance",
"painsound",
"meleestate",
"missilestate",
"deathstate",
"xdeathstate",
"deathsound",
"speed",
"radius",
"height",
"dispoffset",
"mass",
"damage",
"activesound",
"flags",
"raisestate",
NULL,
};
static int mobjinfo_fields_ref = LUA_NOREF;
// mobjinfo_t *, field -> number
static int mobjinfo_get(lua_State *L)
{
mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
enum mobjinfo_e field = Lua_optoption(L, 2, -1, mobjinfo_fields_ref);
I_Assert(info != NULL);
I_Assert(info >= mobjinfo);
switch (field)
{
case mobjinfo_doomednum:
lua_pushinteger(L, info->doomednum);
break;
case mobjinfo_spawnstate:
lua_pushinteger(L, info->spawnstate);
break;
case mobjinfo_spawnhealth:
lua_pushinteger(L, info->spawnhealth);
break;
case mobjinfo_seestate:
lua_pushinteger(L, info->seestate);
break;
case mobjinfo_seesound:
lua_pushinteger(L, info->seesound);
break;
case mobjinfo_reactiontime:
lua_pushinteger(L, info->reactiontime);
break;
case mobjinfo_attacksound:
lua_pushinteger(L, info->attacksound);
break;
case mobjinfo_painstate:
lua_pushinteger(L, info->painstate);
break;
case mobjinfo_painchance:
lua_pushinteger(L, info->painchance);
break;
case mobjinfo_painsound:
lua_pushinteger(L, info->painsound);
break;
case mobjinfo_meleestate:
lua_pushinteger(L, info->meleestate);
break;
case mobjinfo_missilestate:
lua_pushinteger(L, info->missilestate);
break;
case mobjinfo_deathstate:
lua_pushinteger(L, info->deathstate);
break;
case mobjinfo_xdeathstate:
lua_pushinteger(L, info->xdeathstate);
break;
case mobjinfo_deathsound:
lua_pushinteger(L, info->deathsound);
break;
case mobjinfo_speed:
lua_pushinteger(L, info->speed); // sometimes it's fixed_t, sometimes it's not...
break;
case mobjinfo_radius:
lua_pushfixed(L, info->radius);
break;
case mobjinfo_height:
lua_pushfixed(L, info->height);
break;
case mobjinfo_dispoffset:
lua_pushinteger(L, info->dispoffset);
break;
case mobjinfo_mass:
lua_pushinteger(L, info->mass);
break;
case mobjinfo_damage:
lua_pushinteger(L, info->damage);
break;
case mobjinfo_activesound:
lua_pushinteger(L, info->activesound);
break;
case mobjinfo_flags:
lua_pushinteger(L, info->flags);
break;
case mobjinfo_raisestate:
lua_pushinteger(L, info->raisestate);
break;
default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, info);
lua_rawget(L, -2);
if (!lua_istable(L, -1)) { // no extra values table
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", lua_tostring(L, 2));
return 0;
}
lua_pushvalue(L, 2); // field name
lua_gettable(L, -2);
if (lua_isnil(L, -1)) // no value for this field
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", lua_tostring(L, 2));
break;
}
return 1;
}
// mobjinfo_t *, field, number -> mobjinfo[]
static int mobjinfo_set(lua_State *L)
{
mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
enum mobjinfo_e field = Lua_optoption(L, 2, -1, mobjinfo_fields_ref);
if (hud_running)
return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter mobjinfo in BuildCMD code!");
I_Assert(info != NULL);
I_Assert(info >= mobjinfo);
switch (field)
{
case mobjinfo_doomednum:
info->doomednum = (INT32)luaL_checkinteger(L, 3);
break;
case mobjinfo_spawnstate:
info->spawnstate = luaL_checkinteger(L, 3);
break;
case mobjinfo_spawnhealth:
info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
break;
case mobjinfo_seestate:
info->seestate = luaL_checkinteger(L, 3);
break;
case mobjinfo_seesound:
info->seesound = luaL_checkinteger(L, 3);
break;
case mobjinfo_reactiontime:
info->reactiontime = (INT32)luaL_checkinteger(L, 3);
break;
case mobjinfo_attacksound:
info->attacksound = luaL_checkinteger(L, 3);
break;
case mobjinfo_painstate:
info->painstate = luaL_checkinteger(L, 3);
break;
case mobjinfo_painchance:
info->painchance = (INT32)luaL_checkinteger(L, 3);
break;
case mobjinfo_painsound:
info->painsound = luaL_checkinteger(L, 3);
break;
case mobjinfo_meleestate:
info->meleestate = luaL_checkinteger(L, 3);
break;
case mobjinfo_missilestate:
info->missilestate = luaL_checkinteger(L, 3);
break;
case mobjinfo_deathstate:
info->deathstate = luaL_checkinteger(L, 3);
break;
case mobjinfo_xdeathstate:
info->xdeathstate = luaL_checkinteger(L, 3);
break;
case mobjinfo_deathsound:
info->deathsound = luaL_checkinteger(L, 3);
break;
case mobjinfo_speed:
info->speed = luaL_checkfixed(L, 3);
break;
case mobjinfo_radius:
info->radius = luaL_checkfixed(L, 3);
break;
case mobjinfo_height:
info->height = luaL_checkfixed(L, 3);
break;
case mobjinfo_dispoffset:
info->dispoffset = (INT32)luaL_checkinteger(L, 3);
break;
case mobjinfo_mass:
info->mass = (INT32)luaL_checkinteger(L, 3);
break;
case mobjinfo_damage:
info->damage = (INT32)luaL_checkinteger(L, 3);
break;
case mobjinfo_activesound:
info->activesound = luaL_checkinteger(L, 3);
break;
case mobjinfo_flags:
info->flags = (INT32)luaL_checkinteger(L, 3);
break;
case mobjinfo_raisestate:
info->raisestate = luaL_checkinteger(L, 3);
break;
default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, info);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) {
// This index doesn't have a table for extra values yet, let's make one.
lua_pop(L, 1);
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobjinfo_t", lua_tostring(L, 2));
lua_newtable(L);
lua_pushlightuserdata(L, info);
lua_pushvalue(L, -2); // ext value table
lua_rawset(L, -4); // LREG_EXTVARS table
}
lua_pushvalue(L, 2); // key
lua_pushvalue(L, 3); // value to store
lua_settable(L, -3);
lua_pop(L, 2);
}
return 0;
}
// mobjinfo_t * -> MT_*
FUNCINLINE static ATTRINLINE int mobjinfo_num(lua_State *L)
{
mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
I_Assert(info != NULL);
I_Assert(info >= mobjinfo);
lua_pushinteger(L, info-mobjinfo);
return 1;
}
//////////////
// SFX INFO //
//////////////
// Arbitrary S_sfx[] table index -> sfxinfo_t *
FUNCINLINE static ATTRINLINE int lib_getSfxInfo(lua_State *L)
{
UINT32 i;
lua_remove(L, 1);
i = luaL_checkinteger(L, 1);
if (i >= NUMSFX)
return luaL_error(L, "sfxinfo[] index %d out of range (0 - %d)", i, NUMSFX-1);
LUA_PushUserdata(L, &S_sfx[i], META_SFXINFO);
return 1;
}
enum sfxinfo_e {
sfxinfo_name = 0,
sfxinfo_singular,
sfxinfo_priority,
sfxinfo_flags, // "pitch"
sfxinfo_skinsound
};
const char *const sfxinfo_opt[] = {
"name",
"singular",
"priority",
"flags",
"skinsound",
NULL
};
static int sfxinfo_fields_ref = LUA_NOREF;
// stack: dummy, S_sfx[] table index, table of values to set.
static int lib_setSfxInfo(lua_State *L)
{
sfxinfo_t *info;
lua_remove(L, 1);
{
UINT32 i = luaL_checkinteger(L, 1);
if (i >= NUMSFX)
return luaL_error(L, "sfxinfo[] index %d out of range (0 - %d)", i, NUMSFX-1);
info = &S_sfx[i]; // get the mobjinfo to assign to.
}
luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
lua_remove(L, 1); // pop mobjtype num, don't need it any more.
lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the mobjinfo.
if (hud_running)
return luaL_error(L, "Do not alter sfxinfo in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter sfxinfo in BuildCMD code!");
lua_pushnil(L);
while (lua_next(L, 1)) {
enum sfxinfo_e i;
if (lua_isnumber(L, 2))
{
int j = lua_tointeger(L, 2);
// Read and Write enums were combined, need to do this switch now
switch (j)
{
case 1:
i = sfxinfo_singular;
break;
case 2:
i = sfxinfo_priority;
break;
case 3:
i = sfxinfo_flags;
break;
default:
i = -1;
break;
}
}
else
i = Lua_optoption(L, 2, -1, sfxinfo_fields_ref);
switch(i)
{
case sfxinfo_singular:
info->singularity = luaL_checkboolean(L, 3);
break;
case sfxinfo_priority:
info->priority = (INT32)luaL_checkinteger(L, 3);
break;
case sfxinfo_flags:
info->pitch = (INT32)luaL_checkinteger(L, 3);
break;
default:
break;
}
lua_pop(L, 1);
}
return 0;
}
FUNCINLINE static ATTRINLINE int lib_sfxlen(lua_State *L)
{
lua_pushinteger(L, NUMSFX);
return 1;
}
// sfxinfo_t *, field
static int sfxinfo_get(lua_State *L)
{
sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
enum sfxinfo_e field = Lua_optoption(L, 2, -1, sfxinfo_fields_ref);
I_Assert(sfx != NULL);
switch (field)
{
case sfxinfo_name:
lua_pushstring(L, sfx->name);
return 1;
case sfxinfo_singular:
lua_pushboolean(L, sfx->singularity);
return 1;
case sfxinfo_priority:
lua_pushinteger(L, sfx->priority);
return 1;
case sfxinfo_flags:
lua_pushinteger(L, sfx->pitch);
return 1;
case sfxinfo_skinsound:
lua_pushinteger(L, sfx->skinsound);
return 1;
default:
return luaL_error(L, LUA_QL("sfxinfo_t") " has no field named " LUA_QS, lua_tostring(L, 2));
}
return 0;
}
// sfxinfo_t *, field, value
static int sfxinfo_set(lua_State *L)
{
sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
int field = Lua_optoption(L, 2, -1, sfxinfo_fields_ref);
if (hud_running)
return luaL_error(L, "Do not alter S_sfx in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter S_sfx in BuildCMD code!");
I_Assert(sfx != NULL);
lua_remove(L, 1); // remove sfxinfo
lua_remove(L, 1); // remove field
lua_settop(L, 1); // leave only one value
switch (field)
{
case sfxinfo_singular:
sfx->singularity = luaL_checkboolean(L, 1);
break;
case sfxinfo_priority:
sfx->priority = luaL_checkinteger(L, 1);
break;
case sfxinfo_flags:
sfx->pitch = luaL_checkinteger(L, 1);
break;
default:
if (field == -1)
return luaL_error(L, LUA_QL("sfxinfo_t") " has no field named " LUA_QS, lua_tostring(L, 2));
else
return luaL_error(L, LUA_QL("sfxinfo_t") " field " LUA_QS " is read-only", lua_tostring(L, 2));
}
return 0;
}
FUNCINLINE static ATTRINLINE int sfxinfo_num(lua_State *L)
{
sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
I_Assert(sfx != NULL);
I_Assert(sfx >= S_sfx);
lua_pushinteger(L, (UINT32)(sfx-S_sfx));
return 1;
}
//////////////////////////////
//
// Now push all these functions into the Lua state!
//
//
int LUA_InfoLib(lua_State *L)
{
// index of A_Lua actions to run for each state
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, LREG_STATEACTION);
// index of globally available Lua actions by function name
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS);
luaL_newmetatable(L, META_STATE);
lua_pushcfunction(L, state_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, state_set);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, state_num);
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
state_fields_ref = Lua_CreateFieldTable(L, state_opt);
luaL_newmetatable(L, META_MOBJINFO);
lua_pushcfunction(L, mobjinfo_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, mobjinfo_set);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, mobjinfo_num);
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
mobjinfo_fields_ref = Lua_CreateFieldTable(L, mobjinfo_opt);
luaL_newmetatable(L, META_SFXINFO);
lua_pushcfunction(L, sfxinfo_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, sfxinfo_set);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, sfxinfo_num);
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
sfxinfo_fields_ref = Lua_CreateFieldTable(L, sfxinfo_opt);
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getSprname);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_sprnamelen);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "sprnames");
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getState);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_setState);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, lib_statelen);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "states");
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getMobjInfo);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_setMobjInfo);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, lib_mobjinfolen);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "mobjinfo");
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getSfxInfo);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_setSfxInfo);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, lib_sfxlen);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
lua_setglobal(L, "S_sfx");
lua_setglobal(L, "sfxinfo");
return 0;
}