From 32e4080a706d2935a3cfa0b7bd3376871ad7d461 Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 09:45:34 -0500 Subject: [PATCH] [DEMOVERSION INCREASE] Save/Load voices in replays Probably should have been a frame-1 thing, but my brain doesn't work that way --- src/g_demo.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/r_skins.c | 56 +++++++++++++++++++++++++++++++++++++++++++ src/r_skins.h | 1 + 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/g_demo.c b/src/g_demo.c index 0e359bbd2..8c3e83b02 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -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); diff --git a/src/r_skins.c b/src/r_skins.c index a4796e315..f6b10f23b 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -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; } // diff --git a/src/r_skins.h b/src/r_skins.h index bde9eec23..8959b2475 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -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);