Merge branch 'skin-unlocks-sanity' into 'next'

[SUGOI] Remove skin->availability, add SECRET_SKIN

See merge request STJr/SRB2!1474
This commit is contained in:
SteelT 2021-05-28 21:30:16 -04:00
commit a72d6bba22
14 changed files with 311 additions and 59 deletions

View file

@ -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.
@ -1475,7 +1476,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 +1493,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;
}
}

View file

@ -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;
@ -2978,7 +3005,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);
}
@ -3180,11 +3212,16 @@ 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"))
{
Z_Free(unlockables[num].stringVar);
unlockables[num].stringVar = Z_StrDup(word2);
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.

View file

@ -81,6 +81,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

View file

@ -547,13 +547,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"))
{

View file

@ -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_PushUserdata(L, skin->sprites, META_SKINSPRITES);
break;

View file

@ -496,6 +496,64 @@ 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.
INT32 skinnum = R_SkinAvailable(unlock->stringVar);
if (skinnum != -1)
{
return skinnum;
}
}
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.
INT32 skinnum = R_SkinAvailable(emblem->stringVar);
if (skinnum != -1)
{
return skinnum;
}
}
if (emblem->var >= 0 && emblem->var < numskins)
{
// Use the number directly.
return emblem->var;
}
// Invalid skin emblem.
return -1;
}
// ----------------
// Misc Emblem shit
// ----------------

View file

@ -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;
@ -132,6 +134,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.
@ -185,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)

View file

@ -8963,7 +8963,7 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum)
static UINT8 M_SetupChoosePlayerDirect(INT32 choice)
{
INT32 skinnum;
INT32 skinnum, botskinnum;
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)))
{
botskinnum = description[i].skinnum[1];
if ((botskinnum != -1) && (!R_SkinUsable(-1, botskinnum)))
{
// Bot skin isn't unlocked
continue;
}
// Handling order.
if (firstvalid == 255)
firstvalid = i;

View file

@ -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,33 @@ 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)
{
INT32 lockedSkin = M_UnlockableSkinNum(&unlockables[i]);
if (lockedSkin == 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 +5189,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];

View file

@ -11962,6 +11962,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)
{
@ -11986,8 +11987,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;

View file

@ -4576,8 +4576,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();
//

View file

@ -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,78 @@ 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++)
{
INT32 unlockSkin = -1;
if (unlockables[i].type != SECRET_SKIN)
{
continue;
}
unlockSkin = M_UnlockableSkinNum(&unlockables[i]);
if (unlockSkin == 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)
@ -295,6 +377,24 @@ static void SetSkin(player_t *player, INT32 skinnum)
}
}
// Gets the player to the first usuable skin in the game.
// (If your mod locked them all, then you kinda stupid)
INT32 GetPlayerDefaultSkin(INT32 playernum)
{
INT32 i;
for (i = 0; i < numskins; i++)
{
if (R_SkinUsable(playernum, i))
{
return i;
}
}
I_Error("All characters are locked!");
return 0;
}
// network code calls this when a 'skin change' is received
void SetPlayerSkin(INT32 playernum, const char *skinname)
{
@ -309,10 +409,10 @@ 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);
SetSkin(player, 0);
SetSkin(player, GetPlayerDefaultSkin(playernum));
}
// Same as SetPlayerSkin, but uses the skin #.
@ -329,10 +429,10 @@ 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);
SetSkin(player, 0); // not found put the sonic skin
SetSkin(player, GetPlayerDefaultSkin(playernum));
}
//
@ -566,7 +666,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;
@ -681,12 +781,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);
@ -701,7 +795,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;
@ -721,7 +815,7 @@ next_token:
//
// Patch skin sprites
//
void R_PatchSkins(UINT16 wadnum)
void R_PatchSkins(UINT16 wadnum, boolean mainfile)
{
UINT16 lump, lastlump = 0;
char *buf;
@ -834,7 +928,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;

View file

@ -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
@ -91,13 +89,14 @@ extern skin_t skins[MAXSKINS];
/// Function prototypes
void R_InitSkins(void);
INT32 GetPlayerDefaultSkin(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);
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);

View file

@ -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();