Rework bot skin randomisation

- Instead of picking over numskins and adjust the number if it picks an already used skin, build and shuffle a list of allowed skins and pick over that instead.
- Fixes some issues with increased weighting towards the skins immediately after your rivals' skins.
- Use PR_RULESCRAMBLE instead of PR_UNDEFINED.
- Why am I doing it in this branch? I'm getting re-acquainted with how bots are handled between matches in advance of disabling them for bonus events without losing or corrupting data.
This commit is contained in:
toaster 2022-10-11 18:42:04 +01:00
parent dbc067f87a
commit 13c8d45764
3 changed files with 61 additions and 91 deletions

View file

@ -96,14 +96,14 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers)
} }
/*-------------------------------------------------- /*--------------------------------------------------
SINT8 K_BotDefaultSkin(void) UINT8 K_BotDefaultSkin(void)
See header file for description. See header file for description.
--------------------------------------------------*/ --------------------------------------------------*/
SINT8 K_BotDefaultSkin(void) UINT8 K_BotDefaultSkin(void)
{ {
const char *defaultbotskinname = "eggrobo"; const char *defaultbotskinname = "eggrobo";
SINT8 defaultbotskin = R_SkinAvailable(defaultbotskinname); INT32 defaultbotskin = R_SkinAvailable(defaultbotskinname);
if (defaultbotskin == -1) if (defaultbotskin == -1)
{ {
@ -111,7 +111,7 @@ SINT8 K_BotDefaultSkin(void)
defaultbotskin = 0; defaultbotskin = 0;
} }
return defaultbotskin; return (UINT8)defaultbotskin;
} }
/*-------------------------------------------------- /*--------------------------------------------------
@ -121,7 +121,7 @@ SINT8 K_BotDefaultSkin(void)
--------------------------------------------------*/ --------------------------------------------------*/
void K_InitGrandPrixBots(void) void K_InitGrandPrixBots(void)
{ {
const SINT8 defaultbotskin = K_BotDefaultSkin(); const UINT8 defaultbotskin = K_BotDefaultSkin();
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed); const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
UINT8 difficultylevels[MAXPLAYERS]; UINT8 difficultylevels[MAXPLAYERS];
@ -132,7 +132,9 @@ void K_InitGrandPrixBots(void)
UINT8 numplayers = 0; UINT8 numplayers = 0;
UINT8 competitors[MAXSPLITSCREENPLAYERS]; UINT8 competitors[MAXSPLITSCREENPLAYERS];
boolean skinusable[MAXSKINS]; UINT8 usableskins;
UINT8 grabskins[MAXSKINS];
UINT8 botskinlist[MAXPLAYERS]; UINT8 botskinlist[MAXPLAYERS];
UINT8 botskinlistpos = 0; UINT8 botskinlistpos = 0;
@ -142,17 +144,10 @@ void K_InitGrandPrixBots(void)
memset(competitors, MAXPLAYERS, sizeof (competitors)); memset(competitors, MAXPLAYERS, sizeof (competitors));
memset(botskinlist, defaultbotskin, sizeof (botskinlist)); memset(botskinlist, defaultbotskin, sizeof (botskinlist));
// init usable bot skins list // Init usable bot skins list
for (i = 0; i < MAXSKINS; i++) for (usableskins = 0; usableskins < numskins; usableskins++)
{ {
if (i < numskins) grabskins[usableskins] = usableskins;
{
skinusable[i] = true;
}
else
{
skinusable[i] = false;
}
} }
#if MAXPLAYERS != 16 #if MAXPLAYERS != 16
@ -192,7 +187,7 @@ void K_InitGrandPrixBots(void)
if (numplayers < MAXSPLITSCREENPLAYERS && !players[i].spectator) if (numplayers < MAXSPLITSCREENPLAYERS && !players[i].spectator)
{ {
competitors[numplayers] = i; competitors[numplayers] = i;
skinusable[players[i].skin] = false; grabskins[players[i].skin] = MAXSKINS;
numplayers++; numplayers++;
} }
else else
@ -219,52 +214,42 @@ void K_InitGrandPrixBots(void)
{ {
player_t *p = &players[competitors[j]]; player_t *p = &players[competitors[j]];
char *rivalname = skins[p->skin].rivals[i]; char *rivalname = skins[p->skin].rivals[i];
SINT8 rivalnum = R_SkinAvailable(rivalname); INT32 rivalnum = R_SkinAvailable(rivalname);
if (rivalnum != -1 && skinusable[rivalnum]) // Intentionally referenced before (currently dummied out) unlock check. Such a tease!
if (rivalnum != -1 && grabskins[(UINT8)rivalnum] != MAXSKINS)
{ {
botskinlist[botskinlistpos] = rivalnum; botskinlist[botskinlistpos++] = (UINT8)rivalnum;
skinusable[rivalnum] = false; grabskins[(UINT8)rivalnum] = MAXSKINS;
botskinlistpos++; botskinlistpos++;
} }
} }
} }
} }
// Rearrange usable bot skins list to prevent gaps for randomised selection
for (i = 0; i < usableskins; i++)
{
if (grabskins[i] != MAXSKINS /*&& !K_SkinLocked(usableskins)*/)
continue;
grabskins[i] = grabskins[--usableskins];
}
// Pad the remaining list with random skins if we need to // Pad the remaining list with random skins if we need to
if (botskinlistpos < wantedbots) if (botskinlistpos < wantedbots)
{ {
for (i = botskinlistpos; i < wantedbots; i++) while (botskinlistpos < wantedbots)
{ {
UINT8 val = M_RandomKey(numskins); UINT8 skinnum = defaultbotskin;
UINT8 loops = 0;
while (!skinusable[val]) if (usableskins > 0)
{ {
if (loops >= numskins) UINT8 index = M_RandomKey(usableskins);
{ skinnum = grabskins[index];
// no more skins grabskins[index] = grabskins[--usableskins];
break;
}
val++;
if (val >= numskins)
{
val = 0;
}
loops++;
} }
if (loops >= numskins) botskinlist[botskinlistpos++] = skinnum;
{
// leave the rest of the table as the default skin
break;
}
botskinlist[i] = val;
skinusable[val] = false;
} }
} }
@ -519,10 +504,11 @@ void K_IncreaseBotDifficulty(player_t *bot)
--------------------------------------------------*/ --------------------------------------------------*/
void K_RetireBots(void) void K_RetireBots(void)
{ {
const SINT8 defaultbotskin = K_BotDefaultSkin(); const UINT8 defaultbotskin = K_BotDefaultSkin();
SINT8 newDifficulty; SINT8 newDifficulty;
boolean skinusable[MAXSKINS]; UINT8 usableskins;
UINT8 grabskins[MAXSKINS];
UINT8 i; UINT8 i;
@ -532,25 +518,29 @@ void K_RetireBots(void)
return; return;
} }
// init usable bot skins list // Init usable bot skins list
for (i = 0; i < MAXSKINS; i++) for (usableskins = 0; usableskins < numskins; usableskins++)
{ {
if (i < numskins) grabskins[usableskins] = usableskins;
{
skinusable[i] = true;
}
else
{
skinusable[i] = false;
}
} }
// Exclude player skins
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && !players[i].spectator) if (!playeringame[i])
{ continue;
skinusable[players[i].skin] = false; 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 /*&& !K_SkinLocked(usableskins)*/)
continue;
grabskins[i] = grabskins[--usableskins];
} }
if (!grandprixinfo.gp) // Sure, let's let this happen all the time :) if (!grandprixinfo.gp) // Sure, let's let this happen all the time :)
@ -590,35 +580,15 @@ void K_RetireBots(void)
if (bot->pflags & PF_NOCONTEST) if (bot->pflags & PF_NOCONTEST)
{ {
UINT8 skinnum = P_RandomKey(PR_UNDEFINED, numskins); UINT8 skinnum = defaultbotskin;
UINT8 loops = 0;
while (!skinusable[skinnum]) if (usableskins > 0)
{ {
if (loops >= numskins) UINT8 index = P_RandomKey(PR_RULESCRAMBLE, usableskins);
{ skinnum = grabskins[index];
// no more skins grabskins[index] = grabskins[--usableskins];
break;
}
skinnum++;
if (skinnum >= numskins)
{
skinnum = 0;
}
loops++;
} }
if (loops >= numskins)
{
// Use default skin
skinnum = defaultbotskin;
}
skinusable[skinnum] = false;
bot->botvars.difficulty = newDifficulty; bot->botvars.difficulty = newDifficulty;
bot->botvars.diffincrease = 0; bot->botvars.diffincrease = 0;

View file

@ -63,13 +63,13 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers);
/*-------------------------------------------------- /*--------------------------------------------------
SINT8 K_BotDefaultSkin(void); UINT8 K_BotDefaultSkin(void);
Returns the skin number of the skin the game Returns the skin number of the skin the game
uses as a fallback option. uses as a fallback option.
--------------------------------------------------*/ --------------------------------------------------*/
SINT8 K_BotDefaultSkin(void); UINT8 K_BotDefaultSkin(void);
/*-------------------------------------------------- /*--------------------------------------------------

View file

@ -47,7 +47,7 @@ typedef enum
PR_PLAYERSTARTS, // Player starts PR_PLAYERSTARTS, // Player starts
PR_VOICES, // Player voice sounds PR_VOICES, // Player voice sounds
PR_RULESCRAMBLE, // Netgame rule scrambing events PR_RULESCRAMBLE, // Rule scrambing events
PR_ITEM_ROULETTE, // Item results PR_ITEM_ROULETTE, // Item results
PR_ITEM_RINGS, // Flung ring/bumper/player (on death) PR_ITEM_RINGS, // Flung ring/bumper/player (on death)