885 lines
29 KiB
C
885 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_playerlib.c
|
|
/// \brief player object library for Lua scripting
|
|
|
|
#include "doomdef.h"
|
|
#include "fastcmp.h"
|
|
#include "r_main.h"
|
|
#include "r_things.h"
|
|
#include "p_mobj.h"
|
|
#include "d_player.h"
|
|
#include "g_game.h"
|
|
#include "p_local.h"
|
|
#include "d_clisrv.h"
|
|
|
|
#include "lua_script.h"
|
|
#include "lua_libs.h"
|
|
#include "lua_hud.h" // hud_running errors
|
|
#include "lua_hook.h" // hook_cmd_running
|
|
|
|
#include "lua_udatalib.h"
|
|
|
|
int player_name_getter(lua_State *L);
|
|
int player_name_noset(lua_State *L);
|
|
int player_mo_getter(lua_State *L);
|
|
int player_mo_setter(lua_State *L);
|
|
int player_cmd_noset(lua_State *L);
|
|
int player_aiming_setter(lua_State *L);
|
|
int player_powers_noset(lua_State *L);
|
|
int player_kartstuff_noset(lua_State *L);
|
|
int player_skincolor_setter(lua_State *L);
|
|
int player_axis_setter(lua_State *L);
|
|
int player_capsule_setter(lua_State *L);
|
|
int player_awayviewmobj_setter(lua_State *L);
|
|
int player_awayviewtics_setter(lua_State *L);
|
|
int player_bot_noset(lua_State *L);
|
|
int player_splitscreenindex_noset(lua_State *L);
|
|
int player_ping_getter(lua_State *L);
|
|
int player_ping_noset(lua_State *L);
|
|
int player_localskin_getter(lua_State *L);
|
|
int player_localskin_setter(lua_State *L);
|
|
|
|
// Non synch safe!
|
|
int player_sliproll_getter(lua_State *L);
|
|
int player_sliproll_noset(lua_State *L);
|
|
|
|
int player_viewrollangle_getter(lua_State *L);
|
|
int player_viewrollangle_noset(lua_State *L);
|
|
|
|
#define FIELD(type, field_name, getter, setter) { #field_name, offsetof(type, field_name), getter, setter }
|
|
static const udata_field_t player_fields[] = {
|
|
// Player doesn't actually have field "name" so macro fails. Need to declare field manually.
|
|
{ "name", 0, player_name_getter, player_name_noset },
|
|
FIELD(player_t, mo, player_mo_getter, player_mo_setter),
|
|
FIELD(player_t, cmd, udatalib_getter_cmd, player_cmd_noset),
|
|
FIELD(player_t, playerstate, udatalib_getter_playerstate, udatalib_setter_playerstate),
|
|
FIELD(player_t, viewz, udatalib_getter_fixed, udatalib_setter_fixed),
|
|
FIELD(player_t, viewheight, udatalib_getter_fixed, udatalib_setter_fixed),
|
|
FIELD(player_t, aiming, udatalib_getter_angle, player_aiming_setter),
|
|
FIELD(player_t, health, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, pity, udatalib_getter_sint8, udatalib_setter_sint8),
|
|
FIELD(player_t, currentweapon, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, ringweapons, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, powers, udatalib_getter_powers, player_powers_noset),
|
|
FIELD(player_t, kartstuff, udatalib_getter_kartstuff, player_kartstuff_noset),
|
|
FIELD(player_t, frameangle, udatalib_getter_angle, udatalib_setter_angle),
|
|
FIELD(player_t, pflags, udatalib_getter_pflags, udatalib_setter_pflags),
|
|
FIELD(player_t, panim, udatalib_getter_panim, udatalib_setter_panim),
|
|
FIELD(player_t, flashcount, udatalib_getter_uint16, udatalib_setter_uint16),
|
|
FIELD(player_t, flashpal, udatalib_getter_uint16, udatalib_setter_uint16),
|
|
FIELD(player_t, skincolor, udatalib_getter_uint8, player_skincolor_setter),
|
|
FIELD(player_t, localskin, player_localskin_getter, player_localskin_setter),
|
|
FIELD(player_t, score, udatalib_getter_uint32, udatalib_setter_uint32),
|
|
FIELD(player_t, dashspeed, udatalib_getter_fixed, udatalib_setter_fixed),
|
|
FIELD(player_t, dashtime, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, kartspeed, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, kartweight, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, charflags, udatalib_getter_uint32, udatalib_setter_uint32),
|
|
FIELD(player_t, lives, udatalib_getter_sint8, udatalib_setter_sint8),
|
|
FIELD(player_t, continues, udatalib_getter_sint8, udatalib_setter_sint8),
|
|
FIELD(player_t, xtralife, udatalib_getter_sint8, udatalib_setter_sint8),
|
|
FIELD(player_t, gotcontinue, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, speed, udatalib_getter_fixed, udatalib_setter_fixed),
|
|
FIELD(player_t, jumping, udatalib_getter_boolean, udatalib_setter_boolean),
|
|
FIELD(player_t, secondjump, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, fly1, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, scoreadd, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, glidetime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, climbing, udatalib_getter_uint8, udatalib_setter_sint8), // For whatever reason, original setter casted to INT32...
|
|
FIELD(player_t, deadtimer, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, exiting, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, homing, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, skidtime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, cmomx, udatalib_getter_fixed, udatalib_setter_fixed),
|
|
FIELD(player_t, cmomy, udatalib_getter_fixed, udatalib_setter_fixed),
|
|
FIELD(player_t, rmomx, udatalib_getter_fixed, udatalib_setter_fixed),
|
|
FIELD(player_t, rmomy, udatalib_getter_fixed, udatalib_setter_fixed),
|
|
FIELD(player_t, numboxes, udatalib_getter_int16, udatalib_setter_int16),
|
|
FIELD(player_t, totalring, udatalib_getter_int16, udatalib_setter_int16),
|
|
FIELD(player_t, realtime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, laps, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, ctfteam, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, gotflag, udatalib_getter_uint16, udatalib_setter_uint16),
|
|
FIELD(player_t, weapondelay, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, tossdelay, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, starpostx, udatalib_getter_int16, udatalib_setter_int16),
|
|
FIELD(player_t, starposty, udatalib_getter_int16, udatalib_setter_int16),
|
|
FIELD(player_t, starpostz, udatalib_getter_int16, udatalib_setter_int16),
|
|
FIELD(player_t, starpostnum, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, starposttime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, starpostangle, udatalib_getter_angle, udatalib_setter_angle),
|
|
FIELD(player_t, angle_pos, udatalib_getter_angle, udatalib_setter_angle),
|
|
FIELD(player_t, old_angle_pos, udatalib_getter_angle, udatalib_setter_angle),
|
|
FIELD(player_t, axis1, udatalib_getter_mobj, player_axis_setter),
|
|
FIELD(player_t, axis2, udatalib_getter_mobj, player_axis_setter),
|
|
FIELD(player_t, bumpertime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, flyangle, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, drilltimer, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, linkcount, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, linktimer, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, anotherflyangle, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, nightstime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, drillmeter, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, drilldelay, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, bonustime, udatalib_getter_boolean, udatalib_setter_boolean),
|
|
FIELD(player_t, capsule, udatalib_getter_mobj, player_capsule_setter),
|
|
FIELD(player_t, mare, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, marebegunat, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, startedtime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, finishedtime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, finishedrings, udatalib_getter_int16, udatalib_setter_int16),
|
|
FIELD(player_t, marescore, udatalib_getter_uint32, udatalib_setter_uint32),
|
|
FIELD(player_t, lastmarescore, udatalib_getter_uint32, udatalib_setter_uint32),
|
|
FIELD(player_t, lastmare, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, maxlink, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, texttimer, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, textvar, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, lastsidehit, udatalib_getter_int16, udatalib_setter_int16),
|
|
FIELD(player_t, lastlinehit, udatalib_getter_int16, udatalib_setter_int16),
|
|
FIELD(player_t, losstime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, timeshit, udatalib_getter_uint8, udatalib_setter_uint8), // Haha, time shit, funni
|
|
FIELD(player_t, onconveyor, udatalib_getter_int32, udatalib_setter_int32),
|
|
FIELD(player_t, awayviewmobj, udatalib_getter_mobj, player_awayviewmobj_setter),
|
|
FIELD(player_t, awayviewtics, udatalib_getter_int32, player_awayviewtics_setter),
|
|
FIELD(player_t, awayviewaiming, udatalib_getter_angle, udatalib_setter_angle),
|
|
FIELD(player_t, spectator, udatalib_getter_boolean, udatalib_setter_boolean_nocheck), // ffs
|
|
FIELD(player_t, bot, udatalib_getter_uint8, player_bot_noset),
|
|
FIELD(player_t, jointime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, spectatorreentry, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, grieftime, udatalib_getter_tic, udatalib_setter_tic),
|
|
FIELD(player_t, griefstrikes, udatalib_getter_uint8, udatalib_setter_uint8),
|
|
FIELD(player_t, splitscreenindex, udatalib_getter_uint8, player_splitscreenindex_noset),
|
|
FIELD(player_t, fovadd, udatalib_getter_fixed, udatalib_setter_fixed), // Mmm yeah thats definitely synch safe
|
|
|
|
// Same as player.name
|
|
{ "sliproll", 0, player_sliproll_getter, player_sliproll_noset },
|
|
{ "viewrollangle", 0, player_viewrollangle_getter, player_viewrollangle_noset },
|
|
{ "ping", 0, player_ping_getter, player_ping_noset }, // Hmm originally setter doesn't exist so data is written as unreachable custom field...
|
|
{ NULL, 0, NULL, NULL },
|
|
};
|
|
#undef FIELD
|
|
|
|
// Implement udatalib simple getters
|
|
#define pushplayer(L, player) LUA_PushUserdata(L, player, META_PLAYER)
|
|
int udatalib_getter_player(lua_State *L)
|
|
UDATALIB_SIMPLE_GETTER(player_t*, pushplayer)
|
|
#undef pushplayer
|
|
|
|
#define pushcmd(L, cmd) LUA_PushUserdata(L, &cmd, META_TICCMD)
|
|
int udatalib_getter_cmd(lua_State *L)
|
|
UDATALIB_SIMPLE_GETTER(ticcmd_t, pushcmd)
|
|
#undef pushcmd
|
|
|
|
int udatalib_getter_playerstate(lua_State *L)
|
|
UDATALIB_SIMPLE_GETTER(playerstate_t, lua_pushinteger)
|
|
|
|
int udatalib_setter_playerstate(lua_State *L)
|
|
UDATALIB_SIMPLE_SETTER(playerstate_t, luaL_checkinteger)
|
|
|
|
#define pushpowers(L, powers) LUA_PushUserdata(L, &powers, META_POWERS)
|
|
int udatalib_getter_powers(lua_State *L)
|
|
UDATALIB_SIMPLE_GETTER(UINT16, pushpowers)
|
|
#undef pushpowers
|
|
|
|
#define pushkartstuff(L, kartstuff) LUA_PushUserdata(L, &kartstuff, META_KARTSTUFF)
|
|
int udatalib_getter_kartstuff(lua_State *L)
|
|
UDATALIB_SIMPLE_GETTER(INT32, pushkartstuff)
|
|
#undef pushkartstuff
|
|
|
|
int udatalib_getter_pflags(lua_State *L)
|
|
UDATALIB_SIMPLE_GETTER(pflags_t, lua_pushinteger)
|
|
|
|
int udatalib_setter_pflags(lua_State *L)
|
|
UDATALIB_SIMPLE_SETTER(pflags_t, luaL_checkinteger)
|
|
|
|
int udatalib_getter_panim(lua_State *L)
|
|
UDATALIB_SIMPLE_GETTER(panim_t, lua_pushinteger)
|
|
|
|
int udatalib_setter_panim(lua_State *L)
|
|
UDATALIB_SIMPLE_SETTER(panim_t, luaL_checkinteger)
|
|
// Fields that cannot be set directly
|
|
#define NOSET(field) \
|
|
int player_ ## field ## _noset(lua_State *L) \
|
|
{ \
|
|
return luaL_error(L, LUA_QL("player_t") " field " LUA_QS " should not be set directly.", #field); \
|
|
}
|
|
|
|
NOSET(name)
|
|
NOSET(cmd)
|
|
NOSET(powers)
|
|
NOSET(kartstuff)
|
|
NOSET(bot)
|
|
NOSET(splitscreenindex)
|
|
NOSET(ping)
|
|
NOSET(sliproll)
|
|
NOSET(viewrollangle)
|
|
|
|
#undef NOSET
|
|
|
|
// Rest of getters/setters with arbitary logic
|
|
|
|
|
|
#define GETPLAYER() (player_t*)lua_touserdata(L, 1)
|
|
|
|
int player_name_getter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
lua_pushstring(L, player_names[plr-players]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int player_mo_getter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
if (plr->spectator)
|
|
lua_pushnil(L);
|
|
else
|
|
LUA_PushUserdata(L, plr->mo, META_MOBJ);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int player_mo_setter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
|
|
plr->mo->player = NULL; // remove player pointer from old mobj
|
|
(newmo->player = plr)->mo = newmo; // set player pointer for new mobj, and set new mobj as the player's mobj
|
|
|
|
return 0;
|
|
}
|
|
|
|
int player_aiming_setter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
plr->aiming = luaL_checkangle(L, 2);
|
|
if (plr == &players[consoleplayer])
|
|
localaiming[0] = plr->aiming;
|
|
else if (plr == &players[displayplayers[1]])
|
|
localaiming[1] = plr->aiming;
|
|
else if (plr == &players[displayplayers[2]])
|
|
localaiming[2] = plr->aiming;
|
|
else if (plr == &players[displayplayers[3]])
|
|
localaiming[3] = plr->aiming;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int player_skincolor_setter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
UINT8 newcolor = (UINT8)luaL_checkinteger(L,2);
|
|
if (newcolor >= MAXSKINCOLORS)
|
|
return luaL_error(L, "player.skincolor %d out of range (0 - %d).", newcolor, MAXSKINCOLORS-1);
|
|
plr->skincolor = newcolor;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int player_localskin_getter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
if (plr->localskin)
|
|
lua_pushstring(L, (plr->skinlocal ? localskins : skins)[plr->localskin - 1].name);
|
|
else
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int player_localskin_setter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
SetLocalPlayerSkin(plr - players, luaL_optstring(L, 2, "none"), NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int player_axis_setter(lua_State *L)
|
|
{
|
|
mobj_t **axis;
|
|
|
|
UDATALIB_GETFIELD(mobj_t*, axis);
|
|
|
|
P_SetTarget(axis, *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int player_capsule_setter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
mobj_t *mo = NULL;
|
|
if (!lua_isnil(L, 2))
|
|
mo = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
|
|
P_SetTarget(&plr->capsule, mo);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Probably can do same thing as with axis1 and axis2
|
|
int player_awayviewmobj_setter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
mobj_t *mo = NULL;
|
|
if (!lua_isnil(L, 2))
|
|
mo = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
|
|
P_SetTarget(&plr->awayviewmobj, mo);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int player_awayviewtics_setter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
plr->awayviewtics = (INT32)luaL_checkinteger(L, 2);
|
|
if (plr->awayviewtics && !plr->awayviewmobj) // awayviewtics must ALWAYS have an awayviewmobj set!!
|
|
P_SetTarget(&plr->awayviewmobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now.
|
|
|
|
return 0;
|
|
}
|
|
|
|
int player_ping_getter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
lua_pushinteger(L, playerpingtable[( plr - players )]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int player_sliproll_getter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
lua_pushangle(L, R_PlayerSliptideAngle(plr));
|
|
|
|
return 1;
|
|
}
|
|
|
|
int player_viewrollangle_getter(lua_State *L)
|
|
{
|
|
player_t *plr = GETPLAYER();
|
|
|
|
lua_pushangle(L, R_ViewRollAngle(plr));
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int lib_iteratePlayers(lua_State *L)
|
|
{
|
|
INT32 i = -1;
|
|
if (lua_gettop(L) < 2)
|
|
{
|
|
//return luaL_error(L, "Don't call players.iterate() directly, use it as 'for player in players.iterate do <block> end'.");
|
|
lua_pushcfunction(L, lib_iteratePlayers);
|
|
return 1;
|
|
}
|
|
lua_settop(L, 2);
|
|
lua_remove(L, 1); // state is unused.
|
|
if (!lua_isnil(L, 1))
|
|
i = (INT32)(*((player_t **)luaL_checkudata(L, 1, META_PLAYER)) - players);
|
|
for (i++; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
if (!players[i].mo)
|
|
continue;
|
|
LUA_PushUserdata(L, &players[i], META_PLAYER);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int lib_getPlayer(lua_State *L)
|
|
{
|
|
const char *field;
|
|
// i -> players[i]
|
|
if (lua_type(L, 2) == LUA_TNUMBER)
|
|
{
|
|
lua_Integer i = luaL_checkinteger(L, 2);
|
|
if (i < 0 || i >= MAXPLAYERS)
|
|
return luaL_error(L, "players[] index %d out of range (0 - %d)", i, MAXPLAYERS-1);
|
|
if (!playeringame[i])
|
|
return 0;
|
|
if (!players[i].mo)
|
|
return 0;
|
|
LUA_PushUserdata(L, &players[i], META_PLAYER);
|
|
return 1;
|
|
}
|
|
|
|
field = luaL_checkstring(L, 2);
|
|
if (fastcmp(field,"iterate"))
|
|
{
|
|
lua_pushcfunction(L, lib_iteratePlayers);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// #players -> MAXPLAYERS
|
|
static int lib_lenPlayer(lua_State *L)
|
|
{
|
|
lua_pushinteger(L, MAXPLAYERS);
|
|
return 1;
|
|
}
|
|
|
|
// Same deal as the three functions above but for displayplayers
|
|
|
|
static int lib_iterateDisplayplayers(lua_State *L)
|
|
{
|
|
INT32 i = -1;
|
|
INT32 temp = -1;
|
|
INT32 iter = 0;
|
|
|
|
if (lua_gettop(L) < 2)
|
|
{
|
|
//return luaL_error(L, "Don't call displayplayers.iterate() directly, use it as 'for player in displayplayers.iterate do <block> end'.");
|
|
lua_pushcfunction(L, lib_iterateDisplayplayers);
|
|
return 1;
|
|
}
|
|
lua_settop(L, 2);
|
|
lua_remove(L, 1); // state is unused.
|
|
if (!lua_isnil(L, 1))
|
|
{
|
|
temp = (INT32)(*((player_t **)luaL_checkudata(L, 1, META_PLAYER)) - players); // get the player # of the last iterated player.
|
|
|
|
// @FIXME:
|
|
// I didn't quite find a better way for this; Here, we go back to which player in displayplayers we last iterated to resume the for loop below for this new function call
|
|
// I don't understand enough about how the Lua stacks work to get this to work in possibly a single line.
|
|
// So anyone feel free to correct this!
|
|
|
|
for (; iter < MAXSPLITSCREENPLAYERS; iter++)
|
|
{
|
|
if (displayplayers[iter] == temp)
|
|
{
|
|
i = iter;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i++; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
if (i > splitscreen || !playeringame[displayplayers[i]])
|
|
return 0; // Stop! There are no more players for us to go through. There will never be a player gap in displayplayers.
|
|
|
|
if (!players[displayplayers[i]].mo)
|
|
continue;
|
|
LUA_PushUserdata(L, &players[displayplayers[i]], META_PLAYER);
|
|
lua_pushinteger(L, i); // push this to recall what number we were on for the next function call. I suppose this also means you can retrieve the splitscreen player number with 'for p, n in displayplayers.iterate'!
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int lib_getDisplayplayers(lua_State *L)
|
|
{
|
|
const char *field;
|
|
// i -> players[i]
|
|
if (lua_type(L, 2) == LUA_TNUMBER)
|
|
{
|
|
lua_Integer i = luaL_checkinteger(L, 2);
|
|
if (i < 0 || i >= MAXSPLITSCREENPLAYERS)
|
|
return luaL_error(L, "displayplayers[] index %d out of range (0 - %d)", i, MAXSPLITSCREENPLAYERS-1);
|
|
if (i > splitscreen)
|
|
return 0;
|
|
if (!playeringame[displayplayers[i]])
|
|
return 0;
|
|
if (!players[displayplayers[i]].mo)
|
|
return 0;
|
|
LUA_PushUserdata(L, &players[displayplayers[i]], META_PLAYER);
|
|
return 1;
|
|
}
|
|
|
|
field = luaL_checkstring(L, 2);
|
|
if (fastcmp(field,"iterate"))
|
|
{
|
|
lua_pushcfunction(L, lib_iterateDisplayplayers);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// #displayplayers -> MAXSPLITSCREENPLAYERS
|
|
static int lib_lenDisplayplayers(lua_State *L)
|
|
{
|
|
lua_pushinteger(L, MAXSPLITSCREENPLAYERS);
|
|
return 1;
|
|
}
|
|
|
|
static int player_get(lua_State *L)
|
|
{
|
|
player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
|
const char *field = luaL_checkstring(L, 2); // Still gotta have 2 strcmp's
|
|
|
|
if (!plr) {
|
|
if (fastcmp(field,"valid")) {
|
|
lua_pushboolean(L, false);
|
|
return 1;
|
|
}
|
|
return LUA_ErrInvalid(L, "player_t");
|
|
} else if (fastcmp(field,"valid")) {
|
|
lua_pushboolean(L, true);
|
|
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, plr);
|
|
lua_call(L, 1, 1);
|
|
//CONS_Printf("Getter returned %s\n", lua_typename(L, lua_type(L, -1)));
|
|
return 1;
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
|
|
I_Assert(lua_istable(L, -1));
|
|
lua_pushlightuserdata(L, plr);
|
|
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"), "player_t", field);
|
|
return 0;
|
|
}
|
|
lua_getfield(L, -1, field);
|
|
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"), "player_t", field);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int player_set(lua_State *L)
|
|
{
|
|
player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
|
const char *field = luaL_checkstring(L, 2);
|
|
if (!plr)
|
|
return LUA_ErrInvalid(L, "player_t");
|
|
|
|
if (hud_running)
|
|
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
|
|
|
|
if (hook_cmd_running)
|
|
return luaL_error(L, "Do not alter player_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, plr);
|
|
lua_pushvalue(L, 3);
|
|
lua_call(L, 2, 0);
|
|
return 0;
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
|
|
I_Assert(lua_istable(L, -1));
|
|
lua_pushlightuserdata(L, plr);
|
|
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"), "player_t", field);
|
|
lua_newtable(L);
|
|
lua_pushlightuserdata(L, plr);
|
|
lua_pushvalue(L, -2); // ext value table
|
|
lua_rawset(L, -4); // LREG_EXTVARS table
|
|
}
|
|
lua_pushvalue(L, 3); // value to store
|
|
lua_setfield(L, -2, field);
|
|
lua_pop(L, 2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#undef NOSET
|
|
|
|
static int player_num(lua_State *L)
|
|
{
|
|
player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
|
if (!plr)
|
|
return luaL_error(L, "accessed player_t doesn't exist anymore.");
|
|
lua_pushinteger(L, plr-players);
|
|
return 1;
|
|
}
|
|
|
|
// powers, p -> powers[p]
|
|
static int power_get(lua_State *L)
|
|
{
|
|
UINT16 *powers = *((UINT16 **)luaL_checkudata(L, 1, META_POWERS));
|
|
powertype_t p = luaL_checkinteger(L, 2);
|
|
if (p >= NUMPOWERS)
|
|
return luaL_error(L, LUA_QL("powertype_t") " cannot be %u", p);
|
|
lua_pushinteger(L, powers[p]);
|
|
return 1;
|
|
}
|
|
|
|
// powers, p, value -> powers[p] = value
|
|
static int power_set(lua_State *L)
|
|
{
|
|
UINT16 *powers = *((UINT16 **)luaL_checkudata(L, 1, META_POWERS));
|
|
powertype_t p = luaL_checkinteger(L, 2);
|
|
UINT16 i = (UINT16)luaL_checkinteger(L, 3);
|
|
if (p >= NUMPOWERS)
|
|
return luaL_error(L, LUA_QL("powertype_t") " cannot be %u", p);
|
|
if (hud_running)
|
|
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
|
|
if (hook_cmd_running)
|
|
return luaL_error(L, "Do not alter player_t in BuildCMD code!");
|
|
powers[p] = i;
|
|
return 0;
|
|
}
|
|
|
|
// #powers -> NUMPOWERS
|
|
static int power_len(lua_State *L)
|
|
{
|
|
lua_pushinteger(L, NUMPOWERS);
|
|
return 1;
|
|
}
|
|
|
|
// kartstuff, ks -> kartstuff[ks]
|
|
static int kartstuff_get(lua_State *L)
|
|
{
|
|
INT32 *kartstuff = *((INT32 **)luaL_checkudata(L, 1, META_KARTSTUFF));
|
|
kartstufftype_t ks = luaL_checkinteger(L, 2);
|
|
if (ks >= NUMKARTSTUFF)
|
|
return luaL_error(L, LUA_QL("kartstufftype_t") " cannot be %u", ks);
|
|
lua_pushinteger(L, kartstuff[ks]);
|
|
return 1;
|
|
}
|
|
|
|
// kartstuff, ks, value -> kartstuff[ks] = value
|
|
static int kartstuff_set(lua_State *L)
|
|
{
|
|
INT32 *kartstuff = *((INT32 **)luaL_checkudata(L, 1, META_KARTSTUFF));
|
|
kartstufftype_t ks = luaL_checkinteger(L, 2);
|
|
INT32 i = (INT32)luaL_checkinteger(L, 3);
|
|
if (ks >= NUMKARTSTUFF)
|
|
return luaL_error(L, LUA_QL("kartstufftype_t") " cannot be %u", ks);
|
|
if (hud_running)
|
|
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
|
|
if (hook_cmd_running)
|
|
return luaL_error(L, "Do not alter player_t in BuildCMD code!");
|
|
kartstuff[ks] = i;
|
|
return 0;
|
|
}
|
|
|
|
// #kartstuff -> NUMKARTSTUFF
|
|
static int kartstuff_len(lua_State *L)
|
|
{
|
|
lua_pushinteger(L, NUMKARTSTUFF);
|
|
return 1;
|
|
}
|
|
|
|
#define NOFIELD luaL_error(L, LUA_QL("ticcmd_t") " has no field named " LUA_QS, field)
|
|
#define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " should not be set directly.", field)
|
|
|
|
enum ticcmd_e
|
|
{
|
|
ticcmd_forwardmove,
|
|
ticcmd_sidemove,
|
|
ticcmd_angleturn,
|
|
ticcmd_aiming,
|
|
ticcmd_buttons,
|
|
ticcmd_driftturn,
|
|
ticcmd_latency,
|
|
};
|
|
|
|
static const char *const ticcmd_opt[] = {
|
|
"forwardmove",
|
|
"sidemove",
|
|
"angleturn",
|
|
"aiming",
|
|
"buttons",
|
|
"driftturn",
|
|
"latency",
|
|
NULL,
|
|
};
|
|
|
|
static int ticcmd_fields_ref = LUA_NOREF;
|
|
|
|
static int ticcmd_get(lua_State *L)
|
|
{
|
|
ticcmd_t *cmd = *((ticcmd_t **)luaL_checkudata(L, 1, META_TICCMD));
|
|
enum ticcmd_e field = Lua_optoption(L, 2, -1, ticcmd_fields_ref);
|
|
if (!cmd)
|
|
return LUA_ErrInvalid(L, "player_t");
|
|
|
|
switch (field)
|
|
{
|
|
case ticcmd_forwardmove:
|
|
lua_pushinteger(L, cmd->forwardmove);
|
|
break;
|
|
case ticcmd_sidemove:
|
|
lua_pushinteger(L, cmd->sidemove);
|
|
break;
|
|
case ticcmd_angleturn:
|
|
lua_pushinteger(L, cmd->angleturn);
|
|
break;
|
|
case ticcmd_aiming:
|
|
lua_pushinteger(L, cmd->aiming);
|
|
break;
|
|
case ticcmd_buttons:
|
|
lua_pushinteger(L, cmd->buttons);
|
|
break;
|
|
case ticcmd_driftturn:
|
|
lua_pushinteger(L, cmd->driftturn);
|
|
break;
|
|
case ticcmd_latency:
|
|
lua_pushinteger(L, cmd->latency);
|
|
break;
|
|
default:
|
|
return NOFIELD;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ticcmd_set(lua_State *L)
|
|
{
|
|
ticcmd_t *cmd = *((ticcmd_t **)luaL_checkudata(L, 1, META_TICCMD));
|
|
enum ticcmd_e field = Lua_optoption(L, 2, -1, ticcmd_fields_ref);
|
|
if (!cmd)
|
|
return LUA_ErrInvalid(L, "ticcmd_t");
|
|
|
|
if (hud_running)
|
|
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
|
|
|
|
switch (field)
|
|
{
|
|
case ticcmd_forwardmove:
|
|
cmd->forwardmove = (SINT8)luaL_checkinteger(L, 3);
|
|
break;
|
|
case ticcmd_sidemove:
|
|
cmd->sidemove = (SINT8)luaL_checkinteger(L, 3);
|
|
break;
|
|
case ticcmd_angleturn:
|
|
cmd->angleturn = (INT16)luaL_checkinteger(L, 3);
|
|
break;
|
|
case ticcmd_aiming:
|
|
cmd->aiming = (INT16)luaL_checkinteger(L, 3);
|
|
break;
|
|
case ticcmd_buttons:
|
|
cmd->buttons = (UINT16)luaL_checkinteger(L, 3);
|
|
break;
|
|
case ticcmd_driftturn:
|
|
cmd->driftturn = (INT16)luaL_checkinteger(L, 3);
|
|
break;
|
|
case ticcmd_latency:
|
|
return NOSET;
|
|
default:
|
|
return NOFIELD;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#undef NOFIELD
|
|
#undef NOSET
|
|
|
|
int LUA_PlayerLib(lua_State *L)
|
|
{
|
|
luaL_newmetatable(L, META_PLAYER);
|
|
lua_pushcfunction(L, player_get);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, player_set);
|
|
lua_setfield(L, -2, "__newindex");
|
|
|
|
lua_pushcfunction(L, player_num);
|
|
lua_setfield(L, -2, "__len");
|
|
|
|
udatalib_addfields(L, -1, player_fields);
|
|
lua_pop(L,1);
|
|
|
|
luaL_newmetatable(L, META_POWERS);
|
|
lua_pushcfunction(L, power_get);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, power_set);
|
|
lua_setfield(L, -2, "__newindex");
|
|
|
|
lua_pushcfunction(L, power_len);
|
|
lua_setfield(L, -2, "__len");
|
|
lua_pop(L,1);
|
|
|
|
luaL_newmetatable(L, META_KARTSTUFF);
|
|
lua_pushcfunction(L, kartstuff_get);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, kartstuff_set);
|
|
lua_setfield(L, -2, "__newindex");
|
|
|
|
lua_pushcfunction(L, kartstuff_len);
|
|
lua_setfield(L, -2, "__len");
|
|
lua_pop(L,1);
|
|
|
|
luaL_newmetatable(L, META_TICCMD);
|
|
lua_pushcfunction(L, ticcmd_get);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, ticcmd_set);
|
|
lua_setfield(L, -2, "__newindex");
|
|
lua_pop(L,1);
|
|
|
|
ticcmd_fields_ref = Lua_CreateFieldTable(L, ticcmd_opt);
|
|
|
|
lua_newuserdata(L, 0);
|
|
lua_createtable(L, 0, 2);
|
|
lua_pushcfunction(L, lib_getPlayer);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, lib_lenPlayer);
|
|
lua_setfield(L, -2, "__len");
|
|
lua_setmetatable(L, -2);
|
|
lua_setglobal(L, "players");
|
|
|
|
// push displayplayers in the same fashion
|
|
lua_newuserdata(L, 0);
|
|
lua_createtable(L, 0, 2);
|
|
lua_pushcfunction(L, lib_getDisplayplayers);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, lib_lenDisplayplayers);
|
|
lua_setfield(L, -2, "__len");
|
|
lua_setmetatable(L, -2);
|
|
lua_setglobal(L, "displayplayers");
|
|
|
|
return 0;
|
|
}
|