mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-25 05:41:42 +00:00
1229 lines
28 KiB
C
1229 lines
28 KiB
C
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2012-2016 by John "JTE" Muniz.
|
|
// Copyright (C) 2012-2023 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 "doomdef.h"
|
|
#include "fastcmp.h"
|
|
#include "r_data.h"
|
|
#include "r_skins.h"
|
|
#include "r_translation.h"
|
|
#include "p_local.h"
|
|
#include "g_game.h"
|
|
#include "p_setup.h"
|
|
|
|
#include "lua_script.h"
|
|
#include "lua_libs.h"
|
|
#include "lua_hud.h" // hud_running errors
|
|
#include "lua_hook.h" // hook_cmd_running errors
|
|
|
|
enum mobj_e {
|
|
mobj_valid = 0,
|
|
mobj_x,
|
|
mobj_y,
|
|
mobj_z,
|
|
mobj_snext,
|
|
mobj_sprev,
|
|
mobj_angle,
|
|
mobj_pitch,
|
|
mobj_roll,
|
|
mobj_spriteroll,
|
|
mobj_rollangle, // backwards compat
|
|
mobj_sprite,
|
|
mobj_frame,
|
|
mobj_sprite2,
|
|
mobj_anim_duration,
|
|
mobj_spritexscale,
|
|
mobj_spriteyscale,
|
|
mobj_spritexoffset,
|
|
mobj_spriteyoffset,
|
|
mobj_floorspriteslope,
|
|
mobj_drawonlyforplayer,
|
|
mobj_dontdrawforviewmobj,
|
|
mobj_touching_sectorlist,
|
|
mobj_subsector,
|
|
mobj_floorz,
|
|
mobj_ceilingz,
|
|
mobj_floorrover,
|
|
mobj_ceilingrover,
|
|
mobj_radius,
|
|
mobj_height,
|
|
mobj_momx,
|
|
mobj_momy,
|
|
mobj_momz,
|
|
mobj_pmomz,
|
|
mobj_tics,
|
|
mobj_state,
|
|
mobj_flags,
|
|
mobj_flags2,
|
|
mobj_eflags,
|
|
mobj_renderflags,
|
|
mobj_skin,
|
|
mobj_color,
|
|
mobj_translation,
|
|
mobj_blendmode,
|
|
mobj_alpha,
|
|
mobj_bnext,
|
|
mobj_bprev,
|
|
mobj_hnext,
|
|
mobj_hprev,
|
|
mobj_type,
|
|
mobj_info,
|
|
mobj_health,
|
|
mobj_movedir,
|
|
mobj_movecount,
|
|
mobj_target,
|
|
mobj_reactiontime,
|
|
mobj_threshold,
|
|
mobj_player,
|
|
mobj_lastlook,
|
|
mobj_spawnpoint,
|
|
mobj_tracer,
|
|
mobj_friction,
|
|
mobj_movefactor,
|
|
mobj_fuse,
|
|
mobj_watertop,
|
|
mobj_waterbottom,
|
|
mobj_mobjnum,
|
|
mobj_scale,
|
|
mobj_destscale,
|
|
mobj_scalespeed,
|
|
mobj_extravalue1,
|
|
mobj_extravalue2,
|
|
mobj_cusval,
|
|
mobj_cvmem,
|
|
mobj_standingslope,
|
|
mobj_colorized,
|
|
mobj_mirrored,
|
|
mobj_shadowscale,
|
|
mobj_dispoffset
|
|
};
|
|
|
|
static const char *const mobj_opt[] = {
|
|
"valid",
|
|
"x",
|
|
"y",
|
|
"z",
|
|
"snext",
|
|
"sprev",
|
|
"angle",
|
|
"pitch",
|
|
"roll",
|
|
"spriteroll",
|
|
"rollangle", // backwards compat
|
|
"sprite",
|
|
"frame",
|
|
"sprite2",
|
|
"anim_duration",
|
|
"spritexscale",
|
|
"spriteyscale",
|
|
"spritexoffset",
|
|
"spriteyoffset",
|
|
"floorspriteslope",
|
|
"drawonlyforplayer",
|
|
"dontdrawforviewmobj",
|
|
"touching_sectorlist",
|
|
"subsector",
|
|
"floorz",
|
|
"ceilingz",
|
|
"floorrover",
|
|
"ceilingrover",
|
|
"radius",
|
|
"height",
|
|
"momx",
|
|
"momy",
|
|
"momz",
|
|
"pmomz",
|
|
"tics",
|
|
"state",
|
|
"flags",
|
|
"flags2",
|
|
"eflags",
|
|
"renderflags",
|
|
"skin",
|
|
"color",
|
|
"translation",
|
|
"blendmode",
|
|
"alpha",
|
|
"bnext",
|
|
"bprev",
|
|
"hnext",
|
|
"hprev",
|
|
"type",
|
|
"info",
|
|
"health",
|
|
"movedir",
|
|
"movecount",
|
|
"target",
|
|
"reactiontime",
|
|
"threshold",
|
|
"player",
|
|
"lastlook",
|
|
"spawnpoint",
|
|
"tracer",
|
|
"friction",
|
|
"movefactor",
|
|
"fuse",
|
|
"watertop",
|
|
"waterbottom",
|
|
"mobjnum",
|
|
"scale",
|
|
"destscale",
|
|
"scalespeed",
|
|
"extravalue1",
|
|
"extravalue2",
|
|
"cusval",
|
|
"cvmem",
|
|
"standingslope",
|
|
"colorized",
|
|
"mirrored",
|
|
"shadowscale",
|
|
"dispoffset",
|
|
NULL};
|
|
|
|
#define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field])
|
|
|
|
static int mobj_fields_ref = LUA_NOREF;
|
|
|
|
static int mobj_get(lua_State *L)
|
|
{
|
|
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
|
|
enum mobj_e field = Lua_optoption(L, 2, -1, mobj_fields_ref);
|
|
lua_settop(L, 2);
|
|
|
|
if (!mo || !ISINLEVEL) {
|
|
if (field == mobj_valid) {
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
if (!mo) {
|
|
return LUA_ErrInvalid(L, "mobj_t");
|
|
} else
|
|
return luaL_error(L, "Do not access an mobj_t field outside a level!");
|
|
}
|
|
|
|
switch(field)
|
|
{
|
|
case mobj_valid:
|
|
lua_pushboolean(L, 1);
|
|
break;
|
|
case mobj_x:
|
|
lua_pushfixed(L, mo->x);
|
|
break;
|
|
case mobj_y:
|
|
lua_pushfixed(L, mo->y);
|
|
break;
|
|
case mobj_z:
|
|
lua_pushfixed(L, mo->z);
|
|
break;
|
|
case mobj_snext:
|
|
LUA_PushUserdata(L, mo->snext, META_MOBJ);
|
|
break;
|
|
case mobj_sprev:
|
|
// sprev is actually the previous mobj's snext pointer,
|
|
// or the subsector->sector->thing_list if there is no previous mobj,
|
|
// i.e. it will always ultimately point to THIS mobj -- so that's actually not useful to Lua and won't be included.
|
|
return UNIMPLEMENTED;
|
|
case mobj_angle:
|
|
lua_pushangle(L, mo->angle);
|
|
break;
|
|
case mobj_pitch:
|
|
lua_pushangle(L, mo->pitch);
|
|
break;
|
|
case mobj_roll:
|
|
lua_pushangle(L, mo->roll);
|
|
break;
|
|
case mobj_spriteroll:
|
|
case mobj_rollangle: // backwards compat
|
|
lua_pushangle(L, mo->spriteroll);
|
|
break;
|
|
case mobj_sprite:
|
|
lua_pushinteger(L, mo->sprite);
|
|
break;
|
|
case mobj_frame:
|
|
lua_pushinteger(L, mo->frame);
|
|
break;
|
|
case mobj_sprite2:
|
|
lua_pushinteger(L, mo->sprite2);
|
|
break;
|
|
case mobj_anim_duration:
|
|
lua_pushinteger(L, mo->anim_duration);
|
|
break;
|
|
case mobj_spritexscale:
|
|
lua_pushfixed(L, mo->spritexscale);
|
|
break;
|
|
case mobj_spriteyscale:
|
|
lua_pushfixed(L, mo->spriteyscale);
|
|
break;
|
|
case mobj_spritexoffset:
|
|
lua_pushfixed(L, mo->spritexoffset);
|
|
break;
|
|
case mobj_spriteyoffset:
|
|
lua_pushfixed(L, mo->spriteyoffset);
|
|
break;
|
|
case mobj_floorspriteslope:
|
|
LUA_PushUserdata(L, mo->floorspriteslope, META_SLOPE);
|
|
break;
|
|
case mobj_drawonlyforplayer:
|
|
LUA_PushUserdata(L, mo->drawonlyforplayer, META_PLAYER);
|
|
break;
|
|
case mobj_dontdrawforviewmobj:
|
|
if (mo->dontdrawforviewmobj && P_MobjWasRemoved(mo->dontdrawforviewmobj))
|
|
{ // don't put invalid mobj back into Lua.
|
|
P_SetTarget(&mo->dontdrawforviewmobj, NULL);
|
|
return 0;
|
|
}
|
|
LUA_PushUserdata(L, mo->dontdrawforviewmobj, META_MOBJ);
|
|
break;
|
|
case mobj_touching_sectorlist:
|
|
return UNIMPLEMENTED;
|
|
case mobj_subsector:
|
|
LUA_PushUserdata(L, mo->subsector, META_SUBSECTOR);
|
|
break;
|
|
case mobj_floorz:
|
|
lua_pushfixed(L, mo->floorz);
|
|
break;
|
|
case mobj_ceilingz:
|
|
lua_pushfixed(L, mo->ceilingz);
|
|
break;
|
|
case mobj_floorrover:
|
|
LUA_PushUserdata(L, mo->floorrover, META_FFLOOR);
|
|
break;
|
|
case mobj_ceilingrover:
|
|
LUA_PushUserdata(L, mo->ceilingrover, META_FFLOOR);
|
|
break;
|
|
case mobj_radius:
|
|
lua_pushfixed(L, mo->radius);
|
|
break;
|
|
case mobj_height:
|
|
lua_pushfixed(L, mo->height);
|
|
break;
|
|
case mobj_momx:
|
|
lua_pushfixed(L, mo->momx);
|
|
break;
|
|
case mobj_momy:
|
|
lua_pushfixed(L, mo->momy);
|
|
break;
|
|
case mobj_momz:
|
|
lua_pushfixed(L, mo->momz);
|
|
break;
|
|
case mobj_pmomz:
|
|
lua_pushfixed(L, mo->pmomz);
|
|
break;
|
|
case mobj_tics:
|
|
lua_pushinteger(L, mo->tics);
|
|
break;
|
|
case mobj_state: // state number, not struct
|
|
lua_pushinteger(L, mo->state-states);
|
|
break;
|
|
case mobj_flags:
|
|
lua_pushinteger(L, mo->flags);
|
|
break;
|
|
case mobj_flags2:
|
|
lua_pushinteger(L, mo->flags2);
|
|
break;
|
|
case mobj_eflags:
|
|
lua_pushinteger(L, mo->eflags);
|
|
break;
|
|
case mobj_renderflags:
|
|
lua_pushinteger(L, mo->renderflags);
|
|
break;
|
|
case mobj_skin: // skin name or nil, not struct
|
|
if (!mo->skin)
|
|
return 0;
|
|
lua_pushstring(L, ((skin_t *)mo->skin)->name);
|
|
break;
|
|
case mobj_color:
|
|
lua_pushinteger(L, mo->color);
|
|
break;
|
|
case mobj_translation:
|
|
if (mo->translation)
|
|
{
|
|
const char *name = R_GetCustomTranslationName(mo->translation);
|
|
if (name)
|
|
lua_pushstring(L, name);
|
|
break;
|
|
}
|
|
lua_pushnil(L);
|
|
break;
|
|
case mobj_blendmode:
|
|
lua_pushinteger(L, mo->blendmode);
|
|
break;
|
|
case mobj_alpha:
|
|
lua_pushinteger(L, mo->alpha);
|
|
break;
|
|
case mobj_bnext:
|
|
if (mo->blocknode && mo->blocknode->bnext) {
|
|
LUA_PushUserdata(L, mo->blocknode->bnext->mobj, META_MOBJ);
|
|
break;
|
|
}
|
|
else
|
|
return 0;
|
|
case mobj_bprev:
|
|
// bprev -- same deal as sprev above, but for the blockmap.
|
|
return UNIMPLEMENTED;
|
|
case mobj_hnext:
|
|
if (mo->hnext && P_MobjWasRemoved(mo->hnext))
|
|
{ // don't put invalid mobj back into Lua.
|
|
P_SetTarget(&mo->hnext, NULL);
|
|
return 0;
|
|
}
|
|
LUA_PushUserdata(L, mo->hnext, META_MOBJ);
|
|
break;
|
|
case mobj_hprev:
|
|
if (mo->hprev && P_MobjWasRemoved(mo->hprev))
|
|
{ // don't put invalid mobj back into Lua.
|
|
P_SetTarget(&mo->hprev, NULL);
|
|
return 0;
|
|
}
|
|
LUA_PushUserdata(L, mo->hprev, META_MOBJ);
|
|
break;
|
|
case mobj_type:
|
|
lua_pushinteger(L, mo->type);
|
|
break;
|
|
case mobj_info:
|
|
LUA_PushUserdata(L, &mobjinfo[mo->type], META_MOBJINFO);
|
|
break;
|
|
case mobj_health:
|
|
lua_pushinteger(L, mo->health);
|
|
break;
|
|
case mobj_movedir:
|
|
lua_pushinteger(L, mo->movedir);
|
|
break;
|
|
case mobj_movecount:
|
|
lua_pushinteger(L, mo->movecount);
|
|
break;
|
|
case mobj_target:
|
|
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);
|
|
break;
|
|
case mobj_reactiontime:
|
|
lua_pushinteger(L, mo->reactiontime);
|
|
break;
|
|
case mobj_threshold:
|
|
lua_pushinteger(L, mo->threshold);
|
|
break;
|
|
case mobj_player:
|
|
LUA_PushUserdata(L, mo->player, META_PLAYER);
|
|
break;
|
|
case mobj_lastlook:
|
|
lua_pushinteger(L, mo->lastlook);
|
|
break;
|
|
case mobj_spawnpoint:
|
|
LUA_PushUserdata(L, mo->spawnpoint, META_MAPTHING);
|
|
break;
|
|
case mobj_tracer:
|
|
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);
|
|
break;
|
|
case mobj_friction:
|
|
lua_pushfixed(L, mo->friction);
|
|
break;
|
|
case mobj_movefactor:
|
|
lua_pushfixed(L, mo->movefactor);
|
|
break;
|
|
case mobj_fuse:
|
|
lua_pushinteger(L, mo->fuse);
|
|
break;
|
|
case mobj_watertop:
|
|
lua_pushfixed(L, mo->watertop);
|
|
break;
|
|
case mobj_waterbottom:
|
|
lua_pushfixed(L, mo->waterbottom);
|
|
break;
|
|
case mobj_mobjnum:
|
|
// mobjnum is a networking thing generated for $$$.sav
|
|
// and therefore shouldn't be used by Lua.
|
|
return UNIMPLEMENTED;
|
|
case mobj_scale:
|
|
lua_pushfixed(L, mo->scale);
|
|
break;
|
|
case mobj_destscale:
|
|
lua_pushfixed(L, mo->destscale);
|
|
break;
|
|
case mobj_scalespeed:
|
|
lua_pushfixed(L, mo->scalespeed);
|
|
break;
|
|
case mobj_extravalue1:
|
|
lua_pushinteger(L, mo->extravalue1);
|
|
break;
|
|
case mobj_extravalue2:
|
|
lua_pushinteger(L, mo->extravalue2);
|
|
break;
|
|
case mobj_cusval:
|
|
lua_pushinteger(L, mo->cusval);
|
|
break;
|
|
case mobj_cvmem:
|
|
lua_pushinteger(L, mo->cvmem);
|
|
break;
|
|
case mobj_standingslope:
|
|
LUA_PushUserdata(L, mo->standingslope, META_SLOPE);
|
|
break;
|
|
case mobj_colorized:
|
|
lua_pushboolean(L, mo->colorized);
|
|
break;
|
|
case mobj_mirrored:
|
|
lua_pushboolean(L, mo->mirrored);
|
|
break;
|
|
case mobj_shadowscale:
|
|
lua_pushfixed(L, mo->shadowscale);
|
|
break;
|
|
case mobj_dispoffset:
|
|
lua_pushinteger(L, mo->dispoffset);
|
|
break;
|
|
default: // 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));
|
|
break;
|
|
}
|
|
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));
|
|
enum mobj_e field = Lua_optoption(L, 2, mobj_valid, mobj_fields_ref);
|
|
lua_settop(L, 3);
|
|
|
|
INLEVEL
|
|
|
|
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 CMD building code!");
|
|
|
|
switch(field)
|
|
{
|
|
case mobj_valid:
|
|
return NOSET;
|
|
case mobj_x:
|
|
return NOSETPOS;
|
|
case mobj_y:
|
|
return NOSETPOS;
|
|
case mobj_z:
|
|
{
|
|
// z doesn't cross sector bounds so it's okay.
|
|
mobj_t *ptmthing = tmthing;
|
|
mo->z = luaL_checkfixed(L, 3);
|
|
P_CheckPosition(mo, mo->x, mo->y);
|
|
mo->floorz = tmfloorz;
|
|
mo->ceilingz = tmceilingz;
|
|
mo->floorrover = tmfloorrover;
|
|
mo->ceilingrover = tmceilingrover;
|
|
P_SetTarget(&tmthing, ptmthing);
|
|
break;
|
|
}
|
|
case mobj_snext:
|
|
return NOSETPOS;
|
|
case mobj_sprev:
|
|
return UNIMPLEMENTED;
|
|
case mobj_angle:
|
|
mo->angle = luaL_checkangle(L, 3);
|
|
if (mo->player)
|
|
P_SetPlayerAngle(mo->player, mo->angle);
|
|
break;
|
|
case mobj_pitch:
|
|
mo->pitch = luaL_checkangle(L, 3);
|
|
break;
|
|
case mobj_roll:
|
|
mo->roll = luaL_checkangle(L, 3);
|
|
break;
|
|
case mobj_spriteroll:
|
|
case mobj_rollangle: // backwards compat
|
|
mo->spriteroll = luaL_checkangle(L, 3);
|
|
break;
|
|
case mobj_sprite:
|
|
mo->sprite = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_frame:
|
|
mo->frame = (UINT32)luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_sprite2:
|
|
mo->sprite2 = P_GetSkinSprite2(((skin_t *)mo->skin), (UINT8)luaL_checkinteger(L, 3), mo->player);
|
|
break;
|
|
case mobj_anim_duration:
|
|
mo->anim_duration = (UINT16)luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_spritexscale:
|
|
mo->spritexscale = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_spriteyscale:
|
|
mo->spriteyscale = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_spritexoffset:
|
|
mo->spritexoffset = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_spriteyoffset:
|
|
mo->spriteyoffset = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_floorspriteslope:
|
|
return NOSET;
|
|
case mobj_drawonlyforplayer:
|
|
if (lua_isnil(L, 3))
|
|
mo->drawonlyforplayer = NULL;
|
|
else
|
|
{
|
|
player_t *drawonlyforplayer = *((player_t **)luaL_checkudata(L, 3, META_PLAYER));
|
|
mo->drawonlyforplayer = drawonlyforplayer;
|
|
}
|
|
break;
|
|
case mobj_dontdrawforviewmobj:
|
|
if (lua_isnil(L, 3))
|
|
P_SetTarget(&mo->dontdrawforviewmobj, NULL);
|
|
else
|
|
{
|
|
mobj_t *dontdrawforviewmobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
|
|
P_SetTarget(&mo->dontdrawforviewmobj, dontdrawforviewmobj);
|
|
}
|
|
break;
|
|
case mobj_touching_sectorlist:
|
|
return UNIMPLEMENTED;
|
|
case mobj_subsector:
|
|
return NOSETPOS;
|
|
case mobj_floorz:
|
|
return NOSETPOS;
|
|
case mobj_ceilingz:
|
|
return NOSETPOS;
|
|
case mobj_floorrover:
|
|
return NOSET;
|
|
case mobj_ceilingrover:
|
|
return NOSET;
|
|
case mobj_radius:
|
|
{
|
|
mobj_t *ptmthing = tmthing;
|
|
mo->radius = luaL_checkfixed(L, 3);
|
|
if (mo->radius < 0)
|
|
mo->radius = 0;
|
|
P_CheckPosition(mo, mo->x, mo->y);
|
|
mo->floorz = tmfloorz;
|
|
mo->ceilingz = tmceilingz;
|
|
mo->floorrover = tmfloorrover;
|
|
mo->ceilingrover = tmceilingrover;
|
|
P_SetTarget(&tmthing, ptmthing);
|
|
break;
|
|
}
|
|
case mobj_height:
|
|
{
|
|
mobj_t *ptmthing = tmthing;
|
|
mo->height = luaL_checkfixed(L, 3);
|
|
if (mo->height < 0)
|
|
mo->height = 0;
|
|
P_CheckPosition(mo, mo->x, mo->y);
|
|
mo->floorz = tmfloorz;
|
|
mo->ceilingz = tmceilingz;
|
|
mo->floorrover = tmfloorrover;
|
|
mo->ceilingrover = tmceilingrover;
|
|
P_SetTarget(&tmthing, ptmthing);
|
|
break;
|
|
}
|
|
case mobj_momx:
|
|
mo->momx = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_momy:
|
|
mo->momy = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_momz:
|
|
mo->momz = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_pmomz:
|
|
mo->pmomz = luaL_checkfixed(L, 3);
|
|
mo->eflags |= MFE_APPLYPMOMZ;
|
|
break;
|
|
case mobj_tics:
|
|
mo->tics = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_state: // set state by enum
|
|
P_SetMobjState(mo, luaL_checkinteger(L, 3));
|
|
break;
|
|
case mobj_flags: // special handling for MF_NOBLOCKMAP and MF_NOSECTOR
|
|
{
|
|
UINT32 flags = luaL_checkinteger(L, 3);
|
|
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;
|
|
P_SetThingPosition(mo);
|
|
}
|
|
else
|
|
mo->flags = flags;
|
|
break;
|
|
}
|
|
case mobj_flags2:
|
|
mo->flags2 = (UINT32)luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_eflags:
|
|
mo->eflags = (UINT32)luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_renderflags:
|
|
mo->renderflags = (UINT32)luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_skin: // set skin by name
|
|
{
|
|
INT32 i;
|
|
char skin[SKINNAMESIZE+1]; // all skin names are limited to this length
|
|
strlcpy(skin, luaL_checkstring(L, 3), sizeof skin);
|
|
strlwr(skin); // all skin names are lowercase
|
|
for (i = 0; i < numskins; i++)
|
|
if (fastcmp(skins[i]->name, skin))
|
|
{
|
|
if (!mo->player || R_SkinUsable(mo->player-players, i))
|
|
mo->skin = skins[i];
|
|
return 0;
|
|
}
|
|
return luaL_error(L, "mobj.skin '%s' not found!", skin);
|
|
}
|
|
case mobj_color:
|
|
{
|
|
UINT16 newcolor = (UINT16)luaL_checkinteger(L, 3);
|
|
if (newcolor >= numskincolors)
|
|
return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, numskincolors-1);
|
|
mo->color = newcolor;
|
|
break;
|
|
}
|
|
case mobj_translation:
|
|
{
|
|
if (!lua_isnil(L, 3)) {
|
|
const char *tr = luaL_checkstring(L, 3);
|
|
int id = R_FindCustomTranslation(tr);
|
|
if (id != -1)
|
|
mo->translation = id;
|
|
else
|
|
return luaL_error(L, "invalid translation '%s'.", tr);
|
|
}
|
|
else
|
|
mo->translation = 0;
|
|
break;
|
|
}
|
|
case mobj_blendmode:
|
|
{
|
|
INT32 blendmode = (INT32)luaL_checkinteger(L, 3);
|
|
if (blendmode < 0 || blendmode > AST_OVERLAY)
|
|
return luaL_error(L, "mobj.blendmode %d out of range (0 - %d).", blendmode, AST_OVERLAY);
|
|
mo->blendmode = blendmode;
|
|
break;
|
|
}
|
|
case mobj_alpha:
|
|
INT32 alpha = (INT32)luaL_checkinteger(L, 3);
|
|
if (alpha < 0)
|
|
alpha = 0;
|
|
else if (alpha > FRACUNIT)
|
|
alpha = FRACUNIT;
|
|
mo->alpha = alpha;
|
|
break;
|
|
case mobj_bnext:
|
|
return NOSETPOS;
|
|
case mobj_bprev:
|
|
return UNIMPLEMENTED;
|
|
case mobj_hnext:
|
|
if (lua_isnil(L, 3))
|
|
P_SetTarget(&mo->hnext, NULL);
|
|
else
|
|
{
|
|
mobj_t *hnext = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
|
|
P_SetTarget(&mo->hnext, hnext);
|
|
}
|
|
break;
|
|
case mobj_hprev:
|
|
if (lua_isnil(L, 3))
|
|
P_SetTarget(&mo->hprev, NULL);
|
|
else
|
|
{
|
|
mobj_t *hprev = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
|
|
P_SetTarget(&mo->hprev, hprev);
|
|
}
|
|
break;
|
|
case mobj_type: // yeah sure, we'll let you change the mobj's type.
|
|
{
|
|
mobjtype_t newtype = luaL_checkinteger(L, 3);
|
|
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);
|
|
break;
|
|
}
|
|
case mobj_info:
|
|
return NOSET;
|
|
case mobj_health:
|
|
mo->health = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_movedir:
|
|
mo->movedir = (angle_t)luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_movecount:
|
|
mo->movecount = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_target:
|
|
if (lua_isnil(L, 3))
|
|
P_SetTarget(&mo->target, NULL);
|
|
else
|
|
{
|
|
mobj_t *target = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
|
|
P_SetTarget(&mo->target, target);
|
|
}
|
|
break;
|
|
case mobj_reactiontime:
|
|
mo->reactiontime = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_threshold:
|
|
mo->threshold = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_player:
|
|
return NOSET;
|
|
case mobj_lastlook:
|
|
mo->lastlook = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_spawnpoint:
|
|
if (lua_isnil(L, 3))
|
|
mo->spawnpoint = NULL;
|
|
else
|
|
{
|
|
mapthing_t *spawnpoint = *((mapthing_t **)luaL_checkudata(L, 3, META_MAPTHING));
|
|
mo->spawnpoint = spawnpoint;
|
|
}
|
|
break;
|
|
case mobj_tracer:
|
|
if (lua_isnil(L, 3))
|
|
P_SetTarget(&mo->tracer, NULL);
|
|
else
|
|
{
|
|
mobj_t *tracer = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
|
|
P_SetTarget(&mo->tracer, tracer);
|
|
}
|
|
break;
|
|
case mobj_friction:
|
|
mo->friction = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_movefactor:
|
|
mo->movefactor = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_fuse:
|
|
mo->fuse = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_watertop:
|
|
mo->watertop = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_waterbottom:
|
|
mo->waterbottom = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_mobjnum:
|
|
return UNIMPLEMENTED;
|
|
case mobj_scale:
|
|
{
|
|
fixed_t scale = luaL_checkfixed(L, 3);
|
|
if (scale < FRACUNIT/100)
|
|
scale = FRACUNIT/100;
|
|
mo->destscale = scale;
|
|
P_SetScale(mo, scale);
|
|
mo->old_scale = scale;
|
|
break;
|
|
}
|
|
case mobj_destscale:
|
|
{
|
|
fixed_t scale = luaL_checkfixed(L, 3);
|
|
if (scale < FRACUNIT/100)
|
|
scale = FRACUNIT/100;
|
|
mo->destscale = scale;
|
|
break;
|
|
}
|
|
case mobj_scalespeed:
|
|
mo->scalespeed = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_extravalue1:
|
|
mo->extravalue1 = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_extravalue2:
|
|
mo->extravalue2 = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_cusval:
|
|
mo->cusval = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_cvmem:
|
|
mo->cvmem = luaL_checkinteger(L, 3);
|
|
break;
|
|
case mobj_standingslope:
|
|
return NOSET;
|
|
case mobj_colorized:
|
|
mo->colorized = luaL_checkboolean(L, 3);
|
|
break;
|
|
case mobj_mirrored:
|
|
mo->mirrored = luaL_checkboolean(L, 3);
|
|
break;
|
|
case mobj_shadowscale:
|
|
mo->shadowscale = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mobj_dispoffset:
|
|
mo->dispoffset = luaL_checkinteger(L, 3);
|
|
break;
|
|
default:
|
|
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);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#undef UNIMPLEMENTED
|
|
#undef NOSET
|
|
#undef NOSETPOS
|
|
#undef NOFIELD
|
|
|
|
// args, i -> args[i]
|
|
static int thingargs_get(lua_State *L)
|
|
{
|
|
INT32 *args = *((INT32**)luaL_checkudata(L, 1, META_THINGARGS));
|
|
int i = luaL_checkinteger(L, 2);
|
|
if (i < 0 || i >= NUMMAPTHINGARGS)
|
|
return luaL_error(L, LUA_QL("mapthing_t.args") " index cannot be %d", i);
|
|
lua_pushinteger(L, args[i]);
|
|
return 1;
|
|
}
|
|
|
|
// #args -> NUMMAPTHINGARGS
|
|
static int thingargs_len(lua_State* L)
|
|
{
|
|
lua_pushinteger(L, NUMMAPTHINGARGS);
|
|
return 1;
|
|
}
|
|
|
|
// stringargs, i -> stringargs[i]
|
|
static int thingstringargs_get(lua_State *L)
|
|
{
|
|
char **stringargs = *((char***)luaL_checkudata(L, 1, META_THINGSTRINGARGS));
|
|
int i = luaL_checkinteger(L, 2);
|
|
if (i < 0 || i >= NUMMAPTHINGSTRINGARGS)
|
|
return luaL_error(L, LUA_QL("mapthing_t.stringargs") " index cannot be %d", i);
|
|
lua_pushstring(L, stringargs[i]);
|
|
return 1;
|
|
}
|
|
|
|
// #stringargs -> NUMMAPTHINGSTRINGARGS
|
|
static int thingstringargs_len(lua_State *L)
|
|
{
|
|
lua_pushinteger(L, NUMMAPTHINGSTRINGARGS);
|
|
return 1;
|
|
}
|
|
|
|
enum mapthing_e {
|
|
mapthing_valid = 0,
|
|
mapthing_x,
|
|
mapthing_y,
|
|
mapthing_angle,
|
|
mapthing_pitch,
|
|
mapthing_roll,
|
|
mapthing_type,
|
|
mapthing_options,
|
|
mapthing_scale,
|
|
mapthing_spritexscale,
|
|
mapthing_spriteyscale,
|
|
mapthing_z,
|
|
mapthing_extrainfo,
|
|
mapthing_tag,
|
|
mapthing_taglist,
|
|
mapthing_args,
|
|
mapthing_stringargs,
|
|
mapthing_mobj,
|
|
};
|
|
|
|
const char *const mapthing_opt[] = {
|
|
"valid",
|
|
"x",
|
|
"y",
|
|
"angle",
|
|
"pitch",
|
|
"roll",
|
|
"type",
|
|
"options",
|
|
"scale",
|
|
"spritexscale",
|
|
"spriteyscale",
|
|
"z",
|
|
"extrainfo",
|
|
"tag",
|
|
"taglist",
|
|
"args",
|
|
"stringargs",
|
|
"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;
|
|
}
|
|
|
|
if (field == (enum mapthing_e)-1)
|
|
return LUA_ErrInvalid(L, "fields");
|
|
|
|
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_pitch:
|
|
lua_pushinteger(L, mt->pitch);
|
|
break;
|
|
case mapthing_roll:
|
|
lua_pushinteger(L, mt->roll);
|
|
break;
|
|
case mapthing_type:
|
|
lua_pushinteger(L, mt->type);
|
|
break;
|
|
case mapthing_options:
|
|
lua_pushinteger(L, mt->options);
|
|
break;
|
|
case mapthing_scale:
|
|
lua_pushfixed(L, mt->scale);
|
|
break;
|
|
case mapthing_spritexscale:
|
|
lua_pushfixed(L, mt->spritexscale);
|
|
break;
|
|
case mapthing_spriteyscale:
|
|
lua_pushfixed(L, mt->spriteyscale);
|
|
break;
|
|
case mapthing_z:
|
|
lua_pushinteger(L, mt->z);
|
|
break;
|
|
case mapthing_extrainfo:
|
|
lua_pushinteger(L, mt->extrainfo);
|
|
break;
|
|
case mapthing_tag:
|
|
lua_pushinteger(L, Tag_FGet(&mt->tags));
|
|
break;
|
|
case mapthing_taglist:
|
|
LUA_PushUserdata(L, &mt->tags, META_TAGLIST);
|
|
break;
|
|
case mapthing_args:
|
|
LUA_PushUserdata(L, mt->args, META_THINGARGS);
|
|
break;
|
|
case mapthing_stringargs:
|
|
LUA_PushUserdata(L, mt->stringargs, META_THINGSTRINGARGS);
|
|
break;
|
|
case mapthing_mobj:
|
|
LUA_PushUserdata(L, mt->mobj, META_MOBJ);
|
|
break;
|
|
default:
|
|
if (devparm)
|
|
return luaL_error(L, "%s %s", LUA_QL("mapthing_t"), va("has no field named: %ui", field));
|
|
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 (field == (enum mapthing_e)-1)
|
|
return LUA_ErrInvalid(L, "fields");
|
|
|
|
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 CMD building 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_pitch:
|
|
mt->pitch = (INT16)luaL_checkinteger(L, 3);
|
|
break;
|
|
case mapthing_roll:
|
|
mt->roll = (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_scale:
|
|
mt->scale = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mapthing_spritexscale:
|
|
mt->spritexscale = luaL_checkfixed(L, 3);
|
|
break;
|
|
case mapthing_spriteyscale:
|
|
mt->spriteyscale = luaL_checkfixed(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_tag:
|
|
Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3));
|
|
break;
|
|
case mapthing_taglist:
|
|
return LUA_ErrSetDirectly(L, "mapthing_t", "taglist");
|
|
case mapthing_mobj:
|
|
mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
|
|
break;
|
|
default:
|
|
return luaL_error(L, "%s %s", LUA_QL("mapthing_t"), va("has no field named: %ui", field));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mapthing_num(lua_State *L)
|
|
{
|
|
mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
|
|
if (!mt)
|
|
return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
|
|
lua_pushinteger(L, mt-mapthings);
|
|
return 1;
|
|
}
|
|
|
|
static int lib_iterateMapthings(lua_State *L)
|
|
{
|
|
size_t i = 0;
|
|
INLEVEL
|
|
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)
|
|
{
|
|
INLEVEL
|
|
if (lua_isnumber(L, 2))
|
|
{
|
|
size_t i = lua_tointeger(L, 2);
|
|
if (i >= nummapthings)
|
|
return 0;
|
|
LUA_PushUserdata(L, &mapthings[i], META_MAPTHING);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int lib_nummapthings(lua_State *L)
|
|
{
|
|
lua_pushinteger(L, nummapthings);
|
|
return 1;
|
|
}
|
|
|
|
int LUA_MobjLib(lua_State *L)
|
|
{
|
|
LUA_RegisterUserdataMetatable(L, META_MOBJ, mobj_get, mobj_set, NULL);
|
|
LUA_RegisterUserdataMetatable(L, META_THINGARGS, thingargs_get, NULL, thingargs_len);
|
|
LUA_RegisterUserdataMetatable(L, META_THINGSTRINGARGS, thingstringargs_get, NULL, thingstringargs_len);
|
|
LUA_RegisterUserdataMetatable(L, META_MAPTHING, mapthing_get, mapthing_set, mapthing_num);
|
|
|
|
mobj_fields_ref = Lua_CreateFieldTable(L, mobj_opt);
|
|
mapthing_fields_ref = Lua_CreateFieldTable(L, mapthing_opt);
|
|
|
|
LUA_PushTaggableObjectArray(L, "mapthings",
|
|
lib_iterateMapthings,
|
|
lib_getMapthing,
|
|
lib_nummapthings,
|
|
tags_mapthings,
|
|
&nummapthings, &mapthings,
|
|
sizeof (mapthing_t), META_MAPTHING);
|
|
|
|
return 0;
|
|
}
|