diff --git a/src/Sourcefile b/src/Sourcefile index e8e6eb5f7..05c35b7a3 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -110,6 +110,7 @@ lua_blockmaplib.c lua_hudlib.c lua_hudlib_drawlist.c lua_followerlib.c +lua_terrainlib.c k_kart.c k_collide.c k_color.c diff --git a/src/deh_tables.c b/src/deh_tables.c index b0c5795fd..9eb28fce4 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1573,5 +1573,19 @@ struct int_const_s const INT_CONST[] = { {"SNEAKERTYPE_ROCKETSNEAKER", SNEAKERTYPE_ROCKETSNEAKER}, {"SNEAKERTYPE_WATERPANEL", SNEAKERTYPE_WATERPANEL}, + // 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_WATERRUNPANEL",}, + {"TRF_TRIPWIRE",TRF_TRIPWIRE}, + {"TRF_REMAP",TRF_REMAP}, + {"TRF_BYPASSBOOST", TRF_BYPASSBOOST}, + {NULL,0} }; diff --git a/src/k_terrain.c b/src/k_terrain.c index e83d552fc..8e5542ad4 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -1670,8 +1670,9 @@ boolean K_TerrainHasAffect(terrain_t *terrain, boolean badonly) return (terrain->friction != 0 || terrain->speedPad != 0 + || terrain->pogoSpring != 0 || terrain->springStrength != 0 - || terrain->flags != 0); + || (terrain->flags & (TRF_LIQUID|TRF_SNEAKERPANEL|TRF_BYPASSBOOST|TRF_WATERRUNPANEL|TRF_TRIPWIRE))); } /*-------------------------------------------------- diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 99430d32b..ce1dd219a 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -44,6 +44,8 @@ #include "taglist.h" // P_FindSpecialLineFromTag #include "lua_hook.h" // hook_cmd_running errors +#include "k_terrain.h" + #define NOHUD if (hud_running)\ return luaL_error(L, "HUD rendering code should not call this function!");\ else if (hook_cmd_running)\ @@ -229,6 +231,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} }; @@ -4237,6 +4243,97 @@ static int lib_iGetPreciseTime(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_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}, @@ -4555,6 +4652,16 @@ static luaL_Reg lib[] = { // I_System {"I_GetPreciseTime",lib_iGetPreciseTime}, + // k_terrain + {"K_GetDefaultTerrain", lib_kGetDefaultTerrain}, + {"K_GetTerrainForTextureName", lib_kGetTerrainForTextureName}, + {"K_GetTerrainForTextureNum", lib_kGetTerrainForTextureNum}, + {"K_ProcessTerrainEffect", lib_kProcessTerrainEffect}, + {"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 e3ca6c066..9f79a14a4 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -106,6 +106,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); @@ -125,6 +130,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 98b783721..98e519242 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -102,6 +102,7 @@ enum mobj_e { mobj_sprxoff, mobj_spryoff, mobj_sprzoff, + mobj_terrain, mobj_dispoffset, mobj_tid, mobj_special, @@ -189,6 +190,7 @@ static const char *const mobj_opt[] = { "sprxoff", "spryoff", "sprzoff", + "terrain", "dispoffset", "tid", "special", @@ -535,6 +537,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_dispoffset: lua_pushinteger(L, mo->dispoffset); break; @@ -1017,6 +1022,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_dispoffset: mo->dispoffset = luaL_checkinteger(L, 3); break; diff --git a/src/lua_script.c b/src/lua_script.c index b72d14c41..d77ab74a2 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -60,6 +60,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..5266dedab --- /dev/null +++ b/src/lua_terrainlib.c @@ -0,0 +1,836 @@ +// 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_pogospring, + terrain_pogospringmin, + terrain_pogospringmax, + terrain_speedpad, + terrain_speedpadangle, + terrain_springstrength, + 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", + "pogospring", + "pogospringmin", + "pogospringmax", + "speedpad", + "speedpadangle", + "springstrength", + "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_pogospring: + lua_pushfixed(L, terrain->pogoSpring); + break; + case terrain_pogospringmin: + lua_pushfixed(L, terrain->pogoSpringMin); + break; + case terrain_pogospringmax: + lua_pushfixed(L, terrain->pogoSpringMax); + 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_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; +}