Bot library for Lua (botvars getter/setter, bot functions)

This commit is contained in:
JugadorXEI 2025-08-22 01:24:13 +00:00 committed by GenericHeroGuy
parent e8d43ec8a3
commit dc9bec000a
16 changed files with 521 additions and 16 deletions

View file

@ -109,6 +109,7 @@ lua_maplib.c
lua_taglib.c lua_taglib.c
lua_polyobjlib.c lua_polyobjlib.c
lua_blockmaplib.c lua_blockmaplib.c
lua_botvarslib.c
lua_hudlib.c lua_hudlib.c
lua_hudlib_drawlist.c lua_hudlib_drawlist.c
lua_followerlib.c lua_followerlib.c

View file

@ -27,11 +27,11 @@
#include "r_data.h" // patchalphastyle_t #include "r_data.h" // patchalphastyle_t
#include "k_boss.h" // spottype_t (for lua) #include "k_boss.h" // spottype_t (for lua)
#include "k_follower.h" // followermode_t (for lua) #include "k_follower.h" // followermode_t (for lua)
#include "k_bot.h" // bot constants (for lua)
#include "g_input.h" // Game controls (for lua) #include "g_input.h" // Game controls (for lua)
#include "k_kart.h" // awardscaledrings_t #include "k_kart.h" // awardscaledrings_t
#include "k_waypoint.h" // waypoint values (for lua) #include "k_waypoint.h" // waypoint values (for lua)
#include "deh_tables.h" #include "deh_tables.h"
#include "fastcmp.h" #include "fastcmp.h"
@ -1553,6 +1553,47 @@ struct int_const_s const INT_CONST[] = {
{"SNEAKERTYPE_ROCKETSNEAKER", SNEAKERTYPE_ROCKETSNEAKER}, {"SNEAKERTYPE_ROCKETSNEAKER", SNEAKERTYPE_ROCKETSNEAKER},
{"SNEAKERTYPE_WATERPANEL", SNEAKERTYPE_WATERPANEL}, {"SNEAKERTYPE_WATERPANEL", SNEAKERTYPE_WATERPANEL},
// k_bot.h constants
{"MAXBOTDIFFICULTY",MAXBOTDIFFICULTY},
{"DIFFICULTBOT",DIFFICULTBOT},
{"BOTTURNCONFIRM",BOTTURNCONFIRM},
{"BOTSPINDASHCONFIRM",BOTSPINDASHCONFIRM},
{"BOTRESPAWNCONFIRM",BOTRESPAWNCONFIRM},
{"BOT_ITEM_DECISION_TIME",BOT_ITEM_DECISION_TIME},
// botStyle_e
{"BOT_STYLE_NORMAL",BOT_STYLE_NORMAL},
{"BOT_STYLE_STAY",BOT_STYLE_STAY},
{"BOT_STYLE__MAX",BOT_STYLE__MAX},
// botItemPriority_e
/*
{"BOT_ITEM_PR__FALLBACK",BOT_ITEM_PR__FALLBACK},
{"BOT_ITEM_PR_NEUTRAL",BOT_ITEM_PR_NEUTRAL},
{"BOT_ITEM_PR_FRONTRUNNER",BOT_ITEM_PR_FRONTRUNNER},
{"BOT_ITEM_PR_SPEED",BOT_ITEM_PR_SPEED},
{"BOT_ITEM_PR__OVERRIDES",BOT_ITEM_PR__OVERRIDES},
{"BOT_ITEM_PR_RINGDEBT",BOT_ITEM_PR_RINGDEBT},
{"BOT_ITEM_PR_POWER",BOT_ITEM_PR_POWER},
{"BOT_ITEM_PR_SPB",BOT_ITEM_PR_SPB},
{"BOT_ITEM_PR__MAX",BOT_ITEM_PR__MAX},
*/
// textmapbotcontroller_t
{"TMBOT_NORUBBERBAND",TMBOT_NORUBBERBAND},
{"TMBOT_NOCONTROL",TMBOT_NOCONTROL},
{"TMBOT_FORCEDIR",TMBOT_FORCEDIR},
//{"TMBOT_FASTFALL",TMBOT_FASTFALL},
// textmapbottrick_t
/*
{"TMBOTTR_NONE",TMBOTTR_NONE},
{"TMBOTTR_LEFT",TMBOTTR_LEFT},
{"TMBOTTR_RIGHT",TMBOTTR_RIGHT},
{"TMBOTTR_UP",TMBOTTR_UP},
{"TMBOTTR_DOWN",TMBOTTR_DOWN},
*/
// t_overlay_action_t // t_overlay_action_t
{"TOV_UNDEFINED",TOV_UNDEFINED}, {"TOV_UNDEFINED",TOV_UNDEFINED},
{"TOV_STILL",TOV_STILL}, {"TOV_STILL",TOV_STILL},

View file

@ -2410,6 +2410,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
boolean spectator; boolean spectator;
boolean bot; boolean bot;
UINT8 botdifficulty; UINT8 botdifficulty;
botStyle_e style;
INT16 rings; INT16 rings;
angle_t playerangleturn; angle_t playerangleturn;
@ -2469,6 +2470,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
followitem = players[player].followitem; followitem = players[player].followitem;
bot = players[player].bot; bot = players[player].bot;
style = players[player].botvars.style;
botdifficulty = players[player].botvars.difficulty; botdifficulty = players[player].botvars.difficulty;
botdiffincrease = players[player].botvars.diffincrease; botdiffincrease = players[player].botvars.diffincrease;
@ -2631,6 +2633,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->totalring = totalring; p->totalring = totalring;
p->bot = bot; p->bot = bot;
p->botvars.style = style;
p->botvars.difficulty = botdifficulty; p->botvars.difficulty = botdifficulty;
p->rings = rings; p->rings = rings;
p->botvars.diffincrease = botdiffincrease; p->botvars.diffincrease = botdiffincrease;

View file

@ -168,6 +168,8 @@ void K_SetBot(UINT8 newplayernum, UINT16 skinnum, UINT8 difficulty, botStyle_e s
SetPlayerSkinByNum(newplayernum, skinnum); SetPlayerSkinByNum(newplayernum, skinnum);
LUA_HookPlayer(&players[newplayernum], HOOK(BotJoin));
for (UINT8 i = 0; i < PWRLV_NUMTYPES; i++) for (UINT8 i = 0; i < PWRLV_NUMTYPES; i++)
{ {
clientpowerlevels[newplayernum][i] = 0; clientpowerlevels[newplayernum][i] = 0;
@ -395,6 +397,17 @@ void K_UpdateMatchRaceBots(void)
--------------------------------------------------*/ --------------------------------------------------*/
boolean K_PlayerUsesBotMovement(const player_t *player) boolean K_PlayerUsesBotMovement(const player_t *player)
{ {
// Lua can't override the podium sequence result, but it can
// override the following results:
{
UINT8 shouldOverride = LUA_HookPlayerForceResults(const_cast<player_t*>(player),
HOOK(PlayerUsesBotMovement));
if (shouldOverride == 1)
return true;
if (shouldOverride == 2)
return false;
}
if (player->bot) if (player->bot)
return true; return true;
@ -489,11 +502,11 @@ static fixed_t K_BotSpeedScaled(const player_t *player, fixed_t speed)
} }
/*-------------------------------------------------- /*--------------------------------------------------
const botcontroller_t *K_GetBotController(const mobj_t *mobj) botcontroller_t *K_GetBotController(const mobj_t *mobj)
See header file for description. See header file for description.
--------------------------------------------------*/ --------------------------------------------------*/
const botcontroller_t *K_GetBotController(const mobj_t *mobj) botcontroller_t *K_GetBotController(const mobj_t *mobj)
{ {
botcontroller_t *ret = nullptr; botcontroller_t *ret = nullptr;

View file

@ -135,7 +135,7 @@ boolean K_BotCanTakeCut(const player_t *player);
/*-------------------------------------------------- /*--------------------------------------------------
const botcontroller_t *K_GetBotController(const mobj_t *mobj); botcontroller_t *K_GetBotController(const mobj_t *mobj);
Retrieves the current bot controller values from Retrieves the current bot controller values from
the player's current sector. the player's current sector.
@ -147,7 +147,7 @@ boolean K_BotCanTakeCut(const player_t *player);
Pointer to the sector's bot controller struct. Pointer to the sector's bot controller struct.
--------------------------------------------------*/ --------------------------------------------------*/
const botcontroller_t *K_GetBotController(const mobj_t *mobj); botcontroller_t *K_GetBotController(const mobj_t *mobj);
/*-------------------------------------------------- /*--------------------------------------------------

View file

@ -19,7 +19,7 @@
#include "k_kart.h" #include "k_kart.h"
#include "m_random.h" #include "m_random.h"
#include "r_things.h" #include "r_things.h"
#include "lua_hook.h" // LUA_HookGPRankPoints #include "lua_hook.h"
struct grandprixinfo grandprixinfo; struct grandprixinfo grandprixinfo;
@ -641,6 +641,7 @@ void K_RetireBots(void)
K_SetNameForBot(i, skins[skinnum].realname); K_SetNameForBot(i, skins[skinnum].realname);
bot->score = 0; bot->score = 0;
LUA_HookPlayer(bot, HOOK(BotJoin));
bot->pflags &= ~PF_NOCONTEST; bot->pflags &= ~PF_NOCONTEST;
} }
} }

View file

@ -38,6 +38,8 @@
#include "p_spec.h" // P_StartQuake #include "p_spec.h" // P_StartQuake
#include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision
#include "v_video.h" #include "v_video.h"
#include "k_terrain.h"
#include "k_bot.h"
#include "lua_script.h" #include "lua_script.h"
#include "lua_libs.h" #include "lua_libs.h"
@ -45,8 +47,6 @@
#include "taglist.h" // P_FindSpecialLineFromTag #include "taglist.h" // P_FindSpecialLineFromTag
#include "lua_hook.h" // hook_cmd_running errors #include "lua_hook.h" // hook_cmd_running errors
#include "k_terrain.h"
#define NOHUD if (hud_running)\ #define NOHUD if (hud_running)\
return luaL_error(L, "HUD rendering code should not call this function!");\ return luaL_error(L, "HUD rendering code should not call this function!");\
else if (hook_cmd_running)\ else if (hook_cmd_running)\
@ -230,15 +230,20 @@ static const struct {
{META_LUABANKS, "luabanks[]"}, {META_LUABANKS, "luabanks[]"},
{META_ACTIVATOR, "activator_t"}, {META_ACTIVATOR, "activator_t"},
{META_FOLLOWER, "follower_t"}, {META_FOLLOWER, "follower_t"},
{META_SONICLOOPVARS, "sonicloopvars_t"},
{META_SONICLOOPCAMVARS, "sonicloopcamvars_t"},
{META_BOTVARS, "botvars_t"},
{META_BOTCONTROLLER, "botcontroller_t"},
{META_SPLASH, "t_splash_t"}, {META_SPLASH, "t_splash_t"},
{META_FOOTSTEP, "t_footstep_t"}, {META_FOOTSTEP, "t_footstep_t"},
{META_OVERLAY, "t_overlay_t"}, {META_OVERLAY, "t_overlay_t"},
{META_TERRAIN, "terrain_t"}, {META_TERRAIN, "terrain_t"},
{META_SONICLOOPVARS, "sonicloopvars_t"},
{META_SONICLOOPCAMVARS, "sonicloopcamvars_t"},
{NULL, NULL} {NULL, NULL}
}; };
@ -2484,7 +2489,6 @@ static int lib_rSetPlayerSkin(lua_State *L)
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INT32 i = -1, j = -1; INT32 i = -1, j = -1;
NOHUD NOHUD
INLEVEL
if (!player) if (!player)
return LUA_ErrInvalid(L, "player_t"); return LUA_ErrInvalid(L, "player_t");
@ -4344,6 +4348,148 @@ static int lib_kAwardScaledPlayerRings(lua_State *L)
return 0; return 0;
} }
static int lib_kPlayerUsesBotMovement(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushboolean(L, K_PlayerUsesBotMovement(player));
return 1;
}
static int lib_kBotCanTakeCut(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushboolean(L, K_BotCanTakeCut(player));
return 1;
}
static int lib_kGetBotController(lua_State *L)
{
mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
INLEVEL
if (!mobj)
return LUA_ErrInvalid(L, "mobj_t");
botcontroller_t *botController = K_GetBotController(mobj);
if (botController != NULL)
LUA_PushUserdata(L, botController, META_BOTCONTROLLER);
else
lua_pushnil(L);
return 1;
}
static int lib_kBotMapModifier(lua_State *L)
{
INLEVEL
lua_pushfixed(L, K_BotMapModifier());
return 1;
}
static int lib_kBotRubberband(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushfixed(L, K_BotRubberband(player));
return 1;
}
static int lib_kUpdateRubberband(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushfixed(L, K_UpdateRubberband(player));
return 1;
}
static int lib_kDistanceOfLineFromPoint(lua_State *L)
{
fixed_t v1x = luaL_checkfixed(L, 1);
fixed_t v1y = luaL_checkfixed(L, 2);
fixed_t v2x = luaL_checkfixed(L, 3);
fixed_t v2y = luaL_checkfixed(L, 4);
fixed_t cx = luaL_checkfixed(L, 5);
fixed_t cy = luaL_checkfixed(L, 6);
lua_pushfixed(L, K_DistanceOfLineFromPoint(v1x, v1y, v2x, v2y, cx, cy));
return 1;
}
static int lib_kAddBot(lua_State *L)
{
INT32 skinid = -1;
UINT8 difficulty = luaL_checkinteger(L, 2);
botStyle_e style = luaL_checkinteger(L, 3);
UINT8 newplayernum = 0;
// Copypaste of libd_getSprite2Patch, but fails loudly on each fail state instead.
// get skin first!
if (lua_isnumber(L, 1)) // find skin by number
{
skinid = lua_tonumber(L, 1);
if (skinid < 0 || skinid >= numskins)
return luaL_error(L, "skin number %d out of range (0 - %d)", skinid, numskins-1);
}
else // find skin by name
{
const char *name = luaL_checkstring(L, 1);
skinid = R_SkinAvailable(name);
if (skinid == -1)
return luaL_error(L, "could not find skin %s by name", name);
}
INLEVEL
boolean success = K_AddBot(skinid, difficulty, style, &newplayernum);
lua_pushboolean(L, success);
if (success)
LUA_PushUserdata(L, &players[newplayernum - 1], META_PLAYER);
else
lua_pushnil(L);
return 2;
}
static int lib_kSetNameForBot(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
const char *realname = luaL_checkstring(L, 2);
if (!player)
return LUA_ErrInvalid(L, "player_t");
if (!player->bot)
return luaL_error(L, "You may only change bot names.");
K_SetNameForBot(player-players, realname);
return 0;
}
static int lib_kRemoveBot(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
if (!player->bot)
return luaL_error(L, "You may only remove bots.");
CL_RemovePlayer(player-players, KR_LEAVE);
return 0;
}
static int lib_kNextRespawnWaypointIndex(lua_State *L) static int lib_kNextRespawnWaypointIndex(lua_State *L)
{ {
waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT));
@ -5141,6 +5287,19 @@ static luaL_Reg lib[] = {
// I_System // I_System
{"I_GetPreciseTime",lib_iGetPreciseTime}, {"I_GetPreciseTime",lib_iGetPreciseTime},
// k_bot
{"K_PlayerUsesBotMovement", lib_kPlayerUsesBotMovement},
{"K_BotCanTakeCut", lib_kBotCanTakeCut},
{"K_GetBotController", lib_kGetBotController},
{"K_BotMapModifier", lib_kBotMapModifier},
{"K_BotRubberband", lib_kBotRubberband},
{"K_UpdateRubberband", lib_kUpdateRubberband},
{"K_DistanceOfLineFromPoint", lib_kDistanceOfLineFromPoint},
{"K_AddBot", lib_kAddBot},
{"K_SetNameForBot", lib_kSetNameForBot},
// Lua-only function to allow safely removing bots.
{"K_RemoveBot", lib_kRemoveBot},
// k_terrain // k_terrain
{"K_AffectingTerrainActive", lib_kAffectingTerrainActive}, {"K_AffectingTerrainActive", lib_kAffectingTerrainActive},
{"K_GetDefaultTerrain", lib_kGetDefaultTerrain}, {"K_GetDefaultTerrain", lib_kGetDefaultTerrain},

185
src/lua_botvarslib.c Normal file
View file

@ -0,0 +1,185 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// 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 "fastcmp.h"
#include "lua_script.h"
#include "lua_libs.h"
enum botvars {
botvars_valid = 0,
botvars_style,
botvars_difficulty,
botvars_diffincrease,
botvars_rival,
botvars_rubberband,
/*
botvars_itemdelay,
botvars_itemconfirm,
botvars_turnconfirm,
botvars_spindashconfirm,
botvars_respawnconfirm,
botvars_roulettepriority,
botvars_roulettetimeout,
*/
};
static const char *const botvars_opt[] = {
"valid",
"style",
"difficulty",
"diffincrease",
"rival",
"rubberband",
/*
"itemdelay",
"itemconfirm",
"turnconfirm",
"spindashconfirm",
"respawnconfirm",
"roulettepriority",
"roulettetimeout",
*/
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;
/*
case botvars_itemdelay:
lua_pushinteger(L, botvars->itemdelay);
break;
case botvars_itemconfirm:
lua_pushinteger(L, botvars->itemconfirm);
break;
case botvars_turnconfirm:
lua_pushinteger(L, botvars->turnconfirm);
break;
case botvars_spindashconfirm:
lua_pushinteger(L, botvars->spindashconfirm);
break;
case botvars_respawnconfirm:
lua_pushinteger(L, botvars->respawnconfirm);
break;
case botvars_roulettepriority:
lua_pushinteger(L, botvars->roulettePriority);
break;
case botvars_roulettetimeout:
lua_pushinteger(L, botvars->rouletteTimeout);
break;
*/
}
return 1;
}
#define NOSET luaL_error(L, LUA_QL("itemroulette_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;
/*
case botvars_itemdelay:
botvars->itemdelay = luaL_checkinteger(L, 3);
break;
case botvars_itemconfirm:
botvars->itemconfirm = luaL_checkinteger(L, 3);
break;
case botvars_turnconfirm:
botvars->turnconfirm = luaL_checkinteger(L, 3);
break;
case botvars_spindashconfirm:
botvars->spindashconfirm = luaL_checkinteger(L, 3);
break;
case botvars_respawnconfirm:
botvars->respawnconfirm = luaL_checkinteger(L, 3);
break;
case botvars_roulettepriority:
botvars->roulettePriority = luaL_checkinteger(L, 3);
break;
case botvars_roulettetimeout:
botvars->rouletteTimeout = luaL_checkinteger(L, 3);
break;
*/
}
return 0;
}
#undef NOSET
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);
return 0;
}

