From 76b88990ccc6c110b545c777d8ba83393347788a Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 31 Jul 2025 06:12:20 -0400 Subject: [PATCH 01/10] WIP: Foes --- src/d_player.h | 1 + src/g_game.c | 3 + src/k_bot.cpp | 4 +- src/k_botitem.cpp | 2 +- src/k_grandprix.cpp | 168 ++++++++++++++++++++++++++++++-------------- src/k_hud.cpp | 2 +- src/k_kart.c | 18 +++++ src/p_saveg.cpp | 4 ++ src/p_saveg.h | 1 + 9 files changed, 148 insertions(+), 55 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index e73ff2e12..03527a1ae 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -418,6 +418,7 @@ struct botvars_t UINT8 difficulty; // Bot's difficulty setting INT16 diffincrease; // In GP: bot difficulty will increase this much next round boolean rival; // If true, they're the GP rival + boolean foe; // If true, in contention for top X // All entries above persist between rounds and must be recorded in demos diff --git a/src/g_game.c b/src/g_game.c index e59199a08..1b5b5208f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2271,6 +2271,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT16 botdiffincrease; boolean botrival; + boolean botfoe; boolean cangrabitems; @@ -2381,6 +2382,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) botdiffincrease = players[player].botvars.diffincrease; botrival = players[player].botvars.rival; + botfoe = players[player].botvars.foe; totalring = players[player].totalring; xtralife = players[player].xtralife; @@ -2641,6 +2643,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->spheres = spheres; p->botvars.diffincrease = botdiffincrease; p->botvars.rival = botrival; + p->botvars.foe = botfoe; p->xtralife = xtralife; // SRB2kart diff --git a/src/k_bot.cpp b/src/k_bot.cpp index bb4cb3e5c..26ea3a022 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -620,7 +620,7 @@ static UINT32 K_BotRubberbandDistance(const player_t *player) UINT8 pos = 1; UINT8 i; - if (player->botvars.rival || cv_levelskull.value) + if (player->botvars.rival || player->botvars.foe || cv_levelskull.value) { // The rival should always try to be the front runner for the race. return 0; @@ -694,6 +694,8 @@ fixed_t K_BotRubberband(const player_t *player) { UINT8 levelreduce = std::min(3, player->botvars.difficulty/4); // How much to drop the "effective level" of bots that are consistently behind expreduce = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, levelreduce*FRACUNIT, 0); + if (player->botvars.foe) + expreduce /= 2; } fixed_t difficultyEase = (((player->botvars.difficulty - 1) * FRACUNIT) - expreduce) / (MAXBOTDIFFICULTY - 1); diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index d042f0faa..46a6748a4 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -299,7 +299,7 @@ static boolean K_RivalBotAggression(const player_t *bot, const player_t *target) return false; } - if (bot->botvars.rival == false && !cv_levelskull.value) + if (!(bot->botvars.rival || bot->botvars.foe) && !cv_levelskull.value) { // Not the rival, we aren't self-aware. return false; diff --git a/src/k_grandprix.cpp b/src/k_grandprix.cpp index d990dd94a..06ff42578 100644 --- a/src/k_grandprix.cpp +++ b/src/k_grandprix.cpp @@ -98,6 +98,117 @@ static UINT8 K_GetOffsetStartingDifficulty(const UINT8 startingdifficulty, UINT8 return startingdifficulty - offset; } +/*-------------------------------------------------- + static INT16 K_RivalScore(player_t *bot) + + Creates a "rival score" for a bot, used to determine which bot is the + most deserving of the rival status. + + Input Arguments:- + bot - Player to check. + + Return:- + "Rival score" value. +--------------------------------------------------*/ +static INT16 K_RivalScore(player_t *bot) +{ + const UINT16 difficulty = bot->botvars.difficulty; + const UINT16 score = bot->score; + SINT8 roundnum = 1, roundsleft = 1; + UINT16 lowestscore = UINT16_MAX; + UINT8 lowestdifficulty = MAXBOTDIFFICULTY; + UINT8 i; + + if (grandprixinfo.cup != NULL && roundqueue.size > 0) + { + roundnum = roundqueue.roundnum; + roundsleft = grandprixinfo.cup->numlevels - roundnum; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + if (players[i].score < lowestscore) + { + lowestscore = players[i].score; + } + + if (players[i].bot == true && players[i].botvars.difficulty < lowestdifficulty) + { + lowestdifficulty = players[i].botvars.difficulty; + } + } + + // In the early game, difficulty is more important. + // This will try to influence the higher difficulty bots to get rival more often & get even more points. + // However, when we're running low on matches left, we need to focus more on raw score! + + return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * roundnum); +} + +static boolean CompareRivals(player_t *a, player_t *b) +{ + CONS_Printf("compare foes\n"); + if (a == NULL) + return false; + if (b == NULL) + return true; + + CONS_Printf("%s %d VS %s %d\n", player_names[a-players], K_RivalScore(a), player_names[b-players], K_RivalScore(b)); + + if (K_RivalScore(a) != K_RivalScore(b)) + { + // Push bad position to the back. + CONS_Printf("returning known\n"); + return (K_RivalScore(a) > K_RivalScore(b)); + } + + CONS_Printf("returning shuffle\n"); + + // They are equals, so just randomize + return (P_Random(PR_BOTS) & 1); +} + +static void K_AssignFoes(void) +{ + std::vector bots; + CONS_Printf("foe assignment\n"); + for (UINT8 i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false) + continue; + + player_t *player = &players[i]; + + if (!player->spectator && player->bot) + { + CONS_Printf("added %s\n", player_names[i]); + bots.push_back(player); + player->botvars.foe = false; + } + } + + CONS_Printf("sort foes\n"); + std::stable_sort(bots.begin(), bots.end(), CompareRivals); + + UINT8 i = 0; + for (auto &bot : bots) + { + CONS_Printf("assign foes\n"); + + if (bot != NULL) + bot->botvars.foe = true; + + i++; + if (i > 2) + break; + } +} + /*-------------------------------------------------- void K_InitGrandPrixBots(void) @@ -254,6 +365,8 @@ void K_InitGrandPrixBots(void) { break; } + if (i <= 2) + players[newplayernum-1].botvars.foe = true; } } @@ -289,64 +402,13 @@ void K_LoadGrandPrixSaveGame(void) K_SetBot(i, savedata.bots[i].skin, savedata.bots[i].difficulty, BOT_STYLE_NORMAL); players[i].botvars.rival = savedata.bots[i].rival; + players[i].botvars.foe = savedata.bots[i].foe; players[i].score = savedata.bots[i].score; players[i].spectator = K_BotDefaultSpectator(); } } -/*-------------------------------------------------- - static INT16 K_RivalScore(player_t *bot) - - Creates a "rival score" for a bot, used to determine which bot is the - most deserving of the rival status. - - Input Arguments:- - bot - Player to check. - - Return:- - "Rival score" value. ---------------------------------------------------*/ -static INT16 K_RivalScore(player_t *bot) -{ - const UINT16 difficulty = bot->botvars.difficulty; - const UINT16 score = bot->score; - SINT8 roundnum = 1, roundsleft = 1; - UINT16 lowestscore = UINT16_MAX; - UINT8 lowestdifficulty = MAXBOTDIFFICULTY; - UINT8 i; - - if (grandprixinfo.cup != NULL && roundqueue.size > 0) - { - roundnum = roundqueue.roundnum; - roundsleft = grandprixinfo.cup->numlevels - roundnum; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - { - continue; - } - - if (players[i].score < lowestscore) - { - lowestscore = players[i].score; - } - - if (players[i].bot == true && players[i].botvars.difficulty < lowestdifficulty) - { - lowestdifficulty = players[i].botvars.difficulty; - } - } - - // In the early game, difficulty is more important. - // This will try to influence the higher difficulty bots to get rival more often & get even more points. - // However, when we're running low on matches left, we need to focus more on raw score! - - return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * roundnum); -} - /*-------------------------------------------------- void K_UpdateGrandPrixBots(void) @@ -374,6 +436,8 @@ void K_UpdateGrandPrixBots(void) players[i].spectator = K_BotDefaultSpectator(); } + K_AssignFoes(); + if (grandprixinfo.wonround == false) { return; diff --git a/src/k_hud.cpp b/src/k_hud.cpp index c3e444930..9260996b1 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -7314,7 +7314,7 @@ static void K_DrawBotDebugger(void) V_DrawSmallString(8, 14, 0, va("Difficulty: %d / %d", bot->botvars.difficulty, MAXBOTDIFFICULTY)); V_DrawSmallString(8, 18, 0, va("Difficulty increase: %d", bot->botvars.diffincrease)); - V_DrawSmallString(8, 22, 0, va("Rival: %d", (UINT8)(bot->botvars.rival == true))); + V_DrawSmallString(8, 22, 0, va("Rival / Foe: %d / %d", (UINT8)(bot->botvars.rival == true), (UINT8)(bot->botvars.foe == true))); V_DrawSmallString(8, 26, 0, va("Rubberbanding: %.02f", FIXED_TO_FLOAT(bot->botvars.rubberband) * 100.0f)); V_DrawSmallString(8, 32, 0, va("Item delay: %d", bot->botvars.itemdelay)); diff --git a/src/k_kart.c b/src/k_kart.c index ce6d07536..a5c95eab5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1621,6 +1621,8 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed // Double speed for the rival! if (player->botvars.rival || cv_levelskull.value) player->draftpower += add; + else if (player->botvars.foe) + player->draftpower += add/2; else if (dest->player->bot) // Reduce bot gluts. player->draftpower -= 3*add/4; } @@ -3554,6 +3556,10 @@ static fixed_t K_RingDurationBoost(const player_t *player) // x2.0 for Rival ret *= 2; } + else if (player->botvars.foe) + { + ret = 3 * ret / 2; + } } return ret; @@ -3976,6 +3982,11 @@ fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dor // +10% top speed for the rival finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10); } + else if (player->bot && player->botvars.foe) + { + // +5% for foes + finalspeed = FixedMul(finalspeed, 21*FRACUNIT/20); + } } } @@ -10965,6 +10976,13 @@ void K_KartResetPlayerColor(player_t *player) goto base; } + if (player->botvars.foe) + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_BLACK; + goto finalise; + } + if (player->eggmanexplode) // You're gonna diiiiie { const INT32 flashtime = 4<<(player->eggmanexplode/TICRATE); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 45da46e47..0d57458d0 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -138,6 +138,7 @@ static inline void P_ArchivePlayer(savebuffer_t *save) WRITEUINT8(save->p, players[i].botvars.difficulty); WRITEUINT8(save->p, (UINT8)players[i].botvars.rival); + WRITEUINT8(save->p, (UINT8)players[i].botvars.foe); WRITEUINT32(save->p, players[i].score); } @@ -195,6 +196,7 @@ static boolean P_UnArchivePlayer(savebuffer_t *save) savedata.bots[pid].difficulty = READUINT8(save->p); savedata.bots[pid].rival = (boolean)READUINT8(save->p); + savedata.bots[pid].foe = (boolean)READUINT8(save->p); savedata.bots[pid].score = READUINT32(save->p); } @@ -765,6 +767,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].botvars.difficulty); WRITEUINT8(save->p, players[i].botvars.diffincrease); WRITEUINT8(save->p, players[i].botvars.rival); + WRITEUINT8(save->p, players[i].botvars.foe); WRITEFIXED(save->p, players[i].botvars.rubberband); WRITEUINT8(save->p, players[i].botvars.bumpslow); WRITEUINT32(save->p, players[i].botvars.itemdelay); @@ -1430,6 +1433,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].botvars.difficulty = READUINT8(save->p); players[i].botvars.diffincrease = READUINT8(save->p); players[i].botvars.rival = (boolean)READUINT8(save->p); + players[i].botvars.foe = (boolean)READUINT8(save->p); players[i].botvars.rubberband = READFIXED(save->p); players[i].botvars.bumpslow = READUINT8(save->p); players[i].botvars.itemdelay = READUINT32(save->p); diff --git a/src/p_saveg.h b/src/p_saveg.h index f0beb35e3..1848550ad 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -46,6 +46,7 @@ struct savedata_bot_s UINT8 skin; UINT8 difficulty; boolean rival; + boolean foe; UINT32 score; }; From bb417816c2434d35969f0c0b51b7cb208a7b35c1 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 31 Jul 2025 15:22:03 -0400 Subject: [PATCH 02/10] Foes use tighter rubberbanding target --- src/k_bot.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 26ea3a022..02cdbb8e5 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -615,17 +615,20 @@ fixed_t K_BotMapModifier(void) --------------------------------------------------*/ static UINT32 K_BotRubberbandDistance(const player_t *player) { - const UINT32 spacing = FixedDiv(640 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; + UINT32 spacing = FixedDiv(640 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; const UINT8 portpriority = player - players; UINT8 pos = 1; UINT8 i; - if (player->botvars.rival || player->botvars.foe || cv_levelskull.value) + if (player->botvars.rival || cv_levelskull.value) { // The rival should always try to be the front runner for the race. return 0; } + if (player->botvars.foe) + spacing /= 2; + for (i = 0; i < MAXPLAYERS; i++) { if (i == portpriority) From ec36f6bb01f7995bf6c96e88726410389a5b5356 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 1 Aug 2025 15:31:14 -0400 Subject: [PATCH 03/10] Guarantee that Foes/Rivals always try to pass generic bots --- src/k_bot.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 02cdbb8e5..aa3252ece 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -652,6 +652,11 @@ static UINT32 K_BotRubberbandDistance(const player_t *player) continue; } + if (player->botvars.foe && !players[i].botvars.foe) + { + continue; + } + // First check difficulty levels, then score, then settle it with port priority! if (player->botvars.difficulty < players[i].botvars.difficulty) { From 528eaf0e64fbc3bc55e60374394f506819228fd7 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 3 Aug 2025 18:10:05 -0400 Subject: [PATCH 04/10] I would really like to believe that this just makes foes work in demos --- src/g_demo.cpp | 4 ++++ src/k_grandprix.cpp | 11 ----------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 172309016..17e095e29 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -321,6 +321,7 @@ void G_ReadDemoExtraData(void) players[p].botvars.difficulty = READUINT8(demobuf.p); players[p].botvars.diffincrease = READINT16(demobuf.p); // needed to avoid having to duplicate logic players[p].botvars.rival = (boolean)READUINT8(demobuf.p); + players[p].botvars.foe = (boolean)READUINT8(demobuf.p); } } if (extradata & DXD_PLAYSTATE) @@ -497,6 +498,7 @@ void G_WriteDemoExtraData(void) WRITEUINT8(demobuf.p, players[i].botvars.difficulty); WRITEINT16(demobuf.p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.rival); + WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.foe); } } if (demo_extradata[i] & DXD_PLAYSTATE) @@ -2111,6 +2113,7 @@ void G_BeginRecording(void) WRITEUINT8(demobuf.p, player->botvars.difficulty); WRITEINT16(demobuf.p, player->botvars.diffincrease); // needed to avoid having to duplicate logic WRITEUINT8(demobuf.p, (UINT8)player->botvars.rival); + WRITEUINT8(demobuf.p, (UINT8)player->botvars.foe); } // Name @@ -3222,6 +3225,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) players[p].botvars.difficulty = READUINT8(demobuf.p); players[p].botvars.diffincrease = READINT16(demobuf.p); // needed to avoid having to duplicate logic players[p].botvars.rival = (boolean)READUINT8(demobuf.p); + players[p].botvars.foe = (boolean)READUINT8(demobuf.p); } K_UpdateShrinkCheat(&players[p]); diff --git a/src/k_grandprix.cpp b/src/k_grandprix.cpp index 06ff42578..6b6610ff9 100644 --- a/src/k_grandprix.cpp +++ b/src/k_grandprix.cpp @@ -152,23 +152,17 @@ static INT16 K_RivalScore(player_t *bot) static boolean CompareRivals(player_t *a, player_t *b) { - CONS_Printf("compare foes\n"); if (a == NULL) return false; if (b == NULL) return true; - CONS_Printf("%s %d VS %s %d\n", player_names[a-players], K_RivalScore(a), player_names[b-players], K_RivalScore(b)); - if (K_RivalScore(a) != K_RivalScore(b)) { // Push bad position to the back. - CONS_Printf("returning known\n"); return (K_RivalScore(a) > K_RivalScore(b)); } - CONS_Printf("returning shuffle\n"); - // They are equals, so just randomize return (P_Random(PR_BOTS) & 1); } @@ -176,7 +170,6 @@ static boolean CompareRivals(player_t *a, player_t *b) static void K_AssignFoes(void) { std::vector bots; - CONS_Printf("foe assignment\n"); for (UINT8 i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] == false) @@ -186,20 +179,16 @@ static void K_AssignFoes(void) if (!player->spectator && player->bot) { - CONS_Printf("added %s\n", player_names[i]); bots.push_back(player); player->botvars.foe = false; } } - CONS_Printf("sort foes\n"); std::stable_sort(bots.begin(), bots.end(), CompareRivals); UINT8 i = 0; for (auto &bot : bots) { - CONS_Printf("assign foes\n"); - if (bot != NULL) bot->botvars.foe = true; From 73438dd629c9fb722b58da73d99ab8947b0fe227 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 3 Aug 2025 18:17:30 -0400 Subject: [PATCH 05/10] Subdued foes --- src/k_bot.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index aa3252ece..be95aaf81 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -626,8 +626,10 @@ static UINT32 K_BotRubberbandDistance(const player_t *player) return 0; } + /* if (player->botvars.foe) spacing /= 2; + */ for (i = 0; i < MAXPLAYERS; i++) { From 6a2568e7a7dd9b7442c1dd7f305641f1d007bcb2 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 3 Aug 2025 21:18:40 -0400 Subject: [PATCH 06/10] Foes in Match Race --- src/k_bot.cpp | 2 ++ src/k_grandprix.cpp | 4 ++-- src/k_grandprix.h | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index be95aaf81..251f0f3c8 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -408,6 +408,8 @@ void K_UpdateMatchRaceBots(void) clear_bots(wantedbots); } + K_AssignFoes(); + // We should have enough bots now :) #ifdef HAVE_DISCORDRPC diff --git a/src/k_grandprix.cpp b/src/k_grandprix.cpp index 6b6610ff9..5196aac60 100644 --- a/src/k_grandprix.cpp +++ b/src/k_grandprix.cpp @@ -159,7 +159,7 @@ static boolean CompareRivals(player_t *a, player_t *b) if (K_RivalScore(a) != K_RivalScore(b)) { - // Push bad position to the back. + // Rival Score is HIGH when bots are strong. Sort them first! return (K_RivalScore(a) > K_RivalScore(b)); } @@ -167,7 +167,7 @@ static boolean CompareRivals(player_t *a, player_t *b) return (P_Random(PR_BOTS) & 1); } -static void K_AssignFoes(void) +void K_AssignFoes(void) { std::vector bots; for (UINT8 i = 0; i < MAXPLAYERS; i++) diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 8b62187dc..b3e1620bd 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -200,6 +200,8 @@ boolean K_CanChangeRules(boolean allowdemos); boolean K_BotDefaultSpectator(void); +void K_AssignFoes(void); + #ifdef __cplusplus } // extern "C" From d78afd9de38fb3cd1a26db21a7236c4f1ae14604 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 3 Aug 2025 23:36:38 -0400 Subject: [PATCH 07/10] Red FOE nametags, try not to sort empty lists of bots --- src/k_grandprix.cpp | 15 ++++++--- src/k_hud.cpp | 38 +++++++++++----------- src/k_kart.c | 77 +++++++++++++++++++++------------------------ 3 files changed, 67 insertions(+), 63 deletions(-) diff --git a/src/k_grandprix.cpp b/src/k_grandprix.cpp index 5196aac60..8c88de5fa 100644 --- a/src/k_grandprix.cpp +++ b/src/k_grandprix.cpp @@ -170,6 +170,8 @@ static boolean CompareRivals(player_t *a, player_t *b) void K_AssignFoes(void) { std::vector bots; + boolean addedplayer = false; + for (UINT8 i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] == false) @@ -179,16 +181,21 @@ void K_AssignFoes(void) if (!player->spectator && player->bot) { + addedplayer = true; bots.push_back(player); player->botvars.foe = false; } } + // Why the fuck is this blowing up sometimes + if (!addedplayer) + return; + std::stable_sort(bots.begin(), bots.end(), CompareRivals); UINT8 i = 0; for (auto &bot : bots) - { + { if (bot != NULL) bot->botvars.foe = true; @@ -425,13 +432,13 @@ void K_UpdateGrandPrixBots(void) players[i].spectator = K_BotDefaultSpectator(); } - K_AssignFoes(); - if (grandprixinfo.wonround == false) { return; } + K_AssignFoes(); + // Find the rival. for (i = 0; i < MAXPLAYERS; i++) { @@ -692,7 +699,7 @@ void K_IncreaseBotDifficulty(player_t *bot) // RELAXED MODE: // Continues don't drop bot difficulty, because we always advance. // Bots will still level up from standard advancement; we need a - // much steeper rank nudge to keep difficulty at the right level. + // much steeper rank nudge to keep difficulty at the right level. if (grandprixinfo.gamespeed == KARTSPEED_EASY) { switch(averageRank) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 9260996b1..60e32eed0 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -525,7 +525,7 @@ void K_LoadKartHUDGraphics(void) buffer[7] = '0'+((i) % 10); HU_UpdatePatch(&kp_overdrive[0][i], "%s", buffer); } - + sprintf(buffer, "bsOVRDxx"); for (i = 0; i < 32; i++) { @@ -2000,7 +2000,7 @@ static void K_drawKartItem(void) static void K_drawBackupItem(void) { bool tiny = r_splitscreen > 1; - patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; + patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; patch_t *localbg = (kp_itembg[2]); patch_t *localinv = kp_invincibility[((leveltime % (6*3)) / 3) + 7 + tiny]; INT32 fx = 0, fy = 0, fflags = 0, tx = 0, ty = 0; // final coords for hud and flags... @@ -3406,7 +3406,7 @@ static void K_drawKartDuelScores(void) { flags |= V_SNAPTOBOTTOM; flags &= ~V_SNAPTOTOP; - basey = BASEVIDHEIGHT - 40; + basey = BASEVIDHEIGHT - 40; } basex = BASEVIDWIDTH - 80; } @@ -3493,7 +3493,7 @@ static void K_drawKartDuelScores(void) V_DrawScaledPatch(basex-barheight+foeheight, basey, flags, kp_duel_4over); else V_DrawScaledPatch(basex, basey-barheight+foeheight, flags, kp_duel_over); - + if (!use4p) { V_DrawScaledPatch(basex, basey, flags, kp_duel_foe); @@ -3512,7 +3512,7 @@ static void K_drawKartDuelScores(void) } foenum.text("{}", foe->duelscore); - younum.text("{}", stplyr->duelscore); + younum.text("{}", stplyr->duelscore); // minirankings shamelessly copypasted because i know that shit works already // and SURELY we will never need to use this somewhere else, right? @@ -3526,7 +3526,7 @@ static void K_drawKartDuelScores(void) UINT8 drawme = draw ? (stplyr - players) : (foe - players); UINT16 drawx = basex + (draw ? youx : foex); UINT16 drawy = basey + (draw ? youy : foey); - + if (!playeringame[drawme] || players[drawme].spectator) continue; @@ -3790,8 +3790,8 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (R_GetViewNumber() == 1) { flags |= V_SNAPTOBOTTOM; - flags &= ~V_SNAPTOTOP; - basey = 170; + flags &= ~V_SNAPTOTOP; + basey = 170; } } } @@ -3817,7 +3817,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) return; using srb2::Draw; - srb2::Draw::Font scorefont = Draw::Font::kTimer; + srb2::Draw::Font scorefont = Draw::Font::kTimer; if (totalscore > 99) { @@ -3834,7 +3834,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) else { scorefont = Draw::Font::kZVote; - } + } } UINT32 youscore = stplyr->teamimportance; @@ -3870,7 +3870,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (teams_lastleveltime[vn] != leveltime) // Timing consistency { INT32 delta = abs(easedallyscore[vn] - allyscore); // how wrong is display score? - + if (scorechangecooldown[vn] == 0 && delta) { if (allyscore > easedallyscore[vn]) @@ -3886,9 +3886,9 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) enemycolor = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); } scorechangecooldown[vn] = TICRATE/delta; - } + } } - + if (!fromintermission) { // replace scores with eased scores @@ -4009,7 +4009,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (totalscore > 99) { enemynum.text("{:03}", enemyscore); - allynum.text("{:03}", allyscore); + allynum.text("{:03}", allyscore); } else { @@ -5346,8 +5346,10 @@ static void K_DrawCPUTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT32 flag K_DrawNameTagItemSpy(barx, bary, p, flags); } + UINT8 *foecol = R_GetTranslationColormap(TC_RAINBOW, static_cast(SKINCOLOR_RED), GTC_CACHE); + UINT8 blink = ((leveltime / 7) & 1); - V_DrawFixedPatch(x, y, FRACUNIT, flags, kp_cpu[blink], NULL); + V_DrawFixedPatch(x, y, FRACUNIT, flags, kp_cpu[blink], (p->botvars.foe) ? foecol : NULL); } static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT32 flags) @@ -5856,13 +5858,13 @@ INT32 K_GetMinimapSplitFlags(const boolean usingProgressBar) #define ICON_DOT_RADIUS (10) // modified pick from blondedradio/RadioRacers (but there are like 57 things we don't want in the commit) -// (so gogo gadget copypaste, thanks for a good feature and saving me work i was supposed to do anyway) +// (so gogo gadget copypaste, thanks for a good feature and saving me work i was supposed to do anyway) static void K_DrawKartUFOTimer(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags) { fixed_t amnumxpos, amnumypos; INT32 amxpos, amypos; - if (exitcountdown || leveltime > g_battleufo.due || battleprisons) + if (exitcountdown || leveltime > g_battleufo.due || battleprisons) return; tic_t raw = g_battleufo.due - leveltime; @@ -5872,7 +5874,7 @@ static void K_DrawKartUFOTimer(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hud { flags |= (raw / (TICRATE/2) % 2) ? V_YELLOWMAP : 0; } - + if (countdown <= 10) { flags &= ~(V_HUDTRANS|V_HUDTRANSHALF); diff --git a/src/k_kart.c b/src/k_kart.c index a5c95eab5..620721f41 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -135,7 +135,7 @@ boolean K_DuelItemAlwaysSpawns(mapthing_t *mt) boolean K_InRaceDuel(void) { return ( - inDuel && + inDuel && (gametyperules & GTR_CIRCUIT) && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE) && !specialstageinfo.valid && @@ -3776,7 +3776,7 @@ static void K_GetKartBoostPower(player_t *player) // This one's a little special: we add extra top speed per tic of ringboost stored up, to allow for Ring Box to really rocket away. // (We compensate when decrementing ringboost to avoid runaway exponential scaling hell.) fixed_t rb = FixedDiv(player->ringboost * FRACUNIT, max(FRACUNIT, K_RingDurationBoost(player))); - fixed_t rp = ((9 - player->kartspeed) + (9 - player->kartweight)) * ((3*FRACUNIT/20)/16); + fixed_t rp = ((9 - player->kartspeed) + (9 - player->kartweight)) * ((3*FRACUNIT/20)/16); ADDBOOST( ringboost_base + FixedMul(FRACUNIT / 1750, rb) + rp, 4*FRACUNIT, @@ -3804,7 +3804,7 @@ static void K_GetKartBoostPower(player_t *player) // Even when not inputting a turn, drift prediction is hard. // Turn solver will sometimes need to slightly turn to stay "aligned". // Award full boost even if turn solver creates a fractional miniturn. - const INT16 inner_deadzone = KART_FULLTURN / 100; + const INT16 inner_deadzone = KART_FULLTURN / 100; INT16 steer_threshold = FixedMul((FRACUNIT * player->kartweight) / 9, max_steer_threshold)>>FRACBITS; @@ -4310,8 +4310,8 @@ void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact) UINT16 scaledamps = min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10); // Debug print for scaledamps calculation // CONS_Printf("K_SpawnAmps: player=%s, amps=%d, kartspeed=%d, kartweight=%d, itemdistance=%d, itemdistmult=%0.2f, statscaledamps=%d, distscaledamps=%d\n", - // player_names[player-players], amps, player->kartspeed, player->kartweight, - // itemdistance, FixedToFloat(itemdistmult), + // player_names[player-players], amps, player->kartspeed, player->kartweight, + // itemdistance, FixedToFloat(itemdistmult), // min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10), // FixedMul(scaledamps<>FRACBITS); scaledamps = FixedMul(scaledamps<>FRACBITS; @@ -4370,8 +4370,8 @@ void K_AwardPlayerAmps(player_t *player, UINT8 amps) if (oldamps/AMPLEVEL != player->amps/AMPLEVEL) { UINT8 amplevel = player->amps / AMPLEVEL; - static sfxenum_t bwips[7] = {sfx_mbs4c, - sfx_mbs4d, sfx_mbs4e, sfx_mbs4f, sfx_mbs50, + static sfxenum_t bwips[7] = {sfx_mbs4c, + sfx_mbs4d, sfx_mbs4e, sfx_mbs4f, sfx_mbs50, sfx_mbs51, sfx_mbs52}; amplevel = min(amplevel, 6); @@ -4435,7 +4435,7 @@ void K_CheckpointCrossAward(player_t *player) //CONS_Printf("player: %s factor: %.2f exp: %d\n", player_names[player-players], FIXED_TO_FLOAT(player->gradingfactor), player->exp); if (!player->cangrabitems) player->cangrabitems = 1; - + K_AwardPlayerRings(player, (player->bot ? 20 : 10), true); // Update Duel scoring. @@ -6680,7 +6680,7 @@ void K_SpawnFireworkTrail(mobj_t *mo) if (mo->player) dust->color = mo->player->skincolor; - else + else dust->color = mo->color; dust->colorized = true; @@ -9366,7 +9366,7 @@ static void K_UpdateTripwire(player_t *player) else CONS_Printf("airtime: %d, twLen: %d, twAirLen: %d\n", player->airtime, player->tripwireLeniency, player->tripwireAirLeniency); */ - + if (boostExists) { // If player is MOSTLY on the ground. @@ -9557,13 +9557,13 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) for (doubler = 0; doubler < 2; doubler++) { fixed_t heightOffset = player->mo->height + (24*player->mo->scale); - if (P_IsObjectFlipped(player->mo)) + if (P_IsObjectFlipped(player->mo)) { // This counteracts the offset added by K_FlipFromObject so it looks seamless from non-flipped. heightOffset += player->mo->height - FixedMul(player->mo->scale, player->mo->height); heightOffset *= P_MobjFlip(player->mo); // Fleep. } - + mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, player->mo->z + P_GetMobjZMovement(player->mo) + heightOffset, MT_THOK); @@ -9675,7 +9675,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) sub = diff * (sub > 0 ? 1 : -1); player->mo->momz -= sub; } - + } else { @@ -10101,7 +10101,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->invincibilitytimer--; if (player->invincibilitytimer && K_PlayerScamPercentage(player, 1)) player->invincibilitytimer--; - + // Extra tripwire leniency for the end of invincibility if (player->invincibilitytimer <= 0) { player->tripwireLeniency = max( player->tripwireLeniency, TICRATE ); @@ -10112,7 +10112,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) { // freeze the stunned timer while baildrop is active // while retaining the value that was initially set - player->stunned++; + player->stunned++; mobj_t *pmo = player->mo; // particle spawn @@ -10182,7 +10182,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->amps > 0) K_DefensiveOverdrive(player); - P_StartQuakeFromMobj(7, 50 * player->mo->scale, 2048 * player->mo->scale, player->mo); + P_StartQuakeFromMobj(7, 50 * player->mo->scale, 2048 * player->mo->scale, player->mo); player->bailhitlag = false; } @@ -10232,6 +10232,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } S_StartSound(player->mo, sfx_kc33); + P_StartQuakeFromMobj(7, 50 * player->mo->scale, 2048 * player->mo->scale, player->mo); + player->bailquake = false; } // The precise ordering of start-of-level made me want to cut my head off, @@ -10976,13 +10978,6 @@ void K_KartResetPlayerColor(player_t *player) goto base; } - if (player->botvars.foe) - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_BLACK; - goto finalise; - } - if (player->eggmanexplode) // You're gonna diiiiie { const INT32 flashtime = 4<<(player->eggmanexplode/TICRATE); @@ -13743,7 +13738,7 @@ fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original) { INT16 myradius = FixedDiv(player->currentwaypoint->mobj->radius, mapobjectscale) / FRACUNIT; INT16 SMALL_WAYPOINT = 450; - + if (myradius < SMALL_WAYPOINT) errorfrict *= 2; } @@ -14052,7 +14047,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // ...which nullifies a lot of designed advantages for accel types and high-weight racers. // // In addition, it's at Gear 3 Thunderdome speed, which can make it hard for heavies to - // take strong lines without brakedrifting. + // take strong lines without brakedrifting. // // To try and help close this gap, we fudge Ring Box payouts to allow weaker characters // better access to things that make them go fast, without changing core handling. @@ -14072,7 +14067,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // Scale from base payout at 9/1 to max payout at 1/9. award = Easing_InCubic(FRACUNIT*total/maxtotal, 13*award/10, 18*award/10); - // And, because we don't have to give a damn about sandbagging, up the stakes the longer we progress! + // And, because we don't have to give a damn about sandbagging, up the stakes the longer we progress! if (gametyperules & GTR_CIRCUIT) { UINT8 maxgrade = 10; @@ -14212,7 +14207,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) else if (!(player->instaWhipCharge >= INSTAWHIP_CHARGETIME && P_PlayerInPain(player))) // Allow reversal whip player->instaWhipCharge = 0; } - + if (player->cmd.buttons & BT_BAIL && (player->cmd.buttons & BT_RESPAWNMASK) != BT_RESPAWNMASK) { if (leveltime < introtime || (gametyperules & GTR_SPHERES)) @@ -14846,7 +14841,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mobj_t *at1 = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_LIGHTNINGATTACK_VISUAL); mobj_t *at2 = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_LIGHTNINGATTACK_VISUAL); mobj_t *at3 = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_LIGHTNINGATTACK_VISUAL); - + P_SetMobjState(at1, S_THNG); P_SetMobjState(at2, S_THND); P_SetMobjState(at3, S_THNH); @@ -14857,7 +14852,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&at1->target, player->mo); P_SetTarget(&at2->target, player->mo); P_SetTarget(&at3->target, player->mo); - + S_StartSound(player->mo, LIGHTNING_SOUND); } break; @@ -14913,7 +14908,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->curshield != KSHIELD_BUBBLE) { mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BUBBLESHIELD); - // MT_BUBBLESHIELD doesn't have MF_NOBLOCKMAP so we need to remove this manually. + // MT_BUBBLESHIELD doesn't have MF_NOBLOCKMAP so we need to remove this manually. // Otherwise if you roll a bubble shield while flipped, the visuals look too mismatched. shield->eflags &= ~MFE_VERTICALFLIP; P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); @@ -15097,14 +15092,14 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) { S_StartSound(player->mo, sfx_gsha7); - if (P_IsObjectOnGround(player->mo)) // facing angle blends w/ momentum angle for game-feel + if (P_IsObjectOnGround(player->mo)) // facing angle blends w/ momentum angle for game-feel { - P_Thrust(player->mo, player->mo->angle, 25*player->mo->scale); - P_Thrust(player->mo, K_MomentumAngle(player->mo), 25*player->mo->scale); + P_Thrust(player->mo, player->mo->angle, 25*player->mo->scale); + P_Thrust(player->mo, K_MomentumAngle(player->mo), 25*player->mo->scale); } else // air version is momentum angle only, reduces cheese, is twice as strong to compensate { - P_Thrust(player->mo, K_MomentumAngle(player->mo), 50*player->mo->scale); + P_Thrust(player->mo, K_MomentumAngle(player->mo), 50*player->mo->scale); } UINT8 numsparks = 8; @@ -16373,7 +16368,7 @@ boolean K_PlayerCanUseItem(player_t *player) return (player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime); } -// === +// === // THE EXP ZONE // === @@ -16387,7 +16382,7 @@ static boolean K_IsValidOpponent(player_t *me, player_t *them) return false; if (G_SameTeam(me, them)) return false; - + return true; } @@ -16479,7 +16474,7 @@ UINT16 K_GetEXP(player_t *player) UINT16 exp = FixedRescale(player->gradingfactor, factormin, factormax, Easing_Linear, targetminexp, targetmaxexp)>>FRACBITS; - // CONS_Printf("Player %s numgradingpoints=%d gradingpoint=%d targetminexp=%d targetmaxexp=%d factor=%.2f factormin=%.2f factormax=%.2f exp=%d\n", + // CONS_Printf("Player %s numgradingpoints=%d gradingpoint=%d targetminexp=%d targetmaxexp=%d factor=%.2f factormin=%.2f factormax=%.2f exp=%d\n", // player_names[player - players], numgradingpoints, player->gradingpointnum, targetminexp, targetmaxexp, FIXED_TO_FLOAT(player->gradingfactor), FIXED_TO_FLOAT(factormin), FIXED_TO_FLOAT(factormax), exp); return exp; @@ -16493,7 +16488,7 @@ UINT32 K_GetNumGradingPoints(void) return numlaps * (1 + Obj_GetCheckpointCount()); } -// === +// === // END EXP ZONE // === @@ -16512,7 +16507,7 @@ boolean K_IsPickMeUpItem(mobjtype_t type) extern consvar_t cv_debugpickmeup; if (cv_debugpickmeup.value) return false; - + switch (type) { case MT_JAWZ: @@ -16691,7 +16686,7 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile) if (!K_PickUp(victim->player, inflictor)) return false; - K_AddHitLag(victim, 3, false); + K_AddHitLag(victim, 3, false); P_RemoveMobj(inflictor); return true; @@ -16718,7 +16713,7 @@ fixed_t K_TeamComebackMultiplier(player_t *player) else theirdistance += K_GetItemRouletteDistance(&players[i], players[i].itemRoulette.playing); } - + fixed_t multiplier = FixedDiv(ourdistance, theirdistance); multiplier = min(multiplier, 3*FRACUNIT); multiplier = max(multiplier, FRACUNIT); From 11f63ce79e2a9129806f34201df00d7156725300 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Mon, 4 Aug 2025 04:36:21 -0400 Subject: [PATCH 08/10] Rebase fixup --- src/k_kart.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 620721f41..eb251144f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10232,8 +10232,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } S_StartSound(player->mo, sfx_kc33); - P_StartQuakeFromMobj(7, 50 * player->mo->scale, 2048 * player->mo->scale, player->mo); - player->bailquake = false; } // The precise ordering of start-of-level made me want to cut my head off, From c2bab86d7009c49334f64501e40291a475d1be76 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 5 Aug 2025 19:06:46 -0400 Subject: [PATCH 09/10] Stable sort suicide store --- src/k_grandprix.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/k_grandprix.cpp b/src/k_grandprix.cpp index 8c88de5fa..041d092f8 100644 --- a/src/k_grandprix.cpp +++ b/src/k_grandprix.cpp @@ -163,8 +163,8 @@ static boolean CompareRivals(player_t *a, player_t *b) return (K_RivalScore(a) > K_RivalScore(b)); } - // They are equals, so just randomize - return (P_Random(PR_BOTS) & 1); + // Fuck it + return a > b; } void K_AssignFoes(void) @@ -749,8 +749,8 @@ static boolean CompareJoiners(player_t *a, player_t *b) return (a->spectatewait < b->spectatewait); } - // They are equals, so just randomize - return (P_Random(PR_BOTS) & 1); + // Fuck it + return a > b; } static boolean CompareReplacements(player_t *a, player_t *b) @@ -767,8 +767,8 @@ static boolean CompareReplacements(player_t *a, player_t *b) return (a->position < b->position); } - // They are equals, so just randomize - return (P_Random(PR_BOTS) & 1); + // Fuck it + return a > b; } /*-------------------------------------------------- From 77ee717952dafd0dab928807e9ca46ae07e1f3da Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 6 Aug 2025 16:38:54 -0400 Subject: [PATCH 10/10] WIP: End distance EXP scaling --- src/k_bot.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 251f0f3c8..d8da81725 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -819,7 +819,13 @@ fixed_t K_BotRubberband(const player_t *player) scaled_dist = FixedDiv(scaled_dist, mapobjectscale); } - constexpr UINT32 END_DIST = 2048 * 14; + UINT32 END_DIST = 2048 * 14; + + if (K_EffectiveGradingFactor(player) <= FRACUNIT) + { + END_DIST = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, END_DIST * 2, END_DIST); + } + if (scaled_dist < END_DIST) { // At the end of tracks, start slowing down. @@ -837,7 +843,7 @@ fixed_t K_BotRubberband(const player_t *player) fixed_t K_UpdateRubberband(player_t *player) { fixed_t dest = K_BotRubberband(player); - + fixed_t deflect = player->botvars.recentDeflection; if (deflect > BOTMAXDEFLECTION) deflect = BOTMAXDEFLECTION; @@ -2160,7 +2166,7 @@ void K_UpdateBotGameplayVars(player_t *player) UINT32 smo = BOTANGLESAMPLES - 1; player->botvars.recentDeflection = (smo * player->botvars.recentDeflection / BOTANGLESAMPLES) + (dangle / BOTANGLESAMPLES); - + player->botvars.lastAngle = mangle; const botcontroller_t *botController = K_GetBotController(player->mo);