From 2bfcfeb56037a1d18a8d13d23220b6167192cd2c Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 4 Mar 2024 16:54:36 +0000 Subject: [PATCH 1/6] Fix Skin unlocks at the beginning of replays - DXD_JOINDATA: Actually correctly write demo data - SetPlayerSkinByNum, bot skin iteration code: Use demo skinlist index instead of actual skin ID --- src/g_demo.cpp | 29 +++++++++++++++-------------- src/k_bot.cpp | 4 ++-- src/k_grandprix.c | 8 ++++---- src/r_skins.c | 9 ++++++--- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 444138b21..b471a0015 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -317,8 +317,8 @@ void G_ReadDemoExtraData(void) skinid = READUINT8(demobuf.p); if (skinid >= demo.numskins) skinid = 0; - SetPlayerSkinByNum(p, demo.skinlist[skinid].mapping); - demo.currentskinid[p] = skinid; + ghostext[p].skinid = demo.currentskinid[p] = skinid; + SetPlayerSkinByNum(p, skinid); players[p].kartspeed = ghostext[p].kartspeed = demo.skinlist[skinid].kartspeed; players[p].kartweight = ghostext[p].kartweight = demo.skinlist[skinid].kartweight; @@ -424,7 +424,7 @@ void G_WriteDemoExtraData(void) { for (j = 0; j < MAXAVAILABILITY; j++) { - WRITEUINT8(demobuf.p, players[i].availabilities[i]); + WRITEUINT8(demobuf.p, players[i].availabilities[j]); } WRITEUINT8(demobuf.p, (UINT8)players[i].bot); @@ -3354,12 +3354,9 @@ void G_DoPlayDemo(const char *defdemoname) // Skin - i = READUINT8(demobuf.p); - if (i >= demo.numskins) - i = 0; - SetPlayerSkinByNum(p, demo.skinlist[i].mapping); - demo.currentskinid[p] = ghostext[p].skinid = i; - + demo.currentskinid[p] = READUINT8(demobuf.p); + if (demo.currentskinid[p] >= demo.numskins) + demo.currentskinid[p] = 0; lastfakeskin[p] = READUINT8(demobuf.p); // Color @@ -3442,6 +3439,15 @@ void G_DoPlayDemo(const char *defdemoname) UINT8 j; p = slots[i]; + + for (j = 0; j < MAXAVAILABILITY; j++) + { + players[p].availabilities[j] = availabilities[p][j]; + } + + ghostext[p].skinid = demo.currentskinid[p]; + SetPlayerSkinByNum(p, demo.currentskinid[p]); + if (players[p].mo) { players[p].mo->color = players[p].skincolor; @@ -3457,11 +3463,6 @@ void G_DoPlayDemo(const char *defdemoname) players[p].kartweight = ghostext[p].kartweight = demo.skinlist[demo.currentskinid[p]].kartweight; players[p].charflags = ghostext[p].charflags = demo.skinlist[demo.currentskinid[p]].flags; players[p].lastfakeskin = lastfakeskin[p]; - - for (j = 0; j < MAXAVAILABILITY; j++) - { - players[p].availabilities[j] = availabilities[p][j]; - } } demo.deferstart = true; diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 66512be98..372200883 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -177,12 +177,12 @@ void K_UpdateMatchRaceBots(void) UINT8 numbots = 0; UINT8 numwaiting = 0; SINT8 wantedbots = 0; - UINT8 usableskins = 0; + UINT8 usableskins = 0, skincount = (demo.playback ? demo.numskins : numskins);; UINT8 grabskins[MAXSKINS+1]; UINT8 i; // Init usable bot skins list - for (i = 0; i < numskins; i++) + for (i = 0; i < skincount; i++) { grabskins[usableskins++] = i; } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 5b45c8176..92bd7b41b 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -133,7 +133,7 @@ void K_InitGrandPrixBots(void) UINT8 numplayers = 0; UINT8 competitors[MAXSPLITSCREENPLAYERS]; - UINT8 usableskins; + UINT8 usableskins, skincount = (demo.playback ? demo.numskins : numskins);; UINT8 grabskins[MAXSKINS+1]; UINT8 botskinlist[MAXPLAYERS]; @@ -146,7 +146,7 @@ void K_InitGrandPrixBots(void) memset(botskinlist, defaultbotskin, sizeof (botskinlist)); // Init usable bot skins list - for (usableskins = 0; usableskins < numskins; usableskins++) + for (usableskins = 0; usableskins < skincount; usableskins++) { grabskins[usableskins] = usableskins; } @@ -671,7 +671,7 @@ void K_RetireBots(void) const UINT8 defaultbotskin = R_BotDefaultSkin(); SINT8 newDifficulty; - UINT8 usableskins; + UINT8 usableskins, skincount = (demo.playback ? demo.numskins : numskins); UINT8 grabskins[MAXSKINS+1]; UINT8 i; @@ -687,7 +687,7 @@ void K_RetireBots(void) } // Init usable bot skins list - for (usableskins = 0; usableskins < numskins; usableskins++) + for (usableskins = 0; usableskins < skincount; usableskins++) { grabskins[usableskins] = usableskins; } diff --git a/src/r_skins.c b/src/r_skins.c index f67368daa..64dcfd1ec 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -226,7 +226,7 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock, INT32 botforcecharacter) boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins) { boolean needsunlocked = false; - boolean useplayerstruct = (Playing() && playernum != -1); + boolean useplayerstruct = ((Playing() || demo.playback) && playernum != -1); UINT16 i; INT32 skinid; @@ -357,6 +357,9 @@ engineclass_t R_GetEngineClass(SINT8 speed, SINT8 weight, skinflags_t flags) // Auxillary function that actually sets the skin static void SetSkin(player_t *player, INT32 skinnum) { + if (demo.playback) + skinnum = demo.skinlist[skinnum].mapping; + skin_t *skin = &skins[skinnum]; player->skin = skinnum; @@ -404,9 +407,9 @@ static void SetSkin(player_t *player, INT32 skinnum) // (If your mod locked them all, then you kinda stupid) static INT32 GetPlayerDefaultSkin(INT32 playernum) { - INT32 i; + INT32 i, skincount = (demo.playback ? demo.numskins : numskins); - for (i = 0; i < numskins; i++) + for (i = 0; i < skincount; i++) { if (R_SkinUsable(playernum, i, false)) { From 24b850a492ab40bf9efd4ad20bd34a40e2ab58f4 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 4 Mar 2024 16:56:07 +0000 Subject: [PATCH 2/6] LoadRecordGhosts: Use local unlock state, not consoleplayer availabilities (which might not be set yet) to determine whether a ghost should be visible --- src/p_setup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index f73355636..385e2ca3e 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -7851,7 +7851,7 @@ static void P_LoadRecordGhosts(void) if (sameGhosts) { INT32 skin = R_SkinAvailable(cv_skin[0].string); - if (skin < 0 || !R_SkinUsable(consoleplayer, skin, false)) + if (skin < 0 || !R_SkinUsable(-1, skin, false)) skin = 0; // use default skin add_ghosts(fmt::format("{}-{}{}", gpath, skins[skin].name, modeprefix), sameGhosts); } From 7f0df71558751d9b0f70f4d5e8bab8c5bcb4e3e3 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 4 Mar 2024 17:05:23 +0000 Subject: [PATCH 3/6] R_SkinAvailableEX - Most R_SkinAvailable calls should be returning index into demo.skinlist (same numerical value as when demo was recorded), for demo sync - A handful of general things permit exception for this - Expose `replaynumskins` (calculated as `(demo.playback ? demo.numskins : numskins)`) to Lua - There's *always* more that can be done for this, but this is the minimum spec that can at least be somewhat stable --- src/d_main.cpp | 2 +- src/d_netcmd.c | 2 +- src/f_finale.c | 4 ++-- src/g_demo.cpp | 6 ++++-- src/g_demo.h | 2 ++ src/g_gamedata.cpp | 4 ++-- src/hardware/hw_md2.c | 2 +- src/k_menudraw.c | 12 ++++++------ src/lua_baselib.c | 5 +++-- src/lua_hudlib.c | 8 +++++++- src/lua_mobjlib.c | 4 ++++ src/lua_script.c | 4 ++++ src/lua_skinlib.c | 2 +- src/m_cond.c | 6 +++--- src/menus/extras-challenges.c | 2 +- src/menus/main-goner.cpp | 2 +- src/menus/play-char-select.c | 4 ++-- src/p_saveg.c | 4 ++-- src/p_setup.cpp | 2 +- src/r_picformats.c | 2 +- src/r_skins.c | 34 ++++++++++++++++++++++++++++------ src/r_skins.h | 1 + 22 files changed, 78 insertions(+), 36 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 1ece00a2b..6fb28d624 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1946,7 +1946,7 @@ void D_SRB2Main(void) profile_t *pr = PR_GetProfile(cv_ttlprofilen.value); if (pr != NULL) { - INT32 importskin = R_SkinAvailable(pr->skinname); + INT32 importskin = R_SkinAvailableEx(pr->skinname, false); if (importskin != -1) { skins[importskin].records.wins = pr->wins; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 6d640d885..47d272caf 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -960,7 +960,7 @@ static void SendNameAndColor(const UINT8 n) // check if player has the skin loaded (cv_skin may have // the name of a skin that was available in the previous game) - cv_skin[n].value = R_SkinAvailable(cv_skin[n].string); + cv_skin[n].value = R_SkinAvailableEx(cv_skin[n].string, false); if ((cv_skin[n].value < 0) || !R_SkinUsable(playernum, cv_skin[n].value, false)) { CV_StealthSet(&cv_skin[n], DEFAULTSKIN); diff --git a/src/f_finale.c b/src/f_finale.c index 5de1d45dc..fff563921 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1345,12 +1345,12 @@ void F_TitleScreenDrawer(void) if (cache_gametrulystarted == true) { const char *eggName = "eggman"; - INT32 eggSkin = R_SkinAvailable(eggName); + INT32 eggSkin = R_SkinAvailableEx(eggName, false); skincolornum_t eggColor = SKINCOLOR_RED; UINT8 *eggColormap = NULL; const char *tailsName = "tails"; - INT32 tailsSkin = R_SkinAvailable(tailsName); + INT32 tailsSkin = R_SkinAvailableEx(tailsName, false); skincolornum_t tailsColor = SKINCOLOR_ORANGE; UINT8 *tailsColormap = NULL; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index b471a0015..7c472ccba 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -2064,12 +2064,14 @@ static democharlist_t *G_LoadDemoSkins(savebuffer_t *info, UINT8 *worknumskins, return NULL; } - READMEM(info->p, skin, 16); + READMEM(info->p, skinlist[i].name, 16); + skinlist[i].name[16] = '\0'; + skinlist[i].namehash = quickncasehash(skinlist[i].name, SKINNAMESIZE); skinlist[i].kartspeed = READUINT8(info->p); skinlist[i].kartweight = READUINT8(info->p); skinlist[i].flags = READUINT32(info->p); - result = R_SkinAvailable(skin); + result = R_SkinAvailableEx(skin, false); if (result == -1) { if (!getclosest) diff --git a/src/g_demo.h b/src/g_demo.h index 91b2c7ee7..7a43d33c0 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -69,6 +69,8 @@ extern consvar_t cv_recordmultiplayerdemos, cv_netdemosyncquality; extern tic_t demostarttime; struct democharlist_t { + char name[17]; + UINT32 namehash; UINT8 mapping; // No, this isn't about levels. It maps to loaded character ID. UINT8 kartspeed; UINT8 kartweight; diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index d522ce7ab..050f7341e 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -508,7 +508,7 @@ void srb2::load_ng_gamedata() for (auto& skinpair : js.skins) { - INT32 skin = R_SkinAvailable(skinpair.first.c_str()); + INT32 skin = R_SkinAvailableEx(skinpair.first.c_str(), false); skinrecord_t dummyrecord {}; dummyrecord.wins = skinpair.second.records.wins; @@ -683,7 +683,7 @@ void srb2::load_ng_gamedata() dummywindata[j].best_skin.id = MAXSKINS; dummywindata[j].best_skin.unloaded = nullptr; - int skinloaded = R_SkinAvailable(cuppair.second.records[j].bestskin.c_str()); + int skinloaded = R_SkinAvailableEx(cuppair.second.records[j].bestskin.c_str(), false); if (skinloaded >= 0) { dummywindata[j].best_skin.id = skinloaded; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 3fdc221e9..9fabe59b0 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -570,7 +570,7 @@ void HWR_InitModels(void) addskinmodel: // add player model - s = R_SkinAvailable(skinname); + s = R_SkinAvailableEx(skinname, false); if (s != -1) { md2_playermodels[s].skin = s; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ea8a4b665..b7a7298df 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -345,7 +345,7 @@ UINT16 M_GetCvPlayerColor(UINT8 pnum) if (color != SKINCOLOR_NONE) return color; - INT32 skin = R_SkinAvailable(cv_skin[pnum].string); + INT32 skin = R_SkinAvailableEx(cv_skin[pnum].string, false); if (skin == -1) return SKINCOLOR_NONE; @@ -377,7 +377,7 @@ static void M_DrawMenuParty(void) // Despite the work put into it, can't use M_GetCvPlayerColor directly - we need to reference skin always. #define grab_skin_and_colormap(pnum) \ { \ - skin = R_SkinAvailable(cv_skin[pnum].string); \ + skin = R_SkinAvailableEx(cv_skin[pnum].string, false); \ color = cv_playercolor[pnum].value; \ if (skin == -1) \ skin = 0; \ @@ -2277,7 +2277,7 @@ void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) if (p != NULL && p->version) { truecol = p->color; - skinnum = R_SkinAvailable(p->skinname); + skinnum = R_SkinAvailableEx(p->skinname, false); strcpy(pname, p->profilename); } @@ -6516,7 +6516,7 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili INT32 skin = M_UnlockableFollowerNum(ref); if (skin != -1) { - INT32 psk = R_SkinAvailable(cv_skin[0].string); + INT32 psk = R_SkinAvailableEx(cv_skin[0].string, false); UINT16 col = K_GetEffectiveFollowerColor(followers[skin].defaultcolor, &followers[skin], cv_playercolor[0].value, (psk != -1) ? &skins[psk] : &skins[0]); colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); pat = W_CachePatchName(followers[skin].icon, PU_CACHE); @@ -6775,7 +6775,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) } case SECRET_FOLLOWER: { - INT32 skin = R_SkinAvailable(cv_skin[0].string); + INT32 skin = R_SkinAvailableEx(cv_skin[0].string, false); INT32 fskin = M_UnlockableFollowerNum(ref); // Draw proximity reference for character @@ -6806,7 +6806,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) INT32 colorid = M_UnlockableColorNum(ref); if (colorid == SKINCOLOR_NONE) break; - INT32 skin = R_SkinAvailable(cv_skin[0].string); + INT32 skin = R_SkinAvailableEx(cv_skin[0].string, false); if (skin == -1) skin = 0; colormap = R_GetTranslationColormap(skin, colorid, GTC_MENUCACHE); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 71ad3fe75..c76888ce9 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2266,9 +2266,10 @@ static int lib_rSetPlayerSkin(lua_State *L) return luaL_error(L, "argument #2 not given (expected number or string)"); else if (lua_type(L, 2) == LUA_TNUMBER) // skin number { + INT32 skincount = (demo.playback ? demo.numskins : numskins); i = luaL_checkinteger(L, 2); - if (i < 0 || i >= numskins) - return luaL_error(L, "skin %d (argument #2) out of range (0 - %d)", i, numskins-1); + if (i < 0 || i >= skincount) + return luaL_error(L, "skin %d (argument #2) out of range (0 - %d)", i, skincount-1); } else // skin name { diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index c9c463d4a..862d1feef 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -378,7 +378,7 @@ static int libd_getSprite2Patch(lua_State *L) i = lua_tonumber(L, 1); if (i < 0 || i >= MAXSKINS) return luaL_error(L, "skin number %d out of range (0 - %d)", i, MAXSKINS-1); - if (i >= numskins) + if (i >= (demo.playback ? demo.numskins : numskins)) return 0; } else // find skin by name @@ -389,6 +389,9 @@ static int libd_getSprite2Patch(lua_State *L) return 0; } + if (demo.playback) + i = demo.skinlist[i].mapping; + lua_remove(L, 1); // remove skin now if (lua_isnumber(L, 1)) // sprite number given, e.g. SPR2_STIL @@ -1041,6 +1044,9 @@ static int libd_getColormap(lua_State *L) skinnum = i; } + if (demo.playback) + skinnum = demo.skinlist[skinnum].mapping; + // all was successful above, now we generate the colormap at last! colormap = R_GetTranslationColormap(skinnum, color, GTC_CACHE); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 3ef2ff950..afe34b3bd 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -721,7 +721,11 @@ static int mobj_set(lua_State *L) if (skin != -1) { if (!mo->player || R_SkinUsable(mo->player-players, skin, false)) + { + if (demo.playback) + skin = demo.skinlist[skin].mapping; mo->skin = &skins[skin]; + } return 0; } diff --git a/src/lua_script.c b/src/lua_script.c index d47df24dc..ba3c54f44 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -16,6 +16,7 @@ #include "deh_lua.h" #include "z_zone.h" #include "w_wad.h" +#include "r_things.h" // numskins #include "p_setup.h" #include "r_state.h" #include "r_sky.h" @@ -371,6 +372,9 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word,"replayplayback")) { lua_pushboolean(L, demo.playback); return 1; + } else if (fastcmp(word,"replaynumskins")) { + lua_pushinteger(L, (demo.playback ? demo.numskins : numskins)); + return 1; } else if (fastcmp(word, "gamestate")) { lua_pushinteger(L, gamestate); return 1; diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index 2950af461..3626a553a 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -196,7 +196,7 @@ static int lib_getSkin(lua_State *L) } // find skin by name - i = R_SkinAvailable(field); + i = R_SkinAvailableEx(field, false); if (i != -1) { LUA_PushUserdata(L, &skins[i], META_SKIN); diff --git a/src/m_cond.c b/src/m_cond.c index af30e415b..5789e8482 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -1206,7 +1206,7 @@ void M_UpdateConditionSetsPending(void) case UCRP_ISCHARACTER: case UCRP_MAKERETIRE: { - cn->requirement = R_SkinAvailable(cn->stringvar); + cn->requirement = R_SkinAvailableEx(cn->stringvar, false); if (cn->requirement < 0) { @@ -2021,7 +2021,7 @@ static const char *M_GetConditionCharacter(INT32 skin, boolean directlyrequires) for (j = 0; j < SKINRIVALS; j++) { const char *rivalname = skins[i].rivals[j]; - INT32 rivalnum = R_SkinAvailable(rivalname); + INT32 rivalnum = R_SkinAvailableEx(rivalname, false); if (rivalnum != skin) continue; @@ -3504,7 +3504,7 @@ INT32 M_UnlockableSkinNum(unlockable_t *unlock) } // Get the skin from the string. - skinnum = R_SkinAvailable(unlock->stringVar); + skinnum = R_SkinAvailableEx(unlock->stringVar, false); if (skinnum != -1) { unlock->stringVarCache = skinnum; diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index ff169772e..b6b71957e 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -714,7 +714,7 @@ void M_ChallengesTick(void) INT32 fskin = M_UnlockableFollowerNum(ref); if (fskin != -1) { - INT32 psk = R_SkinAvailable(cv_skin[0].string); + INT32 psk = R_SkinAvailableEx(cv_skin[0].string, false); if (psk == -1) psk = 0; bombcolor = K_GetEffectiveFollowerColor(followers[fskin].defaultcolor, &followers[fskin], cv_playercolor[0].value, &skins[psk]); diff --git a/src/menus/main-goner.cpp b/src/menus/main-goner.cpp index 9331c9a9d..9085d517d 100644 --- a/src/menus/main-goner.cpp +++ b/src/menus/main-goner.cpp @@ -84,7 +84,7 @@ public: { if (!skinName.empty()) { - this->skinID = R_SkinAvailable(skinName.c_str()); + this->skinID = R_SkinAvailableEx(skinName.c_str(), false); } this->offset = offset; diff --git a/src/menus/play-char-select.c b/src/menus/play-char-select.c index 80352f312..aae204783 100644 --- a/src/menus/play-char-select.c +++ b/src/menus/play-char-select.c @@ -203,7 +203,7 @@ static INT16 M_GetMenuCategoryFromFollower(setup_player_t *p) static void M_SetupProfileGridPos(setup_player_t *p) { profile_t *pr = PR_GetProfile(p->profilen); - INT32 i = R_SkinAvailable(pr->skinname); + INT32 i = R_SkinAvailableEx(pr->skinname, false); INT32 alt = 0; // Hey it's my character's name! if (i == -1) @@ -247,7 +247,7 @@ static void M_SetupProfileGridPos(setup_player_t *p) static void M_SetupMidGameGridPos(setup_player_t *p, UINT8 num) { - INT32 i = R_SkinAvailable(cv_skin[num].zstring); + INT32 i = R_SkinAvailableEx(cv_skin[num].zstring, false); INT32 alt = 0; // Hey it's my character's name! if (i == -1) diff --git a/src/p_saveg.c b/src/p_saveg.c index 1720561e2..b6efe3530 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -150,7 +150,7 @@ static boolean P_UnArchivePlayer(savebuffer_t *save) INT32 skin; READSTRINGN(save->p, skinname, SKINNAMESIZE); - skin = R_SkinAvailable(skinname); + skin = R_SkinAvailableEx(skinname, false); if (skin == -1) { @@ -176,7 +176,7 @@ static boolean P_UnArchivePlayer(savebuffer_t *save) savedata.bots[pid].valid = true; READSTRINGN(save->p, skinname, SKINNAMESIZE); - skin = R_SkinAvailable(skinname); + skin = R_SkinAvailableEx(skinname, false); if (skin == -1) { diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 385e2ca3e..905470403 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -7850,7 +7850,7 @@ static void P_LoadRecordGhosts(void) if (sameGhosts) { - INT32 skin = R_SkinAvailable(cv_skin[0].string); + INT32 skin = R_SkinAvailableEx(cv_skin[0].string, false); if (skin < 0 || !R_SkinUsable(-1, skin, false)) skin = 0; // use default skin add_ghosts(fmt::format("{}-{}{}", gpath, skins[skin].name, modeprefix), sameGhosts); diff --git a/src/r_picformats.c b/src/r_picformats.c index 996662886..d87988583 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -1435,7 +1435,7 @@ static void R_ParseSpriteInfoSkin(struct ParseSpriteInfoState *parser) skinName[sprinfoTokenLength] = '\0'; strlwr(skinName); - skinnum = R_SkinAvailable(skinName); + skinnum = R_SkinAvailableEx(skinName, false); if (skinnum == -1) I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName); diff --git a/src/r_skins.c b/src/r_skins.c index 64dcfd1ec..d98577c60 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -136,15 +136,17 @@ static void Sk_SetDefaultValue(skin_t *skin) } // Grab the default skin +#define DEFAULTBOTSKINNAME "eggrobo" UINT8 R_BotDefaultSkin(void) { static INT32 defaultbotskin = -1; + if (demo.playback) + return R_SkinAvailableEx(DEFAULTBOTSKINNAME, true); + if (defaultbotskin == -1) { - const char *defaultbotskinname = "eggrobo"; - - defaultbotskin = R_SkinAvailable(defaultbotskinname); + defaultbotskin = R_SkinAvailableEx(DEFAULTBOTSKINNAME, false); if (defaultbotskin == -1) { @@ -155,6 +157,7 @@ UINT8 R_BotDefaultSkin(void) return (UINT8)defaultbotskin; } +#undef DEFAULTBOTSKINNAME // // Initialize the basic skins @@ -318,10 +321,29 @@ UINT32 R_GetLocalRandomSkin(void) // returns true if the skin name is found (loaded from pwad) // warning return -1 if not found INT32 R_SkinAvailable(const char *name) +{ + return R_SkinAvailableEx(name, true); +} + +INT32 R_SkinAvailableEx(const char *name, boolean demoskins) { INT32 i; UINT32 hash = quickncasehash(name, SKINNAMESIZE); + if (demo.playback && demoskins) + { + for (i = 0; i < demo.numskins; i++) + { + if (demo.skinlist[i].namehash != hash) + continue; + + if (stricmp(demo.skinlist[i].name,name)!=0) + continue; + + return i; + } + } + for (i = 0; i < numskins; i++) { if (skins[i].namehash != hash) @@ -946,7 +968,7 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile) // Others can't go in there because we don't want them to be patchable. if (!stricmp(stoken, "name")) { - INT32 skinnum = R_SkinAvailable(value); + INT32 skinnum = R_SkinAvailableEx(value, false); strlwr(value); if (skinnum == -1) STRBUFCPY(skin->name, value); @@ -961,7 +983,7 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile) snprintf(value2, stringspace, "%s%d", value, numskins); value2[stringspace - 1] = '\0'; - if (R_SkinAvailable(value2) == -1) + if (R_SkinAvailableEx(value2, false) == -1) // I'm lazy so if NEW name is already used I leave the 'skin x' // default skin name set in Sk_SetDefaultValue STRBUFCPY(skin->name, value2); @@ -1141,7 +1163,7 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile) if (!stricmp(stoken, "name")) { strlwr(value); - skinnum = R_SkinAvailable(value); + skinnum = R_SkinAvailableEx(value, false); if (skinnum != -1) skin = &skins[skinnum]; else diff --git a/src/r_skins.h b/src/r_skins.h index 89f1c2b91..57b12825f 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -112,6 +112,7 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile); // Access INT32 R_SkinAvailable(const char *name); +INT32 R_SkinAvailableEx(const char *name, boolean demoskins); boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins); UINT8 *R_GetSkinAvailabilities(boolean demolock, INT32 botforcecharacter); From 9b9c2cbcb6441748f53453e94ae34d267e16c9d4 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 4 Mar 2024 17:34:54 +0000 Subject: [PATCH 4/6] M_EndModeAttackRun: Fix recursivity issue that resulted in natural ends to replays losing track of restoreMenu --- src/menus/transient/pause-replay.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/menus/transient/pause-replay.c b/src/menus/transient/pause-replay.c index 9c0c777a7..abb0e89cf 100644 --- a/src/menus/transient/pause-replay.c +++ b/src/menus/transient/pause-replay.c @@ -54,15 +54,22 @@ menu_t PAUSE_PlaybackMenuDef = { void M_EndModeAttackRun(void) { - G_CheckDemoStatus(); // Cancel recording + if (demo.playback) + { + G_CheckDemoStatus(); // Cancel recording + return; + } Command_ExitGame_f(); // Clear a bunch of state + if (!modeattacking) + return; + modeattacking = ATTACKING_NONE; // Kept until now because of Command_ExitGame_f if (demo.attract == DEMO_ATTRACT_TITLE) { - D_StartTitle(); + D_SetDeferredStartTitle(true); } else if (demo.attract == DEMO_ATTRACT_CREDITS) { From 96a6fe85f1088bc76763af07b389260c25d9aa96 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 5 Mar 2024 16:43:27 +0000 Subject: [PATCH 5/6] G_LoadDemoSkins: Actually pass the skin name to R_SkinAvailableEx, idiot Fixes the reported Emerl issue --- src/g_demo.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 7c472ccba..0610ddf5e 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -2033,7 +2033,6 @@ static void G_SaveDemoSkins(UINT8 **pp) static democharlist_t *G_LoadDemoSkins(savebuffer_t *info, UINT8 *worknumskins, boolean getclosest) { - char skin[17]; UINT8 i, byte, shif; democharlist_t *skinlist = NULL; @@ -2052,8 +2051,6 @@ static democharlist_t *G_LoadDemoSkins(savebuffer_t *info, UINT8 *worknumskins, I_Error("G_LoadDemoSkins: Insufficient memory to allocate list"); } - skin[16] = '\0'; - for (i = 0; i < (*worknumskins); i++) { INT32 result = -1; @@ -2071,7 +2068,7 @@ static democharlist_t *G_LoadDemoSkins(savebuffer_t *info, UINT8 *worknumskins, skinlist[i].kartweight = READUINT8(info->p); skinlist[i].flags = READUINT32(info->p); - result = R_SkinAvailableEx(skin, false); + result = R_SkinAvailableEx(skinlist[i].name, false); if (result == -1) { if (!getclosest) From c299831737f58d211377a139db4457836151b49e Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 4 Mar 2024 19:29:37 +0000 Subject: [PATCH 6/6] Fade out characters you haven't unlocked yet in Time Attack demos exclusively - Objects - Minimap All other HUD items need to be done manually, but the lives icon doesn't show up in time attack so this minimum should be fine --- src/k_hud.cpp | 12 ++++++---- src/r_skins.c | 11 ++++++++-- src/r_skins.h | 1 + src/r_spritefx.cpp | 7 ++++++ src/r_things.cpp | 55 +++++++++++++++++++++++++--------------------- 5 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 87d1095ec..5cdef51e4 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -88,6 +88,7 @@ patch_t *kp_facenum[MAXPLAYERS+1]; static patch_t *kp_facehighlight[8]; static patch_t *kp_nocontestminimap; +static patch_t *kp_unknownminimap; static patch_t *kp_spbminimap; static patch_t *kp_wouldyoustillcatchmeifiwereaworm; static patch_t *kp_catcherminimap; @@ -386,7 +387,7 @@ void K_LoadKartHUDGraphics(void) // Special minimap icons HU_UpdatePatch(&kp_nocontestminimap, "MINIDEAD"); - + HU_UpdatePatch(&kp_unknownminimap, "HUHMAP"); HU_UpdatePatch(&kp_spbminimap, "SPBMMAP"); HU_UpdatePatch(&kp_wouldyoustillcatchmeifiwereaworm, "MINIPROG"); @@ -4401,6 +4402,9 @@ static void K_drawKartMinimap(void) skin = ((skin_t*)g->mo->skin)-skins; else skin = 0; + + workingPic = R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap; + if (g->mo->color) { if (g->mo->colorized) @@ -4414,7 +4418,7 @@ static void K_drawKartMinimap(void) interpx = R_InterpolateFixed(g->mo->old_x, g->mo->x); interpy = R_InterpolateFixed(g->mo->old_y, g->mo->y); - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap); + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap); g = g->next; } } @@ -4470,7 +4474,7 @@ static void K_drawKartMinimap(void) { skin = ((skin_t*)mobj->skin)-skins; - workingPic = faceprefix[skin][FACE_MINIMAP]; + workingPic = R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap; if (mobj->color) { @@ -4666,7 +4670,7 @@ static void K_drawKartMinimap(void) { skin = ((skin_t*)mobj->skin)-skins; - workingPic = faceprefix[skin][FACE_MINIMAP]; + workingPic = R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap; if (mobj->color) { diff --git a/src/r_skins.c b/src/r_skins.c index d98577c60..89e5ff159 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -229,7 +229,7 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock, INT32 botforcecharacter) boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins) { boolean needsunlocked = false; - boolean useplayerstruct = ((Playing() || demo.playback) && playernum != -1); + boolean useplayerstruct = ((Playing() || demo.playback) && playernum >= 0); UINT16 i; INT32 skinid; @@ -299,6 +299,13 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins) return (boolean)(gamedata->unlocked[i]); } +boolean R_CanShowSkinInDemo(INT32 skinnum) +{ + if (modeattacking == ATTACKING_NONE && !(demo.playback && demo.attract)) + return true; + return R_SkinUsable(-2, skinnum, false); +} + // Returns a random unlocked skin ID. UINT32 R_GetLocalRandomSkin(void) { @@ -307,7 +314,7 @@ UINT32 R_GetLocalRandomSkin(void) for (i = 0; i < numskins; i++) { - if (!R_SkinUsable(-1, i, false)) + if (!R_SkinUsable(-2, i, false)) continue; grabskins[usableskins++] = i; } diff --git a/src/r_skins.h b/src/r_skins.h index 57b12825f..03816d758 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -115,6 +115,7 @@ INT32 R_SkinAvailable(const char *name); INT32 R_SkinAvailableEx(const char *name, boolean demoskins); boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins); UINT8 *R_GetSkinAvailabilities(boolean demolock, INT32 botforcecharacter); +boolean R_CanShowSkinInDemo(INT32 skinnum); // Setting void SetPlayerSkin(INT32 playernum,const char *skinname); diff --git a/src/r_spritefx.cpp b/src/r_spritefx.cpp index ae3edcf84..af34788ca 100644 --- a/src/r_spritefx.cpp +++ b/src/r_spritefx.cpp @@ -44,6 +44,13 @@ INT32 R_ThingLightLevel(mobj_t* thing) { lightlevel -= 255; } + + if (!R_CanShowSkinInDemo((skin_t*)thing->skin-skins) + && !thing->colorized + && !thing->hitlag) + { + lightlevel -= 128; + } } return lightlevel; diff --git a/src/r_things.cpp b/src/r_things.cpp index 52cb52521..a91eb128f 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -835,37 +835,42 @@ boolean R_ThingIsFlashing(mobj_t *thing) UINT8 *R_GetSpriteTranslation(vissprite_t *vis) { - if (!(vis->cut & SC_PRECIP) && - R_ThingIsFlashing(vis->mobj)) + if (vis->cut & SC_PRECIP) { - return R_GetTranslationColormap(TC_HITLAG, static_cast(0), GTC_CACHE); + // Simplified func, less safe properties to check + if (vis->mobj->color) + R_GetTranslationColormap(TC_DEFAULT, static_cast(vis->mobj->color), GTC_CACHE); + return NULL; } - /* - else if (R_SpriteIsFlashing(vis)) // Bosses "flash" + + size_t skinnum = TC_DEFAULT; + + if (vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player! { - if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized) - return R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE); - else if (vis->mobj->type == MT_METALSONIC_BATTLE) - return R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE); - else - return R_GetTranslationColormap(TC_BOSS, 0, GTC_CACHE); - } - */ - else if (vis->mobj->color) - { - // New colormap stuff for skins Tails 06-07-2002 - if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized) - return R_GetTranslationColormap(TC_RAINBOW, static_cast(vis->mobj->color), GTC_CACHE); - else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player! + skinnum = (skin_t*)vis->mobj->skin-skins; + + // Hide not-yet-unlocked characters in replays from other people + if (!R_CanShowSkinInDemo(skinnum)) { - size_t skinnum = (skin_t*)vis->mobj->skin-skins; - return R_GetTranslationColormap((INT32)skinnum, static_cast(vis->mobj->color), GTC_CACHE); + skinnum = TC_BLINK; } - else // Use the defaults - return R_GetTranslationColormap(TC_DEFAULT, static_cast(vis->mobj->color), GTC_CACHE); } - else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome. - return R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE); + + if (R_ThingIsFlashing(vis->mobj)) + { + if (skinnum != (size_t)TC_BLINK) + skinnum = TC_HITLAG; + + return R_GetTranslationColormap(skinnum, static_cast(0), GTC_CACHE); + } + + if (vis->mobj->color) + { + if (skinnum != (size_t)TC_BLINK && vis->mobj->colorized) + skinnum = TC_RAINBOW; + + return R_GetTranslationColormap(skinnum, static_cast(vis->mobj->color), GTC_CACHE); + } return NULL; }