From 47c2c875d570626c15a49d9b4ab4f99802f42f1f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 14 May 2020 02:18:32 -0400 Subject: [PATCH] Increase bot difficulty every match based on placement, implement rival tag Rival tag does not have an in-game indicator, but it is there & it gives the bot better odds --- src/d_clisrv.c | 4 ++ src/d_clisrv.h | 2 + src/d_player.h | 10 +-- src/g_game.c | 6 ++ src/k_grandprix.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++ src/k_grandprix.h | 2 + src/k_kart.c | 30 ++++++-- src/p_saveg.c | 4 ++ src/p_setup.c | 4 ++ src/p_user.c | 14 +++- 10 files changed, 236 insertions(+), 14 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5d7c0da83..6097d5ec2 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -650,6 +650,8 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->bot = players[i].bot; rsp->bot_difficulty = players[i].botvars.difficulty; + rsp->bot_diffincrease = players[i].botvars.diffincrease; + rsp->bot_rival = players[i].botvars.rival; rsp->bot_itemdelay = players[i].botvars.itemdelay; rsp->bot_itemconfirm = players[i].botvars.itemconfirm; rsp->bot_lastturn = players[i].botvars.lastturn; @@ -780,6 +782,8 @@ static void resynch_read_player(resynch_pak *rsp) players[i].bot = rsp->bot; players[i].botvars.difficulty = rsp->bot_difficulty; + players[i].botvars.diffincrease = rsp->bot_diffincrease; + players[i].botvars.rival = rsp->bot_rival; players[i].botvars.itemdelay = rsp->bot_itemdelay; players[i].botvars.itemconfirm = rsp->bot_itemconfirm; players[i].botvars.lastturn = rsp->bot_lastturn; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 027ad42dd..349951ff6 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -288,6 +288,8 @@ typedef struct boolean bot; UINT8 bot_difficulty; + UINT8 bot_diffincrease; + boolean bot_rival; tic_t bot_itemdelay; tic_t bot_itemconfirm; INT16 bot_lastturn; diff --git a/src/d_player.h b/src/d_player.h index d3af08ede..eb660213c 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -423,12 +423,14 @@ typedef enum // player_t struct for all bot variables typedef struct botvars_s { - UINT8 difficulty; + UINT8 difficulty; // Bot's difficulty setting + UINT8 diffincrease; // In GP: bot difficulty will increase this much next round + boolean rival; // If true, they're the GP rival - tic_t itemdelay; - tic_t itemconfirm; + tic_t itemdelay; // Delay before using item at all + tic_t itemconfirm; // When high enough, they will use their item - INT16 lastturn; + INT16 lastturn; // Last turn direction } botvars_t; // ======================================================================== diff --git a/src/g_game.c b/src/g_game.c index 3ea0c8f0f..2dd31a379 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2598,6 +2598,8 @@ void G_PlayerReborn(INT32 player) boolean spectator; boolean bot; UINT8 botdifficulty; + UINT8 botdiffincrease; + boolean botrival; SINT8 pity; // SRB2kart @@ -2652,6 +2654,8 @@ void G_PlayerReborn(INT32 player) mare = players[player].mare; bot = players[player].bot; botdifficulty = players[player].botvars.difficulty; + botdiffincrease = players[player].botvars.diffincrease; + botrival = players[player].botvars.rival; pity = players[player].pity; // SRB2kart @@ -2733,6 +2737,8 @@ void G_PlayerReborn(INT32 player) p->mare = mare; p->bot = bot; p->botvars.difficulty = botdifficulty; + p->botvars.diffincrease = botdiffincrease; + p->botvars.rival = botrival; p->pity = pity; // SRB2kart diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 33ea7926e..e82f34560 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -248,6 +248,179 @@ void K_InitGrandPrixBots(void) } } +static INT16 K_RivalScore(player_t *bot) +{ + const UINT16 difficulty = bot->botvars.difficulty; + const UINT16 score = bot->score; + const SINT8 roundsleft = grandprixinfo.cup->numlevels - grandprixinfo.roundnum; + + // In the early game, difficulty is more important for long-term challenge. + // When we're running low on matches left though, we need to focus more on score. + + return (difficulty * roundsleft) + (score * grandprixinfo.roundnum); +} + +void K_UpdateGrandPrixBots(void) +{ + player_t *oldrival = NULL; + player_t *newrival = NULL; + UINT16 newrivalscore = 0; + UINT8 i; + + // Find the rival. + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || !players[i].bot) + { + continue; + } + + if (players[i].botvars.diffincrease) + { + players[i].botvars.difficulty += players[i].botvars.diffincrease; + + if (players[i].botvars.difficulty > MAXBOTDIFFICULTY) + { + players[i].botvars.difficulty = MAXBOTDIFFICULTY; + } + + players[i].botvars.diffincrease = 0; + } + + if (players[i].botvars.rival) + { + if (oldrival == NULL) + { + // Record the old rival to compare with our calculated new rival + oldrival = &players[i]; + } + else + { + // Somehow 2 rivals were set?! + // Let's quietly fix our mess... + players[i].botvars.rival = false; + } + } + } + + // Find the bot with the best average of score & difficulty. + for (i = 0; i < MAXPLAYERS; i++) + { + UINT16 ns = 0; + + if (!playeringame[i] || players[i].spectator || !players[i].bot) + { + continue; + } + + ns = K_RivalScore(&players[i]); + + if (ns > newrivalscore) + { + newrival = &players[i]; + newrivalscore = ns; + } + } + + // Even if there's a new rival, we want to make sure that they're a better fit than the current one! + if (oldrival != newrival) + { + if (oldrival != NULL) + { + UINT16 os = K_RivalScore(oldrival); + + if (newrivalscore <= os + 100) + { + // Our current rival's just fine, thank you very much. + return; + } + + // Hand over your badge. + oldrival->botvars.rival = false; + } + + // Set our new rival! + newrival->botvars.rival = true; + CONS_Printf("Rival is now %s\n", player_names[newrival - players]); + } +} + +static UINT8 K_BotExpectedStanding(player_t *bot) +{ + UINT8 pos = 1; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (i == (bot - players)) + { + continue; + } + + if (playeringame[i] && !players[i].spectator) + { + if (players[i].bot) + { + if (players[i].botvars.difficulty > bot->botvars.difficulty) + { + pos++; + } + else if (players[i].score > bot->score) + { + pos++; + } + } + else + { + // Human player, always increment + pos++; + } + } + } + + return pos; +} + +void K_IncreaseBotDifficulty(player_t *bot) +{ + UINT8 expectedstanding; + INT16 standingdiff; + + if (bot->botvars.difficulty >= MAXBOTDIFFICULTY) + { + // Already at max difficulty, don't need to increase + return; + } + + // Increment bot difficulty based on what position you were meant to come in! + expectedstanding = K_BotExpectedStanding(bot); + standingdiff = expectedstanding - bot->kartstuff[k_position]; + + if (standingdiff >= -2) + { + UINT8 increase; + + if (standingdiff > 5) + { + increase = 3; + } + else if (standingdiff > 2) + { + increase = 2; + } + else + { + increase = 1; + } + + bot->botvars.diffincrease = increase; + } + else + { + bot->botvars.diffincrease = 0; + } +} + void K_FakeBotResults(player_t *bot) { const UINT32 distfactor = FixedMul(32 * bot->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; @@ -298,6 +471,7 @@ void K_FakeBotResults(player_t *bot) bot->exiting = 2; bot->realtime += (bot->distancetofinish / distfactor); bot->distancetofinish = 0; + K_IncreaseBotDifficulty(bot); } void K_PlayerLoseLife(player_t *player) diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 28895c2dc..7368b96f7 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -18,6 +18,8 @@ extern struct grandprixinfo UINT8 K_BotStartingDifficulty(SINT8 value); INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); void K_InitGrandPrixBots(void); +void K_UpdateGrandPrixBots(void); +void K_IncreaseBotDifficulty(player_t *bot); void K_FakeBotResults(player_t *bot); void K_PlayerLoseLife(player_t *player); diff --git a/src/k_kart.c b/src/k_kart.c index cf984252c..00dad9549 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -854,7 +854,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) \return void */ -static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot) +static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival) { INT32 newodds; INT32 i; @@ -932,10 +932,12 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp #define POWERITEMODDS(odds) {\ if (franticitems) \ - odds <<= 1; \ - odds = FixedMul(odds<> FRACBITS; \ + odds *= 2; \ + if (rival) \ + odds *= 2; \ + odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \ if (mashed > 0) \ - odds = FixedDiv(odds<> FRACBITS; \ + odds = FixedDiv(odds * FRACUNIT, FRACUNIT + mashed) / FRACUNIT; \ } #define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) @@ -1071,7 +1073,7 @@ static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 for (j = 1; j < NUMKARTRESULTS; j++) { - if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot) > 0) + if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)) > 0) { available = true; break; @@ -1233,7 +1235,9 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) pdis = FixedDiv(pdis * FRACUNIT, mapobjectscale) / FRACUNIT; if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items + { pdis = (15 * pdis) / 14; + } if (spbplace != -1 && player->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell { @@ -1241,6 +1245,12 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) spbrush = true; } + if (player->bot && player->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count // SPECIAL CASE No. 1: @@ -1369,7 +1379,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush); for (i = 1; i < NUMKARTRESULTS; i++) - spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot)); + spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot, (player->bot && player->botvars.rival))); // Award the player whatever power is rolled if (totalspawnchance > 0) @@ -11277,13 +11287,19 @@ static void K_drawDistributionDebugger(void) spbrush = true; } + if (stplyr->bot && stplyr->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); for (i = 1; i < NUMKARTRESULTS; i++) { - const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot); + const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)); if (itemodds <= 0) continue; diff --git a/src/p_saveg.c b/src/p_saveg.c index 6f8b5cb79..354a96b1c 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -271,6 +271,8 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); WRITEUINT8(save_p, players[i].botvars.difficulty); + WRITEUINT8(save_p, players[i].botvars.diffincrease); + WRITEUINT8(save_p, players[i].botvars.rival); WRITEUINT32(save_p, players[i].botvars.itemdelay); WRITEUINT32(save_p, players[i].botvars.itemconfirm); WRITEINT16(save_p, players[i].botvars.lastturn); @@ -448,6 +450,8 @@ static void P_NetUnArchivePlayers(void) players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); 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.itemdelay = READUINT32(save_p); players[i].botvars.itemconfirm = READUINT32(save_p); players[i].botvars.lastturn = READINT16(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 5228f7ed8..b2d14b456 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3375,6 +3375,10 @@ boolean P_SetupLevel(boolean skipprecip) K_InitGrandPrixBots(); grandprixinfo.initalize = false; } + else + { + K_UpdateGrandPrixBots(); + } } else if (!modeattacking) { diff --git a/src/p_user.c b/src/p_user.c index 34fa8642f..733df81b1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1735,10 +1735,18 @@ void P_DoPlayerExit(player_t *player) else player->exiting = raceexittime+2; // Accidental death safeguard??? - if (grandprixinfo.roundnum > 0 && !losing && !player->bot) + if (grandprixinfo.roundnum > 0) { - // YOU WIN - grandprixinfo.wonround = true; + if (player->bot) + { + // Bots are going to get harder... :) + K_IncreaseBotDifficulty(player); + } + else if (!losing) + { + // YOU WIN + grandprixinfo.wonround = true; + } } player->powers[pw_underwater] = 0;