Port most of SECRET_SKIN and player->availabilities changes from RR

based on 6d0637d39d
This commit is contained in:
NepDisk 2025-12-11 20:44:22 -05:00
parent 9eebf2ae67
commit 3028839f2f
18 changed files with 269 additions and 124 deletions

View file

@ -894,6 +894,9 @@ 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(), MAXAVAILABILITY*sizeof(UINT8));
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
}
@ -3759,8 +3762,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
return;
}
node = (UINT8)READUINT8(*p);
newplayernum = (UINT8)READUINT8(*p);
node = READUINT8(*p);
newplayernum = READUINT8(*p);
if (newplayernum+1 > doomcom->numslots)
doomcom->numslots = (INT16)(newplayernum+1);
@ -3771,8 +3774,13 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
console = (UINT8)READUINT8(*p);
splitscreenplayer = (UINT8)READUINT8(*p);
console = READUINT8(*p);
splitscreenplayer = READUINT8(*p);
for (i = 0; i < MAXAVAILABILITY; i++)
{
newplayer->availabilities[i] = READUINT8(*p);
}
G_AddPlayer(newplayernum, console);
@ -3892,10 +3900,10 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
K_SetBot(newplayernum, skinnum, difficulty, style);
}
static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *name2, const char *name3, const char *name4)
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, const char *name, const char *name2, const char *name3, const char *name4)
{
INT32 n, newplayernum;
UINT8 buf[4 + MAXPLAYERNAME];
INT32 n, newplayernum, i;
UINT8 buf[4 + MAXPLAYERNAME + MAXAVAILABILITY];
UINT8 *buf_p = buf;
boolean newplayer = false;
@ -3978,6 +3986,11 @@ static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *na
WRITEUINT8(buf_p, nodetoplayer[node]); // consoleplayer
WRITEUINT8(buf_p, playerpernode[node]); // splitscreen num
for (i = 0; i < MAXAVAILABILITY; i++)
{
WRITEUINT8(buf_p, availabilities[i]);
}
playerpernode[node]++;
SendNetXCmd(XD_ADDPLAYER, buf, buf_p - buf);
@ -4121,9 +4134,11 @@ 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();
SINT8 node = 0;
for (; node < MAXNETNODES; node++)
result |= SV_AddWaitingPlayers(node, 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);
}
return result;
}
@ -4217,6 +4232,7 @@ static void HandleConnect(SINT8 node)
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
INT32 i;
UINT8 availabilitiesbuffer[MAXAVAILABILITY];
// Sal: Dedicated mode is INCREDIBLY hacked together.
// If a server filled out, then it'd overwrite the host and turn everyone into weird husks.....
@ -4341,6 +4357,8 @@ static void HandleConnect(SINT8 node)
}
}
memcpy(availabilitiesbuffer, netbuffer->u.clientcfg.availabilities, sizeof(availabilitiesbuffer));
// client authorised to join
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
if (!nodeingame[node])
@ -4375,7 +4393,7 @@ static void HandleConnect(SINT8 node)
SV_SendSaveGame(node, false); // send a complete game state
DEBFILE("send savegame\n");
}
SV_AddWaitingPlayers(node, names[0], names[1], names[2], names[3]);
SV_AddWaitingPlayers(node, availabilitiesbuffer, names[0], names[1], names[2], names[3]);
joindelay += cv_joindelay.value * TICRATE;
player_joining = true;
}

View file

@ -284,6 +284,7 @@ struct clientconfig_pak
UINT8 localplayers; // number of splitscreen players
UINT8 mode;
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
UINT8 availabilities[MAXAVAILABILITY];
} ATTRPACK;
#define SV_SPEEDMASK 0x03 // used to send kartspeed

View file

@ -1174,11 +1174,6 @@ void D_StartTitle(void)
for (i = 0; i < MAXPLAYERS; i++)
CL_ClearPlayer(i);
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
}
splitscreen = 0;
SplitScreen_OnChange();

View file

