// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2021 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 deh_lua.c /// \brief Lua SOC library #include "g_game.h" #include "s_sound.h" #include "z_zone.h" #include "m_menu.h" #include "m_misc.h" #include "p_local.h" #include "st_stuff.h" #include "fastcmp.h" #include "lua_script.h" #include "lua_libs.h" #include "dehacked.h" #include "deh_lua.h" #include "deh_tables.h" // freeslot takes a name (string only!) // and allocates it to the appropriate free slot. // Returns the slot number allocated for it or nil if failed. // ex. freeslot("MT_MYTHING","S_MYSTATE1","S_MYSTATE2") // TODO: Error checking! @.@; There's currently no way to know which ones failed and why! // static inline int lib_freeslot(lua_State *L) { int n = lua_gettop(L); int r = 0; // args returned char *s, *type,*word; if (!lua_lumploading) return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); while (n-- > 0) { s = Z_StrDup(luaL_checkstring(L,1)); type = strtok(s, "_"); if (type) strupr(type); else { Z_Free(s); return luaL_error(L, "Unknown enum type in '%s'\n", luaL_checkstring(L, 1)); } word = strtok(NULL, "\n"); if (word) strupr(word); else { Z_Free(s); return luaL_error(L, "Missing enum name in '%s'\n", luaL_checkstring(L, 1)); } if (fastcmp(type, "SFX")) { sfxenum_t sfx; strlwr(word); CONS_Printf("Sound sfx_%s allocated.\n",word); sfx = S_AddSoundFx(word, false, 0, false); if (sfx != sfx_None) { lua_pushinteger(L, sfx); r++; } else CONS_Alert(CONS_WARNING, "Ran out of free SFX slots!\n"); } else if (fastcmp(type, "SPR")) { char wad; spritenum_t j; lua_getfield(L, LUA_REGISTRYINDEX, "WAD"); wad = (char)lua_tointeger(L, -1); lua_pop(L, 1); for (j = SPR_FIRSTFREESLOT; j <= SPR_LASTFREESLOT; j++) { if (used_spr[(j-SPR_FIRSTFREESLOT)/8] & (1<<(j%8))) { if (!sprnames[j][4] && memcmp(sprnames[j],word,4)==0) sprnames[j][4] = wad; continue; // Already allocated, next. } // Found a free slot! CONS_Printf("Sprite SPR_%s allocated.\n",word); strncpy(sprnames[j],word,4); //sprnames[j][4] = 0; used_spr[(j-SPR_FIRSTFREESLOT)/8] |= 1<<(j%8); // Okay, this sprite slot has been named now. lua_pushinteger(L, j); r++; break; } if (j > SPR_LASTFREESLOT) CONS_Alert(CONS_WARNING, "Ran out of free sprite slots!\n"); } else if (fastcmp(type, "S")) { statenum_t i; for (i = 0; i < NUMSTATEFREESLOTS; i++) if (!FREE_STATES[i]) { CONS_Printf("State S_%s allocated.\n",word); FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); strcpy(FREE_STATES[i],word); lua_pushinteger(L, S_FIRSTFREESLOT + i); r++; break; } if (i == NUMSTATEFREESLOTS) CONS_Alert(CONS_WARNING, "Ran out of free State slots!\n"); } else if (fastcmp(type, "MT")) { mobjtype_t i; for (i = 0; i < NUMMOBJFREESLOTS; i++) if (!FREE_MOBJS[i]) { CONS_Printf("MobjType MT_%s allocated.\n",word); FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); strcpy(FREE_MOBJS[i],word); lua_pushinteger(L, MT_FIRSTFREESLOT + i); r++; break; } if (i == NUMMOBJFREESLOTS) CONS_Alert(CONS_WARNING, "Ran out of free MobjType slots!\n"); } else if (fastcmp(type, "SKINCOLOR")) { skincolornum_t i; for (i = 0; i < NUMCOLORFREESLOTS; i++) if (!FREE_SKINCOLORS[i]) { CONS_Printf("Skincolor SKINCOLOR_%s allocated.\n",word); FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); strcpy(FREE_SKINCOLORS[i],word); M_AddMenuColor(numskincolors++); lua_pushinteger(L, SKINCOLOR_FIRSTFREESLOT + i); r++; break; } if (i == NUMCOLORFREESLOTS) CONS_Alert(CONS_WARNING, "Ran out of free skincolor slots!\n"); } else if (fastcmp(type, "SPR2")) { // Search if we already have an SPR2 by that name... playersprite_t i; for (i = SPR2_FIRSTFREESLOT; i < free_spr2; i++) if (memcmp(spr2names[i],word,4) == 0) break; // We don't, so allocate a new one. if (i >= free_spr2) { if (free_spr2 < NUMPLAYERSPRITES) { CONS_Printf("Sprite SPR2_%s allocated.\n",word); strncpy(spr2names[free_spr2],word,4); spr2defaults[free_spr2] = 0; lua_pushinteger(L, free_spr2); r++; spr2names[free_spr2++][4] = 0; } else CONS_Alert(CONS_WARNING, "Ran out of free SPR2 slots!\n"); } } else if (fastcmp(type, "TOL")) { // Search if we already have a typeoflevel by that name... int i; for (i = 0; TYPEOFLEVEL[i].name; i++) if (fastcmp(word, TYPEOFLEVEL[i].name)) break; // We don't, so allocate a new one. if (TYPEOFLEVEL[i].name == NULL) { if (lastcustomtol == (UINT32)MAXTOL) // Unless you have way too many, since they're flags. CONS_Alert(CONS_WARNING, "Ran out of free typeoflevel slots!\n"); else { CONS_Printf("TypeOfLevel TOL_%s allocated.\n",word); G_AddTOL(lastcustomtol, word); lua_pushinteger(L, lastcustomtol); lastcustomtol <<= 1; r++; } } } Z_Free(s); lua_remove(L, 1); continue; } return r; } // Wrapper for ALL A_Action functions. // Arguments: mobj_t actor, int var1, int var2 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)); mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); var1 = (INT32)luaL_optinteger(L, 3, 0); var2 = (INT32)luaL_optinteger(L, 4, 0); if (!actor) return LUA_ErrInvalid(L, "mobj_t"); action->acp1(actor); return 0; } // Hardcoded A_Action name to call for super() or NULL if super() would be invalid. // Set in lua_infolib. const char *superactions[MAXRECURSION]; UINT8 superstack = 0; 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. @_@;; } static inline int lib_getenum(lua_State *L) { const char *word, *p; fixed_t i; boolean mathlib = lua_toboolean(L, lua_upvalueindex(1)); if (lua_type(L,2) != LUA_TSTRING) return 0; word = lua_tostring(L,2); if (strlen(word) == 1) { // Assume sprite frame if length 1. if (*word >= 'A' && *word <= '~') { lua_pushinteger(L, *word-'A'); return 1; } if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word); return 0; } else if (fastncmp("MF_", word, 3)) { p = word+3; for (i = 0; MOBJFLAG_LIST[i]; i++) if (fastcmp(p, MOBJFLAG_LIST[i])) { lua_pushinteger(L, ((lua_Integer)1< return action's string name static int lib_getActionName(lua_State *L) { if (lua_isuserdata(L, 1)) // arg 1 is built-in action, expect action userdata { actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION)); const char *name = NULL; if (!action) return luaL_error(L, "not a valid action?"); name = LUA_GetActionName(action); if (!name) // that can't be right? return luaL_error(L, "no name string could be found for this action"); lua_pushstring(L, name); return 1; } else if (lua_isfunction(L, 1)) // arg 1 is a function (either C or Lua) { lua_settop(L, 1); // set top of stack to 1 (removing any extra args, which there shouldn't be) // get the name for this action, if possible. lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS); lua_pushnil(L); // Lua stack at this point: // 1 ... -2 -1 // arg ... LREG_ACTIONS nil while (lua_next(L, -2)) { // Lua stack at this point: // 1 ... -3 -2 -1 // arg ... LREG_ACTIONS "A_ACTION" function if (lua_rawequal(L, -1, 1)) // is this the same as the arg? { // make sure the key (i.e. "A_ACTION") is a string first // (note: we don't use lua_isstring because it also returns true for numbers) if (lua_type(L, -2) == LUA_TSTRING) { lua_pushvalue(L, -2); // push "A_ACTION" string to top of stack return 1; } lua_pop(L, 2); // pop the name and function break; // probably should have succeeded but we didn't, so end the loop } lua_pop(L, 1); } lua_pop(L, 1); // pop LREG_ACTIONS return 0; // return nothing (don't error) } return luaL_typerror(L, 1, "action userdata or Lua function"); } int LUA_SOCLib(lua_State *L) { lua_register(L,"freeslot",lib_freeslot); lua_register(L,"getActionName",lib_getActionName); luaL_newmetatable(L, META_ACTION); lua_pushcfunction(L, action_call); lua_setfield(L, -2, "__call"); lua_pop(L, 1); return 0; } const char *LUA_GetActionName(void *action) { actionf_t *act = (actionf_t *)action; size_t z; for (z = 0; actionpointers[z].name; z++) { if (actionpointers[z].action.acv == act->acv) return actionpointers[z].name; } return NULL; } void LUA_SetActionByName(void *state, const char *actiontocompare) { state_t *st = (state_t *)state; size_t z; for (z = 0; actionpointers[z].name; z++) { if (fasticmp(actiontocompare, actionpointers[z].name)) { st->action = actionpointers[z].action; st->action.acv = actionpointers[z].action.acv; // assign st->action.acp1 = actionpointers[z].action.acp1; return; } } } enum actionnum LUA_GetActionNumByName(const char *actiontocompare) { size_t z; for (z = 0; actionpointers[z].name; z++) if (fasticmp(actiontocompare, actionpointers[z].name)) return z; return z; }