Rework how Bots handle their skin availabilities

Before we can add extra unlock features, we need to make sure we're not building on a house of sand.
- R_SkinUsable: Use Net Unlock system if playernum is -1
- R_BotDefaultSkin: Move to r_skins.c, cache skin search
- R_GetSkinAvailabilities: Use Net Unlock when called for bots (and always permit R_BotDefaultSkin)
- Got_AddBot: Call R_GetSkinAvailabilities for summoned bots to guarantee sync status of available skins
- K_UpdateMatchRaceBots: Tidy up to match grand prix bot skin selection system, hiding server-locked skins and defaulting to R_BotDefaultSkin if you don't have enough unlocked for the remaining player slots
This commit is contained in:
toaster 2023-03-05 18:06:09 +00:00
parent 1ae5df651d
commit 0c75c40060
7 changed files with 70 additions and 87 deletions

View file

@ -824,7 +824,7 @@ static boolean CL_SendJoin(void)
for (; i < MAXSPLITSCREENPLAYERS; i++) for (; i < MAXSPLITSCREENPLAYERS; i++)
strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME); strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME);
memcpy(&netbuffer->u.clientcfg.availabilities, R_GetSkinAvailabilities(false), MAXAVAILABILITY*sizeof(UINT8)); memcpy(&netbuffer->u.clientcfg.availabilities, R_GetSkinAvailabilities(false, false), MAXAVAILABILITY*sizeof(UINT8));
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak)); return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
} }
@ -3723,7 +3723,7 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
static void Got_AddBot(UINT8 **p, INT32 playernum) static void Got_AddBot(UINT8 **p, INT32 playernum)
{ {
INT16 newplayernum; INT16 newplayernum;
UINT8 i, skinnum = 0; UINT8 skinnum = 0;
UINT8 difficulty = DIFFICULTBOT; UINT8 difficulty = DIFFICULTBOT;
if (playernum != serverplayer && !IsPlayerAdmin(playernum)) if (playernum != serverplayer && !IsPlayerAdmin(playernum))
@ -3753,14 +3753,8 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
playernode[newplayernum] = servernode; playernode[newplayernum] = servernode;
// todo find a way to have all auto unlocked for dedicated // this will permit unlocks
if (playeringame[0]) memcpy(&players[newplayernum].availabilities, R_GetSkinAvailabilities(false, true), MAXAVAILABILITY*sizeof(UINT8));
{
for (i = 0; i < MAXAVAILABILITY; i++)
{
players[newplayernum].availabilities[i] = players[0].availabilities[i];
}
}
players[newplayernum].splitscreenindex = 0; players[newplayernum].splitscreenindex = 0;
players[newplayernum].bot = true; players[newplayernum].bot = true;
@ -3936,7 +3930,7 @@ boolean SV_SpawnServer(void)
// strictly speaking, i'm not convinced the following is necessary // strictly speaking, i'm not convinced the following is necessary
// but I'm not confident enough to remove it entirely in case it breaks something // but I'm not confident enough to remove it entirely in case it breaks something
{ {
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false); UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false, false);
SINT8 node = 0; SINT8 node = 0;
for (; node < MAXNETNODES; node++) for (; node < MAXNETNODES; node++)
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring); result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring);

View file

@ -2239,7 +2239,7 @@ static void G_SaveDemoSkins(UINT8 **pp)
{ {
char skin[16]; char skin[16];
UINT8 i; UINT8 i;
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true); UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true, false);
WRITEUINT8((*pp), numskins); WRITEUINT8((*pp), numskins);
for (i = 0; i < numskins; i++) for (i = 0; i < numskins; i++)

View file

