mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-04-05 09:46:40 +00:00
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
This commit is contained in:
parent
6c8d67ab86
commit
47c2c875d5
10 changed files with 236 additions and 14 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
// ========================================================================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
30
src/k_kart.c
30
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, FRACUNIT + ((PLAYERSCALING << FRACBITS) / 25)) >> FRACBITS; \
|
||||
odds *= 2; \
|
||||
if (rival) \
|
||||
odds *= 2; \
|
||||
odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \
|
||||
if (mashed > 0) \
|
||||
odds = FixedDiv(odds<<FRACBITS, FRACUNIT + mashed) >> 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -3375,6 +3375,10 @@ boolean P_SetupLevel(boolean skipprecip)
|
|||
K_InitGrandPrixBots();
|
||||
grandprixinfo.initalize = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
K_UpdateGrandPrixBots();
|
||||
}
|
||||
}
|
||||
else if (!modeattacking)
|
||||
{
|
||||
|
|
|
|||
14
src/p_user.c
14
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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue