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
1022 lines
29 KiB
C
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;
|
|
}
|