mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-04-27 04:21:47 +00:00
Unlockable skins (in a way friendly to #define MAXSKINS 255)
Mammoth commit, sorry. I only realised halfway through writing it that SECRET_SKIN was only partially merged. Ports from 2.2: - Merge SECRET_SKIN (STJr/SRB2!1474) - Default skin is now handled by checking all skins for unlock status, and I_Erroring if none are available - Don't show skin names on game startup, to keep our secrets hidden - Unlockables now have string variables zallocated. - For skin names rather than numbers. - Correctly clean up memory when freeing unlockables and emblems. Bespoke code: - For temporary testing. `unlocks.pk3` - Using this for rapid testing gameboot SOC instead of patch.pk3 because of the intent to turn that into scripts.pk3 - Don't not save gamedata in DEVELOP builds, even if you've used cheats! - `player->availabilities` is now an array of UINT8 - (MAXSKINS + 7)/8 entries, or 32 bytes. - Included with XD_ADDPLAYER instead of XD_NAMEANDCOLOR. - Simplifies a lot of logic with respect to demos, skin changes mid-game, etc. - Seriously, there's a lot of random places in the code that just iterate over MAXSPLITSCREENPLAYERS and g_localplayers to update availabilities in real time in a way that's not particularly netsafe... - Lines up with the plan for handling unlocks when returning to menus. - Was included with XD_ADDBOT, but that actually overruns the netxcmd buffer at first mapload with 7 bots. We might need to consider expanding the size of the netxcmd buffer... - In demos, can be interpreted as both relative to the original replay and the current skin list depending on boolean context provided to R_SkinUsable. - Used for SF_IRONMAN (and will crash if all other skins are inaccessible). - Grand Prix bot randomisation uses the host's unlocks. - Don't show locked characters on the fancy new character select. - DXD_JOINDATA for demos - Replaces the dual-purpose behaviour of DXD_PLAYSTATE - Contains availabilities - Handles bot material in a different way - Forceskin restrictions - Won't run in demos, because it's assumed recorded DXD_SKIN will handle all the conversions the original match had - Won't run if K_CanChangeRules says no - Correctly set `mapvisited` on level visit, even in [fake gasp] MULTIPLAYER/NETGAMES!! 🥹 - Added updating unlockables and extra emblems on `mapvisited` update. - Currently fails to produce the cecho, but that'll be stripped out entirely in a future commit so I'm not bothered.
This commit is contained in:
parent
8179b39773
commit
6d0637d39d
24 changed files with 377 additions and 198 deletions
|
|
@ -1481,7 +1481,7 @@ boolean CV_CompleteValue(consvar_t *var, const char **valstrp, INT32 *intval)
|
||||||
{
|
{
|
||||||
v = R_SkinAvailable(valstr);
|
v = R_SkinAvailable(valstr);
|
||||||
|
|
||||||
if (!R_SkinUsable(-1, v))
|
if (!R_SkinUsable(-1, v, false))
|
||||||
v = -1;
|
v = -1;
|
||||||
|
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
@ -1984,7 +1984,7 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
|
||||||
if (var == &cv_forceskin)
|
if (var == &cv_forceskin)
|
||||||
{
|
{
|
||||||
INT32 skin = R_SkinAvailable(value);
|
INT32 skin = R_SkinAvailable(value);
|
||||||
if ((stricmp(value, "None")) && ((skin == -1) || !R_SkinUsable(-1, skin)))
|
if ((stricmp(value, "None")) && ((skin == -1) || !R_SkinUsable(-1, skin, false)))
|
||||||
{
|
{
|
||||||
CONS_Printf("Please provide a valid skin name (\"None\" disables).\n");
|
CONS_Printf("Please provide a valid skin name (\"None\" disables).\n");
|
||||||
return;
|
return;
|
||||||
|
|
@ -2109,7 +2109,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
|
||||||
else if (newvalue >= numskins)
|
else if (newvalue >= numskins)
|
||||||
newvalue = -1;
|
newvalue = -1;
|
||||||
} while ((oldvalue != newvalue)
|
} while ((oldvalue != newvalue)
|
||||||
&& !(R_SkinUsable(-1, newvalue)));
|
&& !(R_SkinUsable(-1, newvalue, false)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
newvalue = var->value + increment;
|
newvalue = var->value + increment;
|
||||||
|
|
|
||||||
|
|
@ -824,6 +824,8 @@ static boolean CL_SendJoin(void)
|
||||||
for (; i < MAXSPLITSCREENPLAYERS; i++)
|
for (; i < MAXSPLITSCREENPLAYERS; i++)
|
||||||
strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME);
|
strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME);
|
||||||
|
|
||||||
|
memcpy(&netbuffer->u.clientcfg.availabilities, R_GetSkinAvailabilities(false), MAXAVAILABILITY*sizeof(UINT8));
|
||||||
|
|
||||||
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
|
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3565,8 +3567,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = (UINT8)READUINT8(*p);
|
node = READUINT8(*p);
|
||||||
newplayernum = (UINT8)READUINT8(*p);
|
newplayernum = READUINT8(*p);
|
||||||
|
|
||||||
CONS_Debug(DBG_NETPLAY, "addplayer: %d %d\n", node, newplayernum);
|
CONS_Debug(DBG_NETPLAY, "addplayer: %d %d\n", node, newplayernum);
|
||||||
|
|
||||||
|
|
@ -3587,8 +3589,13 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
||||||
|
|
||||||
READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
|
READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
|
||||||
|
|
||||||
console = (UINT8)READUINT8(*p);
|
console = READUINT8(*p);
|
||||||
splitscreenplayer = (UINT8)READUINT8(*p);
|
splitscreenplayer = READUINT8(*p);
|
||||||
|
|
||||||
|
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||||
|
{
|
||||||
|
newplayer->availabilities[i] = READUINT8(*p);
|
||||||
|
}
|
||||||
|
|
||||||
// the server is creating my player
|
// the server is creating my player
|
||||||
if (node == mynode)
|
if (node == mynode)
|
||||||
|
|
@ -3690,8 +3697,9 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
|
||||||
static void Got_AddBot(UINT8 **p, INT32 playernum)
|
static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||||
{
|
{
|
||||||
INT16 newplayernum;
|
INT16 newplayernum;
|
||||||
UINT8 skinnum = 0;
|
UINT8 i, skinnum = 0;
|
||||||
UINT8 difficulty = DIFFICULTBOT;
|
UINT8 difficulty = DIFFICULTBOT;
|
||||||
|
UINT8 availabilitiesbuffer[MAXAVAILABILITY];
|
||||||
|
|
||||||
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
|
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
|
||||||
{
|
{
|
||||||
|
|
@ -3704,9 +3712,9 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
newplayernum = (UINT8)READUINT8(*p);
|
newplayernum = READUINT8(*p);
|
||||||
skinnum = (UINT8)READUINT8(*p);
|
skinnum = READUINT8(*p);
|
||||||
difficulty = (UINT8)READUINT8(*p);
|
difficulty = READUINT8(*p);
|
||||||
|
|
||||||
CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum);
|
CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum);
|
||||||
|
|
||||||
|
|
@ -3720,6 +3728,15 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||||
|
|
||||||
playernode[newplayernum] = servernode;
|
playernode[newplayernum] = servernode;
|
||||||
|
|
||||||
|
// todo find a way to have all auto unlocked for dedicated
|
||||||
|
if (playeringame[0])
|
||||||
|
{
|
||||||
|
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||||
|
{
|
||||||
|
players[newplayernum].availabilities[i] = players[0].availabilities[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
players[newplayernum].splitscreenindex = 0;
|
players[newplayernum].splitscreenindex = 0;
|
||||||
players[newplayernum].bot = true;
|
players[newplayernum].bot = true;
|
||||||
players[newplayernum].botvars.difficulty = difficulty;
|
players[newplayernum].botvars.difficulty = difficulty;
|
||||||
|
|
@ -3737,10 +3754,10 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||||
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
|
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
INT32 n, newplayernum, i;
|
||||||
UINT8 buf[4 + MAXPLAYERNAME];
|
UINT8 buf[4 + MAXPLAYERNAME + MAXAVAILABILITY];
|
||||||
UINT8 *buf_p = buf;
|
UINT8 *buf_p = buf;
|
||||||
boolean newplayer = false;
|
boolean newplayer = false;
|
||||||
|
|
||||||
|
|
@ -3823,6 +3840,11 @@ static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *na
|
||||||
WRITEUINT8(buf_p, nodetoplayer[node]); // consoleplayer
|
WRITEUINT8(buf_p, nodetoplayer[node]); // consoleplayer
|
||||||
WRITEUINT8(buf_p, playerpernode[node]); // splitscreen num
|
WRITEUINT8(buf_p, playerpernode[node]); // splitscreen num
|
||||||
|
|
||||||
|
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||||
|
{
|
||||||
|
WRITEUINT8(buf_p, availabilities[i]);
|
||||||
|
}
|
||||||
|
|
||||||
playerpernode[node]++;
|
playerpernode[node]++;
|
||||||
|
|
||||||
SendNetXCmd(XD_ADDPLAYER, buf, buf_p - buf);
|
SendNetXCmd(XD_ADDPLAYER, buf, buf_p - buf);
|
||||||
|
|
@ -3886,9 +3908,10 @@ boolean SV_SpawnServer(void)
|
||||||
// strictly speaking, i'm not convinced the following is necessary
|
// 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
|
// but I'm not confident enough to remove it entirely in case it breaks something
|
||||||
{
|
{
|
||||||
|
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false);
|
||||||
SINT8 node = 0;
|
SINT8 node = 0;
|
||||||
for (; node < MAXNETNODES; node++)
|
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;
|
return result;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -3971,6 +3994,7 @@ static void HandleConnect(SINT8 node)
|
||||||
{
|
{
|
||||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
|
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
|
||||||
INT32 i;
|
INT32 i;
|
||||||
|
UINT8 availabilitiesbuffer[MAXAVAILABILITY];
|
||||||
|
|
||||||
// Sal: Dedicated mode is INCREDIBLY hacked together.
|
// Sal: Dedicated mode is INCREDIBLY hacked together.
|
||||||
// If a server filled out, then it'd overwrite the host and turn everyone into weird husks.....
|
// If a server filled out, then it'd overwrite the host and turn everyone into weird husks.....
|
||||||
|
|
@ -4077,6 +4101,8 @@ static void HandleConnect(SINT8 node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(availabilitiesbuffer, netbuffer->u.clientcfg.availabilities, sizeof(availabilitiesbuffer));
|
||||||
|
|
||||||
// client authorised to join
|
// client authorised to join
|
||||||
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
|
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
|
||||||
if (!nodeingame[node])
|
if (!nodeingame[node])
|
||||||
|
|
@ -4111,7 +4137,8 @@ static void HandleConnect(SINT8 node)
|
||||||
SV_SendSaveGame(node, false); // send a complete game state
|
SV_SendSaveGame(node, false); // send a complete game state
|
||||||
DEBFILE("send savegame\n");
|
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;
|
joindelay += cv_joindelay.value * TICRATE;
|
||||||
player_joining = true;
|
player_joining = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -246,6 +246,7 @@ typedef struct
|
||||||
UINT8 localplayers; // number of splitscreen players
|
UINT8 localplayers; // number of splitscreen players
|
||||||
UINT8 mode;
|
UINT8 mode;
|
||||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
|
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
|
||||||
|
UINT8 availabilities[MAXAVAILABILITY];
|
||||||
} ATTRPACK clientconfig_pak;
|
} ATTRPACK clientconfig_pak;
|
||||||
|
|
||||||
#define SV_SPEEDMASK 0x03 // used to send kartspeed
|
#define SV_SPEEDMASK 0x03 // used to send kartspeed
|
||||||
|
|
|
||||||
12
src/d_main.c
12
src/d_main.c
|
|
@ -950,11 +950,6 @@ void D_StartTitle(void)
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
CL_ClearPlayer(i);
|
CL_ClearPlayer(i);
|
||||||
|
|
||||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
||||||
{
|
|
||||||
players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
|
|
||||||
}
|
|
||||||
|
|
||||||
splitscreen = 0;
|
splitscreen = 0;
|
||||||
SplitScreen_OnChange();
|
SplitScreen_OnChange();
|
||||||
|
|
||||||
|
|
@ -1148,6 +1143,10 @@ static void IdentifyVersion(void)
|
||||||
#ifdef USE_PATCH_FILE
|
#ifdef USE_PATCH_FILE
|
||||||
D_AddFile(startupiwads, va(pandf,srb2waddir,PATCHNAME));
|
D_AddFile(startupiwads, va(pandf,srb2waddir,PATCHNAME));
|
||||||
#endif
|
#endif
|
||||||
|
#define UNLOCKTESTING
|
||||||
|
#if defined(DEVELOP) && defined(UNLOCKTESTING)
|
||||||
|
D_AddFile(startupiwads, va(pandf,srb2waddir,"unlocks.pk3"));
|
||||||
|
#endif
|
||||||
////
|
////
|
||||||
#undef TEXTURESNAME
|
#undef TEXTURESNAME
|
||||||
#undef MAPSNAME
|
#undef MAPSNAME
|
||||||
|
|
@ -1449,6 +1448,9 @@ void D_SRB2Main(void)
|
||||||
#ifdef USE_PATCH_FILE
|
#ifdef USE_PATCH_FILE
|
||||||
mainwads++; // patch.pk3
|
mainwads++; // patch.pk3
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef UNLOCKTESTING
|
||||||
|
mainwads++; // unlocks.pk3
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif //ifndef DEVELOP
|
#endif //ifndef DEVELOP
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1408,6 +1408,10 @@ boolean CanChangeSkinWhilePlaying(INT32 playernum)
|
||||||
static void ForceAllSkins(INT32 forcedskin)
|
static void ForceAllSkins(INT32 forcedskin)
|
||||||
{
|
{
|
||||||
INT32 i;
|
INT32 i;
|
||||||
|
|
||||||
|
if (demo.playback)
|
||||||
|
return; // DXD_SKIN should handle all changes for us
|
||||||
|
|
||||||
for (i = 0; i < MAXPLAYERS; ++i)
|
for (i = 0; i < MAXPLAYERS; ++i)
|
||||||
{
|
{
|
||||||
if (!playeringame[i])
|
if (!playeringame[i])
|
||||||
|
|
@ -1476,6 +1480,8 @@ static void SendNameAndColor(UINT8 n)
|
||||||
char buf[MAXPLAYERNAME+12];
|
char buf[MAXPLAYERNAME+12];
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
|
UINT16 i = 0;
|
||||||
|
|
||||||
if (splitscreen < n)
|
if (splitscreen < n)
|
||||||
return; // can happen if skin4/color4/name4 changed
|
return; // can happen if skin4/color4/name4 changed
|
||||||
|
|
||||||
|
|
@ -1493,9 +1499,10 @@ static void SendNameAndColor(UINT8 n)
|
||||||
CV_StealthSetValue(&cv_playercolor[n], skins[player->skin].prefcolor);
|
CV_StealthSetValue(&cv_playercolor[n], skins[player->skin].prefcolor);
|
||||||
else if (skincolors[atoi(cv_playercolor[n].defaultvalue)].accessible)
|
else if (skincolors[atoi(cv_playercolor[n].defaultvalue)].accessible)
|
||||||
CV_StealthSet(&cv_playercolor[n], cv_playercolor[n].defaultvalue);
|
CV_StealthSet(&cv_playercolor[n], cv_playercolor[n].defaultvalue);
|
||||||
else {
|
else
|
||||||
UINT16 i = 0;
|
{
|
||||||
while (i<numskincolors && !skincolors[i].accessible) i++;
|
while (i < numskincolors && !skincolors[i].accessible)
|
||||||
|
i++;
|
||||||
CV_StealthSetValue(&cv_playercolor[n], (i != numskincolors) ? i : SKINCOLOR_BLUE);
|
CV_StealthSetValue(&cv_playercolor[n], (i != numskincolors) ? i : SKINCOLOR_BLUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1512,8 +1519,6 @@ static void SendNameAndColor(UINT8 n)
|
||||||
&& cv_followercolor[n].value == player->followercolor)
|
&& cv_followercolor[n].value == player->followercolor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
player->availabilities = R_GetSkinAvailabilities();
|
|
||||||
|
|
||||||
// We'll handle it later if we're not playing.
|
// We'll handle it later if we're not playing.
|
||||||
if (!Playing())
|
if (!Playing())
|
||||||
return;
|
return;
|
||||||
|
|
@ -1530,7 +1535,7 @@ static void SendNameAndColor(UINT8 n)
|
||||||
|
|
||||||
K_KartResetPlayerColor(player);
|
K_KartResetPlayerColor(player);
|
||||||
|
|
||||||
if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
|
if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin, false))
|
||||||
{
|
{
|
||||||
SetPlayerSkin(playernum, cv_skin[n].string);
|
SetPlayerSkin(playernum, cv_skin[n].string);
|
||||||
CV_StealthSet(&cv_skin[n], skins[foundskin].name);
|
CV_StealthSet(&cv_skin[n], skins[foundskin].name);
|
||||||
|
|
@ -1575,7 +1580,7 @@ static void SendNameAndColor(UINT8 n)
|
||||||
// check if player has the skin loaded (cv_skin may have
|
// check if player has the skin loaded (cv_skin may have
|
||||||
// the name of a skin that was available in the previous game)
|
// 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_SkinAvailable(cv_skin[n].string);
|
||||||
if ((cv_skin[n].value < 0) || !R_SkinUsable(playernum, cv_skin[n].value))
|
if ((cv_skin[n].value < 0) || !R_SkinUsable(playernum, cv_skin[n].value, false))
|
||||||
{
|
{
|
||||||
CV_StealthSet(&cv_skin[n], DEFAULTSKIN);
|
CV_StealthSet(&cv_skin[n], DEFAULTSKIN);
|
||||||
cv_skin[n].value = 0;
|
cv_skin[n].value = 0;
|
||||||
|
|
@ -1590,7 +1595,6 @@ static void SendNameAndColor(UINT8 n)
|
||||||
|
|
||||||
// Finally write out the complete packet and send it off.
|
// Finally write out the complete packet and send it off.
|
||||||
WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME);
|
WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME);
|
||||||
WRITEUINT32(p, (UINT32)player->availabilities);
|
|
||||||
WRITEUINT16(p, (UINT16)cv_playercolor[n].value);
|
WRITEUINT16(p, (UINT16)cv_playercolor[n].value);
|
||||||
WRITEUINT8(p, (UINT8)cv_skin[n].value);
|
WRITEUINT8(p, (UINT8)cv_skin[n].value);
|
||||||
WRITEINT16(p, (INT16)cv_follower[n].value);
|
WRITEINT16(p, (INT16)cv_follower[n].value);
|
||||||
|
|
@ -1632,11 +1636,9 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
||||||
}
|
}
|
||||||
|
|
||||||
READSTRINGN(*cp, name, MAXPLAYERNAME);
|
READSTRINGN(*cp, name, MAXPLAYERNAME);
|
||||||
p->availabilities = READUINT32(*cp);
|
|
||||||
color = READUINT16(*cp);
|
color = READUINT16(*cp);
|
||||||
skin = READUINT8(*cp);
|
skin = READUINT8(*cp);
|
||||||
follower = READINT16(*cp);
|
follower = READINT16(*cp);
|
||||||
//CONS_Printf("Recieved follower id %d\n", follower);
|
|
||||||
followercolor = READUINT16(*cp);
|
followercolor = READUINT16(*cp);
|
||||||
|
|
||||||
// set name
|
// set name
|
||||||
|
|
@ -1657,56 +1659,20 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
||||||
{
|
{
|
||||||
boolean kick = false;
|
boolean kick = false;
|
||||||
|
|
||||||
// team colors
|
|
||||||
if (G_GametypeHasTeams())
|
|
||||||
{
|
|
||||||
if (p->ctfteam == 1 && p->skincolor != skincolor_redteam)
|
|
||||||
kick = true;
|
|
||||||
else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam)
|
|
||||||
kick = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't allow inaccessible colors
|
// don't allow inaccessible colors
|
||||||
if (skincolors[p->skincolor].accessible == false)
|
if (skincolors[p->skincolor].accessible == false)
|
||||||
kick = true;
|
kick = true;
|
||||||
|
|
||||||
// availabilities
|
|
||||||
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)
|
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);
|
CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s, color: %d)\n"), player_names[playernum], p->skincolor);
|
||||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set skin
|
// 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;
|
const INT32 forcedskin = cv_forceskin.value;
|
||||||
SetPlayerSkinByNum(playernum, forcedskin);
|
SetPlayerSkinByNum(playernum, forcedskin);
|
||||||
|
|
@ -5729,11 +5695,6 @@ void Command_ExitGame_f(void)
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
CL_ClearPlayer(i);
|
CL_ClearPlayer(i);
|
||||||
|
|
||||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
||||||
{
|
|
||||||
players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
|
|
||||||
}
|
|
||||||
|
|
||||||
splitscreen = 0;
|
splitscreen = 0;
|
||||||
SplitScreen_OnChange();
|
SplitScreen_OnChange();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,7 @@ typedef struct player_s
|
||||||
UINT16 skincolor;
|
UINT16 skincolor;
|
||||||
|
|
||||||
INT32 skin;
|
INT32 skin;
|
||||||
UINT32 availabilities;
|
UINT8 availabilities[MAXAVAILABILITY];
|
||||||
|
|
||||||
UINT8 fakeskin; // ironman
|
UINT8 fakeskin; // ironman
|
||||||
UINT8 lastfakeskin;
|
UINT8 lastfakeskin;
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,36 @@ static float searchfvalue(const char *s)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// These are for clearing all of various things
|
// 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)
|
void clear_conditionsets(void)
|
||||||
{
|
{
|
||||||
UINT8 i;
|
UINT8 i;
|
||||||
|
|
@ -2140,7 +2170,12 @@ void reademblemdata(MYFILE *f, INT32 num)
|
||||||
else if (fastcmp(word, "COLOR"))
|
else if (fastcmp(word, "COLOR"))
|
||||||
emblemlocations[num-1].color = get_number(word2);
|
emblemlocations[num-1].color = get_number(word2);
|
||||||
else if (fastcmp(word, "VAR"))
|
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);
|
emblemlocations[num-1].var = get_number(word2);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
deh_warning("Emblem %d: unknown word '%s'", num, word);
|
deh_warning("Emblem %d: unknown word '%s'", num, word);
|
||||||
}
|
}
|
||||||
|
|
@ -2344,7 +2379,8 @@ void readunlockable(MYFILE *f, INT32 num)
|
||||||
}
|
}
|
||||||
else if (fastcmp(word, "VAR"))
|
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)i;
|
unlockables[num].variable = (INT16)i;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ void readlight(MYFILE *f, INT32 num);
|
||||||
void readskincolor(MYFILE *f, INT32 num);
|
void readskincolor(MYFILE *f, INT32 num);
|
||||||
void readthing(MYFILE *f, INT32 num);
|
void readthing(MYFILE *f, INT32 num);
|
||||||
void readfreeslots(MYFILE *f);
|
void readfreeslots(MYFILE *f);
|
||||||
|
void clear_emblems(void);
|
||||||
|
void clear_unlockables(void);
|
||||||
void clear_levels(void);
|
void clear_levels(void);
|
||||||
void clear_conditionsets(void);
|
void clear_conditionsets(void);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -584,12 +584,13 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clearall || fastcmp(word2, "UNLOCKABLES"))
|
if (clearall || fastcmp(word2, "UNLOCKABLES"))
|
||||||
memset(&unlockables, 0, sizeof(unlockables));
|
{
|
||||||
|
clear_unlockables();
|
||||||
|
}
|
||||||
|
|
||||||
if (clearall || fastcmp(word2, "EMBLEMS"))
|
if (clearall || fastcmp(word2, "EMBLEMS"))
|
||||||
{
|
{
|
||||||
memset(&emblemlocations, 0, sizeof(emblemlocations));
|
clear_emblems();
|
||||||
numemblems = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clearall || fastcmp(word2, "EXTRAEMBLEMS"))
|
if (clearall || fastcmp(word2, "EXTRAEMBLEMS"))
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,8 @@ extern char logfilename[1024];
|
||||||
#define MAXGAMEPADS (MAXSPLITSCREENPLAYERS * 2) // Number of gamepads we'll be allowing
|
#define MAXGAMEPADS (MAXSPLITSCREENPLAYERS * 2) // Number of gamepads we'll be allowing
|
||||||
|
|
||||||
#define MAXSKINS UINT8_MAX
|
#define MAXSKINS UINT8_MAX
|
||||||
#define SKINNAMESIZE 16 // Moved from r_skins.h as including that particular header causes issues later down the line.
|
#define SKINNAMESIZE 16
|
||||||
|
#define MAXAVAILABILITY ((MAXSKINS + 7)/8)
|
||||||
|
|
||||||
#define COLORRAMPSIZE 16
|
#define COLORRAMPSIZE 16
|
||||||
#define MAXCOLORNAME 32
|
#define MAXCOLORNAME 32
|
||||||
|
|
|
||||||
115
src/g_demo.c
115
src/g_demo.c
|
|
@ -276,10 +276,8 @@ void G_ReadDemoExtraData(void)
|
||||||
{
|
{
|
||||||
extradata = READUINT8(demo_p);
|
extradata = READUINT8(demo_p);
|
||||||
|
|
||||||
if (extradata & DXD_PLAYSTATE)
|
if (extradata & DXD_JOINDATA)
|
||||||
{
|
{
|
||||||
i = READUINT8(demo_p);
|
|
||||||
|
|
||||||
if (!playeringame[p])
|
if (!playeringame[p])
|
||||||
{
|
{
|
||||||
CL_ClearPlayer(p);
|
CL_ClearPlayer(p);
|
||||||
|
|
@ -288,14 +286,22 @@ void G_ReadDemoExtraData(void)
|
||||||
players[p].spectator = true;
|
players[p].spectator = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((players[p].bot = !!(i & DXD_PST_ISBOT)))
|
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||||
|
{
|
||||||
|
players[p].availabilities[i] = READUINT8(demo_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
players[p].bot = !!(READUINT8(demo_p));
|
||||||
|
if (players[p].bot)
|
||||||
{
|
{
|
||||||
players[p].botvars.difficulty = READUINT8(demo_p);
|
players[p].botvars.difficulty = READUINT8(demo_p);
|
||||||
players[p].botvars.diffincrease = READUINT8(demo_p); // needed to avoid having to duplicate logic
|
players[p].botvars.diffincrease = READUINT8(demo_p); // needed to avoid having to duplicate logic
|
||||||
players[p].botvars.rival = (boolean)READUINT8(demo_p);
|
players[p].botvars.rival = (boolean)READUINT8(demo_p);
|
||||||
|
|
||||||
i &= ~DXD_PST_ISBOT;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (extradata & DXD_PLAYSTATE)
|
||||||
|
{
|
||||||
|
i = READUINT8(demo_p);
|
||||||
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case DXD_PST_PLAYING:
|
case DXD_PST_PLAYING:
|
||||||
|
|
@ -334,14 +340,6 @@ void G_ReadDemoExtraData(void)
|
||||||
K_CheckBumpers();
|
K_CheckBumpers();
|
||||||
P_CheckRacers();
|
P_CheckRacers();
|
||||||
}
|
}
|
||||||
if (extradata & DXD_RESPAWN)
|
|
||||||
{
|
|
||||||
if (players[p].mo)
|
|
||||||
{
|
|
||||||
// Is this how this should work..?
|
|
||||||
K_DoIngameRespawn(&players[p]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (extradata & DXD_SKIN)
|
if (extradata & DXD_SKIN)
|
||||||
{
|
{
|
||||||
UINT8 skinid;
|
UINT8 skinid;
|
||||||
|
|
@ -397,6 +395,14 @@ void G_ReadDemoExtraData(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (extradata & DXD_RESPAWN)
|
||||||
|
{
|
||||||
|
if (players[p].mo)
|
||||||
|
{
|
||||||
|
// Is this how this should work..?
|
||||||
|
K_DoIngameRespawn(&players[p]);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (extradata & DXD_WEAPONPREF)
|
if (extradata & DXD_WEAPONPREF)
|
||||||
{
|
{
|
||||||
WeaponPref_Parse(&demo_p, p);
|
WeaponPref_Parse(&demo_p, p);
|
||||||
|
|
@ -454,6 +460,21 @@ void G_WriteDemoExtraData(void)
|
||||||
WRITEUINT8(demo_p, i);
|
WRITEUINT8(demo_p, i);
|
||||||
WRITEUINT8(demo_p, demo_extradata[i]);
|
WRITEUINT8(demo_p, demo_extradata[i]);
|
||||||
|
|
||||||
|
if (demo_extradata[i] & DXD_JOINDATA)
|
||||||
|
{
|
||||||
|
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||||
|
{
|
||||||
|
WRITEUINT8(demo_p, players[i].availabilities[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITEUINT8(demo_p, (UINT8)players[i].bot);
|
||||||
|
if (players[i].bot)
|
||||||
|
{
|
||||||
|
WRITEUINT8(demo_p, players[i].botvars.difficulty);
|
||||||
|
WRITEUINT8(demo_p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic
|
||||||
|
WRITEUINT8(demo_p, (UINT8)players[i].botvars.rival);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (demo_extradata[i] & DXD_PLAYSTATE)
|
if (demo_extradata[i] & DXD_PLAYSTATE)
|
||||||
{
|
{
|
||||||
UINT8 pst = DXD_PST_PLAYING;
|
UINT8 pst = DXD_PST_PLAYING;
|
||||||
|
|
@ -472,19 +493,7 @@ void G_WriteDemoExtraData(void)
|
||||||
pst = DXD_PST_SPECTATING;
|
pst = DXD_PST_SPECTATING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (players[i].bot)
|
|
||||||
{
|
|
||||||
pst |= DXD_PST_ISBOT;
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITEUINT8(demo_p, pst);
|
WRITEUINT8(demo_p, pst);
|
||||||
|
|
||||||
if (pst & DXD_PST_ISBOT)
|
|
||||||
{
|
|
||||||
WRITEUINT8(demo_p, players[i].botvars.difficulty);
|
|
||||||
WRITEUINT8(demo_p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic
|
|
||||||
WRITEUINT8(demo_p, (UINT8)players[i].botvars.rival);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//if (demo_extradata[i] & DXD_RESPAWN) has no extra data
|
//if (demo_extradata[i] & DXD_RESPAWN) has no extra data
|
||||||
if (demo_extradata[i] & DXD_SKIN)
|
if (demo_extradata[i] & DXD_SKIN)
|
||||||
|
|
@ -1218,8 +1227,14 @@ void G_GhostTicker(void)
|
||||||
if (ziptic == 0) // Only support player 0 info for now
|
if (ziptic == 0) // Only support player 0 info for now
|
||||||
{
|
{
|
||||||
ziptic = READUINT8(g->p);
|
ziptic = READUINT8(g->p);
|
||||||
|
if (ziptic & DXD_JOINDATA)
|
||||||
|
{
|
||||||
|
g->p += MAXAVAILABILITY;
|
||||||
|
if (READUINT8(g->p) != 0)
|
||||||
|
I_Error("Ghost is not a record attack ghost (bot JOINDATA)");
|
||||||
|
}
|
||||||
if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING)
|
if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING)
|
||||||
I_Error("Ghost is not a record attack ghost PLAYSTATE"); //@TODO lmao don't blow up like this
|
I_Error("Ghost is not a record attack ghost (has PLAYSTATE)");
|
||||||
if (ziptic & DXD_SKIN)
|
if (ziptic & DXD_SKIN)
|
||||||
g->p++; // We _could_ read this info, but it shouldn't change anything in record attack...
|
g->p++; // We _could_ read this info, but it shouldn't change anything in record attack...
|
||||||
if (ziptic & DXD_COLOR)
|
if (ziptic & DXD_COLOR)
|
||||||
|
|
@ -2248,6 +2263,7 @@ static void G_SaveDemoSkins(UINT8 **pp)
|
||||||
{
|
{
|
||||||
char skin[16];
|
char skin[16];
|
||||||
UINT8 i;
|
UINT8 i;
|
||||||
|
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true);
|
||||||
|
|
||||||
WRITEUINT8((*pp), numskins);
|
WRITEUINT8((*pp), numskins);
|
||||||
for (i = 0; i < numskins; i++)
|
for (i = 0; i < numskins; i++)
|
||||||
|
|
@ -2262,12 +2278,17 @@ static void G_SaveDemoSkins(UINT8 **pp)
|
||||||
WRITEUINT8((*pp), skins[i].kartweight);
|
WRITEUINT8((*pp), skins[i].kartweight);
|
||||||
WRITEUINT32((*pp), skins[i].flags);
|
WRITEUINT32((*pp), skins[i].flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||||
|
{
|
||||||
|
WRITEUINT8((*pp), availabilitiesbuffer[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static democharlist_t *G_LoadDemoSkins(UINT8 **pp, UINT8 *worknumskins, boolean getclosest)
|
static democharlist_t *G_LoadDemoSkins(UINT8 **pp, UINT8 *worknumskins, boolean getclosest)
|
||||||
{
|
{
|
||||||
char skin[17];
|
char skin[17];
|
||||||
UINT8 i;
|
UINT8 i, byte, shif;
|
||||||
democharlist_t *skinlist = NULL;
|
democharlist_t *skinlist = NULL;
|
||||||
|
|
||||||
(*worknumskins) = READUINT8((*pp));
|
(*worknumskins) = READUINT8((*pp));
|
||||||
|
|
@ -2310,6 +2331,24 @@ static democharlist_t *G_LoadDemoSkins(UINT8 **pp, UINT8 *worknumskins, boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (byte = 0; byte < MAXAVAILABILITY; byte++)
|
||||||
|
{
|
||||||
|
UINT8 availabilitiesbuffer = READUINT8((*pp));
|
||||||
|
|
||||||
|
for (shif = 0; shif < 8; shif++)
|
||||||
|
{
|
||||||
|
i = (byte*8) + shif;
|
||||||
|
|
||||||
|
if (i >= (*worknumskins))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (availabilitiesbuffer & (1 << shif))
|
||||||
|
{
|
||||||
|
skinlist[i].unlockrequired = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return skinlist;
|
return skinlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2326,6 +2365,8 @@ static void G_SkipDemoSkins(UINT8 **pp)
|
||||||
(*pp)++; // kartweight
|
(*pp)++; // kartweight
|
||||||
(*pp) += 4; // flags
|
(*pp) += 4; // flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*pp) += MAXAVAILABILITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void G_BeginRecording(void)
|
void G_BeginRecording(void)
|
||||||
|
|
@ -2443,6 +2484,11 @@ void G_BeginRecording(void)
|
||||||
M_Memcpy(demo_p,name,16);
|
M_Memcpy(demo_p,name,16);
|
||||||
demo_p += 16;
|
demo_p += 16;
|
||||||
|
|
||||||
|
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||||
|
{
|
||||||
|
WRITEUINT8(demo_p, player->availabilities[j]);
|
||||||
|
}
|
||||||
|
|
||||||
// Skin (now index into demo.skinlist)
|
// Skin (now index into demo.skinlist)
|
||||||
WRITEUINT8(demo_p, player->skin);
|
WRITEUINT8(demo_p, player->skin);
|
||||||
WRITEUINT8(demo_p, player->lastfakeskin);
|
WRITEUINT8(demo_p, player->lastfakeskin);
|
||||||
|
|
@ -2928,6 +2974,7 @@ void G_DoPlayDemo(char *defdemoname)
|
||||||
UINT8 i, p, numslots = 0;
|
UINT8 i, p, numslots = 0;
|
||||||
lumpnum_t l;
|
lumpnum_t l;
|
||||||
char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],*n,*pdemoname;
|
char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],*n,*pdemoname;
|
||||||
|
UINT8 availabilities[MAXPLAYERS][MAXAVAILABILITY];
|
||||||
UINT8 version,subversion;
|
UINT8 version,subversion;
|
||||||
UINT32 randseed[PRNUMCLASS];
|
UINT32 randseed[PRNUMCLASS];
|
||||||
char msg[1024];
|
char msg[1024];
|
||||||
|
|
@ -3293,6 +3340,11 @@ void G_DoPlayDemo(char *defdemoname)
|
||||||
M_Memcpy(player_names[p],demo_p,16);
|
M_Memcpy(player_names[p],demo_p,16);
|
||||||
demo_p += 16;
|
demo_p += 16;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||||
|
{
|
||||||
|
availabilities[p][i] = READUINT8(demo_p);
|
||||||
|
}
|
||||||
|
|
||||||
// Skin
|
// Skin
|
||||||
|
|
||||||
i = READUINT8(demo_p);
|
i = READUINT8(demo_p);
|
||||||
|
|
@ -3376,6 +3428,8 @@ void G_DoPlayDemo(char *defdemoname)
|
||||||
|
|
||||||
for (i = 0; i < numslots; i++)
|
for (i = 0; i < numslots; i++)
|
||||||
{
|
{
|
||||||
|
UINT8 j;
|
||||||
|
|
||||||
p = slots[i];
|
p = slots[i];
|
||||||
if (players[p].mo)
|
if (players[p].mo)
|
||||||
{
|
{
|
||||||
|
|
@ -3392,6 +3446,11 @@ void G_DoPlayDemo(char *defdemoname)
|
||||||
players[p].kartweight = ghostext[p].kartweight = demo.skinlist[demo.currentskinid[p]].kartweight;
|
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].charflags = ghostext[p].charflags = demo.skinlist[demo.currentskinid[p]].flags;
|
||||||
players[p].lastfakeskin = lastfakeskin[p];
|
players[p].lastfakeskin = lastfakeskin[p];
|
||||||
|
|
||||||
|
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||||
|
{
|
||||||
|
players[p].availabilities[j] = availabilities[p][j];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
demo.deferstart = true;
|
demo.deferstart = true;
|
||||||
|
|
|
||||||
10
src/g_demo.h
10
src/g_demo.h
|
|
@ -33,6 +33,7 @@ typedef struct democharlist_s {
|
||||||
UINT8 kartspeed;
|
UINT8 kartspeed;
|
||||||
UINT8 kartweight;
|
UINT8 kartweight;
|
||||||
UINT32 flags;
|
UINT32 flags;
|
||||||
|
boolean unlockrequired;
|
||||||
} democharlist_t;
|
} democharlist_t;
|
||||||
|
|
||||||
// Publicly-accessible demo vars
|
// Publicly-accessible demo vars
|
||||||
|
|
@ -119,20 +120,19 @@ typedef enum
|
||||||
extern UINT8 demo_extradata[MAXPLAYERS];
|
extern UINT8 demo_extradata[MAXPLAYERS];
|
||||||
extern UINT8 demo_writerng;
|
extern UINT8 demo_writerng;
|
||||||
|
|
||||||
#define DXD_PLAYSTATE 0x01 // state changed between playing, spectating, or not in-game
|
#define DXD_JOINDATA 0x01 // join-specific data
|
||||||
#define DXD_RESPAWN 0x02 // "respawn" command in console
|
#define DXD_PLAYSTATE 0x02 // state changed between playing, spectating, or not in-game
|
||||||
#define DXD_SKIN 0x04 // skin changed
|
#define DXD_SKIN 0x04 // skin changed
|
||||||
#define DXD_NAME 0x08 // name changed
|
#define DXD_NAME 0x08 // name changed
|
||||||
#define DXD_COLOR 0x10 // color changed
|
#define DXD_COLOR 0x10 // color changed
|
||||||
#define DXD_FOLLOWER 0x20 // follower was changed
|
#define DXD_FOLLOWER 0x20 // follower was changed
|
||||||
#define DXD_WEAPONPREF 0x40 // netsynced playsim settings were changed
|
#define DXD_RESPAWN 0x40 // "respawn" command in console
|
||||||
|
#define DXD_WEAPONPREF 0x80 // netsynced playsim settings were changed
|
||||||
|
|
||||||
#define DXD_PST_PLAYING 0x01
|
#define DXD_PST_PLAYING 0x01
|
||||||
#define DXD_PST_SPECTATING 0x02
|
#define DXD_PST_SPECTATING 0x02
|
||||||
#define DXD_PST_LEFT 0x03
|
#define DXD_PST_LEFT 0x03
|
||||||
|
|
||||||
#define DXD_PST_ISBOT 0x80 // extra flag
|
|
||||||
|
|
||||||
// Record/playback tics
|
// Record/playback tics
|
||||||
void G_ReadDemoExtraData(void);
|
void G_ReadDemoExtraData(void);
|
||||||
void G_WriteDemoExtraData(void);
|
void G_WriteDemoExtraData(void);
|
||||||
|
|
|
||||||
14
src/g_game.c
14
src/g_game.c
|
|
@ -2238,7 +2238,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
||||||
UINT8 latestlap;
|
UINT8 latestlap;
|
||||||
UINT16 skincolor;
|
UINT16 skincolor;
|
||||||
INT32 skin;
|
INT32 skin;
|
||||||
UINT32 availabilities;
|
UINT8 availabilities[MAXAVAILABILITY];
|
||||||
UINT8 fakeskin;
|
UINT8 fakeskin;
|
||||||
UINT8 lastfakeskin;
|
UINT8 lastfakeskin;
|
||||||
|
|
||||||
|
|
@ -2307,7 +2307,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
||||||
followercolor = players[player].followercolor;
|
followercolor = players[player].followercolor;
|
||||||
followerskin = players[player].followerskin;
|
followerskin = players[player].followerskin;
|
||||||
|
|
||||||
availabilities = players[player].availabilities;
|
memcpy(availabilities, players[player].availabilities, sizeof(availabilities));
|
||||||
|
|
||||||
followitem = players[player].followitem;
|
followitem = players[player].followitem;
|
||||||
|
|
||||||
|
|
@ -2432,7 +2432,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
||||||
p->charflags = charflags;
|
p->charflags = charflags;
|
||||||
p->lastfakeskin = lastfakeskin;
|
p->lastfakeskin = lastfakeskin;
|
||||||
|
|
||||||
p->availabilities = availabilities;
|
memcpy(players[player].availabilities, availabilities, sizeof(availabilities));
|
||||||
p->followitem = followitem;
|
p->followitem = followitem;
|
||||||
|
|
||||||
p->starpostnum = starpostnum;
|
p->starpostnum = starpostnum;
|
||||||
|
|
@ -2918,7 +2918,7 @@ void G_AddPlayer(INT32 playernum)
|
||||||
{
|
{
|
||||||
player_t *p = &players[playernum];
|
player_t *p = &players[playernum];
|
||||||
p->playerstate = PST_REBORN;
|
p->playerstate = PST_REBORN;
|
||||||
demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything
|
demo_extradata[playernum] |= DXD_JOINDATA|DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything
|
||||||
}
|
}
|
||||||
|
|
||||||
void G_ExitLevel(void)
|
void G_ExitLevel(void)
|
||||||
|
|
@ -3690,6 +3690,10 @@ static void G_UpdateVisited(void)
|
||||||
|
|
||||||
if ((earnedEmblems = M_CompletionEmblems()))
|
if ((earnedEmblems = M_CompletionEmblems()))
|
||||||
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
|
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
|
||||||
|
|
||||||
|
if (M_UpdateUnlockablesAndExtraEmblems())
|
||||||
|
S_StartSound(NULL, sfx_ncitem);
|
||||||
|
G_SaveGameData();
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean CanSaveLevel(INT32 mapnum)
|
static boolean CanSaveLevel(INT32 mapnum)
|
||||||
|
|
@ -4507,12 +4511,14 @@ void G_SaveGameData(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef DEVELOP
|
||||||
if (usedCheats)
|
if (usedCheats)
|
||||||
{
|
{
|
||||||
free(savebuffer);
|
free(savebuffer);
|
||||||
save_p = savebuffer = NULL;
|
save_p = savebuffer = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Version test
|
// Version test
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -231,9 +231,9 @@ void K_InitGrandPrixBots(void)
|
||||||
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
||||||
for (i = 0; i < usableskins; i++)
|
for (i = 0; i < usableskins; i++)
|
||||||
{
|
{
|
||||||
if (!(grabskins[i] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
|
||||||
continue;
|
continue;
|
||||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||||
{
|
{
|
||||||
usableskins--;
|
usableskins--;
|
||||||
}
|
}
|
||||||
|
|
@ -557,9 +557,9 @@ void K_RetireBots(void)
|
||||||
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
||||||
for (i = 0; i < usableskins; i++)
|
for (i = 0; i < usableskins; i++)
|
||||||
{
|
{
|
||||||
if (!(grabskins[i] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
|
||||||
continue;
|
continue;
|
||||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||||
usableskins--;
|
usableskins--;
|
||||||
grabskins[i] = grabskins[usableskins];
|
grabskins[i] = grabskins[usableskins];
|
||||||
grabskins[usableskins] = MAXSKINS;
|
grabskins[usableskins] = MAXSKINS;
|
||||||
|
|
|
||||||
|
|
@ -2175,6 +2175,9 @@ void M_CharacterSelectInit(void)
|
||||||
UINT8 x = skins[i].kartspeed-1;
|
UINT8 x = skins[i].kartspeed-1;
|
||||||
UINT8 y = skins[i].kartweight-1;
|
UINT8 y = skins[i].kartweight-1;
|
||||||
|
|
||||||
|
if (!R_SkinUsable(g_localplayers[0], i, false))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (setup_chargrid[x][y].numskins >= MAXCLONES)
|
if (setup_chargrid[x][y].numskins >= MAXCLONES)
|
||||||
CONS_Alert(CONS_ERROR, "Max character alts reached for %d,%d\n", x+1, y+1);
|
CONS_Alert(CONS_ERROR, "Max character alts reached for %d,%d\n", x+1, y+1);
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -2351,7 +2351,7 @@ static int lib_rSetPlayerSkin(lua_State *L)
|
||||||
return luaL_error(L, "skin %s (argument 2) is not loaded", skinname);
|
return luaL_error(L, "skin %s (argument 2) is not loaded", skinname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!R_SkinUsable(j, i))
|
if (!R_SkinUsable(j, i, false))
|
||||||
return luaL_error(L, "skin %d (argument 2) not usable - check with R_SkinUsable(player_t, skin) first.", i);
|
return luaL_error(L, "skin %d (argument 2) not usable - check with R_SkinUsable(player_t, skin) first.", i);
|
||||||
SetPlayerSkinByNum(j, i);
|
SetPlayerSkinByNum(j, i);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -2381,7 +2381,7 @@ static int lib_rSkinUsable(lua_State *L)
|
||||||
return luaL_error(L, "skin %s (argument 2) is not loaded", skinname);
|
return luaL_error(L, "skin %s (argument 2) is not loaded", skinname);
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_pushboolean(L, R_SkinUsable(j, i));
|
lua_pushboolean(L, R_SkinUsable(j, i, false));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -669,7 +669,7 @@ static int mobj_set(lua_State *L)
|
||||||
for (i = 0; i < numskins; i++)
|
for (i = 0; i < numskins; i++)
|
||||||
if (fastcmp(skins[i].name, skin))
|
if (fastcmp(skins[i].name, skin))
|
||||||
{
|
{
|
||||||
if (!mo->player || R_SkinUsable(mo->player-players, i))
|
if (!mo->player || R_SkinUsable(mo->player-players, i, false))
|
||||||
mo->skin = &skins[i];
|
mo->skin = &skins[i];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -408,8 +408,6 @@ static int player_get(lua_State *L)
|
||||||
lua_pushinteger(L, plr->skincolor);
|
lua_pushinteger(L, plr->skincolor);
|
||||||
else if (fastcmp(field,"skin"))
|
else if (fastcmp(field,"skin"))
|
||||||
lua_pushinteger(L, plr->skin);
|
lua_pushinteger(L, plr->skin);
|
||||||
else if (fastcmp(field,"availabilities"))
|
|
||||||
lua_pushinteger(L, plr->availabilities);
|
|
||||||
else if (fastcmp(field,"fakeskin"))
|
else if (fastcmp(field,"fakeskin"))
|
||||||
lua_pushinteger(L, plr->fakeskin);
|
lua_pushinteger(L, plr->fakeskin);
|
||||||
else if (fastcmp(field,"lastfakeskin"))
|
else if (fastcmp(field,"lastfakeskin"))
|
||||||
|
|
@ -577,8 +575,6 @@ static int player_set(lua_State *L)
|
||||||
}
|
}
|
||||||
else if (fastcmp(field,"skin"))
|
else if (fastcmp(field,"skin"))
|
||||||
return NOSET;
|
return NOSET;
|
||||||
else if (fastcmp(field,"availabilities"))
|
|
||||||
return NOSET;
|
|
||||||
else if (fastcmp(field,"fakeskin"))
|
else if (fastcmp(field,"fakeskin"))
|
||||||
return NOSET;
|
return NOSET;
|
||||||
else if (fastcmp(field,"lastfakeskin"))
|
else if (fastcmp(field,"lastfakeskin"))
|
||||||
|
|
|
||||||
35
src/m_cond.c
35
src/m_cond.c
|
|
@ -290,11 +290,6 @@ void M_SilentUpdateUnlockablesAndEmblems(void)
|
||||||
continue;
|
continue;
|
||||||
unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
|
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
|
// Emblem unlocking shit
|
||||||
|
|
@ -372,6 +367,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
|
||||||
if (res)
|
if (res)
|
||||||
++somethingUnlocked;
|
++somethingUnlocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
return somethingUnlocked;
|
return somethingUnlocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -502,6 +498,35 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Misc Emblem shit
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ typedef struct
|
||||||
UINT8 sprite; ///< emblem sprite to use, 0 - 25
|
UINT8 sprite; ///< emblem sprite to use, 0 - 25
|
||||||
UINT16 color; ///< skincolor to use
|
UINT16 color; ///< skincolor to use
|
||||||
INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin)
|
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
|
char hint[110]; ///< Hint for emblem hints menu
|
||||||
UINT8 collected; ///< Do you have this emblem?
|
UINT8 collected; ///< Do you have this emblem?
|
||||||
} emblem_t;
|
} emblem_t;
|
||||||
|
|
@ -100,6 +101,7 @@ typedef struct
|
||||||
UINT8 showconditionset;
|
UINT8 showconditionset;
|
||||||
INT16 type;
|
INT16 type;
|
||||||
INT16 variable;
|
INT16 variable;
|
||||||
|
char *stringVar;
|
||||||
UINT8 nocecho;
|
UINT8 nocecho;
|
||||||
UINT8 nochecklist;
|
UINT8 nochecklist;
|
||||||
UINT8 unlocked;
|
UINT8 unlocked;
|
||||||
|
|
@ -177,4 +179,7 @@ const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big);
|
||||||
UINT8 M_GotEnoughEmblems(INT32 number);
|
UINT8 M_GotEnoughEmblems(INT32 number);
|
||||||
UINT8 M_GotLowEnoughTime(INT32 tictime);
|
UINT8 M_GotLowEnoughTime(INT32 tictime);
|
||||||
|
|
||||||
|
INT32 M_UnlockableSkinNum(unlockable_t *unlock);
|
||||||
|
INT32 M_EmblemSkinNum(emblem_t *emblem);
|
||||||
|
|
||||||
#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved)
|
#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved)
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,12 @@ static void P_NetArchivePlayers(void)
|
||||||
|
|
||||||
WRITEUINT8(save_p, players[i].skincolor);
|
WRITEUINT8(save_p, players[i].skincolor);
|
||||||
WRITEINT32(save_p, players[i].skin);
|
WRITEINT32(save_p, players[i].skin);
|
||||||
WRITEUINT32(save_p, players[i].availabilities);
|
|
||||||
|
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||||
|
{
|
||||||
|
WRITEUINT8(save_p, players[i].availabilities[j]);
|
||||||
|
}
|
||||||
|
|
||||||
WRITEUINT8(save_p, players[i].fakeskin);
|
WRITEUINT8(save_p, players[i].fakeskin);
|
||||||
WRITEUINT8(save_p, players[i].lastfakeskin);
|
WRITEUINT8(save_p, players[i].lastfakeskin);
|
||||||
WRITEUINT32(save_p, players[i].score);
|
WRITEUINT32(save_p, players[i].score);
|
||||||
|
|
@ -472,7 +477,12 @@ static void P_NetUnArchivePlayers(void)
|
||||||
|
|
||||||
players[i].skincolor = READUINT8(save_p);
|
players[i].skincolor = READUINT8(save_p);
|
||||||
players[i].skin = READINT32(save_p);
|
players[i].skin = READINT32(save_p);
|
||||||
players[i].availabilities = READUINT32(save_p);
|
|
||||||
|
for (j = 0; i < MAXAVAILABILITY; j++)
|
||||||
|
{
|
||||||
|
players[i].availabilities[j] = READUINT8(save_p);
|
||||||
|
}
|
||||||
|
|
||||||
players[i].fakeskin = READUINT8(save_p);
|
players[i].fakeskin = READUINT8(save_p);
|
||||||
players[i].lastfakeskin = READUINT8(save_p);
|
players[i].lastfakeskin = READUINT8(save_p);
|
||||||
players[i].score = READUINT32(save_p);
|
players[i].score = READUINT32(save_p);
|
||||||
|
|
|
||||||
|
|
@ -7559,10 +7559,14 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
||||||
nextmapoverride = 0;
|
nextmapoverride = 0;
|
||||||
skipstats = 0;
|
skipstats = 0;
|
||||||
|
|
||||||
if (!(netgame || multiplayer || demo.playback) && !majormods)
|
if (!demo.playback)
|
||||||
|
{
|
||||||
mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED;
|
mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED;
|
||||||
else if (!demo.playback)
|
|
||||||
mapheaderinfo[gamemap-1]->mapvisited |= MV_MP; // you want to record that you've been there this session, but not permanently
|
if (M_UpdateUnlockablesAndExtraEmblems())
|
||||||
|
S_StartSound(NULL, sfx_ncitem);
|
||||||
|
G_SaveGameData();
|
||||||
|
}
|
||||||
|
|
||||||
G_AddMapToBuffer(gamemap-1);
|
G_AddMapToBuffer(gamemap-1);
|
||||||
|
|
||||||
|
|
@ -8036,8 +8040,8 @@ UINT16 P_PartialAddWadFile(const char *wadfilename)
|
||||||
//
|
//
|
||||||
// look for skins
|
// look for skins
|
||||||
//
|
//
|
||||||
R_AddSkins(wadnum); // faB: wadfile index in wadfiles[]
|
R_AddSkins(wadnum, false); // faB: wadfile index in wadfiles[]
|
||||||
R_PatchSkins(wadnum); // toast: PATCH PATCH
|
R_PatchSkins(wadnum, false); // toast: PATCH PATCH
|
||||||
|
|
||||||
//
|
//
|
||||||
// edit music defs
|
// edit music defs
|
||||||
|
|
|
||||||
146
src/r_skins.c
146
src/r_skins.c
|
|
@ -32,6 +32,7 @@
|
||||||
#if 0
|
#if 0
|
||||||
#include "k_kart.h" // K_KartResetPlayerColor
|
#include "k_kart.h" // K_KartResetPlayerColor
|
||||||
#endif
|
#endif
|
||||||
|
#include "k_grandprix.h" // K_CanChangeRules
|
||||||
#ifdef HWRENDER
|
#ifdef HWRENDER
|
||||||
#include "hardware/hw_md2.h"
|
#include "hardware/hw_md2.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -150,36 +151,51 @@ void R_InitSkins(void)
|
||||||
|
|
||||||
for (i = 0; i < numwadfiles; i++)
|
for (i = 0; i < numwadfiles; i++)
|
||||||
{
|
{
|
||||||
R_AddSkins((UINT16)i);
|
R_AddSkins((UINT16)i, true);
|
||||||
R_PatchSkins((UINT16)i);
|
R_PatchSkins((UINT16)i, true);
|
||||||
R_LoadSpriteInfoLumps(i, wadfiles[i]->numlumps);
|
R_LoadSpriteInfoLumps(i, wadfiles[i]->numlumps);
|
||||||
}
|
}
|
||||||
ST_ReloadSkinFaceGraphics();
|
ST_ReloadSkinFaceGraphics();
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT32 R_GetSkinAvailabilities(void)
|
UINT8 *R_GetSkinAvailabilities(boolean demolock)
|
||||||
{
|
{
|
||||||
UINT8 i;
|
UINT8 i, shif, byte;
|
||||||
UINT32 response = 0;
|
INT32 skinid;
|
||||||
|
static UINT8 responsebuffer[MAXAVAILABILITY];
|
||||||
|
|
||||||
|
memset(&responsebuffer, 0, sizeof(responsebuffer));
|
||||||
|
|
||||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||||
{
|
{
|
||||||
if (unlockables[i].type == SECRET_SKIN && unlockables[i].unlocked)
|
if (unlockables[i].type != SECRET_SKIN)
|
||||||
{
|
continue;
|
||||||
UINT8 s = min(unlockables[i].variable, MAXSKINS);
|
|
||||||
response |= (1 << s);
|
if (unlockables[i].unlocked != true && !demolock)
|
||||||
}
|
continue;
|
||||||
|
|
||||||
|
skinid = M_UnlockableSkinNum(&unlockables[i]);
|
||||||
|
|
||||||
|
if (skinid < 0 || skinid >= MAXSKINS)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
shif = (skinid % 8);
|
||||||
|
byte = (skinid / 8);
|
||||||
|
|
||||||
|
responsebuffer[byte] |= (1 << shif);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return responsebuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if available in circumstances, otherwise nope
|
// returns true if available in circumstances, otherwise nope
|
||||||
// warning don't use with an invalid skinnum other than -1 which always returns true
|
// warning don't use with an invalid skinnum other than -1 which always returns true
|
||||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
|
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins)
|
||||||
{
|
{
|
||||||
boolean needsunlocked = false;
|
boolean needsunlocked = false;
|
||||||
|
boolean useplayerstruct = (Playing() && playernum != -1);
|
||||||
UINT8 i;
|
UINT8 i;
|
||||||
|
INT32 skinid;
|
||||||
|
|
||||||
if (skinnum == -1)
|
if (skinnum == -1)
|
||||||
{
|
{
|
||||||
|
|
@ -187,55 +203,54 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modeattacking)
|
if (K_CanChangeRules(true) && (cv_forceskin.value == skinnum))
|
||||||
{
|
|
||||||
// If you have someone else's run, you should be able to take a look
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (netgame && (cv_forceskin.value == skinnum))
|
|
||||||
{
|
{
|
||||||
// Being forced to play as this character by the server
|
// Being forced to play as this character by the server
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metalrecording)
|
|
||||||
{
|
|
||||||
// Recording a Metal Sonic race
|
|
||||||
const INT32 metalskin = R_SkinAvailable("metalsonic");
|
|
||||||
return (skinnum == metalskin);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if this character is supposed to be unlockable or not
|
// Determine if this character is supposed to be unlockable or not
|
||||||
for (i = 0; i < MAXUNLOCKABLES; i++)
|
if (useplayerstruct && demo.playback)
|
||||||
{
|
{
|
||||||
if (unlockables[i].type == SECRET_SKIN && unlockables[i].variable == skinnum)
|
if (!demoskins)
|
||||||
|
skinnum = demo.skinlist[skinnum].mapping;
|
||||||
|
needsunlocked = demo.skinlist[skinnum].unlockrequired;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < MAXUNLOCKABLES; i++)
|
||||||
{
|
{
|
||||||
|
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
|
// i is now the unlockable index, we can use this later
|
||||||
needsunlocked = true;
|
needsunlocked = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsunlocked == true)
|
if (needsunlocked == false)
|
||||||
{
|
|
||||||
// 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
|
|
||||||
{
|
{
|
||||||
// Didn't trip anything, so we can use this character.
|
// Didn't trip anything, so we can use this character.
|
||||||
return true;
|
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)
|
// returns true if the skin name is found (loaded from pwad)
|
||||||
|
|
@ -296,7 +311,25 @@ static void SetSkin(player_t *player, INT32 skinnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// for replays: We have changed our skin mid-game; let the game know so it can do the same in the replay!
|
// for replays: We have changed our skin mid-game; let the game know so it can do the same in the replay!
|
||||||
demo_extradata[playernum] |= DXD_SKIN;
|
demo_extradata[(player-players)] |= DXD_SKIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, false))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I_Error("All characters are locked!");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// network code calls this when a 'skin change' is received
|
// network code calls this when a 'skin change' is received
|
||||||
|
|
@ -305,7 +338,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
|
||||||
INT32 i = R_SkinAvailable(skinname);
|
INT32 i = R_SkinAvailable(skinname);
|
||||||
player_t *player = &players[playernum];
|
player_t *player = &players[playernum];
|
||||||
|
|
||||||
if ((i != -1) && R_SkinUsable(playernum, i))
|
if ((i != -1) && R_SkinUsable(playernum, i, false))
|
||||||
{
|
{
|
||||||
SetSkin(player, i);
|
SetSkin(player, i);
|
||||||
return;
|
return;
|
||||||
|
|
@ -316,7 +349,7 @@ void SetPlayerSkin(INT32 playernum, const char *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);
|
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 #.
|
// Same as SetPlayerSkin, but uses the skin #.
|
||||||
|
|
@ -325,7 +358,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
||||||
{
|
{
|
||||||
player_t *player = &players[playernum];
|
player_t *player = &players[playernum];
|
||||||
|
|
||||||
if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists!
|
if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum, false)) // Make sure it exists!
|
||||||
{
|
{
|
||||||
SetSkin(player, skinnum);
|
SetSkin(player, skinnum);
|
||||||
return;
|
return;
|
||||||
|
|
@ -336,7 +369,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 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);
|
CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum);
|
||||||
|
|
||||||
SetSkin(player, 0); // not found put the eggman skin
|
SetSkin(player, GetPlayerDefaultSkin(playernum)); // not found put the eggman skin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set mo skin but not player_t skin, for ironman
|
// Set mo skin but not player_t skin, for ironman
|
||||||
|
|
@ -386,11 +419,14 @@ void SetRandomFakePlayerSkin(player_t* player, boolean fast)
|
||||||
}
|
}
|
||||||
else if (skins[i].flags & SF_IRONMAN)
|
else if (skins[i].flags & SF_IRONMAN)
|
||||||
continue;
|
continue;
|
||||||
/*if (K_SkinLocked(i))
|
if (!R_SkinUsable(player-players, i, true))
|
||||||
continue;*/
|
continue;
|
||||||
grabskins[usableskins++] = i;
|
grabskins[usableskins++] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!usableskins)
|
||||||
|
I_Error("SetRandomFakePlayerSkin: No valid skins to pick from!?");
|
||||||
|
|
||||||
i = grabskins[P_RandomKey(PR_RANDOMSKIN, usableskins)];
|
i = grabskins[P_RandomKey(PR_RANDOMSKIN, usableskins)];
|
||||||
|
|
||||||
SetFakePlayerSkin(player, i);
|
SetFakePlayerSkin(player, i);
|
||||||
|
|
@ -662,7 +698,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
|
||||||
//
|
//
|
||||||
// Find skin sprites, sounds & optional status bar face, & add them
|
// 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;
|
UINT16 lump, lastlump = 0;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
@ -810,7 +846,8 @@ next_token:
|
||||||
|
|
||||||
R_FlushTranslationColormapCache();
|
R_FlushTranslationColormapCache();
|
||||||
|
|
||||||
CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name);
|
if (mainfile == false)
|
||||||
|
CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name);
|
||||||
|
|
||||||
#ifdef SKINVALUES
|
#ifdef SKINVALUES
|
||||||
skin_cons_t[numskins].value = numskins;
|
skin_cons_t[numskins].value = numskins;
|
||||||
|
|
@ -834,7 +871,7 @@ next_token:
|
||||||
//
|
//
|
||||||
// Patch skin sprites
|
// Patch skin sprites
|
||||||
//
|
//
|
||||||
void R_PatchSkins(UINT16 wadnum)
|
void R_PatchSkins(UINT16 wadnum, boolean mainfile)
|
||||||
{
|
{
|
||||||
UINT16 lump, lastlump = 0;
|
UINT16 lump, lastlump = 0;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
@ -976,7 +1013,8 @@ next_token:
|
||||||
|
|
||||||
R_FlushTranslationColormapCache();
|
R_FlushTranslationColormapCache();
|
||||||
|
|
||||||
CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name);
|
if (mainfile == false)
|
||||||
|
CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,13 @@ void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
|
||||||
void SetFakePlayerSkin(player_t* player, INT32 skinnum);
|
void SetFakePlayerSkin(player_t* player, INT32 skinnum);
|
||||||
void SetRandomFakePlayerSkin(player_t* player, boolean fast);
|
void SetRandomFakePlayerSkin(player_t* player, boolean fast);
|
||||||
void ClearFakePlayerSkin(player_t* player);
|
void ClearFakePlayerSkin(player_t* player);
|
||||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum);
|
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins);
|
||||||
UINT32 R_GetSkinAvailabilities(void);
|
|
||||||
|
UINT8 *R_GetSkinAvailabilities(boolean demolock);
|
||||||
INT32 R_SkinAvailable(const char *name);
|
INT32 R_SkinAvailable(const char *name);
|
||||||
void R_PatchSkins(UINT16 wadnum);
|
|
||||||
void R_AddSkins(UINT16 wadnum);
|
void R_PatchSkins(UINT16 wadnum, boolean mainfile);
|
||||||
|
void R_AddSkins(UINT16 wadnum, boolean mainfile);
|
||||||
|
|
||||||
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
|
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue