Merge branch 'action-super-fix' into 'next'

Make A_Action super act as expected

Closes #718

See merge request STJr/SRB2!1737
This commit is contained in:
sphere 2023-07-19 19:40:42 +00:00
commit 5c87dd52ec
6 changed files with 119 additions and 41 deletions

View file

@ -187,25 +187,31 @@ static inline int lib_freeslot(lua_State *L)
// Arguments: mobj_t actor, int var1, int var2 // Arguments: mobj_t actor, int var1, int var2
static int action_call(lua_State *L) static int action_call(lua_State *L)
{ {
//actionf_t *action = lua_touserdata(L,lua_upvalueindex(1));
actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION)); actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION));
mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
var1 = (INT32)luaL_optinteger(L, 3, 0); var1 = (INT32)luaL_optinteger(L, 3, 0);
var2 = (INT32)luaL_optinteger(L, 4, 0); var2 = (INT32)luaL_optinteger(L, 4, 0);
if (!actor) if (!actor)
{
return LUA_ErrInvalid(L, "mobj_t"); return LUA_ErrInvalid(L, "mobj_t");
}
action->acp1(actor); action->acp1(actor);
return 0; return 0;
} }
// Hardcoded A_Action name to call for super() or NULL if super() would be invalid. // Hardcoded A_Action name to call for super() or NULL if super() would be invalid.
// Set in lua_infolib. // Set in lua_infolib.
const char *superactions[MAXRECURSION]; const char *luaactions[MAX_ACTION_RECURSION];
UINT8 superstack = 0; UINT8 luaactionstack = 0;
static int lib_dummysuper(lua_State *L) static int lib_dummysuper(lua_State *L)
{ {
return luaL_error(L, "Can't call super() outside of hardcode-replacing A_Action functions being called by state changes!"); // convoluted, I know. @_@;; // TODO: Now that the restriction on only being allowed in state changes was lifted,
// it'd be nice to have super extend to Lua A_ functions too :)
return luaL_error(L, "Can't call super() outside of hardcode-replacing A_Action functions!");
} }
static void CacheAndPushConstant(lua_State *L, const char *name, lua_Integer value) static void CacheAndPushConstant(lua_State *L, const char *name, lua_Integer value)
@ -606,42 +612,62 @@ static inline int lib_getenum(lua_State *L)
if (!mathlib && fastncmp("A_",word,2)) { if (!mathlib && fastncmp("A_",word,2)) {
char *caps; char *caps;
// Try to get a Lua action first.
/// \todo Push a closure that sets superactions[] and superstack. // Hardcoded actions come first.
// Trying to call them will invoke LUA_CallAction, which will handle super properly.
// Retrieving them from this metatable allows them to be case-insensitive!
for (i = 0; actionpointers[i].name; i++)
{
if (fasticmp(word, actionpointers[i].name))
{
// We push the actionf_t* itself as userdata!
LUA_PushUserdata(L, &actionpointers[i].action, META_ACTION);
return 1;
}
}
// Now try to get Lua actions.
/// \todo Push a closure that sets luaactions[] and luaactionstack.
/// This would be part one of a step to get super functions working for custom A_ functions.
/// Custom functions.
lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS); lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS);
// actions are stored in all uppercase. // actions are stored in all uppercase.
caps = Z_StrDup(word); caps = Z_StrDup(word);
strupr(caps); strupr(caps);
lua_getfield(L, -1, caps); lua_getfield(L, -1, caps);
Z_Free(caps); Z_Free(caps);
if (!lua_isnil(L, -1)) if (!lua_isnil(L, -1))
{
return 1; // Success! :D That was easy. return 1; // Success! :D That was easy.
}
// Welp, that failed. // Welp, that failed.
lua_pop(L, 2); // pop nil and LREG_ACTIONS lua_pop(L, 2); // pop nil and LREG_ACTIONS
// Hardcoded actions as callable Lua functions!
// Retrieving them from this metatable allows them to be case-insensitive!
for (i = 0; actionpointers[i].name; i++)
if (fasticmp(word, actionpointers[i].name)) {
// We push the actionf_t* itself as userdata!
LUA_PushUserdata(L, &actionpointers[i].action, META_ACTION);
return 1;
}
return 0; return 0;
} }
else if (!mathlib && fastcmp("super",word)) else if (!mathlib && fastcmp("super",word))
{ {
if (!superstack) if (!luaactionstack)
{ {
// Not in A_ action routine
lua_pushcfunction(L, lib_dummysuper); lua_pushcfunction(L, lib_dummysuper);
return 1; return 1;
} }
for (i = 0; actionpointers[i].name; i++) for (i = 0; actionpointers[i].name; i++)
if (fasticmp(superactions[superstack-1], actionpointers[i].name)) { {
if (fasticmp(luaactions[luaactionstack-1], actionpointers[i].name))
{
LUA_PushUserdata(L, &actionpointers[i].action, META_ACTION); LUA_PushUserdata(L, &actionpointers[i].action, META_ACTION);
return 1; return 1;
} }
return 0; }
// Not a hardcoded A_ action.
lua_pushcfunction(L, lib_dummysuper);
return 1;
} }
else if ((!mathlib && LUA_PushGlobals(L, word)) || ScanConstants(L, mathlib, word)) else if ((!mathlib && LUA_PushGlobals(L, word)) || ScanConstants(L, mathlib, word))
return 1; return 1;

