From 0c75c4006030d9e127b18f6727851ec0e5a506ca Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 5 Mar 2023 18:06:09 +0000 Subject: [PATCH] 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 --- src/d_clisrv.c | 16 ++++-------- src/g_demo.c | 2 +- src/k_bot.c | 64 ++++++++++++++++++++--------------------------- src/k_grandprix.c | 23 ++--------------- src/k_grandprix.h | 10 -------- src/r_skins.c | 39 ++++++++++++++++++++++++----- src/r_skins.h | 3 ++- 7 files changed, 70 insertions(+), 87 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 76470b865..43df8ade3 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -824,7 +824,7 @@ static boolean CL_SendJoin(void) for (; i < MAXSPLITSCREENPLAYERS; i++) 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)); } @@ -3723,7 +3723,7 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum) static void Got_AddBot(UINT8 **p, INT32 playernum) { INT16 newplayernum; - UINT8 i, skinnum = 0; + UINT8 skinnum = 0; UINT8 difficulty = DIFFICULTBOT; if (playernum != serverplayer && !IsPlayerAdmin(playernum)) @@ -3753,14 +3753,8 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) playernode[newplayernum] = servernode; - // todo find a way to have all auto unlocked for dedicated - if (playeringame[0]) - { - for (i = 0; i < MAXAVAILABILITY; i++) - { - players[newplayernum].availabilities[i] = players[0].availabilities[i]; - } - } + // this will permit unlocks + memcpy(&players[newplayernum].availabilities, R_GetSkinAvailabilities(false, true), MAXAVAILABILITY*sizeof(UINT8)); players[newplayernum].splitscreenindex = 0; players[newplayernum].bot = true; @@ -3936,7 +3930,7 @@ boolean SV_SpawnServer(void) // 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 { - UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false); + UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false, false); SINT8 node = 0; 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); diff --git a/src/g_demo.c b/src/g_demo.c index 174511e4d..11b1414aa 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2239,7 +2239,7 @@ static void G_SaveDemoSkins(UINT8 **pp) { char skin[16]; UINT8 i; - UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true); + UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true, false); WRITEUINT8((*pp), numskins); for (i = 0; i < numskins; i++) diff --git a/src/k_bot.c b/src/k_bot.c index 56b9a8c1f..fa3635a94 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -108,13 +108,15 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) --------------------------------------------------*/ void K_UpdateMatchRaceBots(void) { + const UINT8 defaultbotskin = R_BotDefaultSkin(); const UINT8 difficulty = cv_kartbot.value; UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value); UINT8 numplayers = 0; UINT8 numbots = 0; UINT8 numwaiting = 0; SINT8 wantedbots = 0; - boolean skinusable[MAXSKINS]; + UINT8 usableskins = 0; + UINT8 grabskins[MAXSKINS+1]; UINT8 i; if (!server) @@ -122,18 +124,12 @@ void K_UpdateMatchRaceBots(void) return; } - // init usable bot skins list - for (i = 0; i < MAXSKINS; i++) + // Init usable bot skins list + for (i = 0; i < numskins; i++) { - if (i < numskins) - { - skinusable[i] = true; - } - else - { - skinusable[i] = false; - } + grabskins[usableskins++] = i; } + grabskins[usableskins] = MAXSKINS; if (cv_maxplayers.value > 0) { @@ -146,7 +142,7 @@ void K_UpdateMatchRaceBots(void) { if (!players[i].spectator) { - skinusable[players[i].skin] = false; + grabskins[players[i].skin] = MAXSKINS; if (players[i].bot) { @@ -185,48 +181,42 @@ void K_UpdateMatchRaceBots(void) { // We require MORE bots! UINT8 newplayernum = 0; - boolean usedallskins = false; if (dedicated) { 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) { - UINT8 skin = M_RandomKey(numskins); + UINT8 skinnum = defaultbotskin; - if (usedallskins == false) + if (usableskins > 0) { - UINT8 loops = 0; - - while (!skinusable[skin]) - { - if (loops >= numskins) - { - // no more skins, stick to our first choice - usedallskins = true; - break; - } - - skin++; - - if (skin >= numskins) - { - skin = 0; - } - - loops++; - } + UINT8 index = M_RandomKey(usableskins); + skinnum = grabskins[index]; + grabskins[index] = grabskins[--usableskins]; } - if (!K_AddBot(skin, difficulty, &newplayernum)) + if (!K_AddBot(skinnum, difficulty, &newplayernum)) { // Not enough player slots to add the bot, break the loop. break; } - skinusable[skin] = false; numbots++; } } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 6d879054b..ef7a4cd0f 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -95,25 +95,6 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) 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) @@ -121,7 +102,7 @@ UINT8 K_BotDefaultSkin(void) --------------------------------------------------*/ void K_InitGrandPrixBots(void) { - const UINT8 defaultbotskin = K_BotDefaultSkin(); + const UINT8 defaultbotskin = R_BotDefaultSkin(); const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed); UINT8 difficultylevels[MAXPLAYERS]; @@ -519,7 +500,7 @@ void K_IncreaseBotDifficulty(player_t *bot) --------------------------------------------------*/ void K_RetireBots(void) { - const UINT8 defaultbotskin = K_BotDefaultSkin(); + const UINT8 defaultbotskin = R_BotDefaultSkin(); SINT8 newDifficulty; UINT8 usableskins; diff --git a/src/k_grandprix.h b/src/k_grandprix.h index a862245c7..b9d426317 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -71,16 +71,6 @@ UINT8 K_BotStartingDifficulty(SINT8 value); 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); diff --git a/src/r_skins.c b/src/r_skins.c index 1f4daba5f..b7fa36ebb 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -129,6 +129,27 @@ static void Sk_SetDefaultValue(skin_t *skin) 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 // @@ -159,11 +180,12 @@ void R_InitSkins(void) M_UpdateConditionSetsPending(); } -UINT8 *R_GetSkinAvailabilities(boolean demolock) +UINT8 *R_GetSkinAvailabilities(boolean demolock, boolean forbots) { UINT8 i, shif, byte; INT32 skinid; static UINT8 responsebuffer[MAXAVAILABILITY]; + UINT8 defaultbotskin = R_BotDefaultSkin(); memset(&responsebuffer, 0, sizeof(responsebuffer)); @@ -172,15 +194,17 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock) if (unlockables[i].type != SECRET_SKIN) continue; - // NEVER EVER EVER M_CheckNetUnlockByID - if (gamedata->unlocked[i] != true && !demolock) - continue; - skinid = M_UnlockableSkinNum(&unlockables[i]); if (skinid < 0 || skinid >= MAXSKINS) 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); byte = (skinid / 8); @@ -251,8 +275,11 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins) 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 - // NOTE: M_CheckNetUnlockByID would be correct in many circumstances... but not all. TODO figure out how to discern. return (boolean)(gamedata->unlocked[i]); } diff --git a/src/r_skins.h b/src/r_skins.h index fe29282e7..40a95b61a 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -91,8 +91,9 @@ void ClearFakePlayerSkin(player_t* player); boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins); 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); +UINT8 R_BotDefaultSkin(void); void R_PatchSkins(UINT16 wadnum, boolean mainfile); void R_AddSkins(UINT16 wadnum, boolean mainfile);