[DEMOVERSION INCREASE] Save/Load voices in replays

Probably should have been a frame-1 thing, but my brain doesn't work that way
This commit is contained in:
yamamama 2025-11-13 09:45:34 -05:00
parent ca7ab13853
commit 32e4080a70
3 changed files with 122 additions and 1 deletions

View file

@ -111,7 +111,7 @@ demoghost *ghosts = NULL;
// DEMO RECORDING
//
#define DEMOVERSION 0x000D
#define DEMOVERSION 0x000E
#define DEMOHEADER "\xF0" "BlanReplay" "\x0F"
#define DF_GHOST 0x01 // This demo contains ghost data too!
@ -222,6 +222,7 @@ typedef struct
char color[16+1];
char follower[16+1];
char followercolor[16+1];
char voice[32+1];
UINT32 score;
UINT16 powerlevel;
@ -342,6 +343,7 @@ typedef struct
// DXD_SKIN
char skinname[17];
char voicename[33];
UINT8 kartspeed;
UINT8 kartweight;
@ -623,7 +625,12 @@ static UINT8 *G_ReadRawExtraData(extradata_t *extra, UINT8 *dp, UINT16 version)
extra->skinname[16] = '\0';
}
else
{
READSTRINGL(dp, extra->skinname, 16+1);
if (version > 0x000D)
READSTRINGL(dp, extra->voicename, 32+1);
}
extra->kartspeed = READUINT8(dp);
extra->kartweight = READUINT8(dp);
}
@ -682,6 +689,7 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
boolean raflag = false;
boolean serverinfo = true;
boolean rapreset = true; // + extended serverinfo length
boolean dubs = true; // Multiple voices
// these may not be present in old demo formats, so initialize them
// also initialize them so the header can be free'd without issues
@ -716,6 +724,9 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header)
case 0x000C:
rapreset = false;
raflag = true;
/* FALLTHRU */
case 0x000D:
dubs = false;
break;
default: // too old, cannot support.
@ -946,6 +957,10 @@ skipfiles:
READSTRINGL(dp, plr->name, 21+1);
READSTRINGL(dp, plr->skin, 16+1);
READSTRINGL(dp, plr->color, 16+1);
if (dubs)
READSTRINGL(dp, plr->voice, 32+1);
READSTRINGL(dp, plr->follower, 16+1);
READSTRINGL(dp, plr->followercolor, 16+1);
}
@ -1247,6 +1262,31 @@ void G_ReadDemoExtraData(void)
if (stricmp(skins[players[p].skin].name, extra.skinname) != 0)
FindClosestSkinForStats(p, extra.kartspeed, extra.kartweight);
// Voice
const INT32 vce = R_FindIDForVoice(extra.voicename);
if (vce == MAXSKINVOICES)
{
// The above function didn't find a voice, so we'll just fall back
// to the skin's voice.
players[p].voice_id = skins[players[p].skin].voice->id;
}
else
{
players[p].voice_id = (UINT16)vce;
}
if (skinvoices[players[p].voice_id].parent ==
skins[players[p].skin].voice->parent &&
skinvoices[players[p].voice_id].id !=
skins[players[p].skin].voice->id &&
!P_MobjWasRemoved(players[p].mo))
{
// This player's mobj exists, and their voice is valid, and isn't
// the default. So, attach the voice to the mobj!
players[p].mo->voice = &skinvoices[players[p].voice_id];
}
players[p].kartspeed = extra.kartspeed;
players[p].kartweight = extra.kartweight;
}
@ -1350,6 +1390,10 @@ void G_WriteDemoExtraData(void)
// Skin
WRITESTRINGL(demobuf.p, skins[players[i].skin].name, 16+1);
// Voice
WRITESTRINGL(demobuf.p, skinvoices[players[i].voice_id].name, 32+1);
// Stats
WRITEUINT8(demobuf.p, skins[players[i].skin].kartspeed);
WRITEUINT8(demobuf.p, skins[players[i].skin].kartweight);
@ -2933,6 +2977,9 @@ void G_BeginRecording(void)
// Color
WRITESTRINGL(demobuf.p, skincolors[player->skincolor].name, 16+1);
// Voice
WRITESTRINGL(demobuf.p, skinvoices[player->voice_id].name, 32+1);
// Save follower's skin name
// PS: We must check for 'follower' to determine if the followerskin is valid. It's going to be 0 if we don't have a follower, but 0 is also absolutely a valid follower!
// Doesn't really matter if the follower mobj is valid so long as it exists in a way or another.
@ -3803,6 +3850,23 @@ void G_DoPlayDemo(char *defdemoname)
break;
}
// Voice
// We'll only assign IDs, since the actual voice assignment happens during
// player spawn.
const INT32 vce = R_FindIDForVoice(plr->voice);
if (vce == MAXSKINVOICES)
{
// The above function didn't find a voice, so we'll just fall back to
// the skin's voice.
players[p].voice_id = skins[players[p].skin].voice->id;
}
else
{
players[p].voice_id = (UINT16)vce;
}
// Follower
K_SetFollowerByName(p, plr->follower);

