diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da820419e0aaa235105503de5c1a81ebc72f88cc..59c0e81f679c269569d6fb195aa37ee6020b5f27 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/deh_tables.c b/src/deh_tables.c index 6a5255e8ee7671a7e09a5d49956c34e0367481a9..dcb18a33e237ffcbfafe3cefbf4ad153ec09a35c 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5206,7 +5206,20 @@ struct int_const_s const INT_CONST[] = { {"TN_NIGHTCOREABLE",TN_NIGHTCOREABLE}, {"TN_CHANGEPITCH",TN_CHANGEPITCH}, {"TN_LOOPING",TN_LOOPING}, - + + // t_overlay_action_t + {"TOV_UNDEFINED",TOV_UNDEFINED}, + {"TOV_STILL",TOV_STILL}, + {"TOV_MOVING",TOV_MOVING}, + {"TOV__MAX",TOV__MAX}, + + // terrain_flags_t + {"TRF_LIQUID",TRF_LIQUID}, + {"TRF_SNEAKERPANEL",TRF_SNEAKERPANEL}, + {"TRF_STAIRJANK",TRF_STAIRJANK}, + {"TRF_TRIPWIRE",TRF_TRIPWIRE}, + {"TRF_REMAP",TRF_REMAP}, + {NULL,0} }; diff --git a/src/k_terrain.c b/src/k_terrain.c index bc41bbc316a1773bd2e307944b0a75dfc40a9ef5..fa6f9742ddb1abfda984d6f9c0c499323a868db1 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -1630,7 +1630,7 @@ boolean K_TerrainHasAffect(terrain_t *terrain, boolean badonly) || terrain->trickPanel != 0 || terrain->speedPad != 0 || terrain->springStrength != 0 - || terrain->flags != 0); + || (terrain->flags & (TRF_LIQUID|TRF_SNEAKERPANEL|TRF_TRIPWIRE))); } /*-------------------------------------------------- diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 8730566436b78ff1c2905a0a6a2e86c7efbcdc69..86a328012bcd99b7eebba31dab6275c95e701e8b 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -40,6 +40,7 @@ #include "k_powerup.h" #include "k_hitlag.h" #include "music.h" // music functions necessary for lua integration +#include "k_terrain.h" #include "lua_script.h" #include "lua_libs.h" @@ -232,6 +233,10 @@ static const struct { {META_ACTIVATOR, "activator_t"}, {META_FOLLOWER, "follower_t"}, + {META_SPLASH, "t_splash_t"}, + {META_FOOTSTEP, "t_footstep_t"}, + {META_OVERLAY, "t_overlay_t"}, + {META_TERRAIN, "terrain_t"}, {NULL, NULL} }; @@ -4074,6 +4079,112 @@ static int lib_startTitlecardCecho(lua_State *L) return 1; } +static int lib_kGetDefaultTerrain(lua_State *L) +{ + LUA_PushUserdata(L, K_GetDefaultTerrain(), META_TERRAIN); + return 1; +} + +static int lib_kGetTerrainForTextureName(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + LUA_PushUserdata(L, K_GetTerrainForTextureName(str), META_TERRAIN); + return 1; +} + +static int lib_kGetTerrainForTextureNum(lua_State *L) +{ + INT32 id = luaL_checkinteger(L, 1); + LUA_PushUserdata(L, K_GetTerrainForTextureNum(id), META_TERRAIN); + return 1; +} + +static int lib_kProcessTerrainEffect(lua_State *L) +{ + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + + NOHUD + INLEVEL + + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + + K_ProcessTerrainEffect(mo); + return 0; +} + +static int lib_kSetDefaultFriction(lua_State *L) +{ + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + + NOHUD + INLEVEL + + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + + K_SetDefaultFriction(mo); + return 0; +} + +static int lib_kSpawnSplashForMobj(lua_State *L) +{ + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + fixed_t impact = luaL_optinteger(L, 2, FRACUNIT); + + NOHUD + INLEVEL + + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + + K_SpawnSplashForMobj(mo, impact); + return 0; +} + +static int lib_kHandleFootstepParticles(lua_State *L) +{ + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + + NOHUD + INLEVEL + + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + + K_HandleFootstepParticles(mo); + return 0; +} + +static int lib_kUpdateTerrainOverlay(lua_State *L) +{ + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + + NOHUD + INLEVEL + + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + + K_UpdateTerrainOverlay(mo); + return 0; +} + +static int lib_kTerrainHasAffect(lua_State *L) +{ + terrain_t *terrain = *((terrain_t **)luaL_checkudata(L, 1, META_TERRAIN)); + boolean badonly = lua_optboolean(L, 2); + + NOHUD + INLEVEL + + if (!terrain) + return LUA_ErrInvalid(L, "terrain_t"); + + lua_pushboolean(L, K_TerrainHasAffect(terrain, badonly)); + return 1; +} + static luaL_Reg lib[] = { {"print", lib_print}, {"chatprint", lib_chatprint}, @@ -4365,6 +4476,17 @@ static luaL_Reg lib[] = { {"Music_UnPauseAll", lib_mMusicUnPauseAll}, {"Music_Loop", lib_mMusicLoop}, {"Music_BatchExempt", lib_mMusicBatchExempt}, + + // k_terrain + {"K_GetDefaultTerrain", lib_kGetDefaultTerrain}, + {"K_GetTerrainForTextureName", lib_kGetTerrainForTextureName}, + {"K_GetTerrainForTextureNum", lib_kGetTerrainForTextureNum}, + {"K_ProcessTerrainEffect", lib_kProcessTerrainEffect}, + {"K_SetDefaultFriction", lib_kSetDefaultFriction}, + {"K_SpawnSplashForMobj", lib_kSpawnSplashForMobj}, + {"K_HandleFootstepParticles", lib_kHandleFootstepParticles}, + {"K_UpdateTerrainOverlay", lib_kUpdateTerrainOverlay}, + {"K_TerrainHasAffect", lib_kTerrainHasAffect}, {NULL, NULL} }; diff --git a/src/lua_libs.h b/src/lua_libs.h index 73d7b4f6ed1da77837b7eeb62aea2afe00d72c92..82704d728e15eb8836f7231a8a039d6eea68fc5a 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -104,6 +104,11 @@ extern lua_State *gL; #define META_FOLLOWER "FOLLOWER_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); @@ -123,6 +128,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 158909815711208a23acf3c0d44f17324fb9c70b..103d46c582f040a041230f565fd10ec4104f71a7 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_playerlib.c b/src/lua_playerlib.c index ee56da06fd47fc66ec5f470082846cff834b4ba5..f8bb9859a8421be2a7cbfc2bbe604c1374f5e563 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -282,6 +282,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->ringboxaward); else if (fastcmp(field,"itemflags")) lua_pushinteger(L, plr->itemflags); + else if (fastcmp(field,"outrun")) + lua_pushinteger(L, plr->outrun); else if (fastcmp(field,"drift")) lua_pushinteger(L, plr->drift); else if (fastcmp(field,"driftcharge")) @@ -682,6 +684,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->griefStrikes); else if (fastcmp(field,"griefwarned")) lua_pushinteger(L, plr->griefWarned); + else if (fastcmp(field,"stairjank")) + lua_pushinteger(L, plr->stairjank); else if (fastcmp(field,"splitscreenindex")) lua_pushinteger(L, plr->splitscreenindex); else if (fastcmp(field,"whip")) @@ -848,6 +852,8 @@ static int player_set(lua_State *L) plr->ringboxaward = luaL_checkinteger(L, 3); else if (fastcmp(field,"itemflags")) plr->itemflags = luaL_checkinteger(L, 3); + else if (fastcmp(field,"outrun")) + plr->outrun = luaL_checkinteger(L, 3); else if (fastcmp(field,"drift")) plr->drift = luaL_checkinteger(L, 3); else if (fastcmp(field,"driftcharge")) @@ -1241,6 +1247,8 @@ static int player_set(lua_State *L) plr->griefStrikes = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"griefwarned")) plr->griefWarned = luaL_checkinteger(L, 3); + else if (fastcmp(field,"stairjank")) + plr->stairjank = luaL_checkinteger(L, 3); else if (fastcmp(field,"splitscreenindex")) return NOSET; else if (fastcmp(field,"whip")) diff --git a/src/lua_script.c b/src/lua_script.c index bfcf84bd4594177d3ee8e8d4f21022bac48f214c..08ca9a6aaf9df24614296ff77a29ecb1e070f31e 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 0000000000000000000000000000000000000000..3ffe504dcf19b320fa31c31a35a8ed7f0fc0f040 --- /dev/null +++ b/src/lua_terrainlib.c @@ -0,0 +1,831 @@ +// 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 lib_iterateSplashes(lua_State *L) +{ + size_t i; + + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, lib_iterateSplashes); + return 1; + } + + lua_settop(L, 2); + lua_remove(L, 1); // state is unused. + + if (!lua_isnil(L, 1)) + i = K_GetSplashHeapIndex(*(t_splash_t **)luaL_checkudata(L, 1, META_SPLASH)) + 1; + else + i = 0; + + // terrains are always valid, only added, never removed + if (i < K_GetNumSplashDefs()) + { + LUA_PushUserdata(L, K_GetSplashByIndex(i), META_SPLASH); + return 1; + } + + return 0; +} + +static int lib_getSplash(lua_State *L) +{ + const char *field; + size_t i; + + // find terrain by number + if (lua_type(L, 2) == LUA_TNUMBER) + { + i = luaL_checkinteger(L, 2); + if (i >= K_GetNumSplashDefs()) + return luaL_error(L, "splashes[] index %d out of range (0 - %d)", i, K_GetNumSplashDefs()-1); + LUA_PushUserdata(L, K_GetSplashByIndex(i), META_SPLASH); + return 1; + } + + field = luaL_checkstring(L, 2); + + // special function iterate + if (fastcmp(field,"iterate")) + { + lua_pushcfunction(L, lib_iterateSplashes); + return 1; + } + + // find terrain by name + t_splash_t *byname = K_GetSplashByName(field); + if (byname != NULL) + { + LUA_PushUserdata(L, byname, META_SPLASH); + return 1; + } + + return 0; +} + +static int lib_numSplashes(lua_State *L) +{ + lua_pushinteger(L, K_GetNumSplashDefs()); + 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 lib_iterateFootsteps(lua_State *L) +{ + size_t i; + + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, lib_iterateFootsteps); + return 1; + } + + lua_settop(L, 2); + lua_remove(L, 1); // state is unused. + + if (!lua_isnil(L, 1)) + i = K_GetFootstepHeapIndex(*(t_footstep_t **)luaL_checkudata(L, 1, META_FOOTSTEP)) + 1; + else + i = 0; + + // footsteps are always valid, only added, never removed + if (i < K_GetNumFootstepDefs()) + { + LUA_PushUserdata(L, K_GetFootstepByIndex(i), META_FOOTSTEP); + return 1; + } + + return 0; +} + +static int lib_getFootstep(lua_State *L) +{ + const char *field; + size_t i; + + // find footstep by number + if (lua_type(L, 2) == LUA_TNUMBER) + { + i = luaL_checkinteger(L, 2); + if (i >= K_GetNumFootstepDefs()) + return luaL_error(L, "footsteps[] index %d out of range (0 - %d)", i, K_GetNumFootstepDefs()-1); + LUA_PushUserdata(L, K_GetFootstepByIndex(i), META_FOOTSTEP); + return 1; + } + + field = luaL_checkstring(L, 2); + + // special function iterate + if (fastcmp(field,"iterate")) + { + lua_pushcfunction(L, lib_iterateFootsteps); + return 1; + } + + // find footstep by name + t_footstep_t *byname = K_GetFootstepByName(field); + if (byname != NULL) + { + LUA_PushUserdata(L, byname, META_FOOTSTEP); + return 1; + } + + return 0; +} + +static int lib_numFootsteps(lua_State *L) +{ + lua_pushinteger(L, K_GetNumFootstepDefs()); + 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 lib_iterateOverlays(lua_State *L) +{ + size_t i; + + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, lib_iterateOverlays); + return 1; + } + + lua_settop(L, 2); + lua_remove(L, 1); // state is unused. + + if (!lua_isnil(L, 1)) + i = K_GetOverlayHeapIndex(*(t_overlay_t **)luaL_checkudata(L, 1, META_OVERLAY)) + 1; + else + i = 0; + + // overlays are always valid, only added, never removed + if (i < K_GetNumOverlayDefs()) + { + LUA_PushUserdata(L, K_GetOverlayByIndex(i), META_OVERLAY); + return 1; + } + + return 0; +} + +static int lib_getOverlay(lua_State *L) +{ + const char *field; + size_t i; + + // find overlay by number + if (lua_type(L, 2) == LUA_TNUMBER) + { + // Making a special condition for this as the game contains no overlays by default. + if (K_GetNumOverlayDefs() == 0) + return luaL_error(L, "no overlays available in overlays[]"); + + i = luaL_checkinteger(L, 2); + if (i >= K_GetNumOverlayDefs()) + return luaL_error(L, "overlays[] index %d out of range (0 - %d)", i, K_GetNumOverlayDefs()-1); + LUA_PushUserdata(L, K_GetOverlayByIndex(i), META_OVERLAY); + return 1; + } + + field = luaL_checkstring(L, 2); + + // special function iterate + if (fastcmp(field,"iterate")) + { + lua_pushcfunction(L, lib_iterateOverlays); + return 1; + } + + // find overlay by name + t_overlay_t *byname = K_GetOverlayByName(field); + if (byname != NULL) + { + LUA_PushUserdata(L, byname, META_OVERLAY); + return 1; + } + + return 0; +} + +static int lib_numOverlays(lua_State *L) +{ + lua_pushinteger(L, K_GetNumOverlayDefs()); + 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; +} + +static int lib_iterateTerrains(lua_State *L) +{ + size_t i; + + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, lib_iterateTerrains); + return 1; + } + + lua_settop(L, 2); + lua_remove(L, 1); // state is unused. + + if (!lua_isnil(L, 1)) + i = K_GetTerrainHeapIndex(*(terrain_t **)luaL_checkudata(L, 1, META_TERRAIN)) + 1; + else + i = 0; + + // terrains are always valid, only added, never removed + if (i < K_GetNumTerrainDefs()) + { + LUA_PushUserdata(L, K_GetTerrainByIndex(i), META_TERRAIN); + return 1; + } + + return 0; +} + +static int lib_getTerrain(lua_State *L) +{ + const char *field; + size_t i; + + // find terrain by number + if (lua_type(L, 2) == LUA_TNUMBER) + { + i = luaL_checkinteger(L, 2); + if (i >= K_GetNumTerrainDefs()) + return luaL_error(L, "terrains[] index %d out of range (0 - %d)", i, K_GetNumTerrainDefs()-1); + LUA_PushUserdata(L, K_GetTerrainByIndex(i), META_TERRAIN); + return 1; + } + + field = luaL_checkstring(L, 2); + + // special function iterate + if (fastcmp(field,"iterate")) + { + lua_pushcfunction(L, lib_iterateTerrains); + return 1; + } + + // find terrain by name + terrain_t *byname = K_GetTerrainByName(field); + if (byname != NULL) + { + LUA_PushUserdata(L, byname, META_TERRAIN); + return 1; + } + + return 0; +} + +static int lib_numTerrains(lua_State *L) +{ + lua_pushinteger(L, K_GetNumTerrainDefs()); + 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); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getSplash); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numSplashes); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "splashes"); + + 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); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getFootstep); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numFootsteps); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "footsteps"); + + 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); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getOverlay); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numOverlays); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "overlays"); + + 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); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getTerrain); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numTerrains); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "terrains"); + + return 0; +} \ No newline at end of file