From 6feb25cefeaad792efa461eaf384d63c0b1f4748 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Thu, 14 Aug 2025 23:00:33 +0200 Subject: [PATCH 01/13] (WIP) Rewrite bot code to be much more sane Bot logic is now handled exclusively by the server, now in the actual game loop code, rather than during ticcmd generation. No TICCMD_BOT needed! botdata_t has been added to store any variables that clients don't need. Behavior differences: * Brakedrifting actually works now... to the detriment of the bots * Respawn triggers 1 second sooner (and actually causes braking) * No longer spams respawn when a track lacks waypoints * Turning might be less responsive? `botvars.turnconfirm += cmd.bot.turnconfirm` is gone... who knows * No longer affected by timescale Plus, no more outdated or copy-pasted comment blocks! --- src/d_player.h | 33 +- src/d_ticcmd.h | 7 - src/g_demo.c | 54 +-- src/g_game.c | 10 +- src/k_bot.cpp | 600 ++++++++++++++++--------------- src/k_bot.h | 90 +++-- src/k_botitem.cpp | 840 ++++++++++++++------------------------------ src/k_botsearch.cpp | 52 ++- src/k_hud.c | 29 +- src/k_kart.c | 57 ++- src/p_saveg.c | 16 - src/p_tick.c | 7 + src/typedef.h | 2 +- 13 files changed, 693 insertions(+), 1104 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 1282b4ad8..8f4478ae7 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -419,17 +419,6 @@ typedef enum // for kickstartaccel #define ACCEL_KICKSTART (TICRATE) -// Minimum percentage for a (non-auto) drift to begin. -#define BOTDRIFTPERCENT (10) - -// Minimum turning percentage for an auto drift to begin. -#define DRIFTSTARTPCT (45) - -#define BOTDRIFTTICS (2*TICRATE/3) -#define BOTDRIFTLOCKOUT (TICRATE/2) - -#define MAXDRIFTSKILL (FRACUNIT/2) - typedef enum { BOT_STYLE_NORMAL, @@ -439,15 +428,6 @@ typedef enum BOT_STYLE__MAX } botStyle_e; -typedef enum -{ - DRIFTSTATE_AUTO, - DRIFTSTATE_STARTING, - DRIFTSTATE_ACTIVE, - DRIFTSTATE_ENDING, - NUMDRIFTSTATES -} botdrift_t; - // player_t struct for all bot variables struct botvars_t { @@ -460,19 +440,8 @@ struct botvars_t fixed_t rubberband; // Bot rubberband value UINT16 controller; // Special bot controller linedef ID - tic_t itemdelay; // Delay before using item at all - tic_t itemconfirm; // When high enough, they will use their item - - SINT8 turnconfirm; // Confirm turn direction - UINT32 respawnconfirm; // Confirm when respawn is needed. - - // Drift-relevant data below: fixed_t driftskill; // The bot's "skill" at drifts. - // Determines how soon a bot starts to drift. - botdrift_t driftstate; // Drifting state - SINT8 driftturn; // Drifting turn direction - tic_t drifttime; // Time spent drifting - tic_t driftlockout; // do not allow drifting for this many tics + // Determines how soon a bot starts to drift. }; struct sonicloopcamvars_t diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index da14ded9e..b4137c712 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -57,7 +57,6 @@ typedef enum #define TICCMD_RECEIVED (0x01) /* Actual tic recieved from client */ #define TICCMD_TYPING (0x02) /* chat window or console open */ #define TICCMD_KEYSTROKE (0x04) /* chat character input */ -#define TICCMD_BOT (0x80) /* generated by bot, demos write bot variables */ #if defined(_MSC_VER) #pragma pack(1) @@ -74,12 +73,6 @@ struct ticcmd_t UINT16 buttons; UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end? UINT8 flags; - struct - { - SINT8 turnconfirm; - SINT8 itemconfirm; - SINT8 respawnconfirm; - } bot; } ATTRPACK; #if defined(_MSC_VER) diff --git a/src/g_demo.c b/src/g_demo.c index f2ea59870..aa890f090 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -146,13 +146,8 @@ demoghost *ghosts = NULL; #define ZT_AIMING 0x0040 #define ZT_LATENCY 0x0080 #define ZT_FLAGS 0x0100 -#define ZT_BOT 0x8000 // Ziptics are UINT16 now, go nuts -#define ZT_BOT_TURN 0x0001 -#define ZT_BOT_ITEM 0x0002 -#define ZT_BOT_RESPAWN 0x0004 - #define DEMOMARKER 0x80 // demobuf.end UINT8 demo_extradata[MAXPLAYERS]; @@ -398,16 +393,12 @@ static UINT8 *G_ReadZipTic(ticcmd_t *cmd, UINT8 *dp, UINT16 version) if (ziptic & ZT_FLAGS) cmd->flags = READUINT8(dp); - if (ziptic & ZT_BOT) + if (version < 0x0009 && ziptic & 0x8000) // ZT_BOT { UINT16 botziptic = READUINT16(dp); - - if (botziptic & ZT_BOT_TURN) - cmd->bot.turnconfirm = READSINT8(dp); - if (botziptic & ZT_BOT_ITEM) - cmd->bot.itemconfirm = READSINT8(dp); - if (botziptic & ZT_BOT_RESPAWN) - cmd->bot.respawnconfirm = READSINT8(dp); + if (botziptic & 1) READSINT8(dp); + if (botziptic & 2) READSINT8(dp); + if (botziptic & 4) READSINT8(dp); } return dp; } @@ -1403,45 +1394,8 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) ziptic |= ZT_FLAGS; } - if (cmd->flags & TICCMD_BOT) - { - ziptic |= ZT_BOT; - } - WRITEUINT16(ziptic_p, ziptic); - if (ziptic & ZT_BOT) - { - UINT16 botziptic = 0; - UINT8 *botziptic_p; - - botziptic_p = demobuf.p; // the ziptic, written at the end of this function - demobuf.p += 2; - - if (cmd->bot.turnconfirm != oldcmd[playernum].bot.turnconfirm) - { - WRITESINT8(demobuf.p, cmd->bot.turnconfirm); - oldcmd[playernum].bot.turnconfirm = cmd->bot.turnconfirm; - botziptic |= ZT_BOT_TURN; - } - - if (cmd->bot.itemconfirm != oldcmd[playernum].bot.itemconfirm) - { - WRITESINT8(demobuf.p, cmd->bot.itemconfirm); - oldcmd[playernum].bot.itemconfirm = cmd->bot.itemconfirm; - botziptic |= ZT_BOT_ITEM; - } - - if (cmd->bot.respawnconfirm != oldcmd[playernum].bot.respawnconfirm) - { - WRITESINT8(demobuf.p, cmd->bot.respawnconfirm); - oldcmd[playernum].bot.respawnconfirm = cmd->bot.respawnconfirm; - botziptic |= ZT_BOT_RESPAWN; - } - - WRITEUINT16(botziptic_p, botziptic); - } - // attention here for the ticcmd size! // latest demos with mouse aiming byte in ticcmd if (!(demoflags & DF_GHOST) && ziptic_p > demobuf.end - 9) diff --git a/src/g_game.c b/src/g_game.c index b1a2d075a..c69297469 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1363,13 +1363,6 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) dest[i].buttons = (UINT16)SHORT(src[i].buttons); dest[i].latency = src[i].latency; dest[i].flags = src[i].flags; - - if (dest[i].flags & TICCMD_BOT) - { - dest[i].bot.itemconfirm = src[i].bot.itemconfirm; - dest[i].bot.turnconfirm = src[i].bot.turnconfirm; - dest[i].bot.respawnconfirm = src[i].bot.respawnconfirm; - } } return dest; } @@ -2670,7 +2663,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->botvars.controller = UINT16_MAX; p->botvars.driftskill = FixedMul(MAXDRIFTSKILL, K_BotDetermineDriftSkill(p)); - p->botvars.driftstate = DRIFTSTATE_AUTO; + + K_BotRespawn(p); if (follower) P_RemoveMobj(follower); diff --git a/src/k_bot.cpp b/src/k_bot.cpp index f042699c9..06b3a3579 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -42,10 +42,46 @@ #endif #include "i_net.h" // doomcom #include "blan/b_soc.h" +#include "v_video.h" // for debugging consvar_t cv_forcebots = CVAR_INIT ("kartforcebots", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL); consvar_t cv_botcontrol = CVAR_INIT ("kartbotcontrol", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL); +botdata_t botdata[MAXPLAYERS]; + +void K_DrawBotDebugger(const player_t *player) +{ + INT32 vflags = V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_30TRANS; + botdata_t *bd = &botdata[player - players]; + + static const char *driftstates[] = { + "auto", // DRIFTSTATE_AUTO + "starting", // DRIFTSTATE_STARTING + "active", // DRIFTSTATE_ACTIVE + "ending", // DRIFTSTATE_ENDING + }; + + if (!cv_kartdebugbot.value || !player->bot) + return; + + INT32 x1 = 26, x2 = 99, y = 92; + + V_DrawThinString(x1, y+0, vflags, va("predict.x: %d", bd->predict.x/FRACUNIT)); + V_DrawThinString(x2, y+0, vflags, va("predict.y: %d", bd->predict.y/FRACUNIT)); + V_DrawThinString(x1, y+8, vflags, va("predict.radius: %d", bd->predict.radius/FRACUNIT)); + V_DrawThinString(x2, y+8, vflags, va("predict.baseradius: %d", bd->predict.baseRadius/FRACUNIT)); + + V_DrawThinString(x1, y+16, vflags, va("itemconfirm: %d", bd->itemconfirm)); + V_DrawThinString(x2, y+16, vflags, va("itemdelay: %d", bd->itemdelay)); + V_DrawThinString(x1, y+24, vflags, va("turnconfirm: %d", bd->turnconfirm)); + V_DrawThinString(x2, y+24, vflags, va("respawnconfirm: %d", bd->respawnconfirm)); + + V_DrawThinString(x1, y+32, vflags, va("driftstate: %s", driftstates[bd->driftstate])); + V_DrawThinString(x2, y+32, vflags|(bd->driftlockout ? V_ORANGEMAP : 0), va("driftlockout: %d", bd->driftlockout)); + V_DrawThinString(x1, y+40, vflags, va("driftturn: %d", bd->driftturn)); + V_DrawThinString(x2, y+40, vflags, va("drifttime: %d", bd->drifttime)); +} + /*-------------------------------------------------- void K_SetNameForBot(UINT8 playerNum, const char *realname) @@ -838,18 +874,8 @@ static fixed_t K_ScaleWPDistWithSlope(fixed_t disttonext, angle_t angletonext, c return disttonext; } -/*-------------------------------------------------- - static botprediction_t *K_CreateBotPrediction(const player_t *player) - - Calculates a point further along the track to attempt to drive towards. - - Input Arguments:- - player - Player to compare. - - Return:- - Bot prediction struct. ---------------------------------------------------*/ -static botprediction_t *K_CreateBotPrediction(player_t *player) +// Calculates a point further along the track to attempt to drive towards. +static boolean K_CreateBotPrediction(botdata_t *bd, player_t *player) { ZoneScoped; @@ -884,17 +910,14 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) boolean pathfindsuccess = false; path_t pathtofinish = {0}; - botprediction_t *predict = nullptr; size_t i; if (wp == nullptr || P_MobjWasRemoved(wp->mobj) == true) { // Can't do any of this if we don't have a waypoint. - return nullptr; + return false; } - predict = static_cast(Z_Calloc(sizeof(botprediction_t), PU_LEVEL, nullptr)); - // Init defaults in case of pathfind failure angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y); @@ -950,22 +973,22 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) // Set our predicted point's coordinates, // and use the smallest radius of all of the waypoints in the chain! - predict->x = wp->mobj->x; - predict->y = wp->mobj->y; + bd->predict.x = wp->mobj->x; + bd->predict.y = wp->mobj->y; - predict->baseRadius = radius; - predict->radius = FixedMul(radiusScaled, radReduce); + bd->predict.baseRadius = radius; + bd->predict.radius = FixedMul(radiusScaled, radReduce); // Set the prediction coordinates between the 2 waypoints if there's still distance left. if (distanceleft > 0) { // Scaled with the leftover anglemul! - predict->x += P_ReturnThrustX(nullptr, angletonext, std::min(disttonext, distanceleft) * FRACUNIT); - predict->y += P_ReturnThrustY(nullptr, angletonext, std::min(disttonext, distanceleft) * FRACUNIT); + bd->predict.x += P_ReturnThrustX(nullptr, angletonext, std::min(disttonext, distanceleft) * FRACUNIT); + bd->predict.y += P_ReturnThrustY(nullptr, angletonext, std::min(disttonext, distanceleft) * FRACUNIT); } ps_bots[player - players].prediction += I_GetPreciseTime() - time; - return predict; + return true; } /*-------------------------------------------------- @@ -980,19 +1003,18 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) Return:- None --------------------------------------------------*/ -static void K_DrawPredictionDebug(botprediction_t *predict, const player_t *player) +static void K_DrawPredictionDebug(botdata_t *bd, const player_t *player) { mobj_t *debugMobj = nullptr; angle_t sideAngle = ANGLE_MAX; UINT8 i = UINT8_MAX; - I_Assert(predict != nullptr); I_Assert(player != nullptr); I_Assert(player->mo != nullptr && P_MobjWasRemoved(player->mo) == false); sideAngle = player->mo->angle + ANGLE_90; - debugMobj = P_SpawnMobj(predict->x, predict->y, player->mo->z, MT_SPARK); + debugMobj = P_SpawnMobj(bd->predict.x, bd->predict.y, player->mo->z, MT_SPARK); P_SetMobjState(debugMobj, S_THOK); debugMobj->frame &= ~FF_TRANSMASK; @@ -1001,22 +1023,22 @@ static void K_DrawPredictionDebug(botprediction_t *predict, const player_t *play debugMobj->color = SKINCOLOR_ORANGE; P_SetScale(debugMobj, debugMobj->destscale * 2); - debugMobj->tics = 2; + debugMobj->tics = 1; for (i = 0; i < 2; i++) { mobj_t *radiusMobj = nullptr; - fixed_t radiusX = predict->x, radiusY = predict->y; + fixed_t radiusX = bd->predict.x, radiusY = bd->predict.y; if (i & 1) { - radiusX -= FixedMul(predict->radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT)); - radiusY -= FixedMul(predict->radius, FINESINE(sideAngle >> ANGLETOFINESHIFT)); + radiusX -= FixedMul(bd->predict.radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT)); + radiusY -= FixedMul(bd->predict.radius, FINESINE(sideAngle >> ANGLETOFINESHIFT)); } else { - radiusX += FixedMul(predict->radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT)); - radiusY += FixedMul(predict->radius, FINESINE(sideAngle >> ANGLETOFINESHIFT)); + radiusX += FixedMul(bd->predict.radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT)); + radiusY += FixedMul(bd->predict.radius, FINESINE(sideAngle >> ANGLETOFINESHIFT)); } radiusMobj = P_SpawnMobj(radiusX, radiusY, player->mo->z, MT_SPARK); @@ -1028,7 +1050,7 @@ static void K_DrawPredictionDebug(botprediction_t *predict, const player_t *play radiusMobj->color = SKINCOLOR_YELLOW; P_SetScale(debugMobj, debugMobj->destscale / 2); - radiusMobj->tics = 2; + radiusMobj->tics = 1; } } @@ -1069,32 +1091,24 @@ static void K_WaypointGetDirectionVector(waypoint_t *wp1, waypoint_t *wp2, vecto See header file for description. --------------------------------------------------*/ -void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout) +void K_BotSetDriftState(player_t *player, botdrift_e newstate, tic_t lockout) { - if (newstate != player->botvars.driftstate) + botdata_t *bd = &botdata[player - players]; + + if (newstate != bd->driftstate) { - player->botvars.driftstate = newstate; - player->botvars.drifttime = 0; + bd->driftstate = newstate; + bd->drifttime = 0; } - if (lockout > player->botvars.driftlockout) - player->botvars.driftlockout = lockout; + if (lockout > bd->driftlockout) + bd->driftlockout = lockout; } #define MINBOTDRIFT (KART_FULLTURN * 2) / 3 // 0.66 -/*-------------------------------------------------- - static INT32 K_BotStartDrift(player_t* player) - - Begins and ends "forced" drifts on a per-waypoint basis. - - Input Arguments:- - player - Player to begin the drift for. - - Return:- - Override value for turn amount. ---------------------------------------------------*/ -static void K_BotStartDrift(player_t* player) +// Begins and ends "forced" drifts on a per-waypoint basis. +static void K_BotStartDrift(botdata_t *bd, player_t* player) { // Handle DRIFTING towards waypoints! boolean shouldDrift; @@ -1116,17 +1130,17 @@ static void K_BotStartDrift(player_t* player) return; } - if (player->botvars.driftlockout) + if (bd->driftlockout) { // things are not working out in our favor - player->botvars.driftlockout--; + bd->driftlockout--; return; } // check for waypoints ahead of us with drift settings, based on our current speed path_t path = {0}; INT32 maxdist = FixedInt(cv_test1.value) + gamespeed*50; - maxdist = FixedMul(maxdist, speedfactor * (player->botvars.driftstate == DRIFTSTATE_ACTIVE ? 1 : 2)); + maxdist = FixedMul(maxdist, speedfactor * (bd->driftstate == DRIFTSTATE_ACTIVE ? 1 : 2)); if (maxdist >= 0 && K_PathfindThruCircuit(player->currentwaypoint, maxdist, &path, false, false)) { @@ -1156,11 +1170,11 @@ static void K_BotStartDrift(player_t* player) if (driftsetting == DRIFT_END) { - if (player->botvars.driftstate != DRIFTSTATE_AUTO) + if (bd->driftstate != DRIFTSTATE_AUTO) K_BotSetDriftState(player, DRIFTSTATE_ENDING, 0); } else if (driftsetting > DRIFT_NONE && driftsetting < DRIFT_END - && player->botvars.driftstate == DRIFTSTATE_AUTO) + && bd->driftstate == DRIFTSTATE_AUTO) { // Randomly decide to drift based on our skill at drifting, // and how fast we're moving. @@ -1195,26 +1209,14 @@ static void K_BotStartDrift(player_t* player) break; } - player->botvars.driftturn = driftturn; + bd->driftturn = driftturn; K_BotSetDriftState(player, DRIFTSTATE_STARTING, 0); } } } -/*-------------------------------------------------- - static INT32 K_HandleBotTrack(const player_t *player, ticcmd_t *cmd, botprediction_t *predict) - - Determines inputs for standard track driving. - - Input Arguments:- - player - Player to generate the ticcmd for. - cmd - The player's ticcmd to modify. - predict - Pointer to the bot's prediction. - - Return:- - New value for turn amount. ---------------------------------------------------*/ -static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *predict, angle_t destangle) +// Determines inputs for standard track driving. +static INT32 K_HandleBotTrack(botdata_t *bd, player_t *player, angle_t destangle) { ZoneScoped; @@ -1225,15 +1227,13 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * INT32 anglediff, anglediff2; fixed_t speedfactor = FixedDiv(player->speed, K_GetKartSpeed(player, false, false)); - I_Assert(predict != nullptr); - moveangle = player->mo->angle; anglediff = AngleDeltaSigned(moveangle, destangle); // line up for an incoming drift - if (player->botvars.driftstate == DRIFTSTATE_STARTING) + if (bd->driftstate == DRIFTSTATE_STARTING) { - anglediff += FixedMul(ANG10-ANG2, speedfactor) * player->botvars.driftturn; + anglediff += FixedMul(ANG10-ANG2, speedfactor) * bd->driftturn; } if (anglediff < 0) @@ -1252,18 +1252,18 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * if (anglediff > ANGLE_67h) { // Wrong way! - cmd->forwardmove = -MAXPLMOVE; - cmd->buttons |= BT_BRAKE; + bd->acceldown = false; + bd->brakedown = true; } else { const fixed_t playerwidth = (player->mo->radius * 2); - fixed_t realrad = predict->radius*3/4; // Remove a "safe" distance away from the edges of the road + fixed_t realrad = bd->predict.radius*3/4; // Remove a "safe" distance away from the edges of the road fixed_t rad = realrad; fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT), - predict->x, predict->y + bd->predict.x, bd->predict.y ); if (realrad < playerwidth) @@ -1296,11 +1296,11 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * } // Full speed ahead! - cmd->buttons |= BT_ACCELERATE; - cmd->forwardmove = MAXPLMOVE; + bd->acceldown = true; + bd->brakedown = false; if (dirdist <= rad - && player->botvars.driftstate != DRIFTSTATE_STARTING) // steer towards waypoints when starting drift + && bd->driftstate != DRIFTSTATE_STARTING) // steer towards waypoints when starting drift { // Going the right way, don't turn at all. turnamt = 0; @@ -1310,11 +1310,11 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * //INT32 mindriftamt = FixedMul(MINBOTDRIFT * (cv_kartspeed.value + 1), 2 * FRACUNIT); // Start or continue a drift. - if (player->botvars.driftstate == DRIFTSTATE_ACTIVE || player->botvars.driftstate == DRIFTSTATE_ENDING) + if (bd->driftstate == DRIFTSTATE_ACTIVE || bd->driftstate == DRIFTSTATE_ENDING) { - cmd->buttons |= BT_DRIFT; + bd->driftdown = true; - fixed_t angofs = K_GetKartSpeedFromStat(5 - (player->kartspeed - 5), false) * -player->botvars.driftturn; + fixed_t angofs = K_GetKartSpeedFromStat(5 - (player->kartspeed - 5), false) * -bd->driftturn; // adjust for speed angofs = FixedMul(angofs, speedfactor - (2-gamespeed)*FRACUNIT/4); @@ -1326,16 +1326,16 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * // brakedrift if we're steering too hard if (abs(driftpower) >= FRACUNIT) - cmd->buttons |= BT_BRAKE; + bd->brakedown = true; // get the raw turn value and "invert" it (higher weight needs harder steering!) - INT16 turnvalue = abs(K_GetKartTurnValue(player, KART_FULLTURN * (player->botvars.driftturn < 0 ? 1 : -1))); + INT16 turnvalue = abs(K_GetKartTurnValue(player, KART_FULLTURN * (bd->driftturn < 0 ? 1 : -1))); turnvalue = 541 - (turnvalue - 541); // weight 5 = 541 turnamt = std::clamp(FixedMul(driftpower, turnvalue), -KART_FULLTURN, KART_FULLTURN); } /* - else if ((turnamt) && (player->botvars.driftstate == DRIFTSTATE_AUTO) && + else if ((turnamt) && (bd->driftstate == DRIFTSTATE_AUTO) && (turnpower > FixedPercentage(DRIFTSTARTPCT))) { // TODO: Figure out a drift prediction system. @@ -1348,6 +1348,38 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * #undef MINBOTDRIFT +static void K_IncrementBotRespawn(player_t *player, UINT8 *respawn, const UINT8 respawnmax) +{ + const fixed_t requireDist = (12*player->mo->scale) / FRACUNIT; + INT32 progress = player->distancetofinishprev - player->distancetofinish; + boolean exceptions = ( + (leveltime < starttime) + || player->flashing != 0 + || player->spinouttimer != 0 + || player->airtime > 3*TICRATE/2 + || (player->justbumped > 0 && player->justbumped < bumptime-1) + ); + + if (!exceptions && (progress < requireDist)) + { + if (*respawn < respawnmax) + { + // Making no progress, start counting against you. + *respawn = *respawn + 1; + if (progress < -requireDist && *respawn < respawnmax) + { + // Making NEGATIVE progress? Start counting even harder. + *respawn = *respawn + 1; + } + } + } + else if (*respawn > 0) + { + // Playing normally. + *respawn = *respawn - 1; + } +} + /*-------------------------------------------------- static INT32 K_HandleBotReverse(const player_t *player, ticcmd_t *cmd, botprediction_t *predict) @@ -1493,26 +1525,24 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * return turnamt; }*/ -/*-------------------------------------------------- - static void K_BuildBotTiccmdNormal(const player_t *player, ticcmd_t *cmd) - - Build ticcmd for bots with a style of BOT_STYLE_NORMAL ---------------------------------------------------*/ -static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd) +// updates server-sided bot logic +void K_BotTicker(player_t *player) { - precise_t t = 0; - - botprediction_t *predict = nullptr; - auto predict_finally = srb2::finally([&predict]() { Z_Free(predict); }); - angle_t destangle = 0; INT32 turnamt = 0; + botdata_t *bd = &botdata[player - players]; + bd->itemwasdown = bd->itemdown; + bd->acceldown = bd->brakedown = bd->driftdown = bd->itemdown = false; + bd->dolookback = false; + bd->itemthrow = 0; + if (!(gametyperules & GTR_BOTS) // No bot behaviors || K_GetNumWaypoints() == 0 // No waypoints || leveltime <= introtime // During intro camera || player->playerstate == PST_DEAD // Dead, respawning. - || player->mo->scale <= 1) // Post-finish "death" animation + || player->mo->scale <= 1 // Post-finish "death" animation + || player->spectator) // spectating { // No need to do anything else. return; @@ -1539,53 +1569,57 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd) if (player->exiting) { - //Bot finish // TODO: Make bots spin around like a player would based on random chance return; } - if (player->botvars.respawnconfirm >= BOTRESPAWNCONFIRM) + // Is a bot not making any progress? Kill it and respawn at next waypoint. + K_IncrementBotRespawn(player, &bd->respawnconfirm, BOTRESPAWNCONFIRM); + + if (bd->respawnconfirm > BOTRESPAWNCONFIRM - TICRATE) { // We want to respawn. Simply hold brake and stop here! - cmd->buttons &= ~BT_ACCELERATE|BT_DRIFT|BT_ATTACK; + bd->acceldown = false; + K_BotSetDriftState(player, DRIFTSTATE_AUTO, TICRATE); if (player->speed > 0) - { - cmd->buttons |= (BT_BRAKE); - cmd->bot.respawnconfirm++; - } + bd->brakedown = true; - if ((player->speed < 10*FRACUNIT)) + if (bd->respawnconfirm >= BOTRESPAWNCONFIRM || player->speed < 10*FRACUNIT) { - cmd->bot.respawnconfirm = TICRATE; + // Now a clean function! Neat, eh? + K_SetRespawnAtNextWaypoint(player); + + // WHAT ARE YOU DOING??? RACE ALREADY! + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); } return; } - else - { - cmd->bot.respawnconfirm = 0; - } + destangle = player->mo->angle; + // it's time to start predicting + memset(&bd->predict, 0, sizeof(bd->predict)); + boolean forcedDir = false; if (botController != nullptr && (botController->flags & TMBOT_FORCEDIR) == TMBOT_FORCEDIR) { const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale; // Overwritten prediction - predict = static_cast(Z_Calloc(sizeof(botprediction_t), PU_STATIC, nullptr)); - - predict->x = player->mo->x + FixedMul(dist, FINECOSINE(botController->forceAngle >> ANGLETOFINESHIFT)); - predict->y = player->mo->y + FixedMul(dist, FINESINE(botController->forceAngle >> ANGLETOFINESHIFT)); - predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale; + bd->predict = { + .x = player->mo->x + FixedMul(dist, FINECOSINE(botController->forceAngle >> ANGLETOFINESHIFT)), + .y = player->mo->y + FixedMul(dist, FINESINE(botController->forceAngle >> ANGLETOFINESHIFT)), + .radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale, + }; forcedDir = true; } if (forcedDir == true) { - destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); - turnamt = K_HandleBotTrack(player, cmd, predict, destangle); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y); + turnamt = K_HandleBotTrack(bd, player, destangle); } else if (leveltime <= starttime) { @@ -1594,210 +1628,69 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd) if (player->botvars.difficulty > 4) { if (leveltime >= starttime-TICRATE-TICRATE/7+finaltiming) - { - cmd->buttons |= BT_ACCELERATE; - cmd->forwardmove = MAXPLMOVE; - } + bd->acceldown = true; } } else { - // Handle steering towards waypoints! - if (predict == nullptr) + // Create a prediction. + if (K_CreateBotPrediction(bd, player)) { - // Create a prediction. - predict = K_CreateBotPrediction(player); - } - - if (predict != nullptr) - { - K_NudgePredictionTowardsObjects(predict, player); - destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); - turnamt = K_HandleBotTrack(player, cmd, predict, destangle); + K_NudgePredictionTowardsObjects(bd, player); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y); + turnamt = K_HandleBotTrack(bd, player, destangle); } } + if (turnamt > KART_FULLTURN) + { + turnamt = KART_FULLTURN; + } + else if (turnamt < -KART_FULLTURN) + { + turnamt = -KART_FULLTURN; + } + + bd->turnamt = turnamt; + if (player->exiting == 0) { // TODO: Allowing projectile items like orbinaut while e-braking would be nice, maybe just pass in the spindash variable? - t = I_GetPreciseTime(); - K_BotItemUsage(player, cmd, turnamt); + precise_t t = I_GetPreciseTime(); + K_BotItemUsage(bd, player); ps_bots[player - players].item = I_GetPreciseTime() - t; } - if (turnamt != 0) + if (turnamt > 0) { - if (turnamt > KART_FULLTURN) + // Count up + if (bd->turnconfirm < BOTTURNCONFIRM) { - turnamt = KART_FULLTURN; + bd->turnconfirm++; } - else if (turnamt < -KART_FULLTURN) + } + else if (turnamt < 0) + { + // Count down + if (bd->turnconfirm > -BOTTURNCONFIRM) { - turnamt = -KART_FULLTURN; + bd->turnconfirm--; } - - if (turnamt > 0) + } + else + { + // Back to neutral + if (bd->turnconfirm < 0) { - // Count up - if (player->botvars.turnconfirm < BOTTURNCONFIRM) - { - cmd->bot.turnconfirm++; - } + bd->turnconfirm++; } - else if (turnamt < 0) + else if (bd->turnconfirm > 0) { - // Count down - if (player->botvars.turnconfirm > -BOTTURNCONFIRM) - { - cmd->bot.turnconfirm--; - } - } - else - { - // Back to neutral - if (player->botvars.turnconfirm < 0) - { - cmd->bot.turnconfirm++; - } - else if (player->botvars.turnconfirm > 0) - { - cmd->bot.turnconfirm--; - } - } - - if (abs(player->botvars.turnconfirm) >= BOTTURNCONFIRM) - { - // You're commiting to your turn, you're allowed! - cmd->turning = turnamt; - if (P_CanPlayerTurn(player, cmd)) - cmd->angle += K_GetKartTurnValue(player, turnamt); + bd->turnconfirm--; } } - // Free the prediction we made earlier - if (predict != nullptr) - { - if (cv_kartdebugbot.value != 0 && player - players == displayplayers[0] && !(paused || P_AutoPause())) - { - K_DrawPredictionDebug(predict, player); - } - } -} - -/*-------------------------------------------------- - void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) - - See header file for description. ---------------------------------------------------*/ -void K_BuildBotTiccmd( - player_t *player, // annoyingly NOT const because of LUA_HookTiccmd... grumble grumble - ticcmd_t *cmd) -{ - ZoneScoped; - - // Remove any existing controls - memset(cmd, 0, sizeof(ticcmd_t)); - - if (player->mo == nullptr - || player->spectator == true - || G_GamestateUsesLevel() == false) - { - // Not in the level. - return; - } - - cmd->angle = player->mo->angle >> TICCMD_REDUCE; - - // Complete override of all ticcmd functionality. - // May add more hooks to individual pieces of bot ticcmd, - // but this should always be here so anyone can roll - // their own :) - if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true) - { - cmd->flags |= TICCMD_BOT; - return; - } - - cmd->flags |= TICCMD_BOT; - - switch (player->botvars.style) - { - case BOT_STYLE_STAY: - { - // Hey, this one's pretty easy :P - break; - } - default: - { - K_BuildBotTiccmdNormal(player, cmd); - break; - } - } -} - -static void K_IncrementBotRespawn(player_t *player, UINT32 *respawn, const UINT32 respawnmax) -{ - const fixed_t requireDist = (12*player->mo->scale) / FRACUNIT; - INT32 progress = player->distancetofinishprev - player->distancetofinish; - boolean exceptions = ( - (leveltime < starttime) - || player->flashing != 0 - || player->spinouttimer != 0 - || player->airtime > 3*TICRATE/2 - || (player->justbumped > 0 && player->justbumped < bumptime-1) - ); - - if (!exceptions && (progress < requireDist)) - { - if (*respawn < respawnmax) - { - // Making no progress, start counting against you. - *respawn = *respawn + 1; - if (progress < -requireDist && *respawn < respawnmax) - { - // Making NEGATIVE progress? Start counting even harder. - *respawn = *respawn + 1; - } - } - } - else if (*respawn > 0) - { - // Playing normally. - *respawn = *respawn - 1; - } -} - -/*-------------------------------------------------- - void K_UpdateBotGameplayVars(player_t *player); - - See header file for description. ---------------------------------------------------*/ -void K_UpdateBotGameplayVars(player_t *player) -{ - if (gamestate != GS_LEVEL || !player->mo) - { - // Not in the level. - return; - } - - player->botvars.rubberband = K_UpdateRubberband(player); - - player->botvars.turnconfirm += player->cmd.bot.turnconfirm; - - // Is a bot not making any progress? Kill it and respawn at next waypoint. - K_IncrementBotRespawn(player, &player->botvars.respawnconfirm, BOTRESPAWNCONFIRM); - - if ((player->cmd.bot.respawnconfirm >= TICRATE) && (player->botvars.respawnconfirm >= BOTRESPAWNCONFIRM)) - { - // Now a clean function! Neat, eh? - K_SetRespawnAtNextWaypoint(player); - - // WHAT ARE YOU DOING??? RACE ALREADY! - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); - - player->botvars.respawnconfirm = 0; - } - else if (player->cmd.forwardmove < 0) + if (!bd->acceldown && bd->brakedown) { // stop drifting if we're reversing K_BotSetDriftState(player, DRIFTSTATE_AUTO, BOTDRIFTLOCKOUT); @@ -1807,15 +1700,15 @@ void K_UpdateBotGameplayVars(player_t *player) // Figure out if we need to drift. // Drift-ending waypoints will kill the drift timer, // so no need to worry about doing that ourselves. - K_BotStartDrift(player); + K_BotStartDrift(bd, player); INT32 limit = FixedInt(cv_test3.value) - gamespeed*5; - INT32 dtime = ++player->botvars.drifttime; + INT32 dtime = ++bd->drifttime; // the faster we are going, the sooner we need to drift fixed_t speedfactor = FixedDiv(player->speed, K_GetKartSpeed(player, false, false)); - switch (player->botvars.driftstate) + switch (bd->driftstate) { case DRIFTSTATE_STARTING: limit = std::max(0, limit - FixedMul(TICRATE/5, speedfactor)); @@ -1838,5 +1731,108 @@ void K_UpdateBotGameplayVars(player_t *player) } } + if (cv_kartdebugbot.value != 0 && player - players == displayplayers[0] && !(paused || P_AutoPause())) + { + K_DrawPredictionDebug(bd, player); + } +} + +/*-------------------------------------------------- + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) + + See header file for description. +--------------------------------------------------*/ +void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) +{ + ZoneScoped; + + botdata_t *bd = &botdata[player - players]; + + // Remove any existing controls + memset(cmd, 0, sizeof(ticcmd_t)); + + if (player->mo == nullptr + || player->spectator == true + || G_GamestateUsesLevel() == false) + { + // Not in the level. + return; + } + + cmd->angle = player->mo->angle >> TICCMD_REDUCE; + + // Complete override of all ticcmd functionality. + // May add more hooks to individual pieces of bot ticcmd, + // but this should always be here so anyone can roll + // their own :) + if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true) + return; + + if (player->botvars.style == BOT_STYLE_STAY) + { + // Hey, this one's pretty easy :P + return; + } + + // BOT_STYLE_NORMAL is the only other style, so... + cmd->forwardmove = !!bd->acceldown * MAXPLMOVE - !!bd->brakedown * MAXPLMOVE/2; + if (bd->itemthrow != 0) + { + cmd->throwdir = bd->itemthrow * KART_FULLTURN; + cmd->buttons |= bd->itemthrow > 0 ? BT_FORWARD : BT_BACKWARD; + } + + if (bd->acceldown) + cmd->buttons |= BT_ACCELERATE; + if (bd->brakedown) + cmd->buttons |= BT_BRAKE; + if (bd->driftdown) + cmd->buttons |= BT_DRIFT; + if (bd->itemdown) + cmd->buttons |= BT_ATTACK; + + if (bd->dolookback) + cmd->buttons |= BT_LOOKBACK; + + if (abs(bd->turnconfirm) >= BOTTURNCONFIRM) + { + // You're commiting to your turn, you're allowed! + cmd->turning = bd->turnamt; + if (P_CanPlayerTurn(player, cmd)) + cmd->angle += K_GetKartTurnValue(player, bd->turnamt); + } +} + +/*-------------------------------------------------- + void K_UpdateBotGameplayVars(player_t *player); + + See header file for description. +--------------------------------------------------*/ +void K_UpdateBotGameplayVars(player_t *player) +{ + if (gamestate != GS_LEVEL || !player->mo) + { + // Not in the level. + return; + } + + player->botvars.rubberband = K_UpdateRubberband(player); + K_UpdateBotGameplayVarsItemUsage(player); } + +// resets some botdata stuff after respawning +void K_BotRespawn(player_t *player) +{ + botdata_t *bd = &botdata[player - players]; + memset(bd, 0, sizeof(*bd)); + bd->driftstate = DRIFTSTATE_AUTO; +} + +void K_BotResetItemConfirm(player_t *player, boolean setdelay) +{ + botdata_t *bd = &botdata[player - players]; + bd->itemconfirm = 0; + if (setdelay) + bd->itemdelay = TICRATE; +} diff --git a/src/k_bot.h b/src/k_bot.h index 139b0d46e..ddfbe368e 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -44,15 +44,60 @@ extern consvar_t cv_botcontrol; // How long it takes for a Lv.1 bot to decide to pick an item. #define BOT_ITEM_DECISION_TIME (2*TICRATE) -// Point for bots to aim for -struct botprediction_t +// Minimum percentage for a (non-auto) drift to begin. +#define BOTDRIFTPERCENT (10) + +// Minimum turning percentage for an auto drift to begin. +#define DRIFTSTARTPCT (45) + +#define BOTDRIFTTICS (2*TICRATE/3) +#define BOTDRIFTLOCKOUT (TICRATE/2) + +#define MAXDRIFTSKILL (FRACUNIT/2) + +typedef enum { - fixed_t x, y; - fixed_t radius, baseRadius; + DRIFTSTATE_AUTO, + DRIFTSTATE_STARTING, + DRIFTSTATE_ACTIVE, + DRIFTSTATE_ENDING, + NUMDRIFTSTATES +} ATTRPACK botdrift_e; + +// unsynced bot variables, exclusively for use by bot logic and ticcmd generation +// nobody except the server cares about this stuff! +struct botdata_t +{ + // Point for bots to aim for + struct + { + fixed_t x, y; + fixed_t radius, baseRadius; + } predict; + + tic_t itemdelay; // Delay before using item at all + tic_t itemconfirm; // When high enough, they will use their item + + SINT8 turnconfirm; // Confirm turn direction + UINT8 respawnconfirm; // Confirm when respawn is needed. + + botdrift_e driftstate; // Drifting state + SINT8 driftturn; // Drifting turn direction + tic_t drifttime; // Time spent drifting + tic_t driftlockout; // do not allow drifting for this many tics + + boolean acceldown, brakedown, driftdown, itemdown; // buttons + boolean dolookback; // look out behind you + boolean itemwasdown; // last tic's item button + SINT8 itemthrow; // throwdir + INT16 turnamt; // turning }; // AVAILABLE FOR LUA +void K_BotTicker(player_t *player); + +void K_DrawBotDebugger(const player_t *player); /*-------------------------------------------------- boolean K_PlayerUsesBotMovement(const player_t *player); @@ -278,21 +323,8 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y); boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y); - -/*-------------------------------------------------- - void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player); - - Moves the bot's prediction, based on objects around the bot. - - Input Arguments:- - predict - The bot's prediction to nudge. - player - Player to compare. - - Return:- - None ---------------------------------------------------*/ - -void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *player); +// Moves the bot's prediction, based on objects around the bot. +void K_NudgePredictionTowardsObjects(botdata_t *bd, const player_t *player); /*-------------------------------------------------- @@ -359,22 +391,12 @@ void K_UpdateBotGameplayVarsItemUsage(player_t *player); void K_UpdateBotGameplayVars(player_t *player); +// Item usage part of ticcmd generation. +void K_BotItemUsage(botdata_t *bd, player_t *player); -/*-------------------------------------------------- - void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt); +void K_BotRespawn(player_t *player); - Item usage part of ticcmd generation. - - Input Arguments:- - player - Player to generate the ticcmd for. - cmd - The player's ticcmd to modify. - turnamt - How hard the bot is turning. - - Return:- - None ---------------------------------------------------*/ - -void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt); +void K_BotResetItemConfirm(player_t *player, boolean setdelay); /*-------------------------------------------------- fixed_t K_BotDetermineDriftSkill(player_t *player) @@ -405,7 +427,7 @@ fixed_t K_BotDetermineDriftSkill(player_t *player); None --------------------------------------------------*/ -void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout); +void K_BotSetDriftState(player_t *player, botdrift_e newstate, tic_t lockout); #ifdef __cplusplus diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index d4529bcc7..a4cc8854d 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -24,52 +24,22 @@ #include "byteptr.h" #include "d_net.h" // nodetoplayer #include "k_kart.h" -#include "z_zone.h" #include "i_system.h" #include "p_maputl.h" -#include "d_ticcmd.h" #include "m_random.h" #include "r_things.h" // numskins #include "m_easing.h" -/*-------------------------------------------------- - static inline boolean K_ItemButtonWasDown(const player_t *player) - - Looks for players around the bot, and presses the item button - if there is one in range. - - Input Arguments:- - player - Bot to check. - - Return:- - true if the item button was pressed last tic, otherwise false. ---------------------------------------------------*/ -static inline boolean K_ItemButtonWasDown(const player_t *player) -{ - return (player->oldcmd.buttons & BT_ATTACK); -} - -/*-------------------------------------------------- - static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fixed_t radius) - - Looks for players around the bot, and presses the item button - if there is one in range. - - Input Arguments:- - player - Bot to compare against. - cmd - The bot's ticcmd. - radius - The radius to look for players in. - - Return:- - true if a player was found & we can press the item button, otherwise false. ---------------------------------------------------*/ -static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fixed_t radius) +// Looks for players around the bot, and presses the item button +// if there is one in range. +// Returns true if a player was found & we can press the item button, otherwise false. +static boolean K_BotUseItemNearPlayer(botdata_t *bd, const player_t *player, fixed_t radius) { ZoneScoped; UINT8 i; - if (K_ItemButtonWasDown(player) == true) + if (bd->itemwasdown) { return false; } @@ -101,7 +71,7 @@ static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fix if (dist <= radius) { - cmd->buttons |= BT_ATTACK; + bd->itemdown = true; return true; } } @@ -311,21 +281,10 @@ static boolean K_RivalBotAggression(const player_t *bot, const player_t *target) return false; } -/*-------------------------------------------------- - static void K_ItemConfirmForTarget(const player_t *bot, ticcmd_t *cmd, const player_t *target, UINT16 amount) - - Handles updating item confirm values for offense items. - - Input Arguments:- - bot - Bot to check. - cmd - Bot's ticcmd to edit. - target - Who the bot wants to attack. - amount - Amount to increase item confirm time by. - - Return:- - None ---------------------------------------------------*/ -static void K_ItemConfirmForTarget(const player_t *bot, ticcmd_t *cmd, const player_t *target, UINT16 amount) +// Handles updating item confirm values for offense items. +// target - Who the bot wants to attack. +// amount - Amount to increase item confirm time by. +static void K_ItemConfirmForTarget(botdata_t *bd, const player_t *bot, const player_t *target, UINT16 amount) { if (bot == NULL || target == NULL) { @@ -335,86 +294,55 @@ static void K_ItemConfirmForTarget(const player_t *bot, ticcmd_t *cmd, const pla if (K_RivalBotAggression(bot, target) == true) { // Double the rate when you're aggressive. - cmd->bot.itemconfirm += amount << 1; + bd->itemconfirm += amount << 1; } else { // Do as normal. - cmd->bot.itemconfirm += amount; + bd->itemconfirm += amount; } } -/*-------------------------------------------------- - static boolean K_BotGenericPressItem(const player_t *player, ticcmd_t *cmd, SINT8 dir) - Presses the item button & aim buttons for the bot. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - dir - Aiming direction: 1 for forwards, -1 for backwards, 0 for neutral. - - Return:- - true if we could press, false if not. ---------------------------------------------------*/ -static boolean K_BotGenericPressItem(const player_t *player, ticcmd_t *cmd, SINT8 dir) +// Presses the item button & aim buttons for the bot. +// dir - Aiming direction: 1 for forwards, -1 for backwards, 0 for neutral. +// Returns true if we could press, false if not. +static boolean K_BotGenericPressItem(botdata_t *bd, SINT8 dir) { ZoneScoped; - if (K_ItemButtonWasDown(player) == true) + if (bd->itemwasdown) { return false; } - cmd->throwdir = KART_FULLTURN * dir; - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 0; + bd->itemthrow = dir; + bd->itemdown = true; + //bd->itemconfirm = 0; return true; } -/*-------------------------------------------------- - static void K_BotItemGenericTap(const player_t *player, ticcmd_t *cmd) - - Item usage for generic items that you need to tap. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemGenericTap(const player_t *player, ticcmd_t *cmd) +// Item usage for generic items that you need to tap. +static void K_BotItemGenericTap(botdata_t *bd) { ZoneScoped; - if (K_ItemButtonWasDown(player) == false) + if (!bd->itemwasdown) { - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 0; + bd->itemdown = true; + //bd->itemconfirm = 0; } } -/*-------------------------------------------------- - static boolean K_BotRevealsGenericTrap(const player_t *player, INT16 turnamt, boolean mine) - - Decides if a bot is ready to reveal their trap item or not. - - Input Arguments:- - player - Bot that has the banana. - turnamt - How hard they currently are turning. - mine - Set to true to handle Mine-specific behaviors. - - Return:- - true if we want the bot to reveal their banana, otherwise false. ---------------------------------------------------*/ -static boolean K_BotRevealsGenericTrap(const player_t *player, INT16 turnamt, boolean mine) +// Decides if a bot is ready to reveal their trap item or not. +// mine - Set to true to handle Mine-specific behaviors. +static boolean K_BotRevealsGenericTrap(botdata_t *bd, const player_t *player, boolean mine) { ZoneScoped; const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - if (abs(turnamt) >= KART_FULLTURN/2) + if (abs(bd->turnamt) >= KART_FULLTURN/2) { // DON'T reveal on turns, we can place bananas on turns whenever we have multiple to spare, // or if you missed your intentioned throw/place on a player. @@ -444,21 +372,9 @@ static boolean K_BotRevealsGenericTrap(const player_t *player, INT16 turnamt, bo return false; } -/*-------------------------------------------------- - static void K_BotItemGenericTrapShield(const player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine) - - Item usage for Eggman shields. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - turnamt - How hard they currently are turning. - mine - Set to true to handle Mine-specific behaviors. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemGenericTrapShield(const player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine) +// Item usage for Eggman shields. +// mine - Set to true to handle Mine-specific behaviors. +static void K_BotItemGenericTrapShield(botdata_t *bd, player_t *player, boolean mine) { ZoneScoped; @@ -467,27 +383,16 @@ static void K_BotItemGenericTrapShield(const player_t *player, ticcmd_t *cmd, IN return; } - cmd->bot.itemconfirm++; + bd->itemconfirm++; - if (K_BotRevealsGenericTrap(player, turnamt, mine) || (player->botvars.itemconfirm > 5*TICRATE)) + if (K_BotRevealsGenericTrap(bd, player, mine) || (bd->itemconfirm > 5*TICRATE)) { - K_BotGenericPressItem(player, cmd, 0); + K_BotGenericPressItem(bd, 0); } } -/*-------------------------------------------------- - static void K_BotItemGenericOrbitShield(const player_t *player, ticcmd_t *cmd) - - Item usage for orbitting shields. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemGenericOrbitShield(const player_t *player, ticcmd_t *cmd) +// Item usage for orbitting shields. +static void K_BotItemGenericOrbitShield(botdata_t *bd, player_t *player) { ZoneScoped; @@ -496,22 +401,11 @@ static void K_BotItemGenericOrbitShield(const player_t *player, ticcmd_t *cmd) return; } - K_BotGenericPressItem(player, cmd, 0); + K_BotGenericPressItem(bd, 0); } -/*-------------------------------------------------- - static void K_BotItemSneaker(const player_t *player, ticcmd_t *cmd) - - Item usage for sneakers. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemSneaker(player_t *player, ticcmd_t *cmd) +// Item usage for sneakers. +static void K_BotItemSneaker(botdata_t *bd, player_t *player) { ZoneScoped; @@ -525,33 +419,22 @@ static void K_BotItemSneaker(player_t *player, ticcmd_t *cmd) || K_GetWaypointIsShortcut(player->nextwaypoint) == true // Going toward a shortcut! || player->speed < K_GetKartSpeed(player, false, true) / 2 // Being slowed down too much || player->speedboost > (FRACUNIT/8) // Have another type of boost (tethering) - || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long + || bd->itemconfirm > 4*TICRATE) // Held onto it for too long { - if (player->sneakertimer == 0 && K_ItemButtonWasDown(player) == false) + if (player->sneakertimer == 0 && !bd->itemwasdown) { - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 2*TICRATE; + bd->itemdown = true; + //bd->itemconfirm = 2*TICRATE; } } else { - cmd->bot.itemconfirm++; + bd->itemconfirm++; } } -/*-------------------------------------------------- - static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) - - Item usage for rocket sneakers. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemRocketSneaker(const player_t *player, ticcmd_t *cmd) +// Item usage for rocket sneakers. +static void K_BotItemRocketSneaker(botdata_t *bd, player_t *player) { ZoneScoped; @@ -561,34 +444,22 @@ static void K_BotItemRocketSneaker(const player_t *player, ticcmd_t *cmd) return; } - if (player->botvars.itemconfirm > TICRATE) + if (bd->itemconfirm > TICRATE) { - if (player->sneakertimer == 0 && K_ItemButtonWasDown(player) == false) + if (player->sneakertimer == 0 && !bd->itemwasdown) { - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 0; + bd->itemdown = true; + //bd->itemconfirm = 0; } } else { - cmd->bot.itemconfirm++; + bd->itemconfirm++; } } -/*-------------------------------------------------- - static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) - - Item usage for trap item throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - turnamt - How hard they currently are turning. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemBanana(const player_t *player, ticcmd_t *cmd, INT16 turnamt) +// Item usage for trap item throwing. +static void K_BotItemBanana(botdata_t *bd, player_t *player) { ZoneScoped; @@ -597,19 +468,19 @@ static void K_BotItemBanana(const player_t *player, ticcmd_t *cmd, INT16 turnamt boolean tryLookback = false; player_t *target = NULL; - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } - if (abs(turnamt) >= KART_FULLTURN/2) + if (abs(bd->turnamt) >= KART_FULLTURN/2) { - cmd->bot.itemconfirm += player->botvars.difficulty / 2; + bd->itemconfirm += player->botvars.difficulty / 2; throwdir = -1; } else @@ -618,36 +489,24 @@ static void K_BotItemBanana(const player_t *player, ticcmd_t *cmd, INT16 turnamt if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * 2); throwdir = 1; } } if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) + if (bd->itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } -/*-------------------------------------------------- - static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) - - Item usage for trap item throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - turnamt - How hard they currently are turning. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemMine(const player_t *player, ticcmd_t *cmd, INT16 turnamt) +// Item usage for trap item throwing. +static void K_BotItemMine(botdata_t *bd, player_t *player) { ZoneScoped; @@ -656,18 +515,18 @@ static void K_BotItemMine(const player_t *player, ticcmd_t *cmd, INT16 turnamt) boolean tryLookback = false; player_t *target = NULL; - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; } - if (abs(turnamt) >= KART_FULLTURN/2) + if (abs(bd->turnamt) >= KART_FULLTURN/2) { - cmd->bot.itemconfirm += player->botvars.difficulty / 2; + bd->itemconfirm += player->botvars.difficulty / 2; throwdir = -1; tryLookback = true; } @@ -676,82 +535,59 @@ static void K_BotItemMine(const player_t *player, ticcmd_t *cmd, INT16 turnamt) target = K_PlayerPredictThrow(player, 0); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * 2); throwdir = 0; } target = K_PlayerPredictThrow(player, 1); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * 2); throwdir = 1; } } if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) + if (bd->itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } -/*-------------------------------------------------- - static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) - - Item usage for landmine tossing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - turnamt - How hard they currently are turning. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemLandmine(const player_t *player, ticcmd_t *cmd, INT16 turnamt) +// Item usage for landmine tossing. +static void K_BotItemLandmine(botdata_t *bd, player_t *player) { ZoneScoped; const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); player_t *target = NULL; - cmd->bot.itemconfirm++; + bd->itemconfirm++; - if (abs(turnamt) >= KART_FULLTURN/2) + if (abs(bd->turnamt) >= KART_FULLTURN/2) { - cmd->bot.itemconfirm += player->botvars.difficulty / 2; + bd->itemconfirm += player->botvars.difficulty / 2; } target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); - cmd->buttons |= BT_LOOKBACK; + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); + bd->dolookback = true; } - if (player->botvars.itemconfirm > 10*TICRATE) + if (bd->itemconfirm > 10*TICRATE) { - K_BotGenericPressItem(player, cmd, -1); + K_BotGenericPressItem(bd, -1); } } -/*-------------------------------------------------- - static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) - - Item usage for Eggman item throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemEggman(const player_t *player, ticcmd_t *cmd) +// Item usage for Eggman item throwing. +static void K_BotItemEggman(botdata_t *bd, player_t *player) { ZoneScoped; @@ -761,37 +597,37 @@ static void K_BotItemEggman(const player_t *player, ticcmd_t *cmd) boolean tryLookback = false; player_t *target = NULL; - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerPredictThrow(player, 0); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty / 2); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty / 2); throwdir = 1; } target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } if (stealth > 1 || player->itemroulette) { - cmd->bot.itemconfirm += player->botvars.difficulty * 4; + bd->itemconfirm += player->botvars.difficulty * 4; throwdir = -1; } if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) + if (bd->itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } @@ -837,19 +673,8 @@ static boolean K_BotRevealsEggbox(const player_t *player) return false; } -/*-------------------------------------------------- - static void K_BotItemEggmanShield(const player_t *player, ticcmd_t *cmd) - - Item usage for Eggman shields. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemEggmanShield(const player_t *player, ticcmd_t *cmd) +// Item usage for Eggman shields. +static void K_BotItemEggmanShield(botdata_t *bd, player_t *player) { ZoneScoped; @@ -858,53 +683,31 @@ static void K_BotItemEggmanShield(const player_t *player, ticcmd_t *cmd) return; } - cmd->bot.itemconfirm++; + bd->itemconfirm++; - if (K_BotRevealsEggbox(player) == true || (player->botvars.itemconfirm > 20*TICRATE)) + if (K_BotRevealsEggbox(player) == true || (bd->itemconfirm > 20*TICRATE)) { - K_BotGenericPressItem(player, cmd, 0); + K_BotGenericPressItem(bd, 0); } } -/*-------------------------------------------------- - static void K_BotItemEggmanExplosion(const player_t *player, ticcmd_t *cmd) - - Item usage for Eggman explosions. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemEggmanExplosion(const player_t *player, ticcmd_t *cmd) +// Item usage for Eggman explosions. +static void K_BotItemEggmanExplosion(botdata_t *bd, player_t *player) { ZoneScoped; - if (player->position == 1) + if (player->position == 1 && bd->acceldown) { // Hey, we aren't gonna find anyone up here... // why don't we slow down a bit? :) - cmd->forwardmove /= 2; + bd->brakedown = true; } - K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); + K_BotUseItemNearPlayer(bd, player, 128*player->mo->scale); } -/*-------------------------------------------------- - static void K_BotItemOrbinaut(const player_t *player, ticcmd_t *cmd) - - Item usage for Orbinaut throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) +// Item usage for Orbinaut throwing. +static void K_BotItemOrbinaut(botdata_t *bd, player_t *player) { ZoneScoped; @@ -921,12 +724,12 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerInCone(player, radius, 15, false); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } else @@ -935,7 +738,7 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } @@ -943,28 +746,17 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 25*TICRATE) + if (bd->itemconfirm > 25*TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } -/*-------------------------------------------------- - static void K_BotItemBallhog(const player_t *player, ticcmd_t *cmd) - - Item usage for Ballhog throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) +// Item usage for Ballhog throwing. +static void K_BotItemBallhog(botdata_t *bd, player_t *player) { ZoneScoped; @@ -985,7 +777,7 @@ static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) target = K_PlayerInCone(player, radius, 15, false); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } else @@ -994,7 +786,7 @@ static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } @@ -1002,7 +794,7 @@ static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } if (target != NULL) @@ -1017,30 +809,19 @@ static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) // If we've been waiting for too long though, then // we'll go for the full charge :) - cmd->bot.itemconfirm++; - hold = (player->botvars.itemconfirm > 10*TICRATE); + bd->itemconfirm++; + hold = (bd->itemconfirm > 10*TICRATE); } if (hold == true) { - cmd->throwdir = KART_FULLTURN * throwdir; - cmd->buttons |= BT_ATTACK; + bd->itemthrow = throwdir; + bd->itemdown = true; } } -/*-------------------------------------------------- - static void K_BotItemJawz(const player_t *player, ticcmd_t *cmd) - - Item usage for Jawz throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) +// Item usage for Jawz throwing. +static void K_BotItemJawz(botdata_t *bd, player_t *player) { ZoneScoped; @@ -1058,12 +839,12 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerInCone(player, radius, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } @@ -1094,67 +875,45 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) if (targettedAlready == false) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } } if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 25*TICRATE) + if (bd->itemconfirm > 25*TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } -/*-------------------------------------------------- - static void K_BotItemLightning(const player_t *player, ticcmd_t *cmd) - - Item usage for Lightning Shield. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemLightning(const player_t *player, ticcmd_t *cmd) +// Item usage for Lightning Shield. +static void K_BotItemLightning(botdata_t *bd, player_t *player) { ZoneScoped; fixed_t radius = 192 * player->mo->scale; radius = Easing_Linear(FRACUNIT * player->botvars.difficulty / MAXBOTDIFFICULTY, 2*radius, radius); - if (K_BotUseItemNearPlayer(player, cmd, radius) == false) + if (K_BotUseItemNearPlayer(bd, player, radius) == false) { - if (player->botvars.itemconfirm > 10*TICRATE) + if (bd->itemconfirm > 10*TICRATE) { - K_BotGenericPressItem(player, cmd, 0); + K_BotGenericPressItem(bd, 0); } else { - cmd->bot.itemconfirm++; + bd->itemconfirm++; } } } -/*-------------------------------------------------- - static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd) - - Item usage for Bubble Shield. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd) +// Item usage for Bubble Shield. +static void K_BotItemBubble(botdata_t *bd, player_t *player) { ZoneScoped; @@ -1164,7 +923,7 @@ static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd) { UINT8 i; - cmd->bot.itemconfirm++; + bd->itemconfirm++; if (player->bubblecool <= 0) { @@ -1206,7 +965,7 @@ static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd) } else if (player->bubbleblowup >= bubbletime) { - if (player->botvars.itemconfirm > 10*TICRATE) + if (bd->itemconfirm > 10*TICRATE) { hold = true; } @@ -1218,23 +977,12 @@ static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd) if (hold && (player->itemflags & IF_HOLDREADY)) { - cmd->buttons |= BT_ATTACK; + bd->itemdown = true; } } -/*-------------------------------------------------- - static void K_BotItemFlame(const player_t *player, ticcmd_t *cmd) - - Item usage for Flame Shield. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemFlame(const player_t *player, ticcmd_t *cmd) +// Item usage for Flame Shield. +static void K_BotItemFlame(botdata_t *bd, player_t *player) { ZoneScoped; @@ -1244,24 +992,24 @@ static void K_BotItemFlame(const player_t *player, ticcmd_t *cmd) return; } - if (player->botvars.itemconfirm > TICRATE) + if (bd->itemconfirm > TICRATE) { if (player->botvars.difficulty >= 6 && player->flametimer <= 2*TICRATE) { - cmd->buttons |= BT_ATTACK; - cmd->bot.itemconfirm = 0; + bd->itemdown = true; + bd->itemconfirm = 0; } else if (player->flametimer > 0) { if (player->botvars.difficulty < 6 || player->flamestore < FLAMESTOREMAX - TICRATE/4) { - cmd->buttons |= BT_ATTACK; - cmd->bot.itemconfirm = 0; + bd->itemdown = true; + bd->itemconfirm = 0; } else { UINT8 difficultyadjust = MAXBOTDIFFICULTY - player->botvars.difficulty; - cmd->bot.itemconfirm = (-TICRATE/2) - difficultyadjust; + bd->itemconfirm = (-TICRATE/2) - difficultyadjust; } } } @@ -1269,26 +1017,15 @@ static void K_BotItemFlame(const player_t *player, ticcmd_t *cmd) { if (player->botvars.difficulty >= 6 && player->flametimer <= 2*TICRATE) { - cmd->bot.itemconfirm = 0; + bd->itemconfirm = 0; } - cmd->bot.itemconfirm++; + bd->itemconfirm++; } } -/*-------------------------------------------------- - static void K_BotItemRings(const player_t *player, ticcmd_t *cmd) - - Item usage for rings. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemRings(player_t *player, ticcmd_t *cmd) +// Item usage for rings. +static void K_BotItemRings(botdata_t *bd, player_t *player) { ZoneScoped; @@ -1300,7 +1037,7 @@ static void K_BotItemRings(player_t *player, ticcmd_t *cmd) return; } - if ((cmd->buttons & BT_ACCELERATE) == 0) + if (!bd->acceldown) { // Don't use rings if you're not trying to accelerate. return; @@ -1335,7 +1072,7 @@ static void K_BotItemRings(player_t *player, ticcmd_t *cmd) if (player->rings > saferingsval) { - cmd->buttons |= BT_ATTACK; + bd->itemdown = true; } } @@ -1396,24 +1133,13 @@ static UINT32 K_BotCalculateUseodds(player_t *player) return K_FindUseodds(player, 0, pdis, bestbumper, spbrush); } -/*-------------------------------------------------- - static void K_BotItemRouletteMash(const player_t *player, ticcmd_t *cmd) - - Item usage for item roulette mashing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) +// Item usage for item roulette mashing. +static void K_BotItemRouletteMash(botdata_t *bd, player_t *player) { boolean mash = false; UINT32 useodds = 0; - if (K_ItemButtonWasDown(player) == true) + if (bd->itemwasdown) { return; } @@ -1455,16 +1181,12 @@ static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) if (mash == true) { - cmd->buttons |= BT_ATTACK; + bd->itemdown = true; } } -/*-------------------------------------------------- - void K_BotItemUsage(const player_t *player, ticcmd_t *cmd, INT16 turnamt) - - See header file for description. ---------------------------------------------------*/ -void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) +// See header file for description. +void K_BotItemUsage(botdata_t *bd, player_t *player) { ZoneScoped; @@ -1473,142 +1195,139 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) if (player->rings > 0) { // Use rings! - K_BotItemRings(player, cmd); + K_BotItemRings(bd, player); } + return; + } + + if (bd->itemdelay) + { + bd->itemdelay--; + bd->itemconfirm = 0; + return; + } + + if (player->itemroulette) + { + // Mashing behaviors + K_BotItemRouletteMash(bd, player); + return; + } + + if (player->stealingtimer != 0) + return; + + if (player->eggmanexplode) + { + K_BotItemEggmanExplosion(bd, player); + } + else if (player->itemflags & IF_EGGMANOUT) + { + K_BotItemEggman(bd, player); + } + else if (player->rocketsneakertimer > 0) + { + K_BotItemRocketSneaker(bd, player); + } + else if (player->flametimer > 0) + { + K_BotItemFlame(bd, player); } else { - if (player->botvars.itemdelay) + switch (player->itemtype) { - return; - } + default: + if (player->itemtype != KITEM_NONE) + { + K_BotItemGenericTap(bd); + } - if (player->itemroulette) - { - // Mashing behaviors - K_BotItemRouletteMash(player, cmd); - return; - } - - if (player->stealingtimer == 0) - { - if (player->eggmanexplode) + //bd->itemconfirm = 0; + break; + case KITEM_INVINCIBILITY: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_SUPERRING: + K_BotItemGenericTap(bd); + break; + case KITEM_ROCKETSNEAKER: + if (player->rocketsneakertimer <= 0) { - K_BotItemEggmanExplosion(player, cmd); + K_BotItemGenericTap(bd); } - else if (player->itemflags & IF_EGGMANOUT) + break; + case KITEM_FLAMESHIELD: + if (player->flametimer <= 0) { - K_BotItemEggman(player, cmd); + K_BotItemGenericTap(bd); } - else if (player->rocketsneakertimer > 0) + break; + case KITEM_SNEAKER: + K_BotItemSneaker(bd, player); + break; + case KITEM_BANANA: + if (!(player->itemflags & IF_ITEMOUT)) { - K_BotItemRocketSneaker(player, cmd); - } - else if (player->flametimer > 0) - { - K_BotItemFlame(player, cmd); + K_BotItemGenericTrapShield(bd, player, false); } else { - switch (player->itemtype) - { - default: - if (player->itemtype != KITEM_NONE) - { - K_BotItemGenericTap(player, cmd); - } - - //player->botvars.itemconfirm = 0; - break; - case KITEM_INVINCIBILITY: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_SUPERRING: - K_BotItemGenericTap(player, cmd); - break; - case KITEM_ROCKETSNEAKER: - if (player->rocketsneakertimer <= 0) - { - K_BotItemGenericTap(player, cmd); - } - break; - case KITEM_FLAMESHIELD: - if (player->flametimer <= 0) - { - K_BotItemGenericTap(player, cmd); - } - break; - case KITEM_SNEAKER: - K_BotItemSneaker(player, cmd); - break; - case KITEM_BANANA: - if (!(player->itemflags & IF_ITEMOUT)) - { - K_BotItemGenericTrapShield(player, cmd, turnamt, false); - } - else - { - K_BotItemBanana(player, cmd, turnamt); - } - break; - case KITEM_EGGMAN: - K_BotItemEggmanShield(player, cmd); - break; - case KITEM_ORBINAUT: - if (!(player->itemflags & IF_ITEMOUT)) - { - K_BotItemGenericOrbitShield(player, cmd); - } - else if (player->position != 1) // Hold onto orbiting items when in 1st :) - { - K_BotItemOrbinaut(player, cmd); - } - break; - case KITEM_JAWZ: - if (!(player->itemflags & IF_ITEMOUT)) - { - K_BotItemGenericOrbitShield(player, cmd); - } - else if (player->position != 1) // Hold onto orbiting items when in 1st :) - { - K_BotItemJawz(player, cmd); - } - break; - case KITEM_MINE: - if (!(player->itemflags & IF_ITEMOUT)) - { - K_BotItemGenericTrapShield(player, cmd, turnamt, true); - } - else - { - K_BotItemMine(player, cmd, turnamt); - } - break; - case KITEM_LANDMINE: - case KITEM_HYUDORO: // Function re-use, as they have about the same usage. - K_BotItemLandmine(player, cmd, turnamt); - break; - case KITEM_BALLHOG: - K_BotItemBallhog(player, cmd); - break; - case KITEM_THUNDERSHIELD: - K_BotItemLightning(player, cmd); - break; - case KITEM_BUBBLESHIELD: - K_BotItemBubble(player, cmd); - break; - } + K_BotItemBanana(bd, player); } + break; + case KITEM_EGGMAN: + K_BotItemEggmanShield(bd, player); + break; + case KITEM_ORBINAUT: + if (!(player->itemflags & IF_ITEMOUT)) + { + K_BotItemGenericOrbitShield(bd, player); + } + else if (player->position != 1) // Hold onto orbiting items when in 1st :) + { + K_BotItemOrbinaut(bd, player); + } + break; + case KITEM_JAWZ: + if (!(player->itemflags & IF_ITEMOUT)) + { + K_BotItemGenericOrbitShield(bd, player); + } + else if (player->position != 1) // Hold onto orbiting items when in 1st :) + { + K_BotItemJawz(bd, player); + } + break; + case KITEM_MINE: + if (!(player->itemflags & IF_ITEMOUT)) + { + K_BotItemGenericTrapShield(bd, player, true); + } + else + { + K_BotItemMine(bd, player); + } + break; + case KITEM_LANDMINE: + case KITEM_HYUDORO: // Function re-use, as they have about the same usage. + K_BotItemLandmine(bd, player); + break; + case KITEM_BALLHOG: + K_BotItemBallhog(bd, player); + break; + case KITEM_THUNDERSHIELD: + K_BotItemLightning(bd, player); + break; + case KITEM_BUBBLESHIELD: + K_BotItemBubble(bd, player); + break; } } } -/*-------------------------------------------------- - void K_UpdateBotGameplayVarsItemUsage(player_t *player) - - See header file for description. ---------------------------------------------------*/ +// See header file for description. void K_UpdateBotGameplayVarsItemUsage(player_t *player) { if (player->itemflags & IF_USERINGS) @@ -1616,22 +1335,6 @@ void K_UpdateBotGameplayVarsItemUsage(player_t *player) return; } - if (player->botvars.itemdelay) - { - player->botvars.itemdelay--; - player->botvars.itemconfirm = 0; - return; - } - - if (player->cmd.bot.itemconfirm < 0 && abs(player->cmd.bot.itemconfirm) > player->botvars.itemconfirm) - { - player->botvars.itemconfirm = 0; - } - else - { - player->botvars.itemconfirm += player->cmd.bot.itemconfirm; - } - if (player->itemflags & IF_USERINGS) { ; @@ -1641,7 +1344,6 @@ void K_UpdateBotGameplayVarsItemUsage(player_t *player) if (player->itemroulette) { // Mashing behaviors - K_BotItemRouletteMash(player, &player->cmd); return; } diff --git a/src/k_botsearch.cpp b/src/k_botsearch.cpp index 3aa10d022..a9f730f87 100644 --- a/src/k_botsearch.cpp +++ b/src/k_botsearch.cpp @@ -644,12 +644,8 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) return BMIT_CONTINUE; } -/*-------------------------------------------------- - void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *player) - - See header file for description. ---------------------------------------------------*/ -void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *player) +// See header file for description. +void K_NudgePredictionTowardsObjects(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -672,20 +668,14 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p SINT8 gotoSide = -1; UINT8 i; - if (predict == NULL) - { - ps_bots[player - players].nudge += I_GetPreciseTime() - time; - return; - } - - distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); - angleToPredict = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); + distToPredict = R_PointToDist2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y); + angleToPredict = R_PointToAngle2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y); radToPredict = distToPredict >> 1; g_nudgeSearch.distancetocheck = FixedMul(radToPredict, radToPredict); - baseNudge = predict->baseRadius >> 3; - maxNudge = predict->baseRadius - baseNudge; + baseNudge = bd->predict.baseRadius >> 3; + maxNudge = bd->predict.baseRadius - baseNudge; g_nudgeSearch.botmo = player->mo; g_nudgeSearch.angle = angleToPredict; @@ -735,7 +725,7 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p avgDist = R_PointToDist2( avgX, avgY, - predict->x, predict->y + bd->predict.x, bd->predict.y ); // High handling characters dodge better @@ -748,14 +738,14 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p // Point away nudgeDir = R_PointToAngle2( avgX, avgY, - predict->x, predict->y + bd->predict.x, bd->predict.y ); - predict->x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT)); - predict->y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT)); - predict->radius = std::max(predict->radius - nudgeDist, baseNudge); + bd->predict.x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT)); + bd->predict.y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT)); + bd->predict.radius = std::max(bd->predict.radius - nudgeDist, baseNudge); - distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); + distToPredict = R_PointToDist2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y); // Flip side, since we want to check for objects to steer towards on the side we're NOT dodging. if (gotoSide == 0) @@ -797,7 +787,7 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p avgY = (g_nudgeSearch.gotoAvgY[gotoSide] / g_nudgeSearch.gotoObjs[gotoSide]) * mapobjectscale; avgDist = R_PointToDist2( - predict->x, predict->y, + bd->predict.x, bd->predict.y, avgX, avgY ); @@ -810,23 +800,23 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p if (avgDist <= nudgeDist) { - predict->x = avgX; - predict->y = avgY; - predict->radius = baseNudge; + bd->predict.x = avgX; + bd->predict.y = avgY; + bd->predict.radius = baseNudge; } else { // Point towards nudgeDir = R_PointToAngle2( - predict->x, predict->y, + bd->predict.x, bd->predict.y, avgX, avgY ); - predict->x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT)); - predict->y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT)); - predict->radius = std::max(predict->radius - nudgeDist, baseNudge); + bd->predict.x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT)); + bd->predict.y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT)); + bd->predict.radius = std::max(bd->predict.radius - nudgeDist, baseNudge); - //distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); + //distToPredict = R_PointToDist2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y); } } diff --git a/src/k_hud.c b/src/k_hud.c index 10efcb213..920931ebb 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -40,7 +40,7 @@ #include "m_random.h" #include "g_party.h" #include "h_timers.h" -#include "v_video.h" +#include "k_bot.h" // K_DrawBotDebugger #define NUMPOSNUMS 10 #define NUMPOSFRAMES 7 // White, three blues, three reds @@ -5068,29 +5068,6 @@ static void K_DrawClusterDebugger(void) } } -static void K_DrawBotDebugger(void) -{ - if (!cv_kartdebugbot.value || !stplyr->bot) - return; - - if (stplyrnum != 0) // only for p1 - return; - - INT32 vflags = V_6WIDTHSPACE|V_ALLOWLOWERCASE; - - static const char *driftstates[] = { - [DRIFTSTATE_AUTO] = "Auto", - [DRIFTSTATE_STARTING] = "Starting", - [DRIFTSTATE_ACTIVE] = "Active", - [DRIFTSTATE_ENDING] = "Ending", - }; - - V_DrawThinString(24, 100, vflags, va("Drift state: %s", driftstates[stplyr->botvars.driftstate])); - V_DrawThinString(24, 108, vflags|(stplyr->botvars.driftlockout ? V_ORANGEMAP : 0), va("Drift lockout: %d", stplyr->botvars.driftlockout)); - V_DrawThinString(24, 116, vflags, va("Drift turn: %d", stplyr->botvars.driftturn)); - V_DrawThinString(24, 124, vflags, va("Drift timer: %d", stplyr->botvars.drifttime)); -} - void K_drawKartHUD(void) { boolean islonesome = false; @@ -5332,9 +5309,11 @@ void K_drawKartHUD(void) } K_DrawWaypointDebugger(); - K_DrawBotDebugger(); K_DrawClusterDebugger(); K_DrawDirectorDebugger(); + + if (stplyrnum == 0) + K_DrawBotDebugger(stplyr); } // Thank you Haya.... diff --git a/src/k_kart.c b/src/k_kart.c index 64aa29424..f48b1b1c8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -647,8 +647,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) if (getitem == KITEM_HYUDORO) // Hyudoro cooldown hyubgone = 20*TICRATE; - player->botvars.itemdelay = TICRATE; - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, true); player->itemtype = K_ItemResultToType(getitem); UINT8 itemamount = K_ItemResultToAmount(getitem); @@ -11161,7 +11160,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (ATTACK_IS_DOWN && player->eggmanexplode <= 3*TICRATE && player->eggmanexplode > 1) { player->eggmanexplode = 1; - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } } // Eggman Monitor throwing @@ -11173,7 +11172,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_PlayAttackTaunt(player->mo); player->itemflags &= ~IF_EGGMANOUT; K_UpdateHnextList(player, true); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } } // Rocket Sneaker usage @@ -11187,7 +11186,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->rocketsneakertimer = 1; else player->rocketsneakertimer -= 2*TICRATE; - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } } // Flame Shield Usage @@ -11300,7 +11299,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_DoSneaker(player, SNEAKERTYPE_SNEAKER); K_PlayBoostTaunt(player->mo); player->itemamount--; - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_ROCKETSNEAKER: @@ -11334,7 +11333,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&prev->hnext, mo); prev = mo; } - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_INVINCIBILITY: @@ -11343,7 +11342,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_DoInvincibility(player, K_GetInvincibilityTime(player)); K_PlayPowerGloatSound(player->mo); player->itemamount--; - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_BANANA: @@ -11374,7 +11373,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&prev->hnext, mo); prev = mo; } - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Banana x3 thrown @@ -11383,7 +11382,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_ThrowKartItem(player, false, MT_BANANA, -1, 0); K_PlayAttackTaunt(player->mo); K_UpdateHnextList(player, false); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_EGGMAN: @@ -11404,7 +11403,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&mo->target, player->mo); P_SetTarget(&player->mo->hnext, mo); } - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_ORBINAUT: @@ -11439,7 +11438,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&prev->hnext, mo); prev = mo; } - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Orbinaut x3 thrown { @@ -11447,7 +11446,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0); K_PlayAttackTaunt(player->mo); K_UpdateHnextList(player, false); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_JAWZ: @@ -11481,7 +11480,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&prev->hnext, mo); prev = mo; } - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Jawz thrown { @@ -11492,7 +11491,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0); K_PlayAttackTaunt(player->mo); K_UpdateHnextList(player, false); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_MINE: @@ -11511,7 +11510,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&mo->target, player->mo); P_SetTarget(&player->mo->hnext, mo); } - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) { @@ -11520,7 +11519,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_PlayAttackTaunt(player->mo); player->itemflags &= ~IF_ITEMOUT; K_UpdateHnextList(player, true); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_LANDMINE: @@ -11529,7 +11528,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->itemamount--; K_ThrowLandMine(player); K_PlayAttackTaunt(player->mo); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_BALLHOG: @@ -11538,7 +11537,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->itemamount--; K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0); K_PlayAttackTaunt(player->mo); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_SPB: @@ -11547,7 +11546,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->itemamount--; K_ThrowKartItem(player, true, MT_SPB, 1, 0); K_PlayAttackTaunt(player->mo); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_GROW: @@ -11591,7 +11590,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) S_StartSound(player->mo, sfx_kc5a); } - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_SHRINK: @@ -11600,7 +11599,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_DoShrink(player); player->itemamount--; K_PlayPowerGloatSound(player->mo); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_THUNDERSHIELD: @@ -11625,7 +11624,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // ...:dumbestass: player->itemamount--; K_PlayAttackTaunt(player->mo); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } } break; @@ -11715,7 +11714,7 @@ bubbledeflate: player->itemamount--; K_DoHyudoroSteal(player); // yes. yes they do. K_PlayAttackTaunt(player->mo); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_POGOSPRING: @@ -11725,7 +11724,7 @@ bubbledeflate: K_PlayBoostTaunt(player->mo); K_DoPogoSpring(player->mo, 32<pogospring = 1; - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_SUPERRING: @@ -11733,7 +11732,7 @@ bubbledeflate: { player->itemamount--; K_AwardScaledPlayerRings(player, ASR_SUPERRING); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_KITCHENSINK: @@ -11752,7 +11751,7 @@ bubbledeflate: P_SetTarget(&mo->target, player->mo); P_SetTarget(&player->mo->hnext, mo); } - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Sink thrown { @@ -11761,7 +11760,7 @@ bubbledeflate: K_PlayAttackTaunt(player->mo); player->itemflags &= ~IF_ITEMOUT; K_UpdateHnextList(player, true); - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; case KITEM_SAD: @@ -11770,7 +11769,7 @@ bubbledeflate: { player->sadtimer = stealtime; player->itemamount--; - player->botvars.itemconfirm = 0; + K_BotResetItemConfirm(player, false); } break; default: diff --git a/src/p_saveg.c b/src/p_saveg.c index d64c624a6..d40572f1e 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -401,15 +401,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].botvars.rival); WRITEFIXED(save->p, players[i].botvars.rubberband); WRITEUINT16(save->p, players[i].botvars.controller); - WRITEUINT32(save->p, players[i].botvars.itemdelay); - WRITEUINT32(save->p, players[i].botvars.itemconfirm); - WRITESINT8(save->p, players[i].botvars.turnconfirm); - WRITEUINT32(save->p, players[i].botvars.respawnconfirm); WRITEFIXED(save->p, players[i].botvars.driftskill); - WRITEINT32(save->p, players[i].botvars.driftstate); - WRITESINT8(save->p, players[i].botvars.driftturn); - WRITEUINT32(save->p, players[i].botvars.drifttime); - WRITEUINT32(save->p, players[i].botvars.driftlockout); WRITEFIXED(save->p, players[i].outrun); WRITEUINT8(save->p, players[i].outruntime); @@ -756,15 +748,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].botvars.rival = (boolean)READUINT8(save->p); players[i].botvars.rubberband = READFIXED(save->p); players[i].botvars.controller = READUINT16(save->p); - players[i].botvars.itemdelay = READUINT32(save->p); - players[i].botvars.itemconfirm = READUINT32(save->p); - players[i].botvars.turnconfirm = READSINT8(save->p); - players[i].botvars.respawnconfirm = READUINT32(save->p); players[i].botvars.driftskill = READFIXED(save->p); - players[i].botvars.driftstate = READINT32(save->p); - players[i].botvars.driftturn = READSINT8(save->p); - players[i].botvars.drifttime = READUINT32(save->p); - players[i].botvars.driftlockout = READUINT32(save->p); players[i].outrun = READFIXED(save->p); players[i].outruntime = READUINT8(save->p); diff --git a/src/p_tick.c b/src/p_tick.c index 67e11ccdd..ec780568f 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -40,6 +40,7 @@ #include "k_waypoint.h" #include "k_director.h" #include "acs/interface.h" +#include "k_bot.h" // K_BotTicker #ifdef PARANOIA #include "deh_tables.h" // MOBJTYPE_LIST @@ -767,6 +768,12 @@ void P_Ticker(boolean run) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); + // run all the bot tickers + if (server) + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].bot && players[i].mo && !P_MobjWasRemoved(players[i].mo)) + K_BotTicker(&players[i]); + // Bosses have a punchy start, so no position. if (bossinfo.boss == true) { diff --git a/src/typedef.h b/src/typedef.h index 424858d4a..72323f202 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -164,7 +164,7 @@ TYPEDEF (bheap_t); TYPEDEF (weakspot_t); // k_bot.h -TYPEDEF (botprediction_t); +TYPEDEF (botdata_t); TYPEDEF (botcontroller_t); // k_brightmap.h From b21319011edeffd67a921c5ca046371ea1b0e9a8 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Thu, 28 Aug 2025 23:24:55 +0200 Subject: [PATCH 02/13] Fix perfstats, fix RNG desync --- src/d_clisrv.c | 8 -------- src/k_bot.cpp | 2 +- src/m_perfstats.c | 15 +++++++++++++-- src/p_tick.c | 18 ++++++++++++++++-- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f4203fc7d..94f4084f6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5638,8 +5638,6 @@ static void SV_Maketic(void) // Moved here so bots and packetloss indication doesn't break.... G_MoveTiccmd(netcmds[maketic % BACKUPTICS], playercmds, MAXPLAYERS); - PS_ResetBotInfo(); - for (i = 0; i < MAXPLAYERS; i++) { packetloss[i][maketic%PACKETMEASUREWINDOW] = false; @@ -5649,13 +5647,7 @@ static void SV_Maketic(void) if (K_PlayerUsesBotMovement(&players[i])) { - const precise_t t = I_GetPreciseTime(); - K_BuildBotTiccmd(&players[i], &netcmds[maketic%BACKUPTICS][i]); - - ps_bots[i].isBot = true; - ps_bots[i].total = I_GetPreciseTime() - t; - ps_botticcmd_time += ps_bots[i].total; continue; } diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 06b3a3579..483d370e7 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1178,7 +1178,7 @@ static void K_BotStartDrift(botdata_t *bd, player_t* player) { // Randomly decide to drift based on our skill at drifting, // and how fast we're moving. - fixed_t driftpotential = P_RandomKey(MAXDRIFTSKILL); + fixed_t driftpotential = M_RandomKey(MAXDRIFTSKILL); if ((driftpotential <= player->botvars.driftskill) && (botDriftSpeed <= player->speed)) diff --git a/src/m_perfstats.c b/src/m_perfstats.c index 993b16ab7..335d9bdbc 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -370,10 +370,14 @@ static void M_DrawTickStats(void) {0} }; + perfstatrow_t bot_time_row[] = { + {"botcmd ", "Bot logic: ", &ps_botticcmd_time}, + {0} + }; + perfstatrow_t extra_thinker_time_row[] = { {"lthinkf", "LUAh_ThinkFrame:", &ps_lua_thinkframe_time}, {"acs ", "ACS_Tick: ", &ps_acs_time}, - {"botcmd ", "Bot logic: ", &ps_botticcmd_time}, {"other ", "Other: ", &extratime}, {0} }; @@ -417,6 +421,7 @@ static void M_DrawTickStats(void) perfstatcol_t tictime_col = {20, 20, V_YELLOWMAP, tictime_row}; perfstatcol_t thinker_time_col = {24, 24, V_YELLOWMAP, thinker_time_row}; perfstatcol_t detailed_thinker_time_col = {28, 28, V_YELLOWMAP, detailed_thinker_time_row}; + perfstatcol_t bot_time_col = {24, 24, V_YELLOWMAP, bot_time_row}; perfstatcol_t extra_thinker_time_col = {24, 24, V_YELLOWMAP, extra_thinker_time_row}; perfstatcol_t thinkercount_col = {90, 115, V_BLUEMAP, thinkercount_row}; @@ -462,6 +467,8 @@ static void M_DrawTickStats(void) M_DrawPerfTiming(&tictime_col); M_DrawPerfTiming(&thinker_time_col); M_DrawPerfTiming(&detailed_thinker_time_col); + if (server) + M_DrawPerfTiming(&bot_time_col); M_DrawPerfTiming(&extra_thinker_time_col); draw_row = 10; @@ -504,7 +511,11 @@ void M_DrawPerfStats(void) } else if (cv_perfstats.value == PS_BOT) // bot ticcmd { - if (vid.width < 640 || vid.height < 400) // low resolution + if (!server) + { + V_DrawThinString(30, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Not applicable to clients"); + } + else if (vid.width < 640 || vid.height < 400) // low resolution { // it's not gonna fit very well.. V_DrawThinString(30, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Not available for resolutions below 640x400"); diff --git a/src/p_tick.c b/src/p_tick.c index ec780568f..c51a5aa83 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -770,9 +770,23 @@ void P_Ticker(boolean run) // run all the bot tickers if (server) + { + PS_ResetBotInfo(); for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].bot && players[i].mo && !P_MobjWasRemoved(players[i].mo)) - K_BotTicker(&players[i]); + { + player_t *player = &players[i]; + if (playeringame[i] && player->mo && !P_MobjWasRemoved(player->mo) && K_PlayerUsesBotMovement(player)) + { + const precise_t t = I_GetPreciseTime(); + + K_BotTicker(player); + + ps_bots[i].total = I_GetPreciseTime() - t; + ps_bots[i].isBot = true; + ps_botticcmd_time += ps_bots[i].total; + } + } + } // Bosses have a punchy start, so no position. if (bossinfo.boss == true) From 68a644fe2f826bf819bcc24d0561b8afb94140f0 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sat, 30 Aug 2025 14:22:33 +0200 Subject: [PATCH 03/13] Replace the test cvars with botdata variables --- src/d_netcmd.c | 4 ---- src/d_netcmd.h | 2 -- src/k_bot.cpp | 12 ++++++++---- src/k_bot.h | 4 ++++ src/k_kart.c | 4 ---- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 25c5c1fd6..994c04e0e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -679,10 +679,6 @@ consvar_t cv_schedule = CVAR_INIT ("schedule", "On", CV_NETVAR|CV_CALL, CV_OnOff consvar_t cv_automate = CVAR_INIT ("automate", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_test1 = CVAR_INIT ("test1", "200", CV_NETVAR|CV_FLOAT, CV_Signed, NULL); -consvar_t cv_test2 = CVAR_INIT ("test2", "40", CV_NETVAR|CV_FLOAT, CV_Signed, NULL); -consvar_t cv_test3 = CVAR_INIT ("test3", "18", CV_NETVAR|CV_FLOAT, CV_Signed, NULL); - char timedemo_name[256]; boolean timedemo_csv; char timedemo_csv_id[256]; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index b9a33bb12..0c3c06f75 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -237,8 +237,6 @@ extern consvar_t cv_director; extern consvar_t cv_schedule; -extern consvar_t cv_test1, cv_test2, cv_test3; - extern char timedemo_name[256]; extern boolean timedemo_csv; extern char timedemo_csv_id[256]; diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 483d370e7..1407f5bc9 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1139,8 +1139,7 @@ static void K_BotStartDrift(botdata_t *bd, player_t* player) // check for waypoints ahead of us with drift settings, based on our current speed path_t path = {0}; - INT32 maxdist = FixedInt(cv_test1.value) + gamespeed*50; - maxdist = FixedMul(maxdist, speedfactor * (bd->driftstate == DRIFTSTATE_ACTIVE ? 1 : 2)); + INT32 maxdist = FixedMul(bd->driftmaxdist, speedfactor * (bd->driftstate == DRIFTSTATE_ACTIVE ? 1 : 2)); if (maxdist >= 0 && K_PathfindThruCircuit(player->currentwaypoint, maxdist, &path, false, false)) { @@ -1322,7 +1321,7 @@ static INT32 K_HandleBotTrack(botdata_t *bd, player_t *player, angle_t destangle fixed_t driftpower = angofs - FixedDiv(anglediff2, ANG1); // arbitrary divider on the final driftpower - driftpower /= FixedInt(cv_test2.value); + driftpower /= bd->driftpowerdiv; // brakedrift if we're steering too hard if (abs(driftpower) >= FRACUNIT) @@ -1702,7 +1701,7 @@ void K_BotTicker(player_t *player) // so no need to worry about doing that ourselves. K_BotStartDrift(bd, player); - INT32 limit = FixedInt(cv_test3.value) - gamespeed*5; + INT32 limit = bd->driftstatedelay; INT32 dtime = ++bd->drifttime; // the faster we are going, the sooner we need to drift @@ -1827,6 +1826,11 @@ void K_BotRespawn(player_t *player) botdata_t *bd = &botdata[player - players]; memset(bd, 0, sizeof(*bd)); bd->driftstate = DRIFTSTATE_AUTO; + + // drift parameters, just fixed values for now + bd->driftmaxdist = 200 + gamespeed*50; + bd->driftpowerdiv = 40; + bd->driftstatedelay = TICRATE/2 - gamespeed*5; } void K_BotResetItemConfirm(player_t *player, boolean setdelay) diff --git a/src/k_bot.h b/src/k_bot.h index ddfbe368e..f7b2a7f32 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -86,6 +86,10 @@ struct botdata_t tic_t drifttime; // Time spent drifting tic_t driftlockout; // do not allow drifting for this many tics + UINT16 driftmaxdist; // how far to look for drift waypoints + UINT16 driftpowerdiv; // arbitrary divider on drift turning + UINT16 driftstatedelay; // max time to wait between drift state changes + boolean acceldown, brakedown, driftdown, itemdown; // buttons boolean dolookback; // look out behind you boolean itemwasdown; // last tic's item button diff --git a/src/k_kart.c b/src/k_kart.c index f48b1b1c8..bcc9d0068 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -385,10 +385,6 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_itemtimers); CV_RegisterVar(&cv_saltyhop); - CV_RegisterVar(&cv_test1); - CV_RegisterVar(&cv_test2); - CV_RegisterVar(&cv_test3); - CV_RegisterVar(&cv_naturalcamera); } From e8d43ec8a34d09765cb5ee874b3dec236142dbe0 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sat, 30 Aug 2025 17:06:42 +0200 Subject: [PATCH 04/13] Constantinople --- src/k_bot.cpp | 10 +++++----- src/k_bot.h | 14 ++++++------- src/k_botitem.cpp | 40 ++++++++++++++++++------------------- src/k_botsearch.cpp | 4 ++-- src/k_kart.c | 48 ++++++++++++++++++++++----------------------- src/k_kart.h | 46 +++++++++++++++++++++---------------------- src/p_local.h | 20 +++++++++---------- src/p_mobj.c | 6 +++--- src/p_user.c | 14 ++++++------- 9 files changed, 101 insertions(+), 101 deletions(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 1407f5bc9..a3cdcc02e 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -406,7 +406,7 @@ boolean K_PlayerUsesBotMovement(const player_t *player) See header file for description. --------------------------------------------------*/ -boolean K_BotCanTakeCut(player_t *player) +boolean K_BotCanTakeCut(const player_t *player) { if ( (K_TripwirePassConditions(player) != TRIPWIRE_NONE || K_ApplyOffroad(player) == false) @@ -493,7 +493,7 @@ static fixed_t K_BotSpeedScaled(const player_t *player, fixed_t speed) See header file for description. --------------------------------------------------*/ -const botcontroller_t *K_GetBotController(mobj_t *mobj) +const botcontroller_t *K_GetBotController(const mobj_t *mobj) { botcontroller_t *ret = nullptr; @@ -875,7 +875,7 @@ static fixed_t K_ScaleWPDistWithSlope(fixed_t disttonext, angle_t angletonext, c } // Calculates a point further along the track to attempt to drive towards. -static boolean K_CreateBotPrediction(botdata_t *bd, player_t *player) +static boolean K_CreateBotPrediction(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -1215,7 +1215,7 @@ static void K_BotStartDrift(botdata_t *bd, player_t* player) } // Determines inputs for standard track driving. -static INT32 K_HandleBotTrack(botdata_t *bd, player_t *player, angle_t destangle) +static INT32 K_HandleBotTrack(botdata_t *bd, const player_t *player, angle_t destangle) { ZoneScoped; @@ -1392,7 +1392,7 @@ static void K_IncrementBotRespawn(player_t *player, UINT8 *respawn, const UINT8 Return:- New value for turn amount. --------------------------------------------------*/ -/*static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t *predict, angle_t destangle) +/*static INT32 K_HandleBotReverse(const player_t *player, ticcmd_t *cmd, botprediction_t *predict, angle_t destangle) { ZoneScoped; diff --git a/src/k_bot.h b/src/k_bot.h index f7b2a7f32..c01787d0b 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -119,7 +119,7 @@ boolean K_PlayerUsesBotMovement(const player_t *player); /*-------------------------------------------------- - boolean K_BotCanTakeCut(player_t *player); + boolean K_BotCanTakeCut(const player_t *player); Tells if this bot is able to take shortcuts (currently unaffected by offroad, or has certain items ready). @@ -131,11 +131,11 @@ boolean K_PlayerUsesBotMovement(const player_t *player); true if able to take shortcuts, otherwise false. --------------------------------------------------*/ -boolean K_BotCanTakeCut(player_t *player); +boolean K_BotCanTakeCut(const player_t *player); /*-------------------------------------------------- - const botcontroller_t *K_GetBotController(mobj_t *mobj); + const 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(player_t *player); Pointer to the sector's bot controller struct. --------------------------------------------------*/ -const botcontroller_t *K_GetBotController(mobj_t *mobj); +const botcontroller_t *K_GetBotController(const mobj_t *mobj); /*-------------------------------------------------- @@ -310,7 +310,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y); /*-------------------------------------------------- - boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) + boolean K_BotHatesThisSector(const player_t *player, sector_t *sec, fixed_t x, fixed_t y) Tells us if a bot will play more careful around this sector. Checks FOFs in the sector, as well. @@ -325,7 +325,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y); true if avoiding this sector, false otherwise. --------------------------------------------------*/ -boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y); +boolean K_BotHatesThisSector(const player_t *player, sector_t *sec, fixed_t x, fixed_t y); // Moves the bot's prediction, based on objects around the bot. void K_NudgePredictionTowardsObjects(botdata_t *bd, const player_t *player); @@ -396,7 +396,7 @@ void K_UpdateBotGameplayVarsItemUsage(player_t *player); void K_UpdateBotGameplayVars(player_t *player); // Item usage part of ticcmd generation. -void K_BotItemUsage(botdata_t *bd, player_t *player); +void K_BotItemUsage(botdata_t *bd, const player_t *player); void K_BotRespawn(player_t *player); diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index a4cc8854d..41a1203a4 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -374,7 +374,7 @@ static boolean K_BotRevealsGenericTrap(botdata_t *bd, const player_t *player, bo // Item usage for Eggman shields. // mine - Set to true to handle Mine-specific behaviors. -static void K_BotItemGenericTrapShield(botdata_t *bd, player_t *player, boolean mine) +static void K_BotItemGenericTrapShield(botdata_t *bd, const player_t *player, boolean mine) { ZoneScoped; @@ -392,7 +392,7 @@ static void K_BotItemGenericTrapShield(botdata_t *bd, player_t *player, boolean } // Item usage for orbitting shields. -static void K_BotItemGenericOrbitShield(botdata_t *bd, player_t *player) +static void K_BotItemGenericOrbitShield(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -405,7 +405,7 @@ static void K_BotItemGenericOrbitShield(botdata_t *bd, player_t *player) } // Item usage for sneakers. -static void K_BotItemSneaker(botdata_t *bd, player_t *player) +static void K_BotItemSneaker(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -434,7 +434,7 @@ static void K_BotItemSneaker(botdata_t *bd, player_t *player) } // Item usage for rocket sneakers. -static void K_BotItemRocketSneaker(botdata_t *bd, player_t *player) +static void K_BotItemRocketSneaker(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -459,7 +459,7 @@ static void K_BotItemRocketSneaker(botdata_t *bd, player_t *player) } // Item usage for trap item throwing. -static void K_BotItemBanana(botdata_t *bd, player_t *player) +static void K_BotItemBanana(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -506,7 +506,7 @@ static void K_BotItemBanana(botdata_t *bd, player_t *player) } // Item usage for trap item throwing. -static void K_BotItemMine(botdata_t *bd, player_t *player) +static void K_BotItemMine(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -559,7 +559,7 @@ static void K_BotItemMine(botdata_t *bd, player_t *player) } // Item usage for landmine tossing. -static void K_BotItemLandmine(botdata_t *bd, player_t *player) +static void K_BotItemLandmine(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -587,7 +587,7 @@ static void K_BotItemLandmine(botdata_t *bd, player_t *player) } // Item usage for Eggman item throwing. -static void K_BotItemEggman(botdata_t *bd, player_t *player) +static void K_BotItemEggman(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -674,7 +674,7 @@ static boolean K_BotRevealsEggbox(const player_t *player) } // Item usage for Eggman shields. -static void K_BotItemEggmanShield(botdata_t *bd, player_t *player) +static void K_BotItemEggmanShield(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -692,7 +692,7 @@ static void K_BotItemEggmanShield(botdata_t *bd, player_t *player) } // Item usage for Eggman explosions. -static void K_BotItemEggmanExplosion(botdata_t *bd, player_t *player) +static void K_BotItemEggmanExplosion(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -707,7 +707,7 @@ static void K_BotItemEggmanExplosion(botdata_t *bd, player_t *player) } // Item usage for Orbinaut throwing. -static void K_BotItemOrbinaut(botdata_t *bd, player_t *player) +static void K_BotItemOrbinaut(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -756,7 +756,7 @@ static void K_BotItemOrbinaut(botdata_t *bd, player_t *player) } // Item usage for Ballhog throwing. -static void K_BotItemBallhog(botdata_t *bd, player_t *player) +static void K_BotItemBallhog(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -821,7 +821,7 @@ static void K_BotItemBallhog(botdata_t *bd, player_t *player) } // Item usage for Jawz throwing. -static void K_BotItemJawz(botdata_t *bd, player_t *player) +static void K_BotItemJawz(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -892,7 +892,7 @@ static void K_BotItemJawz(botdata_t *bd, player_t *player) } // Item usage for Lightning Shield. -static void K_BotItemLightning(botdata_t *bd, player_t *player) +static void K_BotItemLightning(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -913,7 +913,7 @@ static void K_BotItemLightning(botdata_t *bd, player_t *player) } // Item usage for Bubble Shield. -static void K_BotItemBubble(botdata_t *bd, player_t *player) +static void K_BotItemBubble(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -982,7 +982,7 @@ static void K_BotItemBubble(botdata_t *bd, player_t *player) } // Item usage for Flame Shield. -static void K_BotItemFlame(botdata_t *bd, player_t *player) +static void K_BotItemFlame(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -1025,7 +1025,7 @@ static void K_BotItemFlame(botdata_t *bd, player_t *player) } // Item usage for rings. -static void K_BotItemRings(botdata_t *bd, player_t *player) +static void K_BotItemRings(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -1087,7 +1087,7 @@ static void K_BotItemRings(botdata_t *bd, player_t *player) Return:- Returns the useodds of this bot. --------------------------------------------------*/ -static UINT32 K_BotCalculateUseodds(player_t *player) +static UINT32 K_BotCalculateUseodds(const player_t *player) { UINT32 pdis = 0; UINT8 pingame = 0, bestbumper = 0; @@ -1134,7 +1134,7 @@ static UINT32 K_BotCalculateUseodds(player_t *player) } // Item usage for item roulette mashing. -static void K_BotItemRouletteMash(botdata_t *bd, player_t *player) +static void K_BotItemRouletteMash(botdata_t *bd, const player_t *player) { boolean mash = false; UINT32 useodds = 0; @@ -1186,7 +1186,7 @@ static void K_BotItemRouletteMash(botdata_t *bd, player_t *player) } // See header file for description. -void K_BotItemUsage(botdata_t *bd, player_t *player) +void K_BotItemUsage(botdata_t *bd, const player_t *player) { ZoneScoped; diff --git a/src/k_botsearch.cpp b/src/k_botsearch.cpp index a9f730f87..8d2e96883 100644 --- a/src/k_botsearch.cpp +++ b/src/k_botsearch.cpp @@ -137,7 +137,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y) Return:- true if avoiding this sector special, false otherwise. --------------------------------------------------*/ -static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec, const boolean flip) +static boolean K_BotHatesThisSectorsSpecial(const player_t *player, sector_t *sec, const boolean flip) { terrain_t *terrain = K_GetTerrainForFlatNum(flip ? sec->ceilingpic : sec->floorpic); @@ -174,7 +174,7 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec, con See header file for description. --------------------------------------------------*/ -boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) +boolean K_BotHatesThisSector(const player_t *player, sector_t *sec, fixed_t x, fixed_t y) { const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); fixed_t highestfloor = INT32_MAX; diff --git a/src/k_kart.c b/src/k_kart.c index bcc9d0068..e95720329 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1221,7 +1221,7 @@ INT32 K_KartGetLegacyItemOdds(UINT8 pos, SINT8 item, fixed_t clusterDist, fixed_ //{ SRB2kart Roulette Code - Distance Based, yes waypoints -UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) +UINT8 K_FindUseodds(const player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) { UINT8 i; UINT8 useodds = 0; @@ -3435,7 +3435,7 @@ void K_MomentumToFacing(player_t *player) player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy; } -boolean K_ApplyOffroad(player_t *player) +boolean K_ApplyOffroad(const player_t *player) { boolean sneakertimer = CANTCHAINOFFROAD ? (player->sneakertimer && player->realsneakertimer) : player->sneakertimer > 0; @@ -3447,7 +3447,7 @@ boolean K_ApplyOffroad(player_t *player) return true; } -fixed_t K_PlayerTripwireSpeedThreshold(player_t *player) +fixed_t K_PlayerTripwireSpeedThreshold(const player_t *player) { fixed_t required_speed = 2 * K_GetKartSpeed(player, false, false); // 200% @@ -3471,7 +3471,7 @@ fixed_t K_PlayerTripwireSpeedThreshold(player_t *player) return required_speed; } -tripwirepass_t K_TripwirePassConditions(player_t *player) +tripwirepass_t K_TripwirePassConditions(const player_t *player) { if ( player->invincibilitytimer || @@ -3494,7 +3494,7 @@ tripwirepass_t K_TripwirePassConditions(player_t *player) return TRIPWIRE_NONE; } -boolean K_TripwirePass(player_t *player) +boolean K_TripwirePass(const player_t *player) { return (player->tripwirePass != TRIPWIRE_NONE); } @@ -4159,7 +4159,7 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed, boolean karmabomb) return finalspeed; } -fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberband) +fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dorubberband) { boolean karmabomb = ((gametyperules & GTR_KARMA) && player->bumper <= 0); fixed_t finalspeed; @@ -4199,7 +4199,7 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberb return finalspeed; } -fixed_t K_GetKartAccel(player_t *player) +fixed_t K_GetKartAccel(const player_t *player) { fixed_t k_accel = 32; // 36; UINT8 kartspeed = player->kartspeed; @@ -4212,7 +4212,7 @@ fixed_t K_GetKartAccel(player_t *player) return FixedMul(k_accel, FRACUNIT+player->accelboost); } -UINT16 K_GetKartFlashing(player_t *player) +UINT16 K_GetKartFlashing(const player_t *player) { UINT16 tics = flashingtics; @@ -4225,7 +4225,7 @@ UINT16 K_GetKartFlashing(player_t *player) return tics; } -boolean K_PlayerShrinkCheat(player_t *player) +boolean K_PlayerShrinkCheat(const player_t *player) { if (cv_kartdebugshrink.value) return true; @@ -4256,20 +4256,20 @@ void K_UpdateShrinkCheat(player_t *player) } } -boolean K_KartKickstart(player_t *player) +boolean K_KartKickstart(const player_t *player) { return ((player->pflags & PF_KICKSTARTACCEL) && (!K_PlayerUsesBotMovement(player)) && (player->kickstartaccel >= ACCEL_KICKSTART)); } -UINT16 K_GetKartButtons(player_t *player) +UINT16 K_GetKartButtons(const player_t *player) { return (player->cmd.buttons | (K_KartKickstart(player) ? BT_ACCELERATE : 0)); } -SINT8 K_GetForwardMove(player_t *player) +SINT8 K_GetForwardMove(const player_t *player) { SINT8 forwardmove = player->cmd.forwardmove; @@ -4300,7 +4300,7 @@ SINT8 K_GetForwardMove(player_t *player) return forwardmove; } -SINT8 K_GetSideMove(player_t *player) +SINT8 K_GetSideMove(const player_t *player) { SINT8 sidemove = player->cmd.sidemove; @@ -4320,7 +4320,7 @@ SINT8 K_GetSideMove(player_t *player) return sidemove; } -fixed_t K_GetNewSpeed(player_t *player) +fixed_t K_GetNewSpeed(const player_t *player) { const fixed_t accelmax = 4000; const fixed_t p_speed = K_GetKartSpeed(player, true, true); @@ -4360,7 +4360,7 @@ fixed_t K_GetNewSpeed(player_t *player) return finalspeed; } -fixed_t K_3dKartMovement(player_t *player, boolean onground, SINT8 forwardmove) +fixed_t K_3dKartMovement(const player_t *player, boolean onground, SINT8 forwardmove) { fixed_t finalspeed = K_GetNewSpeed(player); @@ -4653,7 +4653,7 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A return ringburst; } -boolean K_IsPlayerDamaged(player_t *player) +boolean K_IsPlayerDamaged(const player_t *player) { return ((player->spinouttimer > 0) || (player->flipovertimer > 0)); } @@ -9772,7 +9772,7 @@ static void K_UpdatePlayerWaypoints(player_t *const player) player->pflags &= ~PF_TRUSTWAYPOINTS; // clear special exception } -INT32 K_GetKartRingPower(player_t *player, boolean boosted) +INT32 K_GetKartRingPower(const player_t *player, boolean boosted) { fixed_t ringPower = ((9 - player->kartspeed) + (9 - player->kartweight)) * (FRACUNIT/2); UINT16 finalPower = 0; @@ -9836,7 +9836,7 @@ boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y) // countersteer is how strong the controls are telling us we are turning // turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left -static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) +static INT16 K_GetKartDriftValue(const player_t *player, fixed_t countersteer) { INT16 basedrift, driftangle; fixed_t driftweight = player->kartweight*14; // 12 @@ -9863,7 +9863,7 @@ static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) return basedrift + FixedMul(driftangle, countersteer); } -INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) +INT16 K_GetKartTurnValue(const player_t *player, INT16 turnvalue) { fixed_t p_topspeed = K_GetKartSpeed(player, false, false); fixed_t p_curspeed = min(player->speed, p_topspeed * 2); @@ -9909,7 +9909,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) return turnvalue; } -INT32 K_GetKartDriftSparkValue(player_t *player) +INT32 K_GetKartDriftSparkValue(const player_t *player) { UINT8 kartspeed = ((gametyperules & GTR_KARMA) && player->bumper <= 0) ? 1 @@ -9917,7 +9917,7 @@ INT32 K_GetKartDriftSparkValue(player_t *player) return (26*4 + kartspeed*2 + (9 - player->kartweight))*8; } -INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage) +INT32 K_GetKartDriftSparkValueForStage(const player_t *player, UINT8 stage) { fixed_t mul = FRACUNIT; @@ -10841,7 +10841,7 @@ void K_StripOther(player_t *player) } } -SINT8 K_Sliptiding(player_t *player) +SINT8 K_Sliptiding(const player_t *player) { return player->drift ? 0 : player->aizdriftstrat; } @@ -10888,7 +10888,7 @@ static void K_AirFailsafe(player_t *player) // // K_PlayerBaseFriction // -fixed_t K_PlayerBaseFriction(player_t *player, fixed_t original) +fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original) { const fixed_t factor = FixedMul( FixedDiv(FRACUNIT - original, FRACUNIT - ORIG_FRICTION), @@ -11014,7 +11014,7 @@ void K_SetTireGrease(player_t *player, tic_t tics) player->tiregrease = tics; } -boolean K_SlopeResistance(player_t *player) +boolean K_SlopeResistance(const player_t *player) { if (player->tiregrease) return true; diff --git a/src/k_kart.h b/src/k_kart.h index 4b16940a0..935c50966 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -191,7 +191,7 @@ fixed_t K_GetKartGameSpeedScalar(SINT8 value); extern consvar_t *KartItemCVars[NUMKARTRESULTS-1]; -UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush); +UINT8 K_FindUseodds(const player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush); INT32 K_FindLegacyUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32 bestbumper, boolean spbrush, boolean dontforcespb); fixed_t K_ItemOddsScale(UINT8 numPlayers, boolean spbrush); UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush); @@ -227,7 +227,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type); void K_FlipPlayer(player_t *player, mobj_t *inflictor, mobj_t *source); INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); -boolean K_IsPlayerDamaged(player_t *player); +boolean K_IsPlayerDamaged(const player_t *player); void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers); void K_DestroyBumpers(player_t *player, UINT8 amount); void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); @@ -263,13 +263,13 @@ void K_RemoveBubbleHealth(player_t *player, INT16 sub); void K_RepairOrbitChain(mobj_t *orbit); void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player); player_t *K_FindJawzTarget(mobj_t *actor, player_t *source); -INT32 K_GetKartRingPower(player_t *player, boolean boosted); +INT32 K_GetKartRingPower(const player_t *player, boolean boosted); size_t K_NextRespawnWaypointIndex(waypoint_t *waypoint); boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y); void K_SetRespawnAtNextWaypoint(player_t * player); -INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue); -INT32 K_GetKartDriftSparkValue(player_t *player); -INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage); +INT16 K_GetKartTurnValue(const player_t *player, INT16 turnvalue); +INT32 K_GetKartDriftSparkValue(const player_t *player); +INT32 K_GetKartDriftSparkValueForStage(const player_t *player, UINT8 stage); void K_SpawnDriftBoostExplosion(player_t *player, int stage); void K_SpawnDriftElectricSparks(player_t *player); INT32 K_GetDriftAngleOffset(player_t *player); @@ -284,31 +284,31 @@ void K_DropKitchenSink(player_t *player); void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); -boolean K_ApplyOffroad(player_t *player); -fixed_t K_PlayerTripwireSpeedThreshold(player_t *player); -tripwirepass_t K_TripwirePassConditions(player_t *player); -boolean K_TripwirePass(player_t *player); +boolean K_ApplyOffroad(const player_t *player); +fixed_t K_PlayerTripwireSpeedThreshold(const player_t *player); +tripwirepass_t K_TripwirePassConditions(const player_t *player); +boolean K_TripwirePass(const player_t *player); boolean K_WaterRun(mobj_t *mobj); void K_SpawnWaterTrail(mobj_t *mobj); boolean K_ItemMobjAllowedtoWaterRun(mobj_t *item); void K_SetTireGrease(player_t *player, tic_t tics); -boolean K_SlopeResistance(player_t *player); +boolean K_SlopeResistance(const player_t *player); void K_SquishPlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_ApplyTripWire(player_t *player, tripwirestate_t state); fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed, boolean karmabomb); -fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberbanding); -fixed_t K_GetKartAccel(player_t *player); -UINT16 K_GetKartFlashing(player_t *player); -boolean K_PlayerShrinkCheat(player_t *player); +fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dorubberbanding); +fixed_t K_GetKartAccel(const player_t *player); +UINT16 K_GetKartFlashing(const player_t *player); +boolean K_PlayerShrinkCheat(const player_t *player); void K_UpdateShrinkCheat(player_t *player); -boolean K_KartKickstart(player_t *player); -UINT16 K_GetKartButtons(player_t *player); -SINT8 K_GetForwardMove(player_t *player); -SINT8 K_GetSideMove(player_t *player); -fixed_t K_GetNewSpeed(player_t *player); -fixed_t K_3dKartMovement(player_t *player, boolean onground, SINT8 fowardmove); -SINT8 K_Sliptiding(player_t *player); -fixed_t K_PlayerBaseFriction(player_t *player, fixed_t original); +boolean K_KartKickstart(const player_t *player); +UINT16 K_GetKartButtons(const player_t *player); +SINT8 K_GetForwardMove(const player_t *player); +SINT8 K_GetSideMove(const player_t *player); +fixed_t K_GetNewSpeed(const player_t *player); +fixed_t K_3dKartMovement(const player_t *player, boolean onground, SINT8 fowardmove); +SINT8 K_Sliptiding(const player_t *player); +fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original); void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(boolean considermapreset); UINT8 K_GetInvincibilityItemFrame(void); diff --git a/src/p_local.h b/src/p_local.h index 97154a8b1..20d8f3e1d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -174,13 +174,13 @@ void P_CalcChasePostImg(player_t *player, camera_t *thiscam); boolean P_CanPlayerTurn(player_t *player, ticcmd_t *cmd); -boolean P_PlayerInPain(player_t *player); +boolean P_PlayerInPain(const player_t *player); void P_ResetPlayer(player_t *player); boolean P_PlayerCanDamage(player_t *player, mobj_t *thing); fixed_t P_GetPlayerHeight(player_t *player); fixed_t P_GetPlayerSpinHeight(player_t *player); -boolean P_IsLocalPlayer(player_t *player); +boolean P_IsLocalPlayer(const player_t *player); boolean P_IsMachineLocalPlayer(player_t *player); boolean P_IsDisplayPlayer(player_t *player); @@ -189,12 +189,12 @@ angle_t P_GetLocalAngle(player_t *player); void P_ForceLocalAngle(player_t *player, angle_t angle, boolean interp); boolean P_PlayerFullbright(player_t *player); -boolean P_IsObjectInGoop(mobj_t *mo); -boolean P_IsObjectOnGround(mobj_t *mo); -boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec); -boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec); // SRB2Kart +boolean P_IsObjectInGoop(const mobj_t *mo); +boolean P_IsObjectOnGround(const mobj_t *mo); +boolean P_IsObjectOnGroundIn(const mobj_t *mo, const sector_t *sec); +boolean P_IsObjectOnRealGround(const mobj_t *mo, const sector_t *sec); // SRB2Kart #define P_IsObjectFlipped(o) (((o)->eflags & MFE_VERTICALFLIP) == MFE_VERTICALFLIP) -boolean P_InQuicksand(mobj_t *mo); +boolean P_InQuicksand(const mobj_t *mo); boolean P_PlayerHitFloor(player_t *player, boolean fromAir); void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); @@ -328,13 +328,13 @@ void P_SceneryThinker(mobj_t *mobj); // This does not need to be added to Lua. // To test it in Lua, check mobj.valid -FUNCINLINE static ATTRINLINE boolean P_MobjWasRemoved(mobj_t *mobj) +FUNCINLINE static ATTRINLINE boolean P_MobjWasRemoved(const mobj_t *mobj) { return mobj == NULL || mobj->thinker.function.acp1 != (actionf_p1)P_MobjThinker; } -fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect); -fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect); +fixed_t P_MobjFloorZ(const mobj_t *mobj, const sector_t *sector, const sector_t *boundsec, fixed_t x, fixed_t y, const line_t *line, boolean lowest, boolean perfect); +fixed_t P_MobjCeilingZ(const mobj_t *mobj, const sector_t *sector, const sector_t *boundsec, fixed_t x, fixed_t y, const line_t *line, boolean lowest, boolean perfect); #define P_GetFloorZ(mobj, sector, x, y, line) P_MobjFloorZ(mobj, sector, NULL, x, y, line, false, false) #define P_GetCeilingZ(mobj, sector, x, y, line) P_MobjCeilingZ(mobj, sector, NULL, x, y, line, true, false) #define P_GetFOFTopZ(mobj, sector, fof, x, y, line) P_MobjCeilingZ(mobj, sectors + fof->secnum, sector, x, y, line, false, false) diff --git a/src/p_mobj.c b/src/p_mobj.c index 3b2aeabf9..f8c27bd5b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -635,7 +635,7 @@ boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover) // Supply boundsec ONLY when checking for specials! It should be the "in-level" sector, and sector the control sector (if separate). // If set, then this function will iterate through boundsec's linedefs to find the highest contact point on the slope. Non-special-checking // usage will handle that later. -static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, line_t *line, pslope_t *slope, boolean actuallylowest) +static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, const line_t *line, const pslope_t *slope, boolean actuallylowest) { // Alright, so we're sitting on a line that contains our slope sector, and need to figure out the highest point we're touching... // The solution is simple! Get the line's vertices, and pull each one in along its line until it touches the object's bounding box @@ -733,7 +733,7 @@ static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, line_t *line, ); } -fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) +fixed_t P_MobjFloorZ(const mobj_t *mobj, const sector_t *sector, const sector_t *boundsec, fixed_t x, fixed_t y, const line_t *line, boolean lowest, boolean perfect) { I_Assert(mobj != NULL); I_Assert(sector != NULL); @@ -810,7 +810,7 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t return sector->floorheight; } -fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) +fixed_t P_MobjCeilingZ(const mobj_t *mobj, const sector_t *sector, const sector_t *boundsec, fixed_t x, fixed_t y, const line_t *line, boolean lowest, boolean perfect) { I_Assert(mobj != NULL); I_Assert(sector != NULL); diff --git a/src/p_user.c b/src/p_user.c index e637274c2..61df99ac8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -465,7 +465,7 @@ UINT8 P_FindHighestLap(void) // Is player in pain?? // Checks for painstate and flashing, if both found return true // -boolean P_PlayerInPain(player_t *player) +boolean P_PlayerInPain(const player_t *player) { if (K_IsPlayerDamaged(player) || player->squishedtimer) return true; @@ -890,7 +890,7 @@ void P_RestoreMusic(player_t *player) // Returns true if the object is inside goop water. // (Spectators and objects otherwise without gravity cannot have goop gravity!) // -boolean P_IsObjectInGoop(mobj_t *mo) +boolean P_IsObjectInGoop(const mobj_t *mo) { if (mo->player && mo->player->spectator) return false; @@ -908,7 +908,7 @@ boolean P_IsObjectInGoop(mobj_t *mo) // on the ground. Takes reverse // gravity and FOFs into account. // -boolean P_IsObjectOnGround(mobj_t *mo) +boolean P_IsObjectOnGround(const mobj_t *mo) { if (P_IsObjectInGoop(mo)) { @@ -953,7 +953,7 @@ boolean P_IsObjectOnGround(mobj_t *mo) // on the ground in a specific sector. Takes reverse // gravity and FOFs into account. // -boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec) +boolean P_IsObjectOnGroundIn(const mobj_t *mo, const sector_t *sec) { ffloor_t *rover; @@ -1040,7 +1040,7 @@ boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec) // Really simple, but personally I think it's also incredibly helpful. I think this is fine in p_user.c // -- Sal -boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec) +boolean P_IsObjectOnRealGround(const mobj_t *mo, const sector_t *sec) { // Is the object in reverse gravity? if (mo->eflags & MFE_VERTICALFLIP) @@ -1132,7 +1132,7 @@ boolean P_IsMachineLocalPlayer(player_t *player) // on the local machine // (or simulated party) // -boolean P_IsLocalPlayer(player_t *player) +boolean P_IsLocalPlayer(const player_t *player) { if (player == NULL) { @@ -1400,7 +1400,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean fromAir) return clipmomz; } -boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand +boolean P_InQuicksand(const mobj_t *mo) // Returns true if you are in quicksand { sector_t *sector = mo->subsector->sector; fixed_t topheight, bottomheight; From dc9bec000a1814fe321e21bf67e37acf6fbb1083 Mon Sep 17 00:00:00 2001 From: JugadorXEI Date: Fri, 22 Aug 2025 01:24:13 +0000 Subject: [PATCH 05/13] 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]; } From bc4408b203f931a8deb4325e42614d1a506a6f76 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 31 Aug 2025 20:37:02 +0200 Subject: [PATCH 06/13] Expose botdata stuff to Lua --- src/deh_tables.c | 13 ++ src/k_bot.cpp | 8 + src/k_bot.h | 1 + src/lua_baselib.c | 1 + src/lua_botvarslib.c | 339 +++++++++++++++++++++++++++++++++++-------- src/lua_libs.h | 1 + 6 files changed, 300 insertions(+), 63 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 33f08ace5..b1c5b1843 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1561,6 +1561,12 @@ struct int_const_s const INT_CONST[] = { {"BOTRESPAWNCONFIRM",BOTRESPAWNCONFIRM}, {"BOT_ITEM_DECISION_TIME",BOT_ITEM_DECISION_TIME}, + {"BOTDRIFTPERCENT",BOTDRIFTPERCENT}, + {"DRIFTSTARTPCT",DRIFTSTARTPCT}, + {"BOTDRIFTTICS",BOTDRIFTTICS}, + {"BOTDRIFTLOCKOUT",BOTDRIFTLOCKOUT}, + {"MAXDRIFTSKILL",MAXDRIFTSKILL}, + // botStyle_e {"BOT_STYLE_NORMAL",BOT_STYLE_NORMAL}, {"BOT_STYLE_STAY",BOT_STYLE_STAY}, @@ -1594,6 +1600,13 @@ struct int_const_s const INT_CONST[] = { {"TMBOTTR_DOWN",TMBOTTR_DOWN}, */ + // botdrift_e + {"DRIFTSTATE_AUTO",DRIFTSTATE_AUTO}, + {"DRIFTSTATE_STARTING",DRIFTSTATE_STARTING}, + {"DRIFTSTATE_ACTIVE",DRIFTSTATE_ACTIVE}, + {"DRIFTSTATE_ENDING",DRIFTSTATE_ENDING}, + {"NUMDRIFTSTATES",NUMDRIFTSTATES}, + // t_overlay_action_t {"TOV_UNDEFINED",TOV_UNDEFINED}, {"TOV_STILL",TOV_STILL}, diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 1b07c657e..85d792985 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1853,3 +1853,11 @@ void K_BotResetItemConfirm(player_t *player, boolean setdelay) if (setdelay) bd->itemdelay = TICRATE; } + +botdata_t *K_GetBotData(UINT8 num) +{ + if (num < 0 || num >= MAXPLAYERS) + return NULL; + + return &botdata[num]; +} diff --git a/src/k_bot.h b/src/k_bot.h index a8cae46a4..3cc977052 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -433,6 +433,7 @@ fixed_t K_BotDetermineDriftSkill(player_t *player); void K_BotSetDriftState(player_t *player, botdrift_e newstate, tic_t lockout); +botdata_t *K_GetBotData(UINT8 num); #ifdef __cplusplus } // extern "C" diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 460912f77..9aeff1ce8 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -238,6 +238,7 @@ static const struct { {META_BOTVARS, "botvars_t"}, {META_BOTCONTROLLER, "botcontroller_t"}, + {META_BOTDATA, "botdata_t"}, {META_SPLASH, "t_splash_t"}, {META_FOOTSTEP, "t_footstep_t"}, diff --git a/src/lua_botvarslib.c b/src/lua_botvarslib.c index 0d22d34e5..57606111b 100644 --- a/src/lua_botvarslib.c +++ b/src/lua_botvarslib.c @@ -16,6 +16,7 @@ #include "lua_script.h" #include "lua_libs.h" +#include "k_bot.h" enum botvars { botvars_valid = 0, @@ -24,15 +25,7 @@ enum botvars { botvars_diffincrease, botvars_rival, botvars_rubberband, - /* - botvars_itemdelay, - botvars_itemconfirm, - botvars_turnconfirm, - botvars_spindashconfirm, - botvars_respawnconfirm, - botvars_roulettepriority, - botvars_roulettetimeout, - */ + botvars_driftskill, }; static const char *const botvars_opt[] = { @@ -42,15 +35,7 @@ static const char *const botvars_opt[] = { "diffincrease", "rival", "rubberband", - /* - "itemdelay", - "itemconfirm", - "turnconfirm", - "spindashconfirm", - "respawnconfirm", - "roulettepriority", - "roulettetimeout", - */ + "driftskill", NULL }; @@ -84,34 +69,14 @@ static int botvars_get(lua_State *L) case botvars_rubberband: lua_pushfixed(L, botvars->rubberband); break; - /* - case botvars_itemdelay: - lua_pushinteger(L, botvars->itemdelay); + case botvars_driftskill: + lua_pushfixed(L, botvars->driftskill); 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]) +#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) { @@ -142,35 +107,265 @@ static int botvars_set(lua_State *L) case botvars_rubberband: botvars->rubberband = luaL_checkfixed(L, 3); break; - /* - case botvars_itemdelay: - botvars->itemdelay = luaL_checkinteger(L, 3); + case botvars_driftskill: + botvars->driftskill = luaL_checkfixed(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 +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_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", + "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); + return 1; + case botdata_itemdelay: + lua_pushinteger(L, botdata->itemdelay); + return 1; + case botdata_itemconfirm: + lua_pushinteger(L, botdata->itemconfirm); + return 1; + case botdata_turnconfirm: + lua_pushinteger(L, botdata->turnconfirm); + return 1; + case botdata_respawnconfirm: + lua_pushinteger(L, botdata->respawnconfirm); + return 1; + case botdata_driftstate: + lua_pushinteger(L, botdata->driftstate); + return 1; + case botdata_driftturn: + lua_pushinteger(L, botdata->driftturn); + return 1; + case botdata_drifttime: + lua_pushinteger(L, botdata->drifttime); + return 1; + case botdata_driftlockout: + lua_pushinteger(L, botdata->driftlockout); + return 1; + case botdata_driftmaxdist: + lua_pushinteger(L, botdata->driftmaxdist); + return 1; + case botdata_driftpowerdiv: + lua_pushinteger(L, botdata->driftpowerdiv); + return 1; + case botdata_driftstatedelay: + lua_pushinteger(L, botdata->driftstatedelay); + return 1; + case botdata_acceldown: + lua_pushboolean(L, botdata->acceldown); + return 1; + case botdata_brakedown: + lua_pushboolean(L, botdata->brakedown); + return 1; + case botdata_driftdown: + lua_pushboolean(L, botdata->driftdown); + return 1; + case botdata_itemdown: + lua_pushboolean(L, botdata->itemdown); + return 1; + case botdata_dolookback: + lua_pushboolean(L, botdata->dolookback); + return 1; + case botdata_itemwasdown: + lua_pushboolean(L, botdata->itemwasdown); + return 1; + case botdata_itemthrow: + lua_pushinteger(L, botdata->itemthrow); + return 1; + case botdata_turnamt: + lua_pushinteger(L, botdata->turnamt); + 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); + return 0; + case botdata_itemconfirm: + botdata->itemconfirm = luaL_checkinteger(L, 3); + return 0; + case botdata_turnconfirm: + botdata->turnconfirm = luaL_checkinteger(L, 3); + return 0; + case botdata_respawnconfirm: + botdata->respawnconfirm = luaL_checkinteger(L, 3); + return 0; + case botdata_driftstate: + botdata->driftstate = luaL_checkinteger(L, 3); + return 0; + case botdata_driftturn: + botdata->driftturn = luaL_checkinteger(L, 3); + return 0; + case botdata_drifttime: + botdata->drifttime = luaL_checkinteger(L, 3); + return 0; + case botdata_driftlockout: + botdata->driftlockout = luaL_checkinteger(L, 3); + return 0; + case botdata_driftmaxdist: + botdata->driftmaxdist = luaL_checkinteger(L, 3); + return 0; + case botdata_driftpowerdiv: + botdata->driftpowerdiv = luaL_checkinteger(L, 3); + return 0; + case botdata_driftstatedelay: + botdata->driftstatedelay = luaL_checkinteger(L, 3); + return 0; + case botdata_acceldown: + botdata->acceldown = luaL_checkboolean(L, 3); + return 0; + case botdata_brakedown: + botdata->brakedown = luaL_checkboolean(L, 3); + return 0; + case botdata_driftdown: + botdata->driftdown = luaL_checkboolean(L, 3); + return 0; + case botdata_itemdown: + botdata->itemdown = luaL_checkboolean(L, 3); + return 0; + case botdata_dolookback: + botdata->dolookback = luaL_checkboolean(L, 3); + return 0; + case botdata_itemwasdown: + botdata->itemwasdown = luaL_checkboolean(L, 3); + return 0; + case botdata_itemthrow: + botdata->itemthrow = luaL_checkinteger(L, 3); + return 0; + case botdata_turnamt: + botdata->turnamt = luaL_checkinteger(L, 3); + 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); @@ -181,5 +376,23 @@ int LUA_BotVarsLib(lua_State *L) 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; } diff --git a/src/lua_libs.h b/src/lua_libs.h index 3fbdc8f32..b261b6be8 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -112,6 +112,7 @@ extern lua_State *gL; #define META_BOTVARS "BOTVARS_T*" #define META_BOTCONTROLLER "BOTCONTROLLER_T*" +#define META_BOTDATA "BOTDATA_T*" #define META_SPLASH "T_SPLASH_T*" #define META_FOOTSTEP "T_FOOTSTEP_T*" From c9802c36c8e38013628c9d7e55afbc025ae9a1c6 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 31 Aug 2025 21:00:22 +0200 Subject: [PATCH 07/13] Add const, move driftskill to botdata No more messing with respawn waypoints in K_BotTicker --- src/d_player.h | 3 --- src/g_game.c | 4 +--- src/k_bot.cpp | 40 +++++++++++++++------------------------- src/k_bot.h | 40 ++++++++-------------------------------- src/lua_botvarslib.c | 16 ++++++++-------- src/p_saveg.c | 2 -- 6 files changed, 32 insertions(+), 73 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 8f4478ae7..45e97f00a 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -439,9 +439,6 @@ struct botvars_t fixed_t rubberband; // Bot rubberband value UINT16 controller; // Special bot controller linedef ID - - fixed_t driftskill; // The bot's "skill" at drifts. - // Determines how soon a bot starts to drift. }; struct sonicloopcamvars_t diff --git a/src/g_game.c b/src/g_game.c index f0b166710..2f0d72dad 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2664,10 +2664,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->botvars.rubberband = FRACUNIT; p->botvars.controller = UINT16_MAX; - p->botvars.driftskill = - FixedMul(MAXDRIFTSKILL, K_BotDetermineDriftSkill(p)); - K_BotRespawn(p); + K_BotReborn(p); if (follower) P_RemoveMobj(follower); diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 85d792985..52292ac4f 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1067,18 +1067,8 @@ static void K_DrawPredictionDebug(botdata_t *bd, const player_t *player) } } -/*-------------------------------------------------- - fixed_t K_BotDetermineDriftSkill(player_t *player) - - Calculates drift skill for a player based on stats. - - Input Arguments:- - player - Player to get drift skill for. - - Return:- - Calculated drift skill. ---------------------------------------------------*/ -fixed_t K_BotDetermineDriftSkill(player_t *player) +// Calculates drift skill for a player based on stats. +fixed_t K_BotDetermineDriftSkill(const player_t *player) { return ((FRACUNIT * (player->kartspeed + player->kartweight)) / 18); } @@ -1099,12 +1089,10 @@ static void K_WaypointGetDirectionVector(waypoint_t *wp1, waypoint_t *wp2, vecto FV3_Normalize(a_o); } -/*-------------------------------------------------- - void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout) - - See header file for description. ---------------------------------------------------*/ -void K_BotSetDriftState(player_t *player, botdrift_e newstate, tic_t lockout) +// Changes a bot's drift state. +// Resets the drift timer if the old and new state are different. +// If lockout is non-zero, apply drift lockout for this many tics. +void K_BotSetDriftState(const player_t *player, botdrift_e newstate, tic_t lockout) { botdata_t *bd = &botdata[player - players]; @@ -1121,7 +1109,7 @@ void K_BotSetDriftState(player_t *player, botdrift_e newstate, tic_t lockout) #define MINBOTDRIFT (KART_FULLTURN * 2) / 3 // 0.66 // Begins and ends "forced" drifts on a per-waypoint basis. -static void K_BotStartDrift(botdata_t *bd, player_t* player) +static void K_BotStartDrift(botdata_t *bd, const player_t* player) { // Handle DRIFTING towards waypoints! boolean shouldDrift; @@ -1192,7 +1180,7 @@ static void K_BotStartDrift(botdata_t *bd, player_t* player) // and how fast we're moving. fixed_t driftpotential = M_RandomKey(MAXDRIFTSKILL); - if ((driftpotential <= player->botvars.driftskill) && + if ((driftpotential <= bd->driftskill) && (botDriftSpeed <= player->speed)) { shouldDrift = true; @@ -1360,7 +1348,7 @@ static INT32 K_HandleBotTrack(botdata_t *bd, const player_t *player, angle_t des #undef MINBOTDRIFT -static void K_IncrementBotRespawn(player_t *player, UINT8 *respawn, const UINT8 respawnmax) +static void K_IncrementBotRespawn(const player_t *player, UINT8 *respawn, const UINT8 respawnmax) { const fixed_t requireDist = (12*player->mo->scale) / FRACUNIT; INT32 progress = player->distancetofinishprev - player->distancetofinish; @@ -1538,7 +1526,7 @@ static void K_IncrementBotRespawn(player_t *player, UINT8 *respawn, const UINT8 }*/ // updates server-sided bot logic -void K_BotTicker(player_t *player) +void K_BotTicker(const player_t *player) { angle_t destangle = 0; INT32 turnamt = 0; @@ -1600,7 +1588,7 @@ void K_BotTicker(player_t *player) if (bd->respawnconfirm >= BOTRESPAWNCONFIRM || player->speed < 10*FRACUNIT) { // Now a clean function! Neat, eh? - K_SetRespawnAtNextWaypoint(player); + //K_SetRespawnAtNextWaypoint(player); // WHAT ARE YOU DOING??? RACE ALREADY! P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); @@ -1834,11 +1822,13 @@ void K_UpdateBotGameplayVars(player_t *player) } // resets some botdata stuff after respawning -void K_BotRespawn(player_t *player) +void K_BotReborn(const player_t *player) { botdata_t *bd = &botdata[player - players]; memset(bd, 0, sizeof(*bd)); + bd->driftstate = DRIFTSTATE_AUTO; + bd->driftskill = FixedMul(MAXDRIFTSKILL, K_BotDetermineDriftSkill(player)); // drift parameters, just fixed values for now bd->driftmaxdist = 200 + gamespeed*50; @@ -1846,7 +1836,7 @@ void K_BotRespawn(player_t *player) bd->driftstatedelay = TICRATE/2 - gamespeed*5; } -void K_BotResetItemConfirm(player_t *player, boolean setdelay) +void K_BotResetItemConfirm(const player_t *player, boolean setdelay) { botdata_t *bd = &botdata[player - players]; bd->itemconfirm = 0; diff --git a/src/k_bot.h b/src/k_bot.h index 3cc977052..294403b01 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -90,6 +90,9 @@ struct botdata_t UINT16 driftpowerdiv; // arbitrary divider on drift turning UINT16 driftstatedelay; // max time to wait between drift state changes + fixed_t driftskill; // The bot's "skill" at drifts. + // Determines how soon a bot starts to drift. + boolean acceldown, brakedown, driftdown, itemdown; // buttons boolean dolookback; // look out behind you boolean itemwasdown; // last tic's item button @@ -99,7 +102,7 @@ struct botdata_t // AVAILABLE FOR LUA -void K_BotTicker(player_t *player); +void K_BotTicker(const player_t *player); void K_DrawBotDebugger(const player_t *player); @@ -398,40 +401,13 @@ void K_UpdateBotGameplayVars(player_t *player); // Item usage part of ticcmd generation. void K_BotItemUsage(botdata_t *bd, const player_t *player); -void K_BotRespawn(player_t *player); +void K_BotReborn(const player_t *player); -void K_BotResetItemConfirm(player_t *player, boolean setdelay); +void K_BotResetItemConfirm(const player_t *player, boolean setdelay); -/*-------------------------------------------------- - fixed_t K_BotDetermineDriftSkill(player_t *player) +fixed_t K_BotDetermineDriftSkill(const player_t *player); - Calculates drift skill for a player based on stats. - - Input Arguments:- - player - Player to get drift skill for. - - Return:- - Calculated drift skill. ---------------------------------------------------*/ - -fixed_t K_BotDetermineDriftSkill(player_t *player); - -/*-------------------------------------------------- - void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout) - - Changes a bot's drift state. - Resets the drift timer if the old and new state are different. - - Input Arguments:- - player - Player to set drift state for. - newstate - The new drift state. - lockout - If non-zero, apply drift lockout for this many tics. - - Return:- - None ---------------------------------------------------*/ - -void K_BotSetDriftState(player_t *player, botdrift_e newstate, tic_t lockout); +void K_BotSetDriftState(const player_t *player, botdrift_e newstate, tic_t lockout); botdata_t *K_GetBotData(UINT8 num); diff --git a/src/lua_botvarslib.c b/src/lua_botvarslib.c index 57606111b..cca32b86b 100644 --- a/src/lua_botvarslib.c +++ b/src/lua_botvarslib.c @@ -25,7 +25,6 @@ enum botvars { botvars_diffincrease, botvars_rival, botvars_rubberband, - botvars_driftskill, }; static const char *const botvars_opt[] = { @@ -35,7 +34,6 @@ static const char *const botvars_opt[] = { "diffincrease", "rival", "rubberband", - "driftskill", NULL }; @@ -69,9 +67,6 @@ static int botvars_get(lua_State *L) case botvars_rubberband: lua_pushfixed(L, botvars->rubberband); break; - case botvars_driftskill: - lua_pushfixed(L, botvars->driftskill); - break; } return 1; } @@ -107,9 +102,6 @@ static int botvars_set(lua_State *L) case botvars_rubberband: botvars->rubberband = luaL_checkfixed(L, 3); break; - case botvars_driftskill: - botvars->driftskill = luaL_checkfixed(L, 3); - break; } return 0; } @@ -129,6 +121,7 @@ enum botdata { botdata_driftmaxdist, botdata_driftpowerdiv, botdata_driftstatedelay, + botdata_driftskill, botdata_acceldown, botdata_brakedown, botdata_driftdown, @@ -152,6 +145,7 @@ static const char *const botdata_opt[] = { "driftmaxdist", "driftpowerdiv", "driftstatedelay", + "driftskill", "acceldown", "brakedown", "driftdown", @@ -209,6 +203,9 @@ static int botdata_get(lua_State *L) case botdata_driftstatedelay: lua_pushinteger(L, botdata->driftstatedelay); return 1; + case botdata_driftskill: + lua_pushfixed(L, botdata->driftskill); + return 1; case botdata_acceldown: lua_pushboolean(L, botdata->acceldown); return 1; @@ -282,6 +279,9 @@ static int botdata_set(lua_State *L) case botdata_driftstatedelay: botdata->driftstatedelay = luaL_checkinteger(L, 3); return 0; + case botdata_driftskill: + botdata->driftskill = luaL_checkfixed(L, 3); + return 0; case botdata_acceldown: botdata->acceldown = luaL_checkboolean(L, 3); return 0; diff --git a/src/p_saveg.c b/src/p_saveg.c index 3121d26d0..d5089dc23 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -402,7 +402,6 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].botvars.rival); WRITEFIXED(save->p, players[i].botvars.rubberband); WRITEUINT16(save->p, players[i].botvars.controller); - WRITEFIXED(save->p, players[i].botvars.driftskill); WRITEFIXED(save->p, players[i].outrun); WRITEUINT8(save->p, players[i].outruntime); @@ -750,7 +749,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].botvars.rival = (boolean)READUINT8(save->p); players[i].botvars.rubberband = READFIXED(save->p); players[i].botvars.controller = READUINT16(save->p); - players[i].botvars.driftskill = READFIXED(save->p); players[i].outrun = READFIXED(save->p); players[i].outruntime = READUINT8(save->p); From 8d99f98bea4598de3aa641db4bb3b285bb72272a Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 31 Aug 2025 21:32:16 +0200 Subject: [PATCH 08/13] Actually sync respawns properly --- src/d_netcmd.c | 7 +++++-- src/k_bot.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 994c04e0e..0a5f7ebef 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3574,7 +3574,10 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum) INT32 respawnplayer = READINT32(*cp); // You can't respawn someone else. Nice try, there. - if (respawnplayer != playernum || P_PlayerInPain(&players[respawnplayer]) || spbplace == players[respawnplayer].position) // srb2kart: "|| (!(gametyperules & GTR_CIRCUIT))" + // Unless it's a bot. They can respawn whenever they feel like it, if the server says so. + if (players[respawnplayer].bot + ? playernum != serverplayer + : respawnplayer != playernum || P_PlayerInPain(&players[respawnplayer]) || spbplace == players[respawnplayer].position) { CONS_Alert(CONS_WARNING, M_GetText("Illegal respawn command received from %s\n"), player_names[playernum]); if (server) @@ -3589,7 +3592,7 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum) return; P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 1, DMG_INSTAKILL); - demo_extradata[playernum] |= DXD_RESPAWN; + demo_extradata[respawnplayer] |= DXD_RESPAWN; } } diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 52292ac4f..317e63a77 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1587,11 +1587,12 @@ void K_BotTicker(const player_t *player) if (bd->respawnconfirm >= BOTRESPAWNCONFIRM || player->speed < 10*FRACUNIT) { - // Now a clean function! Neat, eh? - //K_SetRespawnAtNextWaypoint(player); - // WHAT ARE YOU DOING??? RACE ALREADY! - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); + // ... + // FINE, I'LL SEND A RESPAWN COMMAND ON YOUR BEHALF! + char buf[4], *cp = buf; + WRITEINT32(cp, player - players); + SendNetXCmdForPlayer(consoleplayer, XD_RESPAWN, buf, sizeof(buf)); } return; } From 98fd7d0015b2f46cd969664b9479f542a4cd0cc9 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 31 Aug 2025 21:43:43 +0200 Subject: [PATCH 09/13] Shrink XD_RESPAWN to avoid potential netxcmd bloat --- src/d_netcmd.c | 6 +++--- src/k_bot.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 0a5f7ebef..6f086866c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3517,7 +3517,7 @@ static void Got_Pause(UINT8 **cp, INT32 playernum) // Command for stuck characters in netgames, griefing, etc. static void HandleRespawnCommand(UINT8 localplayer) { - UINT8 buf[4]; + UINT8 buf[1]; UINT8 *cp = buf; if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) @@ -3544,7 +3544,7 @@ static void HandleRespawnCommand(UINT8 localplayer) return; } - WRITEINT32(cp, g_localplayers[localplayer]); + WRITEUINT8(cp, g_localplayers[localplayer]); SendNetXCmdForPlayer(localplayer, XD_RESPAWN, &buf, sizeof(buf)); } @@ -3571,7 +3571,7 @@ static void Command_Respawn4(void) static void Got_Respawn(UINT8 **cp, INT32 playernum) { - INT32 respawnplayer = READINT32(*cp); + INT32 respawnplayer = READUINT8(*cp); // You can't respawn someone else. Nice try, there. // Unless it's a bot. They can respawn whenever they feel like it, if the server says so. diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 317e63a77..372d8f2cc 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1590,8 +1590,8 @@ void K_BotTicker(const player_t *player) // WHAT ARE YOU DOING??? RACE ALREADY! // ... // FINE, I'LL SEND A RESPAWN COMMAND ON YOUR BEHALF! - char buf[4], *cp = buf; - WRITEINT32(cp, player - players); + char buf[1], *cp = buf; + WRITEUINT8(cp, player - players); SendNetXCmdForPlayer(consoleplayer, XD_RESPAWN, buf, sizeof(buf)); } return; From f51a223642d384baedd1bb7067a1238af99b4dd5 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 31 Aug 2025 21:57:03 +0200 Subject: [PATCH 10/13] Add 1 to the version check for ZT_BOT This should let demos recorded on blankart-dev still work properly demoversion is getting incremented soon anyway... --- src/g_demo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_demo.c b/src/g_demo.c index aa890f090..0bdae712a 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -393,7 +393,7 @@ static UINT8 *G_ReadZipTic(ticcmd_t *cmd, UINT8 *dp, UINT16 version) if (ziptic & ZT_FLAGS) cmd->flags = READUINT8(dp); - if (version < 0x0009 && ziptic & 0x8000) // ZT_BOT + if (version < 0x000a && ziptic & 0x8000) // ZT_BOT { UINT16 botziptic = READUINT16(dp); if (botziptic & 1) READSINT8(dp); From d2c010ea0112f4fdff532649a8a7699fc55b8467 Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 31 Aug 2025 22:33:26 +0200 Subject: [PATCH 11/13] Switch to a difficulty-based drift skill formula This will get reworked later down the line (hopefully), but for now, I think it ought to be consistent regardless of stats. Should make driftpoint placement less of a nightmare at least --- src/k_bot.cpp | 7 ++++--- src/k_bot.h | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 372d8f2cc..6e50bc152 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1067,10 +1067,11 @@ static void K_DrawPredictionDebug(botdata_t *bd, const player_t *player) } } -// Calculates drift skill for a player based on stats. -fixed_t K_BotDetermineDriftSkill(const player_t *player) +// Calculates drift skill for a player based on ~~stats~~ difficulty. +static fixed_t K_BotDetermineDriftSkill(const player_t *player) { - return ((FRACUNIT * (player->kartspeed + player->kartweight)) / 18); + return FRACUNIT/8 + (FRACUNIT * player->botvars.difficulty) / DIFFICULTBOT; + //return ((FRACUNIT * (player->kartspeed + player->kartweight)) / 18); } static void K_WaypointGetDirectionVector(waypoint_t *wp1, waypoint_t *wp2, vector3_t *a_o) diff --git a/src/k_bot.h b/src/k_bot.h index 294403b01..5b371007e 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -405,8 +405,6 @@ void K_BotReborn(const player_t *player); void K_BotResetItemConfirm(const player_t *player, boolean setdelay); -fixed_t K_BotDetermineDriftSkill(const player_t *player); - void K_BotSetDriftState(const player_t *player, botdrift_e newstate, tic_t lockout); botdata_t *K_GetBotData(UINT8 num); From 0efd36ac5d6dd42d90b9662a8e68f6bb79baf905 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 31 Aug 2025 20:57:47 -0400 Subject: [PATCH 12/13] Fix the Bot Flame Shield usage. Turns out negative itemconfirm was a shitty hack. Lets rewrite this to use itemdelay instead. --- src/k_botitem.cpp | 69 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index 41a1203a4..06451af84 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -988,40 +988,46 @@ static void K_BotItemFlame(botdata_t *bd, const player_t *player) if (P_IsObjectOnGround(player->mo) == false) { + // Drain itemdelay as needed so theres no delay when landing. + if (bd->itemdelay) + { + bd->itemdelay--; + bd->itemconfirm = 0; + } + // Don't use while mid-air. return; } - if (bd->itemconfirm > TICRATE) + if (player->botvars.difficulty < 6 || player->flametimer <= 2*TICRATE) { - if (player->botvars.difficulty >= 6 && player->flametimer <= 2*TICRATE) + // We aren't smart enough to use this properly. + // ...or we are doing the finishing blow. + bd->itemdown = true; + bd->itemdelay = 0; + bd->itemconfirm++; + return; + } + + if (!bd->itemdelay) + { + if (player->flamestore < FLAMESTOREMAX - TICRATE/4) { bd->itemdown = true; - bd->itemconfirm = 0; } - else if (player->flametimer > 0) + else { - if (player->botvars.difficulty < 6 || player->flamestore < FLAMESTOREMAX - TICRATE/4) - { - bd->itemdown = true; - bd->itemconfirm = 0; - } - else - { - UINT8 difficultyadjust = MAXBOTDIFFICULTY - player->botvars.difficulty; - bd->itemconfirm = (-TICRATE/2) - difficultyadjust; - } - } - } - else - { - if (player->botvars.difficulty >= 6 && player->flametimer <= 2*TICRATE) - { - bd->itemconfirm = 0; + UINT8 difficultyadjust = MAXBOTDIFFICULTY - player->botvars.difficulty; + bd->itemdelay = (TICRATE/2) + difficultyadjust; } bd->itemconfirm++; } + else + { + bd->itemdelay--; + bd->itemconfirm = 0; + } } // Item usage for rings. @@ -1185,6 +1191,25 @@ static void K_BotItemRouletteMash(botdata_t *bd, const player_t *player) } } +// Determines if we should use the Regular Item Delay Logic. +static boolean K_CanHandleItemDelay(botdata_t *bd, const player_t *player) +{ + if (!bd->itemdelay) + { + // We don't even have any. Don't bother. + return false; + } + + if (player->flametimer > 0) + { + // Flame Shield? We handle this there instead. + return false + + } + + return true; +} + // See header file for description. void K_BotItemUsage(botdata_t *bd, const player_t *player) { @@ -1200,7 +1225,7 @@ void K_BotItemUsage(botdata_t *bd, const player_t *player) return; } - if (bd->itemdelay) + if (K_CanHandleItemDelay(bd, player)) { bd->itemdelay--; bd->itemconfirm = 0; From 8637fd772a263bc7472e49ae99f28e7656c96046 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 31 Aug 2025 22:37:18 -0400 Subject: [PATCH 13/13] Allow bots to reflect with the Bubble Shield rework since it no longer hits players. --- src/k_bot.h | 15 ++++++++ src/k_botitem.cpp | 58 +++++++++--------------------- src/k_botsearch.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++++ src/k_collide.c | 16 ++++++--- 4 files changed, 130 insertions(+), 46 deletions(-) diff --git a/src/k_bot.h b/src/k_bot.h index 5b371007e..b74722be0 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -349,6 +349,21 @@ void K_NudgePredictionTowardsObjects(botdata_t *bd, const player_t *player); INT32 K_PositionBully(const player_t *player); +/*-------------------------------------------------- + boolean K_GetBlockedBubbleItem(const player_t *player, , fixed_t radius) + + Searches the blockmap for items to block with the Bubble Shield + + Input Arguments:- + player - Bot to run this for. + radius - Radius around player to check for items. + + Return:- + false if couldn't find anything, otherwise true to attempt blocking item. +--------------------------------------------------*/ + +boolean K_GetBlockedBubbleItem(const player_t *player, fixed_t radius); + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index 06451af84..96da3b04c 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -919,55 +919,27 @@ static void K_BotItemBubble(botdata_t *bd, const player_t *player) boolean hold = false; - if (player->bubbleblowup <= 0) + // We are holding this thing too long, lets get rid of it. + if (bd->itemconfirm > 20*TICRATE) { - UINT8 i; - bd->itemconfirm++; + if (player->itemflags & IF_HOLDREADY) + { + bd->itemdown = true; + } + + return; + } + + if (player->bubbleblowup <= 0) + { if (player->bubblecool <= 0) { fixed_t radius = 192 * player->mo->scale; radius = Easing_Linear(FRACUNIT * player->botvars.difficulty / MAXBOTDIFFICULTY, 2*radius, radius); - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->flashing) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= radius) - { - hold = true; - break; - } - } - } - } - else if (player->bubbleblowup >= bubbletime) - { - if (bd->itemconfirm > 10*TICRATE) - { - hold = true; + hold = K_GetBlockedBubbleItem(player, radius); } } else if (player->bubbleblowup < bubbletime) @@ -979,6 +951,8 @@ static void K_BotItemBubble(botdata_t *bd, const player_t *player) { bd->itemdown = true; } + + bd->itemconfirm++; } // Item usage for Flame Shield. @@ -1203,7 +1177,7 @@ static boolean K_CanHandleItemDelay(botdata_t *bd, const player_t *player) if (player->flametimer > 0) { // Flame Shield? We handle this there instead. - return false + return false; } diff --git a/src/k_botsearch.cpp b/src/k_botsearch.cpp index 8d2e96883..37ae6146d 100644 --- a/src/k_botsearch.cpp +++ b/src/k_botsearch.cpp @@ -981,3 +981,90 @@ INT32 K_PositionBully(const player_t *player) return KART_FULLTURN; } + +static mobj_t *bubbleSource; +static fixed_t bubbleDist; +static boolean bubbleBlock; + +static inline BlockItReturn_t PIT_BubbleShieldBlock(mobj_t *thing) +{ + if (bubbleSource == NULL || P_MobjWasRemoved(bubbleSource)) + { + // Invalid? + return BMIT_ABORT; + } + + if (thing == NULL || P_MobjWasRemoved(thing)) + { + // Invalid? + return BMIT_ABORT; + } + + if (thing == bubbleSource) + { + // Don't block yourself!! + return BMIT_CONTINUE; + } + + if (thing->player) + { + // We don't block players. + return BMIT_CONTINUE; + } + + if (thing->health <= 0) + { + // Dead + return BMIT_CONTINUE; + } + + if (!P_IsKartItem(thing->type) && !(thing->flags & MF_SHOOTABLE)) + { + // Not a Item. + return BMIT_CONTINUE; + } + + if (P_AproxDistance(P_AproxDistance( + bubbleSource->x - thing->x, + bubbleSource->y - thing->y), + (bubbleSource->z - thing->z) / 4) > bubbleDist) + { + // Too far away + return BMIT_CONTINUE; + } + +#if 0 + if (P_CheckSight(bubbleSource, thing) == false) + { + // Not in sight + return BMIT_CONTINUE; + } +#endif + + bubbleBlock = true; + return BMIT_CONTINUE; +} + +// Searches the blockmap for items to block with the Bubble Shield +boolean K_GetBlockedBubbleItem(const player_t *player, fixed_t radius) +{ + INT32 bx, by, xl, xh, yl, yh; + + bubbleSource = player->mo; + bubbleDist = radius; + bubbleBlock = false; + + // Use blockmap to check for nearby harmful items. + yh = (unsigned)(bubbleSource->y + bubbleDist - bmaporgy)>>MAPBLOCKSHIFT; + yl = (unsigned)(bubbleSource->y - bubbleDist - bmaporgy)>>MAPBLOCKSHIFT; + xh = (unsigned)(bubbleSource->x + bubbleDist - bmaporgx)>>MAPBLOCKSHIFT; + xl = (unsigned)(bubbleSource->x - bubbleDist - bmaporgx)>>MAPBLOCKSHIFT; + + BMBOUNDFIX (xl, xh, yl, yh); + + for (by = yl; by <= yh; by++) + for (bx = xl; bx <= xh; bx++) + P_BlockThingsIterator(bx, by, PIT_BubbleShieldBlock); + + return bubbleBlock; +} diff --git a/src/k_collide.c b/src/k_collide.c index 4a42a5d70..cf32bd60a 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -554,9 +554,9 @@ boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2) } else { - t2->momx = -6*t2->momx; - t2->momy = -6*t2->momy; - t2->momz = -6*t2->momz; + t2->momx = (-1*t2->momx)/2; + t2->momy = (-1*t2->momy)/2; + t2->momz = (-1*t2->momz)/2; t2->angle += ANGLE_180; } if (t2->type == MT_JAWZ) @@ -598,7 +598,15 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) if (t2->flags & MF_SHOOTABLE) { - P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); + boolean shootable = P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); + + if (shootable) + { + // Drain my stuff please. + K_BubbleShieldCollideDrain(t1->target->player, t1); + } + + return shootable; } // no interaction