From 8278e621fb5cbe685c014c1e72c9145e9c906909 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 25 Apr 2021 07:18:32 -0400 Subject: [PATCH 1/6] Removed skin->availability Locked skins now are a specific unlockable type, instead of being tied to the skin's properties. This has plagued custom gamedata since 2.2 launch. It's extremely obnoxious having to set aside random numbers as dummy unlockables just to ensure that Amy Fang & Metal are unlocked from the start in a custom map pack. Other changes made to accommodate this: - R_GetSkinAvailabilities is now created from the list of unlockables set to skin type. (1st skin unlockable defined is (1), 2nd skin unlockable defined is (1 << 1), etc...) - The "Added skin x" print shows up when loading addons but not at all for the base game, because the previous behavior of hiding based on if the skin was locked would now require iterating unlockables, which felt wrong to do during that stage of the loading process - I noticed in my test wad that Sonic&Tails would give you Sonic&Sonic out if Tails was locked. I fixed that by making both skins required to show the character select option. Mods that reserved empty dummy unlockables for Amy Fang and Metal won't have to do anything. Mods that wanted to re-lock them behind different requirements will have to update, but in the future they will not have to be in specific slots. Additionally, now Sonic Tails and Knuckles can also be locked for mods. --- src/d_netcmd.c | 26 ++++++++-- src/deh_soc.c | 23 ++++++--- src/lua_skinlib.c | 5 -- src/m_cond.h | 1 + src/m_menu.c | 9 +++- src/p_enemy.c | 29 +++++++++-- src/p_setup.c | 4 +- src/r_skins.c | 120 ++++++++++++++++++++++++++++++++++++---------- src/r_skins.h | 6 +-- src/r_things.c | 4 +- 10 files changed, 175 insertions(+), 52 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 09f9d4651..9261770c7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1475,7 +1475,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (server && (p != &players[consoleplayer] && p != &players[secondarydisplayplayer])) { boolean kick = false; - INT32 s; + UINT32 unlockShift = 0; + UINT32 i; // team colors if (G_GametypeHasTeams()) @@ -1491,12 +1492,29 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) kick = true; // availabilities - for (s = 0; s < MAXSKINS; s++) + for (i = 0; i < MAXUNLOCKABLES; i++) { - if (!skins[s].availability && (p->availabilities & (1 << s))) + if (unlockables[i].type != SECRET_SKIN) + { + continue; + } + + unlockShift++; + } + + // If they set an invalid bit to true, then likely a modified client + if (unlockShift < 32) // 32 is the max the data type allows + { + UINT32 illegalMask = UINT32_MAX; + + for (i = 0; i < unlockShift; i++) + { + illegalMask &= ~(1 << i); + } + + if ((p->availabilities & illegalMask) != 0) { kick = true; - break; } } diff --git a/src/deh_soc.c b/src/deh_soc.c index 5b12ea1b0..e5cba5185 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3219,19 +3219,30 @@ void readunlockable(MYFILE *f, INT32 num) unlockables[num].type = SECRET_WARP; else if (fastcmp(word2, "SOUNDTEST")) unlockables[num].type = SECRET_SOUNDTEST; + else if (fastcmp(word2, "SKIN")) + unlockables[num].type = SECRET_SKIN; else unlockables[num].type = (INT16)i; } else if (fastcmp(word, "VAR")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. + INT32 skinnum = R_SkinAvailable(word2); - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - i = M_MapNumber(word2[0], word2[1]); + if (skinnum != -1) + { + unlockables[num].variable = (INT16)skinnum; + } + else + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. - unlockables[num].variable = (INT16)i; + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + i = M_MapNumber(word2[0], word2[1]); + + unlockables[num].variable = (INT16)i; + } } else deh_warning("Unlockable %d: unknown word '%s'", num+1, word); diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index 56be6bf4f..f6c0879bd 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -53,7 +53,6 @@ enum skin { skin_contspeed, skin_contangle, skin_soundsid, - skin_availability, skin_sprites }; static const char *const skin_opt[] = { @@ -91,7 +90,6 @@ static const char *const skin_opt[] = { "contspeed", "contangle", "soundsid", - "availability", "sprites", NULL}; @@ -209,9 +207,6 @@ static int skin_get(lua_State *L) case skin_soundsid: LUA_PushUserdata(L, skin->soundsid, META_SOUNDSID); break; - case skin_availability: - lua_pushinteger(L, skin->availability); - break; case skin_sprites: LUA_PushLightUserdata(L, skin->sprites, META_SKINSPRITES); break; diff --git a/src/m_cond.h b/src/m_cond.h index 9bb162ff3..08b47c63a 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -132,6 +132,7 @@ typedef struct #define SECRET_WARP 2 // Selectable warp #define SECRET_SOUNDTEST 3 // Sound Test #define SECRET_CREDITS 4 // Enables Credits +#define SECRET_SKIN 5 // Unlocks a skin // If you have more secrets than these variables allow in your game, // you seriously need to get a life. diff --git a/src/m_menu.c b/src/m_menu.c index 0fca39801..651dbecc6 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8963,7 +8963,7 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum) static UINT8 M_SetupChoosePlayerDirect(INT32 choice) { - INT32 skinnum; + INT32 skinnum, botskin; UINT8 i; UINT8 firstvalid = 255, lastvalid = 255; boolean allowed = false; @@ -8995,6 +8995,13 @@ static UINT8 M_SetupChoosePlayerDirect(INT32 choice) skinnum = description[i].skinnum[0]; if ((skinnum != -1) && (R_SkinUsable(-1, skinnum))) { + botskin = description[i].skinnum[1]; + if ((botskin != -1) && (!R_SkinUsable(-1, botskin))) + { + // Bot skin isn't unlocked + continue; + } + // Handling order. if (firstvalid == 255) firstvalid = i; diff --git a/src/p_enemy.c b/src/p_enemy.c index 59176d6cc..306b8b399 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -25,6 +25,7 @@ #include "i_video.h" #include "z_zone.h" #include "lua_hook.h" +#include "m_cond.h" // SECRET_SKIN #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -5101,6 +5102,28 @@ void A_SignSpin(mobj_t *actor) } } +static boolean SignSkinCheck(player_t *player, INT32 num) +{ + INT32 i; + + if (player != NULL) + { + // Use player's availabilities + return R_SkinUsable(player - players, num); + } + + // Player invalid, only show characters that are unlocked from the start. + for (i = 0; i < MAXUNLOCKABLES; i++) + { + if (unlockables[i].type == SECRET_SKIN && unlockables[i].variable == num) + { + return false; + } + } + + return true; +} + // Function: A_SignPlayer // // Description: Changes the state of a level end sign to reflect the player that hit it. @@ -5161,23 +5184,21 @@ void A_SignPlayer(mobj_t *actor) // I turned this function into a fucking mess. I'm so sorry. -Lach if (locvar1 == -2) // random skin { -#define skincheck(num) (player ? !R_SkinUsable(player-players, num) : skins[num].availability > 0) player_t *player = actor->target ? actor->target->player : NULL; UINT8 skinnum; UINT8 skincount = 0; for (skinnum = 0; skinnum < numskins; skinnum++) - if (!skincheck(skinnum)) + if (SignSkinCheck(player, skinnum)) skincount++; skinnum = P_RandomKey(skincount); for (skincount = 0; skincount < numskins; skincount++) { if (skincount > skinnum) break; - if (skincheck(skincount)) + if (!SignSkinCheck(player, skincount)) skinnum++; } skin = &skins[skinnum]; -#undef skincheck } else // specific skin skin = &skins[locvar1]; diff --git a/src/p_setup.c b/src/p_setup.c index 40dd1a284..ae68bac80 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -4527,8 +4527,8 @@ boolean P_AddWadFile(const char *wadfilename) // // look for skins // - R_AddSkins(wadnum); // faB: wadfile index in wadfiles[] - R_PatchSkins(wadnum); // toast: PATCH PATCH + R_AddSkins(wadnum, false); // faB: wadfile index in wadfiles[] + R_PatchSkins(wadnum, false); // toast: PATCH PATCH ST_ReloadSkinFaceGraphics(); // diff --git a/src/r_skins.c b/src/r_skins.c index 6f150f234..587259ae0 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -148,8 +148,6 @@ static void Sk_SetDefaultValue(skin_t *skin) skin->contspeed = 17; skin->contangle = 0; - skin->availability = 0; - for (i = 0; i < sfx_skinsoundslot0; i++) if (S_sfx[i].skinsound != -1) skin->soundsid[S_sfx[i].skinsound] = i; @@ -176,14 +174,34 @@ void R_InitSkins(void) UINT32 R_GetSkinAvailabilities(void) { - INT32 s; UINT32 response = 0; + UINT32 unlockShift = 0; + INT32 i; - for (s = 0; s < MAXSKINS; s++) + for (i = 0; i < MAXUNLOCKABLES; i++) { - if (skins[s].availability && unlockables[skins[s].availability - 1].unlocked) - response |= (1 << s); + if (unlockables[i].type != SECRET_SKIN) + { + continue; + } + + if (unlockShift >= 32) + { + // This crash is impossible to trigger as is, + // but it could happen if MAXUNLOCKABLES is ever made higher than 32, + // and someone makes a mod that has 33+ unlockable characters. :V + I_Error("Too many unlockable characters\n"); + return 0; + } + + if (unlockables[i].unlocked) + { + response |= (1 << unlockShift); + } + + unlockShift++; } + return response; } @@ -191,14 +209,74 @@ UINT32 R_GetSkinAvailabilities(void) // warning don't use with an invalid skinnum other than -1 which always returns true boolean R_SkinUsable(INT32 playernum, INT32 skinnum) { - return ((skinnum == -1) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0... - || (!skins[skinnum].availability) - || (((netgame || multiplayer) && playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked)) - || (modeattacking) // If you have someone else's run you might as well take a look - || (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1. - || (netgame && (cv_forceskin.value == skinnum)) // Force 2. - || (metalrecording && skinnum == 5) // Force 3. - ); + INT32 unlockID = -1; + UINT32 unlockShift = 0; + INT32 i; + + if (skinnum == -1) + { + // Simplifies things elsewhere, since there's already plenty of checks for less-than-0... + return true; + } + + if (modeattacking) + { + // If you have someone else's run you might as well take a look + return true; + } + + if (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) + { + // Force 1. + return true; + } + + if (netgame && (cv_forceskin.value == skinnum)) + { + // Force 2. + return true; + } + + if (metalrecording && skinnum == 5) + { + // Force 3. + return true; + } + + // We will now check if this skin is supposed to be locked or not. + + for (i = 0; i < MAXUNLOCKABLES; i++) + { + if (unlockables[i].type != SECRET_SKIN) + { + continue; + } + + if (unlockables[i].variable == skinnum) + { + unlockID = i; + break; + } + + unlockShift++; + } + + if (unlockID == -1) + { + // This skin isn't locked at all, we're good. + return true; + } + + if ((netgame || multiplayer) && playernum != -1) + { + // We want to check per-player unlockables. + return (players[playernum].availabilities & (1 << unlockShift)); + } + else + { + // We want to check our global unlockables. + return (unlockables[unlockID].unlocked); + } } // returns true if the skin name is found (loaded from pwad) @@ -558,7 +636,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) // // Find skin sprites, sounds & optional status bar face, & add them // -void R_AddSkins(UINT16 wadnum) +void R_AddSkins(UINT16 wadnum, boolean mainfile) { UINT16 lump, lastlump = 0; char *buf; @@ -673,12 +751,6 @@ void R_AddSkins(UINT16 wadnum) if (!realname) STRBUFCPY(skin->realname, skin->hudname); } - else if (!stricmp(stoken, "availability")) - { - skin->availability = atoi(value); - if (skin->availability >= MAXUNLOCKABLES) - skin->availability = 0; - } else if (!R_ProcessPatchableFields(skin, stoken, value)) CONS_Debug(DBG_SETUP, "R_AddSkins: Unknown keyword '%s' in S_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); @@ -693,7 +765,7 @@ next_token: R_FlushTranslationColormapCache(); - if (!skin->availability) // Safe to print... + if (mainfile == false) CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name); #ifdef SKINVALUES skin_cons_t[numskins].value = numskins; @@ -713,7 +785,7 @@ next_token: // // Patch skin sprites // -void R_PatchSkins(UINT16 wadnum) +void R_PatchSkins(UINT16 wadnum, boolean mainfile) { UINT16 lump, lastlump = 0; char *buf; @@ -826,7 +898,7 @@ next_token: R_FlushTranslationColormapCache(); - if (!skin->availability) // Safe to print... + if (mainfile == false) CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name); } return; diff --git a/src/r_skins.h b/src/r_skins.h index fbbb38743..5efd70307 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -80,8 +80,6 @@ typedef struct // contains super versions too spritedef_t sprites[NUMPLAYERSPRITES*2]; spriteinfo_t sprinfo[NUMPLAYERSPRITES*2]; - - UINT8 availability; // lock? } skin_t; /// Externs @@ -96,8 +94,8 @@ void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 boolean R_SkinUsable(INT32 playernum, INT32 skinnum); UINT32 R_GetSkinAvailabilities(void); INT32 R_SkinAvailable(const char *name); -void R_PatchSkins(UINT16 wadnum); -void R_AddSkins(UINT16 wadnum); +void R_AddSkins(UINT16 wadnum, boolean mainfile); +void R_PatchSkins(UINT16 wadnum, boolean mainfile); UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player); diff --git a/src/r_things.c b/src/r_things.c index a7c44c237..c5cf3aeae 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -547,8 +547,8 @@ void R_InitSprites(void) R_InitSkins(); for (i = 0; i < numwadfiles; i++) { - R_AddSkins((UINT16)i); - R_PatchSkins((UINT16)i); + R_AddSkins((UINT16)i, true); + R_PatchSkins((UINT16)i, true); R_LoadSpriteInfoLumps(i, wadfiles[i]->numlumps); } ST_ReloadSkinFaceGraphics(); From 382c0aa7de42b38e647bdfd3b157ff239d1bb5fc Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 25 Apr 2021 16:27:15 -0400 Subject: [PATCH 2/6] Fix overshadowed declaration --- src/m_menu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 651dbecc6..eb330c9e9 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8963,7 +8963,7 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum) static UINT8 M_SetupChoosePlayerDirect(INT32 choice) { - INT32 skinnum, botskin; + INT32 skinnum, botskinnum; UINT8 i; UINT8 firstvalid = 255, lastvalid = 255; boolean allowed = false; @@ -8995,8 +8995,8 @@ static UINT8 M_SetupChoosePlayerDirect(INT32 choice) skinnum = description[i].skinnum[0]; if ((skinnum != -1) && (R_SkinUsable(-1, skinnum))) { - botskin = description[i].skinnum[1]; - if ((botskin != -1) && (!R_SkinUsable(-1, botskin))) + botskinnum = description[i].skinnum[1]; + if ((botskinnum != -1) && (!R_SkinUsable(-1, botskinnum))) { // Bot skin isn't unlocked continue; From f0b9e0e415982f5dfee3942bb8cbdabc80a2b16f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 25 Apr 2021 16:38:33 -0400 Subject: [PATCH 3/6] Default to first usable skin instead of 0, I_Error if none are usable --- src/r_skins.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/r_skins.c b/src/r_skins.c index 587259ae0..758d0980d 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -294,6 +294,23 @@ INT32 R_SkinAvailable(const char *name) return -1; } +// Gets the player to the first usuable skin in the game. (If your mod locked them all, then you kinda stupid) +void SetPlayerDefaultSkin(INT32 playernum) +{ + INT32 i; + + for (i = 0; i < numskins; i++) + { + if (R_SkinUsable(playernum, i)) + { + SetPlayerSkinByNum(playernum, i); + return; + } + } + + I_Error("All characters are locked."); +} + // network code calls this when a 'skin change' is received void SetPlayerSkin(INT32 playernum, const char *skinname) { @@ -311,7 +328,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname) else if(server || IsPlayerAdmin(consoleplayer)) CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname); - SetPlayerSkinByNum(playernum, 0); + SetPlayerDefaultSkin(playernum); } // Same as SetPlayerSkin, but uses the skin #. @@ -402,7 +419,8 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) CONS_Alert(CONS_WARNING, M_GetText("Requested skin %d not found\n"), skinnum); else if(server || IsPlayerAdmin(consoleplayer)) CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum); - SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin + + SetPlayerDefaultSkin(playernum); } // From 92107f28d5dc48e7cb01f0c274aef85911f0ebdd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 25 Apr 2021 17:54:47 -0400 Subject: [PATCH 4/6] Add string variable for unlockables and emblems Skin unlockables / skin emblems are now checked at runtime to see if there's any matches. --- src/d_netcmd.c | 5 +++-- src/deh_soc.c | 54 +++++++++++++++++++++++++++++++++++++------------- src/deh_soc.h | 2 ++ src/dehacked.c | 7 ++----- src/m_cond.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/m_cond.h | 5 +++++ src/p_enemy.c | 9 +++++++-- src/p_mobj.c | 16 +++++++++++++-- src/r_skins.c | 25 +++++++++++++++-------- src/r_skins.h | 2 ++ 10 files changed, 140 insertions(+), 33 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9261770c7..17ab52eb5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1313,8 +1313,9 @@ static void SendNameAndColor(void) cv_skin.value = R_SkinAvailable(cv_skin.string); if ((cv_skin.value < 0) || !R_SkinUsable(consoleplayer, cv_skin.value)) { - CV_StealthSet(&cv_skin, DEFAULTSKIN); - cv_skin.value = 0; + INT32 defaultSkinNum = GetPlayerDefaultSkin(consoleplayer); + CV_StealthSet(&cv_skin, skins[defaultSkinNum].name); + cv_skin.value = defaultSkinNum; } // Finally write out the complete packet and send it off. diff --git a/src/deh_soc.c b/src/deh_soc.c index e5cba5185..1dde6c9f0 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -127,6 +127,33 @@ static float searchfvalue(const char *s) #endif // These are for clearing all of various things +void clear_emblems(void) +{ + INT32 i; + + for (i = 0; i < MAXEMBLEMS; ++i) + { + Z_Free(emblemlocations[i].stringVar); + emblemlocations[i].stringVar = NULL; + } + + memset(&emblemlocations, 0, sizeof(emblemlocations)); + numemblems = 0; +} + +void clear_unlockables(void) +{ + INT32 i; + + for (i = 0; i < MAXUNLOCKABLES; ++i) + { + Z_Free(unlockables[i].stringVar); + unlockables[i].stringVar = NULL; + } + + memset(&unlockables, 0, sizeof(unlockables)); +} + void clear_conditionsets(void) { UINT8 i; @@ -3017,7 +3044,12 @@ void reademblemdata(MYFILE *f, INT32 num) else if (fastcmp(word, "COLOR")) emblemlocations[num-1].color = get_number(word2); else if (fastcmp(word, "VAR")) + { + Z_Free(emblemlocations[num-1].stringVar); + emblemlocations[num-1].stringVar = Z_StrDup(word2); + emblemlocations[num-1].var = get_number(word2); + } else deh_warning("Emblem %d: unknown word '%s'", num, word); } @@ -3226,23 +3258,17 @@ void readunlockable(MYFILE *f, INT32 num) } else if (fastcmp(word, "VAR")) { - INT32 skinnum = R_SkinAvailable(word2); + Z_Free(unlockables[num].stringVar); + unlockables[num].stringVar = Z_StrDup(word2); - if (skinnum != -1) - { - unlockables[num].variable = (INT16)skinnum; - } - else - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - i = M_MapNumber(word2[0], word2[1]); + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + i = M_MapNumber(word2[0], word2[1]); - unlockables[num].variable = (INT16)i; - } + unlockables[num].variable = (INT16)i; } else deh_warning("Unlockable %d: unknown word '%s'", num+1, word); diff --git a/src/deh_soc.h b/src/deh_soc.h index 2bcb52e70..9a3b0884d 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -84,6 +84,8 @@ void readskincolor(MYFILE *f, INT32 num); void readthing(MYFILE *f, INT32 num); void readfreeslots(MYFILE *f); void readPlayer(MYFILE *f, INT32 num); +void clear_emblems(void); +void clear_unlockables(void); void clear_levels(void); void clear_conditionsets(void); #endif diff --git a/src/dehacked.c b/src/dehacked.c index c2ea28d27..a68537f77 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -562,13 +562,10 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) } if (clearall || fastcmp(word2, "UNLOCKABLES")) - memset(&unlockables, 0, sizeof(unlockables)); + clear_unlockables(); if (clearall || fastcmp(word2, "EMBLEMS")) - { - memset(&emblemlocations, 0, sizeof(emblemlocations)); - numemblems = 0; - } + clear_emblems(); if (clearall || fastcmp(word2, "EXTRAEMBLEMS")) { diff --git a/src/m_cond.c b/src/m_cond.c index 36fcd7cf2..ee8e96d64 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -496,6 +496,54 @@ UINT8 M_GotHighEnoughRings(INT32 trings) return false; } +// Gets the skin number for a SECRET_SKIN unlockable. +INT32 M_UnlockableSkinNum(unlockable_t *unlock) +{ + if (unlock->type != SECRET_SKIN) + { + // This isn't a skin unlockable... + return -1; + } + + if (unlock->stringVar && strcmp(unlock->stringVar, "")) + { + // Get the skin from the string. + return R_SkinAvailable(unlock->stringVar); + } + else if (unlock->variable >= 0 && unlock->variable < numskins) + { + // Use the number directly. + return unlock->variable; + } + + // Invalid skin unlockable. + return -1; +} + +// Gets the skin number for a ET_SKIN emblem. +INT32 M_EmblemSkinNum(emblem_t *emblem) +{ + if (emblem->type != ET_SKIN) + { + // This isn't a skin emblem... + return -1; + } + + if (emblem->stringVar && strcmp(emblem->stringVar, "")) + { + // Get the skin from the string. + return R_SkinAvailable(emblem->stringVar); + } + else if (emblem->var >= 0 && emblem->var < numskins) + { + // Use the number directly. + return emblem->var; + } + + // Invalid skin emblem. + return -1; +} + // ---------------- // Misc Emblem shit // ---------------- diff --git a/src/m_cond.h b/src/m_cond.h index 08b47c63a..8003433a7 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -92,6 +92,7 @@ typedef struct UINT8 sprite; ///< emblem sprite to use, 0 - 25 UINT16 color; ///< skincolor to use INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin) + char *stringVar; ///< String version char hint[110]; ///< Hint for emblem hints menu UINT8 collected; ///< Do you have this emblem? } emblem_t; @@ -116,6 +117,7 @@ typedef struct UINT8 showconditionset; INT16 type; INT16 variable; + char *stringVar; UINT8 nocecho; UINT8 nochecklist; UINT8 unlocked; @@ -186,4 +188,7 @@ UINT8 M_GotHighEnoughScore(INT32 tscore); UINT8 M_GotLowEnoughTime(INT32 tictime); UINT8 M_GotHighEnoughRings(INT32 trings); +INT32 M_UnlockableSkinNum(unlockable_t *unlock); +INT32 M_EmblemSkinNum(emblem_t *emblem); + #define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved) diff --git a/src/p_enemy.c b/src/p_enemy.c index 306b8b399..44dace26f 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5115,9 +5115,14 @@ static boolean SignSkinCheck(player_t *player, INT32 num) // Player invalid, only show characters that are unlocked from the start. for (i = 0; i < MAXUNLOCKABLES; i++) { - if (unlockables[i].type == SECRET_SKIN && unlockables[i].variable == num) + if (unlockables[i].type == SECRET_SKIN) { - return false; + INT32 lockedSkin = M_UnlockableSkinNum(&unlockables[i]); + + if (lockedSkin == num) + { + return false; + } } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 49db6daee..540642134 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11964,6 +11964,7 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj) INT32 j; emblem_t* emblem = M_GetLevelEmblems(gamemap); skincolornum_t emcolor; + boolean validEmblem = true; while (emblem) { @@ -11988,8 +11989,19 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj) emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting mobj->color = (UINT16)emcolor; - if (emblemlocations[j].collected - || (emblemlocations[j].type == ET_SKIN && emblemlocations[j].var != players[0].skin)) + validEmblem = !emblemlocations[j].collected; + + if (emblemlocations[j].type == ET_SKIN) + { + INT32 skinnum = M_EmblemSkinNum(&emblemlocations[j]); + + if (players[0].skin != skinnum) + { + validEmblem = false; + } + } + + if (validEmblem == false) { P_UnsetThingPosition(mobj); mobj->flags |= MF_NOCLIP; diff --git a/src/r_skins.c b/src/r_skins.c index 758d0980d..d6021ebbc 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -247,12 +247,16 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum) for (i = 0; i < MAXUNLOCKABLES; i++) { + INT32 unlockSkin = -1; + if (unlockables[i].type != SECRET_SKIN) { continue; } - if (unlockables[i].variable == skinnum) + unlockSkin = M_UnlockableSkinNum(&unlockables[i]); + + if (unlockSkin == skinnum) { unlockID = i; break; @@ -294,8 +298,7 @@ INT32 R_SkinAvailable(const char *name) return -1; } -// Gets the player to the first usuable skin in the game. (If your mod locked them all, then you kinda stupid) -void SetPlayerDefaultSkin(INT32 playernum) +INT32 GetPlayerDefaultSkin(INT32 playernum) { INT32 i; @@ -303,12 +306,18 @@ void SetPlayerDefaultSkin(INT32 playernum) { if (R_SkinUsable(playernum, i)) { - SetPlayerSkinByNum(playernum, i); - return; + return i; } } - I_Error("All characters are locked."); + I_Error("All characters are locked!"); + return 0; +} + +// Gets the player to the first usuable skin in the game. (If your mod locked them all, then you kinda stupid) +void SetPlayerDefaultSkin(INT32 playernum) +{ + SetPlayerSkinByNum(playernum, GetPlayerDefaultSkin(playernum)); } // network code calls this when a 'skin change' is received @@ -325,7 +334,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname) if (P_IsLocalPlayer(player)) CONS_Alert(CONS_WARNING, M_GetText("Skin '%s' not found.\n"), skinname); - else if(server || IsPlayerAdmin(consoleplayer)) + else if (server || IsPlayerAdmin(consoleplayer)) CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname); SetPlayerDefaultSkin(playernum); @@ -417,7 +426,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) if (P_IsLocalPlayer(player)) CONS_Alert(CONS_WARNING, M_GetText("Requested skin %d not found\n"), skinnum); - else if(server || IsPlayerAdmin(consoleplayer)) + else if (server || IsPlayerAdmin(consoleplayer)) CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum); SetPlayerDefaultSkin(playernum); diff --git a/src/r_skins.h b/src/r_skins.h index 5efd70307..675eac5cc 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -89,6 +89,8 @@ extern skin_t skins[MAXSKINS]; /// Function prototypes void R_InitSkins(void); +INT32 GetPlayerDefaultSkin(INT32 playernum); +void SetPlayerDefaultSkin(INT32 playernum); void SetPlayerSkin(INT32 playernum,const char *skinname); void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 boolean R_SkinUsable(INT32 playernum, INT32 skinnum); From 4bafd622716f74df2c5d0b71a317c27257b83b4d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 25 Apr 2021 18:44:07 -0400 Subject: [PATCH 5/6] Only return skin string number if it existed --- src/m_cond.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/m_cond.c b/src/m_cond.c index ee8e96d64..2fc730d3f 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -508,9 +508,14 @@ INT32 M_UnlockableSkinNum(unlockable_t *unlock) if (unlock->stringVar && strcmp(unlock->stringVar, "")) { // Get the skin from the string. - return R_SkinAvailable(unlock->stringVar); + INT32 skinnum = R_SkinAvailable(unlock->stringVar); + if (skinnum != -1) + { + return skinnum; + } } - else if (unlock->variable >= 0 && unlock->variable < numskins) + + if (unlock->variable >= 0 && unlock->variable < numskins) { // Use the number directly. return unlock->variable; @@ -532,9 +537,14 @@ INT32 M_EmblemSkinNum(emblem_t *emblem) if (emblem->stringVar && strcmp(emblem->stringVar, "")) { // Get the skin from the string. - return R_SkinAvailable(emblem->stringVar); + INT32 skinnum = R_SkinAvailable(emblem->stringVar); + if (skinnum != -1) + { + return skinnum; + } } - else if (emblem->var >= 0 && emblem->var < numskins) + + if (emblem->var >= 0 && emblem->var < numskins) { // Use the number directly. return emblem->var; From ec8e884a310fe7978bba4d720ce1259d918a5c8b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 8 May 2021 12:56:33 -0400 Subject: [PATCH 6/6] Removed this function --- src/r_skins.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/r_skins.h b/src/r_skins.h index 675eac5cc..70e9b49c0 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -90,7 +90,6 @@ extern skin_t skins[MAXSKINS]; void R_InitSkins(void); INT32 GetPlayerDefaultSkin(INT32 playernum); -void SetPlayerDefaultSkin(INT32 playernum); void SetPlayerSkin(INT32 playernum,const char *skinname); void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 boolean R_SkinUsable(INT32 playernum, INT32 skinnum);