From 69cb50b37994b5930710f87b3d68dab5aae3c4ec Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 12 Nov 2025 12:40:32 -0500 Subject: [PATCH] Voice cvars --- src/d_netcmd.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++- src/p_mobj.c | 6 ++ src/r_skins.c | 108 ++++++++++++++++++++++++++ src/r_skins.h | 4 +- 4 files changed, 317 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index d6ce95e28..7721b3747 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -142,6 +142,11 @@ static void Followercolor2_OnChange(void); static void Followercolor3_OnChange(void); static void Followercolor4_OnChange(void); +static void Voice_OnChange(void); +static void Voice2_OnChange(void); +static void Voice3_OnChange(void); +static void Voice4_OnChange(void); + static void Color_OnChange(void); static void Color2_OnChange(void); static void Color3_OnChange(void); @@ -344,6 +349,15 @@ consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("followercolor4", "Default", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange) }; +// player's voices...also, uh, saved. +consvar_t cv_voice[MAXSPLITSCREENPLAYERS] = { + CVAR_INIT ("voice", "None", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Voice_OnChange), + CVAR_INIT ("voice2", "None", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Voice2_OnChange), + CVAR_INIT ("voice3", "None", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Voice3_OnChange), + CVAR_INIT ("voice4", "None", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Voice4_OnChange) +}; + + consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL); consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse); @@ -1384,6 +1398,80 @@ static void Skin_FindRealNameSkin(consvar_t *cvar) } } +static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voice, boolean forceme) +{ + // Not the best way to implements this but it will do. + + int i; + const char *value = cvar->string; + kartvoice_t *myvoice = voice; + + if (!myvoice) + { + myvoice = skins[skin_id].voice; + + if (!myvoice) + { + // ...how?! + return; + } + } + + // First, check for freeslotted voices, should they exist at all. + if (numfreeslotvoices) + { + for (i = 0; i < numfreeslotvoices; i++) + { + if (strncmp(value, skinvoices[i + KSKVC_FIRSTFREESLOT].name, sizeof skinvoices[i + KSKVC_FIRSTFREESLOT].name) == 0) + { + // Make sure this voice belongs to the skin we're about to assign it to. + if (skinvoices[i + KSKVC_FIRSTFREESLOT].parent == skin_id || forceme) + { + // Change the cvar to be the value of the name. + CV_StealthSet(cvar, skinvoices[i + KSKVC_FIRSTFREESLOT].name); + } + else + { + // This isn't a valid voice for this skin, so don't assign shit. + CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" doesn't belong to this skin (%s).\n"), skinvoices[i + KSKVC_FIRSTFREESLOT].name, skins[skin_id].name); + + // *Reassign* the voice to whatever the current voice is, at least. + CV_StealthSet(cvar, myvoice->name); + } + + // Return regardless, as we've found the voice we were looking for. + return; + } + } + } + + // Freeslotted voices don't exist or found nothing; check skin voices. + for (i = 0; i < numskinvoices; i++) + { + // Whatever. Go, my code duplication! + if (strncmp(value, skinvoices[i].name, sizeof skinvoices[i].name) == 0) + { + // Make sure this voice belongs to the skin we're about to assign it to. + if (skinvoices[i].parent == skin_id || forceme) + { + // Change the cvar to be the value of the name. + CV_StealthSet(cvar, skinvoices[i].name); + } + else + { + // This isn't a valid voice for this skin, so don't assign shit. + CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" doesn't belong to this skin (%s).\n"), skinvoices[i].name, skins[skin_id].name); + + // *Reassign* the voice cvar to whatever the current voice is, at least. + CV_StealthSet(cvar, myvoice->name); + } + + // Return regardless, as we've found the voice we were looking for. + return; + } + } +} + /** Checks if a name (as received from another player) is okay. * A name is okay if it is no fewer than 1 and no more than ::MAXPLAYERNAME * chars long (not including NUL), it does not begin or end with a space, @@ -1733,12 +1821,16 @@ VaguePartyDescription (int playernum, int size, int default_color) static INT32 snacpending[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; static INT32 chmappending = 0; -// name, color, or skin has changed +// name, color, skin, or voice has changed // static void SendNameAndColor(UINT8 n) { const INT32 playernum = g_localplayers[n]; player_t *player = &players[playernum]; + kartvoice_t *voice; + + // We have no voice, but don't want to topple the house of cards. + boolean permamute = false; char buf[MAXPLAYERNAME+12]; char *p; @@ -1775,11 +1867,26 @@ static void SendNameAndColor(UINT8 n) if (cv_follower[n].value >= numfollowers || cv_follower[n].value < -1) CV_StealthSet(&cv_follower[n], "-1"); + // Check the player's acoustics. + voice = P_GetMobjVoice(player->mo); + + if (!voice) + { + voice = skins[player->skin].voice; + + if (!voice) + { + // ...how?! + permamute = true; + } + } + if (!strcmp(cv_playername[n].string, player_names[playernum]) && cv_playercolor[n].value == player->skincolor && !strcmp(cv_skin[n].string, skins[player->skin].name) && cv_follower[n].value == player->followerskin - && cv_followercolor[n].value == player->followercolor) + && cv_followercolor[n].value == player->followercolor + && (!permamute && !strcmp(cv_voice[n].string, voice->name))) return; player->availabilities = R_GetSkinAvailabilities(); @@ -1825,6 +1932,26 @@ static void SendNameAndColor(UINT8 n) SetPlayerSkin(playernum, cv_skin[n].string); } + // Need to update voices after the fact. + if (!cv_voice[n].string) + CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + + SetPlayerVoice(playernum, cv_voice[n].string); + + const kartvoice_t *valuevoice = P_GetMobjVoice(player->mo); + + if (valuevoice) + { + CV_StealthSet(&cv_voice[n], valuevoice->name); + cv_voice[n].value = (UINT16)valuevoice->id; + } + else + { + // ...this is my paranoia speaking. + CV_StealthSet(&cv_voice[n], "sonic_voice"); + cv_voice[n].value = 0; + } + return; } @@ -1852,6 +1979,9 @@ static void SendNameAndColor(UINT8 n) { CV_StealthSet(&cv_skin[n], DEFAULTSKIN); cv_skin[n].value = 0; + + CV_StealthSet(&cv_voice[n], "sonic_voice"); + cv_voice[n].value = 0; } // Finally write out the complete packet and send it off. @@ -1861,6 +1991,7 @@ static void SendNameAndColor(UINT8 n) WRITEUINT16(p, (UINT16)cv_skin[n].value); WRITESINT8(p, (SINT8)cv_follower[n].value); WRITEUINT16(p, (UINT16)cv_followercolor[n].value); + WRITEUINT16(p, (UINT16)cv_voice[n].value); SendNetXCmdForPlayer(n, XD_NAMEANDCOLOR, buf, p - buf); } @@ -1870,7 +2001,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) player_t *p = &players[playernum]; char name[MAXPLAYERNAME+1]; UINT16 color, followercolor; - UINT16 skin; + UINT16 skin, voice; SINT8 follower; SINT8 localplayer = -1; UINT16 i; @@ -1902,6 +2033,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) skin = READUINT16(*cp); follower = READSINT8(*cp); followercolor = READUINT16(*cp); + voice = READUINT16(*cp); // set name if (player_name_changes[playernum] < MAXNAMECHANGES) @@ -1981,6 +2113,9 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) else SetPlayerSkinByNum(playernum, skin); + // set voice + SetPlayerVoiceByNum(playernum, voice); + // set follower colour: // Don't bother doing garbage and kicking if we receive None, // this is both silly and a waste of time, @@ -7218,6 +7353,68 @@ static void Color4_OnChange(void) lastgoodcolor[3] = cv_playercolor[3].value; } +static void __voice_cvar_func(INT32 pid, UINT8 pnum) +{ + kartvoice_t *myvoice = P_GetMobjVoice(players[pid].mo); + + if (!myvoice) + { + myvoice = skins[players[pid].skin].voice; + + if (!myvoice) + { + // ...how?! + return; + } + } + + Skin_FindValidDub(&cv_voice[pnum], players[pid].skin, myvoice, false); + + if (!Playing()) + return; // do whatever you want + + if (pnum > 0 && !splitscreen) + return; // do whatever you want + + if (pnum < 1) + { + if (!(cht_debug || devparm) && !(multiplayer || netgame) // In single player. + && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y + { + CV_StealthSet(&cv_voice[pnum], myvoice->name); + return; + } + } + + if (!P_PlayerMoving(pid)) + SendNameAndColor(pnum); + else + { + CONS_Alert(CONS_NOTICE, M_GetText("You can't change your voice at the moment.\n")); + CV_StealthSet(&cv_voice[pnum], myvoice->name); + } +} + +static void Voice_OnChange(void) +{ + __voice_cvar_func(consoleplayer, 0); +} + +static void Voice2_OnChange(void) +{ + __voice_cvar_func(g_localplayers[1], 1); +} + +static void Voice3_OnChange(void) +{ + __voice_cvar_func(g_localplayers[2], 2); +} + +static void Voice4_OnChange(void) +{ + __voice_cvar_func(g_localplayers[3], 3); +} + /** Displays the result of the chat being muted or unmuted. * The server or remote admin should already know and be able to talk * regardless, so this is only displayed to clients. diff --git a/src/p_mobj.c b/src/p_mobj.c index a047a5c6e..b7be3dcfa 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -14951,6 +14951,12 @@ fixed_t P_GetMobjZMovement(mobj_t *mo) kartvoice_t *P_GetMobjVoice(const mobj_t *mo) { + if (P_MobjWasRemoved(mo)) + { + // Nonexistent or NULL object. + return NULL; + } + if (mo->voice) { return mo->voice; diff --git a/src/r_skins.c b/src/r_skins.c index 8f6441aca..64c23d4ac 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -640,6 +640,57 @@ void SetPlayerSkin(INT32 playernum, const char *skinname) SetPlayerSkinByNum(playernum, 0); } +// network code calls this when a 'voice change' is received +void SetPlayerVoice(INT32 playernum, const char *voicename) +{ + player_t *player = &players[playernum]; + INT32 i; + + if (P_MobjWasRemoved(player->mo)) + { + // No object to give a voice to. + return; + } + + const kartvoice_t *myvoice = P_GetMobjVoice(player->mo); + + if (!strncmp(voicename, myvoice->name, sizeof myvoice->name)) + { + // Hey... this voice is the same as before! + return; + } + + // We've done the precautions to check for disallowed dubs in the cvar, just do a blind-read for the needed name. + // 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. + player->mo->voice = &skinvoices[i + KSKVC_FIRSTFREESLOT]; + return; + } + } + } + + // Freeslotted voices don't exist or 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. + player->mo->voice = &skinvoices[i]; + return; + } + } + + // Couldn't find a voice. Default to the skin's. + player->mo->voice = NULL; +} + // Same as SetPlayerSkin, but uses the skin #. // network code calls this when a 'skin change' is received void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) @@ -685,6 +736,12 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) if (player->mo) { player->mo->skin = skin; + + if (player->mo->voice) + { + // Clear out the voice pointer, so we can fall back to using skin voices. + player->mo->voice = NULL; + } P_SetScale(player->mo, player->mo->scale); P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames } @@ -708,6 +765,57 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) SetPlayerSkinByNum(playernum, 0); // not found, put in the default skin } +// network code calls this when a 'voice change' is received +void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) +{ + player_t *player = &players[playernum]; + INT32 i; + + if (P_MobjWasRemoved(player->mo)) + { + // No object to give a voice to. + return; + } + + const kartvoice_t *myvoice = P_GetMobjVoice(player->mo); + + if (myvoice->id == voicenum) + { + // Hey... this voice is the same as before! + return; + } + + // We've done the precautions to check for disallowed dubs in the cvar, just do a blind-read for the needed name. + // First, check for freeslotted voices, should they exist at all. + if (numfreeslotvoices) + { + for (i = 0; i < numfreeslotvoices; i++) + { + if (skinvoices[i + KSKVC_FIRSTFREESLOT].id == voicenum) + { + // Found a voice. + player->mo->voice = &skinvoices[i + KSKVC_FIRSTFREESLOT]; + return; + } + } + } + + // Freeslotted voices don't exist or found nothing; check skin voices. + for (i = 0; i < numskinvoices; i++) + { + // Whatever. Go, my code duplication! + if (skinvoices[i].id == voicenum) + { + // Found a voice. + player->mo->voice = &skinvoices[i]; + return; + } + } + + // Couldn't find a voice. Default to the skin's. + player->mo->voice = NULL; +} + // // Add skins from a pwad, each skin preceded by 'S_SKIN' marker // diff --git a/src/r_skins.h b/src/r_skins.h index 4143dbb50..363ef8738 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -40,7 +40,7 @@ extern consvar_t cv_skinselectstyle, cv_skinselectsort; typedef enum { KSKVC_MAXVANILLA = MAXSKINS, - KSKVC_FIRSTFREESLOT, + KSKVC_FIRSTFREESLOT = KSKVC_MAXVANILLA, KSKVC_LASTFREESLOT = 65535, MAXSKINVOICES } kartvoicetype_e; @@ -153,6 +153,8 @@ void R_AddSkins(UINT16 wadnum); UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player); UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe); +void SetPlayerVoice(INT32 playernum, const char *voicename); +void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum); sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound); #ifdef __cplusplus