diff --git a/src/d_think.h b/src/d_think.h index ad16e4ed8a250edfa3bfe004953896246d7ef20a..330747e4b8c9ca1a47fa1a4fbc4ed8d6765da7f8 100644 --- a/src/d_think.h +++ b/src/d_think.h @@ -35,10 +35,7 @@ enum typedef struct { unsigned length; - union { - char *chars; - const char *const_chars; - }; + char *chars; } action_string_t; typedef struct diff --git a/src/deh_lua.c b/src/deh_lua.c index 913727df207a8fd4c00088f5c02e2fab4e53585c..257d8ae0ad5a131a01ac747c0389712211ba1d79 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -188,6 +188,8 @@ static int action_call(lua_State *L) { actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION)); mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + if (!actor) + return LUA_ErrInvalid(L, "mobj_t"); action_val_t *call_args = NULL; @@ -195,19 +197,27 @@ static int action_call(lua_State *L) int num_action_args = n - 2; if (num_action_args > 0) { - call_args = Z_Malloc(num_action_args * sizeof(action_val_t), PU_STATIC, NULL); + call_args = Z_Calloc(num_action_args * sizeof(action_val_t), PU_STATIC, NULL); + for (int i = 3, j = 0; i <= n; i++) - LUA_ValueToActionVal(L, i, &call_args[j++]); - } + { + if (!LUA_ValueIsValidActionVal(L, i)) + { + for (int k = 0; k < j; k++) + Action_FreeValue(call_args[k]); + Z_Free(call_args); + return luaL_error(L, va("value of type %s cannot be passed to an action", luaL_typename(L, i))); + } - if (!actor) - { - Z_Free(call_args); - return LUA_ErrInvalid(L, "mobj_t"); + LUA_ValueToActionVal(L, i, &call_args[j++]); + } } action->acpscr(actor, call_args, num_action_args); + for (int i = 0; i < num_action_args; i++) + Action_FreeValue(call_args[i]); + Z_Free(call_args); return 0; diff --git a/src/deh_soc.c b/src/deh_soc.c index eb68e956c78aac8eb10f6c536c117bf290a9749b..ee339763e4f91eb90c5d7b94f4b744085191d6c3 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2758,14 +2758,6 @@ void readframe(MYFILE *f, INT32 num) { states[num].nextstate = get_state(word2); } - else if (fastcmp(word1, "VAR1")) - { - states[num].vars[0] = ACTION_INTEGER_VAL((INT32)get_number(word2)); - } - else if (fastcmp(word1, "VAR2")) - { - states[num].vars[1] = ACTION_INTEGER_VAL((INT32)get_number(word2)); - } else if (fastcmp(word1, "ACTION")) { size_t z; @@ -2812,6 +2804,14 @@ void readframe(MYFILE *f, INT32 num) free(actiontocompare); } + else if (!strnicmp(word1, "VAR", 3) + && strlen(word1) == 4 + && word1[3] >= '1' && word1[3] <= '8') + { + unsigned varSlot = (word1[3] - 0x30) - 1; + Action_FreeValue(states[num].vars[varSlot]); + states[num].vars[varSlot] = ACTION_INTEGER_VAL((INT32)get_number(word2)); + } else deh_warning("Frame %d: unknown word '%s'", num, word1); } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 16f238abeb19575cb5ce0e626be4660e723727ca..255b35dd9870bb290039c5e37eb7949573ac2af2 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -151,6 +151,7 @@ static const struct { const char *utype; } meta2utype[] = { {META_STATE, "state_t"}, + {META_STATEVARS, "state_t.vars"}, {META_MOBJINFO, "mobjinfo_t"}, {META_SFXINFO, "sfxinfo_t"}, {META_SKINCOLOR, "skincolor_t"}, diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 9f5c33563e00c3f8791a26abddd8b5794058623e..ec77c62eb655bd6adc17fe3dec2646e397343980 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -35,6 +35,95 @@ extern UINT8 skincolor_modified[]; state_t *astate; +boolean LUA_ValueIsValidActionVal(lua_State *L, int i) +{ + switch (lua_type(L, i)) + { + case LUA_TNUMBER: + case LUA_TBOOLEAN: + case LUA_TSTRING: + case LUA_TNIL: + return true; + } + + return false; +} + +#define CHECK_ACTION_VAL_TYPE(n) \ + if (!LUA_ValueIsValidActionVal(L, n)) \ + return luaL_error(L, va("values of type %s cannot be passed to actions as an argument", luaL_typename(L, n))) + +void LUA_ValueToActionVal(lua_State *L, int i, action_val_t *val) +{ + action_val_t value; + + switch (lua_type(L, i)) + { + case LUA_TNUMBER: + value = ACTION_INTEGER_VAL((INT32)luaL_optinteger(L, i, 0)); + break; + case LUA_TBOOLEAN: + value = ACTION_BOOLEAN_VAL(lua_toboolean(L, i)); + break; + case LUA_TSTRING: + { + action_string_t stringval; + Action_MakeString(&stringval, Z_StrDup(lua_tostring(L, i))); + value = ACTION_STRING_VAL(stringval); + break; + } + default: + value = ACTION_NULL_VAL; + break; + } + + memcpy(val, &value, sizeof(action_val_t)); +} + +#define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to states[] (%s)", e) +#define TYPEERROR(f, t1, t2) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t1), lua_typename(L, t2))) + +static boolean GetActionValuesFromTable(lua_State *L, action_val_t *vars, int n) +{ + lua_Integer i = 0; + if (lua_istable(L, n)) + { + lua_pushnil(L); + while (lua_next(L, n)) + { + if (lua_isnumber(L, n + 1)) + { + i = lua_tointeger(L, n + 1); + if (i < 1 || i > MAX_ACTION_VARS) + { + FIELDERROR("vars", va("var %d is invalid", i)); + return false; + } + i--; + } + else + { + FIELDERROR("vars", "vars table requires keys to be numbers"); + } + + CHECK_ACTION_VAL_TYPE(n + 2); + Action_FreeValue(vars[i]); + LUA_ValueToActionVal(L, n + 2, &vars[i]); + lua_pop(L, 1); + } + } + else + { + FIELDERROR("vars", va("%s expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1))); + return false; + } + + return true; +} + +#undef FIELDERROR +#undef TYPEERROR + enum sfxinfo_read { sfxinfor_name = 0, sfxinfor_singular, @@ -776,14 +865,21 @@ static int lib_setState(lua_State *L) return luaL_typerror(L, 3, "function"); } } else if (i == 5 || (str && fastcmp(str, "var1"))) { + CHECK_ACTION_VAL_TYPE(3); + Action_FreeValue(state->vars[0]); LUA_ValueToActionVal(L, 3, &state->vars[0]); } else if (i == 6 || (str && fastcmp(str, "var2"))) { + CHECK_ACTION_VAL_TYPE(3); + Action_FreeValue(state->vars[1]); LUA_ValueToActionVal(L, 3, &state->vars[1]); } 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; + } else if (str && fastcmp(str, "vars")) { + if (!GetActionValuesFromTable(L, state->vars, 3)) + return 0; } lua_pop(L, 1); } @@ -834,33 +930,6 @@ boolean LUA_SetLuaAction(void *stv, const char *action) static UINT8 superstack[NUMACTIONS]; -void LUA_ValueToActionVal(lua_State *L, int i, action_val_t *val) -{ - action_val_t value; - - switch (lua_type(L, i)) - { - case LUA_TNUMBER: - value = ACTION_INTEGER_VAL((INT32)luaL_optinteger(L, i, 0)); - break; - case LUA_TBOOLEAN: - value = ACTION_BOOLEAN_VAL(lua_toboolean(L, i)); - break; - case LUA_TSTRING: - { - action_string_t stringval; - Action_MakeString(&stringval, lua_tostring(L, i)); - value = ACTION_STRING_VAL(stringval); - break; - } - default: - value = ACTION_NULL_VAL; - break; - } - - memcpy(val, &value, sizeof(action_val_t)); -} - boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor, action_val_t *args, unsigned argcount) { I_Assert(actor != NULL); @@ -987,6 +1056,10 @@ static int state_get(lua_State *L) PushActionValue(L, st->vars[1]); return 1; } + else if (fastcmp(field,"vars")) { + LUA_PushUserdata(L, st->vars, META_STATEVARS); + return 1; + } else if (fastcmp(field,"nextstate")) number = st->nextstate; else if (devparm) @@ -1023,7 +1096,7 @@ static int state_set(lua_State *L) switch(lua_type(L, 3)) { case LUA_TNIL: // Null? Set the action to nothing, then. - st->action.acp1 = NULL; + st->action.acpscr = NULL; break; case LUA_TSTRING: // It's a string, expect the name of a built-in action LUA_SetActionByName(st, lua_tostring(L, 3)); @@ -1036,8 +1109,6 @@ static int state_set(lua_State *L) return luaL_error(L, "not a valid action?"); st->action = *action; - st->action.acv = action->acv; - st->action.acp1 = action->acp1; break; } case LUA_TFUNCTION: // It's a function (a Lua function or a C function? either way!) @@ -1049,13 +1120,23 @@ static int state_set(lua_State *L) lua_pop(L, 1); // pop LREG_STATEACTION st->action.acpscr = (actionf_script)A_Lua; // Set the action for the userdata. break; - default: // ?! + default: // Something else return luaL_typerror(L, 3, "function"); } - } else if (fastcmp(field,"var1")) + } else if (fastcmp(field,"var1")) { + CHECK_ACTION_VAL_TYPE(3); + Action_FreeValue(st->vars[0]); LUA_ValueToActionVal(L, 3, &st->vars[0]); - else if (fastcmp(field,"var2")) + } + else if (fastcmp(field,"var2")) { + CHECK_ACTION_VAL_TYPE(3); + Action_FreeValue(st->vars[1]); LUA_ValueToActionVal(L, 3, &st->vars[1]); + } + else if (fastcmp(field,"vars")) { + if (!GetActionValuesFromTable(L, st->vars, 3)) + return 0; + } else if (fastcmp(field,"nextstate")) { value = luaL_checkinteger(L, 3); if (value < S_NULL || value >= NUMSTATES) @@ -1075,6 +1156,34 @@ static int state_num(lua_State *L) return 1; } +static int statevars_get(lua_State *L) +{ + action_val_t *vars = *((action_val_t **)luaL_checkudata(L, 1, META_STATEVARS)); + int n = luaL_checkinteger(L, 2); + if (n <= 0 || n > MAX_ACTION_VARS) + return luaL_error(L, LUA_QL("state_t") " field 'vars' index %d out of range (1 - %d)", n, MAX_ACTION_VARS); + PushActionValue(L, vars[n-1]); + return 1; +} + +static int statevars_set(lua_State *L) +{ + action_val_t *vars = *((action_val_t **)luaL_checkudata(L, 1, META_STATEVARS)); + int n = luaL_checkinteger(L, 2); + if (n <= 0 || n > MAX_ACTION_VARS) + return luaL_error(L, LUA_QL("state_t") " field 'vars' index %d out of range (1 - %d)", n, MAX_ACTION_VARS); + n--; + Action_FreeValue(vars[n]); + LUA_ValueToActionVal(L, 3, &vars[n]); + return 0; +} + +static int statevars_len(lua_State *L) +{ + lua_pushinteger(L, MAX_ACTION_VARS); + return 1; +} + /////////////// // MOBJ INFO // /////////////// @@ -1969,6 +2078,7 @@ int LUA_InfoLib(lua_State *L) lua_setfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS); LUA_RegisterUserdataMetatable(L, META_STATE, state_get, state_set, state_num); + LUA_RegisterUserdataMetatable(L, META_STATEVARS, statevars_get, statevars_set, statevars_len); LUA_RegisterUserdataMetatable(L, META_MOBJINFO, mobjinfo_get, mobjinfo_set, mobjinfo_num); LUA_RegisterUserdataMetatable(L, META_SKINCOLOR, skincolor_get, skincolor_set, skincolor_num); LUA_RegisterUserdataMetatable(L, META_COLORRAMP, colorramp_get, colorramp_set, colorramp_len); diff --git a/src/lua_libs.h b/src/lua_libs.h index 2e3c706528f24dd127f77b0e1256463643c59654..e5305328a6605149e83d44b722c6678c6ddc4f8f 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -24,6 +24,7 @@ extern boolean ignoregameinputs; #define LREG_METATABLES "METATABLES" #define META_STATE "STATE_T*" +#define META_STATEVARS "STATE_T*VARS" #define META_MOBJINFO "MOBJINFO_T*" #define META_SFXINFO "SFXINFO_T*" #define META_SKINCOLOR "SKINCOLOR_T*" diff --git a/src/lua_script.h b/src/lua_script.h index 1074fcbc09f55309bb60692ffa7450dee3f6fcff..23563e09687fb20c693d2c0131420c61f376f5c9 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -47,6 +47,7 @@ extern INT32 lua_lumploading; // is LUA_LoadLump being called? int LUA_GetErrorMessage(lua_State *L); int LUA_Call(lua_State *L, int nargs, int nresults, int errorhandlerindex); boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor, action_val_t *args, unsigned argcount); +boolean LUA_ValueIsValidActionVal(lua_State *L, int i); void LUA_ValueToActionVal(lua_State *L, int i, action_val_t *val); void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults); #ifdef LUA_ALLOW_BYTECODE diff --git a/src/p_action.h b/src/p_action.h index 9a23cab0d2ebf2fd2b8b24823f391e4eca1759bd..fcf95f7fdac8ac4f62e5e649efee319965387de1 100644 --- a/src/p_action.h +++ b/src/p_action.h @@ -20,7 +20,10 @@ INT32 Action_ValueToInteger(action_val_t value); char *Action_ValueToString(action_val_t value); -void Action_MakeString(action_string_t *out, const char *str); +void Action_FreeValue(action_val_t value); + +void Action_MakeString(action_string_t *out, char *str); +void Action_FreeStringChars(action_string_t *str); // IMPORTANT NOTE: If you add/remove from this list of action // functions, don't forget to update them in deh_tables.c! diff --git a/src/p_enemy.c b/src/p_enemy.c index 95d3baabed9b8d6cd9821fe796b106aa51560c7a..28cfaac59dd9361b94acb5468666041683d1aef9 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -700,12 +700,24 @@ for (i = cvar.value; i; --i) spawnchance[numchoices++] = type // // ACTION ROUTINES // -void Action_MakeString(action_string_t *out, const char *str) +void Action_FreeValue(action_val_t value) { - out->const_chars = str; + if (ACTION_VAL_IS_STRING(value)) + Action_FreeStringChars(&value.v_string); +} + +void Action_MakeString(action_string_t *out, char *str) +{ + out->chars = str; out->length = strlen(str); } +void Action_FreeStringChars(action_string_t *str) +{ + Z_Free(str->chars); + str->chars = NULL; +} + static const char *Action_GetTypeName(UINT8 type) { switch (type)