@ -108,13 +108,15 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p)
--------------------------------------------------*/ --------------------------------------------------*/
void K_UpdateMatchRaceBots(void) void K_UpdateMatchRaceBots(void)
{ {
const UINT8 defaultbotskin = R_BotDefaultSkin();
const UINT8 difficulty = cv_kartbot.value; const UINT8 difficulty = cv_kartbot.value;
UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value); UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value);
UINT8 numplayers = 0; UINT8 numplayers = 0;
UINT8 numbots = 0; UINT8 numbots = 0;
UINT8 numwaiting = 0; UINT8 numwaiting = 0;
SINT8 wantedbots = 0; SINT8 wantedbots = 0;
boolean skinusable[MAXSKINS]; UINT8 usableskins = 0;
UINT8 grabskins[MAXSKINS+1];
UINT8 i; UINT8 i;
if (!server) if (!server)
@ -122,18 +124,12 @@ void K_UpdateMatchRaceBots(void)
return; return;
} }
// init usable bot skins list // Init usable bot skins list
for (i = 0; i < MAXSKINS; i++) for (i = 0; i < numskins; i++)
{ {
if (i < numskins) grabskins[usableskins++] = i;
{
skinusable[i] = true;
}
else
{
skinusable[i] = false;
}
} }
grabskins[usableskins] = MAXSKINS;
if (cv_maxplayers.value > 0) if (cv_maxplayers.value > 0)
{ {
@ -146,7 +142,7 @@ void K_UpdateMatchRaceBots(void)
{ {
if (!players[i].spectator) if (!players[i].spectator)
{ {
skinusable[players[i].skin] = false; grabskins[players[i].skin] = MAXSKINS;
if (players[i].bot) if (players[i].bot)
{ {
@ -185,48 +181,42 @@ void K_UpdateMatchRaceBots(void)
{ {
// We require MORE bots! // We require MORE bots!
UINT8 newplayernum = 0; UINT8 newplayernum = 0;
boolean usedallskins = false;
if (dedicated) if (dedicated)
{ {
newplayernum = 1; newplayernum = 1;
} }
// 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;
}
while (numbots < wantedbots) while (numbots < wantedbots)
{ {
UINT8 skin = M_RandomKey(numskins); UINT8 skinnum = defaultbotskin;
if (usedallskins == false) if (usableskins > 0)
{ {
UINT8 loops = 0; UINT8 index = M_RandomKey(usableskins);
skinnum = grabskins[index];
while (!skinusable[skin]) grabskins[index] = grabskins[--usableskins];
{
if (loops >= numskins)
{
// no more skins, stick to our first choice
usedallskins = true;
break;
}
skin++;
if (skin >= numskins)
{
skin = 0;
}
loops++;
}
} }
if (!K_AddBot(skin, difficulty, &newplayernum)) if (!K_AddBot(skinnum, difficulty, &newplayernum))
{ {
// Not enough player slots to add the bot, break the loop. // Not enough player slots to add the bot, break the loop.
break; break;
} }
skinusable[skin] = false;
numbots++; numbots++;
} }
} }

View file

@ -95,25 +95,6 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers)
return points; return points;
} }
/*--------------------------------------------------
UINT8 K_BotDefaultSkin(void)
See header file for description.
--------------------------------------------------*/
UINT8 K_BotDefaultSkin(void)
{
const char *defaultbotskinname = "eggrobo";
INT32 defaultbotskin = R_SkinAvailable(defaultbotskinname);
if (defaultbotskin == -1)
{
// This shouldn't happen, but just in case
defaultbotskin = 0;
}
return (UINT8)defaultbotskin;
}
/*-------------------------------------------------- /*--------------------------------------------------
void K_InitGrandPrixBots(void) void K_InitGrandPrixBots(void)
@ -121,7 +102,7 @@ UINT8 K_BotDefaultSkin(void)
--------------------------------------------------*/ --------------------------------------------------*/
void K_InitGrandPrixBots(void) void K_InitGrandPrixBots(void)
{ {
const UINT8 defaultbotskin = K_BotDefaultSkin(); const UINT8 defaultbotskin = R_BotDefaultSkin();
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed); const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
UINT8 difficultylevels[MAXPLAYERS]; UINT8 difficultylevels[MAXPLAYERS];
@ -519,7 +500,7 @@ void K_IncreaseBotDifficulty(player_t *bot)
--------------------------------------------------*/ --------------------------------------------------*/
void K_RetireBots(void) void K_RetireBots(void)
{ {
const UINT8 defaultbotskin = K_BotDefaultSkin(); const UINT8 defaultbotskin = R_BotDefaultSkin();
SINT8 newDifficulty; SINT8 newDifficulty;
UINT8 usableskins; UINT8 usableskins;

View file

@ -71,16 +71,6 @@ UINT8 K_BotStartingDifficulty(SINT8 value);
INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers);
/*--------------------------------------------------
UINT8 K_BotDefaultSkin(void);
Returns the skin number of the skin the game
uses as a fallback option.
--------------------------------------------------*/
UINT8 K_BotDefaultSkin(void);
/*-------------------------------------------------- /*--------------------------------------------------
void K_InitGrandPrixBots(void); void K_InitGrandPrixBots(void);

View file

@ -129,6 +129,27 @@ static void Sk_SetDefaultValue(skin_t *skin)
skin->soundsid[S_sfx[i].skinsound] = i; skin->soundsid[S_sfx[i].skinsound] = i;
} }
// Grab the default skin
UINT8 R_BotDefaultSkin(void)
{
static INT32 defaultbotskin = -1;
if (defaultbotskin == -1)
{
const char *defaultbotskinname = "eggrobo";
defaultbotskin = R_SkinAvailable(defaultbotskinname);
if (defaultbotskin == -1)
{
// This shouldn't happen, but just in case
defaultbotskin = 0;
}
}
return (UINT8)defaultbotskin;
}
// //
// Initialize the basic skins // Initialize the basic skins
// //
@ -159,11 +180,12 @@ void R_InitSkins(void)
M_UpdateConditionSetsPending(); M_UpdateConditionSetsPending();
} }
UINT8 *R_GetSkinAvailabilities(boolean demolock) UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots)
{ {
UINT8 i, shif, byte; UINT8 i, shif, byte;
INT32 skinid; INT32 skinid;
static UINT8 responsebuffer[MAXAVAILABILITY]; static UINT8 responsebuffer[MAXAVAILABILITY];
UINT8 defaultbotskin = R_BotDefaultSkin();
memset(&responsebuffer, 0, sizeof(responsebuffer)); memset(&responsebuffer, 0, sizeof(responsebuffer));
@ -172,15 +194,17 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock)
if (unlockables[i].type != SECRET_SKIN) if (unlockables[i].type != SECRET_SKIN)
continue; continue;
// NEVER EVER EVER M_CheckNetUnlockByID
if (gamedata->unlocked[i] != true && !demolock)
continue;
skinid = M_UnlockableSkinNum(&unlockables[i]); skinid = M_UnlockableSkinNum(&unlockables[i]);
if (skinid < 0 || skinid >= MAXSKINS) if (skinid < 0 || skinid >= MAXSKINS)
continue; continue;
if ((forbots
? (M_CheckNetUnlockByID(i) || skinid == defaultbotskin) // Assert the host's lock.
: gamedata->unlocked[i]) // Assert the local lock.
!= true && !demolock)
continue;
shif = (skinid % 8); shif = (skinid % 8);
byte = (skinid / 8); byte = (skinid / 8);
@ -251,8 +275,11 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins)
return !!(players[playernum].availabilities[byte] & (1 << shif)); return !!(players[playernum].availabilities[byte] & (1 << shif));
} }
// Use the host's if it's checking general state
if (playernum == -1)
return M_CheckNetUnlockByID(i);
// Use the unlockables table directly // Use the unlockables table directly
// NOTE: M_CheckNetUnlockByID would be correct in many circumstances... but not all. TODO figure out how to discern.
return (boolean)(gamedata->unlocked[i]); return (boolean)(gamedata->unlocked[i]);
} }

View file

@ -91,8 +91,9 @@ void ClearFakePlayerSkin(player_t* player);
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins); boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins);
INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags, boolean unlock); INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight, UINT32 flags, boolean unlock);
UINT8 *R_GetSkinAvailabilities(boolean demolock); UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots);
INT32 R_SkinAvailable(const char *name); INT32 R_SkinAvailable(const char *name);
UINT8 R_BotDefaultSkin(void);
void R_PatchSkins(UINT16 wadnum, boolean mainfile); void R_PatchSkins(UINT16 wadnum, boolean mainfile);
void R_AddSkins(UINT16 wadnum, boolean mainfile); void R_AddSkins(UINT16 wadnum, boolean mainfile);