View file

@ -82,6 +82,8 @@ automatically.
X (PlayerCmd),/* building the player's ticcmd struct */\ X (PlayerCmd),/* building the player's ticcmd struct */\
X (MusicChange),\ X (MusicChange),\
X (VoteThinker),/* Y_VoteTicker */\ X (VoteThinker),/* Y_VoteTicker */\
X (PlayerUsesBotMovement),/* K_PlayerUsesBotMovement */\
X (BotJoin),\
X (GPRankPoints),/* K_CalculateGPRankPoints */\ X (GPRankPoints),/* K_CalculateGPRankPoints */\
X (AddonLoaded),\ X (AddonLoaded),\
@ -136,6 +138,7 @@ void LUA_HookInt(INT32 integer, int hook);
void LUA_HookGamemap(int hook); void LUA_HookGamemap(int hook);
void LUA_HookBool(boolean value, int hook); void LUA_HookBool(boolean value, int hook);
int LUA_HookPlayer(player_t *, int hook); int LUA_HookPlayer(player_t *, int hook);
int LUA_HookPlayerForceResults(player_t *, int hook);
int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook);
int LUA_HookKey(event_t *event, int hook); // Hooks for key events int LUA_HookKey(event_t *event, int hook); // Hooks for key events

View file

@ -676,6 +676,17 @@ int LUA_HookPlayer(player_t *player, int hook_type)
return hook.status; return hook.status;
} }
int LUA_HookPlayerForceResults(player_t *player, int hook_type)
{
Hook_State hook;
if (prepare_hook(&hook, 0, hook_type))
{
LUA_PushUserdata(gL, player, META_PLAYER);
call_hooks(&hook, 1, res_force);
}
return hook.status;
}
int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type)
{ {
Hook_State hook; Hook_State hook;

View file

@ -107,14 +107,17 @@ extern lua_State *gL;
#define META_FOLLOWER "FOLLOWER_T*" #define META_FOLLOWER "FOLLOWER_T*"
#define META_WAYPOINT "WAYPOINT_T*" #define META_WAYPOINT "WAYPOINT_T*"
#define META_SONICLOOPVARS "SONICLOOPVARS_T*"
#define META_SONICLOOPCAMVARS "SONICLOOPCAMVARS_T*"
#define META_BOTVARS "BOTVARS_T*"
#define META_BOTCONTROLLER "BOTCONTROLLER_T*"
#define META_SPLASH "T_SPLASH_T*" #define META_SPLASH "T_SPLASH_T*"
#define META_FOOTSTEP "T_FOOTSTEP_T*" #define META_FOOTSTEP "T_FOOTSTEP_T*"
#define META_OVERLAY "T_OVERLAY_T*" #define META_OVERLAY "T_OVERLAY_T*"
#define META_TERRAIN "TERRAIN_T*" #define META_TERRAIN "TERRAIN_T*"
#define META_SONICLOOPVARS "SONICLOOPVARS_T*"
#define META_SONICLOOPCAMVARS "SONICLOOPCAMVARS_T*"
boolean luaL_checkboolean(lua_State *L, int narg); boolean luaL_checkboolean(lua_State *L, int narg);
int LUA_EnumLib(lua_State *L); int LUA_EnumLib(lua_State *L);
@ -134,6 +137,7 @@ int LUA_PolyObjLib(lua_State *L);
int LUA_BlockmapLib(lua_State *L); int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L); int LUA_HudLib(lua_State *L);
int LUA_FollowerLib(lua_State *L); int LUA_FollowerLib(lua_State *L);
int LUA_BotVarsLib(lua_State *L);
int LUA_TerrainLib(lua_State *L); int LUA_TerrainLib(lua_State *L);
int LUA_WaypointLib(lua_State *L); int LUA_WaypointLib(lua_State *L);

View file

@ -57,6 +57,7 @@ enum sector_e {
sector_triggerer, sector_triggerer,
sector_friction, sector_friction,
sector_gravity, sector_gravity,
sector_botcontroller,
sector_action, sector_action,
sector_args, sector_args,
sector_stringargs, sector_stringargs,
@ -91,6 +92,7 @@ static const char *const sector_opt[] = {
"triggerer", "triggerer",
"friction", "friction",
"gravity", "gravity",
"botcontroller",
"action", "action",
"args" "args"
"stringargs", "stringargs",
@ -409,6 +411,20 @@ static const char *const activator_opt[] = {
"sector", "sector",
"po", "po",
NULL}; NULL};
enum botcontroller_e {
botcontroller_valid = 0,
//botcontroller_trick,
botcontroller_flags,
botcontroller_forceangle,
};
static const char *const botcontroller_opt[] = {
"valid",
//"trick",
"flags",
"forceangle",
NULL};
static const char *const array_opt[] ={"iterate",NULL}; static const char *const array_opt[] ={"iterate",NULL};
static const char *const valid_opt[] ={"valid",NULL}; static const char *const valid_opt[] ={"valid",NULL};
@ -767,6 +783,9 @@ static int sector_get(lua_State *L)
case sector_gravity: // gravity case sector_gravity: // gravity
lua_pushfixed(L, sector->gravity); lua_pushfixed(L, sector->gravity);
return 1; return 1;
case sector_botcontroller: // botController
LUA_PushUserdata(L, &sector->botController, META_BOTCONTROLLER);
return 1;
case sector_action: // action case sector_action: // action
lua_pushinteger(L, (INT16)sector->action); lua_pushinteger(L, (INT16)sector->action);
return 1; return 1;
@ -2690,6 +2709,49 @@ static int activator_get(lua_State *L)
return 0; return 0;
} }
/////////////////////
// botcontroller_t //
/////////////////////
static int botcontroller_get(lua_State *L)
{
botcontroller_t *botcontroller = *((botcontroller_t **)luaL_checkudata(L, 1, META_BOTCONTROLLER));
enum botcontroller_e field = luaL_checkoption(L, 2, botcontroller_opt[0], botcontroller_opt);
if (!botcontroller)
{
if (field == botcontroller_valid) {
lua_pushboolean(L, false);
return 1;
}
return luaL_error(L, "accessed botcontroller_t doesn't exist anymore.");
}
switch (field)
{
case botcontroller_valid:
lua_pushboolean(L, true);
return 1;
//case botcontroller_trick:
//lua_pushinteger(L, botcontroller->trick);
//return 1;
case botcontroller_flags:
lua_pushinteger(L, botcontroller->flags);
return 1;
case botcontroller_forceangle:
lua_pushangle(L, botcontroller->forceAngle);
return 1;
default:
break;
}
return 0;
}
int LUA_MapLib(lua_State *L) int LUA_MapLib(lua_State *L)
{ {
luaL_newmetatable(L, META_SECTORLINES); luaL_newmetatable(L, META_SECTORLINES);
@ -2856,6 +2918,12 @@ int LUA_MapLib(lua_State *L)
lua_pushcfunction(L, activator_get); lua_pushcfunction(L, activator_get);
lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "__index");
lua_pop(L, 1); lua_pop(L, 1);
luaL_newmetatable(L, META_BOTCONTROLLER);
lua_pushcfunction(L, botcontroller_get);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
LUA_PushTaggableObjectArray(L, "sectors", LUA_PushTaggableObjectArray(L, "sectors",
lib_iterateSectors, lib_iterateSectors,

View file

@ -345,6 +345,7 @@ enum player_e
player_spectator, player_spectator,
player_spectatewait, player_spectatewait,
player_bot, player_bot,
player_botvars,
player_jointime, player_jointime,
player_spectatorreentry, player_spectatorreentry,
player_grieftime, player_grieftime,
@ -539,6 +540,7 @@ static const char *const player_opt[] = {
"spectator", "spectator",
"spectatewait", "spectatewait",
"bot", "bot",
"botvars",
"jointime", "jointime",
"spectatorreentry", "spectatorreentry",
"grieftime", "grieftime",
@ -1135,6 +1137,9 @@ static int player_get(lua_State *L)
case player_bot: case player_bot:
lua_pushboolean(L, plr->bot); lua_pushboolean(L, plr->bot);
break; break;
case player_botvars:
LUA_PushUserdata(L, &plr->botvars, META_BOTVARS);
break;
case player_jointime: case player_jointime:
lua_pushinteger(L, plr->jointime); lua_pushinteger(L, plr->jointime);
break; break;
@ -1786,6 +1791,8 @@ static int player_set(lua_State *L)
break; break;
case player_bot: case player_bot:
return NOSET; return NOSET;
case player_botvars:
return NOSET;
case player_jointime: case player_jointime:
plr->jointime = (tic_t)luaL_checkinteger(L, 3); plr->jointime = (tic_t)luaL_checkinteger(L, 3);
break; break;

View file

@ -60,6 +60,7 @@ static lua_CFunction liblist[] = {
LUA_BlockmapLib, // blockmap stuff LUA_BlockmapLib, // blockmap stuff
LUA_HudLib, // HUD stuff LUA_HudLib, // HUD stuff
LUA_FollowerLib, // follower_t, followers[] LUA_FollowerLib, // follower_t, followers[]
LUA_BotVarsLib, // botvars_t
LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t
LUA_WaypointLib, // waypoint_t LUA_WaypointLib, // waypoint_t
NULL NULL

View file

@ -396,6 +396,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
// botvars_t // botvars_t
WRITEUINT8(save->p, players[i].bot); WRITEUINT8(save->p, players[i].bot);
WRITEUINT8(save->p, players[i].botvars.style);
WRITEUINT8(save->p, players[i].botvars.difficulty); WRITEUINT8(save->p, players[i].botvars.difficulty);
WRITEUINT8(save->p, players[i].botvars.diffincrease); WRITEUINT8(save->p, players[i].botvars.diffincrease);
WRITEUINT8(save->p, players[i].botvars.rival); WRITEUINT8(save->p, players[i].botvars.rival);
@ -743,6 +744,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
// botvars_t // botvars_t
players[i].bot = READUINT8(save->p); players[i].bot = READUINT8(save->p);
players[i].botvars.style = READUINT8(save->p);
players[i].botvars.difficulty = READUINT8(save->p); players[i].botvars.difficulty = READUINT8(save->p);
players[i].botvars.diffincrease = READUINT8(save->p); players[i].botvars.diffincrease = READUINT8(save->p);
players[i].botvars.rival = (boolean)READUINT8(save->p); players[i].botvars.rival = (boolean)READUINT8(save->p);

View file

@ -2934,6 +2934,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
} }
else if (P_IsMachineLocalPlayer(player)) else if (P_IsMachineLocalPlayer(player))
{ {
// Players-turned-bots outside of end of race contexts (Lua)
// don't update their local camera angle, so it should be updated
// somewhere - I choose here because it makes the most sense.
if (K_PlayerUsesBotMovement(player) && !player->bot)
P_ForceLocalAngle(player, mo->angle, true);
focusangle = localangle[num]; focusangle = localangle[num];
focusaiming = localaiming[num]; focusaiming = localaiming[num];
} }