Files
SRB2Kart-Saturn/src/lua_mobjlib.c
T
Alug 4676885521 fixup: replace stricmp with fasticmp
actually replace everything correctly
my dumbass forgor about the fast both strcmp and stricmp matter in sort stuff so we cannot use the fastcmp stuff there, which means we should not redifine stricmp as fasticmp in case something relies on that behaviour ugh
2025-11-01 23:45:06 +01:00

1022 lines
29 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
// Copyright (C) 2012-2018 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 lua_mobjlib.c
/// \brief mobj/thing library for Lua scripting
#include <stddef.h>
#include "doomdef.h"
#include "fastcmp.h"
#include "r_things.h"
#include "r_skins.h"
#include "r_main.h"
#include "p_local.h"
#include "g_game.h"
#include "p_setup.h"
#include "doomdef.h"
#include "d_netcmd.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
#include "lua_hook.h" // cmd errors
#include "lua_udatalib.h"
int mobj_nosetpos_x(lua_State *L);
int mobj_nosetpos_y(lua_State *L);
int mobj_nosetpos_subsector(lua_State *L);
int mobj_nosetpos_floorz(lua_State *L);
int mobj_nosetpos_ceilingz(lua_State *L);
int mobj_snext_noset(lua_State *L);
int mobj_z_setter(lua_State *L);
int mobj_sprev_unimplemented(lua_State *L);
int mobj_angle_setter(lua_State *L);
int mobj_sloperoll_noop(lua_State *L);
int mobj_spritescale_setter(lua_State *L);
int mobj_spriteoffset_setter(lua_State *L);
int mobj_touching_sectorlist_unimplemented(lua_State *L);
int mobj_radius_setter(lua_State *L);
int mobj_height_setter(lua_State *L);
int mobj_pmomz_setter(lua_State *L);
int mobj_state_getter(lua_State *L);
int mobj_state_setter(lua_State *L);
int mobj_flags_setter(lua_State *L);
int mobj_skin_getter(lua_State *L);
int mobj_skin_setter(lua_State *L);
int mobj_localskin_getter(lua_State *L);
int mobj_localskin_setter(lua_State *L);
int mobj_color_setter(lua_State *L);
int mobj_blendmode_setter(lua_State *L);
int mobj_bnext_noset(lua_State *L);
int mobj_bprev_unimplemented(lua_State *L);
int mobj_hnext_setter(lua_State *L);
int mobj_hprev_setter(lua_State *L);
int mobj_type_setter(lua_State *L);
int mobj_info_getter(lua_State *L);
int mobj_info_noset(lua_State *L);
int mobj_target_getter(lua_State *L);
int mobj_target_setter(lua_State *L);
int mobj_player_noset(lua_State *L);
int mobj_spawnpoint_setter(lua_State *L);
int mobj_tracer_getter(lua_State *L);
int mobj_tracer_setter(lua_State *L);
int mobj_mobjnum_unimplemented(lua_State *L);
int mobj_scale_setter(lua_State *L);
int mobj_destscale_setter(lua_State *L);
int mobj_standingslope_noset(lua_State *L);
int mobj_rollsum_getter(lua_State *L);
int mobj_rollsum_noset(lua_State *L);
static const char *const array_opt[] ={"iterate",NULL};
#define FIELD(type, field_name, getter, setter) { #field_name, offsetof(type, field_name), getter, setter }
static const udata_field_t mobj_fields[] = {
FIELD(mobj_t, x, udatalib_getter_fixed, mobj_nosetpos_x),
FIELD(mobj_t, y, udatalib_getter_fixed, mobj_nosetpos_y),
FIELD(mobj_t, z, udatalib_getter_fixed, mobj_z_setter),
FIELD(mobj_t, snext, udatalib_getter_mobj, mobj_snext_noset),
FIELD(mobj_t, sprev, mobj_sprev_unimplemented, mobj_sprev_unimplemented),
FIELD(mobj_t, angle, udatalib_getter_angle, mobj_angle_setter),
FIELD(mobj_t, pitch, udatalib_getter_angle, udatalib_setter_angle),
FIELD(mobj_t, roll, udatalib_getter_angle, udatalib_setter_angle),
FIELD(mobj_t, rollangle, udatalib_getter_angle, udatalib_setter_angle),
FIELD(mobj_t, sloperoll, udatalib_getter_angle, mobj_sloperoll_noop),
FIELD(mobj_t, slopepitch, udatalib_getter_angle, mobj_sloperoll_noop),
// Macro fails here
{ "rollsum", 0, mobj_rollsum_getter, mobj_rollsum_noset },
FIELD(mobj_t, sprite, udatalib_getter_spritenum, udatalib_setter_spritenum),
FIELD(mobj_t, frame, udatalib_getter_uint32, udatalib_setter_uint32),
FIELD(mobj_t, anim_duration, udatalib_getter_uint16, udatalib_setter_uint16),
FIELD(mobj_t, spritexscale, udatalib_getter_fixed, mobj_spritescale_setter),
FIELD(mobj_t, spriteyscale, udatalib_getter_fixed, mobj_spritescale_setter),
FIELD(mobj_t, spritexoffset, udatalib_getter_fixed, mobj_spriteoffset_setter),
FIELD(mobj_t, spriteyoffset, udatalib_getter_fixed, mobj_spriteoffset_setter),
FIELD(mobj_t, touching_sectorlist, mobj_touching_sectorlist_unimplemented, mobj_touching_sectorlist_unimplemented),
FIELD(mobj_t, lightlevel, udatalib_getter_int16, udatalib_setter_int16),
FIELD(mobj_t, subsector, udatalib_getter_subsector, mobj_nosetpos_subsector),
FIELD(mobj_t, floorz, udatalib_getter_fixed, mobj_nosetpos_floorz),
FIELD(mobj_t, ceilingz, udatalib_getter_fixed, mobj_nosetpos_ceilingz),
FIELD(mobj_t, radius, udatalib_getter_fixed, mobj_radius_setter),
FIELD(mobj_t, height, udatalib_getter_fixed, mobj_height_setter),
FIELD(mobj_t, momx, udatalib_getter_fixed, udatalib_setter_fixed),
FIELD(mobj_t, momy, udatalib_getter_fixed, udatalib_setter_fixed),
FIELD(mobj_t, momz, udatalib_getter_fixed, udatalib_setter_fixed),
FIELD(mobj_t, pmomz, udatalib_getter_fixed, mobj_pmomz_setter),
FIELD(mobj_t, tics, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, state, mobj_state_getter, mobj_state_setter),
FIELD(mobj_t, flags, udatalib_getter_uint32, mobj_flags_setter),
FIELD(mobj_t, flags2, udatalib_getter_uint32, udatalib_setter_uint32),
FIELD(mobj_t, eflags, udatalib_getter_uint32, udatalib_setter_uint32),
FIELD(mobj_t, skin, mobj_skin_getter, mobj_skin_setter),
FIELD(mobj_t, localskin, mobj_localskin_getter, mobj_localskin_setter),
FIELD(mobj_t, color, udatalib_getter_uint8, mobj_color_setter),
FIELD(mobj_t, blendmode, udatalib_getter_int32, mobj_blendmode_setter),
FIELD(mobj_t, bnext, udatalib_getter_mobj, mobj_bnext_noset),
FIELD(mobj_t, bprev, mobj_bprev_unimplemented, mobj_bprev_unimplemented),
FIELD(mobj_t, hnext, udatalib_getter_mobj, mobj_hnext_setter),
FIELD(mobj_t, hprev, udatalib_getter_mobj, mobj_hprev_setter),
FIELD(mobj_t, type, udatalib_getter_mobjtype, mobj_type_setter),
FIELD(mobj_t, info, mobj_info_getter, mobj_info_noset),
FIELD(mobj_t, health, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, movedir, udatalib_getter_angle, udatalib_setter_angle), // Differs a bit from original setter and getter
FIELD(mobj_t, movecount, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, target, mobj_target_getter, mobj_target_setter),
FIELD(mobj_t, reactiontime, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, threshold, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, player, udatalib_getter_player, mobj_player_noset),
FIELD(mobj_t, lastlook, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, spawnpoint, udatalib_getter_mapthing, mobj_spawnpoint_setter),
FIELD(mobj_t, tracer, mobj_tracer_getter, mobj_tracer_setter),
FIELD(mobj_t, friction, udatalib_getter_fixed, udatalib_setter_fixed),
FIELD(mobj_t, movefactor, udatalib_getter_fixed, udatalib_setter_fixed),
FIELD(mobj_t, fuse, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, watertop, udatalib_getter_fixed, udatalib_setter_fixed),
FIELD(mobj_t, waterbottom, udatalib_getter_fixed, udatalib_setter_fixed),
FIELD(mobj_t, mobjnum, mobj_mobjnum_unimplemented, mobj_mobjnum_unimplemented),
FIELD(mobj_t, scale, udatalib_getter_fixed, mobj_scale_setter),
FIELD(mobj_t, destscale, udatalib_getter_fixed, mobj_destscale_setter),
FIELD(mobj_t, scalespeed, udatalib_getter_fixed, udatalib_setter_fixed),
FIELD(mobj_t, extravalue1, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, extravalue2, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, cusval, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, cvmem, udatalib_getter_int32, udatalib_setter_int32),
FIELD(mobj_t, standingslope, udatalib_getter_slope, mobj_standingslope_noset),
FIELD(mobj_t, colorized, udatalib_getter_boolean, udatalib_setter_boolean),
FIELD(mobj_t, mirrored, udatalib_getter_boolean, udatalib_setter_boolean),
FIELD(mobj_t, islocal, udatalib_getter_boolean, udatalib_setter_boolean),
{ NULL, 0, NULL, NULL },
};
#undef FIELD
// First, implement udatalib simple getters
#define pushmobj(L, mobj) LUA_PushUserdata(L, mobj, META_MOBJ)
int udatalib_getter_mobj(lua_State *L)
UDATALIB_SIMPLE_GETTER(mobj_t*, pushmobj)
#undef pushmobj
int udatalib_getter_mobjtype(lua_State *L)
UDATALIB_SIMPLE_GETTER(mobjtype_t, lua_pushinteger)
#define pushmapthing(L, mapthing) LUA_PushUserdata(L, mapthing, META_MAPTHING)
int udatalib_getter_mapthing(lua_State *L)
UDATALIB_SIMPLE_GETTER(mapthing_t*, pushmapthing)
#undef pushmapthing
// Now specific fields related to mobj positions that cannot be set directly
#define NOSETPOS(field) \
int mobj_nosetpos_ ## field(lua_State *L) \
{ \
return luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("P_Move") ", " LUA_QL("P_TryMove") ", or " LUA_QL("P_SetOrigin") ", or " LUA_QL("P_MoveOrigin") " instead.", #field); \
}
NOSETPOS(x)
NOSETPOS(y)
NOSETPOS(subsector)
NOSETPOS(floorz)
NOSETPOS(ceilingz)
#undef NOSETPOS
// Other fields that cannot be set directly
#define NOSET(field) \
int mobj_ ## field ## _noset(lua_State *L) \
{ \
return luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly.", #field); \
}
#define NOSET_USE(field, use) \
int mobj_ ## field ## _noset(lua_State *L) \
{ \
return luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly. Use " LUA_QL(use) " instead.", #field); \
}
NOSET(snext)
NOSET(bnext)
NOSET(info)
NOSET(player)
NOSET(standingslope)
NOSET_USE(rollsum, "rollangle")
#undef NOSET
#undef NOSET_USE
// Unimplemented fields (why would you need to set them like that explicitly?
// No idea, i'm keeping it for synch reasons)
#define UNIMPLEMENTED(field) \
int mobj_ ## field ## _unimplemented(lua_State *L) \
{ \
return luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", #field); \
}
UNIMPLEMENTED(sprev)
UNIMPLEMENTED(touching_sectorlist)
UNIMPLEMENTED(bprev)
UNIMPLEMENTED(mobjnum)
#undef UNIMPLEMENTED
// For some dumb reason it is valid to set sloperoll, even though it is read
// only
int mobj_sloperoll_noop(lua_State *L) { (void)L; return 0; }
// Now other getters/setters with arbitary logic
// Getters get light userdata, which in this case is just mobj_t pointer
#define GETMO() (mobj_t*)lua_touserdata(L, 1)
int mobj_z_setter(lua_State *L)
{
mobj_t *mo = GETMO();
// z doesn't cross sector bounds so it's okay.
mobj_t *ptmthing = tmthing;
mo->z = luaL_checkfixed(L, 2);
P_CheckPosition(mo, mo->x, mo->y);
mo->floorz = tmfloorz;
mo->ceilingz = tmceilingz;
P_SetTarget(&tmthing, ptmthing);
return 0;
}
int mobj_angle_setter(lua_State *L)
{
mobj_t *mo = GETMO();
mo->angle = luaL_checkangle(L, 2);
for (UINT8 i = 0; i <= splitscreen; i++)
{
if (mo->player == P_GetLocalPlayerForNum(i))
{
localangle[i] = mo->angle;
break;
}
}
return 0;
}
int mobj_spritescale_setter(lua_State *L)
{
mobj_t *mo = GETMO();
fixed_t *spritescale;
UDATALIB_GETFIELD(fixed_t, spritescale);
if (!mo->player)
*spritescale = luaL_checkfixed(L, 2);
else
{
// Mmm yea
if (spritescale == &mo->spritexscale)
mo->realxscale = luaL_checkfixed(L, 2);
else
mo->realyscale = luaL_checkfixed(L, 2);
}
return 0;
}
int mobj_spriteoffset_setter(lua_State *L)
{
mobj_t *mo = GETMO();
fixed_t *spriteoffset;
UDATALIB_GETFIELD(fixed_t, spriteoffset);
if (!mo->player)
*spriteoffset = luaL_checkfixed(L, 2);
else
{
// Mmm yea
if (spriteoffset == &mo->spritexoffset)
mo->realxoffset = luaL_checkfixed(L, 2);
else
mo->realyoffset = luaL_checkfixed(L, 2);
}
return 0;
}
int mobj_radius_setter(lua_State *L)
{
mobj_t *mo = GETMO();
mobj_t *ptmthing = tmthing;
mo->radius = luaL_checkfixed(L, 2);
if (mo->radius < 0)
mo->radius = 0;
P_CheckPosition(mo, mo->x, mo->y);
mo->floorz = tmfloorz;
mo->ceilingz = tmceilingz;
P_SetTarget(&tmthing, ptmthing);
return 0;
}
int mobj_height_setter(lua_State *L)
{
mobj_t *mo = GETMO();
mobj_t *ptmthing = tmthing;
mo->height = luaL_checkfixed(L, 2);
if (mo->height < 0)
mo->height = 0;
P_CheckPosition(mo, mo->x, mo->y);
mo->floorz = tmfloorz;
mo->ceilingz = tmceilingz;
P_SetTarget(&tmthing, ptmthing);
return 0;
}
int mobj_pmomz_setter(lua_State *L)
{
mobj_t *mo = GETMO();
mo->pmomz = luaL_checkfixed(L, 2);
mo->eflags |= MFE_APPLYPMOMZ;
return 0;
}
int mobj_state_getter(lua_State *L)
{
mobj_t *mo = GETMO();
lua_pushinteger(L, mo->state-states);
return 1;
}
int mobj_state_setter(lua_State *L)
{
mobj_t *mo = GETMO();
if (mo->player)
P_SetPlayerMobjState(mo, luaL_checkinteger(L, 2));
else
P_SetMobjState(mo, luaL_checkinteger(L, 2));
return 0;
}
int mobj_flags_setter(lua_State *L)
{
mobj_t *mo = GETMO();
UINT32 flags = luaL_checkinteger(L, 2);
if ((flags & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (mo->flags & (MF_NOBLOCKMAP|MF_NOSECTOR)))
{
P_UnsetThingPosition(mo);
mo->flags = flags;
if (flags & MF_NOSECTOR && sector_list)
{
P_DelSeclist(sector_list);
sector_list = NULL;
}
mo->snext = NULL, mo->sprev = NULL;
mo->bnext = NULL, mo->bprev = NULL;
P_SetThingPosition(mo);
}
else
mo->flags = flags;
return 0;
}
int mobj_skin_getter(lua_State *L)
{
mobj_t *mo = GETMO();
if (!mo->skin)
return 0;
// HUD ONLY!!!!!!!!!!
if (hud_running && cv_luaimmersion.value)
{
lua_pushstring(L, K_GetMobjSkin(mo)->name);
}
else
lua_pushstring(L, ((skin_t *)mo->skin)->name);
return 1;
}
int mobj_skin_setter(lua_State *L)
{
mobj_t *mo = GETMO();
INT32 i;
char skin[SKINNAMESIZE+1]; // all skin names are limited to this length
strlcpy(skin, luaL_checkstring(L, 2), sizeof skin);
strlwr(skin); // all skin names are lowercase
for (i = 0; i < numskins; i++)
{
if (fastcmp(skins[i].name, skin))
{
mo->skin = &skins[i];
return 0;
}
}
return luaL_error(L, "mobj.skin '%s' not found!", skin);
}
int mobj_localskin_getter(lua_State *L)
{
mobj_t *mo = GETMO();
if (mo->localskin)
lua_pushstring(L, ((skin_t *)mo->localskin)->name);
else
lua_pushnil(L);
return 1;
}
int mobj_localskin_setter(lua_State *L)
{
mobj_t *mo = GETMO();
if (mo->player)
{
SetLocalPlayerSkin(mo->player - players, luaL_optstring(L, 2, "none"), NULL);
}
else
{
INT32 i;
char skin[SKINNAMESIZE+1]; // all skin names are limited to this length
strlcpy(skin, luaL_optstring(L, 2, "none"), sizeof skin);
strlwr(skin); // all skin names are lowercase
if (!fasticmp(skin, "none"))
{
// Try localskins
for (i = 0; i < numlocalskins; i++)
{
if (fasticmp(localskins[i].name, skin))
{
mo->localskin = &localskins[i];
mo->skinlocal = true;
return 0;
}
}
// Try other skins
for (i = 0; i < numskins; i++)
{
if (fasticmp(skins[i].name, skin))
{
mo->localskin = &skins[i];
mo->skinlocal = false;
return 0;
}
}
}
else
{
mo->localskin = 0;
mo->skinlocal = false;
}
}
return 0;
}
int mobj_color_setter(lua_State *L)
{
mobj_t *mo = GETMO();
UINT8 newcolor = (UINT8)luaL_checkinteger(L, 2);
if (newcolor >= MAXTRANSLATIONS)
return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, MAXTRANSLATIONS-1);
mo->color = newcolor;
return 0;
}
int mobj_blendmode_setter(lua_State *L)
{
mobj_t *mo = GETMO();
INT32 blendmode = (INT32)luaL_checkinteger(L, 2);
if (blendmode < 0 || blendmode > AST_OVERLAY)
return luaL_error(L, "mobj.blendmode %d out of range (0 - %d).", blendmode, AST_OVERLAY);
mo->blendmode = blendmode;
return 0;
}
int mobj_hnext_setter(lua_State *L)
{
mobj_t *mo = GETMO();
if (lua_isnil(L, 2))
P_SetTarget(&mo->hnext, NULL);
else
{
mobj_t *hnext = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
P_SetTarget(&mo->hnext, hnext);
}
return 0;
}
int mobj_hprev_setter(lua_State *L)
{
mobj_t *mo = GETMO();
if (lua_isnil(L, 2))
P_SetTarget(&mo->hprev, NULL);
else
{
mobj_t *hprev = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
P_SetTarget(&mo->hprev, hprev);
}
return 0;
}
int mobj_type_setter(lua_State *L)
{
mobj_t *mo = GETMO();
mobjtype_t newtype = luaL_checkinteger(L, 2);
if (newtype >= NUMMOBJTYPES)
return luaL_error(L, "mobj.type %d out of range (0 - %d).", newtype, NUMMOBJTYPES-1);
mo->type = newtype;
mo->info = &mobjinfo[newtype];
P_SetScale(mo, mo->scale);
return 0;
}
int mobj_info_getter(lua_State *L)
{
mobj_t *mo = GETMO();
LUA_PushUserdata(L, &mobjinfo[mo->type], META_MOBJINFO);
return 1;
}
int mobj_target_getter(lua_State *L)
{
mobj_t *mo = GETMO();
if (mo->target && P_MobjWasRemoved(mo->target))
{ // don't put invalid mobj back into Lua.
P_SetTarget(&mo->target, NULL);
return 0;
}
LUA_PushUserdata(L, mo->target, META_MOBJ);
return 1;
}
int mobj_target_setter(lua_State *L)
{
mobj_t *mo = GETMO();
if (lua_isnil(L, 2))
P_SetTarget(&mo->target, NULL);
else
{
mobj_t *target = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
P_SetTarget(&mo->target, target);
}
return 0;
}
int mobj_spawnpoint_setter(lua_State *L)
{
mobj_t *mo = GETMO();
if (lua_isnil(L, 2))
mo->spawnpoint = NULL;
else
{
mapthing_t *spawnpoint = *((mapthing_t **)luaL_checkudata(L, 2, META_MAPTHING));
mo->spawnpoint = spawnpoint;
}
return 0;
}
int mobj_tracer_getter(lua_State *L)
{
mobj_t *mo = GETMO();
if (mo->tracer && P_MobjWasRemoved(mo->tracer))
{ // don't put invalid mobj back into Lua.
P_SetTarget(&mo->tracer, NULL);
return 0;
}
LUA_PushUserdata(L, mo->tracer, META_MOBJ);
return 1;
}
int mobj_tracer_setter(lua_State *L)
{
mobj_t *mo = GETMO();
if (lua_isnil(L, 2))
P_SetTarget(&mo->tracer, NULL);
else
{
mobj_t *tracer = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
P_SetTarget(&mo->tracer, tracer);
}
return 0;
}
int mobj_scale_setter(lua_State *L)
{
mobj_t *mo = GETMO();
fixed_t scale = luaL_checkfixed(L, 2);
if (scale < FRACUNIT/100)
scale = FRACUNIT/100;
mo->destscale = scale;
P_SetScale(mo, scale);
mo->old_scale = scale;
return 0;
}
int mobj_destscale_setter(lua_State *L)
{
mobj_t *mo = GETMO();
fixed_t scale = luaL_checkfixed(L, 2);
if (scale < FRACUNIT/100)
scale = FRACUNIT/100;
mo->destscale = scale;
return 0;
}
// WARNING: Not synch safe!
// Don't use this field in game logic code!
int mobj_rollsum_getter(lua_State *L)
{
mobj_t *mo = GETMO();
angle_t pitchnroll = P_MobjPitchAndRoll(mo);
angle_t rollsum = mo->rollangle + pitchnroll;
if (mo->player)
{
rollsum += R_PlayerSliptideAngle(mo->player);
}
lua_pushangle(L, rollsum);
return 1;
}
static int mobj_get(lua_State *L)
{
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
// We still gonna have 2 strcmp for "valid" field
const char *field = luaL_checkstring(L, 2);
lua_settop(L, 2);
if (!mo) {
if (fastcmp(field, "valid")) {
lua_pushboolean(L, 0);
return 1;
}
return LUA_ErrInvalid(L, "mobj_t");
} else if (fastcmp(field, "valid")) {
lua_pushboolean(L, 1);
return 1;
}
lua_getmetatable(L, 1);
lua_pushvalue(L, 2); // Push field name
lua_rawget(L, -2); // Get getter/setter table from metatable
// If field exists, run getter for it
if (!lua_isnil(L, -1)) {
//CONS_Printf("Running getter for field %s\n", field);
lua_rawgeti(L, -1, UDATALIB_GETTER);
lua_pushlightuserdata(L, mo);
lua_call(L, 1, 1);
//CONS_Printf("Getter returned %s\n", lua_typename(L, lua_type(L, -1)));
return 1;
}
lua_pop(L, 1);
//CONS_Printf("Getting custom field %s\n", field);
// Othervise, return extra value
// extra custom variables in Lua memory
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, mo);
lua_rawget(L, -2);
if (!lua_istable(L, -1)) { // no extra values table
CONS_Debug(DBG_LUA, M_GetText("'%s' has no extvars table or field named '%s'; returning nil.\n"), "mobj_t", lua_tostring(L, 2));
return 0;
}
lua_pushvalue(L, 2); // field name
lua_gettable(L, -2);
if (lua_isnil(L, -1)) // no value for this field
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobj_t", lua_tostring(L, 2));
return 1;
}
#define NOSET luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly.", mobj_opt[field])
#define NOSETPOS luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("P_Move") ", " LUA_QL("P_TryMove") ", or " LUA_QL("P_SetOrigin") ", or " LUA_QL("P_MoveOrigin") " instead.", mobj_opt[field])
static int mobj_set(lua_State *L)
{
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
lua_settop(L, 3);
if (!mo)
return LUA_ErrInvalid(L, "mobj_t");
if (hud_running)
return luaL_error(L, "Do not alter mobj_t in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter mobj_t in BuildCMD code!");
lua_getmetatable(L, 1); // Push metatable
lua_pushvalue(L, 2); // Push field name
lua_rawget(L, -2); // Get getter/setter table from metatable
// If field exists, run setter for it
if (!lua_isnil(L, -1)) {
//CONS_Printf("Running setter for field %s\n", luaL_checkstring(L, 2));
lua_rawgeti(L, -1, UDATALIB_SETTER);
lua_pushlightuserdata(L, mo);
lua_pushvalue(L, 3);
lua_call(L, 2, 0);
return 0;
}
lua_pop(L, 1);
//CONS_Printf("Adding custom field %s\n", luaL_checkstring(L, 2));
// Otherwise, set custom field
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, mo);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) {
// This index doesn't have a table for extra values yet, let's make one.
lua_pop(L, 1);
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobj_t", lua_tostring(L, 2));
lua_newtable(L);
lua_pushlightuserdata(L, mo);
lua_pushvalue(L, -2); // ext value table
lua_rawset(L, -4); // LREG_EXTVARS table
}
lua_pushvalue(L, 2); // key
lua_pushvalue(L, 3); // value to store
lua_settable(L, -3);
lua_pop(L, 2);
return 0;
}
#undef UNIMPLEMENTED
#undef NOSET
#undef NOSETPOS
#undef NOFIELD
enum mapthing_e {
mapthing_valid = 0,
mapthing_x,
mapthing_y,
mapthing_angle,
mapthing_type,
mapthing_options,
mapthing_z,
mapthing_extrainfo,
mapthing_mobj,
};
const char *const mapthing_opt[] = {
"valid",
"x",
"y",
"angle",
"type",
"options",
"z",
"extrainfo",
"mobj",
NULL,
};
static int mapthing_fields_ref = LUA_NOREF;
static int mapthing_get(lua_State *L)
{
mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
enum mapthing_e field = Lua_optoption(L, 2, -1, mapthing_fields_ref);
lua_settop(L, 2);
if (!mt) {
if (field == mapthing_valid) {
lua_pushboolean(L, false);
return 1;
}
if (devparm)
return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
return 0;
}
switch (field)
{
case mapthing_valid:
lua_pushboolean(L, true);
break;
case mapthing_x:
lua_pushinteger(L, mt->x);
break;
case mapthing_y:
lua_pushinteger(L, mt->y);
break;
case mapthing_angle:
lua_pushinteger(L, mt->angle);
break;
case mapthing_type:
lua_pushinteger(L, mt->type);
break;
case mapthing_options:
lua_pushinteger(L, mt->options);
break;
case mapthing_z:
lua_pushinteger(L, mt->z);
break;
case mapthing_extrainfo:
lua_pushinteger(L, mt->extrainfo);
break;
case mapthing_mobj:
LUA_PushUserdata(L, mt->mobj, META_MOBJ);
break;
default:
if (devparm)
return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, lua_tostring(L, 2));
else
return 0;
}
return 1;
}
static int mapthing_set(lua_State *L)
{
mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
enum mapthing_e field = Lua_optoption(L, 2, -1, mapthing_fields_ref);
lua_settop(L, 3);
if (!mt)
return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
if (hud_running)
return luaL_error(L, "Do not alter mapthing_t in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter mapthing_t in BuildCMD code!");
switch (field)
{
case mapthing_x:
mt->x = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_y:
mt->y = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_angle:
mt->angle = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_type:
mt->type = (UINT16)luaL_checkinteger(L, 3);
break;
case mapthing_options:
mt->options = (UINT16)luaL_checkinteger(L, 3);
break;
case mapthing_z:
mt->z = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_extrainfo:
{
INT32 extrainfo = luaL_checkinteger(L, 3);
if (extrainfo & ~15)
return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15);
mt->extrainfo = (UINT8)extrainfo;
break;
}
case mapthing_mobj:
mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
break;
default:
return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, lua_tostring(L, 2));
}
return 0;
}
static int lib_iterateMapthings(lua_State *L)
{
size_t i = 0;
if (lua_gettop(L) < 2)
return luaL_error(L, "Don't call mapthings.iterate() directly, use it as 'for mapthing in mapthings.iterate do <block> end'.");
lua_settop(L, 2);
lua_remove(L, 1); // state is unused.
if (!lua_isnil(L, 1))
i = (size_t)(*((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING)) - mapthings) + 1;
if (i < nummapthings)
{
LUA_PushUserdata(L, &mapthings[i], META_MAPTHING);
return 1;
}
return 0;
}
static int lib_getMapthing(lua_State *L)
{
int field;
lua_settop(L, 2);
lua_remove(L, 1); // dummy userdata table is unused.
if (lua_isnumber(L, 1))
{
size_t i = lua_tointeger(L, 1);
if (i >= nummapthings)
return 0;
LUA_PushUserdata(L, &mapthings[i], META_MAPTHING);
return 1;
}
field = luaL_checkoption(L, 1, NULL, array_opt);
switch(field)
{
case 0: // iterate
lua_pushcfunction(L, lib_iterateMapthings);
return 1;
}
return 0;
}
FUNCINLINE static ATTRINLINE int lib_nummapthings(lua_State *L)
{
lua_pushinteger(L, nummapthings);
return 1;
}
int LUA_MobjLib(lua_State *L)
{
luaL_newmetatable(L, META_MOBJ);
lua_pushcfunction(L, mobj_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, mobj_set);
lua_setfield(L, -2, "__newindex");
udatalib_addfields(L, -1, mobj_fields);
lua_pop(L,1);
luaL_newmetatable(L, META_MAPTHING);
lua_pushcfunction(L, mapthing_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, mapthing_set);
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
mapthing_fields_ref = Lua_CreateFieldTable(L, mapthing_opt);
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getMapthing);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_nummapthings);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "mapthings");
return 0;
}