View file

@ -30,6 +30,7 @@ extern UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite
memset(FREE_MOBJS,0,sizeof(char *) * NUMMOBJFREESLOTS);\ memset(FREE_MOBJS,0,sizeof(char *) * NUMMOBJFREESLOTS);\
memset(FREE_SKINCOLORS,0,sizeof(char *) * NUMCOLORFREESLOTS);\ memset(FREE_SKINCOLORS,0,sizeof(char *) * NUMCOLORFREESLOTS);\
memset(used_spr,0,sizeof(UINT8) * ((NUMSPRITEFREESLOTS / 8) + 1));\ memset(used_spr,0,sizeof(UINT8) * ((NUMSPRITEFREESLOTS / 8) + 1));\
memset(actionsoverridden, LUA_REFNIL, sizeof(actionsoverridden));\
} }
struct flickytypes_s { struct flickytypes_s {

View file

@ -40,9 +40,9 @@ extern boolean gamedataadded;
extern boolean titlechanged; extern boolean titlechanged;
extern boolean introchanged; extern boolean introchanged;
#define MAXRECURSION 30 #define MAX_ACTION_RECURSION 30
extern const char *superactions[MAXRECURSION]; extern const char *luaactions[MAX_ACTION_RECURSION];
extern UINT8 superstack; extern UINT8 luaactionstack;
// If the dehacked patch does not match this version, we throw a warning // If the dehacked patch does not match this version, we throw a warning
#define PATCHVERSION 220 #define PATCHVERSION 220

View file

@ -18,6 +18,7 @@
#include "d_think.h" #include "d_think.h"
#include "sounds.h" #include "sounds.h"
#include "m_fixed.h" #include "m_fixed.h"
#include "dehacked.h" // MAX_ACTION_RECURSION
// deh_tables.c now has lists for the more named enums! PLEASE keep them up to date! // deh_tables.c now has lists for the more named enums! PLEASE keep them up to date!
// For great modding!! // For great modding!!
@ -564,7 +565,7 @@ void A_DragonWing();
void A_DragonSegment(); void A_DragonSegment();
void A_ChangeHeight(); void A_ChangeHeight();
extern boolean actionsoverridden[NUMACTIONS]; extern int actionsoverridden[NUMACTIONS][MAX_ACTION_RECURSION];
// ratio of states to sprites to mobj types is roughly 6 : 1 : 1 // ratio of states to sprites to mobj types is roughly 6 : 1 : 1
#define NUMMOBJFREESLOTS 512 #define NUMMOBJFREESLOTS 512

View file

@ -66,7 +66,7 @@ const char *const sfxinfo_wopt[] = {
"caption", "caption",
NULL}; NULL};
boolean actionsoverridden[NUMACTIONS] = {false}; int actionsoverridden[NUMACTIONS][MAX_ACTION_RECURSION];
// //
// Sprite Names // Sprite Names
@ -645,8 +645,8 @@ static void A_Lua(mobj_t *actor)
if (lua_rawequal(gL, -1, -4)) if (lua_rawequal(gL, -1, -4))
{ {
found = true; found = true;
superactions[superstack] = lua_tostring(gL, -2); // "A_ACTION" luaactions[luaactionstack] = lua_tostring(gL, -2); // "A_ACTION"
++superstack; ++luaactionstack;
lua_pop(gL, 2); // pop the name and function lua_pop(gL, 2); // pop the name and function
break; break;
} }
@ -661,8 +661,8 @@ static void A_Lua(mobj_t *actor)
if (found) if (found)
{ {
--superstack; --luaactionstack;
superactions[superstack] = NULL; luaactions[luaactionstack] = NULL;
} }
} }
@ -812,22 +812,54 @@ boolean LUA_SetLuaAction(void *stv, const char *action)
return true; // action successfully set. return true; // action successfully set.
} }
static UINT8 superstack[NUMACTIONS];
boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor) boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
{ {
I_Assert(actor != NULL); I_Assert(actor != NULL);
if (!actionsoverridden[actionnum]) // The action is not overriden, if (actionsoverridden[actionnum][0] == LUA_REFNIL)
return false; // action not called. {
// The action was not overridden at all,
// so just call the hardcoded version.
return false;
}
if (superstack && fasticmp(actionpointers[actionnum].name, superactions[superstack-1])) // the action is calling itself, if (luaactionstack && fasticmp(actionpointers[actionnum].name, luaactions[luaactionstack-1]))
return false; // let it call the hardcoded function instead. {
// The action is calling itself,
// so look up the next Lua reference in its stack.
// 0 is just the reference to the one we're calling,
// so we increment here.
superstack[actionnum]++;
if (superstack[actionnum] >= MAX_ACTION_RECURSION)
{
CONS_Alert(CONS_WARNING, "Max Lua super recursion reached! Cool it on calling super!\n");
superstack[actionnum] = 0;
return false;
}
}
if (actionsoverridden[actionnum][superstack[actionnum]] == LUA_REFNIL)
{
// No Lua reference beyond this point.
// Let it call the hardcoded function instead.
if (superstack[actionnum])
{
// Decrement super stack
superstack[actionnum]--;
}
return false;
}
// Push error function
lua_pushcfunction(gL, LUA_GetErrorMessage); lua_pushcfunction(gL, LUA_GetErrorMessage);
// grab function by uppercase name. // Push function by reference.
lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS); lua_getref(gL, actionsoverridden[actionnum][superstack[actionnum]]);
lua_getfield(gL, -1, actionpointers[actionnum].name);
lua_remove(gL, -2); // pop LREG_ACTIONS
if (lua_isnil(gL, -1)) // no match if (lua_isnil(gL, -1)) // no match
{ {
@ -835,7 +867,7 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
return false; // action not called. return false; // action not called.
} }
if (superstack == MAXRECURSION) if (luaactionstack >= MAX_ACTION_RECURSION)
{ {
CONS_Alert(CONS_WARNING, "Max Lua Action recursion reached! Cool it on the calling A_Action functions from inside A_Action functions!\n"); 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 lua_pop(gL, 2); // pop function and error handler
@ -849,14 +881,20 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
lua_pushinteger(gL, var1); lua_pushinteger(gL, var1);
lua_pushinteger(gL, var2); lua_pushinteger(gL, var2);
superactions[superstack] = actionpointers[actionnum].name; luaactions[luaactionstack] = actionpointers[actionnum].name;
++superstack; ++luaactionstack;
LUA_Call(gL, 3, 0, -(2 + 3)); LUA_Call(gL, 3, 0, -(2 + 3));
lua_pop(gL, -1); // Error handler lua_pop(gL, -1); // Error handler
--superstack; if (superstack[actionnum])
superactions[superstack] = NULL; {
// Decrement super stack
superstack[actionnum]--;
}
--luaactionstack;
luaactions[luaactionstack] = NULL;
return true; // action successfully called. return true; // action successfully called.
} }

View file

@ -501,7 +501,19 @@ static int setglobals(lua_State *L)
actionnum = LUA_GetActionNumByName(name); actionnum = LUA_GetActionNumByName(name);
if (actionnum < NUMACTIONS) if (actionnum < NUMACTIONS)
actionsoverridden[actionnum] = true; {
int i;
for (i = MAX_ACTION_RECURSION-1; i > 0; i--)
{
// Move other references deeper.
actionsoverridden[actionnum][i] = actionsoverridden[actionnum][i - 1];
}
// Add the new reference.
lua_pushvalue(L, 2);
actionsoverridden[actionnum][0] = luaL_ref(L, LUA_REGISTRYINDEX);
}
Z_Free(name); Z_Free(name);
return 0; return 0;