View file

@ -647,6 +647,40 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
SetPlayerSkinByNum(playernum, 0);
}
// Gets the corresponding voice ID for a given voice's name.
// Returns MAXSKINVOICES if nothing is found.
kartvoicetype_e R_FindIDForVoice(const char *voicename)
{
INT32 i;
// First, check for freeslotted voices, should they exist at all.
if (numfreeslotvoices)
{
for (i = 0; i < numfreeslotvoices; i++)
{
if (strncmp(voicename, skinvoices[i + KSKVC_FIRSTFREESLOT].name, sizeof skinvoices[i + KSKVC_FIRSTFREESLOT].name) == 0)
{
// Found a voice.
return (kartvoicetype_e)(i + KSKVC_FIRSTFREESLOT);
}
}
}
// Freeslotted voices don't exist, or we've found nothing; check skin voices.
for (i = 0; i < numskinvoices; i++)
{
// Whatever. Go, my code duplication!
if (strncmp(voicename, skinvoices[i].name, sizeof skinvoices[i].name) == 0)
{
// Found a voice.
return (kartvoicetype_e)i;
}
}
// Couldn't find a voice.
return MAXSKINVOICES;
}
// network code calls this when a 'voice change' is received
void SetPlayerVoice(INT32 playernum, const char *voicename)
{
@ -658,6 +692,9 @@ void SetPlayerVoice(INT32 playernum, const char *voicename)
// No object to give a voice to.
// Fall back to our skin.
player->voice_id = skins[player->skin].voice->id;
// Tell the demo that we've updated our voice_id as well.
demo_extradata[playernum] |= DXD_SKIN;
return;
}
@ -668,6 +705,9 @@ void SetPlayerVoice(INT32 playernum, const char *voicename)
// Hey... this voice is the same as before!
// WAIT! Before we go, reset the player's voice ID!
player->voice_id = myvoice->id;
// AND tell the game to rescan for skins/voices!
demo_extradata[playernum] |= DXD_SKIN;
return;
}
@ -682,6 +722,7 @@ void SetPlayerVoice(INT32 playernum, const char *voicename)
// Found a voice.
player->mo->voice = &skinvoices[i + KSKVC_FIRSTFREESLOT];
player->voice_id = (kartvoicetype_e)(i + KSKVC_FIRSTFREESLOT);
demo_extradata[playernum] |= DXD_SKIN;
return;
}
}
@ -696,6 +737,7 @@ void SetPlayerVoice(INT32 playernum, const char *voicename)
// Found a voice.
player->mo->voice = &skinvoices[i];
player->voice_id = (kartvoicetype_e)i;
demo_extradata[playernum] |= DXD_SKIN;
return;
}
}
@ -703,6 +745,9 @@ void SetPlayerVoice(INT32 playernum, const char *voicename)
// Couldn't find a voice. Default to the skin's.
player->mo->voice = NULL;
player->voice_id = skins[player->skin].voice->id;
// Tell the demo that we've updated our voice_id as well.
demo_extradata[playernum] |= DXD_SKIN;
}
// Same as SetPlayerSkin, but uses the skin #.
@ -790,6 +835,9 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum)
// No object to give a voice to.
// Fall back to our skin.
player->voice_id = skins[player->skin].voice->id;
// Tell the demo that we've updated our voice_id as well.
demo_extradata[playernum] |= DXD_SKIN;
return;
}
@ -800,6 +848,9 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum)
// Hey... this voice is the same as before!
// WAIT! Before we go, reset the player's voice ID!
player->voice_id = myvoice->id;
// AND tell the game to rescan for skins/voices!
demo_extradata[playernum] |= DXD_SKIN;
return;
}
@ -814,6 +865,7 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum)
// Found a voice.
player->mo->voice = &skinvoices[i + KSKVC_FIRSTFREESLOT];
player->voice_id = (kartvoicetype_e)(i + KSKVC_FIRSTFREESLOT);
demo_extradata[playernum] |= DXD_SKIN;
return;
}
}
@ -828,6 +880,7 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum)
// Found a voice.
player->mo->voice = &skinvoices[i];
player->voice_id = (kartvoicetype_e)i;
demo_extradata[playernum] |= DXD_SKIN;
return;
}
}
@ -835,6 +888,9 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum)
// Couldn't find a voice. Default to the skin's.
player->mo->voice = NULL;
player->voice_id = skins[player->skin].voice->id;
// Signal a voice_id change to the demo.
demo_extradata[playernum] |= DXD_SKIN;
}
//

View file

@ -160,6 +160,7 @@ UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe);
void R_ClearVoice(kartvoicetype_e voicetype);
void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound);
kartvoicetype_e R_FindIDForVoice(const char *voicename);
void SetPlayerVoice(INT32 playernum, const char *voicename);
void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum);
sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound);