1016 lines
29 KiB
C
1016 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);
|
|
if (mo->player)
|
|
P_ForceLocalAngle(mo->player, mo->angle);
|
|
|
|
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 = NULL;
|
|
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;
|
|
}
|