From dc9bec000a1814fe321e21bf67e37acf6fbb1083 Mon Sep 17 00:00:00 2001 From: JugadorXEI Date: Fri, 22 Aug 2025 01:24:13 +0000 Subject: [PATCH] Bot library for Lua (botvars getter/setter, bot functions) --- src/Sourcefile | 1 + src/deh_tables.c | 43 +++++++++- src/g_game.c | 3 + src/k_bot.cpp | 17 +++- src/k_bot.h | 4 +- src/k_grandprix.c | 3 +- src/lua_baselib.c | 173 ++++++++++++++++++++++++++++++++++++++-- src/lua_botvarslib.c | 185 +++++++++++++++++++++++++++++++++++++++++++ src/lua_hook.h | 3 + src/lua_hooklib.c | 11 +++ src/lua_libs.h | 10 ++- src/lua_maplib.c | 68 ++++++++++++++++ src/lua_playerlib.c | 7 ++ src/lua_script.c | 1 + src/p_saveg.c | 2 + src/p_user.c | 6 ++ 16 files changed, 521 insertions(+), 16 deletions(-) create mode 100644 src/lua_botvarslib.c diff --git a/src/Sourcefile b/src/Sourcefile index e13a8a47f..c5e65c549 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -109,6 +109,7 @@ lua_maplib.c lua_taglib.c lua_polyobjlib.c lua_blockmaplib.c +lua_botvarslib.c lua_hudlib.c lua_hudlib_drawlist.c lua_followerlib.c diff --git a/src/deh_tables.c b/src/deh_tables.c index 257a99d0d..33f08ace5 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -27,11 +27,11 @@ #include "r_data.h" // patchalphastyle_t #include "k_boss.h" // spottype_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 "k_kart.h" // awardscaledrings_t #include "k_waypoint.h" // waypoint values (for lua) - #include "deh_tables.h" #include "fastcmp.h" @@ -1553,6 +1553,47 @@ struct int_const_s const INT_CONST[] = { {"SNEAKERTYPE_ROCKETSNEAKER", SNEAKERTYPE_ROCKETSNEAKER}, {"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 {"TOV_UNDEFINED",TOV_UNDEFINED}, {"TOV_STILL",TOV_STILL}, diff --git a/src/g_game.c b/src/g_game.c index c69297469..f0b166710 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2410,6 +2410,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) boolean spectator; boolean bot; UINT8 botdifficulty; + botStyle_e style; INT16 rings; angle_t playerangleturn; @@ -2469,6 +2470,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) followitem = players[player].followitem; bot = players[player].bot; + style = players[player].botvars.style; botdifficulty = players[player].botvars.difficulty; botdiffincrease = players[player].botvars.diffincrease; @@ -2631,6 +2633,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->totalring = totalring; p->bot = bot; + p->botvars.style = style; p->botvars.difficulty = botdifficulty; p->rings = rings; p->botvars.diffincrease = botdiffincrease; diff --git a/src/k_bot.cpp b/src/k_bot.cpp index a3cdcc02e..1b07c657e 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -168,6 +168,8 @@ void K_SetBot(UINT8 newplayernum, UINT16 skinnum, UINT8 difficulty, botStyle_e s SetPlayerSkinByNum(newplayernum, skinnum); + LUA_HookPlayer(&players[newplayernum], HOOK(BotJoin)); + for (UINT8 i = 0; i < PWRLV_NUMTYPES; i++) { clientpowerlevels[newplayernum][i] = 0; @@ -395,6 +397,17 @@ void K_UpdateMatchRaceBots(void) --------------------------------------------------*/ 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), + HOOK(PlayerUsesBotMovement)); + if (shouldOverride == 1) + return true; + if (shouldOverride == 2) + return false; + } + if (player->bot) 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. --------------------------------------------------*/ -const botcontroller_t *K_GetBotController(const mobj_t *mobj) +botcontroller_t *K_GetBotController(const mobj_t *mobj) { botcontroller_t *ret = nullptr; diff --git a/src/k_bot.h b/src/k_bot.h index c01787d0b..a8cae46a4 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -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 the player's current sector. @@ -147,7 +147,7 @@ boolean K_BotCanTakeCut(const player_t *player); 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); /*-------------------------------------------------- diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 74bfd4776..464655449 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -19,7 +19,7 @@ #include "k_kart.h" #include "m_random.h" #include "r_things.h" -#include "lua_hook.h" // LUA_HookGPRankPoints +#include "lua_hook.h" struct grandprixinfo grandprixinfo; @@ -641,6 +641,7 @@ void K_RetireBots(void) K_SetNameForBot(i, skins[skinnum].realname); bot->score = 0; + LUA_HookPlayer(bot, HOOK(BotJoin)); bot->pflags &= ~PF_NOCONTEST; } } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 81d01d62b..460912f77 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -38,6 +38,8 @@ #include "p_spec.h" // P_StartQuake #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision #include "v_video.h" +#include "k_terrain.h" +#include "k_bot.h" #include "lua_script.h" #include "lua_libs.h" @@ -45,8 +47,6 @@ #include "taglist.h" // P_FindSpecialLineFromTag #include "lua_hook.h" // hook_cmd_running errors -#include "k_terrain.h" - #define NOHUD if (hud_running)\ return luaL_error(L, "HUD rendering code should not call this function!");\ else if (hook_cmd_running)\ @@ -230,15 +230,20 @@ static const struct { {META_LUABANKS, "luabanks[]"}, {META_ACTIVATOR, "activator_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_FOOTSTEP, "t_footstep_t"}, {META_OVERLAY, "t_overlay_t"}, {META_TERRAIN, "terrain_t"}, - - {META_SONICLOOPVARS, "sonicloopvars_t"}, - {META_SONICLOOPCAMVARS, "sonicloopcamvars_t"}, + {NULL, NULL} }; @@ -2484,7 +2489,6 @@ static int lib_rSetPlayerSkin(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); INT32 i = -1, j = -1; NOHUD - INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); @@ -4344,6 +4348,148 @@ static int lib_kAwardScaledPlayerRings(lua_State *L) 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) { waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT)); @@ -5141,6 +5287,19 @@ static luaL_Reg lib[] = { // I_System {"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_AffectingTerrainActive", lib_kAffectingTerrainActive}, {"K_GetDefaultTerrain", lib_kGetDefaultTerrain}, diff --git a/src/lua_botvarslib.c b/src/lua_botvarslib.c new file mode 100644 index 000000000..0d22d34e5 --- /dev/null +++ b/src/lua_botvarslib.c @@ -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; +} diff --git a/src/lua_hook.h b/src/lua_hook.h index eb0db2a0f..8e715193a 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -82,6 +82,8 @@ automatically. X (PlayerCmd),/* building the player's ticcmd struct */\ X (MusicChange),\ X (VoteThinker),/* Y_VoteTicker */\ + X (PlayerUsesBotMovement),/* K_PlayerUsesBotMovement */\ + X (BotJoin),\ X (GPRankPoints),/* K_CalculateGPRankPoints */\ X (AddonLoaded),\ @@ -136,6 +138,7 @@ void LUA_HookInt(INT32 integer, int hook); void LUA_HookGamemap(int hook); void LUA_HookBool(boolean value, 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_HookKey(event_t *event, int hook); // Hooks for key events diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 7c7c06a82..c1d059fb1 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -676,6 +676,17 @@ int LUA_HookPlayer(player_t *player, int hook_type) 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) { Hook_State hook; diff --git a/src/lua_libs.h b/src/lua_libs.h index d0d9f4b60..3fbdc8f32 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -107,14 +107,17 @@ extern lua_State *gL; #define META_FOLLOWER "FOLLOWER_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_FOOTSTEP "T_FOOTSTEP_T*" #define META_OVERLAY "T_OVERLAY_T*" #define META_TERRAIN "TERRAIN_T*" -#define META_SONICLOOPVARS "SONICLOOPVARS_T*" -#define META_SONICLOOPCAMVARS "SONICLOOPCAMVARS_T*" - boolean luaL_checkboolean(lua_State *L, int narg); int LUA_EnumLib(lua_State *L); @@ -134,6 +137,7 @@ int LUA_PolyObjLib(lua_State *L); int LUA_BlockmapLib(lua_State *L); int LUA_HudLib(lua_State *L); int LUA_FollowerLib(lua_State *L); +int LUA_BotVarsLib(lua_State *L); int LUA_TerrainLib(lua_State *L); int LUA_WaypointLib(lua_State *L); diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 2305a4d7a..4d3f050dd 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -57,6 +57,7 @@ enum sector_e { sector_triggerer, sector_friction, sector_gravity, + sector_botcontroller, sector_action, sector_args, sector_stringargs, @@ -91,6 +92,7 @@ static const char *const sector_opt[] = { "triggerer", "friction", "gravity", + "botcontroller", "action", "args" "stringargs", @@ -409,6 +411,20 @@ static const char *const activator_opt[] = { "sector", "po", 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 valid_opt[] ={"valid",NULL}; @@ -767,6 +783,9 @@ static int sector_get(lua_State *L) case sector_gravity: // gravity lua_pushfixed(L, sector->gravity); return 1; + case sector_botcontroller: // botController + LUA_PushUserdata(L, §or->botController, META_BOTCONTROLLER); + return 1; case sector_action: // action lua_pushinteger(L, (INT16)sector->action); return 1; @@ -2690,6 +2709,49 @@ static int activator_get(lua_State *L) 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) { luaL_newmetatable(L, META_SECTORLINES); @@ -2856,6 +2918,12 @@ int LUA_MapLib(lua_State *L) lua_pushcfunction(L, activator_get); lua_setfield(L, -2, "__index"); 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", lib_iterateSectors, diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 9510da504..28d0c5593 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -345,6 +345,7 @@ enum player_e player_spectator, player_spectatewait, player_bot, + player_botvars, player_jointime, player_spectatorreentry, player_grieftime, @@ -539,6 +540,7 @@ static const char *const player_opt[] = { "spectator", "spectatewait", "bot", + "botvars", "jointime", "spectatorreentry", "grieftime", @@ -1135,6 +1137,9 @@ static int player_get(lua_State *L) case player_bot: lua_pushboolean(L, plr->bot); break; + case player_botvars: + LUA_PushUserdata(L, &plr->botvars, META_BOTVARS); + break; case player_jointime: lua_pushinteger(L, plr->jointime); break; @@ -1786,6 +1791,8 @@ static int player_set(lua_State *L) break; case player_bot: return NOSET; + case player_botvars: + return NOSET; case player_jointime: plr->jointime = (tic_t)luaL_checkinteger(L, 3); break; diff --git a/src/lua_script.c b/src/lua_script.c index d8e680b54..f646db2ac 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -60,6 +60,7 @@ static lua_CFunction liblist[] = { LUA_BlockmapLib, // blockmap stuff LUA_HudLib, // HUD stuff LUA_FollowerLib, // follower_t, followers[] + LUA_BotVarsLib, // botvars_t LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t LUA_WaypointLib, // waypoint_t NULL diff --git a/src/p_saveg.c b/src/p_saveg.c index d40572f1e..3121d26d0 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -396,6 +396,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) // botvars_t 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.diffincrease); WRITEUINT8(save->p, players[i].botvars.rival); @@ -743,6 +744,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) // botvars_t players[i].bot = READUINT8(save->p); + players[i].botvars.style = READUINT8(save->p); players[i].botvars.difficulty = READUINT8(save->p); players[i].botvars.diffincrease = READUINT8(save->p); players[i].botvars.rival = (boolean)READUINT8(save->p); diff --git a/src/p_user.c b/src/p_user.c index 61df99ac8..612229f0e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2934,6 +2934,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } 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]; focusaiming = localaiming[num]; }