@ -1723,6 +1723,10 @@ UINT8 CanChangeSkin(INT32 playernum)
static void ForceAllSkins(INT32 forcedskin)
{
INT32 i, j;
if (demo.playback)
return; // DXD_SKIN should handle all changes for us
for (i = 0; i < MAXPLAYERS; ++i)
{
if (!playeringame[i])
@ -1793,6 +1797,8 @@ static void SendNameAndColor(UINT8 n)
char buf[MAXPLAYERNAME+12];
char *p;
UINT16 i = 0;
if (splitscreen < n)
return; // can happen if skin4/color4/name4 changed
@ -1810,9 +1816,10 @@ static void SendNameAndColor(UINT8 n)
CV_StealthSetValue(&cv_playercolor[n], skins[player->skin].prefcolor);
else if (skincolors[atoi(cv_playercolor[n].defaultvalue)].accessible)
CV_StealthSet(&cv_playercolor[n], cv_playercolor[n].defaultvalue);
else {
UINT16 i = 0;
while (i<numskincolors && !skincolors[i].accessible) i++;
else
{
while (i<numskincolors && !skincolors[i].accessible)
i++;
CV_StealthSetValue(&cv_playercolor[n], (i != numskincolors) ? i : SKINCOLOR_BLUE);
}
}
@ -1842,8 +1849,6 @@ static void SendNameAndColor(UINT8 n)
&& fasticmp(cv_voice[n].string, voice->name))
return;
player->availabilities = R_GetSkinAvailabilities();
// We'll handle it later if we're not playing.
if (!Playing())
return;
@ -2063,7 +2068,6 @@ static void SendNameAndColor(UINT8 n)
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME);
WRITEUINT32(p, (UINT32)player->availabilities);
WRITEUINT16(p, (UINT16)cv_playercolor[n].value);
WRITEUINT16(p, (UINT16)cv_skin[n].value);
WRITEINT32(p, (INT32)cv_follower[n].value);
@ -2105,7 +2109,6 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
}
READSTRINGN(*cp, name, MAXPLAYERNAME);
p->availabilities = READUINT32(*cp);
color = READUINT16(*cp);
skin = READUINT16(*cp);
follower = READINT32(*cp);
@ -2143,36 +2146,6 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
if (skincolors[p->skincolor].accessible == false)
kick = true;
// availabilities
// TODO: Port commit from RR: 6d0637d3
/*
for (i = 0; i < MAXSKINS; i++)
{
UINT32 playerhasunlocked = (p->availabilities & (1 << i));
boolean islocked = false;
UINT8 j;
for (j = 0; j < MAXUNLOCKABLES; j++)
{
if (unlockables[j].type != SECRET_SKIN)
continue;
if (unlockables[j].variable == i)
{
islocked = true;
break;
}
}
if (islocked == false && playerhasunlocked == true)
{
// hacked client that enabled every bit
kick = true;
break;
}
}
*/
if (kick)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s (team: %d), color: %d)\n"), player_names[playernum], p->ctfteam, p->skincolor);
@ -2182,7 +2155,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
}
// set skin
if (cv_forceskin.value >= 0 && (netgame || multiplayer)) // Server wants everyone to use the same player
if (cv_forceskin.value >= 0 && K_CanChangeRules(true)) // Server wants everyone to use the same player
{
const INT32 forcedskin = cv_forceskin.value;
SetPlayerSkinByNum(playernum, forcedskin);
@ -6631,11 +6604,6 @@ void Command_ExitGame_f(void)
for (i = 0; i < MAXPLAYERS; i++)
CL_ClearPlayer(i);
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
}
splitscreen = 0;
SplitScreen_OnChange();

View file

@ -498,7 +498,7 @@ struct player_t
INT32 skin;
UINT16 voice_id;
UINT32 availabilities;
UINT8 availabilities[MAXAVAILABILITY];
UINT8 kartspeed; // Kart speed stat between 1 and 9
UINT8 kartweight; // Kart weight stat between 1 and 9

View file

@ -134,6 +134,36 @@ 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].level);
emblemlocations[i].level = NULL;
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;
@ -2679,7 +2709,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);
}
@ -2883,7 +2918,8 @@ void readunlockable(MYFILE *f, INT32 num)
}
else if (fastcmp(word, "VAR"))
{
// TODO: different field for level name string
Z_Free(unlockables[num].stringVar);
unlockables[num].stringVar = Z_StrDup(word2);
unlockables[num].variable = (INT16)G_MapNumber(word2);
}
else

