mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +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);
|
||||
|
||||
if (!R_SkinUsable(-1, v))
|
||||
if (!R_SkinUsable(-1, v, false))
|
||||
v = -1;
|
||||
|
||||
goto finish;
|
||||
|
|
@ -1984,7 +1984,7 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
|
|||
if (var == &cv_forceskin)
|
||||
{
|
||||
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");
|
||||
return;
|
||||
|
|
@ -2109,7 +2109,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
|
|||
else if (newvalue >= numskins)
|
||||
newvalue = -1;
|
||||
} while ((oldvalue != newvalue)
|
||||
&& !(R_SkinUsable(-1, newvalue)));
|
||||
&& !(R_SkinUsable(-1, newvalue, false)));
|
||||
}
|
||||
else
|
||||
newvalue = var->value + increment;
|
||||
|
|
|
|||
|
|
@ -824,6 +824,8 @@ 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(false), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
|
||||
}
|
||||
|
||||
|
|
@ -3565,8 +3567,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
return;
|
||||
}
|
||||
|
||||
node = (UINT8)READUINT8(*p);
|
||||
newplayernum = (UINT8)READUINT8(*p);
|
||||
node = READUINT8(*p);
|
||||
newplayernum = READUINT8(*p);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// the server is creating my player
|
||||
if (node == mynode)
|
||||
|
|
@ -3690,8 +3697,9 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
|
|||
static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||
{
|
||||
INT16 newplayernum;
|
||||
UINT8 skinnum = 0;
|
||||
UINT8 i, skinnum = 0;
|
||||
UINT8 difficulty = DIFFICULTBOT;
|
||||
UINT8 availabilitiesbuffer[MAXAVAILABILITY];
|
||||
|
||||
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
|
||||
{
|
||||
|
|
@ -3704,9 +3712,9 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
return;
|
||||
}
|
||||
|
||||
newplayernum = (UINT8)READUINT8(*p);
|
||||
skinnum = (UINT8)READUINT8(*p);
|
||||
difficulty = (UINT8)READUINT8(*p);
|
||||
newplayernum = READUINT8(*p);
|
||||
skinnum = READUINT8(*p);
|
||||
difficulty = READUINT8(*p);
|
||||
|
||||
CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum);
|
||||
|
||||
|
|
@ -3720,6 +3728,15 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
|
||||
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].bot = true;
|
||||
players[newplayernum].botvars.difficulty = difficulty;
|
||||
|
|
@ -3737,10 +3754,10 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
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;
|
||||
UINT8 buf[4 + MAXPLAYERNAME];
|
||||
INT32 n, newplayernum, i;
|
||||
UINT8 buf[4 + MAXPLAYERNAME + MAXAVAILABILITY];
|
||||
UINT8 *buf_p = buf;
|
||||
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, playerpernode[node]); // splitscreen num
|
||||
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
WRITEUINT8(buf_p, availabilities[i]);
|
||||
}
|
||||
|
||||
playerpernode[node]++;
|
||||
|
||||
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
|
||||
// but I'm not confident enough to remove it entirely in case it breaks something
|
||||
{
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false);
|
||||
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;
|
||||
#endif
|
||||
|
|
@ -3971,6 +3994,7 @@ static void HandleConnect(SINT8 node)
|
|||
{
|
||||
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.....
|
||||
|
|
@ -4077,6 +4101,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])
|
||||
|
|
@ -4111,7 +4137,8 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,6 +246,7 @@ typedef struct
|
|||
UINT8 localplayers; // number of splitscreen players
|
||||
UINT8 mode;
|
||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
|
||||
UINT8 availabilities[MAXAVAILABILITY];
|
||||
} ATTRPACK clientconfig_pak;
|
||||
|
||||
#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++)
|
||||
CL_ClearPlayer(i);
|
||||
|
||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
|
||||
}
|
||||
|
||||
splitscreen = 0;
|
||||
SplitScreen_OnChange();
|
||||
|
||||
|
|
@ -1148,6 +1143,10 @@ static void IdentifyVersion(void)
|
|||
#ifdef USE_PATCH_FILE
|
||||
D_AddFile(startupiwads, va(pandf,srb2waddir,PATCHNAME));
|
||||
#endif
|
||||
#define UNLOCKTESTING
|
||||
#if defined(DEVELOP) && defined(UNLOCKTESTING)
|
||||
D_AddFile(startupiwads, va(pandf,srb2waddir,"unlocks.pk3"));
|
||||
#endif
|
||||
////
|
||||
#undef TEXTURESNAME
|
||||
#undef MAPSNAME
|
||||
|
|
@ -1449,6 +1448,9 @@ void D_SRB2Main(void)
|
|||
#ifdef USE_PATCH_FILE
|
||||
mainwads++; // patch.pk3
|
||||
#endif
|
||||
#ifdef UNLOCKTESTING
|
||||
mainwads++; // unlocks.pk3
|
||||
#endif
|
||||
|
||||
#endif //ifndef DEVELOP
|
||||
|
||||
|
|
|
|||
|
|
@ -1408,6 +1408,10 @@ boolean CanChangeSkinWhilePlaying(INT32 playernum)
|
|||
static void ForceAllSkins(INT32 forcedskin)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
if (demo.playback)
|
||||
return; // DXD_SKIN should handle all changes for us
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
|
|
@ -1476,6 +1480,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
|
||||
|
||||
|
|
@ -1493,9 +1499,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1512,8 +1519,6 @@ static void SendNameAndColor(UINT8 n)
|
|||
&& cv_followercolor[n].value == player->followercolor)
|
||||
return;
|
||||
|
||||
player->availabilities = R_GetSkinAvailabilities();
|
||||
|
||||
// We'll handle it later if we're not playing.
|
||||
if (!Playing())
|
||||
return;
|
||||
|
|
@ -1530,7 +1535,7 @@ static void SendNameAndColor(UINT8 n)
|
|||
|
||||
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);
|
||||
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
|
||||
// the name of a skin that was available in the previous game)
|
||||
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_skin[n].value = 0;
|
||||
|
|
@ -1590,7 +1595,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);
|
||||
WRITEUINT8(p, (UINT8)cv_skin[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);
|
||||
p->availabilities = READUINT32(*cp);
|
||||
color = READUINT16(*cp);
|
||||
skin = READUINT8(*cp);
|
||||
follower = READINT16(*cp);
|
||||
//CONS_Printf("Recieved follower id %d\n", follower);
|
||||
followercolor = READUINT16(*cp);
|
||||
|
||||
// set name
|
||||
|
|
@ -1657,56 +1659,20 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
|||
{
|
||||
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
|
||||
if (skincolors[p->skincolor].accessible == false)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
|
@ -5729,11 +5695,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();
|
||||
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ typedef struct player_s
|
|||
UINT16 skincolor;
|
||||
|
||||
INT32 skin;
|
||||
UINT32 availabilities;
|
||||
UINT8 availabilities[MAXAVAILABILITY];
|
||||
|
||||
UINT8 fakeskin; // ironman
|
||||
UINT8 lastfakeskin;
|
||||
|
|
|
|||
|
|
@ -131,6 +131,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;
|
||||
|
|
@ -2140,7 +2170,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);
|
||||
}
|
||||
|
|
@ -2344,7 +2379,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)i;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ void readlight(MYFILE *f, INT32 num);
|
|||
void readskincolor(MYFILE *f, INT32 num);
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -584,12 +584,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"))
|
||||
|
|
|
|||
|
|
@ -201,7 +201,8 @@ extern char logfilename[1024];
|
|||
#define MAXGAMEPADS (MAXSPLITSCREENPLAYERS * 2) // Number of gamepads we'll be allowing
|
||||
|
||||
#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 MAXCOLORNAME 32
|
||||
|
|
|
|||
115
src/g_demo.c
115
src/g_demo.c
|
|
@ -276,10 +276,8 @@ void G_ReadDemoExtraData(void)
|
|||
{
|
||||
extradata = READUINT8(demo_p);
|
||||
|
||||
if (extradata & DXD_PLAYSTATE)
|
||||
if (extradata & DXD_JOINDATA)
|
||||
{
|
||||
i = READUINT8(demo_p);
|
||||
|
||||
if (!playeringame[p])
|
||||
{
|
||||
CL_ClearPlayer(p);
|
||||
|
|
@ -288,14 +286,22 @@ void G_ReadDemoExtraData(void)
|
|||
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.diffincrease = READUINT8(demo_p); // needed to avoid having to duplicate logic
|
||||
players[p].botvars.rival = (boolean)READUINT8(demo_p);
|
||||
|
||||
i &= ~DXD_PST_ISBOT;
|
||||
}
|
||||
}
|
||||
if (extradata & DXD_PLAYSTATE)
|
||||
{
|
||||
i = READUINT8(demo_p);
|
||||
|
||||
switch (i) {
|
||||
case DXD_PST_PLAYING:
|
||||
|
|
@ -334,14 +340,6 @@ void G_ReadDemoExtraData(void)
|
|||
K_CheckBumpers();
|
||||
P_CheckRacers();
|
||||
}
|
||||
if (extradata & DXD_RESPAWN)
|
||||
{
|
||||
if (players[p].mo)
|
||||
{
|
||||
// Is this how this should work..?
|
||||
K_DoIngameRespawn(&players[p]);
|
||||
}
|
||||
}
|
||||
if (extradata & DXD_SKIN)
|
||||
{
|
||||
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)
|
||||
{
|
||||
WeaponPref_Parse(&demo_p, p);
|
||||
|
|
@ -454,6 +460,21 @@ void G_WriteDemoExtraData(void)
|
|||
WRITEUINT8(demo_p, 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)
|
||||
{
|
||||
UINT8 pst = DXD_PST_PLAYING;
|
||||
|
|
@ -472,19 +493,7 @@ void G_WriteDemoExtraData(void)
|
|||
pst = DXD_PST_SPECTATING;
|
||||
}
|
||||
|
||||
if (players[i].bot)
|
||||
{
|
||||
pst |= DXD_PST_ISBOT;
|
||||
}
|
||||
|
||||
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_SKIN)
|
||||
|
|
@ -1218,8 +1227,14 @@ void G_GhostTicker(void)
|
|||
if (ziptic == 0) // Only support player 0 info for now
|
||||
{
|
||||
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)
|
||||
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)
|
||||
g->p++; // We _could_ read this info, but it shouldn't change anything in record attack...
|
||||
if (ziptic & DXD_COLOR)
|
||||
|
|
@ -2248,6 +2263,7 @@ static void G_SaveDemoSkins(UINT8 **pp)
|
|||
{
|
||||
char skin[16];
|
||||
UINT8 i;
|
||||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true);
|
||||
|
||||
WRITEUINT8((*pp), numskins);
|
||||
for (i = 0; i < numskins; i++)
|
||||
|
|
@ -2262,12 +2278,17 @@ static void G_SaveDemoSkins(UINT8 **pp)
|
|||
WRITEUINT8((*pp), skins[i].kartweight);
|
||||
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)
|
||||
{
|
||||
char skin[17];
|
||||
UINT8 i;
|
||||
UINT8 i, byte, shif;
|
||||
democharlist_t *skinlist = NULL;
|
||||
|
||||
(*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;
|
||||
}
|
||||
|
||||
|
|
@ -2326,6 +2365,8 @@ static void G_SkipDemoSkins(UINT8 **pp)
|
|||
(*pp)++; // kartweight
|
||||
(*pp) += 4; // flags
|
||||
}
|
||||
|
||||
(*pp) += MAXAVAILABILITY;
|
||||
}
|
||||
|
||||
void G_BeginRecording(void)
|
||||
|
|
@ -2443,6 +2484,11 @@ void G_BeginRecording(void)
|
|||
M_Memcpy(demo_p,name,16);
|
||||
demo_p += 16;
|
||||
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
{
|
||||
WRITEUINT8(demo_p, player->availabilities[j]);
|
||||
}
|
||||
|
||||
// Skin (now index into demo.skinlist)
|
||||
WRITEUINT8(demo_p, player->skin);
|
||||
WRITEUINT8(demo_p, player->lastfakeskin);
|
||||
|
|
@ -2928,6 +2974,7 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
UINT8 i, p, numslots = 0;
|
||||
lumpnum_t l;
|
||||
char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],*n,*pdemoname;
|
||||
UINT8 availabilities[MAXPLAYERS][MAXAVAILABILITY];
|
||||
UINT8 version,subversion;
|
||||
UINT32 randseed[PRNUMCLASS];
|
||||
char msg[1024];
|
||||
|
|
@ -3293,6 +3340,11 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
M_Memcpy(player_names[p],demo_p,16);
|
||||
demo_p += 16;
|
||||
|
||||
for (i = 0; i < MAXAVAILABILITY; i++)
|
||||
{
|
||||
availabilities[p][i] = READUINT8(demo_p);
|
||||
}
|
||||
|
||||
// Skin
|
||||
|
||||
i = READUINT8(demo_p);
|
||||
|
|
@ -3376,6 +3428,8 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
|
||||
for (i = 0; i < numslots; i++)
|
||||
{
|
||||
UINT8 j;
|
||||
|
||||
p = slots[i];
|
||||
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].charflags = ghostext[p].charflags = demo.skinlist[demo.currentskinid[p]].flags;
|
||||
players[p].lastfakeskin = lastfakeskin[p];
|
||||
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
{
|
||||
players[p].availabilities[j] = availabilities[p][j];
|
||||
}
|
||||
}
|
||||
|
||||
demo.deferstart = true;
|
||||
|
|
|
|||
10
src/g_demo.h
10
src/g_demo.h
|
|
@ -33,6 +33,7 @@ typedef struct democharlist_s {
|
|||
UINT8 kartspeed;
|
||||
UINT8 kartweight;
|
||||
UINT32 flags;
|
||||
boolean unlockrequired;
|
||||
} democharlist_t;
|
||||
|
||||
// Publicly-accessible demo vars
|
||||
|
|
@ -119,20 +120,19 @@ typedef enum
|
|||
extern UINT8 demo_extradata[MAXPLAYERS];
|
||||
extern UINT8 demo_writerng;
|
||||
|
||||
#define DXD_PLAYSTATE 0x01 // state changed between playing, spectating, or not in-game
|
||||
#define DXD_RESPAWN 0x02 // "respawn" command in console
|
||||
#define DXD_JOINDATA 0x01 // join-specific data
|
||||
#define DXD_PLAYSTATE 0x02 // state changed between playing, spectating, or not in-game
|
||||
#define DXD_SKIN 0x04 // skin changed
|
||||
#define DXD_NAME 0x08 // name changed
|
||||
#define DXD_COLOR 0x10 // color 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_SPECTATING 0x02
|
||||
#define DXD_PST_LEFT 0x03
|
||||
|
||||
#define DXD_PST_ISBOT 0x80 // extra flag
|
||||
|
||||
// Record/playback tics
|
||||
void G_ReadDemoExtraData(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;
|
||||
UINT16 skincolor;
|
||||
INT32 skin;
|
||||
UINT32 availabilities;
|
||||
UINT8 availabilities[MAXAVAILABILITY];
|
||||
UINT8 fakeskin;
|
||||
UINT8 lastfakeskin;
|
||||
|
||||
|
|
@ -2307,7 +2307,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
followercolor = players[player].followercolor;
|
||||
followerskin = players[player].followerskin;
|
||||
|
||||
availabilities = players[player].availabilities;
|
||||
memcpy(availabilities, players[player].availabilities, sizeof(availabilities));
|
||||
|
||||
followitem = players[player].followitem;
|
||||
|
||||
|
|
@ -2432,7 +2432,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->charflags = charflags;
|
||||
p->lastfakeskin = lastfakeskin;
|
||||
|
||||
p->availabilities = availabilities;
|
||||
memcpy(players[player].availabilities, availabilities, sizeof(availabilities));
|
||||
p->followitem = followitem;
|
||||
|
||||
p->starpostnum = starpostnum;
|
||||
|
|
@ -2918,7 +2918,7 @@ void G_AddPlayer(INT32 playernum)
|
|||
{
|
||||
player_t *p = &players[playernum];
|
||||
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)
|
||||
|
|
@ -3690,6 +3690,10 @@ static void G_UpdateVisited(void)
|
|||
|
||||
if ((earnedEmblems = M_CompletionEmblems()))
|
||||
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)
|
||||
|
|
@ -4507,12 +4511,14 @@ void G_SaveGameData(void)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifndef DEVELOP
|
||||
if (usedCheats)
|
||||
{
|
||||
free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Version test
|
||||
|
||||
|
|
|
|||
|
|
@ -231,9 +231,9 @@ void K_InitGrandPrixBots(void)
|
|||
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
||||
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;
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||
{
|
||||
usableskins--;
|
||||
}
|
||||
|
|
@ -557,9 +557,9 @@ void K_RetireBots(void)
|
|||
// Rearrange usable bot skins list to prevent gaps for randomised selection
|
||||
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;
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||
usableskins--;
|
||||
grabskins[i] = grabskins[usableskins];
|
||||
grabskins[usableskins] = MAXSKINS;
|
||||
|
|
|
|||
|
|
@ -2175,6 +2175,9 @@ void M_CharacterSelectInit(void)
|
|||
UINT8 x = skins[i].kartspeed-1;
|
||||
UINT8 y = skins[i].kartweight-1;
|
||||
|
||||
if (!R_SkinUsable(g_localplayers[0], i, false))
|
||||
continue;
|
||||
|
||||
if (setup_chargrid[x][y].numskins >= MAXCLONES)
|
||||
CONS_Alert(CONS_ERROR, "Max character alts reached for %d,%d\n", x+1, y+1);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -2351,7 +2351,7 @@ static int lib_rSetPlayerSkin(lua_State *L)
|
|||
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);
|
||||
SetPlayerSkinByNum(j, i);
|
||||
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);
|
||||
}
|
||||
|
||||
lua_pushboolean(L, R_SkinUsable(j, i));
|
||||
lua_pushboolean(L, R_SkinUsable(j, i, false));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -669,7 +669,7 @@ static int mobj_set(lua_State *L)
|
|||
for (i = 0; i < numskins; i++)
|
||||
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];
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -408,8 +408,6 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->skincolor);
|
||||
else if (fastcmp(field,"skin"))
|
||||
lua_pushinteger(L, plr->skin);
|
||||
else if (fastcmp(field,"availabilities"))
|
||||
lua_pushinteger(L, plr->availabilities);
|
||||
else if (fastcmp(field,"fakeskin"))
|
||||
lua_pushinteger(L, plr->fakeskin);
|
||||
else if (fastcmp(field,"lastfakeskin"))
|
||||
|
|
@ -577,8 +575,6 @@ static int player_set(lua_State *L)
|
|||
}
|
||||
else if (fastcmp(field,"skin"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"availabilities"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"fakeskin"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"lastfakeskin"))
|
||||
|
|
|
|||
35
src/m_cond.c
35
src/m_cond.c
|
|
@ -290,11 +290,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
|
||||
|
|
@ -372,6 +367,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
|
|||
if (res)
|
||||
++somethingUnlocked;
|
||||
}
|
||||
|
||||
return somethingUnlocked;
|
||||
}
|
||||
|
||||
|
|
@ -502,6 +498,35 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
|
|||
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
|
||||
// ----------------
|
||||
|
|
|
|||
|
|
@ -77,6 +77,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;
|
||||
|
|
@ -100,6 +101,7 @@ typedef struct
|
|||
UINT8 showconditionset;
|
||||
INT16 type;
|
||||
INT16 variable;
|
||||
char *stringVar;
|
||||
UINT8 nocecho;
|
||||
UINT8 nochecklist;
|
||||
UINT8 unlocked;
|
||||
|
|
@ -177,4 +179,7 @@ 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);
|
||||
|
||||
#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved)
|
||||
|
|
|
|||
|
|
@ -150,7 +150,12 @@ static void P_NetArchivePlayers(void)
|
|||
|
||||
WRITEUINT8(save_p, players[i].skincolor);
|
||||
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].lastfakeskin);
|
||||
WRITEUINT32(save_p, players[i].score);
|
||||
|
|
@ -472,7 +477,12 @@ static void P_NetUnArchivePlayers(void)
|
|||
|
||||
players[i].skincolor = READUINT8(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].lastfakeskin = READUINT8(save_p);
|
||||
players[i].score = READUINT32(save_p);
|
||||
|
|
|
|||
|
|
@ -7559,10 +7559,14 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
nextmapoverride = 0;
|
||||
skipstats = 0;
|
||||
|
||||
if (!(netgame || multiplayer || demo.playback) && !majormods)
|
||||
if (!demo.playback)
|
||||
{
|
||||
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);
|
||||
|
||||
|
|
@ -8036,8 +8040,8 @@ UINT16 P_PartialAddWadFile(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
|
||||
|
||||
//
|
||||
// edit music defs
|
||||
|
|
|
|||
146
src/r_skins.c
146
src/r_skins.c
|
|
@ -32,6 +32,7 @@
|
|||
#if 0
|
||||
#include "k_kart.h" // K_KartResetPlayerColor
|
||||
#endif
|
||||
#include "k_grandprix.h" // K_CanChangeRules
|
||||
#ifdef HWRENDER
|
||||
#include "hardware/hw_md2.h"
|
||||
#endif
|
||||
|
|
@ -150,36 +151,51 @@ void R_InitSkins(void)
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
UINT32 R_GetSkinAvailabilities(void)
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock)
|
||||
{
|
||||
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)
|
||||
{
|
||||
UINT8 s = min(unlockables[i].variable, MAXSKINS);
|
||||
response |= (1 << s);
|
||||
}
|
||||
if (unlockables[i].type != SECRET_SKIN)
|
||||
continue;
|
||||
|
||||
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
|
||||
// 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 useplayerstruct = (Playing() && playernum != -1);
|
||||
UINT8 i;
|
||||
INT32 skinid;
|
||||
|
||||
if (skinnum == -1)
|
||||
{
|
||||
|
|
@ -187,55 +203,54 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (modeattacking)
|
||||
{
|
||||
// If you have someone else's run, you should be able to take a look
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
|
@ -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!
|
||||
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
|
||||
|
|
@ -305,7 +338,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
|
|||
INT32 i = R_SkinAvailable(skinname);
|
||||
player_t *player = &players[playernum];
|
||||
|
||||
if ((i != -1) && R_SkinUsable(playernum, i))
|
||||
if ((i != -1) && R_SkinUsable(playernum, i, false))
|
||||
{
|
||||
SetSkin(player, i);
|
||||
return;
|
||||
|
|
@ -316,7 +349,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);
|
||||
|
||||
SetSkin(player, 0);
|
||||
SetSkin(player, GetPlayerDefaultSkin(playernum));
|
||||
}
|
||||
|
||||
// Same as SetPlayerSkin, but uses the skin #.
|
||||
|
|
@ -325,7 +358,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
|||
{
|
||||
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);
|
||||
return;
|
||||
|
|
@ -336,7 +369,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);
|
||||
|
||||
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
|
||||
|
|
@ -386,11 +419,14 @@ void SetRandomFakePlayerSkin(player_t* player, boolean fast)
|
|||
}
|
||||
else if (skins[i].flags & SF_IRONMAN)
|
||||
continue;
|
||||
/*if (K_SkinLocked(i))
|
||||
continue;*/
|
||||
if (!R_SkinUsable(player-players, i, true))
|
||||
continue;
|
||||
grabskins[usableskins++] = i;
|
||||
}
|
||||
|
||||
if (!usableskins)
|
||||
I_Error("SetRandomFakePlayerSkin: No valid skins to pick from!?");
|
||||
|
||||
i = grabskins[P_RandomKey(PR_RANDOMSKIN, usableskins)];
|
||||
|
||||
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
|
||||
//
|
||||
void R_AddSkins(UINT16 wadnum)
|
||||
void R_AddSkins(UINT16 wadnum, boolean mainfile)
|
||||
{
|
||||
UINT16 lump, lastlump = 0;
|
||||
char *buf;
|
||||
|
|
@ -810,7 +846,8 @@ next_token:
|
|||
|
||||
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
|
||||
skin_cons_t[numskins].value = numskins;
|
||||
|
|
@ -834,7 +871,7 @@ next_token:
|
|||
//
|
||||
// Patch skin sprites
|
||||
//
|
||||
void R_PatchSkins(UINT16 wadnum)
|
||||
void R_PatchSkins(UINT16 wadnum, boolean mainfile)
|
||||
{
|
||||
UINT16 lump, lastlump = 0;
|
||||
char *buf;
|
||||
|
|
@ -976,7 +1013,8 @@ next_token:
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,11 +84,13 @@ void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
|
|||
void SetFakePlayerSkin(player_t* player, INT32 skinnum);
|
||||
void SetRandomFakePlayerSkin(player_t* player, boolean fast);
|
||||
void ClearFakePlayerSkin(player_t* player);
|
||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum);
|
||||
UINT32 R_GetSkinAvailabilities(void);
|
||||
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins);
|
||||
|
||||
UINT8 *R_GetSkinAvailabilities(boolean demolock);
|
||||
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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue