From 4b9f9999cd1a0bf4c0de3f478e992759530edd53 Mon Sep 17 00:00:00 2001 From: Anonimus Date: Wed, 12 Nov 2025 03:08:49 -0500 Subject: [PATCH 01/44] Kartvoice_t Supercedes the soundsid array and may allow for a dubbing system later on --- src/f_finale.c | 15 +++- src/hardware/hw3sound.c | 12 ++- src/k_kart.c | 20 ++++- src/lua_baselib.c | 11 ++- src/p_mobj.c | 14 ++++ src/p_mobj.h | 7 ++ src/p_saveg.c | 10 +++ src/p_user.c | 10 ++- src/r_skins.c | 176 ++++++++++++++++++++++++++++++++++++++-- src/r_skins.h | 44 +++++++++- src/s_sound.c | 12 ++- src/typedef.h | 1 + 12 files changed, 310 insertions(+), 22 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index d6ce50c6e..29a79c042 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -414,7 +414,11 @@ void F_IntroTicker(void) // Need to use M_Random otherwise it always uses the same sound INT32 rskin = M_RandomKey(numskins); UINT8 rtaunt = M_RandomKey(2); - sfxenum_t rsound = skins[rskin].soundsid[SKSKBST1+rtaunt]; + + if (!skins[rskin].voice) // Should NOT happen! + I_Error("F_IntroTicker: Skin no. %d has no voice assigned", rskin); + + sfxenum_t rsound = skins[rskin].voice->boost[rtaunt]; S_StartSound(NULL, rsound); } } @@ -443,11 +447,16 @@ void F_IntroTicker(void) // Tails: NepDisk // Chao: Alug // Aiai: GHG - // Sakura: Anon + // Sakura: Yama // Doomguy: Minenice char chars[6][10] = {"tails", "chao", "aiai", "sakura", "doom"}; SINT8 random = M_RandomRange(0, 4); - sfxenum_t rsound = skins[R_SkinAvailable(chars[random])].soundsid[SKSKWIN]; + const INT32 rskin = R_SkinAvailable(chars[random]); + + if (!skins[rskin].voice) // Should NOT happen! + I_Error("F_IntroTicker: Skin no. %d has no voice assigned", rskin); + + sfxenum_t rsound = skins[rskin].voice->win; S_StartSound(NULL, sfx_flgcap); S_StartSound(NULL, rsound); } diff --git a/src/hardware/hw3sound.c b/src/hardware/hw3sound.c index 52f691b9e..2d478938f 100644 --- a/src/hardware/hw3sound.c +++ b/src/hardware/hw3sound.c @@ -345,6 +345,7 @@ static void make_outphase_sfx(void *dest, void *src, INT32 size) INT32 HW3S_I_StartSound(const void *origin_p, source3D_data_t *source_parm, channel_type_t c_type, sfxenum_t sfx_id, INT32 volume, INT32 pitch, INT32 sep) { sfxinfo_t *sfx; + kartvoice_t *voice; const mobj_t *origin = (const mobj_t *)origin_p; source3D_data_t source3d_data; INT32 s_num = 0; @@ -361,9 +362,14 @@ INT32 HW3S_I_StartSound(const void *origin_p, source3D_data_t *source_parm, chan if (sfx->skinsound!=-1 && origin && origin->skin) { - // it redirect player sound to the sound in the skin table - sfx_id = ((skin_t *)origin->skin)->soundsid[sfx->skinsound]; - sfx = &S_sfx[sfx_id]; + // it redirect player sound to any voice values, should they exist + voice = P_GetMobjVoice(origin); + + if (voice) + { + sfx_id = sfx_id = P_GetVoiceSFX(voice, sfx->skinsound); // ((skin_t *)origin->skin)->soundsid[sfx->skinsound] + sfx = &S_sfx[sfx_id]; + } } if (!sfx->data) diff --git a/src/k_kart.c b/src/k_kart.c index 155d67d24..ab92002ad 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1923,7 +1923,15 @@ void K_PlayPainSound(mobj_t *source, mobj_t *other) { sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons - sfxenum_t sfx_id = ((skin_t *)source->skin)->soundsid[S_sfx[sfx_khurt1 + pick].skinsound]; + kartvoice_t *src_voice = P_GetMobjVoice(source); + + if (!src_voice) + { + // No voice! + return; + } + + sfxenum_t sfx_id = src_voice->pain[pick]; boolean alwaysHear = false; if (cv_kartvoices.value) @@ -1952,7 +1960,15 @@ void K_PlayPainSound(mobj_t *source, mobj_t *other) void K_PlayHitEmSound(mobj_t *source, mobj_t *other) { - sfxenum_t sfx_id = ((skin_t *)source->skin)->soundsid[S_sfx[sfx_khitem].skinsound]; + kartvoice_t *src_voice = P_GetMobjVoice(source); + + if (!src_voice) + { + // No voice! + return; + } + + sfxenum_t sfx_id = src_voice->hitem; boolean alwaysHear = false; if (cv_kartvoices.value) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 29e41d0ec..77dc3e2c3 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3679,12 +3679,21 @@ static int lib_kGloatSound(lua_State *L) static int lib_kLossSound(lua_State *L) { mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); // let's require a mobj for consistency with the other functions + kartvoice_t *mo_voice; sfxenum_t sfx_id; NOHUD if (!mobj->player) return luaL_error(L, "K_PlayLossSound: mobj_t isn't a player object."); - sfx_id = ((skin_t *)mobj->skin)->soundsid[S_sfx[sfx_klose].skinsound]; + mo_voice = P_GetMobjVoice(mobj); + + if (!mo_voice) + { + // No voice! + return 0; + } + + sfx_id = mo_voice->lose; S_StartSound(mobj, sfx_id); return 0; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 86ed4a66b..a047a5c6e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -14949,6 +14949,20 @@ fixed_t P_GetMobjZMovement(mobj_t *mo) return P_ReturnThrustY(mo, slope->zangle, P_ReturnThrustX(mo, angDiff, speed)); } +kartvoice_t *P_GetMobjVoice(const mobj_t *mo) +{ + if (mo->voice) + { + return mo->voice; + } + else if (mo->skin && ((skin_t *)mo->skin)->voice) + { + return ((skin_t *)mo->skin)->voice; + } + + return NULL; +} + // // Thing IDs / tags // diff --git a/src/p_mobj.h b/src/p_mobj.h index 000dd2613..68b94c098 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -352,6 +352,11 @@ struct mobj_t void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin) // Player and mobj sprites in multiplayer modes are modified // using an internal color lookup table for re-indexing. + + // Pointer to a set of voice values. If this doesn't exist, + // the game attempts to use tke skin's voice, should this object have a skin assigned. + kartvoice_t *voice; + UINT16 color; // This replaces MF_TRANSLATION. Use 0 for default (no translation). // More list: links in sector (if needed) @@ -594,6 +599,8 @@ void P_RingZMovement(mobj_t *mo); boolean P_SceneryZMovement(mobj_t *mo); void P_PlayerZMovement(mobj_t *mo); +kartvoice_t *P_GetMobjVoice(const mobj_t *mo); + extern INT32 modulothing; #define MAXHUNTEMERALDS 64 diff --git a/src/p_saveg.c b/src/p_saveg.c index 8b430dcc0..0ed4fbff3 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -1846,6 +1846,7 @@ enum mobj_diff_t MD3_BAKEDOFFSET, MD3_EXTVAL3, MD3_LIFETIME, + MD3_VOICE, MD__MAX }; @@ -2008,6 +2009,7 @@ static void DiffMobj(const mobj_t *mobj, UINT32 diff[]) DIFF(mobj->bakexoff || mobj->bakeyoff || mobj->bakezoff || mobj->bakexpiv || mobj->bakeypiv || mobj->bakezpiv, MD3_BAKEDOFFSET); DIFF(mobj->extravalue3, MD3_EXTVAL3); DIFF(mobj->mobjlifetime, MD3_LIFETIME); + DIFF(mobj->voice, MD3_VOICE); } static thinker_t *SyncMobjThinker(savebuffer_t *save, actionf_p1 thinker, thinker_t *th, UINT8 type) @@ -2333,6 +2335,14 @@ static thinker_t *SyncMobjThinker(savebuffer_t *save, actionf_p1 thinker, thinke SYNCF(MD3_EXTVAL3, mobj->extravalue3); SYNCF(MD3_LIFETIME, mobj->mobjlifetime); + if (GETB(MD3_VOICE)) + { + if (save->write) + WRITEUINT16(save->p, (UINT16)((kartvoice_t *)mobj->voice - skinvoices)); + else + mobj->voice = &skinvoices[READUINT16(save->p)]; + } + if (!save->write) { // Reset some non-synch values diff --git a/src/p_user.c b/src/p_user.c index e9f6dff94..f4cb5cb51 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1313,9 +1313,15 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) if (cv_kartvoices.value) { - const INT32 sfx_id = (losing ? sfx_klose : sfx_kwin); + //const INT32 sfx_id = (losing ? sfx_klose : sfx_kwin); skin_t *playerskin = &skins[player->skin]; - S_StartSound(player->mo, playerskin->soundsid[S_sfx[sfx_id].skinsound]); + + const kartvoice_t *playervoice = (!P_MobjWasRemoved(player->mo) ? P_GetMobjVoice(player->mo) : playerskin->voice); + + if (playervoice) + { + S_StartSound(player->mo, (losing ? playervoice->lose : playervoice->win)); + } } // See Y_StartIntermission timer handling diff --git a/src/r_skins.c b/src/r_skins.c index bd8d70e19..8f6441aca 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -39,8 +39,11 @@ #include "discord.h" INT32 numskins = 0; +INT32 numskinvoices = 0; +INT32 numfreeslotvoices = 0; skin_t skins[MAXSKINS]; INT32 skinsorted[MAXSKINS]; +kartvoice_t skinvoices[MAXSKINVOICES]; // FIXTHIS: don't work because it must be inistilised before the config load //#define SKINVALUES @@ -170,7 +173,132 @@ UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe) return P_GetSkinSprite2(skin, spr2, NULL); } -static void Sk_SetDefaultValue(skin_t *skin) +static void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound) +{ + INT32 usesound; + + switch(skinsound) + { + case SKSKWIN: // Win quote + voice->win = writesound; + break; + case SKSKLOSE: // Lose quote + voice->lose = writesound; + break; + case SKSKPAN1: // Pain + case SKSKPAN2: + usesound = skinsound - SKSKPAN1; + if (usesound < 2) + { + voice->pain[usesound] = writesound; + } + else + { + I_Error("Sk_SetSkinVoiceValue: Attempted to allocate a pain sound of ID %d.\n", usesound); + } + + break; + case SKSKATK1: // Offense item taunt + case SKSKATK2: + usesound = skinsound - SKSKATK1; + if (usesound < 2) + { + voice->attack[usesound] = writesound; + } + else + { + I_Error("Sk_SetSkinVoiceValue: Attempted to allocate an attack sound of ID %d.\n", usesound); + } + break; + case SKSKBST1: // Boost item taunt + case SKSKBST2: + usesound = skinsound - SKSKBST1; + if (usesound < 2) + { + voice->boost[usesound] = writesound; + } + else + { + I_Error("Sk_SetSkinVoiceValue: Attempted to allocate a boost sound of ID %d.\n", usesound); + } + break; + case SKSKSLOW: // Overtake taunt + voice->overtake = writesound; + break; + case SKSKHITM: // Hit confirm taunt + voice->hitem = writesound; + break; + case SKSKPOWR: // Power item taunt + voice->power = writesound; + break; + default: + // Ignore any values outside of this range. + CONS_Alert(CONS_NOTICE, "Sk_SetSkinVoiceValue: ID %d isn't in the range of possible skin sounds (0 to %d)! Ignoring...\n", skinsound, NUMSKINSOUNDS - 1); + break; + } +} + +sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound) +{ + INT32 usesound; + + switch(skinsound) + { + case SKSKWIN: // Win quote + return voice->win; + case SKSKLOSE: // Lose quote + return voice->lose; + case SKSKPAN1: // Pain + case SKSKPAN2: + usesound = skinsound - SKSKPAN1; + if (usesound < 2) + { + return voice->pain[usesound]; + } + else + { + I_Error("P_GetVoiceSFX: Attempted to retrieve a pain sound of ID %d.\n", usesound); + } + + break; + case SKSKATK1: // Offense item taunt + case SKSKATK2: + usesound = skinsound - SKSKATK1; + if (usesound < 2) + { + return voice->attack[usesound]; + } + else + { + I_Error("P_GetVoiceSFX: Attempted to retrieve an attack sound of ID %d.\n", usesound); + } + break; + case SKSKBST1: // Boost item taunt + case SKSKBST2: + usesound = skinsound - SKSKBST1; + if (usesound < 2) + { + return voice->boost[usesound]; + } + else + { + I_Error("P_GetVoiceSFX: Attempted to retrieve a boost sound of ID %d.\n", usesound); + } + break; + case SKSKSLOW: // Overtake taunt + return voice->overtake; + case SKSKHITM: // Hit confirm taunt + return voice->hitem; + case SKSKPOWR: // Power item taunt + return voice->power; + default: + return sfx_thok; + } + + return sfx_thok; +} + +static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice) { INT32 i; // @@ -201,8 +329,24 @@ static void Sk_SetDefaultValue(skin_t *skin) skin->highresscale = FRACUNIT; for (i = 0; i < sfx_skinsoundslot0; i++) + { if (S_sfx[i].skinsound != -1) + { skin->soundsid[S_sfx[i].skinsound] = i; + Sk_SetSkinVoiceValue(skin_voice, S_sfx[i].skinsound, i); + } + } + + // TODO: Make this hexadecimal + snprintf(skin_voice->name, + sizeof skin_voice->name, "voice %u", (UINT32)(skin_voice-skinvoices)); + skin_voice->name[sizeof skin_voice->name - 1] = '\0'; + + skin_voice->parent = (UINT16)(skin - skins); + + skin_voice->id = skin_voice-skinvoices; + + skin->voice = skin_voice; } static void R_IHateThatHedgehog(UINT16 wadnum); @@ -776,10 +920,14 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski static void R_IHateThatHedgehog(UINT16 wadnum) { skin_t *skin = &skins[0]; - Sk_SetDefaultValue(skin); + kartvoice_t *skin_voice = &skinvoices[0]; + Sk_SetDefaultValue(skin, skin_voice); skin->wadnum = wadnum; strcpy(skin->name, "sonic"); + strcpy(skin->voice->name, "sonic_voice"); + skin->voice->parent = 0; + #ifdef SKINVALUES skin_cons_t[0].value = 0; skin_cons_t[0].strvalue = skin->name; @@ -915,7 +1063,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) else // let's check if it's a sound, otherwise error out { boolean found = false; - sfxenum_t i; + sfxenum_t i, newskinsound; size_t stokenadjust; // Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.) @@ -940,8 +1088,14 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) && !stricmp(S_sfx[i].name, stoken + stokenadjust)) { - skin->soundsid[S_sfx[i].skinsound] = - S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].flags, true); + newskinsound = S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].flags, true); + skin->soundsid[S_sfx[i].skinsound] = newskinsound; + + if (skin->voice) + { + Sk_SetSkinVoiceValue(skin->voice, S_sfx[i].skinsound, newskinsound); + } + found = true; } } @@ -962,6 +1116,7 @@ void R_AddSkins(UINT16 wadnum) char *value; size_t size; skin_t *skin; + kartvoice_t *skin_voice; boolean realname; boolean iscompatskin = false; @@ -991,7 +1146,8 @@ void R_AddSkins(UINT16 wadnum) // set defaults skin = &skins[numskins]; - Sk_SetDefaultValue(skin); + skin_voice = &skinvoices[numskins]; + Sk_SetDefaultValue(skin, skin_voice); skin->wadnum = wadnum; realname = false; // parse @@ -1018,7 +1174,11 @@ void R_AddSkins(UINT16 wadnum) INT32 skinnum = R_SkinAvailable(value); strlwr(value); if (skinnum == -1) + { STRBUFCPY(skin->name, value); + STRBUFCPY(skin->voice->name, va("%s_voice", value)); + skin->voice->parent = (UINT16)(skin - skins); + } // the skin name must uniquely identify a single skin // if the name is already used I make the name 'namex' // using the default skin name's number set above @@ -1031,9 +1191,13 @@ void R_AddSkins(UINT16 wadnum) "%s%d", value, numskins); value2[stringspace - 1] = '\0'; if (R_SkinAvailable(value2) == -1) + { // I'm lazy so if NEW name is already used I leave the 'skin x' // default skin name set in Sk_SetDefaultValue STRBUFCPY(skin->name, value2); + STRBUFCPY(skin->voice->name, va("%s_voice", value2)); + skin->voice->parent = (UINT16)(skin - skins); + } Z_Free(value2); } diff --git a/src/r_skins.h b/src/r_skins.h index 82669bc76..4143dbb50 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -27,6 +27,7 @@ extern "C" { /// Defaults #define SKINNAMESIZE 16 +#define VOICENAMESIZE 32 #define SKINRIVALS 3 // should be all lowercase!! S_SKIN processing does a strlwr #define DEFAULTSKIN "sonic" @@ -36,6 +37,38 @@ extern "C" { extern consvar_t cv_skinselectstyle, cv_skinselectsort; +typedef enum +{ + KSKVC_MAXVANILLA = MAXSKINS, + KSKVC_FIRSTFREESLOT, + KSKVC_LASTFREESLOT = 65535, + MAXSKINVOICES +} kartvoicetype_e; + +// Player voice struct +// ## TODO: +// - Dub implementation (freeslotting, voice lookup, assigning voices on race start) +// - Get rid of soundsid for good, replace it with some hacky compat system +struct kartvoice_t +{ + UINT32 id; + + char name[VOICENAMESIZE+1]; // INT16 descriptive name of the voice. + + // UINT16 ID of the parent skin this voice belongs to. + // Checked when the voice is assigned to prevent erroneous dubs. + UINT16 parent; + + sfxenum_t win; + sfxenum_t lose; + sfxenum_t pain[2]; + sfxenum_t attack[2]; + sfxenum_t boost[2]; + sfxenum_t overtake; + sfxenum_t hitem; + sfxenum_t power; +}; + /// The skin_t struct struct skin_t { @@ -64,7 +97,11 @@ struct skin_t char rivals[SKINRIVALS][SKINNAMESIZE+1]; // Your top 3 rivals for GP mode. Uses names so that you can reference skins that aren't added // specific sounds per skin - sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table + sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table (Deprecated; remove eventually) + + // Pointer to a skin's voice. + // If an mobj_t doesn't have a voice, it defaults to this value, given a skin is in use. + kartvoice_t *voice; // contains super versions too spritedef_t sprites[NUMPLAYERSPRITES*2]; @@ -91,9 +128,10 @@ enum skinmenusortoption }; /// Externs -extern INT32 numskins; +extern INT32 numskins, numskinvoices, numfreeslotvoices; extern skin_t skins[MAXSKINS]; extern INT32 skinsorted[MAXSKINS]; +extern kartvoice_t skinvoices[MAXSKINVOICES]; extern CV_PossibleValue_t Forceskin_cons_t[]; @@ -115,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); +sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/s_sound.c b/src/s_sound.c index 37a0fcce9..c2808e41a 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -520,6 +520,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) const INT32 initial_volume = (origin ? S_ScaleVolumeWithSplitscreen(volume) : volume); sfxinfo_t *sfx; + kartvoice_t *cur_voice; INT32 sep, pitch, priority, cnum; boolean anyListeners = false; boolean itsUs = false; @@ -611,9 +612,14 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) if (sfx->skinsound != -1 && origin && origin->skin) { - // redirect player sound to the sound in the skin table - sfx_id = ((skin_t *)origin->skin)->soundsid[sfx->skinsound]; - sfx = &S_sfx[sfx_id]; + // redirect player sound to any voice values, should they exist + cur_voice = P_GetMobjVoice(origin); + + if (cur_voice) + { + sfx_id = P_GetVoiceSFX(cur_voice, sfx->skinsound); // ((skin_t *)origin->skin)->soundsid[sfx->skinsound] + sfx = &S_sfx[sfx_id]; + } } // Initialize sound parameters diff --git a/src/typedef.h b/src/typedef.h index 88536d024..bbae58272 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -376,6 +376,7 @@ TYPEDEF (visffloor_t); TYPEDEF (portal_t); // r_skins.h +TYPEDEF (kartvoice_t); TYPEDEF (skin_t); // r_splats.h From 69cb50b37994b5930710f87b3d68dab5aae3c4ec Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 12 Nov 2025 12:40:32 -0500 Subject: [PATCH 02/44] 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 From b7a60c2fea317ae4b71cfaec0272980259ec11e6 Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 12 Nov 2025 17:04:42 -0500 Subject: [PATCH 03/44] Voice dub freeslotting Full transparency: the way I set up dub reading from an SOC is Yanderedev-tier garbage. --- src/d_netcmd.c | 53 ++++++++++++++- src/d_netcmd.h | 1 + src/d_player.h | 1 + src/deh_soc.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++ src/deh_soc.h | 2 + src/deh_tables.c | 19 ++++++ src/deh_tables.h | 3 + src/dehacked.c | 27 ++++++++ src/g_game.c | 3 + src/info.c | 17 ++++- src/p_mobj.c | 5 ++ src/p_saveg.c | 1 + src/r_skins.c | 26 ++++++- src/r_skins.h | 7 ++ 14 files changed, 330 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7721b3747..c4c57938f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1190,6 +1190,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_follower[i]); CV_RegisterVar(&cv_followercolor[i]); CV_RegisterVar(&cv_jitterlegacy[i]); + CV_RegisterVar(&cv_voice[i]); } // preferred number of players @@ -1406,6 +1407,13 @@ static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voic const char *value = cvar->string; kartvoice_t *myvoice = voice; + if (!Playing()) + { + // Don't bother. + CONS_Printf(M_GetText("You must be in a game to use this.\n")); + return; + } + if (!myvoice) { myvoice = skins[skin_id].voice; @@ -1437,6 +1445,7 @@ static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voic // *Reassign* the voice to whatever the current voice is, at least. CV_StealthSet(cvar, myvoice->name); + cvar->value = myvoice->id; } // Return regardless, as we've found the voice we were looking for. @@ -1464,12 +1473,20 @@ static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voic // *Reassign* the voice cvar to whatever the current voice is, at least. CV_StealthSet(cvar, myvoice->name); + cvar->value = myvoice->id; } // Return regardless, as we've found the voice we were looking for. return; } } + + // Found diddly-squat, say as much. + CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist.\n"), value); + + // Reassign the voice cvar to our current voice. + CV_StealthSet(cvar, myvoice->name); + cvar->value = myvoice->id; } /** Checks if a name (as received from another player) is okay. @@ -1828,6 +1845,7 @@ static void SendNameAndColor(UINT8 n) const INT32 playernum = g_localplayers[n]; player_t *player = &players[playernum]; kartvoice_t *voice; + INT32 prevskin; // We have no voice, but don't want to topple the house of cards. boolean permamute = false; @@ -1920,16 +1938,34 @@ static void SendNameAndColor(UINT8 n) } else if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin)) { + prevskin = player->skin; + cv_skin[n].value = foundskin; SetPlayerSkin(playernum, cv_skin[n].string); CV_StealthSet(&cv_skin[n], skins[cv_skin[n].value].name); + + // Reset the voice to that of the skin's. + if (prevskin != player->skin && skins[player->skin].voice) + { + CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + cv_voice[n].value = (UINT16)skins[player->skin].voice->id; + } } else { + prevskin = player->skin; + cv_skin[n].value = players[playernum].skin; CV_StealthSet(&cv_skin[n], skins[player->skin].name); // will always be same as current SetPlayerSkin(playernum, cv_skin[n].string); + + // Reset the voice to that of the skin's. + if (prevskin != player->skin && skins[player->skin].voice) + { + CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + cv_voice[n].value = (UINT16)skins[player->skin].voice->id; + } } // Need to update voices after the fact. @@ -1947,9 +1983,20 @@ static void SendNameAndColor(UINT8 n) } else { - // ...this is my paranoia speaking. - CV_StealthSet(&cv_voice[n], "sonic_voice"); - cv_voice[n].value = 0; + // Try getting the skin's voice. + valuevoice = skins[player->skin].voice; + + if (valuevoice) + { + CV_StealthSet(&cv_voice[n], valuevoice->name); + cv_voice[n].value = (UINT16)valuevoice->id; + } + else + { + // ...still nothing? + CV_StealthSet(&cv_voice[n], "sonic_voice"); + cv_voice[n].value = 0; + } } return; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 7fca388e6..eee52d485 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -29,6 +29,7 @@ extern consvar_t cv_playercolor[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_skin[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_follower[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_followercolor[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_voice[MAXSPLITSCREENPLAYERS]; // preferred number of players extern consvar_t cv_splitplayers; diff --git a/src/d_player.h b/src/d_player.h index 50ce06aa8..8722eb700 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -497,6 +497,7 @@ struct player_t UINT16 skincolor; INT32 skin; + UINT16 voice_id; UINT32 availabilities; UINT8 kartspeed; // Kart speed stat between 1 and 9 diff --git a/src/deh_soc.c b/src/deh_soc.c index c128b1691..dfbf19e01 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -4241,6 +4241,166 @@ void readkartresult(MYFILE *f, kartresult_t *result) #undef WARN #undef WARN0 +#define WARN(str, ...) deh_warning("KartVoice %s: " str, strbuf_get(voicenames, voice->info.nameofs), __VA_ARGS__) +#define WARN0(str) deh_warning("KartVoice %s: " str, strbuf_get(voicenames, voice->info.nameofs)) + +// Extremely shitty and lifted almost entirely from r_skins.c +// Sets the sound effect value for a voice, given a valid skinsound value is passed. +static UINT8 reallygrossvoicesfxadder(INT32 skinsound, const char *value, kartvoice_t *voice) +{ + sfxenum_t i; + char printval[6]; + + // Remove the prefix. (We can affect this directly since we're not going to use it again.) + if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS* + value += 2; + else // sfx_* + value += 4; + + strncpy(printval, value, 6); + + // copy name of sounds that are remapped + // for this skin + for (i = 0; i < sfx_skinsoundslot0; i++) + { + if (!S_sfx[i].name) + continue; + if (!stricmp(S_sfx[i].name, value)) + { + if (voice) + { + Sk_SetSkinVoiceValue(voice, skinsound, i); + return 0; + } + } + } + + strlwr(printval); + WARN("'sfx_%s' does not exist", printval); + return 1; +} + +void readkartvoice(MYFILE *f, kartvoice_t *voice) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + INT32 i; + + do if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = (tmp += 2); + strupr(word2); + + if (fastcmp(word, "NAME")) + { + strlwr(word2); + // Catch any voices with similar names. + for (i = KSKVC_FIRSTFREESLOT; i < MAXSKINVOICES; i++) + { + if (strnicmp(word2, skinvoices[i].name, sizeof skinvoices[i].name) == 0) + { + // Name collision! + // Append the ID to differentiate... poorly. + snprintf(word2, sizeof voice->name, "%u_%s", voice->id, skinvoices[i].name); + } + } + strncpy(voice->name, word2, sizeof voice->name); + CONS_Printf("KartVoice %s: renamed to \"%s\"\n", strbuf_get(voicenames, voice->info.nameofs), voice->name); + } + else if (fastcmp(word, "SKIN")) + { + i = R_SkinAvailable(word2); + + if (i == -1) + { + strlwr(word2); + WARN("skin '%s' does not exist or cannot be used", word2); + } + else + { + voice->parent = (UINT16)i; + } + } + else if (fastcmp(word, "GLOAT")) + { + reallygrossvoicesfxadder(SKSKPOWR, word2, voice); + } + else if (fastcmp(word, "WIN")) + { + reallygrossvoicesfxadder(SKSKWIN, word2, voice); + } + else if (fastcmp(word, "LOSE")) + { + reallygrossvoicesfxadder(SKSKLOSE, word2, voice); + } + else if (fastcmp(word, "SLOW") || fastcmp(word, "OVERTAKE") || fastcmp(word, "PASS")) + { + reallygrossvoicesfxadder(SKSKSLOW, word2, voice); + } + else if (fastcmp(word, "PAIN1") || fastcmp(word, "HURT1") || fastcmp(word, "DAMAGE1")) + { + reallygrossvoicesfxadder(SKSKPAN1, word2, voice); + } + else if (fastcmp(word, "PAIN2") || fastcmp(word, "HURT2") || fastcmp(word, "DAMAGE2")) + { + reallygrossvoicesfxadder(SKSKPAN2, word2, voice); + } + else if (fastcmp(word, "ATTACK1")) + { + reallygrossvoicesfxadder(SKSKATK1, word2, voice); + } + else if (fastcmp(word, "ATTACK2")) + { + reallygrossvoicesfxadder(SKSKATK2, word2, voice); + } + else if (fastcmp(word, "BOOST1")) + { + reallygrossvoicesfxadder(SKSKBST1, word2, voice); + } + else if (fastcmp(word, "BOOST2")) + { + reallygrossvoicesfxadder(SKSKBST2, word2, voice); + } + else if (fastcmp(word, "HITEM") || fastcmp(word, "HITCONFIRM")) + { + reallygrossvoicesfxadder(SKSKHITM, word2, voice); + } + else + WARN("unknown word '%s'", word); + } + while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +#undef WARN +#undef WARN0 + // // // @@ -4421,6 +4581,17 @@ useoddsfunc_f *get_useoddsfunc(const char *word) return NULL; } +kartvoicetype_e get_kartvoice(const char *word) +{ // Returns the value of KVOICE_ enumerations + kartvoicetype_e i; + if (fastncmp("KVOICE_",word,7)) + word += 7; // take off the KVOICE_ + i = DEH_FindVoice(word); + if (i == MAXVOICEFREESLOTS) + deh_warning("Couldn't find voice named 'KVOICE_%s'", word); + return i; +} + /// \todo Make ANY of this completely over-the-top math craziness obey the order of operations. static fixed_t op_mul(fixed_t a, fixed_t b) { return a*b; } static fixed_t op_div(fixed_t a, fixed_t b) { return a/b; } diff --git a/src/deh_soc.h b/src/deh_soc.h index 143c21a3d..ed97e3338 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -65,6 +65,7 @@ menudrawer_f *get_menudrawer(const char *word); skincolornum_t get_skincolor(const char *word); kartitemtype_e get_kartitem(const char *word); useoddsfunc_f *get_useoddsfunc(const char *word); +kartvoicetype_e get_kartvoice(const char *word); void readwipes(MYFILE *f); void readmaincfg(MYFILE *f); @@ -96,6 +97,7 @@ preciptype_t get_precip(const char *word); void readweather(MYFILE *f, INT32 num); void readkartitem(MYFILE *f, kartitem_t *item); void readkartresult(MYFILE *f, kartresult_t *result); +void readkartvoice(MYFILE *f, kartvoice_t *voice); #ifdef __cplusplus } // extern "C" diff --git a/src/deh_tables.c b/src/deh_tables.c index 633ce0c6f..cb90f9467 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -41,6 +41,7 @@ strbuf_t *mobjnames; strbuf_t *skincolornames; strbuf_t *menunames; strbuf_t *kartitemnames; +strbuf_t *voicenames; UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway. const char *DEH_MobjtypeName(mobjtype_t i) @@ -68,6 +69,11 @@ const char *DEH_KartItemName(kartitemtype_e i) return strbuf_get(kartitemnames, kartitems[i].info.nameofs); } +const char *DEH_VoiceName(kartvoicetype_e i) +{ + return strbuf_get(voicenames, skinvoices[i + KSKVC_FIRSTFREESLOT].info.nameofs); +} + mobjtype_t DEH_FindMobjtype(const char *word) { mobjtype_t i; @@ -133,6 +139,19 @@ kartitemtype_e DEH_FindKartItem(const char *word) return MAXKARTITEMS; } +kartvoicetype_e DEH_FindVoice(const char *word) +{ + INT32 i; + UINT32 hash = HASH32(word, strlen(word)); + for (i = 0; i < numfreeslotvoices; i++) { + if (hash != skinvoices[i + KSKVC_FIRSTFREESLOT].info.namehash) + continue; + if (fastcmp(word, DEH_VoiceName(i))) + return (kartvoicetype_e)i; + } + return MAXVOICEFREESLOTS; +} + struct flickytypes_s FLICKYTYPES[] = { {"BLUEBIRD", MT_FLICKY_01}, // Flicky (Flicky) {"RABBIT", MT_FLICKY_02}, // Pocky (1) diff --git a/src/deh_tables.h b/src/deh_tables.h index 5aba111c0..0db150418 100644 --- a/src/deh_tables.h +++ b/src/deh_tables.h @@ -32,6 +32,7 @@ extern strbuf_t *mobjnames; extern strbuf_t *skincolornames; extern strbuf_t *menunames; extern strbuf_t *kartitemnames; +extern strbuf_t *voicenames; extern UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway. const char *DEH_MobjtypeName(mobjtype_t i); @@ -39,12 +40,14 @@ const char *DEH_StateName(statenum_t i); const char *DEH_SkincolorName(skincolornum_t i); const char *DEH_MenutypeName(menutype_t i); const char *DEH_KartItemName(kartitemtype_e i); +const char *DEH_VoiceName(kartvoicetype_e i); mobjtype_t DEH_FindMobjtype(const char *word); statenum_t DEH_FindState(const char *word); skincolornum_t DEH_FindSkincolor(const char *word); menutype_t DEH_FindMenutype(const char *word); kartitemtype_e DEH_FindKartItem(const char *word); +kartvoicetype_e DEH_FindVoice(const char *word); struct flickytypes_s { const char *name; diff --git a/src/dehacked.c b/src/dehacked.c index dd0fd670e..a8b0ef5c5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -390,6 +390,20 @@ INT32 DEH_ReadFreeslot(const char *type, const char *word, INT32 *out) DEH_Link(word, &kartitems[numkartitems].info, &kartitemnames); K_RegisterItem(numkartitems); *out = numkartitems++; + } + else if (fastcmp(type, "KVOICE")) + { + *out = DEH_FindVoice(word); + if (*out != MAXVOICEFREESLOTS) + return 0; // already allocated + + if (numfreeslotvoices == MAXVOICEFREESLOTS) { + CONS_Alert(CONS_WARNING, "Ran out of free voice slots!\n"); + return -1; + } + CONS_Printf("Voice KVOICE_%s allocated.\n",word); + DEH_Link(word, &skinvoices[numfreeslotvoices + KSKVC_FIRSTFREESLOT].info, &voicenames); + *out = numfreeslotvoices++; return 0; } @@ -787,6 +801,19 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) result = K_RegisterResult(word2, alternate); readkartresult(f, result); } + else if (fastncmp(word, "KARTVOICE", 9)) + { + if (i == 0 && word2[0] != '0') // If word2 isn't a number + i = get_kartvoice(word2); // find a voice by name + if ((i + 1) >= 1 && i < numfreeslotvoices) + readkartvoice(f, &skinvoices[i + KSKVC_FIRSTFREESLOT]); + else + { + // zero-based, but let's start at 1 + deh_warning("KartVoice number %d out of range (1 - %d)", i, numfreeslotvoices); + ignorelines(f); + } + } else if (fastcmp(word, "WEATHER") || fastcmp(word, "PRECIP") || fastcmp(word, "PRECIPITATION")) { if (i == 0 && word2[0] != '0') // If word2 isn't a number diff --git a/src/g_game.c b/src/g_game.c index a93f85bc9..5f865acae 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2703,6 +2703,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT8 latestlap; UINT16 skincolor; INT32 skin; + kartvoicetype_e voice; UINT32 availabilities; tic_t jointime; @@ -2761,6 +2762,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) skincolor = players[player].skincolor; skin = players[player].skin; + voice = players[player].voice_id; // SRB2kart kartspeed = players[player].kartspeed; @@ -2951,6 +2953,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) // save player config truth reborn p->skincolor = skincolor; p->skin = skin; + p->voice_id = voice; p->kartspeed = kartspeed; p->kartweight = kartweight; // diff --git a/src/info.c b/src/info.c index 28513667c..827fb8eb9 100644 --- a/src/info.c +++ b/src/info.c @@ -240,7 +240,7 @@ void P_ResetData(INT32 flags) for (i = 0; i < MN_FIRSTFREESLOT; i++, name += strlen(name)+1) DEH_Link(name, &menudefs[i].info, &menunames); } - + // kartitems if (init) { @@ -258,4 +258,19 @@ void P_ResetData(INT32 flags) K_RegisterItem(i); } } + + // voices + if (init) + { + for (i = KSKVC_FIRSTFREESLOT; i < MAXSKINVOICES; i++) + { + R_ClearVoice(i); + } + + numfreeslotvoices = 0; + + if (voicenames) + Z_Free(voicenames); + voicenames = strbuf_alloc(); + } } diff --git a/src/p_mobj.c b/src/p_mobj.c index b7be3dcfa..58cf2582c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12371,6 +12371,11 @@ void P_SpawnPlayer(INT32 playernum) mobj->skin = &skins[p->skin]; P_SetupStateAnimation(mobj, mobj->state); + if (p->voice_id != skins[p->skin].voice->id) + { + mobj->voice = &skinvoices[p->voice_id]; + } + mobj->health = 1; p->playerstate = PST_LIVE; diff --git a/src/p_saveg.c b/src/p_saveg.c index 0ed4fbff3..aeacc00e5 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -476,6 +476,7 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(players[i].skincolor); SYNC(players[i].skin); + SYNC(players[i].voice_id); SYNC(players[i].availabilities); SYNC(players[i].score); SYNC(players[i].lives); diff --git a/src/r_skins.c b/src/r_skins.c index 64c23d4ac..a4796e315 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -173,7 +173,7 @@ UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe) return P_GetSkinSprite2(skin, spr2, NULL); } -static void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound) +void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound) { INT32 usesound; @@ -298,6 +298,13 @@ sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound) return sfx_thok; } +void R_ClearVoice(kartvoicetype_e voicetype) +{ + kartvoice_t *voice = &skinvoices[voicetype]; + + memset(voice, 0, sizeof(kartvoice_t)); +} + static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice) { INT32 i; @@ -649,6 +656,8 @@ void SetPlayerVoice(INT32 playernum, const char *voicename) if (P_MobjWasRemoved(player->mo)) { // No object to give a voice to. + // Fall back to our skin. + player->voice_id = skins[player->skin].voice->id; return; } @@ -657,6 +666,8 @@ void SetPlayerVoice(INT32 playernum, const char *voicename) if (!strncmp(voicename, myvoice->name, sizeof myvoice->name)) { // Hey... this voice is the same as before! + // WAIT! Before we go, reset the player's voice ID! + player->voice_id = myvoice->id; return; } @@ -670,12 +681,13 @@ 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); return; } } } - // Freeslotted voices don't exist or found nothing; check skin voices. + // Freeslotted voices don't exist, or we've found nothing; check skin voices. for (i = 0; i < numskinvoices; i++) { // Whatever. Go, my code duplication! @@ -683,12 +695,14 @@ void SetPlayerVoice(INT32 playernum, const char *voicename) { // Found a voice. player->mo->voice = &skinvoices[i]; + player->voice_id = (kartvoicetype_e)i; return; } } // Couldn't find a voice. Default to the skin's. player->mo->voice = NULL; + player->voice_id = skins[player->skin].voice->id; } // Same as SetPlayerSkin, but uses the skin #. @@ -774,6 +788,8 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) if (P_MobjWasRemoved(player->mo)) { // No object to give a voice to. + // Fall back to our skin. + player->voice_id = skins[player->skin].voice->id; return; } @@ -782,6 +798,8 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) if (myvoice->id == voicenum) { // Hey... this voice is the same as before! + // WAIT! Before we go, reset the player's voice ID! + player->voice_id = myvoice->id; return; } @@ -795,6 +813,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); return; } } @@ -808,12 +827,14 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) { // Found a voice. player->mo->voice = &skinvoices[i]; + player->voice_id = (kartvoicetype_e)i; return; } } // Couldn't find a voice. Default to the skin's. player->mo->voice = NULL; + player->voice_id = skins[player->skin].voice->id; } // @@ -1355,6 +1376,7 @@ void R_AddSkins(UINT16 wadnum) skinsorted[numskins] = numskins; numskins++; + numskinvoices++; } SortSkins(); return; diff --git a/src/r_skins.h b/src/r_skins.h index 363ef8738..bde9eec23 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -45,6 +45,8 @@ typedef enum MAXSKINVOICES } kartvoicetype_e; +#define MAXVOICEFREESLOTS (MAXSKINVOICES - KSKVC_FIRSTFREESLOT) + // Player voice struct // ## TODO: // - Dub implementation (freeslotting, voice lookup, assigning voices on race start) @@ -53,6 +55,8 @@ struct kartvoice_t { UINT32 id; + dehinfo_t info; + char name[VOICENAMESIZE+1]; // INT16 descriptive name of the voice. // UINT16 ID of the parent skin this voice belongs to. @@ -153,6 +157,9 @@ 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 R_ClearVoice(kartvoicetype_e voicetype); +void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound); + void SetPlayerVoice(INT32 playernum, const char *voicename); void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum); sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound); From 57c8fdc62d4ae44c7ccb50db3081713eec27c0cb Mon Sep 17 00:00:00 2001 From: yamamama Date: Wed, 12 Nov 2025 18:46:32 -0500 Subject: [PATCH 04/44] Properly netsync voices and dubs This netcode goddamn sucks --- src/d_netcmd.c | 95 +++++++++++++++++++++++++++++++++++++++++++------- src/dehacked.c | 1 + 2 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c4c57938f..cc7059471 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1399,7 +1399,7 @@ static void Skin_FindRealNameSkin(consvar_t *cvar) } } -static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voice, boolean forceme) +static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voice, boolean forceme, boolean silent) { // Not the best way to implements this but it will do. @@ -1411,7 +1411,7 @@ static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voic { // Don't bother. CONS_Printf(M_GetText("You must be in a game to use this.\n")); - return; + return 0; } if (!myvoice) @@ -1421,7 +1421,7 @@ static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voic if (!myvoice) { // ...how?! - return; + return 0; } } @@ -1437,19 +1437,22 @@ static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voic { // Change the cvar to be the value of the name. CV_StealthSet(cvar, skinvoices[i + KSKVC_FIRSTFREESLOT].name); + cvar->value = skinvoices[i + KSKVC_FIRSTFREESLOT].id; } 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); + if (!silent) + 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); cvar->value = myvoice->id; + return 1; } // Return regardless, as we've found the voice we were looking for. - return; + return 0; } } } @@ -1465,28 +1468,33 @@ static void Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voic { // Change the cvar to be the value of the name. CV_StealthSet(cvar, skinvoices[i].name); + cvar->value = skinvoices[i].id; } 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); + if (!silent) + 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); cvar->value = myvoice->id; + return 1; } // Return regardless, as we've found the voice we were looking for. - return; + return 0; } } // Found diddly-squat, say as much. - CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist.\n"), value); + if (!silent) + CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist.\n"), value); // Reassign the voice cvar to our current voice. CV_StealthSet(cvar, myvoice->name); cvar->value = myvoice->id; + return 1; } /** Checks if a name (as received from another player) is okay. @@ -1845,6 +1853,7 @@ static void SendNameAndColor(UINT8 n) const INT32 playernum = g_localplayers[n]; player_t *player = &players[playernum]; kartvoice_t *voice; + kartvoice_t *valuevoice; INT32 prevskin; // We have no voice, but don't want to topple the house of cards. @@ -1974,7 +1983,7 @@ static void SendNameAndColor(UINT8 n) SetPlayerVoice(playernum, cv_voice[n].string); - const kartvoice_t *valuevoice = P_GetMobjVoice(player->mo); + valuevoice = P_GetMobjVoice(player->mo); if (valuevoice) { @@ -2017,8 +2026,17 @@ static void SendNameAndColor(UINT8 n) // Don't change skin if the server doesn't want you to. if (!CanChangeSkin(playernum)) + { CV_StealthSet(&cv_skin[n], skins[player->skin].name); + if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, false, true)) + { + // Our voice is no longer valid, set it to that of our skin's. + CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + cv_voice[n].value = skins[player->skin].voice->id; + } + } + // 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); @@ -2031,6 +2049,43 @@ static void SendNameAndColor(UINT8 n) cv_voice[n].value = 0; } + // Need to update voices after the fact. + if (!cv_voice[n].string) + CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + + if (!permamute) + { + if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, false, true)) + { + // In the chance our current voice isn't valid, + // rescan our current voice and send that over the network. + valuevoice = P_GetMobjVoice(player->mo); + + if (valuevoice && valuevoice->parent == cv_skin[n].value) + { + CV_StealthSet(&cv_voice[n], valuevoice->name); + cv_voice[n].value = (UINT16)valuevoice->id; + } + else + { + // Try getting the skin's voice. + valuevoice = skins[cv_skin[n].value].voice; + + if (valuevoice) + { + CV_StealthSet(&cv_voice[n], valuevoice->name); + cv_voice[n].value = (UINT16)valuevoice->id; + } + else + { + // ...still nothing? + CV_StealthSet(&cv_voice[n], "sonic_voice"); + cv_voice[n].value = 0; + } + } + } + } + // Finally write out the complete packet and send it off. WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME); WRITEUINT32(p, (UINT32)player->availabilities); @@ -2155,13 +2210,29 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) SetPlayerSkinByNum(playernum, forcedskin); if (localplayer != -1) + { CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name); + + CV_StealthSet(&cv_voice[localplayer], skins[forcedskin].voice->name); + cv_voice[localplayer].value = skins[forcedskin].voice->id; + } + + // set voice + SetPlayerVoiceByNum(playernum, skins[forcedskin].voice->id); } else + { SetPlayerSkinByNum(playernum, skin); - // set voice - SetPlayerVoiceByNum(playernum, voice); + // set voice + SetPlayerVoiceByNum(playernum, voice); + + if (localplayer != -1) + { + CV_StealthSet(&cv_voice[localplayer], skinvoices[voice].name); + cv_voice[localplayer].value = skinvoices[voice].id; + } + } // set follower colour: // Don't bother doing garbage and kicking if we receive None, @@ -7415,7 +7486,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) } } - Skin_FindValidDub(&cv_voice[pnum], players[pid].skin, myvoice, false); + Skin_FindValidDub(&cv_voice[pnum], players[pid].skin, myvoice, false, false); if (!Playing()) return; // do whatever you want diff --git a/src/dehacked.c b/src/dehacked.c index a8b0ef5c5..029813a96 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -403,6 +403,7 @@ INT32 DEH_ReadFreeslot(const char *type, const char *word, INT32 *out) } CONS_Printf("Voice KVOICE_%s allocated.\n",word); DEH_Link(word, &skinvoices[numfreeslotvoices + KSKVC_FIRSTFREESLOT].info, &voicenames); + skinvoices[numfreeslotvoices + KSKVC_FIRSTFREESLOT].id = numfreeslotvoices + KSKVC_FIRSTFREESLOT; *out = numfreeslotvoices++; return 0; } From e778f0857d120ee67689e68210f612f3e89c8433 Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 00:31:42 -0500 Subject: [PATCH 05/44] Don't blind-assign voices by voice ID Should fix every bot sounding like Sonic --- src/p_mobj.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 58cf2582c..2b586aab5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12373,7 +12373,17 @@ void P_SpawnPlayer(INT32 playernum) if (p->voice_id != skins[p->skin].voice->id) { - mobj->voice = &skinvoices[p->voice_id]; + const kartvoice_t *tentative_voice = &skinvoices[p->voice_id]; + + if (tentative_voice->parent == skins[p->skin].voice->parent) + { + mobj->voice = tentative_voice; + } + else + { + p->voice_id = skins[p->skin].voice->id; + mobj->voice = NULL; + } } mobj->health = 1; From bb8f4301f4d094919058f9a51e8644db62fca8cf Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 00:47:02 -0500 Subject: [PATCH 06/44] Add some basic commenting --- src/p_mobj.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2b586aab5..5dfcfaa83 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12373,14 +12373,19 @@ void P_SpawnPlayer(INT32 playernum) if (p->voice_id != skins[p->skin].voice->id) { + // During respawns, do a quick check on our voice ID to make sure + // our skin can use it. + // This should prevent bots from always having Sonic's voice. const kartvoice_t *tentative_voice = &skinvoices[p->voice_id]; if (tentative_voice->parent == skins[p->skin].voice->parent) { + // Seems OK, set our voice! mobj->voice = tentative_voice; } else { + // Parent mismatch; use the skin default. p->voice_id = skins[p->skin].voice->id; mobj->voice = NULL; } From ca7ab138532c91afd090d4b28f9fb3c1ebbc6ead Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 06:34:51 -0500 Subject: [PATCH 07/44] assignment discards const qualifier from deez nuts --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 5dfcfaa83..8dec318e2 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12376,7 +12376,7 @@ void P_SpawnPlayer(INT32 playernum) // During respawns, do a quick check on our voice ID to make sure // our skin can use it. // This should prevent bots from always having Sonic's voice. - const kartvoice_t *tentative_voice = &skinvoices[p->voice_id]; + kartvoice_t *tentative_voice = &skinvoices[p->voice_id]; if (tentative_voice->parent == skins[p->skin].voice->parent) { From 32e4080a706d2935a3cfa0b7bd3376871ad7d461 Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 09:45:34 -0500 Subject: [PATCH 08/44] [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); From 8d38d0df6d1fbe7163f07a1f596a51051f8f2686 Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 09:51:19 -0500 Subject: [PATCH 09/44] Update TODO --- src/r_skins.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/r_skins.h b/src/r_skins.h index 8959b2475..4b57acb4b 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -49,7 +49,6 @@ typedef enum // Player voice struct // ## TODO: -// - Dub implementation (freeslotting, voice lookup, assigning voices on race start) // - Get rid of soundsid for good, replace it with some hacky compat system struct kartvoice_t { From 6e250eb26cf307b7d9e516e25875689bd2f09c79 Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 10:21:06 -0500 Subject: [PATCH 10/44] Unset dubs in Kart replays --- src/g_demo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_demo.c b/src/g_demo.c index 8c3e83b02..c696124e2 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -735,7 +735,7 @@ static headerstatus_e G_ReadDemoHeader(UINT8 *dp, demoheader_t *header) } else if (!memcmp(startdp, "\xF0" "KartReplay" "\x0F", 12)) { - rapreset = raflag = false; + dubs = rapreset = raflag = false; serverinfo = false; switch (header->demoversion) { From 9bd3aa6460df7098654136a114ecb6e2b34ebdea Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 10:31:00 -0500 Subject: [PATCH 11/44] Init extra.voicename when reading raw data Thanks, GHG! --- src/g_demo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/g_demo.c b/src/g_demo.c index c696124e2..ba791ebb1 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -623,6 +623,8 @@ static UINT8 *G_ReadRawExtraData(extradata_t *extra, UINT8 *dp, UINT16 version) { READMEM(dp, extra->skinname, 16); extra->skinname[16] = '\0'; + + extra->voicename[0] = '\0'; } else { @@ -630,6 +632,8 @@ static UINT8 *G_ReadRawExtraData(extradata_t *extra, UINT8 *dp, UINT16 version) if (version > 0x000D) READSTRINGL(dp, extra->voicename, 32+1); + else + extra->voicename[0] = '\0'; } extra->kartspeed = READUINT8(dp); extra->kartweight = READUINT8(dp); From 2cbad13f255f0f5f7df7f76999b3d4647fe31357 Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 13 Nov 2025 12:26:16 -0500 Subject: [PATCH 12/44] Clang the voice assign functions, preserve voice IDs even if objects don't exist --- src/d_netcmd.c | 1 - src/r_skins.c | 285 +++++++++++++++++++++++++------------------------ 2 files changed, 143 insertions(+), 143 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cc7059471..23372b7ed 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1410,7 +1410,6 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi if (!Playing()) { // Don't bother. - CONS_Printf(M_GetText("You must be in a game to use this.\n")); return 0; } diff --git a/src/r_skins.c b/src/r_skins.c index f6b10f23b..ddccbd680 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -647,109 +647,6 @@ 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) -{ - player_t *player = &players[playernum]; - INT32 i; - - if (P_MobjWasRemoved(player->mo)) - { - // 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; - } - - const kartvoice_t *myvoice = P_GetMobjVoice(player->mo); - - if (!strncmp(voicename, myvoice->name, sizeof myvoice->name)) - { - // 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; - } - - // 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]; - player->voice_id = (kartvoicetype_e)(i + KSKVC_FIRSTFREESLOT); - demo_extradata[playernum] |= DXD_SKIN; - return; - } - } - } - - // 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. - player->mo->voice = &skinvoices[i]; - player->voice_id = (kartvoicetype_e)i; - demo_extradata[playernum] |= DXD_SKIN; - return; - } - } - - // 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 #. // network code calls this when a 'skin change' is received void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) @@ -824,24 +721,152 @@ 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) +// Gets the corresponding voice ID for a given voice's name. +// Returns MAXSKINVOICES if nothing is found. +kartvoicetype_e R_FindIDForVoice(const char* voicename) { - player_t *player = &players[playernum]; 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; +} + +static kartvoicetype_e R_FindIDForVoiceNum(UINT16 voicenum) +{ + INT32 i; + + // 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. + 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 (skinvoices[i].id == voicenum) + { + // 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) +{ + player_t* player = &players[playernum]; + + kartvoicetype_e voxid = R_FindIDForVoice(voicename); + + if (voxid == MAXSKINVOICES) + { + // Couldn't find a voice. Default to the skin's. + voxid = skins[player->skin].voice->id; + } + if (P_MobjWasRemoved(player->mo)) { // No object to give a voice to. - // Fall back to our skin. - player->voice_id = skins[player->skin].voice->id; + // Store the found ID in the player, at least. Object spawning will take care of + // assigning a voice. + player->voice_id = (UINT16)voxid; // Tell the demo that we've updated our voice_id as well. demo_extradata[playernum] |= DXD_SKIN; return; } - const kartvoice_t *myvoice = P_GetMobjVoice(player->mo); + const kartvoice_t* myvoice = P_GetMobjVoice(player->mo); + + if (!strncmp(voicename, myvoice->name, sizeof myvoice->name)) + { + // 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; + } + + // We've done the precautions to check for disallowed dubs in the cvar, just do a blind-read + // for the needed name. If we're down here, we've passed the safeguards and can assign a + // voice. + + if (voxid != skins[player->skin].voice->id) + player->mo->voice = &skinvoices[(UINT16)voxid]; + else + player->mo->voice = NULL; + + player->voice_id = (UINT16)(voxid); + + // Tell the demo that we've updated our voice_id as well. + demo_extradata[playernum] |= DXD_SKIN; +} + +// network code calls this when a 'voice change' is received +void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) +{ + player_t* player = &players[playernum]; + kartvoicetype_e voxid = R_FindIDForVoiceNum(voicenum); + + if (voxid == MAXSKINVOICES) + { + // Couldn't find a voice. Default to the skin's. + voxid = skins[player->skin].voice->id; + } + + if (P_MobjWasRemoved(player->mo)) + { + // No object to give a voice to. + // Store the found ID in the player, at least. Object spawning will take care of + // assigning a voice. + player->voice_id = (UINT16)voxid; + + // Tell the demo that we've updated our voice_id as well. + demo_extradata[playernum] |= DXD_SKIN; + return; + } + + const kartvoice_t* myvoice = P_GetMobjVoice(player->mo); if (myvoice->id == voicenum) { @@ -854,42 +879,18 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) 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]; - player->voice_id = (kartvoicetype_e)(i + KSKVC_FIRSTFREESLOT); - demo_extradata[playernum] |= DXD_SKIN; - return; - } - } - } + // We've done the precautions to check for disallowed dubs in the cvar, just do a blind-read + // for the needed name. If we're down here, we've passed the safeguards and can assign a + // voice. - // 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]; - player->voice_id = (kartvoicetype_e)i; - demo_extradata[playernum] |= DXD_SKIN; - return; - } - } + if (voxid != skins[player->skin].voice->id) + player->mo->voice = &skinvoices[(UINT16)voxid]; + else + player->mo->voice = NULL; - // Couldn't find a voice. Default to the skin's. - player->mo->voice = NULL; - player->voice_id = skins[player->skin].voice->id; + player->voice_id = (UINT16)(voxid); - // Signal a voice_id change to the demo. + // Tell the demo that we've updated our voice_id as well. demo_extradata[playernum] |= DXD_SKIN; } From 69c50c94d5592b705938c28bdeb9b7207f5e6916 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 15 Nov 2025 06:10:56 -0500 Subject: [PATCH 13/44] Lua support + compat for voices --- src/CMakeLists.txt | 1 + src/hardware/hw3sound.c | 2 +- src/lua_baselib.c | 1 + src/lua_libs.h | 3 + src/lua_playerlib.c | 5 + src/lua_script.c | 1 + src/lua_skinlib.c | 15 ++- src/lua_voicelib.c | 289 ++++++++++++++++++++++++++++++++++++++++ src/r_skins.c | 2 - src/r_skins.h | 10 +- src/s_sound.c | 2 +- 11 files changed, 321 insertions(+), 10 deletions(-) create mode 100644 src/lua_voicelib.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0478bcb71..46ff0ce45 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,6 +124,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 lua_vectorlib.c lua_matrixlib.c lua_quaternionlib.c + lua_voicelib.c k_kart.c k_collide.c k_color.c diff --git a/src/hardware/hw3sound.c b/src/hardware/hw3sound.c index 2d478938f..05de41c5d 100644 --- a/src/hardware/hw3sound.c +++ b/src/hardware/hw3sound.c @@ -367,7 +367,7 @@ INT32 HW3S_I_StartSound(const void *origin_p, source3D_data_t *source_parm, chan if (voice) { - sfx_id = sfx_id = P_GetVoiceSFX(voice, sfx->skinsound); // ((skin_t *)origin->skin)->soundsid[sfx->skinsound] + sfx_id = sfx_id = P_GetVoiceSFX(voice, sfx->skinsound); sfx = &S_sfx[sfx_id]; } } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 77dc3e2c3..1783548f7 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -249,6 +249,7 @@ static const struct { {META_VECTOR3, "vector3_t"}, {META_MATRIX, "matrix_t"}, {META_QUATERNION, "quaternion_t"}, + {META_VOICE, "kartvoice_t"}, {NULL, NULL} }; diff --git a/src/lua_libs.h b/src/lua_libs.h index bcde88696..aef4e921f 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -122,6 +122,8 @@ extern lua_State *gL; #define META_MATRIX "MATRIX_T" #define META_QUATERNION "QUATERNION_T" +#define META_VOICE "KARTVOICE_T*" + boolean luaL_checkboolean(lua_State *L, int narg); int LUA_EnumLib(lua_State *L); @@ -147,6 +149,7 @@ int LUA_WaypointLib(lua_State *L); int LUA_MatrixLib(lua_State *L); int LUA_QuaternionLib(lua_State *L); int LUA_VectorLib(lua_State *L); +int LUA_VoiceLib(lua_State* L); #ifdef __cplusplus } // extern "C" diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 4fd179536..2696a7d67 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -388,6 +388,7 @@ enum player_e player_flashpal, player_skincolor, player_skin, + player_voice_id, player_availabilities, player_score, player_kartspeed, @@ -604,6 +605,7 @@ static const char *const player_opt[] = { "flashpal", "skincolor", "skin", + "voice_id", "availabilities", "score", "kartspeed", @@ -1172,6 +1174,9 @@ static int player_get(lua_State *L) case player_skin: lua_pushinteger(L, plr->skin); break; + case player_voice_id: + lua_pushinteger(L, plr->voice_id); + break; case player_availabilities: lua_pushinteger(L, plr->availabilities); break; diff --git a/src/lua_script.c b/src/lua_script.c index 6175350e4..5ebc165fe 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -68,6 +68,7 @@ static lua_CFunction liblist[] = { LUA_VectorLib, // vectors LUA_MatrixLib, // matrices LUA_QuaternionLib, // quaternions + LUA_VoiceLib, // kartvoice_t, skinvoices[] NULL }; diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index f04ef9371..c526da23e 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -39,6 +39,7 @@ enum skin { skin_highresscale, skin_rivals, skin_soundsid, + skin_voice, skin_sprites }; static const char *const skin_opt[] = { @@ -60,6 +61,7 @@ static const char *const skin_opt[] = { "highresscale", "rivals", "soundsid", + "voice", "sprites", NULL }; @@ -141,6 +143,9 @@ static int skin_get(lua_State *L) case skin_soundsid: LUA_PushUserdata(L, skin->soundsid, META_SOUNDSID); break; + case skin_voice: + LUA_PushUserdata(L, skin->voice, META_VOICE); + break; case skin_sprites: LUA_PushUserdata(L, skin->sprites, META_SKINSPRITES); break; @@ -241,11 +246,16 @@ static int lib_numSkins(lua_State *L) // soundsid, i -> soundsid[i] static int soundsid_get(lua_State *L) { + // soundsid is a route to the voice struct. That's how we're doing this. sfxenum_t *soundsid = *((sfxenum_t **)luaL_checkudata(L, 1, META_SOUNDSID)); + skin_t *s = (skin_t*)((char*)soundsid - offsetof(skin_t, soundsid)); + skinsound_t i = luaL_checkinteger(L, 2); + if (i >= NUMSKINSOUNDS) - return luaL_error(L, LUA_QL("skinsound_t") " cannot be %u", i); - lua_pushinteger(L, soundsid[i]); + return luaL_error(L, "%s cannot be %s", LUA_QL("skinsound_t"), va("%u", (UINT32)i)); + + lua_pushinteger(L, P_GetVoiceSFX(s->voice, i)); return 1; } @@ -320,6 +330,7 @@ int LUA_SkinLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_pop(L,1); + luaL_newmetatable(L, META_SKINSPRITES); lua_pushcfunction(L, lib_getSkinSprite); lua_setfield(L, -2, "__index"); diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c new file mode 100644 index 000000000..dae6edf1b --- /dev/null +++ b/src/lua_voicelib.c @@ -0,0 +1,289 @@ +// BLANKART +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by "yama". +// Copyright (C) 2025 by BlanKart Team. +// Copyright (C) 2014-2025 by Sonic Team Junior. +// Copyright (C) 2016 by John "JTE" Muniz. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file lua_waypointslib.c +/// \brief wapoint structure library for Lua scripting + +#include "doomdef.h" +#include "fastcmp.h" +#include "r_skins.h" +#include "sounds.h" + +#include "lua_script.h" +#include "lua_libs.h" + +enum voicevars +{ + voicevars_id = 0, + voicevars_name, + voicevars_parent, + voicevars_parentname, // Not actually in kartvoice_t; returns the name of the parent skin + voicevars_win, + voicevars_lose, + voicevars_pain, + voicevars_attack, + voicevars_boost, + voicevars_overtake, + voicevars_hitem, + voicevars_power +}; + +static const char *const voicevars_opt[] = { + "id", + "name", + "parent", + "parentname", + "win", + "lose", + "pain", + "attack", + "boost", + "overtake", + "hitem", + "power", + NULL +}; + +#define RNOFIELD luaL_error(L, LUA_QL("kartvoice_t") " has no field named " LUA_QS, field) +#define RNOSET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be set.", field) +#define RNOGET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be get.", field) + +static int voice_get(lua_State* L) +{ + kartvoice_t* voice = *((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE)); + enum voicevars field = luaL_checkoption(L, 2, NULL, voicevars_opt); + + // voices are always valid, only added, never removed + I_Assert(voice != NULL); + + switch (field) + { + case voicevars_id: + lua_pushinteger(L, voice->id); + break; + case voicevars_name: + lua_pushstring(L, voice->name); + break; + case voicevars_parent: + lua_pushinteger(L, voice->parent); + break; + case voicevars_parentname: + lua_pushstring(L, skins[voice->parent].name); + break; + case voicevars_win: + lua_pushinteger(L, voice->win); + break; + case voicevars_lose: + lua_pushinteger(L, voice->lose); + break; + case voicevars_pain: + // Wasteful and shitty, I'll wager, but it's better than risking segfaults + const size_t painarraysize = (sizeof voice->pain) / (sizeof voice->pain[0]); + + lua_createtable(L, painarraysize, 0); + for (size_t i = 0; i < painarraysize; i++) + { + lua_pushinteger(L, voice->pain[i]); + lua_rawseti(L, -2, 1 + i); + } + break; + case voicevars_attack: + const size_t atkarraysize = (sizeof voice->attack) / (sizeof voice->attack[0]); + + lua_createtable(L, atkarraysize, 0); + for (size_t i = 0; i < atkarraysize; i++) + { + lua_pushinteger(L, voice->attack[i]); + lua_rawseti(L, -2, 1 + i); + } + break; + case voicevars_boost: + const size_t bstarraysize = (sizeof voice->boost) / (sizeof voice->boost[0]); + + lua_createtable(L, bstarraysize, 0); + for (size_t i = 0; i < bstarraysize; i++) + { + lua_pushinteger(L, voice->boost[i]); + lua_rawseti(L, -2, 1 + i); + } + break; + case voicevars_overtake: + lua_pushinteger(L, voice->overtake); + break; + case voicevars_hitem: + lua_pushinteger(L, voice->hitem); + break; + case voicevars_power: + lua_pushinteger(L, voice->power); + break; + default: + return RNOFIELD; + } + + return 1; +} + +static int voice_set(lua_State* L) +{ + // ...let's just operate by skin rules. So much for random-line voice dubs... + return luaL_error(L, LUA_QL("kartvoice_t") " struct cannot be edited by Lua."); +} + +#undef RNOSET +#undef RNOFIELD + +static int voice_num(lua_State* L) +{ + kartvoice_t* voice = *((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE)); + + // voices are always valid, only added, never removed + I_Assert(voice != NULL); + + lua_pushinteger(L, voice - skinvoices); + return 1; +} + +static int lib_iterateVoices(lua_State* L) +{ + INT32 i; + + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, lib_iterateVoices); + return 1; + } + + lua_settop(L, 2); + lua_remove(L, 1); // state is unused. + + if (!lua_isnil(L, 1)) + i = (INT32)(*((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE)) - skinvoices) + 1; + else + i = 0; + + // voices are always valid, only added, never removed + if (i >= KSKVC_MAXVANILLA) + { + // Check freeslotted voices. + if ((i - KSKVC_MAXVANILLA) < numfreeslotvoices) + { + LUA_PushUserdata(L, &skinvoices[i], META_VOICE); + return 1; + } + } + else if (i < numskinvoices) + { + LUA_PushUserdata(L, &skinvoices[i], META_VOICE); + return 1; + } + + return 0; +} + +static int lib_getVoice(lua_State* L) +{ + const char* field; + INT32 i; + + // find voice by number + if (lua_type(L, 2) == LUA_TNUMBER) + { + i = luaL_checkinteger(L, 2); + if (i < 0 || i >= MAXSKINVOICES) + { + return luaL_error( + L, "skinvoices[] index %d out of range (0 - %d)", i, MAXSKINVOICES - 1); + } + if (i >= KSKVC_MAXVANILLA) + { + // Check freeslotted voices. + if ((i - KSKVC_MAXVANILLA) >= numfreeslotvoices) + { + return 0; + } + } + else if (i >= numskinvoices) + { + return 0; + } + + LUA_PushUserdata(L, &skinvoices[i], META_VOICE); + return 1; + } + + field = luaL_checkstring(L, 2); + + // special function iterate + if (fastcmp(field, "iterate")) + { + lua_pushcfunction(L, lib_iterateVoices); + return 1; + } + + // Special functions for getting voice counts per individual table. + if (fastcmp(field, "basevoices")) + { + lua_pushinteger(L, numskinvoices); + return 1; + } + + if (fastcmp(field, "freeslotvoices")) + { + lua_pushinteger(L, numfreeslotvoices); + return 1; + } + + // find voice by name + // Glad I made a function for this... + i = R_FindIDForVoice(field); + + if (i != MAXSKINVOICES) + { + // Found a voice. + LUA_PushUserdata(L, &skinvoices[i], META_VOICE); + return 1; + } + + return 0; +} + +// In this case: returns TOTAL voices, and not individual table counts. +static int lib_numVoices(lua_State* L) +{ + lua_pushinteger(L, numskinvoices + numfreeslotvoices); + return 1; +} + +int LUA_VoiceLib(lua_State* L) +{ + luaL_newmetatable(L, META_VOICE); + lua_pushcfunction(L, voice_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, voice_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, voice_num); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getVoice); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numVoices); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "skinvoices"); + + return 0; +} \ No newline at end of file diff --git a/src/r_skins.c b/src/r_skins.c index ddccbd680..4c8443f92 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -339,7 +339,6 @@ static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice) { if (S_sfx[i].skinsound != -1) { - skin->soundsid[S_sfx[i].skinsound] = i; Sk_SetSkinVoiceValue(skin_voice, S_sfx[i].skinsound, i); } } @@ -1275,7 +1274,6 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) stoken + stokenadjust)) { newskinsound = S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].flags, true); - skin->soundsid[S_sfx[i].skinsound] = newskinsound; if (skin->voice) { diff --git a/src/r_skins.h b/src/r_skins.h index 4b57acb4b..5b0ac7b46 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -99,13 +99,15 @@ struct skin_t char rivals[SKINRIVALS][SKINNAMESIZE+1]; // Your top 3 rivals for GP mode. Uses names so that you can reference skins that aren't added - // specific sounds per skin - sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table (Deprecated; remove eventually) - - // Pointer to a skin's voice. + // Pointer to a skin's voice, providing specific sounds per-skin. // If an mobj_t doesn't have a voice, it defaults to this value, given a skin is in use. kartvoice_t *voice; + // Proxy value for the (now removed) soundsid array. + // This is ONLY used in a Lua context. + // It doesn't actually have anything and will never actually have anything. + skinsound_t soundsid[1]; + // contains super versions too spritedef_t sprites[NUMPLAYERSPRITES*2]; spriteinfo_t sprinfo[NUMPLAYERSPRITES*2]; diff --git a/src/s_sound.c b/src/s_sound.c index c2808e41a..efea4a744 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -617,7 +617,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) if (cur_voice) { - sfx_id = P_GetVoiceSFX(cur_voice, sfx->skinsound); // ((skin_t *)origin->skin)->soundsid[sfx->skinsound] + sfx_id = P_GetVoiceSFX(cur_voice, sfx->skinsound); sfx = &S_sfx[sfx_id]; } } From 0625e92dc1a695322ac669d9c4725ac8bc82c43a Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 15 Nov 2025 06:20:59 -0500 Subject: [PATCH 14/44] Update (remove) TODO --- src/r_skins.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/r_skins.h b/src/r_skins.h index 5b0ac7b46..f41bf6a6b 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -48,8 +48,6 @@ typedef enum #define MAXVOICEFREESLOTS (MAXSKINVOICES - KSKVC_FIRSTFREESLOT) // Player voice struct -// ## TODO: -// - Get rid of soundsid for good, replace it with some hacky compat system struct kartvoice_t { UINT32 id; From e0d4a8e385b341811bd98efc6330354b7829dd36 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 15 Nov 2025 09:34:44 -0500 Subject: [PATCH 15/44] Consolidate voice allocation Skin voices and freeslot voices are no longer separated, with all voices allocating as skin voices would prior. Due to this, skin voice allocation has been refactored a bit: - No longer relies on a name string, instead using Dehacked name info hashes and string offsets - Name string checks now check the voice's Dehacked name; the skin 'tails' will allocate KVOICE_TAILS, and so on - Overall refactors to fit this new system --- src/d_netcmd.c | 177 +++++++++++++++++++++++++++------------------ src/deh_soc.c | 23 ++---- src/deh_tables.c | 8 +- src/dehacked.c | 20 +---- src/g_demo.c | 7 +- src/info.c | 4 +- src/lua_voicelib.c | 39 ++-------- src/r_skins.c | 99 ++++++++++++++----------- src/r_skins.h | 6 +- 9 files changed, 191 insertions(+), 192 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 23372b7ed..8270fadaf 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1406,6 +1406,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi int i; const char *value = cvar->string; kartvoice_t *myvoice = voice; + char cvarname[VOICENAMESIZE+1]; if (!Playing()) { @@ -1424,66 +1425,43 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi } } - // First, check for freeslotted voices, should they exist at all. - if (numfreeslotvoices) + // Scan through our allocated skin voices for the wanted voice. + strncpy(cvarname, value, VOICENAMESIZE); + strupr(cvarname); + + i = DEH_FindVoice(cvarname); + + if (i != MAXSKINVOICES) { - for (i = 0; i < numfreeslotvoices; i++) + // Make sure this voice belongs to the skin we're about to assign it to. + if (skinvoices[i].parent == skin_id || forceme) { - 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); - cvar->value = skinvoices[i + KSKVC_FIRSTFREESLOT].id; - } - else - { - // This isn't a valid voice for this skin, so don't assign shit. - if (!silent) - 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); + // Change the cvar to be the value of the name. + strncpy(cvarname, DEH_VoiceName(i), VOICENAMESIZE); + strlwr(cvarname); - // *Reassign* the voice to whatever the current voice is, at least. - CV_StealthSet(cvar, myvoice->name); - cvar->value = myvoice->id; - return 1; - } - - // Return regardless, as we've found the voice we were looking for. - return 0; - } + CV_StealthSet(cvar, cvarname); + cvar->value = skinvoices[i].id; } - } - - // 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) + else { - // 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); - cvar->value = skinvoices[i].id; - } - else - { - // This isn't a valid voice for this skin, so don't assign shit. - if (!silent) - CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" doesn't belong to this skin (%s).\n"), skinvoices[i].name, skins[skin_id].name); + // This isn't a valid voice for this skin, so don't assign shit. + if (!silent) + CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" doesn't belong to this skin (%s).\n"), DEH_VoiceName(i), skins[skin_id].name); - // *Reassign* the voice cvar to whatever the current voice is, at least. - CV_StealthSet(cvar, myvoice->name); - cvar->value = myvoice->id; - return 1; - } + // *Reassign* the voice cvar to whatever the current voice is, at least. + strncpy(cvarname, DEH_VoiceName(myvoice->id), VOICENAMESIZE); + strlwr(cvarname); - // Return regardless, as we've found the voice we were looking for. - return 0; + CV_StealthSet(cvar, cvarname); + cvar->value = myvoice->id; + + // Signal to networking that we failed to set a voice. + return 1; } + + // We've found the voice we were looking for! + return 0; } // Found diddly-squat, say as much. @@ -1491,8 +1469,13 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist.\n"), value); // Reassign the voice cvar to our current voice. - CV_StealthSet(cvar, myvoice->name); + strncpy(cvarname, DEH_VoiceName(myvoice->id), VOICENAMESIZE); + strlwr(cvarname); + + CV_StealthSet(cvar, cvarname); cvar->value = myvoice->id; + + // Signal to networking that we failed to set a voice. return 1; } @@ -1859,6 +1842,7 @@ static void SendNameAndColor(UINT8 n) boolean permamute = false; char buf[MAXPLAYERNAME+12]; + char voicebuf[VOICENAMESIZE+1]; char *p; if (splitscreen < n) @@ -1912,7 +1896,7 @@ static void SendNameAndColor(UINT8 n) && !strcmp(cv_skin[n].string, skins[player->skin].name) && cv_follower[n].value == player->followerskin && cv_followercolor[n].value == player->followercolor - && (!permamute && !strcmp(cv_voice[n].string, voice->name))) + && (!permamute && !stricmp(cv_voice[n].string, DEH_VoiceName(voice->id)))) return; player->availabilities = R_GetSkinAvailabilities(); @@ -1955,7 +1939,10 @@ static void SendNameAndColor(UINT8 n) // Reset the voice to that of the skin's. if (prevskin != player->skin && skins[player->skin].voice) { - CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = (UINT16)skins[player->skin].voice->id; } } @@ -1971,14 +1958,22 @@ static void SendNameAndColor(UINT8 n) // Reset the voice to that of the skin's. if (prevskin != player->skin && skins[player->skin].voice) { - CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = (UINT16)skins[player->skin].voice->id; } } // Need to update voices after the fact. if (!cv_voice[n].string) - CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + { + strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); + } SetPlayerVoice(playernum, cv_voice[n].string); @@ -1986,7 +1981,10 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - CV_StealthSet(&cv_voice[n], valuevoice->name); + strncpy(voicebuf, DEH_VoiceName(valuevoice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = (UINT16)valuevoice->id; } else @@ -1996,13 +1994,19 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - CV_StealthSet(&cv_voice[n], valuevoice->name); + strncpy(voicebuf, DEH_VoiceName(valuevoice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = (UINT16)valuevoice->id; } else { // ...still nothing? - CV_StealthSet(&cv_voice[n], "sonic_voice"); + strncpy(voicebuf, DEH_VoiceName(0), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = 0; } } @@ -2031,7 +2035,10 @@ static void SendNameAndColor(UINT8 n) if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, false, true)) { // Our voice is no longer valid, set it to that of our skin's. - CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = skins[player->skin].voice->id; } } @@ -2050,7 +2057,12 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) - CV_StealthSet(&cv_voice[n], skins[player->skin].voice->name); + { + strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); + } if (!permamute) { @@ -2062,7 +2074,10 @@ static void SendNameAndColor(UINT8 n) if (valuevoice && valuevoice->parent == cv_skin[n].value) { - CV_StealthSet(&cv_voice[n], valuevoice->name); + strncpy(voicebuf, DEH_VoiceName(valuevoice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = (UINT16)valuevoice->id; } else @@ -2072,13 +2087,19 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - CV_StealthSet(&cv_voice[n], valuevoice->name); + strncpy(voicebuf, DEH_VoiceName(valuevoice->id), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = (UINT16)valuevoice->id; } else { // ...still nothing? - CV_StealthSet(&cv_voice[n], "sonic_voice"); + strncpy(voicebuf, DEH_VoiceName(0), VOICENAMESIZE); + strlwr(voicebuf); + + CV_StealthSet(&cv_voice[n], voicebuf); cv_voice[n].value = 0; } } @@ -2101,6 +2122,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { player_t *p = &players[playernum]; char name[MAXPLAYERNAME+1]; + char voicename[VOICENAMESIZE+1]; UINT16 color, followercolor; UINT16 skin, voice; SINT8 follower; @@ -2212,7 +2234,10 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name); - CV_StealthSet(&cv_voice[localplayer], skins[forcedskin].voice->name); + strncpy(voicename, DEH_VoiceName(skins[forcedskin].voice->id), VOICENAMESIZE); + strlwr(voicename); + + CV_StealthSet(&cv_voice[localplayer], voicename); cv_voice[localplayer].value = skins[forcedskin].voice->id; } @@ -2228,7 +2253,10 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (localplayer != -1) { - CV_StealthSet(&cv_voice[localplayer], skinvoices[voice].name); + strncpy(voicename, DEH_VoiceName(voice), VOICENAMESIZE); + strlwr(voicename); + + CV_StealthSet(&cv_voice[localplayer], voicename); cv_voice[localplayer].value = skinvoices[voice].id; } } @@ -7472,6 +7500,8 @@ static void Color4_OnChange(void) static void __voice_cvar_func(INT32 pid, UINT8 pnum) { + char cvarname[VOICENAMESIZE+1]; + kartvoice_t *myvoice = P_GetMobjVoice(players[pid].mo); if (!myvoice) @@ -7498,7 +7528,11 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) 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); + strncpy(cvarname, DEH_VoiceName(myvoice->id), VOICENAMESIZE); + strlwr(cvarname); + CV_StealthSet(&cv_voice[pnum], cvarname); + + cv_voice[pnum].value = myvoice->id; return; } } @@ -7508,7 +7542,12 @@ static void __voice_cvar_func(INT32 pid, UINT8 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); + + strncpy(cvarname, DEH_VoiceName(myvoice->id), VOICENAMESIZE); + strlwr(cvarname); + CV_StealthSet(&cv_voice[pnum], cvarname); + + cv_voice[pnum].value = myvoice->id; } } diff --git a/src/deh_soc.c b/src/deh_soc.c index dfbf19e01..fcba24275 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -4316,23 +4316,7 @@ void readkartvoice(MYFILE *f, kartvoice_t *voice) word2 = (tmp += 2); strupr(word2); - if (fastcmp(word, "NAME")) - { - strlwr(word2); - // Catch any voices with similar names. - for (i = KSKVC_FIRSTFREESLOT; i < MAXSKINVOICES; i++) - { - if (strnicmp(word2, skinvoices[i].name, sizeof skinvoices[i].name) == 0) - { - // Name collision! - // Append the ID to differentiate... poorly. - snprintf(word2, sizeof voice->name, "%u_%s", voice->id, skinvoices[i].name); - } - } - strncpy(voice->name, word2, sizeof voice->name); - CONS_Printf("KartVoice %s: renamed to \"%s\"\n", strbuf_get(voicenames, voice->info.nameofs), voice->name); - } - else if (fastcmp(word, "SKIN")) + if (fastcmp(word, "SKIN")) { i = R_SkinAvailable(word2); @@ -4395,6 +4379,9 @@ void readkartvoice(MYFILE *f, kartvoice_t *voice) } while (!myfeof(f)); // finish when the line is empty + // Set our ID afterwards. + voice->id = (UINT32)(voice - skinvoices); + Z_Free(s); } @@ -4587,7 +4574,7 @@ kartvoicetype_e get_kartvoice(const char *word) if (fastncmp("KVOICE_",word,7)) word += 7; // take off the KVOICE_ i = DEH_FindVoice(word); - if (i == MAXVOICEFREESLOTS) + if (i == MAXSKINVOICES) deh_warning("Couldn't find voice named 'KVOICE_%s'", word); return i; } diff --git a/src/deh_tables.c b/src/deh_tables.c index cb90f9467..c05fe3036 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -71,7 +71,7 @@ const char *DEH_KartItemName(kartitemtype_e i) const char *DEH_VoiceName(kartvoicetype_e i) { - return strbuf_get(voicenames, skinvoices[i + KSKVC_FIRSTFREESLOT].info.nameofs); + return strbuf_get(voicenames, skinvoices[i].info.nameofs); } mobjtype_t DEH_FindMobjtype(const char *word) @@ -143,13 +143,13 @@ kartvoicetype_e DEH_FindVoice(const char *word) { INT32 i; UINT32 hash = HASH32(word, strlen(word)); - for (i = 0; i < numfreeslotvoices; i++) { - if (hash != skinvoices[i + KSKVC_FIRSTFREESLOT].info.namehash) + for (i = 0; i < numskinvoices; i++) { + if (hash != skinvoices[i].info.namehash) continue; if (fastcmp(word, DEH_VoiceName(i))) return (kartvoicetype_e)i; } - return MAXVOICEFREESLOTS; + return MAXSKINVOICES; } struct flickytypes_s FLICKYTYPES[] = { diff --git a/src/dehacked.c b/src/dehacked.c index 029813a96..6cdb8db65 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -393,19 +393,7 @@ INT32 DEH_ReadFreeslot(const char *type, const char *word, INT32 *out) } else if (fastcmp(type, "KVOICE")) { - *out = DEH_FindVoice(word); - if (*out != MAXVOICEFREESLOTS) - return 0; // already allocated - - if (numfreeslotvoices == MAXVOICEFREESLOTS) { - CONS_Alert(CONS_WARNING, "Ran out of free voice slots!\n"); - return -1; - } - CONS_Printf("Voice KVOICE_%s allocated.\n",word); - DEH_Link(word, &skinvoices[numfreeslotvoices + KSKVC_FIRSTFREESLOT].info, &voicenames); - skinvoices[numfreeslotvoices + KSKVC_FIRSTFREESLOT].id = numfreeslotvoices + KSKVC_FIRSTFREESLOT; - *out = numfreeslotvoices++; - return 0; + return R_AllocVoice(false, word, out); } return -2; @@ -806,12 +794,12 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) { if (i == 0 && word2[0] != '0') // If word2 isn't a number i = get_kartvoice(word2); // find a voice by name - if ((i + 1) >= 1 && i < numfreeslotvoices) - readkartvoice(f, &skinvoices[i + KSKVC_FIRSTFREESLOT]); + if ((i + 1) >= 1 && i < numskinvoices) + readkartvoice(f, &skinvoices[i]); else { // zero-based, but let's start at 1 - deh_warning("KartVoice number %d out of range (1 - %d)", i, numfreeslotvoices); + deh_warning("KartVoice number %d out of range (1 - %d)", i, numskinvoices); ignorelines(f); } } diff --git a/src/g_demo.c b/src/g_demo.c index ba791ebb1..9fba3164a 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -41,6 +41,7 @@ #include "lua_hook.h" #include "p_saveg.h" // savebuffer_t #include "g_party.h" +#include "deh_tables.h" // SRB2Kart #include "d_netfil.h" // nameonly @@ -54,7 +55,7 @@ #include "k_color.h" #include "k_follower.h" #include "k_grandprix.h" -#include "strbuf.h" +//#include "strbuf.h" static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; consvar_t cv_recordmultiplayerdemos = CVAR_INIT ("netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL); @@ -1395,7 +1396,7 @@ void G_WriteDemoExtraData(void) WRITESTRINGL(demobuf.p, skins[players[i].skin].name, 16+1); // Voice - WRITESTRINGL(demobuf.p, skinvoices[players[i].voice_id].name, 32+1); + WRITESTRINGL(demobuf.p, DEH_VoiceName(players[i].voice_id), 32+1); // Stats WRITEUINT8(demobuf.p, skins[players[i].skin].kartspeed); @@ -2982,7 +2983,7 @@ void G_BeginRecording(void) WRITESTRINGL(demobuf.p, skincolors[player->skincolor].name, 16+1); // Voice - WRITESTRINGL(demobuf.p, skinvoices[player->voice_id].name, 32+1); + WRITESTRINGL(demobuf.p, DEH_VoiceName(player->voice_id), 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! diff --git a/src/info.c b/src/info.c index 827fb8eb9..438a56d3c 100644 --- a/src/info.c +++ b/src/info.c @@ -262,12 +262,12 @@ void P_ResetData(INT32 flags) // voices if (init) { - for (i = KSKVC_FIRSTFREESLOT; i < MAXSKINVOICES; i++) + for (i = 0; i < MAXSKINVOICES; i++) { R_ClearVoice(i); } - numfreeslotvoices = 0; + numskinvoices = 0; if (voicenames) Z_Free(voicenames); diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index dae6edf1b..2b9a7d5a1 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -16,6 +16,7 @@ #include "fastcmp.h" #include "r_skins.h" #include "sounds.h" +#include "deh_tables.h" #include "lua_script.h" #include "lua_libs.h" @@ -23,7 +24,7 @@ enum voicevars { voicevars_id = 0, - voicevars_name, + voicevars_name, // Not actually in kartvoice_t; returns the name of the voice voicevars_parent, voicevars_parentname, // Not actually in kartvoice_t; returns the name of the parent skin voicevars_win, @@ -70,7 +71,7 @@ static int voice_get(lua_State* L) lua_pushinteger(L, voice->id); break; case voicevars_name: - lua_pushstring(L, voice->name); + lua_pushstring(L, DEH_VoiceName((kartvoicetype_e)(voice - skinvoices))); break; case voicevars_parent: lua_pushinteger(L, voice->parent); @@ -170,16 +171,7 @@ static int lib_iterateVoices(lua_State* L) i = 0; // voices are always valid, only added, never removed - if (i >= KSKVC_MAXVANILLA) - { - // Check freeslotted voices. - if ((i - KSKVC_MAXVANILLA) < numfreeslotvoices) - { - LUA_PushUserdata(L, &skinvoices[i], META_VOICE); - return 1; - } - } - else if (i < numskinvoices) + if (i < numskinvoices) { LUA_PushUserdata(L, &skinvoices[i], META_VOICE); return 1; @@ -202,14 +194,6 @@ static int lib_getVoice(lua_State* L) return luaL_error( L, "skinvoices[] index %d out of range (0 - %d)", i, MAXSKINVOICES - 1); } - if (i >= KSKVC_MAXVANILLA) - { - // Check freeslotted voices. - if ((i - KSKVC_MAXVANILLA) >= numfreeslotvoices) - { - return 0; - } - } else if (i >= numskinvoices) { return 0; @@ -228,19 +212,6 @@ static int lib_getVoice(lua_State* L) return 1; } - // Special functions for getting voice counts per individual table. - if (fastcmp(field, "basevoices")) - { - lua_pushinteger(L, numskinvoices); - return 1; - } - - if (fastcmp(field, "freeslotvoices")) - { - lua_pushinteger(L, numfreeslotvoices); - return 1; - } - // find voice by name // Glad I made a function for this... i = R_FindIDForVoice(field); @@ -258,7 +229,7 @@ static int lib_getVoice(lua_State* L) // In this case: returns TOTAL voices, and not individual table counts. static int lib_numVoices(lua_State* L) { - lua_pushinteger(L, numskinvoices + numfreeslotvoices); + lua_pushinteger(L, numskinvoices); return 1; } diff --git a/src/r_skins.c b/src/r_skins.c index 4c8443f92..2957c9f5b 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -28,6 +28,7 @@ #include "dehacked.h" // get_number (for thok) #include "m_cond.h" #include "d_main.h" // MAINWAD_CHARS +#include "deh_tables.h" // Voice freeslotting #if 0 #include "k_kart.h" // K_KartResetPlayerColor #endif @@ -40,7 +41,6 @@ INT32 numskins = 0; INT32 numskinvoices = 0; -INT32 numfreeslotvoices = 0; skin_t skins[MAXSKINS]; INT32 skinsorted[MAXSKINS]; kartvoice_t skinvoices[MAXSKINVOICES]; @@ -344,10 +344,6 @@ static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice) } // TODO: Make this hexadecimal - snprintf(skin_voice->name, - sizeof skin_voice->name, "voice %u", (UINT32)(skin_voice-skinvoices)); - skin_voice->name[sizeof skin_voice->name - 1] = '\0'; - skin_voice->parent = (UINT16)(skin - skins); skin_voice->id = skin_voice-skinvoices; @@ -725,27 +721,14 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) kartvoicetype_e R_FindIDForVoice(const char* voicename) { INT32 i; + const char *checkvoicename; - // 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. + // Scan through our allocated skin voices for the wanted voice. for (i = 0; i < numskinvoices; i++) { // Whatever. Go, my code duplication! - if (strncmp(voicename, skinvoices[i].name, sizeof skinvoices[i].name) == 0) + checkvoicename = DEH_VoiceName(i); + if (strnicmp(voicename, checkvoicename, VOICENAMESIZE) == 0) { // Found a voice. return (kartvoicetype_e)i; @@ -760,20 +743,7 @@ static kartvoicetype_e R_FindIDForVoiceNum(UINT16 voicenum) { INT32 i; - // 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. - return (kartvoicetype_e)(i + KSKVC_FIRSTFREESLOT); - } - } - } - - // Freeslotted voices don't exist, or we've found nothing; check skin voices. + // Scan through our allocated skin voices for the wanted voice. for (i = 0; i < numskinvoices; i++) { // Whatever. Go, my code duplication! @@ -815,7 +785,8 @@ void SetPlayerVoice(INT32 playernum, const char* voicename) const kartvoice_t* myvoice = P_GetMobjVoice(player->mo); - if (!strncmp(voicename, myvoice->name, sizeof myvoice->name)) + const char* myvoicename = DEH_VoiceName(myvoice->id); + if (!strnicmp(voicename, myvoicename, VOICENAMESIZE)) { // Hey... this voice is the same as before! // WAIT! Before we go, reset the player's voice ID! @@ -1109,10 +1080,16 @@ static void R_IHateThatHedgehog(UINT16 wadnum) Sk_SetDefaultValue(skin, skin_voice); skin->wadnum = wadnum; strcpy(skin->name, "sonic"); - - strcpy(skin->voice->name, "sonic_voice"); skin->voice->parent = 0; + // Add voice + INT32 dummy; + if (R_AllocVoice(true, skin->name, &dummy) < 0) + { + // Hey guy, take care! + I_Error("Failed to allocate initial skin voice\n"); + } + #ifdef SKINVALUES skin_cons_t[0].value = 0; skin_cons_t[0].strvalue = skin->name; @@ -1288,6 +1265,33 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) return true; } +// +// Allocates a voice onto the dehacked list, and iterates the provided output pointer +// (should it exist). +// +INT32 R_AllocVoice(boolean skinalloc, const char* name, INT32 *out) +{ + char voxname_upper[VOICENAMESIZE]; + + strncpy(voxname_upper, name, VOICENAMESIZE); + strupr(voxname_upper); + + *out = DEH_FindVoice(voxname_upper); + if (*out != MAXSKINVOICES) + return skinalloc ? -1 : 0; // already allocated + + if (numskinvoices == MAXSKINVOICES - 1) { + CONS_Alert(CONS_WARNING, "Ran out of free voice slots!\n"); + return -1; + } + + CONS_Printf("Voice KVOICE_%s allocated.\n",voxname_upper); + DEH_Link(voxname_upper, &skinvoices[numskinvoices].info, &voicenames); + + *out = numskinvoices++; + return 0; +} + // // Find skin sprites, sounds & optional status bar face, & add them // @@ -1330,7 +1334,7 @@ void R_AddSkins(UINT16 wadnum) // set defaults skin = &skins[numskins]; - skin_voice = &skinvoices[numskins]; + skin_voice = &skinvoices[numskinvoices]; Sk_SetDefaultValue(skin, skin_voice); skin->wadnum = wadnum; realname = false; @@ -1360,7 +1364,6 @@ void R_AddSkins(UINT16 wadnum) if (skinnum == -1) { STRBUFCPY(skin->name, value); - STRBUFCPY(skin->voice->name, va("%s_voice", value)); skin->voice->parent = (UINT16)(skin - skins); } // the skin name must uniquely identify a single skin @@ -1379,7 +1382,6 @@ void R_AddSkins(UINT16 wadnum) // I'm lazy so if NEW name is already used I leave the 'skin x' // default skin name set in Sk_SetDefaultValue STRBUFCPY(skin->name, value2); - STRBUFCPY(skin->voice->name, va("%s_voice", value2)); skin->voice->parent = (UINT16)(skin - skins); } Z_Free(value2); @@ -1405,6 +1407,18 @@ void R_AddSkins(UINT16 wadnum) } free(buf2); + // Add voice + INT32 dummy; + if (R_AllocVoice(true, skin->name, &dummy) < 0) + { + // We couldn't allocate our own skin's voice?! + I_Error("Failed to allocate voice for skin %s\n", skin->name); + } + else + { + CONS_Printf(M_GetText("%d voices allocated.\n"), numskinvoices); + } + // Add sprites R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); //ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere @@ -1431,7 +1445,6 @@ void R_AddSkins(UINT16 wadnum) skinsorted[numskins] = numskins; numskins++; - numskinvoices++; } SortSkins(); return; diff --git a/src/r_skins.h b/src/r_skins.h index f41bf6a6b..eb69d950e 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -52,10 +52,9 @@ struct kartvoice_t { UINT32 id; + // Dehacked info for the voice's name (hash and pointer within the voicenames array). dehinfo_t info; - char name[VOICENAMESIZE+1]; // INT16 descriptive name of the voice. - // UINT16 ID of the parent skin this voice belongs to. // Checked when the voice is assigned to prevent erroneous dubs. UINT16 parent; @@ -131,7 +130,7 @@ enum skinmenusortoption }; /// Externs -extern INT32 numskins, numskinvoices, numfreeslotvoices; +extern INT32 numskins, numskinvoices; extern skin_t skins[MAXSKINS]; extern INT32 skinsorted[MAXSKINS]; extern kartvoice_t skinvoices[MAXSKINVOICES]; @@ -156,6 +155,7 @@ 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); +INT32 R_AllocVoice(boolean skinalloc, const char* name, INT32 *out); void R_ClearVoice(kartvoicetype_e voicetype); void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound); From fa478682405dece7e7d5c44b86ffaf61538221c8 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 15 Nov 2025 20:45:00 -0500 Subject: [PATCH 16/44] Automatically allocate voice sounds should they not already exist in memory No more stupid SFX freeslotting in SOCs --- src/deh_soc.c | 61 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index fcba24275..ab1a631fe 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -4244,11 +4244,26 @@ void readkartresult(MYFILE *f, kartresult_t *result) #define WARN(str, ...) deh_warning("KartVoice %s: " str, strbuf_get(voicenames, voice->info.nameofs), __VA_ARGS__) #define WARN0(str) deh_warning("KartVoice %s: " str, strbuf_get(voicenames, voice->info.nameofs)) +// Necessary for sound adding, so we can gather flags and singularity data. +static sfxenum_t skinsoundreroute[] = { + sfx_kwin, + sfx_klose, + sfx_khurt1, + sfx_khurt2, + sfx_kattk1, // Offense item taunt + sfx_kattk2, + sfx_kbost1, // Boost item taunt + sfx_kbost2, + sfx_kslow, // Overtake taunt + sfx_khitem, // Hit confirm taunt + sfx_kgloat, // Power item taunt +}; + // Extremely shitty and lifted almost entirely from r_skins.c // Sets the sound effect value for a voice, given a valid skinsound value is passed. static UINT8 reallygrossvoicesfxadder(INT32 skinsound, const char *value, kartvoice_t *voice) { - sfxenum_t i; + sfxenum_t i, j, reroute; char printval[6]; // Remove the prefix. (We can affect this directly since we're not going to use it again.) @@ -4259,24 +4274,44 @@ static UINT8 reallygrossvoicesfxadder(INT32 skinsound, const char *value, kartvo strncpy(printval, value, 6); - // copy name of sounds that are remapped - // for this skin - for (i = 0; i < sfx_skinsoundslot0; i++) + reroute = skinsoundreroute[skinsound]; + + // Automatically allocate any defined voice sounds. + // Freeslotted voice clips won't ever belong to a skin. + + i = sfx_None; + + // Find if this sound already exists first. + for (j = 0; j < NUMSFX; j++) { - if (!S_sfx[i].name) + if (!S_sfx[j].name) continue; - if (!stricmp(S_sfx[i].name, value)) + + if (!stricmp(S_sfx[j].name, value)) { - if (voice) - { - Sk_SetSkinVoiceValue(voice, skinsound, i); - return 0; - } + // Found an existing sound. + i = j; + break; } } - strlwr(printval); - WARN("'sfx_%s' does not exist", printval); + // This sound doesn't seem to exist, so let's add a new one. + if (i == sfx_None) + { + CONS_Printf("Sound sfx_%s allocated.\n", value); + i = S_AddSoundFx(value, S_sfx[reroute].singularity, S_sfx[reroute].flags, false); + } + + if (i != sfx_None) + { + if (voice) + { + Sk_SetSkinVoiceValue(voice, skinsound, i); + return 0; + } + } + + WARN0("Ran out of free SFX slots!"); return 1; } From 27c2c68a99e3490207657beb9b89625faf3155a1 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 15 Nov 2025 20:45:35 -0500 Subject: [PATCH 17/44] Lua kartvoice_t now routes to the relevant skin and sfxinfo structs --- src/lua_voicelib.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index 2b9a7d5a1..e3035d015 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -26,7 +26,6 @@ enum voicevars voicevars_id = 0, voicevars_name, // Not actually in kartvoice_t; returns the name of the voice voicevars_parent, - voicevars_parentname, // Not actually in kartvoice_t; returns the name of the parent skin voicevars_win, voicevars_lose, voicevars_pain, @@ -41,7 +40,6 @@ static const char *const voicevars_opt[] = { "id", "name", "parent", - "parentname", "win", "lose", "pain", @@ -68,22 +66,18 @@ static int voice_get(lua_State* L) switch (field) { case voicevars_id: - lua_pushinteger(L, voice->id); - break; + return RNOGET; case voicevars_name: lua_pushstring(L, DEH_VoiceName((kartvoicetype_e)(voice - skinvoices))); break; case voicevars_parent: - lua_pushinteger(L, voice->parent); - break; - case voicevars_parentname: - lua_pushstring(L, skins[voice->parent].name); + LUA_PushUserdata(L, &skins[voice->parent], META_SKIN); break; case voicevars_win: - lua_pushinteger(L, voice->win); + LUA_PushUserdata(L, &S_sfx[voice->win], META_SFXINFO); break; case voicevars_lose: - lua_pushinteger(L, voice->lose); + LUA_PushUserdata(L, &S_sfx[voice->lose], META_SFXINFO); break; case voicevars_pain: // Wasteful and shitty, I'll wager, but it's better than risking segfaults @@ -92,7 +86,7 @@ static int voice_get(lua_State* L) lua_createtable(L, painarraysize, 0); for (size_t i = 0; i < painarraysize; i++) { - lua_pushinteger(L, voice->pain[i]); + LUA_PushUserdata(L, &S_sfx[voice->pain[i]], META_SFXINFO); lua_rawseti(L, -2, 1 + i); } break; @@ -102,7 +96,7 @@ static int voice_get(lua_State* L) lua_createtable(L, atkarraysize, 0); for (size_t i = 0; i < atkarraysize; i++) { - lua_pushinteger(L, voice->attack[i]); + LUA_PushUserdata(L, &S_sfx[voice->attack[i]], META_SFXINFO); lua_rawseti(L, -2, 1 + i); } break; @@ -112,18 +106,18 @@ static int voice_get(lua_State* L) lua_createtable(L, bstarraysize, 0); for (size_t i = 0; i < bstarraysize; i++) { - lua_pushinteger(L, voice->boost[i]); + LUA_PushUserdata(L, &S_sfx[voice->boost[i]], META_SFXINFO); lua_rawseti(L, -2, 1 + i); } break; case voicevars_overtake: - lua_pushinteger(L, voice->overtake); + LUA_PushUserdata(L, &S_sfx[voice->overtake], META_SFXINFO); break; case voicevars_hitem: - lua_pushinteger(L, voice->hitem); + LUA_PushUserdata(L, &S_sfx[voice->hitem], META_SFXINFO); break; case voicevars_power: - lua_pushinteger(L, voice->power); + LUA_PushUserdata(L, &S_sfx[voice->power], META_SFXINFO); break; default: return RNOFIELD; From f2f6ba9a729935b7fbc06ad193aa33d2f806c6fd Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 11:24:36 -0500 Subject: [PATCH 18/44] Remove redundant KVOICE_ check, automatically allocate KARTVOICE fields in SOCs --- src/deh_soc.c | 11 ++++++++--- src/dehacked.c | 4 ---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index ab1a631fe..1f1eef330 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -32,6 +32,7 @@ #include "r_picformats.h" #include "r_things.h" // R_Char2Frame #include "r_sky.h" +#include "r_skins.h" // R_AllocVoice #include "fastcmp.h" #include "lua_script.h" // Reluctantly included for LUA_EvalMath #include "d_clisrv.h" @@ -4606,11 +4607,15 @@ useoddsfunc_f *get_useoddsfunc(const char *word) kartvoicetype_e get_kartvoice(const char *word) { // Returns the value of KVOICE_ enumerations kartvoicetype_e i; - if (fastncmp("KVOICE_",word,7)) - word += 7; // take off the KVOICE_ i = DEH_FindVoice(word); if (i == MAXSKINVOICES) - deh_warning("Couldn't find voice named 'KVOICE_%s'", word); + { + // Freeslot voices that don't exist yet. + const INT32 allocresult = R_AllocVoice(false, word, &i); + + if (allocresult < 0) + deh_warning("Voice '%s' could not be allocated.", word); + } return i; } diff --git a/src/dehacked.c b/src/dehacked.c index 6cdb8db65..6f70f2ded 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -391,10 +391,6 @@ INT32 DEH_ReadFreeslot(const char *type, const char *word, INT32 *out) K_RegisterItem(numkartitems); *out = numkartitems++; } - else if (fastcmp(type, "KVOICE")) - { - return R_AllocVoice(false, word, out); - } return -2; } From 0ddf23b2aea7a7c0da4601f4019184ec6857a70f Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 11:24:48 -0500 Subject: [PATCH 19/44] Trim this fucking enum a bit --- src/r_skins.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/r_skins.h b/src/r_skins.h index eb69d950e..25861b190 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -39,9 +39,8 @@ extern consvar_t cv_skinselectstyle, cv_skinselectsort; typedef enum { - KSKVC_MAXVANILLA = MAXSKINS, - KSKVC_FIRSTFREESLOT = KSKVC_MAXVANILLA, - KSKVC_LASTFREESLOT = 65535, + KSKVC_FIRSTSLOT = 0, + KSKVC_LASTSLOT = 65535, MAXSKINVOICES } kartvoicetype_e; From fbbb5c93432687479982ac07ffdd6ab85cdeaa30 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 11:29:27 -0500 Subject: [PATCH 20/44] Lua voice allocation and setters Idiot proofed these as much as I could; setting arrays in this language is hell --- src/lua_baselib.c | 37 +++++++++++ src/lua_voicelib.c | 156 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 191 insertions(+), 2 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 1783548f7..0e7f2006a 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -27,6 +27,7 @@ #include "y_inter.h" #include "hu_stuff.h" // HU_AddChatText #include "console.h" +#include "deh_tables.h" // DEH_FindVoice #include "k_kart.h" // SRB2Kart #include "k_battle.h" #include "k_boss.h" @@ -2612,6 +2613,39 @@ static int lib_rGetNameByColor(lua_State *L) return 1; } + +// R_SKINS +//////////// + +// Lua-exclusive; allocates a voice into memory, and returns a pointer to said voice. +// Mangled clone of deh_soc's get_kartvoice, modified for Lua. +static int lib_rAddVoice(lua_State *L) +{ + const char* word = luaL_checkstring(L, 1); + NOHUD + + char wordupper[VOICENAMESIZE+1]; + + strncpy(wordupper, word, VOICENAMESIZE); + strupr(wordupper); + + INT32 i = DEH_FindVoice(wordupper); + if (i == MAXSKINVOICES) + { + // Freeslot voices that don't exist yet. + const INT32 allocresult = R_AllocVoice(false, word, &i); + + if (allocresult < 0) + return luaL_error(L, "kartvoice_t '%s' could not be allocated.", word); + else + skinvoices[i].id = (UINT32)(&skinvoices[i] - skinvoices); + } + + // If we're down here, we managed to allocate our voice. + LUA_PushUserdata(L, &skinvoices[i], META_VOICE); + return 1; +} + // S_SOUND //////////// static int GetValidSoundOrigin(lua_State *L, void **origin) @@ -5361,6 +5395,9 @@ static luaL_Reg lib[] = { {"R_GetSuperColorByName", lib_rGetSuperColorByName}, {"R_GetNameByColor", lib_rGetNameByColor}, + // r_skins (voice) + {"R_AddVoice", lib_rAddVoice}, + // s_sound {"S_StartSound",lib_sStartSound}, {"S_StartSoundAtVolume",lib_sStartSoundAtVolume}, diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index e3035d015..0d6a186b4 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -20,6 +20,8 @@ #include "lua_script.h" #include "lua_libs.h" +#include "lua_hud.h" // hud_running errors +#include "lua_hook.h" // hook_cmd_running errors enum voicevars { @@ -54,6 +56,7 @@ static const char *const voicevars_opt[] = { #define RNOFIELD luaL_error(L, LUA_QL("kartvoice_t") " has no field named " LUA_QS, field) #define RNOSET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be set.", field) #define RNOGET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be get.", field) +#define UNIMPLEMENTED luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", voicevars_opt[field]) static int voice_get(lua_State* L) { @@ -128,12 +131,161 @@ static int voice_get(lua_State* L) static int voice_set(lua_State* L) { - // ...let's just operate by skin rules. So much for random-line voice dubs... - return luaL_error(L, LUA_QL("kartvoice_t") " struct cannot be edited by Lua."); + kartvoice_t* voice = *((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE)); + enum voicevars field = luaL_checkoption(L, 2, voicevars_opt[0], voicevars_opt); + + // voices are always valid, only added, never removed + I_Assert(voice != NULL); + + if (hud_running) + return luaL_error(L, "Do not alter kartvoice_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter kartvoice_t in CMD building code!"); + + lua_Integer i, k; + + i = 0; + k = sfx_thok; + + switch (field) + { + case voicevars_id: + return RNOSET; + case voicevars_name: + return RNOSET; + case voicevars_parent: + // It would make no sense not to accept a skin struct here. + skin_t* my_new_parent = *((skin_t**)luaL_checkudata(L, 3, META_SKIN)); + + if (!my_new_parent) + { + luaL_error(L, "skin does not exist."); + } + else + { + voice->parent = (UINT16)(my_new_parent - skins); + } + break; + case voicevars_win: + // For the actual sound values, you pass the actual sfxenum_t enums. + // Yes, I know what I said about skin structs literally just above. + voice->win = (sfxenum_t)luaL_checkinteger(L, 3); + break; + case voicevars_lose: + voice->lose = (sfxenum_t)luaL_checkinteger(L, 3); + break; + case voicevars_pain: + luaL_checktype(L, 3, LUA_TTABLE); + + // Pop the other values; they're no longer relevant. + lua_remove(L, 1); + lua_remove(L, 1); + + lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. + + lua_pushnil(L); + while (lua_next(L, 1)) { + i = 0; + + if (!lua_isnoneornil(L, 2)) + i = luaL_checkinteger(L, 2); + + if (i > 0 && i <= (sizeof(voice->pain) / sizeof(voice->pain[0]))) + { + // Only ever accept the first two values. + if (!lua_isnoneornil(L, 3)) + { + // Safely ignore nil values, so modders can do voice.pain = {nil, sfx_mysound} + k = luaL_checkinteger(L, 3); + + voice->pain[i - 1] = (sfxenum_t)k; + } + } + + lua_pop(L, 1); + }; + break; + case voicevars_attack: + luaL_checktype(L, 3, LUA_TTABLE); + + // Pop the other values; they're no longer relevant. + lua_remove(L, 1); + lua_remove(L, 1); + + lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. + + lua_pushnil(L); + while (lua_next(L, 1)) { + i = 0; + + if (!lua_isnoneornil(L, 2)) + i = luaL_checkinteger(L, 2); + + if (i > 0 && i <= (sizeof(voice->attack) / sizeof(voice->attack[0]))) + { + // Only ever accept the first two values. + if (!lua_isnoneornil(L, 3)) + { + // Safely ignore nil values, so modders can do voice.pain = {nil, sfx_mysound} + k = luaL_checkinteger(L, 3); + + voice->attack[i - 1] = (sfxenum_t)k; + } + } + + lua_pop(L, 1); + }; + break; + case voicevars_boost: + luaL_checktype(L, 3, LUA_TTABLE); + + // Pop the other values; they're no longer relevant. + lua_remove(L, 1); + lua_remove(L, 1); + + lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. + + lua_pushnil(L); + while (lua_next(L, 1)) { + i = 0; + + if (!lua_isnoneornil(L, 2)) + i = luaL_checkinteger(L, 2); + + if (i > 0 && i <= (sizeof(voice->boost) / sizeof(voice->boost[0]))) + { + // Only ever accept the first two values. + if (!lua_isnoneornil(L, 3)) + { + // Safely ignore nil values. + k = luaL_checkinteger(L, 3); + + voice->boost[i - 1] = (sfxenum_t)k; + } + } + + lua_pop(L, 1); + }; + break; + case voicevars_overtake: + voice->overtake = (sfxenum_t)luaL_checkinteger(L, 3); + break; + case voicevars_hitem: + voice->hitem = (sfxenum_t)luaL_checkinteger(L, 3); + break; + case voicevars_power: + voice->power = (sfxenum_t)luaL_checkinteger(L, 3); + break; + default: + return RNOFIELD; + } + + return 0; } #undef RNOSET #undef RNOFIELD +#undef UNIMPLEMENTED static int voice_num(lua_State* L) { From 22ff92ea439586d5b0a07215f0741d21869211f7 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 13:43:54 -0500 Subject: [PATCH 21/44] Voice -> KartVoice (mostly) Guest-starring: META_VOICE_ARRAY --- src/d_netcmd.c | 42 ++++++++++++++++++++--------------------- src/deh_soc.c | 12 +++--------- src/deh_tables.c | 6 +++--- src/deh_tables.h | 4 ++-- src/g_demo.c | 4 ++-- src/hardware/hw3sound.c | 2 +- src/lua_baselib.c | 11 ++++++----- src/lua_libs.h | 1 + src/lua_skinlib.c | 2 +- src/r_skins.c | 20 ++++++++++---------- src/r_skins.h | 11 ++++++----- src/s_sound.c | 2 +- 12 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8270fadaf..58f1afc61 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1429,7 +1429,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi strncpy(cvarname, value, VOICENAMESIZE); strupr(cvarname); - i = DEH_FindVoice(cvarname); + i = DEH_FindKartVoice(cvarname); if (i != MAXSKINVOICES) { @@ -1437,7 +1437,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi if (skinvoices[i].parent == skin_id || forceme) { // Change the cvar to be the value of the name. - strncpy(cvarname, DEH_VoiceName(i), VOICENAMESIZE); + strncpy(cvarname, DEH_KartVoiceName(i), VOICENAMESIZE); strlwr(cvarname); CV_StealthSet(cvar, cvarname); @@ -1447,10 +1447,10 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi { // This isn't a valid voice for this skin, so don't assign shit. if (!silent) - CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" doesn't belong to this skin (%s).\n"), DEH_VoiceName(i), skins[skin_id].name); + CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" doesn't belong to this skin (%s).\n"), DEH_KartVoiceName(i), skins[skin_id].name); // *Reassign* the voice cvar to whatever the current voice is, at least. - strncpy(cvarname, DEH_VoiceName(myvoice->id), VOICENAMESIZE); + strncpy(cvarname, DEH_KartVoiceName(myvoice->id), VOICENAMESIZE); strlwr(cvarname); CV_StealthSet(cvar, cvarname); @@ -1469,7 +1469,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist.\n"), value); // Reassign the voice cvar to our current voice. - strncpy(cvarname, DEH_VoiceName(myvoice->id), VOICENAMESIZE); + strncpy(cvarname, DEH_KartVoiceName(myvoice->id), VOICENAMESIZE); strlwr(cvarname); CV_StealthSet(cvar, cvarname); @@ -1896,7 +1896,7 @@ static void SendNameAndColor(UINT8 n) && !strcmp(cv_skin[n].string, skins[player->skin].name) && cv_follower[n].value == player->followerskin && cv_followercolor[n].value == player->followercolor - && (!permamute && !stricmp(cv_voice[n].string, DEH_VoiceName(voice->id)))) + && (!permamute && !stricmp(cv_voice[n].string, DEH_KartVoiceName(voice->id)))) return; player->availabilities = R_GetSkinAvailabilities(); @@ -1939,7 +1939,7 @@ static void SendNameAndColor(UINT8 n) // Reset the voice to that of the skin's. if (prevskin != player->skin && skins[player->skin].voice) { - strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -1958,7 +1958,7 @@ static void SendNameAndColor(UINT8 n) // Reset the voice to that of the skin's. if (prevskin != player->skin && skins[player->skin].voice) { - strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -1969,7 +1969,7 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) { - strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -1981,7 +1981,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strncpy(voicebuf, DEH_VoiceName(valuevoice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(valuevoice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -1994,7 +1994,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strncpy(voicebuf, DEH_VoiceName(valuevoice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(valuevoice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2003,7 +2003,7 @@ static void SendNameAndColor(UINT8 n) else { // ...still nothing? - strncpy(voicebuf, DEH_VoiceName(0), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(0), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2035,7 +2035,7 @@ static void SendNameAndColor(UINT8 n) if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, false, true)) { // Our voice is no longer valid, set it to that of our skin's. - strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2058,7 +2058,7 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) { - strncpy(voicebuf, DEH_VoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2074,7 +2074,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice && valuevoice->parent == cv_skin[n].value) { - strncpy(voicebuf, DEH_VoiceName(valuevoice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(valuevoice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2087,7 +2087,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strncpy(voicebuf, DEH_VoiceName(valuevoice->id), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(valuevoice->id), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2096,7 +2096,7 @@ static void SendNameAndColor(UINT8 n) else { // ...still nothing? - strncpy(voicebuf, DEH_VoiceName(0), VOICENAMESIZE); + strncpy(voicebuf, DEH_KartVoiceName(0), VOICENAMESIZE); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2234,7 +2234,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name); - strncpy(voicename, DEH_VoiceName(skins[forcedskin].voice->id), VOICENAMESIZE); + strncpy(voicename, DEH_KartVoiceName(skins[forcedskin].voice->id), VOICENAMESIZE); strlwr(voicename); CV_StealthSet(&cv_voice[localplayer], voicename); @@ -2253,7 +2253,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (localplayer != -1) { - strncpy(voicename, DEH_VoiceName(voice), VOICENAMESIZE); + strncpy(voicename, DEH_KartVoiceName(voice), VOICENAMESIZE); strlwr(voicename); CV_StealthSet(&cv_voice[localplayer], voicename); @@ -7528,7 +7528,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) if (!(cht_debug || devparm) && !(multiplayer || netgame) // In single player. && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y { - strncpy(cvarname, DEH_VoiceName(myvoice->id), VOICENAMESIZE); + strncpy(cvarname, DEH_KartVoiceName(myvoice->id), VOICENAMESIZE); strlwr(cvarname); CV_StealthSet(&cv_voice[pnum], cvarname); @@ -7543,7 +7543,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your voice at the moment.\n")); - strncpy(cvarname, DEH_VoiceName(myvoice->id), VOICENAMESIZE); + strncpy(cvarname, DEH_KartVoiceName(myvoice->id), VOICENAMESIZE); strlwr(cvarname); CV_StealthSet(&cv_voice[pnum], cvarname); diff --git a/src/deh_soc.c b/src/deh_soc.c index 1f1eef330..5a9425ba4 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -32,7 +32,7 @@ #include "r_picformats.h" #include "r_things.h" // R_Char2Frame #include "r_sky.h" -#include "r_skins.h" // R_AllocVoice +#include "r_skins.h" // R_AllocKartVoice #include "fastcmp.h" #include "lua_script.h" // Reluctantly included for LUA_EvalMath #include "d_clisrv.h" @@ -4607,15 +4607,9 @@ useoddsfunc_f *get_useoddsfunc(const char *word) kartvoicetype_e get_kartvoice(const char *word) { // Returns the value of KVOICE_ enumerations kartvoicetype_e i; - i = DEH_FindVoice(word); + i = DEH_FindKartVoice(word); if (i == MAXSKINVOICES) - { - // Freeslot voices that don't exist yet. - const INT32 allocresult = R_AllocVoice(false, word, &i); - - if (allocresult < 0) - deh_warning("Voice '%s' could not be allocated.", word); - } + deh_warning("Couldn't find voice named 'KVOICE_%s'", word); return i; } diff --git a/src/deh_tables.c b/src/deh_tables.c index c05fe3036..19696625d 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -69,7 +69,7 @@ const char *DEH_KartItemName(kartitemtype_e i) return strbuf_get(kartitemnames, kartitems[i].info.nameofs); } -const char *DEH_VoiceName(kartvoicetype_e i) +const char *DEH_KartVoiceName(kartvoicetype_e i) { return strbuf_get(voicenames, skinvoices[i].info.nameofs); } @@ -139,14 +139,14 @@ kartitemtype_e DEH_FindKartItem(const char *word) return MAXKARTITEMS; } -kartvoicetype_e DEH_FindVoice(const char *word) +kartvoicetype_e DEH_FindKartVoice(const char *word) { INT32 i; UINT32 hash = HASH32(word, strlen(word)); for (i = 0; i < numskinvoices; i++) { if (hash != skinvoices[i].info.namehash) continue; - if (fastcmp(word, DEH_VoiceName(i))) + if (fastcmp(word, DEH_KartVoiceName(i))) return (kartvoicetype_e)i; } return MAXSKINVOICES; diff --git a/src/deh_tables.h b/src/deh_tables.h index 0db150418..d5ca7bf64 100644 --- a/src/deh_tables.h +++ b/src/deh_tables.h @@ -40,14 +40,14 @@ const char *DEH_StateName(statenum_t i); const char *DEH_SkincolorName(skincolornum_t i); const char *DEH_MenutypeName(menutype_t i); const char *DEH_KartItemName(kartitemtype_e i); -const char *DEH_VoiceName(kartvoicetype_e i); +const char *DEH_KartVoiceName(kartvoicetype_e i); mobjtype_t DEH_FindMobjtype(const char *word); statenum_t DEH_FindState(const char *word); skincolornum_t DEH_FindSkincolor(const char *word); menutype_t DEH_FindMenutype(const char *word); kartitemtype_e DEH_FindKartItem(const char *word); -kartvoicetype_e DEH_FindVoice(const char *word); +kartvoicetype_e DEH_FindKartVoice(const char *word); struct flickytypes_s { const char *name; diff --git a/src/g_demo.c b/src/g_demo.c index 9fba3164a..b94142a8f 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -1396,7 +1396,7 @@ void G_WriteDemoExtraData(void) WRITESTRINGL(demobuf.p, skins[players[i].skin].name, 16+1); // Voice - WRITESTRINGL(demobuf.p, DEH_VoiceName(players[i].voice_id), 32+1); + WRITESTRINGL(demobuf.p, DEH_KartVoiceName(players[i].voice_id), 32+1); // Stats WRITEUINT8(demobuf.p, skins[players[i].skin].kartspeed); @@ -2983,7 +2983,7 @@ void G_BeginRecording(void) WRITESTRINGL(demobuf.p, skincolors[player->skincolor].name, 16+1); // Voice - WRITESTRINGL(demobuf.p, DEH_VoiceName(player->voice_id), 32+1); + WRITESTRINGL(demobuf.p, DEH_KartVoiceName(player->voice_id), 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! diff --git a/src/hardware/hw3sound.c b/src/hardware/hw3sound.c index 05de41c5d..a958c62e0 100644 --- a/src/hardware/hw3sound.c +++ b/src/hardware/hw3sound.c @@ -367,7 +367,7 @@ INT32 HW3S_I_StartSound(const void *origin_p, source3D_data_t *source_parm, chan if (voice) { - sfx_id = sfx_id = P_GetVoiceSFX(voice, sfx->skinsound); + sfx_id = sfx_id = P_GetKartVoiceSFX(voice, sfx->skinsound); sfx = &S_sfx[sfx_id]; } } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 0e7f2006a..96280901d 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -27,7 +27,7 @@ #include "y_inter.h" #include "hu_stuff.h" // HU_AddChatText #include "console.h" -#include "deh_tables.h" // DEH_FindVoice +#include "deh_tables.h" // DEH_FindKartVoice #include "k_kart.h" // SRB2Kart #include "k_battle.h" #include "k_boss.h" @@ -251,6 +251,7 @@ static const struct { {META_MATRIX, "matrix_t"}, {META_QUATERNION, "quaternion_t"}, {META_VOICE, "kartvoice_t"}, + {META_VOICE_ARRAY, "kartvoice_t.array"}, {NULL, NULL} }; @@ -2619,7 +2620,7 @@ static int lib_rGetNameByColor(lua_State *L) // Lua-exclusive; allocates a voice into memory, and returns a pointer to said voice. // Mangled clone of deh_soc's get_kartvoice, modified for Lua. -static int lib_rAddVoice(lua_State *L) +static int lib_rAddKartVoice(lua_State *L) { const char* word = luaL_checkstring(L, 1); NOHUD @@ -2629,11 +2630,11 @@ static int lib_rAddVoice(lua_State *L) strncpy(wordupper, word, VOICENAMESIZE); strupr(wordupper); - INT32 i = DEH_FindVoice(wordupper); + INT32 i = DEH_FindKartVoice(wordupper); if (i == MAXSKINVOICES) { // Freeslot voices that don't exist yet. - const INT32 allocresult = R_AllocVoice(false, word, &i); + const INT32 allocresult = R_AllocKartVoice(false, word, &i); if (allocresult < 0) return luaL_error(L, "kartvoice_t '%s' could not be allocated.", word); @@ -5396,7 +5397,7 @@ static luaL_Reg lib[] = { {"R_GetNameByColor", lib_rGetNameByColor}, // r_skins (voice) - {"R_AddVoice", lib_rAddVoice}, + {"R_AddKartVoice", lib_rAddKartVoice}, // s_sound {"S_StartSound",lib_sStartSound}, diff --git a/src/lua_libs.h b/src/lua_libs.h index aef4e921f..1b690f191 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -123,6 +123,7 @@ extern lua_State *gL; #define META_QUATERNION "QUATERNION_T" #define META_VOICE "KARTVOICE_T*" +#define META_VOICE_ARRAY "KARTVOICE_T*ARRAY" boolean luaL_checkboolean(lua_State *L, int narg); diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index c526da23e..e6c56fd59 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -255,7 +255,7 @@ static int soundsid_get(lua_State *L) if (i >= NUMSKINSOUNDS) return luaL_error(L, "%s cannot be %s", LUA_QL("skinsound_t"), va("%u", (UINT32)i)); - lua_pushinteger(L, P_GetVoiceSFX(s->voice, i)); + lua_pushinteger(L, P_GetKartVoiceSFX(s->voice, i)); return 1; } diff --git a/src/r_skins.c b/src/r_skins.c index 2957c9f5b..37da62192 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -238,7 +238,7 @@ void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound) } } -sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound) +sfxenum_t P_GetKartVoiceSFX(kartvoice_t *voice, INT32 skinsound) { INT32 usesound; @@ -257,7 +257,7 @@ sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound) } else { - I_Error("P_GetVoiceSFX: Attempted to retrieve a pain sound of ID %d.\n", usesound); + I_Error("P_GetKartVoiceSFX: Attempted to retrieve a pain sound of ID %d.\n", usesound); } break; @@ -270,7 +270,7 @@ sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound) } else { - I_Error("P_GetVoiceSFX: Attempted to retrieve an attack sound of ID %d.\n", usesound); + I_Error("P_GetKartVoiceSFX: Attempted to retrieve an attack sound of ID %d.\n", usesound); } break; case SKSKBST1: // Boost item taunt @@ -282,7 +282,7 @@ sfxenum_t P_GetVoiceSFX(kartvoice_t *voice, INT32 skinsound) } else { - I_Error("P_GetVoiceSFX: Attempted to retrieve a boost sound of ID %d.\n", usesound); + I_Error("P_GetKartVoiceSFX: Attempted to retrieve a boost sound of ID %d.\n", usesound); } break; case SKSKSLOW: // Overtake taunt @@ -727,7 +727,7 @@ kartvoicetype_e R_FindIDForVoice(const char* voicename) for (i = 0; i < numskinvoices; i++) { // Whatever. Go, my code duplication! - checkvoicename = DEH_VoiceName(i); + checkvoicename = DEH_KartVoiceName(i); if (strnicmp(voicename, checkvoicename, VOICENAMESIZE) == 0) { // Found a voice. @@ -785,7 +785,7 @@ void SetPlayerVoice(INT32 playernum, const char* voicename) const kartvoice_t* myvoice = P_GetMobjVoice(player->mo); - const char* myvoicename = DEH_VoiceName(myvoice->id); + const char* myvoicename = DEH_KartVoiceName(myvoice->id); if (!strnicmp(voicename, myvoicename, VOICENAMESIZE)) { // Hey... this voice is the same as before! @@ -1084,7 +1084,7 @@ static void R_IHateThatHedgehog(UINT16 wadnum) // Add voice INT32 dummy; - if (R_AllocVoice(true, skin->name, &dummy) < 0) + if (R_AllocKartVoice(true, skin->name, &dummy) < 0) { // Hey guy, take care! I_Error("Failed to allocate initial skin voice\n"); @@ -1269,14 +1269,14 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) // Allocates a voice onto the dehacked list, and iterates the provided output pointer // (should it exist). // -INT32 R_AllocVoice(boolean skinalloc, const char* name, INT32 *out) +INT32 R_AllocKartVoice(boolean skinalloc, const char* name, INT32 *out) { char voxname_upper[VOICENAMESIZE]; strncpy(voxname_upper, name, VOICENAMESIZE); strupr(voxname_upper); - *out = DEH_FindVoice(voxname_upper); + *out = DEH_FindKartVoice(voxname_upper); if (*out != MAXSKINVOICES) return skinalloc ? -1 : 0; // already allocated @@ -1409,7 +1409,7 @@ void R_AddSkins(UINT16 wadnum) // Add voice INT32 dummy; - if (R_AllocVoice(true, skin->name, &dummy) < 0) + if (R_AllocKartVoice(true, skin->name, &dummy) < 0) { // We couldn't allocate our own skin's voice?! I_Error("Failed to allocate voice for skin %s\n", skin->name); diff --git a/src/r_skins.h b/src/r_skins.h index 25861b190..5dacf2b7c 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -28,6 +28,7 @@ extern "C" { /// Defaults #define SKINNAMESIZE 16 #define VOICENAMESIZE 32 +#define VOICEARRAYSIZE 2 #define SKINRIVALS 3 // should be all lowercase!! S_SKIN processing does a strlwr #define DEFAULTSKIN "sonic" @@ -60,9 +61,9 @@ struct kartvoice_t sfxenum_t win; sfxenum_t lose; - sfxenum_t pain[2]; - sfxenum_t attack[2]; - sfxenum_t boost[2]; + sfxenum_t pain[VOICEARRAYSIZE]; + sfxenum_t attack[VOICEARRAYSIZE]; + sfxenum_t boost[VOICEARRAYSIZE]; sfxenum_t overtake; sfxenum_t hitem; sfxenum_t power; @@ -154,14 +155,14 @@ 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); -INT32 R_AllocVoice(boolean skinalloc, const char* name, INT32 *out); +INT32 R_AllocKartVoice(boolean skinalloc, const char* name, INT32 *out); 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); +sfxenum_t P_GetKartVoiceSFX(kartvoice_t *voice, INT32 skinsound); #ifdef __cplusplus } // extern "C" diff --git a/src/s_sound.c b/src/s_sound.c index efea4a744..1d8879fed 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -617,7 +617,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) if (cur_voice) { - sfx_id = P_GetVoiceSFX(cur_voice, sfx->skinsound); + sfx_id = P_GetKartVoiceSFX(cur_voice, sfx->skinsound); sfx = &S_sfx[sfx_id]; } } From dd88549968eed16573667158a1bed3c477270a08 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 13:44:23 -0500 Subject: [PATCH 22/44] Refactor voice finding to not rely on get_kartvoice --- src/dehacked.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 6f70f2ded..bac6047c9 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -789,13 +789,26 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) else if (fastncmp(word, "KARTVOICE", 9)) { if (i == 0 && word2[0] != '0') // If word2 isn't a number - i = get_kartvoice(word2); // find a voice by name - if ((i + 1) >= 1 && i < numskinvoices) + i = DEH_FindKartVoice(word2); // find a voice by name + + if (i == MAXSKINVOICES) + { + // Found nothing; auto-allocate our voice. + const INT32 allocresult = R_AllocKartVoice(false, word, &i); + + if (allocresult < 0) + { + deh_warning("Voice '%s' could not be allocated.", word); + ignorelines(f); + } + } + + if (i >= 0 && i < numskinvoices) readkartvoice(f, &skinvoices[i]); else { // zero-based, but let's start at 1 - deh_warning("KartVoice number %d out of range (1 - %d)", i, numskinvoices); + deh_warning("KartVoice number %d out of range (0 - %d)", i, numskinvoices); ignorelines(f); } } From 46c7ae33eea58009bb194d13c0944b5678bb7b67 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 13:45:08 -0500 Subject: [PATCH 23/44] Introduce voice arrays Better than relying on pushing and pulling a bunch of fucking Lua table structs; what am I doing here? --- src/lua_voicelib.c | 171 +++++++++++++++------------------------------ 1 file changed, 55 insertions(+), 116 deletions(-) diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index 0d6a186b4..5a399b2e8 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -54,8 +54,11 @@ static const char *const voicevars_opt[] = { }; #define RNOFIELD luaL_error(L, LUA_QL("kartvoice_t") " has no field named " LUA_QS, field) + #define RNOSET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be set.", field) #define RNOGET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be get.", field) +#define NOSET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " should not be set directly.", field) + #define UNIMPLEMENTED luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", voicevars_opt[field]) static int voice_get(lua_State* L) @@ -71,7 +74,7 @@ static int voice_get(lua_State* L) case voicevars_id: return RNOGET; case voicevars_name: - lua_pushstring(L, DEH_VoiceName((kartvoicetype_e)(voice - skinvoices))); + lua_pushstring(L, DEH_KartVoiceName((kartvoicetype_e)(voice - skinvoices))); break; case voicevars_parent: LUA_PushUserdata(L, &skins[voice->parent], META_SKIN); @@ -83,35 +86,13 @@ static int voice_get(lua_State* L) LUA_PushUserdata(L, &S_sfx[voice->lose], META_SFXINFO); break; case voicevars_pain: - // Wasteful and shitty, I'll wager, but it's better than risking segfaults - const size_t painarraysize = (sizeof voice->pain) / (sizeof voice->pain[0]); - - lua_createtable(L, painarraysize, 0); - for (size_t i = 0; i < painarraysize; i++) - { - LUA_PushUserdata(L, &S_sfx[voice->pain[i]], META_SFXINFO); - lua_rawseti(L, -2, 1 + i); - } + LUA_PushUserdata(L, voice->pain, META_VOICE_ARRAY); break; case voicevars_attack: - const size_t atkarraysize = (sizeof voice->attack) / (sizeof voice->attack[0]); - - lua_createtable(L, atkarraysize, 0); - for (size_t i = 0; i < atkarraysize; i++) - { - LUA_PushUserdata(L, &S_sfx[voice->attack[i]], META_SFXINFO); - lua_rawseti(L, -2, 1 + i); - } + LUA_PushUserdata(L, voice->attack, META_VOICE_ARRAY); break; case voicevars_boost: - const size_t bstarraysize = (sizeof voice->boost) / (sizeof voice->boost[0]); - - lua_createtable(L, bstarraysize, 0); - for (size_t i = 0; i < bstarraysize; i++) - { - LUA_PushUserdata(L, &S_sfx[voice->boost[i]], META_SFXINFO); - lua_rawseti(L, -2, 1 + i); - } + LUA_PushUserdata(L, voice->boost, META_VOICE_ARRAY); break; case voicevars_overtake: LUA_PushUserdata(L, &S_sfx[voice->overtake], META_SFXINFO); @@ -175,98 +156,11 @@ static int voice_set(lua_State* L) voice->lose = (sfxenum_t)luaL_checkinteger(L, 3); break; case voicevars_pain: - luaL_checktype(L, 3, LUA_TTABLE); - - // Pop the other values; they're no longer relevant. - lua_remove(L, 1); - lua_remove(L, 1); - - lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. - - lua_pushnil(L); - while (lua_next(L, 1)) { - i = 0; - - if (!lua_isnoneornil(L, 2)) - i = luaL_checkinteger(L, 2); - - if (i > 0 && i <= (sizeof(voice->pain) / sizeof(voice->pain[0]))) - { - // Only ever accept the first two values. - if (!lua_isnoneornil(L, 3)) - { - // Safely ignore nil values, so modders can do voice.pain = {nil, sfx_mysound} - k = luaL_checkinteger(L, 3); - - voice->pain[i - 1] = (sfxenum_t)k; - } - } - - lua_pop(L, 1); - }; - break; + return NOSET; case voicevars_attack: - luaL_checktype(L, 3, LUA_TTABLE); - - // Pop the other values; they're no longer relevant. - lua_remove(L, 1); - lua_remove(L, 1); - - lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. - - lua_pushnil(L); - while (lua_next(L, 1)) { - i = 0; - - if (!lua_isnoneornil(L, 2)) - i = luaL_checkinteger(L, 2); - - if (i > 0 && i <= (sizeof(voice->attack) / sizeof(voice->attack[0]))) - { - // Only ever accept the first two values. - if (!lua_isnoneornil(L, 3)) - { - // Safely ignore nil values, so modders can do voice.pain = {nil, sfx_mysound} - k = luaL_checkinteger(L, 3); - - voice->attack[i - 1] = (sfxenum_t)k; - } - } - - lua_pop(L, 1); - }; - break; + return NOSET; case voicevars_boost: - luaL_checktype(L, 3, LUA_TTABLE); - - // Pop the other values; they're no longer relevant. - lua_remove(L, 1); - lua_remove(L, 1); - - lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. - - lua_pushnil(L); - while (lua_next(L, 1)) { - i = 0; - - if (!lua_isnoneornil(L, 2)) - i = luaL_checkinteger(L, 2); - - if (i > 0 && i <= (sizeof(voice->boost) / sizeof(voice->boost[0]))) - { - // Only ever accept the first two values. - if (!lua_isnoneornil(L, 3)) - { - // Safely ignore nil values. - k = luaL_checkinteger(L, 3); - - voice->boost[i - 1] = (sfxenum_t)k; - } - } - - lua_pop(L, 1); - }; - break; + return NOSET; case voicevars_overtake: voice->overtake = (sfxenum_t)luaL_checkinteger(L, 3); break; @@ -298,6 +192,39 @@ static int voice_num(lua_State* L) return 1; } +// voice_array, i -> voice.array[i] +static int voice_array_get(lua_State *L) +{ + sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY)); + INT32 i = luaL_checkinteger(L, 2); + + if (i < 0 || i >= VOICEARRAYSIZE) + return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE); + + LUA_PushUserdata(L, &S_sfx[voice_array[i]], META_SFXINFO); + return 1; +} + +static int voice_array_set(lua_State *L) +{ + sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY)); + INT32 i = luaL_checkinteger(L, 2); + + if (i < 0 || i >= VOICEARRAYSIZE) + return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE); + + voice_array[i] = (sfxenum_t)luaL_checkinteger(L, 3); + return 0; +} + +// #voice.array -> VOICEARRAYSIZE +static int voice_array_num(lua_State *L) +{ + lua_pushinteger(L, VOICEARRAYSIZE); + return 1; +} + + static int lib_iterateVoices(lua_State* L) { INT32 i; @@ -392,6 +319,18 @@ int LUA_VoiceLib(lua_State* L) lua_setfield(L, -2, "__len"); lua_pop(L, 1); + luaL_newmetatable(L, META_VOICE_ARRAY); + lua_pushcfunction(L, voice_array_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, voice_array_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, voice_array_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + lua_newuserdata(L, 0); lua_createtable(L, 0, 2); lua_pushcfunction(L, lib_getVoice); From 75de5ed71ad8c5c88e850bc81f5b46eaa2be2f52 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 14:41:35 -0500 Subject: [PATCH 24/44] Fix cvar misnaming bug --- src/d_netcmd.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 58f1afc61..71224d873 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1989,8 +1989,14 @@ static void SendNameAndColor(UINT8 n) } else { - // Try getting the skin's voice. - valuevoice = skins[player->skin].voice; + // Try getting the voice from our ID. + valuevoice = &skinvoices[player->voice_id]; + + if (valuevoice->parent != skins[player->skin].voice->parent) + { + // Parent mismatch; use the skin's voice. + valuevoice = skins[player->skin].voice; + } if (valuevoice) { @@ -2082,8 +2088,14 @@ static void SendNameAndColor(UINT8 n) } else { - // Try getting the skin's voice. - valuevoice = skins[cv_skin[n].value].voice; + // Try getting the voice from our ID. + valuevoice = &skinvoices[player->voice_id]; + + if (valuevoice->parent != skins[cv_skin[n].value].voice->parent) + { + // Parent mismatch; use the skin's voice. + valuevoice = skins[cv_skin[n].value].voice; + } if (valuevoice) { From f48317a04595a9ba8c17e7c80f79a932ecd490a3 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 14:42:41 -0500 Subject: [PATCH 25/44] Fix up dehacked, adjust voice allocation printouts --- src/dehacked.c | 4 ++-- src/r_skins.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index bac6047c9..4a7b8959f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -794,11 +794,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) if (i == MAXSKINVOICES) { // Found nothing; auto-allocate our voice. - const INT32 allocresult = R_AllocKartVoice(false, word, &i); + const INT32 allocresult = R_AllocKartVoice(false, word2, &i); if (allocresult < 0) { - deh_warning("Voice '%s' could not be allocated.", word); + deh_warning("Voice '%s' could not be allocated.", word2); ignorelines(f); } } diff --git a/src/r_skins.c b/src/r_skins.c index 37da62192..fd68a67ed 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -1285,7 +1285,7 @@ INT32 R_AllocKartVoice(boolean skinalloc, const char* name, INT32 *out) return -1; } - CONS_Printf("Voice KVOICE_%s allocated.\n",voxname_upper); + CONS_Printf("Voice %s allocated.\n",voxname_upper); DEH_Link(voxname_upper, &skinvoices[numskinvoices].info, &voicenames); *out = numskinvoices++; From f47e123c8b6687c159698f677dae18423a92502f Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 16 Nov 2025 23:32:19 +0100 Subject: [PATCH 26/44] Declaration following a label is a C23 extension No newline at EOF is UB --- src/lua_voicelib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index 5a399b2e8..cea205069 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -135,6 +135,7 @@ static int voice_set(lua_State* L) case voicevars_name: return RNOSET; case voicevars_parent: + { // It would make no sense not to accept a skin struct here. skin_t* my_new_parent = *((skin_t**)luaL_checkudata(L, 3, META_SKIN)); @@ -147,6 +148,7 @@ static int voice_set(lua_State* L) voice->parent = (UINT16)(my_new_parent - skins); } break; + } case voicevars_win: // For the actual sound values, you pass the actual sfxenum_t enums. // Yes, I know what I said about skin structs literally just above. @@ -342,4 +344,4 @@ int LUA_VoiceLib(lua_State* L) lua_setglobal(L, "skinvoices"); return 0; -} \ No newline at end of file +} From 3f23e3b00dcf715a3ca67a8c4e933d248dcc159c Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 16 Nov 2025 21:05:04 -0500 Subject: [PATCH 27/44] Use srtlcpy for voice name conversions --- src/d_netcmd.c | 38 +++++++++++++++++++------------------- src/lua_baselib.c | 2 +- src/r_skins.c | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 71224d873..434a1f3e5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1426,7 +1426,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi } // Scan through our allocated skin voices for the wanted voice. - strncpy(cvarname, value, VOICENAMESIZE); + strlcpy(cvarname, value, sizeof(cvarname)); strupr(cvarname); i = DEH_FindKartVoice(cvarname); @@ -1437,7 +1437,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi if (skinvoices[i].parent == skin_id || forceme) { // Change the cvar to be the value of the name. - strncpy(cvarname, DEH_KartVoiceName(i), VOICENAMESIZE); + strlcpy(cvarname, DEH_KartVoiceName(i), sizeof(cvarname)); strlwr(cvarname); CV_StealthSet(cvar, cvarname); @@ -1450,7 +1450,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" doesn't belong to this skin (%s).\n"), DEH_KartVoiceName(i), skins[skin_id].name); // *Reassign* the voice cvar to whatever the current voice is, at least. - strncpy(cvarname, DEH_KartVoiceName(myvoice->id), VOICENAMESIZE); + strlcpy(cvarname, DEH_KartVoiceName(myvoice->id), sizeof(cvarname)); strlwr(cvarname); CV_StealthSet(cvar, cvarname); @@ -1469,7 +1469,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist.\n"), value); // Reassign the voice cvar to our current voice. - strncpy(cvarname, DEH_KartVoiceName(myvoice->id), VOICENAMESIZE); + strlcpy(cvarname, DEH_KartVoiceName(myvoice->id), sizeof(cvarname)); strlwr(cvarname); CV_StealthSet(cvar, cvarname); @@ -1939,7 +1939,7 @@ static void SendNameAndColor(UINT8 n) // Reset the voice to that of the skin's. if (prevskin != player->skin && skins[player->skin].voice) { - strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -1958,7 +1958,7 @@ static void SendNameAndColor(UINT8 n) // Reset the voice to that of the skin's. if (prevskin != player->skin && skins[player->skin].voice) { - strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -1969,7 +1969,7 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) { - strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -1981,7 +1981,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strncpy(voicebuf, DEH_KartVoiceName(valuevoice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2000,7 +2000,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strncpy(voicebuf, DEH_KartVoiceName(valuevoice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2009,7 +2009,7 @@ static void SendNameAndColor(UINT8 n) else { // ...still nothing? - strncpy(voicebuf, DEH_KartVoiceName(0), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(0), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2041,7 +2041,7 @@ static void SendNameAndColor(UINT8 n) if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, false, true)) { // Our voice is no longer valid, set it to that of our skin's. - strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2064,7 +2064,7 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) { - strncpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2080,7 +2080,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice && valuevoice->parent == cv_skin[n].value) { - strncpy(voicebuf, DEH_KartVoiceName(valuevoice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2099,7 +2099,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strncpy(voicebuf, DEH_KartVoiceName(valuevoice->id), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2108,7 +2108,7 @@ static void SendNameAndColor(UINT8 n) else { // ...still nothing? - strncpy(voicebuf, DEH_KartVoiceName(0), VOICENAMESIZE); + strlcpy(voicebuf, DEH_KartVoiceName(0), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2246,7 +2246,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name); - strncpy(voicename, DEH_KartVoiceName(skins[forcedskin].voice->id), VOICENAMESIZE); + strlcpy(voicename, DEH_KartVoiceName(skins[forcedskin].voice->id), sizeof(voicename)); strlwr(voicename); CV_StealthSet(&cv_voice[localplayer], voicename); @@ -2265,7 +2265,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (localplayer != -1) { - strncpy(voicename, DEH_KartVoiceName(voice), VOICENAMESIZE); + strlcpy(voicename, DEH_KartVoiceName(voice), sizeof(voicename)); strlwr(voicename); CV_StealthSet(&cv_voice[localplayer], voicename); @@ -7540,7 +7540,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) if (!(cht_debug || devparm) && !(multiplayer || netgame) // In single player. && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y { - strncpy(cvarname, DEH_KartVoiceName(myvoice->id), VOICENAMESIZE); + strlcpy(cvarname, DEH_KartVoiceName(myvoice->id), sizeof(cvarname)); strlwr(cvarname); CV_StealthSet(&cv_voice[pnum], cvarname); @@ -7555,7 +7555,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your voice at the moment.\n")); - strncpy(cvarname, DEH_KartVoiceName(myvoice->id), VOICENAMESIZE); + strlcpy(cvarname, DEH_KartVoiceName(myvoice->id), sizeof(cvarname)); strlwr(cvarname); CV_StealthSet(&cv_voice[pnum], cvarname); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 96280901d..261609105 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2627,7 +2627,7 @@ static int lib_rAddKartVoice(lua_State *L) char wordupper[VOICENAMESIZE+1]; - strncpy(wordupper, word, VOICENAMESIZE); + strlcpy(wordupper, word, sizeof(wordupper)); strupr(wordupper); INT32 i = DEH_FindKartVoice(wordupper); diff --git a/src/r_skins.c b/src/r_skins.c index fd68a67ed..056a60d6d 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -1273,7 +1273,7 @@ INT32 R_AllocKartVoice(boolean skinalloc, const char* name, INT32 *out) { char voxname_upper[VOICENAMESIZE]; - strncpy(voxname_upper, name, VOICENAMESIZE); + strlcpy(voxname_upper, name, sizeof(voxname_upper)); strupr(voxname_upper); *out = DEH_FindKartVoice(voxname_upper); From 7efa1c04f4a224103a6af8691b81cf1b51303d41 Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 27 Nov 2025 03:21:31 -0500 Subject: [PATCH 28/44] Fix dubs saving for only Sonic Voice changing now does a two-pass check to make sure the given dub is both a thing and can be used by the skin Prevents the annoying edgecase of Sonic and ONLY Sonic keeping dubs through games --- src/d_netcmd.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 434a1f3e5..24e09a529 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1909,6 +1909,7 @@ static void SendNameAndColor(UINT8 n) if (!netgame) { INT32 foundskin; + kartvoicetype_e vid; CleanupPlayerName(playernum, cv_playername[n].zstring); strcpy(player_names[playernum], cv_playername[n].zstring); @@ -1936,14 +1937,50 @@ static void SendNameAndColor(UINT8 n) SetPlayerSkin(playernum, cv_skin[n].string); CV_StealthSet(&cv_skin[n], skins[cv_skin[n].value].name); - // Reset the voice to that of the skin's. + // Reset the voice. if (prevskin != player->skin && skins[player->skin].voice) { - strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); + // Try getting the voice from our ID. + valuevoice = &skinvoices[player->voice_id]; + + if (valuevoice->parent != skins[player->skin].voice->parent) + { + // Parent mismatch 1; let's try the cvar. + + if (cv_voice[n].string) + { + vid = R_FindIDForVoice(cv_voice[n].string); + + if (vid == MAXSKINVOICES) + { + // No dice; use the skin. + valuevoice = skins[player->skin].voice; + } + else + { + if (skinvoices[vid].parent == skins[player->skin].voice->parent) + { + valuevoice = &skinvoices[vid]; + } + else + { + // Parent mismatch... again. + valuevoice = skins[player->skin].voice; + } + } + } + else + { + // NULL string; default to the skin. + valuevoice = skins[player->skin].voice; + } + } + + strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = (UINT16)skins[player->skin].voice->id; + cv_voice[n].value = (UINT16)valuevoice->id; } } else @@ -1955,14 +1992,50 @@ static void SendNameAndColor(UINT8 n) // will always be same as current SetPlayerSkin(playernum, cv_skin[n].string); - // Reset the voice to that of the skin's. + // Reset the voice. if (prevskin != player->skin && skins[player->skin].voice) { - strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); + // Try getting the voice from our ID. + valuevoice = &skinvoices[player->voice_id]; + + if (valuevoice->parent != skins[player->skin].voice->parent) + { + // Parent mismatch 1; let's try the cvar. + + if (cv_voice[n].string) + { + vid = R_FindIDForVoice(cv_voice[n].string); + + if (vid == MAXSKINVOICES) + { + // No dice; use the skin. + valuevoice = skins[player->skin].voice; + } + else + { + if (skinvoices[vid].parent == skins[player->skin].voice->parent) + { + valuevoice = &skinvoices[vid]; + } + else + { + // Parent mismatch... again. + valuevoice = skins[player->skin].voice; + } + } + } + else + { + // NULL string; default to the skin. + valuevoice = skins[player->skin].voice; + } + } + + strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = (UINT16)skins[player->skin].voice->id; + cv_voice[n].value = (UINT16)valuevoice->id; } } From 51a005a7dec4c076ea0dc582d143ca5e8c275ea7 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 29 Nov 2025 20:51:25 -0500 Subject: [PATCH 29/44] Refactor voices * Tie all voices to skins, allocate voices during skin allocation and patching * Bring back voice.name, kill voice.parent, add voice.id * Lua compatibility for voices (again) soon (hopefully) --- src/d_netcmd.c | 194 ++++++++------------- src/deh_soc.c | 18 +- src/deh_soc.h | 2 - src/deh_tables.c | 20 +-- src/deh_tables.h | 4 +- src/dehacked.c | 4 +- src/f_finale.c | 10 +- src/g_demo.c | 25 +-- src/g_game.c | 2 +- src/info.c | 15 -- src/lua_baselib.c | 35 ---- src/lua_skinlib.c | 8 +- src/lua_voicelib.c | 117 +------------ src/p_mobj.c | 20 +-- src/p_saveg.c | 13 +- src/p_user.c | 2 +- src/r_skins.c | 414 +++++++++++++++++++++++++++++++++++++++------ src/r_skins.h | 30 ++-- 18 files changed, 490 insertions(+), 443 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9791e1ed8..07d8ff576 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1424,7 +1424,7 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi if (!myvoice) { - myvoice = skins[skin_id].voice; + myvoice = &skins[skin_id].voices[0]; if (!myvoice) { @@ -1437,36 +1437,19 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi strlcpy(cvarname, value, sizeof(cvarname)); strupr(cvarname); - i = DEH_FindKartVoice(cvarname); + i = R_FindIDForVoice(&skins[skin_id], cvarname); if (i != MAXSKINVOICES) { - // 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. - strlcpy(cvarname, DEH_KartVoiceName(i), sizeof(cvarname)); - strlwr(cvarname); + // Voices are tied to skins; if, SOMEHOW, this voice has a *different* parent, + // something has gone terribly wrong. - CV_StealthSet(cvar, cvarname); - cvar->value = skinvoices[i].id; - } - else - { - // This isn't a valid voice for this skin, so don't assign shit. - if (!silent) - CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" doesn't belong to this skin (%s).\n"), DEH_KartVoiceName(i), skins[skin_id].name); + // Change the cvar to be the value of the name. + strlcpy(cvarname, skins[skin_id].voices[i].name, sizeof(cvarname)); + strlwr(cvarname); - // *Reassign* the voice cvar to whatever the current voice is, at least. - strlcpy(cvarname, DEH_KartVoiceName(myvoice->id), sizeof(cvarname)); - strlwr(cvarname); - - CV_StealthSet(cvar, cvarname); - cvar->value = myvoice->id; - - // Signal to networking that we failed to set a voice. - return 1; - } + CV_StealthSet(cvar, cvarname); + cvar->value = skins[skin_id].voices[i].id; // We've found the voice we were looking for! return 0; @@ -1474,10 +1457,10 @@ static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voi // Found diddly-squat, say as much. if (!silent) - CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist.\n"), value); + CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist for this skin.\n"), value); // Reassign the voice cvar to our current voice. - strlcpy(cvarname, DEH_KartVoiceName(myvoice->id), sizeof(cvarname)); + strlcpy(cvarname, myvoice->name, sizeof(cvarname)); strlwr(cvarname); CV_StealthSet(cvar, cvarname); @@ -1890,7 +1873,7 @@ static void SendNameAndColor(UINT8 n) if (!voice) { - voice = skins[player->skin].voice; + voice = &skins[player->skin].voices[0]; if (!voice) { @@ -1904,7 +1887,7 @@ static void SendNameAndColor(UINT8 n) && !strcmp(cv_skin[n].string, skins[player->skin].name) && cv_follower[n].value == player->followerskin && cv_followercolor[n].value == player->followercolor - && (!permamute && !stricmp(cv_voice[n].string, DEH_KartVoiceName(voice->id)))) + && (!permamute && !stricmp(cv_voice[n].string, voice->name))) return; player->availabilities = R_GetSkinAvailabilities(); @@ -1916,8 +1899,7 @@ static void SendNameAndColor(UINT8 n) // If you're not in a netgame, merely update the skin, color, and name. if (!netgame) { - INT32 foundskin; - kartvoicetype_e vid; + INT32 foundskin, vid; CleanupPlayerName(playernum, cv_playername[n].zstring); strcpy(player_names[playernum], cv_playername[n].zstring); @@ -1939,52 +1921,37 @@ static void SendNameAndColor(UINT8 n) } else if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin)) { - prevskin = player->skin; + prevskin = skins[cv_skin[n].value].name; cv_skin[n].value = foundskin; SetPlayerSkin(playernum, cv_skin[n].string); CV_StealthSet(&cv_skin[n], skins[cv_skin[n].value].name); // Reset the voice. - if (prevskin != player->skin && skins[player->skin].voice) + if (prevskin != player->skin) { - // Try getting the voice from our ID. - valuevoice = &skinvoices[player->voice_id]; - - if (valuevoice->parent != skins[player->skin].voice->parent) + if (cv_voice[n].string) { - // Parent mismatch 1; let's try the cvar. + vid = R_FindIDForVoice(&skins[player->skin], cv_voice[n].string); - if (cv_voice[n].string) + if (vid == MAXSKINVOICES) { - vid = R_FindIDForVoice(cv_voice[n].string); - - if (vid == MAXSKINVOICES) - { - // No dice; use the skin. - valuevoice = skins[player->skin].voice; - } - else - { - if (skinvoices[vid].parent == skins[player->skin].voice->parent) - { - valuevoice = &skinvoices[vid]; - } - else - { - // Parent mismatch... again. - valuevoice = skins[player->skin].voice; - } - } + // No dice; use the default voice. + valuevoice = &skins[player->skin].voices[0]; } else { - // NULL string; default to the skin. - valuevoice = skins[player->skin].voice; + + valuevoice = &skins[player->skin].voices[vid]; } } + else + { + // NULL string; default to the skin's default. + valuevoice = &skins[player->skin].voices[0]; + } - strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); + strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2001,45 +1968,30 @@ static void SendNameAndColor(UINT8 n) SetPlayerSkin(playernum, cv_skin[n].string); // Reset the voice. - if (prevskin != player->skin && skins[player->skin].voice) + if (prevskin != player->skin) { - // Try getting the voice from our ID. - valuevoice = &skinvoices[player->voice_id]; - - if (valuevoice->parent != skins[player->skin].voice->parent) + if (cv_voice[n].string) { - // Parent mismatch 1; let's try the cvar. + vid = R_FindIDForVoice(&skins[player->skin], cv_voice[n].string); - if (cv_voice[n].string) + if (vid == MAXSKINVOICES) { - vid = R_FindIDForVoice(cv_voice[n].string); - - if (vid == MAXSKINVOICES) - { - // No dice; use the skin. - valuevoice = skins[player->skin].voice; - } - else - { - if (skinvoices[vid].parent == skins[player->skin].voice->parent) - { - valuevoice = &skinvoices[vid]; - } - else - { - // Parent mismatch... again. - valuevoice = skins[player->skin].voice; - } - } + // No dice; use the default voice. + valuevoice = &skins[player->skin].voices[0]; } else { - // NULL string; default to the skin. - valuevoice = skins[player->skin].voice; + + valuevoice = &skins[player->skin].voices[vid]; } } + else + { + // NULL string; default to the skin's default. + valuevoice = &skins[player->skin].voices[0]; + } - strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); + strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2050,7 +2002,7 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) { - strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); + strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2062,7 +2014,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); + strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2070,18 +2022,13 @@ static void SendNameAndColor(UINT8 n) } else { - // Try getting the voice from our ID. - valuevoice = &skinvoices[player->voice_id]; - - if (valuevoice->parent != skins[player->skin].voice->parent) - { - // Parent mismatch; use the skin's voice. - valuevoice = skins[player->skin].voice; - } + // Get the voice from our ID. + // No need to compare parents, + valuevoice = &skins[player->skin].voices[player->voice_id]; if (valuevoice) { - strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); + strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2090,7 +2037,7 @@ static void SendNameAndColor(UINT8 n) else { // ...still nothing? - strlcpy(voicebuf, DEH_KartVoiceName(0), sizeof(voicebuf)); + strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2122,11 +2069,11 @@ static void SendNameAndColor(UINT8 n) if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, false, true)) { // Our voice is no longer valid, set it to that of our skin's. - strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); + strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = skins[player->skin].voice->id; + cv_voice[n].value = skins[player->skin].voices[0].id; } } @@ -2145,7 +2092,7 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) { - strlcpy(voicebuf, DEH_KartVoiceName(skins[player->skin].voice->id), sizeof(voicebuf)); + strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2159,9 +2106,9 @@ static void SendNameAndColor(UINT8 n) // rescan our current voice and send that over the network. valuevoice = P_GetMobjVoice(player->mo); - if (valuevoice && valuevoice->parent == cv_skin[n].value) + if (valuevoice) { - strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); + strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2169,18 +2116,13 @@ static void SendNameAndColor(UINT8 n) } else { - // Try getting the voice from our ID. - valuevoice = &skinvoices[player->voice_id]; - - if (valuevoice->parent != skins[cv_skin[n].value].voice->parent) - { - // Parent mismatch; use the skin's voice. - valuevoice = skins[cv_skin[n].value].voice; - } + // Get the voice from our ID. + // No need to compare parents. + valuevoice = &skins[player->skin].voices[player->voice_id]; if (valuevoice) { - strlcpy(voicebuf, DEH_KartVoiceName(valuevoice->id), sizeof(voicebuf)); + strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2189,7 +2131,7 @@ static void SendNameAndColor(UINT8 n) else { // ...still nothing? - strlcpy(voicebuf, DEH_KartVoiceName(0), sizeof(voicebuf)); + strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); strlwr(voicebuf); CV_StealthSet(&cv_voice[n], voicebuf); @@ -2327,15 +2269,15 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name); - strlcpy(voicename, DEH_KartVoiceName(skins[forcedskin].voice->id), sizeof(voicename)); + strlcpy(voicename, skins[forcedskin].voices[0].name, sizeof(voicename)); strlwr(voicename); CV_StealthSet(&cv_voice[localplayer], voicename); - cv_voice[localplayer].value = skins[forcedskin].voice->id; + cv_voice[localplayer].value = skins[forcedskin].voices[0].id; } // set voice - SetPlayerVoiceByNum(playernum, skins[forcedskin].voice->id); + SetPlayerVoiceByNum(playernum, skins[forcedskin].voices[0].id); } else { @@ -2346,11 +2288,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (localplayer != -1) { - strlcpy(voicename, DEH_KartVoiceName(voice), sizeof(voicename)); + strlcpy(voicename, skins[p->skin].voices[p->voice_id].name, sizeof(voicename)); strlwr(voicename); CV_StealthSet(&cv_voice[localplayer], voicename); - cv_voice[localplayer].value = skinvoices[voice].id; + cv_voice[localplayer].value = skins[p->skin].voices[p->voice_id].id; } } @@ -7599,7 +7541,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) if (!myvoice) { - myvoice = skins[players[pid].skin].voice; + myvoice = &skins[players[pid].skin].voices[0]; if (!myvoice) { @@ -7621,7 +7563,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) if (!(cht_debug || devparm) && !(multiplayer || netgame) // In single player. && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y { - strlcpy(cvarname, DEH_KartVoiceName(myvoice->id), sizeof(cvarname)); + strlcpy(cvarname, myvoice->name, sizeof(cvarname)); strlwr(cvarname); CV_StealthSet(&cv_voice[pnum], cvarname); @@ -7636,7 +7578,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your voice at the moment.\n")); - strlcpy(cvarname, DEH_KartVoiceName(myvoice->id), sizeof(cvarname)); + strlcpy(cvarname, myvoice->name, sizeof(cvarname)); strlwr(cvarname); CV_StealthSet(&cv_voice[pnum], cvarname); diff --git a/src/deh_soc.c b/src/deh_soc.c index 5a9425ba4..62990b4cc 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -4242,8 +4242,9 @@ void readkartresult(MYFILE *f, kartresult_t *result) #undef WARN #undef WARN0 -#define WARN(str, ...) deh_warning("KartVoice %s: " str, strbuf_get(voicenames, voice->info.nameofs), __VA_ARGS__) -#define WARN0(str) deh_warning("KartVoice %s: " str, strbuf_get(voicenames, voice->info.nameofs)) +/* +#define WARN(str, ...) deh_warning("KartVoice %s: " str, voice->name, __VA_ARGS__) +#define WARN0(str) deh_warning("KartVoice %s: " str, voice->name) // Necessary for sound adding, so we can gather flags and singularity data. static sfxenum_t skinsoundreroute[] = { @@ -4416,13 +4417,13 @@ void readkartvoice(MYFILE *f, kartvoice_t *voice) while (!myfeof(f)); // finish when the line is empty // Set our ID afterwards. - voice->id = (UINT32)(voice - skinvoices); + //voice->id = (UINT32)(voice - skinvoices); Z_Free(s); } #undef WARN -#undef WARN0 +#undef WARN0*/ // // @@ -4604,15 +4605,6 @@ useoddsfunc_f *get_useoddsfunc(const char *word) return NULL; } -kartvoicetype_e get_kartvoice(const char *word) -{ // Returns the value of KVOICE_ enumerations - kartvoicetype_e i; - i = DEH_FindKartVoice(word); - if (i == MAXSKINVOICES) - deh_warning("Couldn't find voice named 'KVOICE_%s'", word); - return i; -} - /// \todo Make ANY of this completely over-the-top math craziness obey the order of operations. static fixed_t op_mul(fixed_t a, fixed_t b) { return a*b; } static fixed_t op_div(fixed_t a, fixed_t b) { return a/b; } diff --git a/src/deh_soc.h b/src/deh_soc.h index ed97e3338..143c21a3d 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -65,7 +65,6 @@ menudrawer_f *get_menudrawer(const char *word); skincolornum_t get_skincolor(const char *word); kartitemtype_e get_kartitem(const char *word); useoddsfunc_f *get_useoddsfunc(const char *word); -kartvoicetype_e get_kartvoice(const char *word); void readwipes(MYFILE *f); void readmaincfg(MYFILE *f); @@ -97,7 +96,6 @@ preciptype_t get_precip(const char *word); void readweather(MYFILE *f, INT32 num); void readkartitem(MYFILE *f, kartitem_t *item); void readkartresult(MYFILE *f, kartresult_t *result); -void readkartvoice(MYFILE *f, kartvoice_t *voice); #ifdef __cplusplus } // extern "C" diff --git a/src/deh_tables.c b/src/deh_tables.c index 9d9705bf7..d17947f2b 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -41,7 +41,7 @@ strbuf_t *mobjnames; strbuf_t *skincolornames; strbuf_t *menunames; strbuf_t *kartitemnames; -strbuf_t *voicenames; + UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway. const char *DEH_MobjtypeName(mobjtype_t i) @@ -69,11 +69,6 @@ const char *DEH_KartItemName(kartitemtype_e i) return strbuf_get(kartitemnames, kartitems[i].info.nameofs); } -const char *DEH_KartVoiceName(kartvoicetype_e i) -{ - return strbuf_get(voicenames, skinvoices[i].info.nameofs); -} - mobjtype_t DEH_FindMobjtype(const char *word) { mobjtype_t i; @@ -139,19 +134,6 @@ kartitemtype_e DEH_FindKartItem(const char *word) return MAXKARTITEMS; } -kartvoicetype_e DEH_FindKartVoice(const char *word) -{ - INT32 i; - UINT32 hash = HASH32(word, strlen(word)); - for (i = 0; i < numskinvoices; i++) { - if (hash != skinvoices[i].info.namehash) - continue; - if (fastcmp(word, DEH_KartVoiceName(i))) - return (kartvoicetype_e)i; - } - return MAXSKINVOICES; -} - struct flickytypes_s FLICKYTYPES[] = { {"BLUEBIRD", MT_FLICKY_01}, // Flicky (Flicky) {"RABBIT", MT_FLICKY_02}, // Pocky (1) diff --git a/src/deh_tables.h b/src/deh_tables.h index d5ca7bf64..3c558cb48 100644 --- a/src/deh_tables.h +++ b/src/deh_tables.h @@ -32,7 +32,7 @@ extern strbuf_t *mobjnames; extern strbuf_t *skincolornames; extern strbuf_t *menunames; extern strbuf_t *kartitemnames; -extern strbuf_t *voicenames; + extern UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway. const char *DEH_MobjtypeName(mobjtype_t i); @@ -40,14 +40,12 @@ const char *DEH_StateName(statenum_t i); const char *DEH_SkincolorName(skincolornum_t i); const char *DEH_MenutypeName(menutype_t i); const char *DEH_KartItemName(kartitemtype_e i); -const char *DEH_KartVoiceName(kartvoicetype_e i); mobjtype_t DEH_FindMobjtype(const char *word); statenum_t DEH_FindState(const char *word); skincolornum_t DEH_FindSkincolor(const char *word); menutype_t DEH_FindMenutype(const char *word); kartitemtype_e DEH_FindKartItem(const char *word); -kartvoicetype_e DEH_FindKartVoice(const char *word); struct flickytypes_s { const char *name; diff --git a/src/dehacked.c b/src/dehacked.c index 4a7b8959f..f582be246 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -786,7 +786,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) result = K_RegisterResult(word2, alternate); readkartresult(f, result); } - else if (fastncmp(word, "KARTVOICE", 9)) + /*else if (fastncmp(word, "KARTVOICE", 9)) (Deprecated; getting merged into skins) { if (i == 0 && word2[0] != '0') // If word2 isn't a number i = DEH_FindKartVoice(word2); // find a voice by name @@ -811,7 +811,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) deh_warning("KartVoice number %d out of range (0 - %d)", i, numskinvoices); ignorelines(f); } - } + }*/ else if (fastcmp(word, "WEATHER") || fastcmp(word, "PRECIP") || fastcmp(word, "PRECIPITATION")) { if (i == 0 && word2[0] != '0') // If word2 isn't a number diff --git a/src/f_finale.c b/src/f_finale.c index 29a79c042..0a54a60d4 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -415,10 +415,7 @@ void F_IntroTicker(void) INT32 rskin = M_RandomKey(numskins); UINT8 rtaunt = M_RandomKey(2); - if (!skins[rskin].voice) // Should NOT happen! - I_Error("F_IntroTicker: Skin no. %d has no voice assigned", rskin); - - sfxenum_t rsound = skins[rskin].voice->boost[rtaunt]; + sfxenum_t rsound = skins[rskin].voices[0].boost[rtaunt]; S_StartSound(NULL, rsound); } } @@ -452,11 +449,8 @@ void F_IntroTicker(void) char chars[6][10] = {"tails", "chao", "aiai", "sakura", "doom"}; SINT8 random = M_RandomRange(0, 4); const INT32 rskin = R_SkinAvailable(chars[random]); - - if (!skins[rskin].voice) // Should NOT happen! - I_Error("F_IntroTicker: Skin no. %d has no voice assigned", rskin); - sfxenum_t rsound = skins[rskin].voice->win; + sfxenum_t rsound = skins[rskin].voices[0].win; S_StartSound(NULL, sfx_flgcap); S_StartSound(NULL, rsound); } diff --git a/src/g_demo.c b/src/g_demo.c index b94142a8f..a15fc2028 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -1268,28 +1268,31 @@ void G_ReadDemoExtraData(void) FindClosestSkinForStats(p, extra.kartspeed, extra.kartweight); // Voice - const INT32 vce = R_FindIDForVoice(extra.voicename); + const INT32 vce = R_FindIDForVoice(&skins[players[p].skin], 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; + players[p].voice_id = 0; } 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 && + if (skins[players[p].skin].voices[players[p].voice_id].id == 0) + { + // Default voice for this skin. Just strip the object of its voice. + players[p].mo->voice = NULL; + } + else if (skins[players[p].skin].voices[players[p].voice_id].id != + skins[players[p].skin].voices[0].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].mo->voice = &skins[players[p].skin].voices[players[p].voice_id]; } players[p].kartspeed = extra.kartspeed; @@ -1396,7 +1399,7 @@ void G_WriteDemoExtraData(void) WRITESTRINGL(demobuf.p, skins[players[i].skin].name, 16+1); // Voice - WRITESTRINGL(demobuf.p, DEH_KartVoiceName(players[i].voice_id), 32+1); + WRITESTRINGL(demobuf.p, skins[players[i].skin].voices[players[i].voice_id].name, 32+1); // Stats WRITEUINT8(demobuf.p, skins[players[i].skin].kartspeed); @@ -2983,7 +2986,7 @@ void G_BeginRecording(void) WRITESTRINGL(demobuf.p, skincolors[player->skincolor].name, 16+1); // Voice - WRITESTRINGL(demobuf.p, DEH_KartVoiceName(player->voice_id), 32+1); + WRITESTRINGL(demobuf.p, skins[player->skin].voices[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! @@ -3859,13 +3862,13 @@ void G_DoPlayDemo(char *defdemoname) // We'll only assign IDs, since the actual voice assignment happens during // player spawn. - const INT32 vce = R_FindIDForVoice(plr->voice); + const INT32 vce = R_FindIDForVoice(&skins[players[p].skin], 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; + players[p].voice_id = 0; } else { diff --git a/src/g_game.c b/src/g_game.c index 5f865acae..22c1b98e1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2703,7 +2703,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT8 latestlap; UINT16 skincolor; INT32 skin; - kartvoicetype_e voice; + UINT16 voice; UINT32 availabilities; tic_t jointime; diff --git a/src/info.c b/src/info.c index 438a56d3c..4a48dc8e3 100644 --- a/src/info.c +++ b/src/info.c @@ -258,19 +258,4 @@ void P_ResetData(INT32 flags) K_RegisterItem(i); } } - - // voices - if (init) - { - for (i = 0; i < MAXSKINVOICES; i++) - { - R_ClearVoice(i); - } - - numskinvoices = 0; - - if (voicenames) - Z_Free(voicenames); - voicenames = strbuf_alloc(); - } } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index c1a90b5c4..817de91f0 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2615,38 +2615,6 @@ static int lib_rGetNameByColor(lua_State *L) } -// R_SKINS -//////////// - -// Lua-exclusive; allocates a voice into memory, and returns a pointer to said voice. -// Mangled clone of deh_soc's get_kartvoice, modified for Lua. -static int lib_rAddKartVoice(lua_State *L) -{ - const char* word = luaL_checkstring(L, 1); - NOHUD - - char wordupper[VOICENAMESIZE+1]; - - strlcpy(wordupper, word, sizeof(wordupper)); - strupr(wordupper); - - INT32 i = DEH_FindKartVoice(wordupper); - if (i == MAXSKINVOICES) - { - // Freeslot voices that don't exist yet. - const INT32 allocresult = R_AllocKartVoice(false, word, &i); - - if (allocresult < 0) - return luaL_error(L, "kartvoice_t '%s' could not be allocated.", word); - else - skinvoices[i].id = (UINT32)(&skinvoices[i] - skinvoices); - } - - // If we're down here, we managed to allocate our voice. - LUA_PushUserdata(L, &skinvoices[i], META_VOICE); - return 1; -} - // S_SOUND //////////// static int GetValidSoundOrigin(lua_State *L, void **origin) @@ -5426,9 +5394,6 @@ static luaL_Reg lib[] = { {"R_GetSuperColorByName", lib_rGetSuperColorByName}, {"R_GetNameByColor", lib_rGetNameByColor}, - // r_skins (voice) - {"R_AddKartVoice", lib_rAddKartVoice}, - // s_sound {"S_StartSound",lib_sStartSound}, {"S_StartSoundAtVolume",lib_sStartSoundAtVolume}, diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index e6c56fd59..01a483057 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -144,8 +144,10 @@ static int skin_get(lua_State *L) LUA_PushUserdata(L, skin->soundsid, META_SOUNDSID); break; case skin_voice: - LUA_PushUserdata(L, skin->voice, META_VOICE); - break; + // ...uuuuuuughhhhh... + return UNIMPLEMENTED; + //LUA_PushUserdata(L, skin->voice, META_VOICE); + //break; case skin_sprites: LUA_PushUserdata(L, skin->sprites, META_SKINSPRITES); break; @@ -255,7 +257,7 @@ static int soundsid_get(lua_State *L) if (i >= NUMSKINSOUNDS) return luaL_error(L, "%s cannot be %s", LUA_QL("skinsound_t"), va("%u", (UINT32)i)); - lua_pushinteger(L, P_GetKartVoiceSFX(s->voice, i)); + lua_pushinteger(L, P_GetKartVoiceSFX(&s->voices[0], i)); return 1; } diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index cea205069..4bee3ac81 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -27,7 +27,6 @@ enum voicevars { voicevars_id = 0, voicevars_name, // Not actually in kartvoice_t; returns the name of the voice - voicevars_parent, voicevars_win, voicevars_lose, voicevars_pain, @@ -41,7 +40,6 @@ enum voicevars static const char *const voicevars_opt[] = { "id", "name", - "parent", "win", "lose", "pain", @@ -74,10 +72,7 @@ static int voice_get(lua_State* L) case voicevars_id: return RNOGET; case voicevars_name: - lua_pushstring(L, DEH_KartVoiceName((kartvoicetype_e)(voice - skinvoices))); - break; - case voicevars_parent: - LUA_PushUserdata(L, &skins[voice->parent], META_SKIN); + lua_pushstring(L, voice->name); break; case voicevars_win: LUA_PushUserdata(L, &S_sfx[voice->win], META_SFXINFO); @@ -134,21 +129,6 @@ static int voice_set(lua_State* L) return RNOSET; case voicevars_name: return RNOSET; - case voicevars_parent: - { - // It would make no sense not to accept a skin struct here. - skin_t* my_new_parent = *((skin_t**)luaL_checkudata(L, 3, META_SKIN)); - - if (!my_new_parent) - { - luaL_error(L, "skin does not exist."); - } - else - { - voice->parent = (UINT16)(my_new_parent - skins); - } - break; - } case voicevars_win: // For the actual sound values, you pass the actual sfxenum_t enums. // Yes, I know what I said about skin structs literally just above. @@ -190,7 +170,7 @@ static int voice_num(lua_State* L) // voices are always valid, only added, never removed I_Assert(voice != NULL); - lua_pushinteger(L, voice - skinvoices); + lua_pushinteger(L, voice->id); return 1; } @@ -226,88 +206,6 @@ static int voice_array_num(lua_State *L) return 1; } - -static int lib_iterateVoices(lua_State* L) -{ - INT32 i; - - if (lua_gettop(L) < 2) - { - lua_pushcfunction(L, lib_iterateVoices); - return 1; - } - - lua_settop(L, 2); - lua_remove(L, 1); // state is unused. - - if (!lua_isnil(L, 1)) - i = (INT32)(*((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE)) - skinvoices) + 1; - else - i = 0; - - // voices are always valid, only added, never removed - if (i < numskinvoices) - { - LUA_PushUserdata(L, &skinvoices[i], META_VOICE); - return 1; - } - - return 0; -} - -static int lib_getVoice(lua_State* L) -{ - const char* field; - INT32 i; - - // find voice by number - if (lua_type(L, 2) == LUA_TNUMBER) - { - i = luaL_checkinteger(L, 2); - if (i < 0 || i >= MAXSKINVOICES) - { - return luaL_error( - L, "skinvoices[] index %d out of range (0 - %d)", i, MAXSKINVOICES - 1); - } - else if (i >= numskinvoices) - { - return 0; - } - - LUA_PushUserdata(L, &skinvoices[i], META_VOICE); - return 1; - } - - field = luaL_checkstring(L, 2); - - // special function iterate - if (fastcmp(field, "iterate")) - { - lua_pushcfunction(L, lib_iterateVoices); - return 1; - } - - // find voice by name - // Glad I made a function for this... - i = R_FindIDForVoice(field); - - if (i != MAXSKINVOICES) - { - // Found a voice. - LUA_PushUserdata(L, &skinvoices[i], META_VOICE); - return 1; - } - - return 0; -} - -// In this case: returns TOTAL voices, and not individual table counts. -static int lib_numVoices(lua_State* L) -{ - lua_pushinteger(L, numskinvoices); - return 1; -} - int LUA_VoiceLib(lua_State* L) { luaL_newmetatable(L, META_VOICE); @@ -332,16 +230,5 @@ int LUA_VoiceLib(lua_State* L) lua_setfield(L, -2, "__len"); lua_pop(L,1); - - lua_newuserdata(L, 0); - lua_createtable(L, 0, 2); - lua_pushcfunction(L, lib_getVoice); - lua_setfield(L, -2, "__index"); - - lua_pushcfunction(L, lib_numVoices); - lua_setfield(L, -2, "__len"); - lua_setmetatable(L, -2); - lua_setglobal(L, "skinvoices"); - return 0; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 3d598418d..d71f3cfa4 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12381,24 +12381,12 @@ void P_SpawnPlayer(INT32 playernum) mobj->skin = &skins[p->skin]; P_SetupStateAnimation(mobj, mobj->state); - if (p->voice_id != skins[p->skin].voice->id) + if (p->voice_id) { // During respawns, do a quick check on our voice ID to make sure // our skin can use it. // This should prevent bots from always having Sonic's voice. - kartvoice_t *tentative_voice = &skinvoices[p->voice_id]; - - if (tentative_voice->parent == skins[p->skin].voice->parent) - { - // Seems OK, set our voice! - mobj->voice = tentative_voice; - } - else - { - // Parent mismatch; use the skin default. - p->voice_id = skins[p->skin].voice->id; - mobj->voice = NULL; - } + mobj->voice = &skins[p->skin].voices[p->voice_id]; } mobj->health = 1; @@ -14991,9 +14979,9 @@ kartvoice_t *P_GetMobjVoice(const mobj_t *mo) { return mo->voice; } - else if (mo->skin && ((skin_t *)mo->skin)->voice) + else if (mo->skin) { - return ((skin_t *)mo->skin)->voice; + return &((skin_t *)mo->skin)->voices[0]; } return NULL; diff --git a/src/p_saveg.c b/src/p_saveg.c index e60bedc95..7874caebd 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2337,12 +2337,19 @@ static thinker_t *SyncMobjThinker(savebuffer_t *save, actionf_p1 thinker, thinke SYNCF(MD3_EXTVAL3, mobj->extravalue3); SYNCF(MD3_LIFETIME, mobj->mobjlifetime); - if (GETB(MD3_VOICE)) + if (GETB(MD3_VOICE) && mobj->skin) { if (save->write) - WRITEUINT16(save->p, (UINT16)((kartvoice_t *)mobj->voice - skinvoices)); + WRITEUINT16(save->p, (UINT16)(mobj->voice - &((skin_t *)(mobj->skin))->voices[0])); else - mobj->voice = &skinvoices[READUINT16(save->p)]; + { + UINT16 savedvoice = READUINT16(save->p); + + if (savedvoice > ((skin_t *)(mobj->skin))->numvoices) + savedvoice = 0; + + mobj->voice = &((skin_t *)(mobj->skin))->voices[savedvoice]; + } } if (!save->write) diff --git a/src/p_user.c b/src/p_user.c index e70bbaf0f..379d22f34 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1316,7 +1316,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) //const INT32 sfx_id = (losing ? sfx_klose : sfx_kwin); skin_t *playerskin = &skins[player->skin]; - const kartvoice_t *playervoice = (!P_MobjWasRemoved(player->mo) ? P_GetMobjVoice(player->mo) : playerskin->voice); + const kartvoice_t *playervoice = (!P_MobjWasRemoved(player->mo) ? P_GetMobjVoice(player->mo) : &playerskin->voices[0]); if (playervoice) { diff --git a/src/r_skins.c b/src/r_skins.c index 056a60d6d..1724e92eb 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -43,7 +43,7 @@ INT32 numskins = 0; INT32 numskinvoices = 0; skin_t skins[MAXSKINS]; INT32 skinsorted[MAXSKINS]; -kartvoice_t skinvoices[MAXSKINVOICES]; +//kartvoice_t skinvoices[MAXSKINVOICES]; // FIXTHIS: don't work because it must be inistilised before the config load //#define SKINVALUES @@ -298,9 +298,9 @@ sfxenum_t P_GetKartVoiceSFX(kartvoice_t *voice, INT32 skinsound) return sfx_thok; } -void R_ClearVoice(kartvoicetype_e voicetype) +void R_ClearVoice(skin_t *skin, INT32 voicetype) { - kartvoice_t *voice = &skinvoices[voicetype]; + kartvoice_t *voice = &skin->voices[voicetype]; memset(voice, 0, sizeof(kartvoice_t)); } @@ -343,12 +343,11 @@ static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice) } } - // TODO: Make this hexadecimal - skin_voice->parent = (UINT16)(skin - skins); - - skin_voice->id = skin_voice-skinvoices; + strlcpy(skin_voice->name, "default", sizeof(skin_voice->name)); + strlcpy(skin_voice->realname, "Default", sizeof(skin_voice->realname)); - skin->voice = skin_voice; + skin->numvoices++; + CONS_Printf(M_GetText("%d %s allocated for this skin.\n"), skin->numvoices, (skin->numvoices == 1) ? "voice" : "voices"); } static void R_IHateThatHedgehog(UINT16 wadnum); @@ -718,20 +717,31 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) // 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 R_FindIDForVoice(skin_t *skin, const char* voicename) { INT32 i; const char *checkvoicename; // Scan through our allocated skin voices for the wanted voice. - for (i = 0; i < numskinvoices; i++) + + // First pass: Check the actual name. + for (i = 0; i < MAXSKINVOICES; i++) { - // Whatever. Go, my code duplication! - checkvoicename = DEH_KartVoiceName(i); - if (strnicmp(voicename, checkvoicename, VOICENAMESIZE) == 0) + if (strnicmp(voicename, skin->voices[i].name, VOICENAMESIZE) == 0) { // Found a voice. - return (kartvoicetype_e)i; + return i; + } + } + + // Second pass: check the display name. + for (i = 0; i < MAXSKINVOICES; i++) + { + // Whatever. Go, my code duplication! + if (strnicmp(voicename, skin->voices[i].realname, VOICENAMESIZE) == 0) + { + // Found a voice. + return i; } } @@ -739,18 +749,18 @@ kartvoicetype_e R_FindIDForVoice(const char* voicename) return MAXSKINVOICES; } -static kartvoicetype_e R_FindIDForVoiceNum(UINT16 voicenum) +static INT32 R_FindIDForVoiceNum(skin_t *skin, UINT16 voicenum) { INT32 i; // Scan through our allocated skin voices for the wanted voice. - for (i = 0; i < numskinvoices; i++) + for (i = 0; i < MAXSKINVOICES; i++) { // Whatever. Go, my code duplication! - if (skinvoices[i].id == voicenum) + if (skin->voices[i].id == voicenum) { // Found a voice. - return (kartvoicetype_e)i; + return i; } } @@ -763,12 +773,12 @@ void SetPlayerVoice(INT32 playernum, const char* voicename) { player_t* player = &players[playernum]; - kartvoicetype_e voxid = R_FindIDForVoice(voicename); + INT32 voxid = R_FindIDForVoice(&skins[player->skin], voicename); if (voxid == MAXSKINVOICES) { - // Couldn't find a voice. Default to the skin's. - voxid = skins[player->skin].voice->id; + // Couldn't find a voice. Default to the skin's default. + voxid = 0; } if (P_MobjWasRemoved(player->mo)) @@ -785,8 +795,7 @@ void SetPlayerVoice(INT32 playernum, const char* voicename) const kartvoice_t* myvoice = P_GetMobjVoice(player->mo); - const char* myvoicename = DEH_KartVoiceName(myvoice->id); - if (!strnicmp(voicename, myvoicename, VOICENAMESIZE)) + if (!strnicmp(voicename, myvoice->name, VOICENAMESIZE)) { // Hey... this voice is the same as before! // WAIT! Before we go, reset the player's voice ID! @@ -801,8 +810,8 @@ void SetPlayerVoice(INT32 playernum, const char* voicename) // for the needed name. If we're down here, we've passed the safeguards and can assign a // voice. - if (voxid != skins[player->skin].voice->id) - player->mo->voice = &skinvoices[(UINT16)voxid]; + if (voxid) + player->mo->voice = &skins[player->skin].voices[(UINT16)voxid]; else player->mo->voice = NULL; @@ -816,12 +825,12 @@ void SetPlayerVoice(INT32 playernum, const char* voicename) void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) { player_t* player = &players[playernum]; - kartvoicetype_e voxid = R_FindIDForVoiceNum(voicenum); + INT32 voxid = R_FindIDForVoiceNum(&skins[player->skin], voicenum); if (voxid == MAXSKINVOICES) { - // Couldn't find a voice. Default to the skin's. - voxid = skins[player->skin].voice->id; + // Couldn't find a voice. Default to the skin's default. + voxid = 0; } if (P_MobjWasRemoved(player->mo)) @@ -853,8 +862,8 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) // for the needed name. If we're down here, we've passed the safeguards and can assign a // voice. - if (voxid != skins[player->skin].voice->id) - player->mo->voice = &skinvoices[(UINT16)voxid]; + if (voxid) + player->mo->voice = &skins[player->skin].voices[(UINT16)voxid]; else player->mo->voice = NULL; @@ -1076,15 +1085,14 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski static void R_IHateThatHedgehog(UINT16 wadnum) { skin_t *skin = &skins[0]; - kartvoice_t *skin_voice = &skinvoices[0]; + kartvoice_t *skin_voice = &skin->voices[0]; Sk_SetDefaultValue(skin, skin_voice); skin->wadnum = wadnum; strcpy(skin->name, "sonic"); - skin->voice->parent = 0; // Add voice INT32 dummy; - if (R_AllocKartVoice(true, skin->name, &dummy) < 0) + if (R_FindIDForVoice(skin, "default") == MAXSKINVOICES) { // Hey guy, take care! I_Error("Failed to allocate initial skin voice\n"); @@ -1103,6 +1111,136 @@ static void R_IHateThatHedgehog(UINT16 wadnum) #endif } +// Necessary for sound adding, so we can gather flags and singularity data. +static sfxenum_t skinsoundreroute[] = { + sfx_kwin, + sfx_klose, + sfx_khurt1, + sfx_khurt2, + sfx_kattk1, // Offense item taunt + sfx_kattk2, + sfx_kbost1, // Boost item taunt + sfx_kbost2, + sfx_kslow, // Overtake taunt + sfx_khitem, // Hit confirm taunt + sfx_kgloat, // Power item taunt +}; + +// Extremely shitty and lifted almost entirely from r_skins.c +// Sets the sound effect value for a voice, given a valid skinsound value is passed. +static UINT8 reallygrossvoicesfxadder(INT32 skinsound, const char *value, kartvoice_t *voice) +{ + sfxenum_t i, j, reroute; + char printval[6]; + + // Remove the prefix. (We can affect this directly since we're not going to use it again.) + if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS* + value += 2; + else // sfx_* + value += 4; + + strncpy(printval, value, 6); + + reroute = skinsoundreroute[skinsound]; + + // Automatically allocate any defined voice sounds. + // Freeslotted voice clips won't ever belong to a skin. + + i = sfx_None; + + // Find if this sound already exists first. + for (j = 0; j < NUMSFX; j++) + { + if (!S_sfx[j].name) + continue; + + if (!stricmp(S_sfx[j].name, value)) + { + // Found an existing sound. + i = j; + break; + } + } + + // This sound doesn't seem to exist, so let's add a new one. + if (i == sfx_None) + { + CONS_Printf("Sound sfx_%s allocated.\n", value); + i = S_AddSoundFx(value, S_sfx[reroute].singularity, S_sfx[reroute].flags, false); + } + + if (i != sfx_None) + { + if (voice) + { + Sk_SetSkinVoiceValue(voice, skinsound, i); + return 0; + } + } + + return 1; +} + +static kartvoice_t *allocvoice; + +static void readkartvoice(skin_t *skin, char *stoken, char *value, kartvoice_t *voice) +{ + if (!stricmp(stoken, "voicename")) + { + // Allocate the actual voice, + } + else if (!stricmp(stoken, "GLOAT")) + { + reallygrossvoicesfxadder(SKSKPOWR, value, voice); + } + else if (!stricmp(stoken, "WIN")) + { + reallygrossvoicesfxadder(SKSKWIN, value, voice); + } + else if (!stricmp(stoken, "LOSE")) + { + reallygrossvoicesfxadder(SKSKLOSE, value, voice); + } + else if (!stricmp(stoken, "SLOW") || !stricmp(stoken, "OVERTAKE") || !stricmp(stoken, "PASS")) + { + reallygrossvoicesfxadder(SKSKSLOW, value, voice); + } + else if (!stricmp(stoken, "PAIN1") || !stricmp(stoken, "HURT1") || !stricmp(stoken, "DAMAGE1")) + { + reallygrossvoicesfxadder(SKSKPAN1, value, voice); + } + else if (!stricmp(stoken, "PAIN2") || !stricmp(stoken, "HURT2") || !stricmp(stoken, "DAMAGE2")) + { + reallygrossvoicesfxadder(SKSKPAN2, value, voice); + } + else if (!stricmp(stoken, "ATTACK")) + { + // Needs to be handled like rival allocation + } + else if (!stricmp(stoken, "ATTACK1")) + { + reallygrossvoicesfxadder(SKSKATK1, value, voice); + } + else if (!stricmp(stoken, "ATTACK2")) + { + reallygrossvoicesfxadder(SKSKATK2, value, voice); + } + else if (!stricmp(stoken, "BOOST1")) + { + reallygrossvoicesfxadder(SKSKBST1, value, voice); + } + else if (!stricmp(stoken, "BOOST2")) + { + reallygrossvoicesfxadder(SKSKBST2, value, voice); + } + else if (!stricmp(stoken, "HITEM") || !stricmp(stoken, "HITCONFIRM")) + { + reallygrossvoicesfxadder(SKSKHITM, value, voice); + } + + return -1; +} + // returns whether found appropriate property static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) { @@ -1222,6 +1360,173 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) GETPATCH(facemmap) #undef GETPATCH + else if (!stricmp(stoken, "voicename")) + { + // Change the current voice. + INT32 vox_id = R_FindIDForVoice(skin, value); + + if (vox_id == MAXSKINVOICES) + { + if (!stricmp(stoken, "default")) + { + // "default" should ALWAYS exist! + I_Error("Failed to allocate default voice for skin %s\n", skin->name); + } + + // Couldn't find this voice; let's try allocating. + if (R_AllocKartVoice(skin, value, &vox_id) + 1) + { + // Allocated a voice! + allocvoice = &skin->voices[vox_id]; + CONS_Printf(M_GetText("%d %s allocated for this skin.\n"), skin->numvoices, (skin->numvoices == 1) ? "voice" : "voices"); + } + + // Found nothing, or we're full. Let's just keep moving. + } + else + allocvoice = &skin->voices[vox_id]; + } + else if (!stricmp(stoken, "voicerealname")) + { + // Set the "realname" field for the current voice. + STRBUFCPY(allocvoice->realname, value); + SYMBOLCONVERT(allocvoice->realname); + } + else if (!stricmp(stoken, "voicegloat")) + { + reallygrossvoicesfxadder(SKSKPOWR, value, allocvoice); + } + else if (!stricmp(stoken, "voicewin")) + { + reallygrossvoicesfxadder(SKSKWIN, value, allocvoice); + } + else if (!stricmp(stoken, "voicelose")) + { + reallygrossvoicesfxadder(SKSKLOSE, value, allocvoice); + } + else if (!stricmp(stoken, "voiceslow") || !stricmp(stoken, "voiceovertake") || !stricmp(stoken, "voicepass")) + { + reallygrossvoicesfxadder(SKSKSLOW, value, allocvoice); + } + else if (!stricmp(stoken, "voicepain1") || !stricmp(stoken, "voicehurt1") || !stricmp(stoken, "voicedamage1")) + { + reallygrossvoicesfxadder(SKSKPAN1, value, allocvoice); + } + else if (!stricmp(stoken, "voicepain2") || !stricmp(stoken, "voicehurt2") || !stricmp(stoken, "voicedamage2")) + { + reallygrossvoicesfxadder(SKSKPAN2, value, allocvoice); + } + else if (!stricmp(stoken, "voiceattack1")) + { + reallygrossvoicesfxadder(SKSKATK1, value, allocvoice); + } + else if (!stricmp(stoken, "voiceattack2")) + { + reallygrossvoicesfxadder(SKSKATK2, value, allocvoice); + } + else if (!stricmp(stoken, "voiceboost1")) + { + reallygrossvoicesfxadder(SKSKBST1, value, allocvoice); + } + else if (!stricmp(stoken, "voiceboost2")) + { + reallygrossvoicesfxadder(SKSKBST2, value, allocvoice); + } + else if (!stricmp(stoken, "voicehitem") || !stricmp(stoken, "voicehitconfirm")) + { + reallygrossvoicesfxadder(SKSKHITM, value, allocvoice); + } + else if (!stricmp(stoken, "voicehurt") || !stricmp(stoken, "voicepain") || !stricmp(stoken, "voicedamage")) + { + // (that one gif of patrick drooling where his mouth is his entire face) + size_t len = strlen(value); + size_t i; + char voicename[VOICENAMESIZE] = ""; + UINT8 pos = 0; + UINT8 numvoices = 0; + + for (i = 0; i <= len; i++) + { + if (numvoices >= VOICEARRAYSIZE) + { + break; + } + + if (value[i] == ',' || i == len) + { + reallygrossvoicesfxadder((numvoices < 1) ? SKSKPAN1 : SKSKPAN2, voicename, allocvoice); + numvoices++; + + memset(voicename, 0, sizeof (voicename)); + pos = 0; + + continue; + } + + voicename[pos] = value[i]; + pos++; + } + } + else if (!stricmp(stoken, "voiceattack")) + { + size_t len = strlen(value); + size_t i; + char voicename[VOICENAMESIZE] = ""; + UINT8 pos = 0; + UINT8 numvoices = 0; + + for (i = 0; i <= len; i++) + { + if (numvoices >= VOICEARRAYSIZE) + { + break; + } + + if (value[i] == ',' || i == len) + { + reallygrossvoicesfxadder((numvoices < 1) ? SKSKATK1 : SKSKATK2, voicename, allocvoice); + numvoices++; + + memset(voicename, 0, sizeof (voicename)); + pos = 0; + + continue; + } + + voicename[pos] = value[i]; + pos++; + } + } + else if (!stricmp(stoken, "voiceboost")) + { + size_t len = strlen(value); + size_t i; + char voicename[VOICENAMESIZE] = ""; + UINT8 pos = 0; + UINT8 numvoices = 0; + + for (i = 0; i <= len; i++) + { + if (numvoices >= VOICEARRAYSIZE) + { + break; + } + + if (value[i] == ',' || i == len) + { + reallygrossvoicesfxadder((numvoices < 1) ? SKSKBST1 : SKSKBST2, voicename, allocvoice); + numvoices++; + + memset(voicename, 0, sizeof (voicename)); + pos = 0; + + continue; + } + + voicename[pos] = value[i]; + pos++; + } + } else // let's check if it's a sound, otherwise error out { boolean found = false; @@ -1252,10 +1557,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) { newskinsound = S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].flags, true); - if (skin->voice) - { - Sk_SetSkinVoiceValue(skin->voice, S_sfx[i].skinsound, newskinsound); - } + Sk_SetSkinVoiceValue(allocvoice, S_sfx[i].skinsound, newskinsound); found = true; } @@ -1269,26 +1571,30 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) // Allocates a voice onto the dehacked list, and iterates the provided output pointer // (should it exist). // -INT32 R_AllocKartVoice(boolean skinalloc, const char* name, INT32 *out) +INT32 R_AllocKartVoice(skin_t *skin, const char* name, INT32 *out) { char voxname_upper[VOICENAMESIZE]; strlcpy(voxname_upper, name, sizeof(voxname_upper)); strupr(voxname_upper); - *out = DEH_FindKartVoice(voxname_upper); + *out = R_FindIDForVoice(skin, name); if (*out != MAXSKINVOICES) - return skinalloc ? -1 : 0; // already allocated + return -1; // already allocated - if (numskinvoices == MAXSKINVOICES - 1) { + if (skin->numvoices == MAXSKINVOICES - 1) { CONS_Alert(CONS_WARNING, "Ran out of free voice slots!\n"); return -1; } - CONS_Printf("Voice %s allocated.\n",voxname_upper); - DEH_Link(voxname_upper, &skinvoices[numskinvoices].info, &voicenames); + strlcpy(skin->voices[skin->numvoices].name, name, sizeof(skin->voices[skin->numvoices].name)); + strlwr(skin->voices[skin->numvoices].name); - *out = numskinvoices++; + skin->voices[skin->numvoices].id = skin->numvoices; + + CONS_Printf("Voice %s allocated for skin %s.\n",skin->voices[skin->numvoices].name,skin->name); + + *out = skin->numvoices++; return 0; } @@ -1334,7 +1640,9 @@ void R_AddSkins(UINT16 wadnum) // set defaults skin = &skins[numskins]; - skin_voice = &skinvoices[numskinvoices]; + skin_voice = &skin->voices[0]; + allocvoice = skin_voice; + Sk_SetDefaultValue(skin, skin_voice); skin->wadnum = wadnum; realname = false; @@ -1364,7 +1672,6 @@ void R_AddSkins(UINT16 wadnum) if (skinnum == -1) { STRBUFCPY(skin->name, value); - skin->voice->parent = (UINT16)(skin - skins); } // the skin name must uniquely identify a single skin // if the name is already used I make the name 'namex' @@ -1382,7 +1689,6 @@ void R_AddSkins(UINT16 wadnum) // I'm lazy so if NEW name is already used I leave the 'skin x' // default skin name set in Sk_SetDefaultValue STRBUFCPY(skin->name, value2); - skin->voice->parent = (UINT16)(skin - skins); } Z_Free(value2); } @@ -1409,15 +1715,11 @@ void R_AddSkins(UINT16 wadnum) // Add voice INT32 dummy; - if (R_AllocKartVoice(true, skin->name, &dummy) < 0) + if (R_FindIDForVoice(skin, "default") == MAXSKINVOICES) { // We couldn't allocate our own skin's voice?! I_Error("Failed to allocate voice for skin %s\n", skin->name); } - else - { - CONS_Printf(M_GetText("%d voices allocated.\n"), numskinvoices); - } // Add sprites R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); @@ -1463,6 +1765,7 @@ void R_PatchSkins(UINT16 wadnum) size_t size; skin_t *skin; boolean noskincomplain, realname; + sfxenum_t *array_target; // // search for all skin patch markers in pwad @@ -1505,6 +1808,8 @@ void R_PatchSkins(UINT16 wadnum) value = strtok(NULL, "\r\n= "); + array_target = NULL; + if (!value) I_Error("R_PatchSkins: syntax error in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); @@ -1515,7 +1820,10 @@ void R_PatchSkins(UINT16 wadnum) strlwr(value); skinnum = R_SkinAvailable(value); if (skinnum != -1) + { skin = &skins[skinnum]; + allocvoice = &skins[skinnum].voices[0]; + } else { CONS_Debug(DBG_SETUP, "R_PatchSkins: unknown skin name in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); @@ -1573,6 +1881,8 @@ void R_PatchSkins(UINT16 wadnum) } else if (!R_ProcessPatchableFields(skin, stoken, value)) CONS_Debug(DBG_SETUP, "R_PatchSkins: Unknown keyword '%s' in P_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); + + } if (!skin) diff --git a/src/r_skins.h b/src/r_skins.h index 5dacf2b7c..88813c902 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -38,26 +38,18 @@ extern "C" { extern consvar_t cv_skinselectstyle, cv_skinselectsort; -typedef enum -{ - KSKVC_FIRSTSLOT = 0, - KSKVC_LASTSLOT = 65535, - MAXSKINVOICES -} kartvoicetype_e; - -#define MAXVOICEFREESLOTS (MAXSKINVOICES - KSKVC_FIRSTFREESLOT) +#define MAXSKINVOICES 16 // 16 voices * 4096 skins = 65536 voices in total :^) // Player voice struct struct kartvoice_t { UINT32 id; - // Dehacked info for the voice's name (hash and pointer within the voicenames array). - dehinfo_t info; + // The voice's name. + char name[VOICENAMESIZE+1]; - // UINT16 ID of the parent skin this voice belongs to. - // Checked when the voice is assigned to prevent erroneous dubs. - UINT16 parent; + // Display name, for things such as menus + char realname[VOICENAMESIZE+1]; sfxenum_t win; sfxenum_t lose; @@ -98,7 +90,9 @@ struct skin_t // Pointer to a skin's voice, providing specific sounds per-skin. // If an mobj_t doesn't have a voice, it defaults to this value, given a skin is in use. - kartvoice_t *voice; + kartvoice_t voices[MAXSKINVOICES]; + + UINT16 numvoices; // Proxy value for the (now removed) soundsid array. // This is ONLY used in a Lua context. @@ -133,7 +127,7 @@ enum skinmenusortoption extern INT32 numskins, numskinvoices; extern skin_t skins[MAXSKINS]; extern INT32 skinsorted[MAXSKINS]; -extern kartvoice_t skinvoices[MAXSKINVOICES]; +//extern kartvoice_t skinvoices[MAXSKINVOICES]; extern CV_PossibleValue_t Forceskin_cons_t[]; @@ -155,11 +149,11 @@ 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); -INT32 R_AllocKartVoice(boolean skinalloc, const char* name, INT32 *out); -void R_ClearVoice(kartvoicetype_e voicetype); +INT32 R_AllocKartVoice(skin_t *skin, const char* name, INT32 *out); +void R_ClearVoice(skin_t *skin, INT32 voicetype); void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound); -kartvoicetype_e R_FindIDForVoice(const char *voicename); +INT32 R_FindIDForVoice(skin_t *skin, const char *voicename); void SetPlayerVoice(INT32 playernum, const char *voicename); void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum); sfxenum_t P_GetKartVoiceSFX(kartvoice_t *voice, INT32 skinsound); From fd3ced710b61e39d69232d86d97322446d3e527c Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 29 Nov 2025 21:54:44 -0500 Subject: [PATCH 30/44] Readd skin voices to Lua Featuring more reroute nonsense --- src/lua_baselib.c | 1 + src/lua_libs.h | 1 + src/lua_skinlib.c | 46 ++++++++++++++++++++++++++++++++++++++-------- src/r_skins.h | 2 +- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 817de91f0..195720491 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -182,6 +182,7 @@ static const struct { {META_SOUNDSID, "skin_t.soundsid"}, {META_SKINSPRITES, "skin_t.sprites"}, {META_SKINSPRITESLIST, "skin_t.sprites[]"}, + {META_SKINVOICES, "skin_t.voices"}, {META_VERTEX, "vertex_t"}, {META_LINE, "line_t"}, diff --git a/src/lua_libs.h b/src/lua_libs.h index 1b690f191..e1da50bc0 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -53,6 +53,7 @@ extern lua_State *gL; #define META_SOUNDSID "SKIN_T*SOUNDSID" #define META_SKINSPRITES "SKIN_T*SPRITES" #define META_SKINSPRITESLIST "SKIN_T*SPRITES[]" +#define META_SKINVOICES "SKIN_T*VOICES" #define META_VERTEX "VERTEX_T*" #define META_LINE "LINE_T*" diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index 01a483057..899a7151c 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -39,7 +39,7 @@ enum skin { skin_highresscale, skin_rivals, skin_soundsid, - skin_voice, + skin_voices, skin_sprites }; static const char *const skin_opt[] = { @@ -61,7 +61,7 @@ static const char *const skin_opt[] = { "highresscale", "rivals", "soundsid", - "voice", + "voices", "sprites", NULL }; @@ -141,13 +141,12 @@ static int skin_get(lua_State *L) // This would be pretty cool to push return UNIMPLEMENTED; case skin_soundsid: - LUA_PushUserdata(L, skin->soundsid, META_SOUNDSID); + LUA_PushUserdata(L, &skin->soundsid[0], META_SOUNDSID); break; - case skin_voice: + case skin_voices: // ...uuuuuuughhhhh... - return UNIMPLEMENTED; - //LUA_PushUserdata(L, skin->voice, META_VOICE); - //break; + LUA_PushUserdata(L, &skin->soundsid[1], META_SKINVOICES); + break; case skin_sprites: LUA_PushUserdata(L, skin->sprites, META_SKINSPRITES); break; @@ -250,7 +249,7 @@ static int soundsid_get(lua_State *L) { // soundsid is a route to the voice struct. That's how we're doing this. sfxenum_t *soundsid = *((sfxenum_t **)luaL_checkudata(L, 1, META_SOUNDSID)); - skin_t *s = (skin_t*)((char*)soundsid - offsetof(skin_t, soundsid)); + skin_t *s = (skin_t*)((char*)soundsid - offsetof(skin_t, soundsid[0])); skinsound_t i = luaL_checkinteger(L, 2); @@ -268,6 +267,29 @@ static int soundsid_num(lua_State *L) return 1; } +// voices, i -> voices[i] +static int skinvoices_get(lua_State *L) +{ + // Use soundsid to route to the voice array so that voices[0] isn't considered THE VOICE ARRAY + sfxenum_t *svoices = *((sfxenum_t **)luaL_checkudata(L, 1, META_SKINVOICES)); + skin_t *s = (skin_t*)((char*)svoices - offsetof(skin_t, soundsid[1])); + + skinsound_t i = luaL_checkinteger(L, 2); + + if (i >= s->numvoices) + return luaL_error(L, "skin.voices[] index %s out of range (0 - %d)", va("%u", (UINT32)i), s->numvoices - 1); + + LUA_PushUserdata(L, &s->voices[i], META_VOICE); + return 1; +} + +// #voices -> NUMSKINSOUNDS +static int skinvoices_num(lua_State *L) +{ + lua_pushinteger(L, MAXSKINVOICES); + return 1; +} + enum spritesopt { numframes = 0 }; @@ -346,6 +368,14 @@ int LUA_SkinLib(lua_State *L) lua_setfield(L, -2, "__index"); lua_pop(L,1); + luaL_newmetatable(L, META_SKINVOICES); + lua_pushcfunction(L, skinvoices_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, skinvoices_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + lua_newuserdata(L, 0); lua_createtable(L, 0, 2); lua_pushcfunction(L, lib_getSkin); diff --git a/src/r_skins.h b/src/r_skins.h index 88813c902..519ab06d3 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -97,7 +97,7 @@ struct skin_t // Proxy value for the (now removed) soundsid array. // This is ONLY used in a Lua context. // It doesn't actually have anything and will never actually have anything. - skinsound_t soundsid[1]; + skinsound_t soundsid[2]; // contains super versions too spritedef_t sprites[NUMPLAYERSPRITES*2]; From 500ccd40bb13e0ce476e4bff30901ded7801e252 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 29 Nov 2025 21:57:48 -0500 Subject: [PATCH 31/44] Remove readkartvoice --- src/r_skins.c | 58 --------------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/src/r_skins.c b/src/r_skins.c index 1724e92eb..6bca79b0f 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -1183,64 +1183,6 @@ static UINT8 reallygrossvoicesfxadder(INT32 skinsound, const char *value, kartvo static kartvoice_t *allocvoice; -static void readkartvoice(skin_t *skin, char *stoken, char *value, kartvoice_t *voice) -{ - if (!stricmp(stoken, "voicename")) - { - // Allocate the actual voice, - } - else if (!stricmp(stoken, "GLOAT")) - { - reallygrossvoicesfxadder(SKSKPOWR, value, voice); - } - else if (!stricmp(stoken, "WIN")) - { - reallygrossvoicesfxadder(SKSKWIN, value, voice); - } - else if (!stricmp(stoken, "LOSE")) - { - reallygrossvoicesfxadder(SKSKLOSE, value, voice); - } - else if (!stricmp(stoken, "SLOW") || !stricmp(stoken, "OVERTAKE") || !stricmp(stoken, "PASS")) - { - reallygrossvoicesfxadder(SKSKSLOW, value, voice); - } - else if (!stricmp(stoken, "PAIN1") || !stricmp(stoken, "HURT1") || !stricmp(stoken, "DAMAGE1")) - { - reallygrossvoicesfxadder(SKSKPAN1, value, voice); - } - else if (!stricmp(stoken, "PAIN2") || !stricmp(stoken, "HURT2") || !stricmp(stoken, "DAMAGE2")) - { - reallygrossvoicesfxadder(SKSKPAN2, value, voice); - } - else if (!stricmp(stoken, "ATTACK")) - { - // Needs to be handled like rival allocation - } - else if (!stricmp(stoken, "ATTACK1")) - { - reallygrossvoicesfxadder(SKSKATK1, value, voice); - } - else if (!stricmp(stoken, "ATTACK2")) - { - reallygrossvoicesfxadder(SKSKATK2, value, voice); - } - else if (!stricmp(stoken, "BOOST1")) - { - reallygrossvoicesfxadder(SKSKBST1, value, voice); - } - else if (!stricmp(stoken, "BOOST2")) - { - reallygrossvoicesfxadder(SKSKBST2, value, voice); - } - else if (!stricmp(stoken, "HITEM") || !stricmp(stoken, "HITCONFIRM")) - { - reallygrossvoicesfxadder(SKSKHITM, value, voice); - } - - return -1; -} - // returns whether found appropriate property static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) { From cd6b73f84f654f155b04273c7e222eebe83f4935 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sat, 29 Nov 2025 21:59:13 -0500 Subject: [PATCH 32/44] More r_skins cleanup --- src/r_skins.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/r_skins.c b/src/r_skins.c index 6bca79b0f..508eacb86 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -720,7 +720,6 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) INT32 R_FindIDForVoice(skin_t *skin, const char* voicename) { INT32 i; - const char *checkvoicename; // Scan through our allocated skin voices for the wanted voice. @@ -1091,7 +1090,6 @@ static void R_IHateThatHedgehog(UINT16 wadnum) strcpy(skin->name, "sonic"); // Add voice - INT32 dummy; if (R_FindIDForVoice(skin, "default") == MAXSKINVOICES) { // Hey guy, take care! @@ -1656,7 +1654,6 @@ void R_AddSkins(UINT16 wadnum) free(buf2); // Add voice - INT32 dummy; if (R_FindIDForVoice(skin, "default") == MAXSKINVOICES) { // We couldn't allocate our own skin's voice?! @@ -1707,7 +1704,6 @@ void R_PatchSkins(UINT16 wadnum) size_t size; skin_t *skin; boolean noskincomplain, realname; - sfxenum_t *array_target; // // search for all skin patch markers in pwad @@ -1750,8 +1746,6 @@ void R_PatchSkins(UINT16 wadnum) value = strtok(NULL, "\r\n= "); - array_target = NULL; - if (!value) I_Error("R_PatchSkins: syntax error in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); From 8a34029f173a3ef07e73d980f84367b4a42fabc9 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 30 Nov 2025 15:55:32 -0500 Subject: [PATCH 33/44] We've seriously been feeding the compiler the skin's NAME? --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9d9e39e10..3328816a4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1921,7 +1921,7 @@ static void SendNameAndColor(UINT8 n) } else if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin)) { - prevskin = skins[cv_skin[n].value].name; + prevskin = cv_skin[n].value; cv_skin[n].value = foundskin; SetPlayerSkin(playernum, cv_skin[n].string); From 136b2cce545663f4e88732c43a3b12f001067451 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 30 Nov 2025 15:59:55 -0500 Subject: [PATCH 34/44] vid -> voxid patrick_mouth.gif --- src/d_netcmd.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3328816a4..eed2c2237 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1899,7 +1899,7 @@ static void SendNameAndColor(UINT8 n) // If you're not in a netgame, merely update the skin, color, and name. if (!netgame) { - INT32 foundskin, vid; + INT32 foundskin, voxid; CleanupPlayerName(playernum, cv_playername[n].zstring); strcpy(player_names[playernum], cv_playername[n].zstring); @@ -1932,9 +1932,9 @@ static void SendNameAndColor(UINT8 n) { if (cv_voice[n].string) { - vid = R_FindIDForVoice(&skins[player->skin], cv_voice[n].string); + voxid = R_FindIDForVoice(&skins[player->skin], cv_voice[n].string); - if (vid == MAXSKINVOICES) + if (voxid == MAXSKINVOICES) { // No dice; use the default voice. valuevoice = &skins[player->skin].voices[0]; @@ -1942,7 +1942,7 @@ static void SendNameAndColor(UINT8 n) else { - valuevoice = &skins[player->skin].voices[vid]; + valuevoice = &skins[player->skin].voices[voxid]; } } else @@ -1972,9 +1972,9 @@ static void SendNameAndColor(UINT8 n) { if (cv_voice[n].string) { - vid = R_FindIDForVoice(&skins[player->skin], cv_voice[n].string); + voxid = R_FindIDForVoice(&skins[player->skin], cv_voice[n].string); - if (vid == MAXSKINVOICES) + if (voxid == MAXSKINVOICES) { // No dice; use the default voice. valuevoice = &skins[player->skin].voices[0]; @@ -1982,7 +1982,7 @@ static void SendNameAndColor(UINT8 n) else { - valuevoice = &skins[player->skin].voices[vid]; + valuevoice = &skins[player->skin].voices[voxid]; } } else From aebe13c2d61032a5314f05a644d9eec44647067a Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 30 Nov 2025 22:15:16 +0100 Subject: [PATCH 35/44] Whole-diff garbage collection --- src/d_netcmd.c | 8 +- src/deh_soc.c | 184 ---------------------------------------------- src/dehacked.c | 27 +------ src/g_demo.c | 2 - src/info.c | 2 +- src/lua_baselib.c | 1 - src/r_skins.c | 12 --- src/r_skins.h | 4 +- 8 files changed, 7 insertions(+), 233 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index eed2c2237..9feeba5b2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1407,7 +1407,7 @@ static void Skin_FindRealNameSkin(consvar_t *cvar) } } -static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voice, boolean forceme, boolean silent) +static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voice, boolean silent) { // Not the best way to implements this but it will do. @@ -2066,7 +2066,7 @@ static void SendNameAndColor(UINT8 n) { CV_StealthSet(&cv_skin[n], skins[player->skin].name); - if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, false, true)) + if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, true)) { // Our voice is no longer valid, set it to that of our skin's. strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); @@ -2100,7 +2100,7 @@ static void SendNameAndColor(UINT8 n) if (!permamute) { - if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, false, true)) + if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, true)) { // In the chance our current voice isn't valid, // rescan our current voice and send that over the network. @@ -7550,7 +7550,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) } } - Skin_FindValidDub(&cv_voice[pnum], players[pid].skin, myvoice, false, false); + Skin_FindValidDub(&cv_voice[pnum], players[pid].skin, myvoice, false); if (!Playing()) return; // do whatever you want diff --git a/src/deh_soc.c b/src/deh_soc.c index 62990b4cc..c128b1691 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -32,7 +32,6 @@ #include "r_picformats.h" #include "r_things.h" // R_Char2Frame #include "r_sky.h" -#include "r_skins.h" // R_AllocKartVoice #include "fastcmp.h" #include "lua_script.h" // Reluctantly included for LUA_EvalMath #include "d_clisrv.h" @@ -4242,189 +4241,6 @@ void readkartresult(MYFILE *f, kartresult_t *result) #undef WARN #undef WARN0 -/* -#define WARN(str, ...) deh_warning("KartVoice %s: " str, voice->name, __VA_ARGS__) -#define WARN0(str) deh_warning("KartVoice %s: " str, voice->name) - -// Necessary for sound adding, so we can gather flags and singularity data. -static sfxenum_t skinsoundreroute[] = { - sfx_kwin, - sfx_klose, - sfx_khurt1, - sfx_khurt2, - sfx_kattk1, // Offense item taunt - sfx_kattk2, - sfx_kbost1, // Boost item taunt - sfx_kbost2, - sfx_kslow, // Overtake taunt - sfx_khitem, // Hit confirm taunt - sfx_kgloat, // Power item taunt -}; - -// Extremely shitty and lifted almost entirely from r_skins.c -// Sets the sound effect value for a voice, given a valid skinsound value is passed. -static UINT8 reallygrossvoicesfxadder(INT32 skinsound, const char *value, kartvoice_t *voice) -{ - sfxenum_t i, j, reroute; - char printval[6]; - - // Remove the prefix. (We can affect this directly since we're not going to use it again.) - if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS* - value += 2; - else // sfx_* - value += 4; - - strncpy(printval, value, 6); - - reroute = skinsoundreroute[skinsound]; - - // Automatically allocate any defined voice sounds. - // Freeslotted voice clips won't ever belong to a skin. - - i = sfx_None; - - // Find if this sound already exists first. - for (j = 0; j < NUMSFX; j++) - { - if (!S_sfx[j].name) - continue; - - if (!stricmp(S_sfx[j].name, value)) - { - // Found an existing sound. - i = j; - break; - } - } - - // This sound doesn't seem to exist, so let's add a new one. - if (i == sfx_None) - { - CONS_Printf("Sound sfx_%s allocated.\n", value); - i = S_AddSoundFx(value, S_sfx[reroute].singularity, S_sfx[reroute].flags, false); - } - - if (i != sfx_None) - { - if (voice) - { - Sk_SetSkinVoiceValue(voice, skinsound, i); - return 0; - } - } - - WARN0("Ran out of free SFX slots!"); - return 1; -} - -void readkartvoice(MYFILE *f, kartvoice_t *voice) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - INT32 i; - - do if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = (tmp += 2); - strupr(word2); - - if (fastcmp(word, "SKIN")) - { - i = R_SkinAvailable(word2); - - if (i == -1) - { - strlwr(word2); - WARN("skin '%s' does not exist or cannot be used", word2); - } - else - { - voice->parent = (UINT16)i; - } - } - else if (fastcmp(word, "GLOAT")) - { - reallygrossvoicesfxadder(SKSKPOWR, word2, voice); - } - else if (fastcmp(word, "WIN")) - { - reallygrossvoicesfxadder(SKSKWIN, word2, voice); - } - else if (fastcmp(word, "LOSE")) - { - reallygrossvoicesfxadder(SKSKLOSE, word2, voice); - } - else if (fastcmp(word, "SLOW") || fastcmp(word, "OVERTAKE") || fastcmp(word, "PASS")) - { - reallygrossvoicesfxadder(SKSKSLOW, word2, voice); - } - else if (fastcmp(word, "PAIN1") || fastcmp(word, "HURT1") || fastcmp(word, "DAMAGE1")) - { - reallygrossvoicesfxadder(SKSKPAN1, word2, voice); - } - else if (fastcmp(word, "PAIN2") || fastcmp(word, "HURT2") || fastcmp(word, "DAMAGE2")) - { - reallygrossvoicesfxadder(SKSKPAN2, word2, voice); - } - else if (fastcmp(word, "ATTACK1")) - { - reallygrossvoicesfxadder(SKSKATK1, word2, voice); - } - else if (fastcmp(word, "ATTACK2")) - { - reallygrossvoicesfxadder(SKSKATK2, word2, voice); - } - else if (fastcmp(word, "BOOST1")) - { - reallygrossvoicesfxadder(SKSKBST1, word2, voice); - } - else if (fastcmp(word, "BOOST2")) - { - reallygrossvoicesfxadder(SKSKBST2, word2, voice); - } - else if (fastcmp(word, "HITEM") || fastcmp(word, "HITCONFIRM")) - { - reallygrossvoicesfxadder(SKSKHITM, word2, voice); - } - else - WARN("unknown word '%s'", word); - } - while (!myfeof(f)); // finish when the line is empty - - // Set our ID afterwards. - //voice->id = (UINT32)(voice - skinvoices); - - Z_Free(s); -} - -#undef WARN -#undef WARN0*/ - // // // diff --git a/src/dehacked.c b/src/dehacked.c index f582be246..dd0fd670e 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -390,6 +390,7 @@ INT32 DEH_ReadFreeslot(const char *type, const char *word, INT32 *out) DEH_Link(word, &kartitems[numkartitems].info, &kartitemnames); K_RegisterItem(numkartitems); *out = numkartitems++; + return 0; } return -2; @@ -786,32 +787,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) result = K_RegisterResult(word2, alternate); readkartresult(f, result); } - /*else if (fastncmp(word, "KARTVOICE", 9)) (Deprecated; getting merged into skins) - { - if (i == 0 && word2[0] != '0') // If word2 isn't a number - i = DEH_FindKartVoice(word2); // find a voice by name - - if (i == MAXSKINVOICES) - { - // Found nothing; auto-allocate our voice. - const INT32 allocresult = R_AllocKartVoice(false, word2, &i); - - if (allocresult < 0) - { - deh_warning("Voice '%s' could not be allocated.", word2); - ignorelines(f); - } - } - - if (i >= 0 && i < numskinvoices) - readkartvoice(f, &skinvoices[i]); - else - { - // zero-based, but let's start at 1 - deh_warning("KartVoice number %d out of range (0 - %d)", i, numskinvoices); - ignorelines(f); - } - }*/ else if (fastcmp(word, "WEATHER") || fastcmp(word, "PRECIP") || fastcmp(word, "PRECIPITATION")) { if (i == 0 && word2[0] != '0') // If word2 isn't a number diff --git a/src/g_demo.c b/src/g_demo.c index a15fc2028..ae31ec6a3 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -41,7 +41,6 @@ #include "lua_hook.h" #include "p_saveg.h" // savebuffer_t #include "g_party.h" -#include "deh_tables.h" // SRB2Kart #include "d_netfil.h" // nameonly @@ -55,7 +54,6 @@ #include "k_color.h" #include "k_follower.h" #include "k_grandprix.h" -//#include "strbuf.h" static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; consvar_t cv_recordmultiplayerdemos = CVAR_INIT ("netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL); diff --git a/src/info.c b/src/info.c index 4a48dc8e3..28513667c 100644 --- a/src/info.c +++ b/src/info.c @@ -240,7 +240,7 @@ void P_ResetData(INT32 flags) for (i = 0; i < MN_FIRSTFREESLOT; i++, name += strlen(name)+1) DEH_Link(name, &menudefs[i].info, &menunames); } - + // kartitems if (init) { diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 195720491..2c1d8dc0f 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -27,7 +27,6 @@ #include "y_inter.h" #include "hu_stuff.h" // HU_AddChatText #include "console.h" -#include "deh_tables.h" // DEH_FindKartVoice #include "k_kart.h" // SRB2Kart #include "k_battle.h" #include "k_boss.h" diff --git a/src/r_skins.c b/src/r_skins.c index 508eacb86..80913aa67 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -28,7 +28,6 @@ #include "dehacked.h" // get_number (for thok) #include "m_cond.h" #include "d_main.h" // MAINWAD_CHARS -#include "deh_tables.h" // Voice freeslotting #if 0 #include "k_kart.h" // K_KartResetPlayerColor #endif @@ -40,10 +39,8 @@ #include "discord.h" INT32 numskins = 0; -INT32 numskinvoices = 0; skin_t skins[MAXSKINS]; INT32 skinsorted[MAXSKINS]; -//kartvoice_t skinvoices[MAXSKINVOICES]; // FIXTHIS: don't work because it must be inistilised before the config load //#define SKINVALUES @@ -298,13 +295,6 @@ sfxenum_t P_GetKartVoiceSFX(kartvoice_t *voice, INT32 skinsound) return sfx_thok; } -void R_ClearVoice(skin_t *skin, INT32 voicetype) -{ - kartvoice_t *voice = &skin->voices[voicetype]; - - memset(voice, 0, sizeof(kartvoice_t)); -} - static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice) { INT32 i; @@ -1817,8 +1807,6 @@ void R_PatchSkins(UINT16 wadnum) } else if (!R_ProcessPatchableFields(skin, stoken, value)) CONS_Debug(DBG_SETUP, "R_PatchSkins: Unknown keyword '%s' in P_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); - - } if (!skin) diff --git a/src/r_skins.h b/src/r_skins.h index 519ab06d3..f7d9a323c 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -124,10 +124,9 @@ enum skinmenusortoption }; /// Externs -extern INT32 numskins, numskinvoices; +extern INT32 numskins; extern skin_t skins[MAXSKINS]; extern INT32 skinsorted[MAXSKINS]; -//extern kartvoice_t skinvoices[MAXSKINVOICES]; extern CV_PossibleValue_t Forceskin_cons_t[]; @@ -150,7 +149,6 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player); UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe); INT32 R_AllocKartVoice(skin_t *skin, const char* name, INT32 *out); -void R_ClearVoice(skin_t *skin, INT32 voicetype); void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound); INT32 R_FindIDForVoice(skin_t *skin, const char *voicename); From 32cb39b6c848e302c951ff0c1ae3b88e559172cb Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Sun, 30 Nov 2025 22:49:35 +0100 Subject: [PATCH 36/44] Cleanup, curbing the paranoia --- src/d_netcmd.c | 214 +++++++++---------------------------------------- src/r_skins.c | 113 +++----------------------- 2 files changed, 47 insertions(+), 280 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9feeba5b2..562c63839 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1407,69 +1407,6 @@ static void Skin_FindRealNameSkin(consvar_t *cvar) } } -static UINT8 Skin_FindValidDub(consvar_t *cvar, UINT16 skin_id, kartvoice_t *voice, boolean silent) -{ - // Not the best way to implements this but it will do. - - int i; - const char *value = cvar->string; - kartvoice_t *myvoice = voice; - char cvarname[VOICENAMESIZE+1]; - - if (!Playing()) - { - // Don't bother. - return 0; - } - - if (!myvoice) - { - myvoice = &skins[skin_id].voices[0]; - - if (!myvoice) - { - // ...how?! - return 0; - } - } - - // Scan through our allocated skin voices for the wanted voice. - strlcpy(cvarname, value, sizeof(cvarname)); - strupr(cvarname); - - i = R_FindIDForVoice(&skins[skin_id], cvarname); - - if (i != MAXSKINVOICES) - { - // Voices are tied to skins; if, SOMEHOW, this voice has a *different* parent, - // something has gone terribly wrong. - - // Change the cvar to be the value of the name. - strlcpy(cvarname, skins[skin_id].voices[i].name, sizeof(cvarname)); - strlwr(cvarname); - - CV_StealthSet(cvar, cvarname); - cvar->value = skins[skin_id].voices[i].id; - - // We've found the voice we were looking for! - return 0; - } - - // Found diddly-squat, say as much. - if (!silent) - CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist for this skin.\n"), value); - - // Reassign the voice cvar to our current voice. - strlcpy(cvarname, myvoice->name, sizeof(cvarname)); - strlwr(cvarname); - - CV_StealthSet(cvar, cvarname); - cvar->value = myvoice->id; - - // Signal to networking that we failed to set a voice. - return 1; -} - /** 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, @@ -1829,11 +1766,7 @@ static void SendNameAndColor(UINT8 n) kartvoice_t *valuevoice; INT32 prevskin; - // We have no voice, but don't want to topple the house of cards. - boolean permamute = false; - char buf[MAXPLAYERNAME+12]; - char voicebuf[VOICENAMESIZE+1]; char *p; if (splitscreen < n) @@ -1873,13 +1806,8 @@ static void SendNameAndColor(UINT8 n) if (!voice) { + // ...how?! voice = &skins[player->skin].voices[0]; - - if (!voice) - { - // ...how?! - permamute = true; - } } if (!strcmp(cv_playername[n].string, player_names[playernum]) @@ -1887,7 +1815,7 @@ static void SendNameAndColor(UINT8 n) && !strcmp(cv_skin[n].string, skins[player->skin].name) && cv_follower[n].value == player->followerskin && cv_followercolor[n].value == player->followercolor - && (!permamute && !stricmp(cv_voice[n].string, voice->name))) + && !stricmp(cv_voice[n].string, voice->name)) return; player->availabilities = R_GetSkinAvailabilities(); @@ -1951,11 +1879,7 @@ static void SendNameAndColor(UINT8 n) valuevoice = &skins[player->skin].voices[0]; } - strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = (UINT16)valuevoice->id; + CV_StealthSet(&cv_voice[n], valuevoice->name); } } else @@ -1991,21 +1915,14 @@ static void SendNameAndColor(UINT8 n) valuevoice = &skins[player->skin].voices[0]; } - strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = (UINT16)valuevoice->id; + CV_StealthSet(&cv_voice[n], valuevoice->name); } } // Need to update voices after the fact. if (!cv_voice[n].string) { - strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); + CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name); } SetPlayerVoice(playernum, cv_voice[n].string); @@ -2014,11 +1931,7 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = (UINT16)valuevoice->id; + CV_StealthSet(&cv_voice[n], valuevoice->name); } else { @@ -2028,20 +1941,12 @@ static void SendNameAndColor(UINT8 n) if (valuevoice) { - strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = (UINT16)valuevoice->id; + CV_StealthSet(&cv_voice[n], valuevoice->name); } else { // ...still nothing? - strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = 0; + CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name); } } @@ -2066,14 +1971,10 @@ static void SendNameAndColor(UINT8 n) { CV_StealthSet(&cv_skin[n], skins[player->skin].name); - if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, true)) + if (R_FindIDForVoice(&skins[player->skin], cv_voice[n].string) == MAXSKINVOICES) { // Our voice is no longer valid, set it to that of our skin's. - strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = skins[player->skin].voices[0].id; + CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name); } } @@ -2092,51 +1993,33 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) { - strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); + CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name); } - if (!permamute) + if (R_FindIDForVoice(&skins[player->skin], cv_voice[n].string) == MAXSKINVOICES) { - if (Skin_FindValidDub(&cv_voice[n], cv_skin[n].value, voice, true)) + // In the chance our current voice isn't valid, + // rescan our current voice and send that over the network. + valuevoice = P_GetMobjVoice(player->mo); + + if (valuevoice) { - // In the chance our current voice isn't valid, - // rescan our current voice and send that over the network. - valuevoice = P_GetMobjVoice(player->mo); + CV_StealthSet(&cv_voice[n], valuevoice->name); + } + else + { + // Get the voice from our ID. + // No need to compare parents. + valuevoice = &skins[player->skin].voices[player->voice_id]; if (valuevoice) { - strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = (UINT16)valuevoice->id; + CV_StealthSet(&cv_voice[n], valuevoice->name); } else { - // Get the voice from our ID. - // No need to compare parents. - valuevoice = &skins[player->skin].voices[player->voice_id]; - - if (valuevoice) - { - strlcpy(voicebuf, valuevoice->name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = (UINT16)valuevoice->id; - } - else - { - // ...still nothing? - strlcpy(voicebuf, skins[player->skin].voices[0].name, sizeof(voicebuf)); - strlwr(voicebuf); - - CV_StealthSet(&cv_voice[n], voicebuf); - cv_voice[n].value = 0; - } + // ...still nothing? + CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name); } } } @@ -2157,7 +2040,6 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { player_t *p = &players[playernum]; char name[MAXPLAYERNAME+1]; - char voicename[VOICENAMESIZE+1]; UINT16 color, followercolor; UINT16 skin, voice; INT32 follower; @@ -2268,12 +2150,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (localplayer != -1) { CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name); - - strlcpy(voicename, skins[forcedskin].voices[0].name, sizeof(voicename)); - strlwr(voicename); - - CV_StealthSet(&cv_voice[localplayer], voicename); - cv_voice[localplayer].value = skins[forcedskin].voices[0].id; + CV_StealthSet(&cv_voice[localplayer], skins[forcedskin].voices[0].name); } // set voice @@ -2288,11 +2165,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (localplayer != -1) { - strlcpy(voicename, skins[p->skin].voices[p->voice_id].name, sizeof(voicename)); - strlwr(voicename); - - CV_StealthSet(&cv_voice[localplayer], voicename); - cv_voice[localplayer].value = skins[p->skin].voices[p->voice_id].id; + CV_StealthSet(&cv_voice[localplayer], skins[p->skin].voices[p->voice_id].name); } } @@ -7535,22 +7408,20 @@ static void Color4_OnChange(void) static void __voice_cvar_func(INT32 pid, UINT8 pnum) { - char cvarname[VOICENAMESIZE+1]; - kartvoice_t *myvoice = P_GetMobjVoice(players[pid].mo); if (!myvoice) { + // ...how?! myvoice = &skins[players[pid].skin].voices[0]; - - if (!myvoice) - { - // ...how?! - return; - } } - Skin_FindValidDub(&cv_voice[pnum], players[pid].skin, myvoice, false); + if (R_FindIDForVoice(&skins[players[pid].skin], cv_voice[pnum].string) == MAXSKINVOICES) + { + CONS_Alert(CONS_NOTICE, M_GetText("Voice \"%s\" does not exist for this skin.\n"), cv_voice[pnum].string); + CV_StealthSet(&cv_voice[pnum], myvoice->name); + return; + } if (!Playing()) return; // do whatever you want @@ -7563,11 +7434,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) if (!(cht_debug || devparm) && !(multiplayer || netgame) // In single player. && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y { - strlcpy(cvarname, myvoice->name, sizeof(cvarname)); - strlwr(cvarname); - CV_StealthSet(&cv_voice[pnum], cvarname); - - cv_voice[pnum].value = myvoice->id; + CV_StealthSet(&cv_voice[pnum], myvoice->name); return; } } @@ -7577,12 +7444,7 @@ static void __voice_cvar_func(INT32 pid, UINT8 pnum) else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your voice at the moment.\n")); - - strlcpy(cvarname, myvoice->name, sizeof(cvarname)); - strlwr(cvarname); - CV_StealthSet(&cv_voice[pnum], cvarname); - - cv_voice[pnum].value = myvoice->id; + CV_StealthSet(&cv_voice[pnum], myvoice->name); } } diff --git a/src/r_skins.c b/src/r_skins.c index 80913aa67..98d07057b 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -714,7 +714,7 @@ INT32 R_FindIDForVoice(skin_t *skin, const char* voicename) // Scan through our allocated skin voices for the wanted voice. // First pass: Check the actual name. - for (i = 0; i < MAXSKINVOICES; i++) + for (i = 0; i < skin->numvoices; i++) { if (strnicmp(voicename, skin->voices[i].name, VOICENAMESIZE) == 0) { @@ -724,7 +724,7 @@ INT32 R_FindIDForVoice(skin_t *skin, const char* voicename) } // Second pass: check the display name. - for (i = 0; i < MAXSKINVOICES; i++) + for (i = 0; i < skin->numvoices; i++) { // Whatever. Go, my code duplication! if (strnicmp(voicename, skin->voices[i].realname, VOICENAMESIZE) == 0) @@ -738,31 +738,10 @@ INT32 R_FindIDForVoice(skin_t *skin, const char* voicename) return MAXSKINVOICES; } -static INT32 R_FindIDForVoiceNum(skin_t *skin, UINT16 voicenum) -{ - INT32 i; - - // Scan through our allocated skin voices for the wanted voice. - for (i = 0; i < MAXSKINVOICES; i++) - { - // Whatever. Go, my code duplication! - if (skin->voices[i].id == voicenum) - { - // Found a voice. - return 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) +void SetPlayerVoice(INT32 playernum, const char *voicename) { - player_t* player = &players[playernum]; - - INT32 voxid = R_FindIDForVoice(&skins[player->skin], voicename); + INT32 voxid = R_FindIDForVoice(&skins[players[playernum].skin], voicename); if (voxid == MAXSKINVOICES) { @@ -770,93 +749,24 @@ void SetPlayerVoice(INT32 playernum, const char* voicename) voxid = 0; } - if (P_MobjWasRemoved(player->mo)) - { - // No object to give a voice to. - // Store the found ID in the player, at least. Object spawning will take care of - // assigning a voice. - player->voice_id = (UINT16)voxid; - - // Tell the demo that we've updated our voice_id as well. - demo_extradata[playernum] |= DXD_SKIN; - return; - } - - const kartvoice_t* myvoice = P_GetMobjVoice(player->mo); - - if (!strnicmp(voicename, myvoice->name, VOICENAMESIZE)) - { - // 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; - } - - // We've done the precautions to check for disallowed dubs in the cvar, just do a blind-read - // for the needed name. If we're down here, we've passed the safeguards and can assign a - // voice. - - if (voxid) - player->mo->voice = &skins[player->skin].voices[(UINT16)voxid]; - else - player->mo->voice = NULL; - - player->voice_id = (UINT16)(voxid); - - // Tell the demo that we've updated our voice_id as well. - demo_extradata[playernum] |= DXD_SKIN; + SetPlayerVoiceByNum(playernum, voxid); } // network code calls this when a 'voice change' is received void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) { player_t* player = &players[playernum]; - INT32 voxid = R_FindIDForVoiceNum(&skins[player->skin], voicenum); - if (voxid == MAXSKINVOICES) - { - // Couldn't find a voice. Default to the skin's default. - voxid = 0; - } - - if (P_MobjWasRemoved(player->mo)) - { - // No object to give a voice to. - // Store the found ID in the player, at least. Object spawning will take care of - // assigning a voice. - player->voice_id = (UINT16)voxid; - - // Tell the demo that we've updated our voice_id as well. - demo_extradata[playernum] |= DXD_SKIN; - return; - } - - const kartvoice_t* myvoice = P_GetMobjVoice(player->mo); - - if (myvoice->id == voicenum) + if (player->voice_id == 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; } - // We've done the precautions to check for disallowed dubs in the cvar, just do a blind-read - // for the needed name. If we're down here, we've passed the safeguards and can assign a - // voice. + player->voice_id = voicenum; - if (voxid) - player->mo->voice = &skins[player->skin].voices[(UINT16)voxid]; - else - player->mo->voice = NULL; - - player->voice_id = (UINT16)(voxid); + if (!P_MobjWasRemoved(player->mo)) + player->mo->voice = &skins[player->skin].voices[voicenum]; // Tell the demo that we've updated our voice_id as well. demo_extradata[playernum] |= DXD_SKIN; @@ -1503,11 +1413,6 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) // INT32 R_AllocKartVoice(skin_t *skin, const char* name, INT32 *out) { - char voxname_upper[VOICENAMESIZE]; - - strlcpy(voxname_upper, name, sizeof(voxname_upper)); - strupr(voxname_upper); - *out = R_FindIDForVoice(skin, name); if (*out != MAXSKINVOICES) return -1; // already allocated From 7838c2311f2fd250be342d1ea2177e410674da09 Mon Sep 17 00:00:00 2001 From: yamamama Date: Sun, 30 Nov 2025 19:57:55 -0500 Subject: [PATCH 37/44] Fix up some bugs introduced --- src/d_netcmd.c | 27 +++++++++++++++++++++++---- src/g_demo.c | 21 +++++++-------------- src/lua_mobjlib.c | 4 +++- src/p_mobj.c | 10 +++++++++- src/p_saveg.c | 2 +- src/r_skins.c | 28 ++++++++++++++++++++++------ src/r_skins.h | 2 ++ 7 files changed, 67 insertions(+), 27 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 039864f98..ab842f5e7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2001,10 +2001,10 @@ static void SendNameAndColor(UINT8 n) // Need to update voices after the fact. if (!cv_voice[n].string) { - CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name); + CV_StealthSet(&cv_voice[n], skins[cv_skin[n].value].voices[0].name); } - if (R_FindIDForVoice(&skins[player->skin], cv_voice[n].string) == MAXSKINVOICES) + if (R_FindIDForVoice(&skins[cv_skin[n].value], cv_voice[n].string) == MAXSKINVOICES) { // In the chance our current voice isn't valid, // rescan our current voice and send that over the network. @@ -2018,7 +2018,7 @@ static void SendNameAndColor(UINT8 n) { // Get the voice from our ID. // No need to compare parents. - valuevoice = &skins[player->skin].voices[player->voice_id]; + valuevoice = &skins[cv_skin[n].value].voices[player->voice_id]; if (valuevoice) { @@ -2027,11 +2027,24 @@ static void SendNameAndColor(UINT8 n) else { // ...still nothing? - CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name); + CV_StealthSet(&cv_voice[n], skins[cv_skin[n].value].voices[0].name); } } } + // After EVERYTHING, do one last check on our voice so we can set the value. + INT32 cvar_voxid = R_FindIDForVoice(&skins[cv_skin[n].value], cv_voice[n].string); + + if (cvar_voxid == MAXSKINVOICES) + { + // ...huh?! Reset to the default. + cv_voice[n].value = 0; + } + else + { + cv_voice[n].value = cvar_voxid; + } + // Finally write out the complete packet and send it off. WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME); WRITEUINT32(p, (UINT32)player->availabilities); @@ -7416,6 +7429,12 @@ static void Color4_OnChange(void) static void __voice_cvar_func(INT32 pid, UINT8 pnum) { + if (!numskins) + { + // There aren't even any skins yet! + return; + } + kartvoice_t *myvoice = P_GetMobjVoice(players[pid].mo); if (!myvoice) diff --git a/src/g_demo.c b/src/g_demo.c index 5a98929d9..c0ce6774a 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -1279,17 +1279,9 @@ void G_ReadDemoExtraData(void) players[p].voice_id = (UINT16)vce; } - if (skins[players[p].skin].voices[players[p].voice_id].id == 0) + if (!P_MobjWasRemoved(players[p].mo)) { - // Default voice for this skin. Just strip the object of its voice. - players[p].mo->voice = NULL; - } - else if (skins[players[p].skin].voices[players[p].voice_id].id != - skins[players[p].skin].voices[0].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! + // This player's mobj exists. So, attach the voice to the mobj! players[p].mo->voice = &skins[players[p].skin].voices[players[p].voice_id]; } @@ -2105,7 +2097,7 @@ void G_GhostTicker(void) { default: case GHC_RETURNSKIN: - g->mo->skin = g->oldmo.skin; + R_ApplySkin(g->mo, g->oldmo.skin); /* FALLTHRU */ case GHC_NORMAL: // Go back to skin color g->mo->color = g->oldmo.color; @@ -2166,7 +2158,7 @@ void G_GhostTicker(void) fmo->colorized = true; if (zt.followflags & FZT_SKIN) - fmo->skin = &skins[zt.follow.skin]; + R_ApplySkin(fmo, &skins[zt.follow.skin]); } if (fmo) { @@ -2569,7 +2561,7 @@ void G_ReadMetalTic(mobj_t *metal) follow->colorized = true; if (followtic & FZT_SKIN) - follow->skin = &skins[READUINT8(metal_p)]; + R_ApplySkin(follow, &skins[READUINT8(metal_p)]); } if (follow) { @@ -4124,7 +4116,8 @@ void G_AddGhost(char *defdemoname) gh->mo->tics = -1; // Set skin - gh->mo->skin = gh->oldmo.skin = ghskin; + R_ApplySkin(&gh->oldmo, ghskin); + R_ApplySkin(gh->mo, gh->oldmo.skin); // Set color gh->mo->color = ((skin_t*)gh->mo->skin)->prefcolor; diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 3c7d03c71..ec47bcfba 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -894,7 +894,9 @@ static int mobj_set(lua_State *L) if (fastcmp(skins[i].name, skin)) { if (!mo->player || R_SkinUsable(mo->player-players, i)) - mo->skin = &skins[i]; + { + R_ApplySkin(mo, &skins[i]); + } return 0; } return luaL_error(L, "mobj.skin '%s' not found!", skin); diff --git a/src/p_mobj.c b/src/p_mobj.c index d71f3cfa4..c7c226b14 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12378,7 +12378,7 @@ void P_SpawnPlayer(INT32 playernum) // set 'spritedef' override in mobj for player skins.. (see ProjectSprite) // (usefulness: when body mobj is detached from player (who respawns), // the dead body mobj retains the skin through the 'spritedef' override). - mobj->skin = &skins[p->skin]; + R_ApplySkin(mobj, &skins[p->skin]); P_SetupStateAnimation(mobj, mobj->state); if (p->voice_id) @@ -14979,12 +14979,20 @@ kartvoice_t *P_GetMobjVoice(const mobj_t *mo) { return mo->voice; } +#ifdef PARANOIA + else + { + I_Error("P_GetMobjVoice: This object has no voice!"); + return NULL; + } +#else else if (mo->skin) { return &((skin_t *)mo->skin)->voices[0]; } return NULL; +#endif } // diff --git a/src/p_saveg.c b/src/p_saveg.c index 7874caebd..a9dade76e 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2224,7 +2224,7 @@ static thinker_t *SyncMobjThinker(savebuffer_t *save, actionf_p1 thinker, thinke if (save->write) WRITEUINT16(save->p, (UINT16)((skin_t *)mobj->skin - skins)); else - mobj->skin = &skins[READUINT16(save->p)]; + R_ApplySkin(mobj, &skins[READUINT16(save->p)]); } SYNCF(MD2_COLOR, mobj->color); SYNCF(MD2_EXTVAL1, mobj->extravalue1); diff --git a/src/r_skins.c b/src/r_skins.c index 98d07057b..e3c6bc183 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -675,13 +675,8 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) if (player->mo) { - player->mo->skin = skin; + R_ApplySkin(player->mo, 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 } @@ -772,6 +767,27 @@ void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum) demo_extradata[playernum] |= DXD_SKIN; } +/** \brief Applies the skin and its default voice. + + \param mo (mobj_t) the given object the skin and voice are being assigned to + \param skin (skin_t) the skin to attach to the object. The voice is sourced from this skin +*/ +void R_ApplySkin(mobj_t *mo, skin_t *skin) +{ + mo->skin = skin; + mo->voice = &((skin_t *)mo->skin)->voices[0]; +} + +/** \brief Applies only the skin. + + \param mo (mobj_t) the given object the skin is being assigned to + \param skin (skin_t) the skin to attach to the object +*/ +void R_ApplySkinOnly(mobj_t *mo, skin_t *skin) +{ + mo->skin = skin; +} + // // 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 f7d9a323c..9c4bc2d47 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -144,6 +144,8 @@ UINT32 R_GetSkinAvailabilities(void); INT32 R_SkinAvailable(const char *name); void R_PatchSkins(UINT16 wadnum); void R_AddSkins(UINT16 wadnum); +void R_ApplySkin(mobj_t *mo, skin_t *skin); +void R_ApplySkinOnly(mobj_t *mo, skin_t *skin); UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player); UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe); From 58b4949c24e270554a466f7d68285ae63c8795dc Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Mon, 1 Dec 2025 21:37:00 +0100 Subject: [PATCH 38/44] Completely retire the old skinsound system Everything in Lua related to it is now locked behind compatmode Also fixed SFX allocations eating into skinsounds --- src/CMakeLists.txt | 1 + src/deh_soc.c | 6 +- src/deh_tables.c | 22 +- src/dehacked.c | 2 +- src/hardware/hw3sound.c | 1 + src/lua_infolib.c | 9 +- src/lua_skinlib.c | 19 +- src/r_skins.c | 437 ++++++++++------------------------------ src/r_skins.h | 6 +- src/s_sound.c | 23 +-- src/sounds.c | 56 +++-- src/sounds.h | 29 +-- 12 files changed, 184 insertions(+), 427 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46ff0ce45..d865141e3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -508,6 +508,7 @@ target_compile_options(SRB2SDL2 PRIVATE -Wno-error=constant-conversion -Wno-unused-but-set-variable -Wno-error=unused-but-set-variable + -Wshadow > # C++, GNU, Clang and Apple Clang diff --git a/src/deh_soc.c b/src/deh_soc.c index c128b1691..bf6eb89a4 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2544,7 +2544,9 @@ void readsound(MYFILE *f, INT32 num) } else if (fastcmp(word, "SKINSOUND")) { - S_sfx[num].skinsound = get_number(word2); + // ignore this for the sake of not needing an asset update + // this was only exposed in blan anyway + //S_sfx[num].skinsound = get_number(word2); } else if (fastcmp(word, "CAPTION") || fastcmp(word, "DESCRIPTION")) { @@ -4325,7 +4327,7 @@ sfxenum_t get_sfx(const char *word) if (S_sfx[i].name && fasticmp(word, S_sfx[i].name)) return i; deh_warning("Couldn't find sfx named 'SFX_%s'",word); - return -1; + return NUMSFX; } menutype_t get_menutype(const char *word) diff --git a/src/deh_tables.c b/src/deh_tables.c index d17947f2b..82658ee99 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1178,17 +1178,17 @@ struct int_const_s const INT_CONST[] = { // Customisable sounds for Skins, from sounds.h // SRB2kart - {"SKSKWIN",SKSKWIN}, // Win quote - {"SKSKLOSE",SKSKLOSE}, // Lose quote - {"SKSKPAN1",SKSKPAN1}, // Pain - {"SKSKPAN2",SKSKPAN2}, - {"SKSKATK1",SKSKATK1}, // Offense item taunt - {"SKSKATK2",SKSKATK2}, - {"SKSKBST1",SKSKBST1}, // Boost item taunt - {"SKSKBST2",SKSKBST2}, - {"SKSKSLOW",SKSKSLOW}, // Overtake taunt - {"SKSKHITM",SKSKHITM}, // Hit confirm taunt - {"SKSKPOWR",SKSKPOWR}, // Power item taunt + {"SKSKWIN",0}, // Win quote + {"SKSKLOSE",1}, // Lose quote + {"SKSKPAN1",2}, // Pain + {"SKSKPAN2",3}, + {"SKSKATK1",4}, // Offense item taunt + {"SKSKATK2",5}, + {"SKSKBST1",6}, // Boost item taunt + {"SKSKBST2",7}, + {"SKSKSLOW",8}, // Overtake taunt + {"SKSKHITM",9}, // Hit confirm taunt + {"SKSKPOWR",10}, // Power item taunt // 3D Floor/Fake Floor/FOF/whatever flags {"FOF_EXISTS",FOF_EXISTS}, ///< Always set, to check for validity. diff --git a/src/dehacked.c b/src/dehacked.c index dd0fd670e..967c06b5f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -219,7 +219,7 @@ INT32 DEH_ReadFreeslot(const char *type, const char *word, INT32 *out) if (fastcmp(type, "SFX")) { CONS_Printf("Sound sfx_%s allocated.\n", word); - i = S_AddSoundFx(word, false, 0, false); + i = S_AddSoundFx(word, false); if (i != sfx_None) { *out = i; diff --git a/src/hardware/hw3sound.c b/src/hardware/hw3sound.c index a958c62e0..ef578a92e 100644 --- a/src/hardware/hw3sound.c +++ b/src/hardware/hw3sound.c @@ -360,6 +360,7 @@ INT32 HW3S_I_StartSound(const void *origin_p, source3D_data_t *source_parm, chan sfx = &S_sfx[sfx_id]; +#error See? This shit's been unused for over a decade if (sfx->skinsound!=-1 && origin && origin->skin) { // it redirect player sound to any voice values, should they exist diff --git a/src/lua_infolib.c b/src/lua_infolib.c index b830ab3b8..32bde3101 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -1477,12 +1477,15 @@ static int sfxinfo_get(lua_State *L) lua_pushstring(L, sfx->caption); return 1; case sfxinfor_skinsound: - lua_pushinteger(L, sfx->skinsound); + if (!lua_compatmode) + goto nope; + sfxenum_t sound = sfx - S_sfx; + lua_pushinteger(L, sound >= sfx_kwin && sound <= sfx_kgloat ? sound - sfx_kwin : -1); return 1; - case sfxinfor_string: { + case sfxinfor_string: lua_pushstring(L, sfx->name); return 1; - } + nope: default: return luaL_error(L, "Field does not exist in sfxinfo_t"); } diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index 899a7151c..391e95550 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -141,6 +141,8 @@ static int skin_get(lua_State *L) // This would be pretty cool to push return UNIMPLEMENTED; case skin_soundsid: + if (!lua_compatmode) + return UNIMPLEMENTED; // close enough LUA_PushUserdata(L, &skin->soundsid[0], META_SOUNDSID); break; case skin_voices: @@ -251,12 +253,12 @@ static int soundsid_get(lua_State *L) sfxenum_t *soundsid = *((sfxenum_t **)luaL_checkudata(L, 1, META_SOUNDSID)); skin_t *s = (skin_t*)((char*)soundsid - offsetof(skin_t, soundsid[0])); - skinsound_t i = luaL_checkinteger(L, 2); + INT32 i = luaL_checkinteger(L, 2); - if (i >= NUMSKINSOUNDS) - return luaL_error(L, "%s cannot be %s", LUA_QL("skinsound_t"), va("%u", (UINT32)i)); + if (i < 0 || i >= NUMSKINSOUNDS) + return luaL_error(L, "%s cannot be %s", LUA_QL("skinsound_t"), va("%u", i)); - lua_pushinteger(L, P_GetKartVoiceSFX(&s->voices[0], i)); + lua_pushinteger(L, R_GetLegacySkinSound(&s->voices[0], sfx_kwin + i)); return 1; } @@ -267,23 +269,22 @@ static int soundsid_num(lua_State *L) return 1; } -// voices, i -> voices[i] static int skinvoices_get(lua_State *L) { // Use soundsid to route to the voice array so that voices[0] isn't considered THE VOICE ARRAY sfxenum_t *svoices = *((sfxenum_t **)luaL_checkudata(L, 1, META_SKINVOICES)); skin_t *s = (skin_t*)((char*)svoices - offsetof(skin_t, soundsid[1])); - skinsound_t i = luaL_checkinteger(L, 2); + INT32 i = luaL_checkinteger(L, 2); - if (i >= s->numvoices) - return luaL_error(L, "skin.voices[] index %s out of range (0 - %d)", va("%u", (UINT32)i), s->numvoices - 1); + if (i < 0 || i >= s->numvoices) + return luaL_error(L, "skin.voices[] index %s out of range (0 - %d)", va("%u", i), s->numvoices - 1); LUA_PushUserdata(L, &s->voices[i], META_VOICE); return 1; } -// #voices -> NUMSKINSOUNDS +// #voice -> MAXSKINVOICES static int skinvoices_num(lua_State *L) { lua_pushinteger(L, MAXSKINVOICES); diff --git a/src/r_skins.c b/src/r_skins.c index e3c6bc183..e388f155c 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -170,134 +170,72 @@ UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe) return P_GetSkinSprite2(skin, spr2, NULL); } -void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound) +// returns the sound flags that should be applied to a voice's sound array +static INT32 R_GetVoiceArraySoundFlags(const kartvoice_t *voice, const sfxenum_t *array) { - INT32 usesound; - - switch(skinsound) - { - case SKSKWIN: // Win quote - voice->win = writesound; - break; - case SKSKLOSE: // Lose quote - voice->lose = writesound; - break; - case SKSKPAN1: // Pain - case SKSKPAN2: - usesound = skinsound - SKSKPAN1; - if (usesound < 2) - { - voice->pain[usesound] = writesound; - } - else - { - I_Error("Sk_SetSkinVoiceValue: Attempted to allocate a pain sound of ID %d.\n", usesound); - } - - break; - case SKSKATK1: // Offense item taunt - case SKSKATK2: - usesound = skinsound - SKSKATK1; - if (usesound < 2) - { - voice->attack[usesound] = writesound; - } - else - { - I_Error("Sk_SetSkinVoiceValue: Attempted to allocate an attack sound of ID %d.\n", usesound); - } - break; - case SKSKBST1: // Boost item taunt - case SKSKBST2: - usesound = skinsound - SKSKBST1; - if (usesound < 2) - { - voice->boost[usesound] = writesound; - } - else - { - I_Error("Sk_SetSkinVoiceValue: Attempted to allocate a boost sound of ID %d.\n", usesound); - } - break; - case SKSKSLOW: // Overtake taunt - voice->overtake = writesound; - break; - case SKSKHITM: // Hit confirm taunt - voice->hitem = writesound; - break; - case SKSKPOWR: // Power item taunt - voice->power = writesound; - break; - default: - // Ignore any values outside of this range. - CONS_Alert(CONS_NOTICE, "Sk_SetSkinVoiceValue: ID %d isn't in the range of possible skin sounds (0 to %d)! Ignoring...\n", skinsound, NUMSKINSOUNDS - 1); - break; - } + if (array == &voice->overtake || array == &voice->hitem) + return SF_NOINTERRUPT; + else if (array == &voice->power) + return SF_NOINTERRUPT|SF_X8AWAYSOUND; + else + return SF_NOINTERRUPT|SF_X2AWAYSOUND; } -sfxenum_t P_GetKartVoiceSFX(kartvoice_t *voice, INT32 skinsound) +// parses the name of a voice sound array from S_SKIN without the "Voice" prefix +// returns the length and pointer to the matching voice sound array, or 0 if nothing matched +static size_t R_ParseVoiceSoundKey(kartvoice_t *voice, const char *name, sfxenum_t **outarray) { - INT32 usesound; - - switch(skinsound) + // woah! comma operator! +#define S(str, ret) if (!stricmp(name, #str)) return *outarray = voice->ret, sizeof(voice->ret)/sizeof(*voice->ret) +#define S1(str, ret) if (!stricmp(name, #str)) return *outarray = &voice->ret, 1 + S1(WIN, win); + S1(LOSE, lose); + S(HURT, pain); + S(PAIN, pain); + S(DAMAGE, pain); + S(ATTACK, attack); + S(BOOST, boost); + S1(SLOW, overtake); + S1(OVERTAKE, overtake); + S1(PASS, overtake); + S1(HITEM, hitem); + S1(HITCONFIRM, hitem); + S1(GLOAT, power); +#undef S + + return 0; +} + +sfxenum_t R_GetLegacySkinSound(const kartvoice_t *voice, sfxenum_t sound) +{ + switch (sound) { - case SKSKWIN: // Win quote + case sfx_kwin: // Win quote return voice->win; - case SKSKLOSE: // Lose quote + case sfx_klose: // Lose quote return voice->lose; - case SKSKPAN1: // Pain - case SKSKPAN2: - usesound = skinsound - SKSKPAN1; - if (usesound < 2) - { - return voice->pain[usesound]; - } - else - { - I_Error("P_GetKartVoiceSFX: Attempted to retrieve a pain sound of ID %d.\n", usesound); - } - - break; - case SKSKATK1: // Offense item taunt - case SKSKATK2: - usesound = skinsound - SKSKATK1; - if (usesound < 2) - { - return voice->attack[usesound]; - } - else - { - I_Error("P_GetKartVoiceSFX: Attempted to retrieve an attack sound of ID %d.\n", usesound); - } - break; - case SKSKBST1: // Boost item taunt - case SKSKBST2: - usesound = skinsound - SKSKBST1; - if (usesound < 2) - { - return voice->boost[usesound]; - } - else - { - I_Error("P_GetKartVoiceSFX: Attempted to retrieve a boost sound of ID %d.\n", usesound); - } - break; - case SKSKSLOW: // Overtake taunt + case sfx_khurt1: // Pain + case sfx_khurt2: + return voice->pain[sound - sfx_khurt1]; + case sfx_kattk1: // Offense item taunt + case sfx_kattk2: + return voice->attack[sound - sfx_kattk1]; + case sfx_kbost1: // Boost item taunt + case sfx_kbost2: + return voice->boost[sound - sfx_kbost1]; + case sfx_kslow: // Overtake taunt return voice->overtake; - case SKSKHITM: // Hit confirm taunt + case sfx_khitem: // Hit confirm taunt return voice->hitem; - case SKSKPOWR: // Power item taunt + case sfx_kgloat: // Power item taunt return voice->power; default: return sfx_thok; } - - return sfx_thok; } static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice) { - INT32 i; // // set default skin values // @@ -325,13 +263,14 @@ static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice) skin->highresscale = FRACUNIT; - for (i = 0; i < sfx_skinsoundslot0; i++) - { - if (S_sfx[i].skinsound != -1) - { - Sk_SetSkinVoiceValue(skin_voice, S_sfx[i].skinsound, i); - } - } + skin_voice->win = sfx_thok; + skin_voice->lose = sfx_thok; + skin_voice->pain[0] = skin_voice->pain[1] = sfx_thok; + skin_voice->attack[0] = skin_voice->attack[1] = sfx_thok; + skin_voice->boost[0] = skin_voice->boost[1] = sfx_thok; + skin_voice->overtake = sfx_thok; + skin_voice->hitem = sfx_thok; + skin_voice->power = sfx_thok; strlcpy(skin_voice->name, "default", sizeof(skin_voice->name)); strlcpy(skin_voice->realname, "Default", sizeof(skin_voice->realname)); @@ -1025,76 +964,6 @@ static void R_IHateThatHedgehog(UINT16 wadnum) #endif } -// Necessary for sound adding, so we can gather flags and singularity data. -static sfxenum_t skinsoundreroute[] = { - sfx_kwin, - sfx_klose, - sfx_khurt1, - sfx_khurt2, - sfx_kattk1, // Offense item taunt - sfx_kattk2, - sfx_kbost1, // Boost item taunt - sfx_kbost2, - sfx_kslow, // Overtake taunt - sfx_khitem, // Hit confirm taunt - sfx_kgloat, // Power item taunt -}; - -// Extremely shitty and lifted almost entirely from r_skins.c -// Sets the sound effect value for a voice, given a valid skinsound value is passed. -static UINT8 reallygrossvoicesfxadder(INT32 skinsound, const char *value, kartvoice_t *voice) -{ - sfxenum_t i, j, reroute; - char printval[6]; - - // Remove the prefix. (We can affect this directly since we're not going to use it again.) - if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS* - value += 2; - else // sfx_* - value += 4; - - strncpy(printval, value, 6); - - reroute = skinsoundreroute[skinsound]; - - // Automatically allocate any defined voice sounds. - // Freeslotted voice clips won't ever belong to a skin. - - i = sfx_None; - - // Find if this sound already exists first. - for (j = 0; j < NUMSFX; j++) - { - if (!S_sfx[j].name) - continue; - - if (!stricmp(S_sfx[j].name, value)) - { - // Found an existing sound. - i = j; - break; - } - } - - // This sound doesn't seem to exist, so let's add a new one. - if (i == sfx_None) - { - CONS_Printf("Sound sfx_%s allocated.\n", value); - i = S_AddSoundFx(value, S_sfx[reroute].singularity, S_sfx[reroute].flags, false); - } - - if (i != sfx_None) - { - if (voice) - { - Sk_SetSkinVoiceValue(voice, skinsound, i); - return 0; - } - } - - return 1; -} - static kartvoice_t *allocvoice; // returns whether found appropriate property @@ -1248,51 +1117,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) STRBUFCPY(allocvoice->realname, value); SYMBOLCONVERT(allocvoice->realname); } - else if (!stricmp(stoken, "voicegloat")) - { - reallygrossvoicesfxadder(SKSKPOWR, value, allocvoice); - } - else if (!stricmp(stoken, "voicewin")) - { - reallygrossvoicesfxadder(SKSKWIN, value, allocvoice); - } - else if (!stricmp(stoken, "voicelose")) - { - reallygrossvoicesfxadder(SKSKLOSE, value, allocvoice); - } - else if (!stricmp(stoken, "voiceslow") || !stricmp(stoken, "voiceovertake") || !stricmp(stoken, "voicepass")) - { - reallygrossvoicesfxadder(SKSKSLOW, value, allocvoice); - } - else if (!stricmp(stoken, "voicepain1") || !stricmp(stoken, "voicehurt1") || !stricmp(stoken, "voicedamage1")) - { - reallygrossvoicesfxadder(SKSKPAN1, value, allocvoice); - } - else if (!stricmp(stoken, "voicepain2") || !stricmp(stoken, "voicehurt2") || !stricmp(stoken, "voicedamage2")) - { - reallygrossvoicesfxadder(SKSKPAN2, value, allocvoice); - } - else if (!stricmp(stoken, "voiceattack1")) - { - reallygrossvoicesfxadder(SKSKATK1, value, allocvoice); - } - else if (!stricmp(stoken, "voiceattack2")) - { - reallygrossvoicesfxadder(SKSKATK2, value, allocvoice); - } - else if (!stricmp(stoken, "voiceboost1")) - { - reallygrossvoicesfxadder(SKSKBST1, value, allocvoice); - } - else if (!stricmp(stoken, "voiceboost2")) - { - reallygrossvoicesfxadder(SKSKBST2, value, allocvoice); - } - else if (!stricmp(stoken, "voicehitem") || !stricmp(stoken, "voicehitconfirm")) - { - reallygrossvoicesfxadder(SKSKHITM, value, allocvoice); - } - else if (!stricmp(stoken, "voicehurt") || !stricmp(stoken, "voicepain") || !stricmp(stoken, "voicedamage")) + else if (!strnicmp(stoken, "voice", 5)) { // (that one gif of patrick drooling where his mouth is his entire face) size_t len = strlen(value); @@ -1301,17 +1126,30 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) UINT8 pos = 0; UINT8 numvoices = 0; + sfxenum_t *voicearray; + size_t voicearraylen = R_ParseVoiceSoundKey(allocvoice, stoken+5, &voicearray); + if (voicearraylen == 0) + return false; + for (i = 0; i <= len; i++) { - if (numvoices >= VOICEARRAYSIZE) - { - break; - } - if (value[i] == ',' || i == len) { - reallygrossvoicesfxadder((numvoices < 1) ? SKSKPAN1 : SKSKPAN2, voicename, allocvoice); - numvoices++; + // Automatically allocate any defined voice sounds. + // Freeslotted voice clips won't ever belong to a skin. + sfxenum_t j; + const char *sfxname = voicename; + if (toupper(voicename[0]) == 'D' && toupper(voicename[1]) == 'S') + sfxname += 2; + + j = S_AddSoundFx(sfxname, true); + if (j == sfx_None) + I_Error("Skin %s: out of skin sound slots", skin->name); + S_sfx[j].flags = R_GetVoiceArraySoundFlags(allocvoice, voicearray); + + voicearray[numvoices++] = j; + if (numvoices >= voicearraylen && i != len) + I_Error("Skin %s: voice array %s exceeds max length of %zu", skin->name, stoken, voicearraylen); memset(voicename, 0, sizeof (voicename)); pos = 0; @@ -1323,102 +1161,43 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) pos++; } } - else if (!stricmp(stoken, "voiceattack")) + else // let's check if it's a legacy skinsound, otherwise error out { - size_t len = strlen(value); - size_t i; - char voicename[VOICENAMESIZE] = ""; - UINT8 pos = 0; - UINT8 numvoices = 0; - - for (i = 0; i <= len; i++) - { - if (numvoices >= VOICEARRAYSIZE) - { + sfxenum_t i; + for (i = sfx_kwin; i <= sfx_kgloat; i++) + if (!stricmp(stoken+2, S_sfx[i].name)) break; - } + if (i > sfx_kgloat) + return false; - if (value[i] == ',' || i == len) - { - reallygrossvoicesfxadder((numvoices < 1) ? SKSKATK1 : SKSKATK2, voicename, allocvoice); - numvoices++; + static const char *tonewname[] = { + "WIN", + "LOSE", + "PAIN", + "PAIN", + "ATTACK", + "ATTACK", + "BOOST", + "BOOST", + "SLOW", + "HITEM", + "GLOAT", + }; - memset(voicename, 0, sizeof (voicename)); - pos = 0; + sfxenum_t *voicearray; + size_t len = R_ParseVoiceSoundKey(allocvoice, tonewname[i - sfx_kwin], &voicearray); + if (len == 0) + return false; - continue; - } + // grab the flags now before messing with the pointer + INT32 flags = R_GetVoiceArraySoundFlags(allocvoice, voicearray); - voicename[pos] = value[i]; - pos++; - } - } - else if (!stricmp(stoken, "voiceboost")) - { - size_t len = strlen(value); - size_t i; - char voicename[VOICENAMESIZE] = ""; - UINT8 pos = 0; - UINT8 numvoices = 0; + if (i == sfx_khurt2 || i == sfx_kattk2 || i == sfx_kbost2) + voicearray++; - for (i = 0; i <= len; i++) - { - if (numvoices >= VOICEARRAYSIZE) - { - break; - } - - if (value[i] == ',' || i == len) - { - reallygrossvoicesfxadder((numvoices < 1) ? SKSKBST1 : SKSKBST2, voicename, allocvoice); - numvoices++; - - memset(voicename, 0, sizeof (voicename)); - pos = 0; - - continue; - } - - voicename[pos] = value[i]; - pos++; - } - } - else // let's check if it's a sound, otherwise error out - { - boolean found = false; - sfxenum_t i, newskinsound; - size_t stokenadjust; - - // Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.) - if ((stoken[0] == 'D' || stoken[0] == 'd') && (stoken[1] == 'S' || stoken[1] == 's')) // DS* - stokenadjust = 2; - else // sfx_* - stokenadjust = 4; - - // Remove the prefix. (We can affect this directly since we're not going to use it again.) - if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS* - value += 2; - else // sfx_* - value += 4; - - // copy name of sounds that are remapped - // for this skin - for (i = 0; i < sfx_skinsoundslot0; i++) - { - if (!S_sfx[i].name) - continue; - if (S_sfx[i].skinsound != -1 - && !stricmp(S_sfx[i].name, - stoken + stokenadjust)) - { - newskinsound = S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].flags, true); - - Sk_SetSkinVoiceValue(allocvoice, S_sfx[i].skinsound, newskinsound); - - found = true; - } - } - return found; + *voicearray = S_AddSoundFx(value+2, true); + if (*voicearray != sfx_None) + S_sfx[*voicearray].flags = flags; } return true; } diff --git a/src/r_skins.h b/src/r_skins.h index 9c4bc2d47..7b0c2a35d 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -97,7 +97,7 @@ struct skin_t // Proxy value for the (now removed) soundsid array. // This is ONLY used in a Lua context. // It doesn't actually have anything and will never actually have anything. - skinsound_t soundsid[2]; + UINT8 soundsid[2]; // contains super versions too spritedef_t sprites[NUMPLAYERSPRITES*2]; @@ -151,12 +151,10 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player); UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe); INT32 R_AllocKartVoice(skin_t *skin, const char* name, INT32 *out); -void Sk_SetSkinVoiceValue(kartvoice_t *voice, INT32 skinsound, INT32 writesound); - INT32 R_FindIDForVoice(skin_t *skin, const char *voicename); void SetPlayerVoice(INT32 playernum, const char *voicename); void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum); -sfxenum_t P_GetKartVoiceSFX(kartvoice_t *voice, INT32 skinsound); +sfxenum_t R_GetLegacySkinSound(const kartvoice_t *voice, sfxenum_t sound); #ifdef __cplusplus } // extern "C" diff --git a/src/s_sound.c b/src/s_sound.c index 1d8879fed..7c0cd193a 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -520,7 +520,6 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) const INT32 initial_volume = (origin ? S_ScaleVolumeWithSplitscreen(volume) : volume); sfxinfo_t *sfx; - kartvoice_t *cur_voice; INT32 sep, pitch, priority, cnum; boolean anyListeners = false; boolean itsUs = false; @@ -604,24 +603,20 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) return; } + if (origin && sfx_id >= sfx_kwin && sfx_id <= sfx_kgloat) + { + // legacy skinsound remapping + kartvoice_t *cur_voice = P_GetMobjVoice(origin); + if (cur_voice != NULL) + sfx_id = R_GetLegacySkinSound(cur_voice, sfx_id); + } + // check for bogus sound # I_Assert(sfx_id >= 1); I_Assert(sfx_id < NUMSFX); sfx = &S_sfx[sfx_id]; - if (sfx->skinsound != -1 && origin && origin->skin) - { - // redirect player sound to any voice values, should they exist - cur_voice = P_GetMobjVoice(origin); - - if (cur_voice) - { - sfx_id = P_GetKartVoiceSFX(cur_voice, sfx->skinsound); - sfx = &S_sfx[sfx_id]; - } - } - // Initialize sound parameters pitch = NORM_PITCH; priority = NORM_PRIORITY; @@ -1280,7 +1275,7 @@ void S_StartSoundName(void *mo, const char *soundname) return; } - soundnum = S_AddSoundFx(soundname, false, 0, false); + soundnum = S_AddSoundFx(soundname, false); newsounds[i] = soundnum; } diff --git a/src/sounds.c b/src/sounds.c index 816ef6faa..474a64212 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -86,7 +86,6 @@ void S_InitRuntimeSounds (void) S_sfx[i].volume = -1; S_sfx[i].data = NULL; S_sfx[i].length = 0; - S_sfx[i].skinsound = -1; S_sfx[i].usefulness = -1; S_sfx[i].lumpnum = LUMPERROR; //strlcpy(S_sfx[i].caption, "", 1); @@ -94,53 +93,50 @@ void S_InitRuntimeSounds (void) } } -sfxenum_t sfxfree = sfx_freeslot0; +sfxenum_t sfxfree = sfx_freeslot0, skinsfxfree = sfx_skinsoundslot0; // Add a new sound fx into a free sfx slot. // -sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags, boolean skinsound) +sfxenum_t S_AddSoundFx(const char *name, boolean skinsound) { sfxenum_t i; if (skinsound) { - for (i = sfx_skinsoundslot0; i < NUMSFX; i++) + if (skinsfxfree > sfx_lastskinsoundslot) { - if (S_sfx[i].priority) - continue; - break; + CONS_Alert(CONS_WARNING, M_GetText("No more free skin sound slots\n")); + return sfx_None; } + i = skinsfxfree++; } else - i = sfxfree; - - if (i < NUMSFX) { - strncpy(soundnames[i], name, 6); - S_sfx[i].singularity = singular; - S_sfx[i].priority = 60; - S_sfx[i].flags = flags; - S_sfx[i].volume = -1; - S_sfx[i].lumpnum = LUMPERROR; - S_sfx[i].skinsound = -1; - S_sfx[i].usefulness = -1; - - /// \todo if precached load it here - S_sfx[i].data = NULL; - - if (!skinsound) - sfxfree++; - - return i; + if (sfxfree > sfx_lastfreeslot) + { + CONS_Alert(CONS_WARNING, M_GetText("No more free sound slots\n")); + return sfx_None; + } + i = sfxfree++; } - CONS_Alert(CONS_WARNING, M_GetText("No more free sound slots\n")); - return 0; + + strncpy(soundnames[i], name, 6); + S_sfx[i].singularity = false; + S_sfx[i].priority = 60; + S_sfx[i].flags = 0; + S_sfx[i].volume = -1; + S_sfx[i].lumpnum = LUMPERROR; + S_sfx[i].usefulness = -1; + + /// \todo if precached load it here + S_sfx[i].data = NULL; + + return i; } void S_RemoveSoundFx(sfxenum_t id) { - if (id >= sfx_freeslot0 && id <= sfx_lastskinsoundslot - && S_sfx[id].priority != 0) + if (id >= sfx_freeslot0 && id < NUMSFX && S_sfx[id].priority != 0) { S_sfx[id].lumpnum = LUMPERROR; I_FreeSfx(&S_sfx[id]); diff --git a/src/sounds.h b/src/sounds.h index 3c78d999a..102b22c2e 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -20,27 +20,12 @@ extern "C" { #endif -// Customisable sounds for Skins -typedef enum -{ - // SRB2kart - SKSKWIN, // Win quote - SKSKLOSE, // Lose quote - SKSKPAN1, // Pain - SKSKPAN2, - SKSKATK1, // Offense item taunt - SKSKATK2, - SKSKBST1, // Boost item taunt - SKSKBST2, - SKSKSLOW, // Overtake taunt - SKSKHITM, // Hit confirm taunt - SKSKPOWR, // Power item taunt - NUMSKINSOUNDS -} skinsound_t; +// keep this one around at least, for compatibility code +#define NUMSKINSOUNDS 11 // free sfx for S_AddSoundFx() #define NUMSFXFREESLOTS 2500 // Matches SOC Editor. -#define NUMSKINSFXSLOTS (MAXSKINS*NUMSKINSOUNDS) +#define NUMSKINSFXSLOTS (50000 - NUMSFXFREESLOTS) // // SoundFX struct. @@ -69,10 +54,6 @@ struct sfxinfo_t // length of sound data size_t length; - // sound that can be remapped for a skin, indexes skins[].skinsounds - // 0 up to (NUMSKINSOUNDS-1), -1 = not skin specifc - INT32 skinsound; - // this is checked every second to see if sound // can be thrown out (if 0, then decrement, if -1, // then throw out, if > 0, then it is in use) @@ -114,8 +95,8 @@ typedef enum void S_InitRuntimeSounds(void); -sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags, boolean skinsound); -extern sfxenum_t sfxfree; // sound test and slotting +sfxenum_t S_AddSoundFx(const char *name, boolean skinsound); +extern sfxenum_t sfxfree, skinsfxfree; // sound test and slotting void S_RemoveSoundFx(sfxenum_t id); #ifdef __cplusplus From 891459b53b688351e32fadcb7ad82dac23f8b9ce Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Mon, 1 Dec 2025 22:26:52 +0100 Subject: [PATCH 39/44] Change all voice arrays to be exposed as arrays in Lua For the sake of consistency and future expansion! Also expose mobj->voice (read-only for now, hopefully), and fix missing NOSET for player->voice_id --- src/lua_baselib.c | 3 ++- src/lua_libs.h | 4 ++- src/lua_mobjlib.c | 11 ++++++++ src/lua_playerlib.c | 2 ++ src/lua_voicelib.c | 62 +++++++++++++++++++++++++++++++++++++++------ 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 2c1d8dc0f..565f40f4c 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -251,7 +251,8 @@ static const struct { {META_MATRIX, "matrix_t"}, {META_QUATERNION, "quaternion_t"}, {META_VOICE, "kartvoice_t"}, - {META_VOICE_ARRAY, "kartvoice_t.array"}, + {META_VOICE_ARRAY, "kartvoice_t.array[]"}, + {META_VOICE_ARRAY1, "kartvoice_t.array[1]"}, {NULL, NULL} }; diff --git a/src/lua_libs.h b/src/lua_libs.h index e1da50bc0..7367b50df 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -124,7 +124,9 @@ extern lua_State *gL; #define META_QUATERNION "QUATERNION_T" #define META_VOICE "KARTVOICE_T*" -#define META_VOICE_ARRAY "KARTVOICE_T*ARRAY" +#define META_VOICE_ARRAY "KARTVOICE_T*[]" +// TODO: kill this one +#define META_VOICE_ARRAY1 "KARTVOICE_T*[1]" boolean luaL_checkboolean(lua_State *L, int narg); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index ec47bcfba..da2dba14b 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -64,6 +64,7 @@ enum mobj_e { mobj_eflags, mobj_renderflags, mobj_skin, + mobj_voice, mobj_color, mobj_bnext, mobj_bprev, @@ -161,6 +162,7 @@ static const char *const mobj_opt[] = { "eflags", "renderflags", "skin", + "voice", "color", "bnext", "bprev", @@ -427,6 +429,9 @@ static int mobj_get(lua_State *L) return 0; lua_pushstring(L, ((skin_t *)mo->skin)->name); break; + case mobj_voice: // just push the struct damnit! + LUA_PushUserdata(L, mo->voice, META_VOICE); + break; case mobj_color: lua_pushinteger(L, mo->color); break; @@ -901,6 +906,12 @@ static int mobj_set(lua_State *L) } return luaL_error(L, "mobj.skin '%s' not found!", skin); } + case mobj_voice: + // as much as i want to allow ironman without overwriting the player's skin, + // letting the mobj's voice desync with the skin has some nasty implications at the moment + return NOSET; + //mo->voice = *((kartvoice_t **)luaL_checkudata(L, 3, META_VOICE)); + //break; case mobj_color: { UINT16 newcolor = (UINT16)luaL_checkinteger(L,3); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 9ecb5ce15..e08b6c6b8 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -1507,6 +1507,8 @@ static int player_set(lua_State *L) } case player_skin: return NOSET; + case player_voice_id: + return NOSET; case player_availabilities: return NOSET; case player_score: diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index 4bee3ac81..7e7e809bb 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -75,10 +75,10 @@ static int voice_get(lua_State* L) lua_pushstring(L, voice->name); break; case voicevars_win: - LUA_PushUserdata(L, &S_sfx[voice->win], META_SFXINFO); + LUA_PushUserdata(L, &voice->win, META_VOICE_ARRAY1); break; case voicevars_lose: - LUA_PushUserdata(L, &S_sfx[voice->lose], META_SFXINFO); + LUA_PushUserdata(L, &voice->lose, META_VOICE_ARRAY1); break; case voicevars_pain: LUA_PushUserdata(L, voice->pain, META_VOICE_ARRAY); @@ -90,13 +90,13 @@ static int voice_get(lua_State* L) LUA_PushUserdata(L, voice->boost, META_VOICE_ARRAY); break; case voicevars_overtake: - LUA_PushUserdata(L, &S_sfx[voice->overtake], META_SFXINFO); + LUA_PushUserdata(L, &voice->overtake, META_VOICE_ARRAY1); break; case voicevars_hitem: - LUA_PushUserdata(L, &S_sfx[voice->hitem], META_SFXINFO); + LUA_PushUserdata(L, &voice->hitem, META_VOICE_ARRAY1); break; case voicevars_power: - LUA_PushUserdata(L, &S_sfx[voice->power], META_SFXINFO); + LUA_PushUserdata(L, &voice->power, META_VOICE_ARRAY1); break; default: return RNOFIELD; @@ -181,9 +181,9 @@ static int voice_array_get(lua_State *L) INT32 i = luaL_checkinteger(L, 2); if (i < 0 || i >= VOICEARRAYSIZE) - return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE); + return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE-1); - LUA_PushUserdata(L, &S_sfx[voice_array[i]], META_SFXINFO); + lua_pushinteger(L, voice_array[i]); return 1; } @@ -193,7 +193,7 @@ static int voice_array_set(lua_State *L) INT32 i = luaL_checkinteger(L, 2); if (i < 0 || i >= VOICEARRAYSIZE) - return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE); + return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE-1); voice_array[i] = (sfxenum_t)luaL_checkinteger(L, 3); return 0; @@ -206,6 +206,41 @@ static int voice_array_num(lua_State *L) return 1; } +// i would've liked to shove the length of the voice array into the userdata... +// but this codebase is not ready for that, sooooo... have yet ANOTHER userdata type! + +// voice_array, i -> voice.array[i] +static int voice_array1_get(lua_State *L) +{ + sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY1)); + INT32 i = luaL_checkinteger(L, 2); + + if (i < 0 || i >= 1) + return luaL_error(L, "index %d out of range (0 - %d)", i, 1-1); + + lua_pushinteger(L, voice_array[i]); + return 1; +} + +static int voice_array1_set(lua_State *L) +{ + sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY1)); + INT32 i = luaL_checkinteger(L, 2); + + if (i < 0 || i >= 1) + return luaL_error(L, "index %d out of range (0 - %d)", i, 1-1); + + voice_array[i] = (sfxenum_t)luaL_checkinteger(L, 3); + return 0; +} + +// #voice.array -> 1 +static int voice_array1_num(lua_State *L) +{ + lua_pushinteger(L, 1); + return 1; +} + int LUA_VoiceLib(lua_State* L) { luaL_newmetatable(L, META_VOICE); @@ -230,5 +265,16 @@ int LUA_VoiceLib(lua_State* L) lua_setfield(L, -2, "__len"); lua_pop(L,1); + luaL_newmetatable(L, META_VOICE_ARRAY1); + lua_pushcfunction(L, voice_array1_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, voice_array1_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, voice_array1_num); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + return 0; } From 797b0adecd0534ae2616afbca74a1acff64a56ce Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Mon, 1 Dec 2025 22:42:43 +0100 Subject: [PATCH 40/44] Whoops, forgot about voices still being writable from kartvoice_t which means kartvoice_t no longer has any writable fields, so gut the setter --- src/lua_voicelib.c | 82 ++++++++-------------------------------------- 1 file changed, 13 insertions(+), 69 deletions(-) diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index 7e7e809bb..9fd9e628d 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -9,8 +9,8 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file lua_waypointslib.c -/// \brief wapoint structure library for Lua scripting +/// \file lua_voicelib.c +/// \brief voice structure library for Lua scripting #include "doomdef.h" #include "fastcmp.h" @@ -26,7 +26,7 @@ enum voicevars { voicevars_id = 0, - voicevars_name, // Not actually in kartvoice_t; returns the name of the voice + voicevars_name, voicevars_win, voicevars_lose, voicevars_pain, @@ -52,12 +52,7 @@ static const char *const voicevars_opt[] = { }; #define RNOFIELD luaL_error(L, LUA_QL("kartvoice_t") " has no field named " LUA_QS, field) - -#define RNOSET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be set.", field) #define RNOGET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " cannot be get.", field) -#define NOSET luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " should not be set directly.", field) - -#define UNIMPLEMENTED luaL_error(L, LUA_QL("kartvoice_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", voicevars_opt[field]) static int voice_get(lua_State* L) { @@ -105,64 +100,6 @@ static int voice_get(lua_State* L) return 1; } -static int voice_set(lua_State* L) -{ - kartvoice_t* voice = *((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE)); - enum voicevars field = luaL_checkoption(L, 2, voicevars_opt[0], voicevars_opt); - - // voices are always valid, only added, never removed - I_Assert(voice != NULL); - - if (hud_running) - return luaL_error(L, "Do not alter kartvoice_t in HUD rendering code!"); - if (hook_cmd_running) - return luaL_error(L, "Do not alter kartvoice_t in CMD building code!"); - - lua_Integer i, k; - - i = 0; - k = sfx_thok; - - switch (field) - { - case voicevars_id: - return RNOSET; - case voicevars_name: - return RNOSET; - case voicevars_win: - // For the actual sound values, you pass the actual sfxenum_t enums. - // Yes, I know what I said about skin structs literally just above. - voice->win = (sfxenum_t)luaL_checkinteger(L, 3); - break; - case voicevars_lose: - voice->lose = (sfxenum_t)luaL_checkinteger(L, 3); - break; - case voicevars_pain: - return NOSET; - case voicevars_attack: - return NOSET; - case voicevars_boost: - return NOSET; - case voicevars_overtake: - voice->overtake = (sfxenum_t)luaL_checkinteger(L, 3); - break; - case voicevars_hitem: - voice->hitem = (sfxenum_t)luaL_checkinteger(L, 3); - break; - case voicevars_power: - voice->power = (sfxenum_t)luaL_checkinteger(L, 3); - break; - default: - return RNOFIELD; - } - - return 0; -} - -#undef RNOSET -#undef RNOFIELD -#undef UNIMPLEMENTED - static int voice_num(lua_State* L) { kartvoice_t* voice = *((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE)); @@ -192,6 +129,11 @@ static int voice_array_set(lua_State *L) sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY)); INT32 i = luaL_checkinteger(L, 2); + if (hud_running) + return luaL_error(L, "Do not alter kartvoice_t.array[] in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter kartvoice_t.array[] in CMD building code!"); + if (i < 0 || i >= VOICEARRAYSIZE) return luaL_error(L, "index %d out of range (0 - %d)", i, VOICEARRAYSIZE-1); @@ -227,6 +169,11 @@ static int voice_array1_set(lua_State *L) sfxenum_t *voice_array = *((sfxenum_t **)luaL_checkudata(L, 1, META_VOICE_ARRAY1)); INT32 i = luaL_checkinteger(L, 2); + if (hud_running) + return luaL_error(L, "Do not alter kartvoice_t.array[1] in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter kartvoice_t.array[1] in CMD building code!"); + if (i < 0 || i >= 1) return luaL_error(L, "index %d out of range (0 - %d)", i, 1-1); @@ -247,9 +194,6 @@ int LUA_VoiceLib(lua_State* L) lua_pushcfunction(L, voice_get); lua_setfield(L, -2, "__index"); - lua_pushcfunction(L, voice_set); - lua_setfield(L, -2, "__newindex"); - lua_pushcfunction(L, voice_num); lua_setfield(L, -2, "__len"); lua_pop(L, 1); From 241030e080c89fd18706bb27d4aa6f628005c0bb Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Mon, 1 Dec 2025 22:54:41 +0100 Subject: [PATCH 41/44] You're supposed to do it like this actually --- src/lua_voicelib.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lua_voicelib.c b/src/lua_voicelib.c index 9fd9e628d..06c4d20f4 100644 --- a/src/lua_voicelib.c +++ b/src/lua_voicelib.c @@ -100,6 +100,11 @@ static int voice_get(lua_State* L) return 1; } +static int voice_set(lua_State *L) +{ + return luaL_error(L, LUA_QL("kartvoice_t") " struct cannot be edited by Lua."); +} + static int voice_num(lua_State* L) { kartvoice_t* voice = *((kartvoice_t**)luaL_checkudata(L, 1, META_VOICE)); @@ -194,6 +199,9 @@ int LUA_VoiceLib(lua_State* L) lua_pushcfunction(L, voice_get); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, voice_set); + lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, voice_num); lua_setfield(L, -2, "__len"); lua_pop(L, 1); From a8a5d703eb315ce82b7c34b45ab960b2a5a93b80 Mon Sep 17 00:00:00 2001 From: yamamama Date: Tue, 2 Dec 2025 11:05:10 -0500 Subject: [PATCH 42/44] P_GetMobjVoice: Mute objects shouldn't break reality (removes the crash when an object has no voice) --- src/p_mobj.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index f6a032e3c..68a84488b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -14987,20 +14987,17 @@ kartvoice_t *P_GetMobjVoice(const mobj_t *mo) { return mo->voice; } + #ifdef PARANOIA - else - { - I_Error("P_GetMobjVoice: This object has no voice!"); - return NULL; - } -#else - else if (mo->skin) - { - return &((skin_t *)mo->skin)->voices[0]; - } + // Scream at the idiot that let this happen :^) + CONS_Printf( + "PARANOIA/P_GetMobjVoice: %p MT_%s passed to function with NULL voice\n", + (void*)mo, + MobjTypeName(mo) + ); +#endif return NULL; -#endif } // From 2a11dc33e730ac9c06e3de2ec6af3ade2d736f63 Mon Sep 17 00:00:00 2001 From: yamamama Date: Tue, 2 Dec 2025 11:22:32 -0500 Subject: [PATCH 43/44] Expose MobjTypeName in PARANOIA mode --- src/p_tick.c | 6 +++--- src/p_tick.h | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index 05819a69c..e3aa7046f 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -262,7 +262,7 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker) } #ifdef PARANOIA -static const char *MobjTypeName(const mobj_t *mobj) +const char *P_MobjTypeName(const mobj_t *mobj) { actionf_p1 p1 = mobj->thinker.function; @@ -338,7 +338,7 @@ void P_RemoveThinkerDelayed(thinker_t *thinker) CONS_Printf( "PARANOIA/P_RemoveThinkerDelayed: %p MT_%s references=%d\n", (void*)thinker, - MobjTypeName((mobj_t*)thinker), + P_MobjTypeName((mobj_t*)thinker), thinker->references ); @@ -428,7 +428,7 @@ mobj_t *P_SetTarget2(mobj_t **mop, mobj_t *targ CONS_Printf( "PARANOIA/P_SetTarget: %p MT_%s %s references=%d, references go negative! (%s:%d)\n", (void*)*mop, - MobjTypeName(*mop), + P_MobjTypeName(*mop), MobjThinkerName(*mop), (*mop)->thinker.references, source_file, diff --git a/src/p_tick.h b/src/p_tick.h index 6a187e7d5..e4b167439 100644 --- a/src/p_tick.h +++ b/src/p_tick.h @@ -44,6 +44,10 @@ mobj_t *P_SetTarget2(mobj_t **mo, mobj_t *target #define P_SetTarget P_SetTarget2 #endif +#ifdef PARANOIA +const char *P_MobjTypeName(const mobj_t *mobj); +#endif + extern UINT32 thinker_era; // Negate the value for tics From 3c5a561a566ec75a04d4697940d08fb6c7cff74d Mon Sep 17 00:00:00 2001 From: yamamama Date: Tue, 2 Dec 2025 11:22:40 -0500 Subject: [PATCH 44/44] Fix compiling dammit --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 68a84488b..f7e7d4377 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -14993,7 +14993,7 @@ kartvoice_t *P_GetMobjVoice(const mobj_t *mo) CONS_Printf( "PARANOIA/P_GetMobjVoice: %p MT_%s passed to function with NULL voice\n", (void*)mo, - MobjTypeName(mo) + P_MobjTypeName(mo) ); #endif