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; };