View file

@ -86,6 +86,8 @@ void readlight(MYFILE *f, INT32 num);
void readskincolor(MYFILE *f, INT32 num, boolean mainfile);
void readthing(MYFILE *f, INT32 num);
void readfreeslots(MYFILE *f);
void clear_emblems(void);
void clear_unlockables(void);
void clear_levels(void);
void clear_conditionsets(void);

View file

@ -846,12 +846,13 @@ 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

@ -199,6 +199,8 @@ extern char logfilename[1024];
#define MAXSKINS 4096
#define MAXFOLLOWERS UINT16_MAX
#define MAXSKINUNAVAILABLE 128
#define MAXAVAILABILITY MAXSKINUNAVAILABLE/8
#define COLORRAMPSIZE 16
#define MAXCOLORNAME 32

View file

@ -110,7 +110,7 @@ demoghost *ghosts = NULL;
// DEMO RECORDING
//
#define DEMOVERSION 0x000E
#define DEMOVERSION 0x000F
#define DEMOHEADER "\xF0" "BlanReplay" "\x0F"
#define DF_GHOST 0x01 // This demo contains ghost data too!
@ -228,6 +228,7 @@ typedef struct
UINT8 kartspeed;
UINT8 kartweight;
mobjtype_t followitem;
UINT8 availabilities[MAXAVAILABILITY];
} demoplayer_t;
typedef struct
@ -550,6 +551,7 @@ static void G_FreeExtraZipTic(ziptic_t *zt)
static UINT8 *G_ReadRawExtraData(extradata_t *extra, UINT8 *dp, UINT16 version)
{
INT32 i;
UINT8 extradata;
extra->playernum = READUINT8(dp);
boolean kart = version <= 0x0002;
@ -591,6 +593,14 @@ static UINT8 *G_ReadRawExtraData(extradata_t *extra, UINT8 *dp, UINT16 version)
if (extradata & DXD_JOINDATA)
{
if (version >= 0x000F)
{
for (i = 0; i < MAXAVAILABILITY; i++)
{
players[extra->playernum].availabilities[i] = READUINT8(dp);
}
}
extra->joindata.bot = !!(READUINT8(dp));
if (extra->joindata.bot)
{
@ -693,6 +703,8 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
boolean serverinfo = true;
boolean rapreset = true; // + extended serverinfo length
boolean dubs = true; // Multiple voices
boolean availabilities = true; // Store player availabilities
INT32 i;
// these may not be present in old demo formats, so initialize them
// also initialize them so the header can be free'd without issues
@ -730,6 +742,8 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
/* FALLTHRU */
case 0x000D:
dubs = false;
case 0x000E:
availabilities = false;
break;
default: // too old, cannot support.
@ -739,7 +753,7 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
else if (!memcmp(startdp, "\xF0" "KartReplay" "\x0F", 12))
{
dubs = rapreset = raflag = false;
serverinfo = false;
serverinfo = availabilities = false;
switch (header->demoversion)
{
case 0x0001: // SRB2Kart 1.0.x (only staff ghosts supported)
@ -832,7 +846,7 @@ readfiles:
header->numfiles = READUINT8(dp);
if (header->numfiles)
header->files = malloc(sizeof(demofile_t) * header->numfiles);
for (UINT8 i = 0; i < header->numfiles; i++)
for (i = 0; i < header->numfiles; i++)
{
char filename[MAX_WADPATH];
READSTRINGL(dp, filename, MAX_WADPATH);
@ -891,7 +905,7 @@ skipfiles:
header->numcvars = READUINT16(dp);
if (header->numcvars)
header->cvars = malloc(sizeof(democvar_t) * header->numcvars);
for (UINT16 i = 0; i < header->numcvars; i++)
for (i = 0; i < header->numcvars; i++)
{
if (kart)
{
@ -994,6 +1008,11 @@ skipfiles:
plr->kartspeed = READUINT8(dp);
plr->kartweight = READUINT8(dp);
plr->followitem = !kart ? READUINT32(dp) : MT_NULL;
for (i = 0; i < MAXAVAILABILITY; i++)
{
plr->availabilities[i] = availabilities ? READUINT8(dp) : 0;
}
}
// Sigh ... it's an empty demo. Again.
@ -1342,7 +1361,7 @@ void G_ReadDemoExtraData(void)
void G_WriteDemoExtraData(void)
{
INT32 i;
INT32 i, j;
for (i = 0; i < MAXPLAYERS; i++)
{
if (demo_extradata[i])
@ -1352,6 +1371,11 @@ void G_WriteDemoExtraData(void)
if (demo_extradata[i] & DXD_JOINDATA)
{
for (j = 0; j < MAXAVAILABILITY; j++)
{
WRITEUINT8(demobuf.p, players[i].availabilities[i]);
}
WRITEUINT8(demobuf.p, (UINT8)players[i].bot);
if (players[i].bot)
{
@ -2821,7 +2845,7 @@ void G_RecordMetal(void)
void G_BeginRecording(void)
{
UINT8 i, p;
UINT8 i, j, p;
char name[MAXCOLORNAME+1];
player_t *player = &players[consoleplayer];
@ -3005,6 +3029,11 @@ void G_BeginRecording(void)
// And mobjtype_t is best with UINT32 too...
WRITEUINT32(demobuf.p, player->followitem);
for (j = 0; j < MAXAVAILABILITY; j++)
{
WRITEUINT8(demobuf.p, player->availabilities[j]);
}
}
}
@ -3895,6 +3924,11 @@ void G_DoPlayDemo(char *defdemoname)
// Followitem
player->followitem = plr->followitem;
for (i = 0; i < MAXAVAILABILITY; i++)
{
player->availabilities[i] = plr->availabilities[i];
}
}
// end of player read (the 0xFF marker)

View file

@ -2713,7 +2713,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
UINT16 skincolor;
INT32 skin;
UINT16 voice;
UINT32 availabilities;
UINT8 availabilities[MAXAVAILABILITY];
tic_t jointime;
@ -2783,7 +2783,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
jitterlegacy = players[player].jitterlegacy;
availabilities = players[player].availabilities;
memcpy(availabilities, players[player].availabilities, sizeof(availabilities));
charflags = players[player].charflags;
@ -2967,7 +2967,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->kartweight = kartweight;
//
p->charflags = charflags;
p->availabilities = availabilities;
memcpy(players[player].availabilities, availabilities, sizeof(availabilities));
p->followitem = followitem;
p->starpostx = starpostx;

View file

@ -147,6 +147,7 @@ void K_SetNameForBot(UINT8 newplayernum, const char *realname)
--------------------------------------------------*/
void K_SetBot(UINT8 newplayernum, UINT16 skinnum, UINT8 difficulty, botStyle_e style)
{
INT32 i;
CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum);
G_AddPlayer(newplayernum, newplayernum);
@ -154,6 +155,15 @@ void K_SetBot(UINT8 newplayernum, UINT16 skinnum, UINT8 difficulty, botStyle_e s
if (newplayernum+1 > doomcom->numslots)
doomcom->numslots = (INT16)(newplayernum+1);
// 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];
}
}
playernode[newplayernum] = servernode;
players[newplayernum].splitscreenindex = 0;

View file

@ -1191,9 +1191,9 @@ static int player_get(lua_State *L)
case player_voice_id:
lua_pushinteger(L, plr->voice_id);
break;
case player_availabilities:
/*case player_availabilities:
lua_pushinteger(L, plr->availabilities);
break;
break;*/
case player_score:
lua_pushinteger(L, plr->score);
break;
@ -1518,8 +1518,8 @@ static int player_set(lua_State *L)
return NOSET;
case player_voice_id:
return NOSET;
case player_availabilities:
return NOSET;
/*case player_availabilities:
return NOSET;*/
case player_score:
plr->score = luaL_checkinteger(L, 3);
break;

View file

@ -48,17 +48,17 @@ extraemblem_t extraemblems[MAXEXTRAEMBLEMS] =
// Default Unlockables
unlockable_t unlockables[MAXUNLOCKABLES] =
{
// Name, Objective, Showing Conditionset, ConditionSet, Unlock Type, Variable, NoCecho, NoChecklist
/* 01 */ {"Egg Cup", "", -1, 1, SECRET_NONE, 0, false, false, 0},
/* 02 */ {"Chao Cup", "", -1, 2, SECRET_NONE, 0, false, false, 0},
/* 03 */ {"SMK Cup", "", 2, 3, SECRET_NONE, 0, false, false, 0},
// Name, Objective, Showing Conditionset, ConditionSet, Unlock Type, Variable, NoCecho, NoChecklist, Unlocked
/* 01 */ {"Egg Cup", "", -1, 1, SECRET_NONE, 0, "", false, false, 0},
/* 02 */ {"Chao Cup", "", -1, 2, SECRET_NONE, 0, "", false, false, 0},
/* 03 */ {"SMK Cup", "", 2, 3, SECRET_NONE, 0, "", false, false, 0},
/* 04 */ {"Hard Game Speed", "", -1, 4, SECRET_HARDSPEED, 0, false, false, 0},
/* 05 */ {"Encore Mode", "", 4, 5, SECRET_ENCORE, 0, false, false, 0},
/* 06 */ {"Hell Attack", "", 6, 6, SECRET_HELLATTACK, 0, false, false, 0},
/* 04 */ {"Hard Game Speed", "", -1, 4, SECRET_HARDSPEED, 0, "", false, false, 0},
/* 05 */ {"Encore Mode", "", 4, 5, SECRET_ENCORE, 0, "", false, false, 0},
/* 06 */ {"Hell Attack", "", 6, 6, SECRET_HELLATTACK, 0, "", false, false, 0},
/* 07 */ {"Record Attack", "", -1, -1, SECRET_TIMEATTACK, 0, true, true, 0},
/* 08 */ {"Capsule Attack", "", -1, -1, SECRET_ITEMBREAKER, 0, true, true, 0},
/* 07 */ {"Record Attack", "", -1, -1, SECRET_TIMEATTACK, 0, "" ,true, true, 0},
/* 08 */ {"Capsule Attack", "", -1, -1, SECRET_ITEMBREAKER, 0, "", true, true, 0},
};
// Number of emblems and extra emblems
@ -344,11 +344,6 @@ void M_SilentUpdateUnlockablesAndEmblems(void)
continue;
unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
}
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
}
}
// Emblem unlocking shit
@ -555,6 +550,35 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
*/
}
// 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;
}
// ----------------
// Misc Emblem shit
// ----------------

View file

@ -84,6 +84,7 @@ struct emblem_t
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?
};
@ -107,6 +108,7 @@ struct unlockable_t
UINT8 showconditionset;
INT16 type;
INT16 variable;
char *stringVar;
UINT8 nocecho;
UINT8 nochecklist;
UINT8 unlocked;
@ -185,6 +187,9 @@ const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big);
UINT8 M_GotEnoughEmblems(INT32 number);
UINT8 M_GotLowEnoughTime(INT32 tictime);
INT32 M_UnlockableSkinNum(unlockable_t *unlock);
INT32 M_EmblemSkinNum(emblem_t *emblem);
// vvvvvvvvvv anti-integer promotion, do not remove
#define M_Achieved(a) ((unsigned)(a) >= MAXCONDITIONSETS || conditionSets[a].achieved)

View file

@ -477,7 +477,12 @@ static void P_NetSyncPlayers(savebuffer_t *save)
SYNC(players[i].skincolor);
SYNC(players[i].skin);
SYNC(players[i].voice_id);
SYNC(players[i].availabilities);
for (j = 0; j < MAXAVAILABILITY; j++)
{
SYNC(players[i].availabilities[j]);
}
SYNC(players[i].score);
SYNC(players[i].lives);
SYNC(players[i].xtralife);

View file

@ -36,6 +36,8 @@
#include "hardware/hw_md2.h"
#endif
#include "k_grandprix.h"
#include "discord.h"
INT32 numskins = 0;
@ -305,21 +307,34 @@ void R_InitSkins(void)
ST_ReloadSkinFaceGraphics();
}
UINT32 R_GetSkinAvailabilities(void)
UINT8 *R_GetSkinAvailabilities(void)
{
UINT8 i;
UINT32 response = 0;
UINT8 i, shif, byte;
INT32 skinid;
static UINT8 responsebuffer[MAXAVAILABILITY];
memset(&responsebuffer, 0, sizeof(responsebuffer));
for (i = 0; i < MAXUNLOCKABLES; i++)
{
if (unlockables[i].type == SECRET_SKIN && unlockables[i].unlocked)
{
UINT16 s = min(unlockables[i].variable, MAXSKINS);
response |= (1 << s);
}
if (unlockables[i].type != SECRET_SKIN)
continue;
if (unlockables[i].unlocked != true)
continue;
skinid = M_UnlockableSkinNum(&unlockables[i]);
if (skinid < 0 || skinid >= MAXSKINUNAVAILABLE)
continue;
shif = (skinid % 8);
byte = (skinid / 8);
responsebuffer[byte] |= (1 << shif);
}
return response;
return responsebuffer;
}
//
@ -461,7 +476,9 @@ INT32 FindSortedSkinIndex(INT32 skinnum)
boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
{
boolean needsunlocked = false;
boolean useplayerstruct = (Playing() && playernum != -1);
UINT8 i;
INT32 skinid;
if (skinnum == -1)
{
@ -475,7 +492,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
return true;
}
if (netgame && (cv_forceskin.value == skinnum))
if (K_CanChangeRules(true) && (cv_forceskin.value == skinnum))
{
// Being forced to play as this character by the server
return true;
@ -488,36 +505,45 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
return (skinnum == metalskin);
}
if (skinnum >= MAXSKINUNAVAILABLE)
{
// Keeping our packet size nice and sane in the wake of MAXSKINS increase, as suggested by toaster
return true;
}
// Determine if this character is supposed to be unlockable or not
for (i = 0; i < MAXUNLOCKABLES; i++)
{
if (unlockables[i].type == SECRET_SKIN && unlockables[i].variable == skinnum)
{
// i is now the unlockable index, we can use this later
needsunlocked = true;
break;
}
if (unlockables[i].type != SECRET_SKIN)
continue;
skinid = M_UnlockableSkinNum(&unlockables[i]);
if (skinid != skinnum)
continue;
// i is now the unlockable index, we can use this later
needsunlocked = true;
break;
}
if (needsunlocked == true)
{
// You can use this character IF you have it unlocked.
if ((netgame || multiplayer) && playernum != -1)
{
// Use the netgame synchronized unlocks.
return (boolean)(!(players[playernum].availabilities & (1 << skinnum)));
}
else
{
// Use the unlockables table directly
return (boolean)(unlockables[i].unlocked);
}
}
else
if (needsunlocked == false)
{
// Didn't trip anything, so we can use this character.
return true;
}
// Ok, you can use this character IF you have it unlocked.
if (useplayerstruct)
{
// Use the netgame synchronized unlocks.
UINT8 shif = (skinnum % 8);
UINT8 byte = (skinnum / 8);
return !!(players[playernum].availabilities[byte] & (1 << shif));
}
// Use the unlockables table directly
return (boolean)(unlockables[i].unlocked);
}
// returns true if the skin name is found (loaded from pwad)
@ -535,6 +561,24 @@ 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)
static 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)
{
@ -552,7 +596,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);
SetPlayerSkinByNum(playernum, GetPlayerDefaultSkin(playernum));
}
// Same as SetPlayerSkin, but uses the skin #.
@ -621,7 +665,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 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 in the default skin
SetPlayerSkinByNum(playernum, GetPlayerDefaultSkin(playernum)); // not found, put in the default skin
}
// Gets the corresponding voice ID for a given voice's name.

View file

@ -140,7 +140,7 @@ INT32 FindSortedSkinIndex(INT32 skinnum);
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);
UINT8 *R_GetSkinAvailabilities(void);
INT32 R_SkinAvailable(const char *name);
void R_PatchSkins(UINT16 wadnum);
void R_AddSkins(UINT16 wadnum);