Merge branch 'bot-minor' into 'master'

K_RetireBots revamp (resolves #1498, #1535, #1615, #1616)

Closes #1498, #1535, #1615, and #1616

See merge request kart-krew-dev/ring-racers-internal!2716
This commit is contained in:
Oni VelocitOni 2025-08-03 21:53:07 +00:00
commit f0bc1071f9
5 changed files with 130 additions and 94 deletions

View file

@ -4893,7 +4893,7 @@ static void G_DoCompleted(void)
} }
else else
{ {
Y_MidIntermission(); //Y_MidIntermission(); -- we don't want bots retired or teams wiped
if (grandprixinfo.gp == true) if (grandprixinfo.gp == true)
{ {
K_UpdateGPRank(&grandprixinfo.rank); K_UpdateGPRank(&grandprixinfo.rank);

View file

@ -718,99 +718,29 @@ static boolean CompareReplacements(player_t *a, player_t *b)
--------------------------------------------------*/ --------------------------------------------------*/
void K_RetireBots(void) void K_RetireBots(void)
{ {
const UINT8 defaultbotskin = R_BotDefaultSkin();
SINT8 newDifficulty;
UINT8 usableskins, skincount = (demo.playback ? demo.numskins : numskins);
UINT8 grabskins[MAXSKINS+1];
UINT8 i; UINT8 i;
if (grandprixinfo.gp == true if (grandprixinfo.gp == true
&& (grandprixinfo.eventmode != GPEVENT_NONE && grandprixinfo.eventmode != GPEVENT_NONE)
|| (grandprixinfo.cup != NULL
&& roundqueue.size > 0
&& roundqueue.roundnum >= grandprixinfo.cup->numlevels)))
{ {
// No replacement. // Bots can't do anything here, replacement means they never get their spotlight.
return; return;
} }
// Init usable bot skins list if (roundqueue.size > 0)
for (usableskins = 0; usableskins < skincount; usableskins++)
{ {
grabskins[usableskins] = usableskins; // Get the last non-special entry
} i = roundqueue.size;
grabskins[usableskins] = MAXSKINS; while (--i > roundqueue.position)
// Exclude player skins
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
grabskins[players[i].skin] = MAXSKINS;
}
// Rearrange usable bot skins list to prevent gaps for randomised selection
for (i = 0; i < usableskins; i++)
{
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
continue;
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
usableskins--;
grabskins[i] = grabskins[usableskins];
grabskins[usableskins] = MAXSKINS;
}
if (grandprixinfo.gp) // Sure, let's let this happen all the time :)
{
if (grandprixinfo.masterbots == true)
{ {
newDifficulty = MAXBOTDIFFICULTY; if (!roundqueue.entries[i].rankrestricted)
} break;
else
{
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
newDifficulty = startingdifficulty - 4;
if (roundqueue.size > 0)
{
newDifficulty += roundqueue.roundnum;
}
}
}
else
{
newDifficulty = cv_kartbot.value;
}
if (newDifficulty > MAXBOTDIFFICULTY)
{
newDifficulty = MAXBOTDIFFICULTY;
}
else if (newDifficulty < 1)
{
newDifficulty = 1;
}
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *bot = NULL;
if (!playeringame[i] || !players[i].bot || players[i].spectator)
{
continue;
} }
bot = &players[i]; if (roundqueue.position >= i)
if (bot->pflags & PF_NOCONTEST)
{ {
// HACK!!!!! two days to end of cleanup period :) // Out of non-special entries, no replacement.
// we do this so that any bot that's been removed doesn't count for K_SetNameForBot conflicts return;
player_names[i][0] = '0';
} }
} }
@ -861,8 +791,6 @@ void K_RetireBots(void)
std::stable_sort(humans.begin(), humans.end(), CompareReplacements); std::stable_sort(humans.begin(), humans.end(), CompareReplacements);
std::stable_sort(bots.begin(), bots.end(), CompareReplacements); std::stable_sort(bots.begin(), bots.end(), CompareReplacements);
boolean did_replacement = false;
if (G_GametypeHasSpectators() == true && grandprixinfo.gp == false && cv_shuffleloser.value != 0) if (G_GametypeHasSpectators() == true && grandprixinfo.gp == false && cv_shuffleloser.value != 0)
{ {
// While joiners and players still exist, insert joiners. // While joiners and players still exist, insert joiners.
@ -911,24 +839,113 @@ void K_RetireBots(void)
// Add our waiting-to-play spectator to the game. // Add our waiting-to-play spectator to the game.
P_SpectatorJoinGame(joiner); P_SpectatorJoinGame(joiner);
did_replacement = true;
replacements--; replacements--;
num_joining--; num_joining--;
} }
} }
if (did_replacement == true) // No need to run any of this code if no bots afoot
if (bots.size() == 0)
{ {
// No need to run the bot swapping code,
// we already replaced the loser.
return; return;
} }
// Replace last place bot. // Remove all still-in-contestors
if (bots.size() > 0) bots.erase(std::remove_if(bots.begin(), bots.end(),
{ [](player_t *bot) {
player_t *bot = bots.back(); return (
bot->spectator
|| !(bot->pflags & PF_NOCONTEST)
);
}
), bots.end());
// No need to run any of this code if no *NO-CONTESTORS* afoot
if (bots.size() == 0)
{
return;
}
// Okay, now this is essentially the original contents of K_RetireBots with cpp swag
const UINT8 defaultbotskin = R_BotDefaultSkin();
SINT8 newDifficulty;
UINT8 usableskins, skincount = (demo.playback ? demo.numskins : numskins);
UINT8 grabskins[MAXSKINS+1];
// Handle adjusting difficulty for new bots
{
if (grandprixinfo.gp) // Sure, let's let this happen all the time :)
{
if (grandprixinfo.masterbots == true)
{
newDifficulty = MAXBOTDIFFICULTY;
}
else
{
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
newDifficulty = startingdifficulty - 4;
if (roundqueue.size > 0)
{
newDifficulty += roundqueue.roundnum;
}
}
}
else
{
newDifficulty = cv_kartbot.value;
}
if (newDifficulty > MAXBOTDIFFICULTY)
{
newDifficulty = MAXBOTDIFFICULTY;
}
else if (newDifficulty < 1)
{
newDifficulty = 1;
}
}
// HACK!!!!! that survived allll the way since two days to end of cleanup period :)
// we do this so that any bot that's been removed doesn't count for K_SetNameForBot conflicts
for (player_t *bot : bots)
{
player_names[(bot-players)][0] = '0';
}
// Init usable bot skins list
for (usableskins = 0; usableskins < skincount; usableskins++)
{
grabskins[usableskins] = usableskins;
}
grabskins[usableskins] = MAXSKINS;
// Exclude player skins
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
grabskins[players[i].skin] = MAXSKINS;
}
// Rearrange usable bot skins list to prevent gaps for randomised selection
for (i = 0; i < usableskins; i++)
{
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
continue;
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
usableskins--;
grabskins[i] = grabskins[usableskins];
grabskins[usableskins] = MAXSKINS;
}
// Replace nocontested bots.
for (player_t *bot : bots)
{
UINT8 skinnum = defaultbotskin; UINT8 skinnum = defaultbotskin;
if (usableskins > 0) if (usableskins > 0)

View file

@ -5434,7 +5434,13 @@ playertagtype_t K_WhichPlayerTag(player_t *p)
} }
else if (p->bot) else if (p->bot)
{ {
if ((p->botvars.rival == true || cv_levelskull.value) && (!K_InRaceDuel())) if (gametype == GT_TUTORIAL)
{
return (skins[p->skin].flags & SF_MACHINE)
? PLAYERTAG_CPU
: PLAYERTAG_TUTORIALFAKELOCAL;
}
else if ((p->botvars.rival == true || cv_levelskull.value) && (!K_InRaceDuel()))
{ {
return PLAYERTAG_RIVAL; return PLAYERTAG_RIVAL;
} }
@ -5470,6 +5476,15 @@ void K_DrawPlayerTag(fixed_t x, fixed_t y, player_t *p, playertagtype_t type, bo
K_DrawRivalTagForPlayer(x, y, p, flags); K_DrawRivalTagForPlayer(x, y, p, flags);
break; break;
case PLAYERTAG_TUTORIALFAKELOCAL:
if (p-players < 4)
{
flags |= V_SPLITSCREEN;
K_DrawLocalTagForPlayer(x, y, p, (p - players), flags);
break;
}
// FALLTHRU
case PLAYERTAG_CPU: case PLAYERTAG_CPU:
flags |= V_SPLITSCREEN; flags |= V_SPLITSCREEN;
flags |= foreground ? 0 : V_60TRANS; flags |= foreground ? 0 : V_60TRANS;

View file

@ -153,6 +153,7 @@ typedef enum
PLAYERTAG_CPU, PLAYERTAG_CPU,
PLAYERTAG_RIVAL, PLAYERTAG_RIVAL,
PLAYERTAG_NAME, PLAYERTAG_NAME,
PLAYERTAG_TUTORIALFAKELOCAL,
} }
playertagtype_t; playertagtype_t;

View file

@ -735,7 +735,10 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
V_DrawMappedPatch(x, y, 0, resbar, NULL); V_DrawMappedPatch(x, y, 0, resbar, NULL);
V_DrawRightAlignedThinString(x+13, y-2, 0, va("%d", standings->pos[i])); if (gametype != GT_TUTORIAL)
{
V_DrawRightAlignedThinString(x+13, y-2, 0, va("%d", standings->pos[i]));
}
//if (players[pnum].skincolor != SKINCOLOR_NONE) //if (players[pnum].skincolor != SKINCOLOR_NONE)
{ {