433 lines
10 KiB
C
433 lines
10 KiB
C
// BLANKART
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2024 by Kart Krew.
|
|
// Copyright (C) 2020 by Sonic Team Junior.
|
|
// Copyright (C) 2016 by John "JTE" Muniz.
|
|
//
|
|
// 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_botvarslib.c
|
|
/// \brief player botvars structure library for Lua scripting
|
|
|
|
#include "doomdef.h"
|
|
#include "lua_script.h"
|
|
#include "lua_libs.h"
|
|
#include "k_bot.h"
|
|
|
|
enum botvars {
|
|
botvars_valid = 0,
|
|
botvars_style,
|
|
botvars_difficulty,
|
|
botvars_diffincrease,
|
|
botvars_rival,
|
|
botvars_rubberband,
|
|
};
|
|
|
|
static const char *const botvars_opt[] = {
|
|
"valid",
|
|
"style",
|
|
"difficulty",
|
|
"diffincrease",
|
|
"rival",
|
|
"rubberband",
|
|
NULL
|
|
};
|
|
|
|
#define UNIMPLEMENTED luaL_error(L, LUA_QL("botvars_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", follower_opt[field])
|
|
|
|
static int botvars_get(lua_State *L)
|
|
{
|
|
botvars_t *botvars = *((botvars_t **)luaL_checkudata(L, 1, META_BOTVARS));
|
|
enum botvars field = luaL_checkoption(L, 2, NULL, botvars_opt);
|
|
|
|
// This is a property that always exists in a player.
|
|
I_Assert(botvars != NULL);
|
|
|
|
switch (field)
|
|
{
|
|
case botvars_valid:
|
|
lua_pushboolean(L, botvars != NULL);
|
|
break;
|
|
case botvars_style:
|
|
lua_pushinteger(L, botvars->style);
|
|
break;
|
|
case botvars_difficulty:
|
|
lua_pushinteger(L, botvars->difficulty);
|
|
break;
|
|
case botvars_diffincrease:
|
|
lua_pushinteger(L, botvars->diffincrease);
|
|
break;
|
|
case botvars_rival:
|
|
lua_pushboolean(L, botvars->rival);
|
|
break;
|
|
case botvars_rubberband:
|
|
lua_pushfixed(L, botvars->rubberband);
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#define NOSET luaL_error(L, LUA_QL("botvars_t") " field " LUA_QS " should not be set directly.", botvars_opt[field])
|
|
|
|
static int botvars_set(lua_State *L)
|
|
{
|
|
botvars_t *botvars = *((botvars_t **)luaL_checkudata(L, 1, META_BOTVARS));
|
|
enum botvars field = luaL_checkoption(L, 2, botvars_opt[0], botvars_opt);
|
|
|
|
// This is a property that always exists in a player.
|
|
I_Assert(botvars != NULL);
|
|
|
|
INLEVEL
|
|
|
|
switch(field)
|
|
{
|
|
case botvars_valid:
|
|
return NOSET;
|
|
case botvars_style:
|
|
botvars->style = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botvars_difficulty:
|
|
botvars->difficulty = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botvars_diffincrease:
|
|
botvars->diffincrease = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botvars_rival:
|
|
botvars->rival = luaL_checkboolean(L, 3);
|
|
break;
|
|
case botvars_rubberband:
|
|
botvars->rubberband = luaL_checkfixed(L, 3);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#undef NOSET
|
|
|
|
enum botdata {
|
|
botdata_valid,
|
|
botdata_itemdelay,
|
|
botdata_itemconfirm,
|
|
botdata_turnconfirm,
|
|
botdata_respawnconfirm,
|
|
botdata_driftstate,
|
|
botdata_driftturn,
|
|
botdata_drifttime,
|
|
botdata_driftlockout,
|
|
botdata_driftmaxdist,
|
|
botdata_driftpowerdiv,
|
|
botdata_driftstatedelay,
|
|
botdata_driftskill,
|
|
botdata_acceldown,
|
|
botdata_brakedown,
|
|
botdata_driftdown,
|
|
botdata_itemdown,
|
|
botdata_dolookback,
|
|
botdata_itemwasdown,
|
|
botdata_itemthrow,
|
|
botdata_turnamt,
|
|
};
|
|
|
|
static const char *const botdata_opt[] = {
|
|
"valid",
|
|
"itemdelay",
|
|
"itemconfirm",
|
|
"turnconfirm",
|
|
"respawnconfirm",
|
|
"driftstate",
|
|
"driftturn",
|
|
"drifttime",
|
|
"driftlockout",
|
|
"driftmaxdist",
|
|
"driftpowerdiv",
|
|
"driftstatedelay",
|
|
"driftskill",
|
|
"acceldown",
|
|
"brakedown",
|
|
"driftdown",
|
|
"itemdown",
|
|
"dolookback",
|
|
"itemwasdown",
|
|
"itemthrow",
|
|
"turnamt",
|
|
NULL
|
|
};
|
|
|
|
static int botdata_get(lua_State *L)
|
|
{
|
|
botdata_t *botdata = *((botdata_t **)luaL_checkudata(L, 1, META_BOTDATA));
|
|
enum botdata field = luaL_checkoption(L, 2, NULL, botdata_opt);
|
|
|
|
// it's a static array... i think...
|
|
I_Assert(botdata != NULL);
|
|
|
|
switch (field)
|
|
{
|
|
case botdata_valid:
|
|
lua_pushboolean(L, botdata != NULL);
|
|
break;
|
|
case botdata_itemdelay:
|
|
lua_pushinteger(L, botdata->itemdelay);
|
|
break;
|
|
case botdata_itemconfirm:
|
|
lua_pushinteger(L, botdata->itemconfirm);
|
|
break;
|
|
case botdata_turnconfirm:
|
|
lua_pushinteger(L, botdata->turnconfirm);
|
|
break;
|
|
case botdata_respawnconfirm:
|
|
lua_pushinteger(L, botdata->respawnconfirm);
|
|
break;
|
|
case botdata_driftstate:
|
|
lua_pushinteger(L, botdata->driftstate);
|
|
break;
|
|
case botdata_driftturn:
|
|
lua_pushinteger(L, botdata->driftturn);
|
|
break;
|
|
case botdata_drifttime:
|
|
lua_pushinteger(L, botdata->drifttime);
|
|
break;
|
|
case botdata_driftlockout:
|
|
lua_pushinteger(L, botdata->driftlockout);
|
|
break;
|
|
case botdata_driftmaxdist:
|
|
lua_pushinteger(L, botdata->driftmaxdist);
|
|
break;
|
|
case botdata_driftpowerdiv:
|
|
lua_pushinteger(L, botdata->driftpowerdiv);
|
|
break;
|
|
case botdata_driftstatedelay:
|
|
lua_pushinteger(L, botdata->driftstatedelay);
|
|
break;
|
|
case botdata_driftskill:
|
|
lua_pushfixed(L, botdata->driftskill);
|
|
break;
|
|
case botdata_acceldown:
|
|
lua_pushboolean(L, botdata->acceldown);
|
|
break;
|
|
case botdata_brakedown:
|
|
lua_pushboolean(L, botdata->brakedown);
|
|
break;
|
|
case botdata_driftdown:
|
|
lua_pushboolean(L, botdata->driftdown);
|
|
break;
|
|
case botdata_itemdown:
|
|
lua_pushboolean(L, botdata->itemdown);
|
|
break;
|
|
case botdata_dolookback:
|
|
lua_pushboolean(L, botdata->dolookback);
|
|
break;
|
|
case botdata_itemwasdown:
|
|
lua_pushboolean(L, botdata->itemwasdown);
|
|
break;
|
|
case botdata_itemthrow:
|
|
lua_pushinteger(L, botdata->itemthrow);
|
|
break;
|
|
case botdata_turnamt:
|
|
lua_pushinteger(L, botdata->turnamt);
|
|
break;
|
|
default:
|
|
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
|
|
I_Assert(lua_istable(L, -1));
|
|
lua_pushlightuserdata(L, botdata);
|
|
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"), "botdata_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"), "botdata_t", lua_tostring(L, 2));
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define NOSET luaL_error(L, LUA_QL("botdata_t") " field " LUA_QS " should not be set directly.", botdata_opt[field])
|
|
|
|
static int botdata_set(lua_State *L)
|
|
{
|
|
botdata_t *botdata = *((botdata_t **)luaL_checkudata(L, 1, META_BOTDATA));
|
|
enum botdata field = luaL_checkoption(L, 2, NULL, botdata_opt);
|
|
|
|
I_Assert(botdata != NULL);
|
|
|
|
switch (field)
|
|
{
|
|
case botdata_valid:
|
|
return NOSET;
|
|
case botdata_itemdelay:
|
|
botdata->itemdelay = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_itemconfirm:
|
|
botdata->itemconfirm = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_turnconfirm:
|
|
botdata->turnconfirm = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_respawnconfirm:
|
|
botdata->respawnconfirm = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_driftstate:
|
|
botdata->driftstate = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_driftturn:
|
|
botdata->driftturn = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_drifttime:
|
|
botdata->drifttime = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_driftlockout:
|
|
botdata->driftlockout = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_driftmaxdist:
|
|
botdata->driftmaxdist = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_driftpowerdiv:
|
|
botdata->driftpowerdiv = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_driftstatedelay:
|
|
botdata->driftstatedelay = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_driftskill:
|
|
botdata->driftskill = luaL_checkfixed(L, 3);
|
|
break;
|
|
case botdata_acceldown:
|
|
botdata->acceldown = luaL_checkboolean(L, 3);
|
|
break;
|
|
case botdata_brakedown:
|
|
botdata->brakedown = luaL_checkboolean(L, 3);
|
|
break;
|
|
case botdata_driftdown:
|
|
botdata->driftdown = luaL_checkboolean(L, 3);
|
|
break;
|
|
case botdata_itemdown:
|
|
botdata->itemdown = luaL_checkboolean(L, 3);
|
|
break;
|
|
case botdata_dolookback:
|
|
botdata->dolookback = luaL_checkboolean(L, 3);
|
|
break;
|
|
case botdata_itemwasdown:
|
|
botdata->itemwasdown = luaL_checkboolean(L, 3);
|
|
break;
|
|
case botdata_itemthrow:
|
|
botdata->itemthrow = luaL_checkinteger(L, 3);
|
|
break;
|
|
case botdata_turnamt:
|
|
botdata->turnamt = luaL_checkinteger(L, 3);
|
|
break;
|
|
default:
|
|
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
|
|
I_Assert(lua_istable(L, -1));
|
|
lua_pushlightuserdata(L, botdata);
|
|
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"), "botdata_t", lua_tostring(L, 2));
|
|
lua_newtable(L);
|
|
lua_pushlightuserdata(L, botdata);
|
|
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 NOSET
|
|
|
|
static int lib_iterateBotData(lua_State *L)
|
|
{
|
|
INT32 i;
|
|
|
|
if (lua_gettop(L) < 2)
|
|
{
|
|
lua_pushcfunction(L, lib_iterateBotData);
|
|
return 1;
|
|
}
|
|
|
|
lua_settop(L, 2);
|
|
lua_remove(L, 1); // state is unused.
|
|
|
|
if (!lua_isnil(L, 1))
|
|
i = (INT32)(*((botdata_t **)luaL_checkudata(L, 1, META_BOTDATA)) - K_GetBotData(0)) + 1;
|
|
else
|
|
i = 0;
|
|
|
|
if (i < MAXPLAYERS)
|
|
{
|
|
LUA_PushUserdata(L, K_GetBotData(i), META_BOTDATA);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lib_getBotData(lua_State *L)
|
|
{
|
|
INT32 i;
|
|
|
|
if (lua_type(L, 2) == LUA_TNUMBER)
|
|
{
|
|
i = luaL_checkinteger(L, 2);
|
|
if (i < 0 || i >= MAXPLAYERS)
|
|
return luaL_error(L, "botdata[] index %d out of range (0 - %d)", i, MAXPLAYERS-1);
|
|
LUA_PushUserdata(L, K_GetBotData(i), META_BOTDATA);
|
|
return 1;
|
|
}
|
|
|
|
if (fastcmp(luaL_checkstring(L, 2), "iterate"))
|
|
{
|
|
lua_pushcfunction(L, lib_iterateBotData);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lib_numBotData(lua_State *L)
|
|
{
|
|
lua_pushinteger(L, MAXPLAYERS);
|
|
return 1;
|
|
}
|
|
|
|
int LUA_BotVarsLib(lua_State *L)
|
|
{
|
|
luaL_newmetatable(L, META_BOTVARS);
|
|
lua_pushcfunction(L, botvars_get);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, botvars_set);
|
|
lua_setfield(L, -2, "__newindex");
|
|
lua_pop(L,1);
|
|
|
|
luaL_newmetatable(L, META_BOTDATA);
|
|
lua_pushcfunction(L, botdata_get);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, botdata_set);
|
|
lua_setfield(L, -2, "__newindex");
|
|
lua_pop(L,1);
|
|
|
|
lua_newuserdata(L, 0);
|
|
lua_createtable(L, 0, 2);
|
|
lua_pushcfunction(L, lib_getBotData);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
lua_pushcfunction(L, lib_numBotData);
|
|
lua_setfield(L, -2, "__len");
|
|
lua_setmetatable(L, -2);
|
|
lua_setglobal(L, "botdata");
|
|
|
|
return 0;
|
|
}
|