diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da820419e..59c0e81f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,6 +118,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 lua_thinkerlib.c lua_maplib.c lua_taglib.c + lua_terrainlib.c lua_polyobjlib.c lua_blockmaplib.c lua_hudlib.c diff --git a/src/lua_baselib.c b/src/lua_baselib.c index e3c070fb5..72021c1c3 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -235,6 +235,11 @@ static const struct { {META_SONICLOOPVARS, "sonicloopvars_t"}, {META_SONICLOOPCAMVARS, "sonicloopcamvars_t"}, + + {META_SPLASH, "t_splash_t"}, + {META_FOOTSTEP, "t_footstep_t"}, + {META_OVERLAY, "t_overlay_t"}, + {META_TERRAIN, "terrain_t"}, {NULL, NULL} }; diff --git a/src/lua_libs.h b/src/lua_libs.h index b1f5ea2e8..8613b2638 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -107,6 +107,11 @@ extern lua_State *gL; #define META_SONICLOOPVARS "SONICLOOPVARS_T*" #define META_SONICLOOPCAMVARS "SONICLOOPCAMVARS_T*" +#define META_SPLASH "T_SPLASH_T*" +#define META_FOOTSTEP "T_FOOTSTEP_T*" +#define META_OVERLAY "T_OVERLAY_T*" +#define META_TERRAIN "TERRAIN_T*" + boolean luaL_checkboolean(lua_State *L, int narg); int LUA_EnumLib(lua_State *L); @@ -126,6 +131,7 @@ int LUA_PolyObjLib(lua_State *L); int LUA_BlockmapLib(lua_State *L); int LUA_HudLib(lua_State *L); int LUA_FollowerLib(lua_State *L); +int LUA_TerrainLib(lua_State *L); #ifdef __cplusplus } // extern "C" diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index d673627b5..ba2fbb6d1 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -101,6 +101,7 @@ enum mobj_e { mobj_sprxoff, mobj_spryoff, mobj_sprzoff, + mobj_terrain, mobj_hitlag, mobj_waterskip, mobj_dispoffset, @@ -191,6 +192,7 @@ static const char *const mobj_opt[] = { "sprxoff", "spryoff", "sprzoff", + "terrain", "hitlag", "waterskip", "dispoffset", @@ -479,6 +481,9 @@ static int mobj_get(lua_State *L) case mobj_sprzoff: lua_pushfixed(L, mo->sprzoff); break; + case mobj_terrain: + LUA_PushUserdata(L, mo->terrain, META_TERRAIN); + break; case mobj_hitlag: lua_pushinteger(L, mo->hitlag); break; @@ -894,6 +899,9 @@ static int mobj_set(lua_State *L) case mobj_sprzoff: mo->sprzoff = luaL_checkfixed(L, 3); break; + case mobj_terrain: + mo->terrain = *((terrain_t **)luaL_checkudata(L, 3, META_TERRAIN)); + break; case mobj_hitlag: mo->hitlag = luaL_checkinteger(L, 3); break; diff --git a/src/lua_script.c b/src/lua_script.c index bfcf84bd4..08ca9a6aa 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -61,6 +61,7 @@ static lua_CFunction liblist[] = { LUA_BlockmapLib, // blockmap stuff LUA_HudLib, // HUD stuff LUA_FollowerLib, // follower_t, followers[] + LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t NULL }; diff --git a/src/lua_terrainlib.c b/src/lua_terrainlib.c new file mode 100644 index 000000000..54be0b985 --- /dev/null +++ b/src/lua_terrainlib.c @@ -0,0 +1,511 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by Kart Krew. +// Copyright (C) 2020 by Sonic Team Junior. +// Copyright (C) 2016 by John "JTE" Muniz. +// +// 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_terrainlib.c +/// \brief terrain structure library for Lua scripting + +#include "doomdef.h" +#include "fastcmp.h" + +#include "lua_script.h" +#include "lua_libs.h" +#include "k_terrain.h" + +enum terrain { + terrain_valid = 0, + terrain_name, + terrain_hash, + terrain_splashid, + terrain_footstepid, + terrain_overlayid, + terrain_friction, + terrain_offroad, + terrain_damagetype, + terrain_trickpanel, + terrain_speedpad, + terrain_speedpadangle, + terrain_springstrength, + terrain_springstarcolor, + terrain_outrun, + terrain_floorclip, + terrain_flags +}; + +enum splash { + splash_valid = 0, + splash_name, + splash_hash, + splash_mobjtype, + splash_sfx, + splash_scale, + splash_color, + splash_pushH, + splash_pushV, + splash_spread, + splash_cone, + splash_numparticles +}; + +enum footstep { + footstep_valid = 0, + footstep_name, + footstep_hash, + footstep_mobjtype, + footstep_sfx, + footstep_scale, + footstep_color, + footstep_pushH, + footstep_pushV, + footstep_spread, + footstep_cone, + footstep_sfxfreq, + footstep_frequency, + footstep_requiredspeed +}; + +enum overlay { + overlay_valid = 0, + overlay_name, + overlay_hash, + overlay_states, + overlay_scale, + overlay_color, + overlay_speed +}; + +static const char *const terrain_opt[] = { + "valid", + "name", + "hash", + "splashid", + "footstepid", + "overlayid", + "friction", + "offroad", + "damagetype", + "trickpanel", + "speedpad", + "speedpadangle", + "springstrength", + "springstarcolor", + "outrun", + "floorclip", + "flags", + NULL +}; + +static const char *const splash_opt[] = { + "valid", + "name", + "hash", + "mobjtype", + "sfx", + "scale", + "color", + "pushh", + "pushv", + "spread", + "cone", + "numparticles", + NULL +}; + +static const char *const footstep_opt[] = { + "valid", + "name", + "hash", + "mobjtype", + "sfx", + "scale", + "color", + "pushh", + "pushv", + "spread", + "cone", + "sfxfreq", + "frequency", + "requiredspeed", + NULL +}; + +static const char *const overlay_opt[] = { + "valid", + "name", + "hash", + "states", + "scale", + "color", + "speed", + NULL +}; + +static int splash_get(lua_State *L) +{ + t_splash_t *splash = *((t_splash_t **)luaL_checkudata(L, 1, META_SPLASH)); + enum splash field = luaL_checkoption(L, 2, splash_opt[0], splash_opt); + + if (!splash) + { + switch (field) + { + case splash_valid: + lua_pushboolean(L, false); + return 1; + default: + return LUA_ErrInvalid(L, "t_splash_t"); + } + } + + switch (field) + { + case splash_valid: + lua_pushboolean(L, true); + break; + case splash_name: + lua_pushstring(L, splash->name); + break; + case splash_hash: + lua_pushnumber(L, splash->hash); + break; + case splash_mobjtype: + lua_pushnumber(L, splash->mobjType); + break; + case splash_sfx: + lua_pushnumber(L, splash->sfx); + break; + case splash_scale: + lua_pushfixed(L, splash->scale); + break; + case splash_color: + lua_pushnumber(L, splash->color); + break; + case splash_pushH: + lua_pushfixed(L, splash->pushH); + break; + case splash_pushV: + lua_pushfixed(L, splash->pushV); + break; + case splash_spread: + lua_pushfixed(L, splash->spread); + break; + case splash_cone: + lua_pushangle(L, splash->cone); + break; + case splash_numparticles: + lua_pushnumber(L, splash->numParticles); + break; + } + + return 1; +} + +static int splash_set(lua_State *L) +{ + return luaL_error(L, LUA_QL("t_splash_t") " struct cannot be edited by Lua."); +} + +static int splash_num(lua_State *L) +{ + t_splash_t *splash = *((t_splash_t **)luaL_checkudata(L, 1, META_SPLASH)); + + // This should never happen. + I_Assert(splash != NULL); + + lua_pushinteger(L, K_GetSplashHeapIndex(splash)); + return 1; +} + +static int footstep_get(lua_State *L) +{ + t_footstep_t *footstep = *((t_footstep_t **)luaL_checkudata(L, 1, META_FOOTSTEP)); + enum footstep field = luaL_checkoption(L, 2, footstep_opt[0], footstep_opt); + + if (!footstep) + { + switch (field) + { + case footstep_valid: + lua_pushboolean(L, false); + return 1; + default: + return LUA_ErrInvalid(L, "t_footstep_t"); + } + } + + switch (field) + { + case footstep_valid: + lua_pushboolean(L, true); + break; + case footstep_name: + lua_pushstring(L, footstep->name); + break; + case footstep_hash: + lua_pushnumber(L, footstep->hash); + break; + case footstep_mobjtype: + lua_pushnumber(L, footstep->mobjType); + break; + case footstep_sfx: + lua_pushnumber(L, footstep->sfx); + break; + case footstep_scale: + lua_pushfixed(L, footstep->scale); + break; + case footstep_color: + lua_pushnumber(L, footstep->color); + break; + case footstep_pushH: + lua_pushfixed(L, footstep->pushH); + break; + case footstep_pushV: + lua_pushfixed(L, footstep->pushV); + break; + case footstep_spread: + lua_pushfixed(L, footstep->spread); + break; + case footstep_cone: + lua_pushangle(L, footstep->cone); + break; + case footstep_sfxfreq: + lua_pushnumber(L, footstep->sfxFreq); + break; + case footstep_frequency: + lua_pushnumber(L, footstep->frequency); + break; + case footstep_requiredspeed: + lua_pushfixed(L, footstep->requiredSpeed); + break; + } + + return 1; +} + +static int footstep_set(lua_State *L) +{ + return luaL_error(L, LUA_QL("t_footstep_t") " struct cannot be edited by Lua."); +} + +static int footstep_num(lua_State *L) +{ + t_footstep_t *footstep = *((t_footstep_t **)luaL_checkudata(L, 1, META_FOOTSTEP)); + + // This should never happen. + I_Assert(footstep != NULL); + + lua_pushinteger(L, K_GetFootstepHeapIndex(footstep)); + return 1; +} + +static int overlay_get(lua_State *L) +{ + t_overlay_t *overlay = *((t_overlay_t **)luaL_checkudata(L, 1, META_OVERLAY)); + enum overlay field = luaL_checkoption(L, 2, overlay_opt[0], overlay_opt); + + if (!overlay) + { + switch (field) + { + case overlay_valid: + lua_pushboolean(L, false); + return 1; + default: + return LUA_ErrInvalid(L, "t_overlay_t"); + } + } + + switch (field) + { + case overlay_valid: + lua_pushboolean(L, true); + break; + case overlay_name: + lua_pushstring(L, overlay->name); + break; + case overlay_hash: + lua_pushnumber(L, overlay->hash); + break; + case overlay_states: + lua_createtable(L, TOV__MAX, 0); + for (size_t i = 0; i < TOV__MAX; i++) + { + lua_pushinteger(L, overlay->states[i]); + lua_rawseti(L, -2, 1 + i); + } + break; + case overlay_scale: + lua_pushboolean(L, overlay->scale); + break; + case overlay_color: + lua_pushnumber(L, overlay->color); + break; + case overlay_speed: + lua_pushfixed(L, overlay->speed); + break; + } + + return 1; +} + +static int overlay_set(lua_State *L) +{ + return luaL_error(L, LUA_QL("t_overlay_t") " struct cannot be edited by Lua."); +} + +static int overlay_num(lua_State *L) +{ + t_overlay_t *overlay = *((t_overlay_t **)luaL_checkudata(L, 1, META_OVERLAY)); + + // This should never happen. + I_Assert(overlay != NULL); + + lua_pushinteger(L, K_GetOverlayHeapIndex(overlay)); + return 1; +} + +static int terrain_get(lua_State *L) +{ + terrain_t *terrain = *((terrain_t **)luaL_checkudata(L, 1, META_TERRAIN)); + enum terrain field = luaL_checkoption(L, 2, terrain_opt[0], terrain_opt); + + if (!terrain) + { + switch (field) + { + case terrain_valid: + lua_pushboolean(L, false); + return 1; + default: + return LUA_ErrInvalid(L, "terrain_t"); + } + } + + switch (field) + { + case terrain_valid: + lua_pushboolean(L, true); + break; + case terrain_name: + lua_pushstring(L, terrain->name); + break; + case terrain_hash: + lua_pushnumber(L, terrain->hash); + break; + case terrain_splashid: + lua_pushnumber(L, terrain->splashID); + break; + case terrain_footstepid: + lua_pushnumber(L, terrain->footstepID); + break; + case terrain_overlayid: + lua_pushnumber(L, terrain->overlayID); + break; + case terrain_friction: + lua_pushfixed(L, terrain->friction); + break; + case terrain_offroad: + lua_pushfixed(L, terrain->offroad); + break; + case terrain_damagetype: + lua_pushnumber(L, terrain->damageType); + break; + case terrain_trickpanel: + lua_pushfixed(L, terrain->trickPanel); + break; + case terrain_speedpad: + lua_pushfixed(L, terrain->speedPad); + break; + case terrain_speedpadangle: + lua_pushangle(L, terrain->speedPadAngle); + break; + case terrain_springstrength: + lua_pushfixed(L, terrain->springStrength); + break; + case terrain_springstarcolor: + lua_pushnumber(L, terrain->springStarColor); + break; + case terrain_outrun: + lua_pushfixed(L, terrain->outrun); + break; + case terrain_floorclip: + lua_pushfixed(L, terrain->floorClip); + break; + case terrain_flags: + lua_pushnumber(L, terrain->flags); + break; + } + + return 1; +} + +static int terrain_set(lua_State *L) +{ + return luaL_error(L, LUA_QL("terrain_t") " struct cannot be edited by Lua."); +} + +static int terrain_num(lua_State *L) +{ + terrain_t *terrain = *((terrain_t **)luaL_checkudata(L, 1, META_TERRAIN)); + + // This should never happen. + I_Assert(terrain != NULL); + + lua_pushinteger(L, K_GetTerrainHeapIndex(terrain)); + return 1; +} + +int LUA_TerrainLib(lua_State *L) +{ + luaL_newmetatable(L, META_SPLASH); + lua_pushcfunction(L, splash_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, splash_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, splash_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + luaL_newmetatable(L, META_FOOTSTEP); + lua_pushcfunction(L, footstep_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, footstep_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, footstep_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + luaL_newmetatable(L, META_OVERLAY); + lua_pushcfunction(L, overlay_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, overlay_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, overlay_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + luaL_newmetatable(L, META_TERRAIN); + lua_pushcfunction(L, terrain_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, terrain_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, terrain_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + return 0; +} \ No newline at end of file