Merge pull request '[FEAT] Skin-based voice dubs' (#190) from subvsdub into next
Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/190
This commit is contained in:
commit
851ca0b262
33 changed files with 1216 additions and 150 deletions
|
|
@ -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
|
||||
|
|
@ -507,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
|
||||
|
|
|
|||
269
src/d_netcmd.c
269
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);
|
||||
|
|
@ -347,6 +352,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);
|
||||
|
|
@ -1196,6 +1210,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
|
||||
|
|
@ -1753,12 +1768,15 @@ 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;
|
||||
kartvoice_t *valuevoice;
|
||||
INT32 prevskin;
|
||||
|
||||
char buf[MAXPLAYERNAME+12];
|
||||
char *p;
|
||||
|
|
@ -1795,11 +1813,21 @@ 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)
|
||||
{
|
||||
// ...how?!
|
||||
voice = &skins[player->skin].voices[0];
|
||||
}
|
||||
|
||||
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
|
||||
&& !stricmp(cv_voice[n].string, voice->name))
|
||||
return;
|
||||
|
||||
player->availabilities = R_GetSkinAvailabilities();
|
||||
|
|
@ -1811,7 +1839,7 @@ static void SendNameAndColor(UINT8 n)
|
|||
// If you're not in a netgame, merely update the skin, color, and name.
|
||||
if (!netgame)
|
||||
{
|
||||
INT32 foundskin;
|
||||
INT32 foundskin, voxid;
|
||||
|
||||
CleanupPlayerName(playernum, cv_playername[n].zstring);
|
||||
strcpy(player_names[playernum], cv_playername[n].zstring);
|
||||
|
|
@ -1833,16 +1861,105 @@ static void SendNameAndColor(UINT8 n)
|
|||
}
|
||||
else if ((foundskin = R_SkinAvailable(cv_skin[n].string)) != -1 && R_SkinUsable(playernum, foundskin))
|
||||
{
|
||||
prevskin = cv_skin[n].value;
|
||||
|
||||
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)
|
||||
{
|
||||
if (cv_voice[n].string)
|
||||
{
|
||||
voxid = R_FindIDForVoice(&skins[player->skin], cv_voice[n].string);
|
||||
|
||||
if (voxid == MAXSKINVOICES)
|
||||
{
|
||||
// No dice; use the default voice.
|
||||
valuevoice = &skins[player->skin].voices[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
valuevoice = &skins[player->skin].voices[voxid];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NULL string; default to the skin's default.
|
||||
valuevoice = &skins[player->skin].voices[0];
|
||||
}
|
||||
|
||||
CV_StealthSet(&cv_voice[n], valuevoice->name);
|
||||
}
|
||||
}
|
||||
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.
|
||||
if (prevskin != player->skin)
|
||||
{
|
||||
if (cv_voice[n].string)
|
||||
{
|
||||
voxid = R_FindIDForVoice(&skins[player->skin], cv_voice[n].string);
|
||||
|
||||
if (voxid == MAXSKINVOICES)
|
||||
{
|
||||
// No dice; use the default voice.
|
||||
valuevoice = &skins[player->skin].voices[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
valuevoice = &skins[player->skin].voices[voxid];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NULL string; default to the skin's default.
|
||||
valuevoice = &skins[player->skin].voices[0];
|
||||
}
|
||||
|
||||
CV_StealthSet(&cv_voice[n], valuevoice->name);
|
||||
}
|
||||
}
|
||||
|
||||
// Need to update voices after the fact.
|
||||
if (!cv_voice[n].string)
|
||||
{
|
||||
CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name);
|
||||
}
|
||||
|
||||
SetPlayerVoice(playernum, cv_voice[n].string);
|
||||
|
||||
valuevoice = P_GetMobjVoice(player->mo);
|
||||
|
||||
if (valuevoice)
|
||||
{
|
||||
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)
|
||||
{
|
||||
CV_StealthSet(&cv_voice[n], valuevoice->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...still nothing?
|
||||
CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -1863,8 +1980,16 @@ 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 (R_FindIDForVoice(&skins[player->skin], cv_voice[n].string) == MAXSKINVOICES)
|
||||
{
|
||||
// Our voice is no longer valid, set it to that of our skin's.
|
||||
CV_StealthSet(&cv_voice[n], skins[player->skin].voices[0].name);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
|
@ -1872,6 +1997,56 @@ 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;
|
||||
}
|
||||
|
||||
// Need to update voices after the fact.
|
||||
if (!cv_voice[n].string)
|
||||
{
|
||||
CV_StealthSet(&cv_voice[n], skins[cv_skin[n].value].voices[0].name);
|
||||
}
|
||||
|
||||
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.
|
||||
valuevoice = P_GetMobjVoice(player->mo);
|
||||
|
||||
if (valuevoice)
|
||||
{
|
||||
CV_StealthSet(&cv_voice[n], valuevoice->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the voice from our ID.
|
||||
// No need to compare parents.
|
||||
valuevoice = &skins[cv_skin[n].value].voices[player->voice_id];
|
||||
|
||||
if (valuevoice)
|
||||
{
|
||||
CV_StealthSet(&cv_voice[n], valuevoice->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...still nothing?
|
||||
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.
|
||||
|
|
@ -1881,6 +2056,7 @@ static void SendNameAndColor(UINT8 n)
|
|||
WRITEUINT16(p, (UINT16)cv_skin[n].value);
|
||||
WRITEINT32(p, (INT32)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);
|
||||
}
|
||||
|
|
@ -1890,7 +2066,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;
|
||||
INT32 follower;
|
||||
SINT8 localplayer = -1;
|
||||
UINT16 i;
|
||||
|
|
@ -1922,6 +2098,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
|||
skin = READUINT16(*cp);
|
||||
follower = READINT32(*cp);
|
||||
followercolor = READUINT16(*cp);
|
||||
voice = READUINT16(*cp);
|
||||
|
||||
// set name
|
||||
if (player_name_changes[playernum] < MAXNAMECHANGES)
|
||||
|
|
@ -1996,11 +2173,27 @@ 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].voices[0].name);
|
||||
}
|
||||
|
||||
// set voice
|
||||
SetPlayerVoiceByNum(playernum, skins[forcedskin].voices[0].id);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPlayerSkinByNum(playernum, skin);
|
||||
|
||||
// set voice
|
||||
SetPlayerVoiceByNum(playernum, voice);
|
||||
|
||||
if (localplayer != -1)
|
||||
{
|
||||
CV_StealthSet(&cv_voice[localplayer], skins[p->skin].voices[p->voice_id].name);
|
||||
}
|
||||
}
|
||||
|
||||
// set follower colour:
|
||||
// Don't bother doing garbage and kicking if we receive None,
|
||||
// this is both silly and a waste of time,
|
||||
|
|
@ -7273,6 +7466,74 @@ static void Color4_OnChange(void)
|
|||
lastgoodcolor[3] = cv_playercolor[3].value;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// ...how?!
|
||||
myvoice = &skins[players[pid].skin].voices[0];
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ strbuf_t *mobjnames;
|
|||
strbuf_t *skincolornames;
|
||||
strbuf_t *menunames;
|
||||
strbuf_t *kartitemnames;
|
||||
|
||||
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)
|
||||
|
|
@ -1177,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.
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ extern strbuf_t *mobjnames;
|
|||
extern strbuf_t *skincolornames;
|
||||
extern strbuf_t *menunames;
|
||||
extern strbuf_t *kartitemnames;
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -414,7 +414,8 @@ 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];
|
||||
|
||||
sfxenum_t rsound = skins[rskin].voices[0].boost[rtaunt];
|
||||
S_StartSound(NULL, rsound);
|
||||
}
|
||||
}
|
||||
|
|
@ -443,11 +444,13 @@ 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]);
|
||||
|
||||
sfxenum_t rsound = skins[rskin].voices[0].win;
|
||||
S_StartSound(NULL, sfx_flgcap);
|
||||
S_StartSound(NULL, rsound);
|
||||
}
|
||||
|
|
|
|||
77
src/g_demo.c
77
src/g_demo.c
|
|
@ -54,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);
|
||||
|
|
@ -111,7 +110,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 +221,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 +342,7 @@ typedef struct
|
|||
|
||||
// DXD_SKIN
|
||||
char skinname[17];
|
||||
char voicename[33];
|
||||
UINT8 kartspeed;
|
||||
UINT8 kartweight;
|
||||
|
||||
|
|
@ -621,9 +622,18 @@ 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
|
||||
{
|
||||
READSTRINGL(dp, extra->skinname, 16+1);
|
||||
|
||||
if (version > 0x000D)
|
||||
READSTRINGL(dp, extra->voicename, 32+1);
|
||||
else
|
||||
extra->voicename[0] = '\0';
|
||||
}
|
||||
extra->kartspeed = READUINT8(dp);
|
||||
extra->kartweight = READUINT8(dp);
|
||||
}
|
||||
|
|
@ -682,6 +692,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 +727,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.
|
||||
|
|
@ -724,7 +738,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)
|
||||
{
|
||||
|
|
@ -946,6 +960,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);
|
||||
}
|
||||
|
|
@ -1249,6 +1267,26 @@ void G_ReadDemoExtraData(void)
|
|||
if (stricmp(skins[player->skin].name, extra.skinname) != 0)
|
||||
FindClosestSkinForStats(p, extra.kartspeed, extra.kartweight);
|
||||
|
||||
// Voice
|
||||
const INT32 vce = R_FindIDForVoice(&skins[player->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.
|
||||
player->voice_id = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->voice_id = (UINT16)vce;
|
||||
}
|
||||
|
||||
if (!P_MobjWasRemoved(player->mo))
|
||||
{
|
||||
// This player's mobj exists. So, attach the voice to the mobj!
|
||||
player->mo->voice = &skins[player->skin].voices[player->voice_id];
|
||||
}
|
||||
|
||||
player->kartspeed = extra.kartspeed;
|
||||
player->kartweight = extra.kartweight;
|
||||
}
|
||||
|
|
@ -1352,6 +1390,10 @@ void G_WriteDemoExtraData(void)
|
|||
// Skin
|
||||
WRITESTRINGL(demobuf.p, skins[players[i].skin].name, 16+1);
|
||||
|
||||
// Voice
|
||||
WRITESTRINGL(demobuf.p, skins[players[i].skin].voices[players[i].voice_id].name, 32+1);
|
||||
|
||||
// Stats
|
||||
WRITEUINT8(demobuf.p, skins[players[i].skin].kartspeed);
|
||||
WRITEUINT8(demobuf.p, skins[players[i].skin].kartweight);
|
||||
|
||||
|
|
@ -2057,7 +2099,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;
|
||||
|
|
@ -2118,7 +2160,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)
|
||||
{
|
||||
|
|
@ -2521,7 +2563,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)
|
||||
{
|
||||
|
|
@ -2935,6 +2977,9 @@ void G_BeginRecording(void)
|
|||
// Color
|
||||
WRITESTRINGL(demobuf.p, skincolors[player->skincolor].name, 16+1);
|
||||
|
||||
// Voice
|
||||
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!
|
||||
// Doesn't really matter if the follower mobj is valid so long as it exists in a way or another.
|
||||
|
|
@ -3807,6 +3852,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(&skins[player->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.
|
||||
player->voice_id = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->voice_id = (UINT16)vce;
|
||||
}
|
||||
|
||||
// Follower
|
||||
K_SetFollowerByName(p, plr->follower);
|
||||
|
||||
|
|
@ -4058,7 +4120,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;
|
||||
|
|
|
|||
|
|
@ -2712,6 +2712,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
UINT8 latestlap;
|
||||
UINT16 skincolor;
|
||||
INT32 skin;
|
||||
UINT16 voice;
|
||||
UINT32 availabilities;
|
||||
|
||||
tic_t jointime;
|
||||
|
|
@ -2770,6 +2771,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;
|
||||
|
|
@ -2960,6 +2962,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;
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -359,11 +360,17 @@ 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 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_GetKartVoiceSFX(voice, sfx->skinsound);
|
||||
sfx = &S_sfx[sfx_id];
|
||||
}
|
||||
}
|
||||
|
||||
if (!sfx->data)
|
||||
|
|
|
|||
20
src/k_kart.c
20
src/k_kart.c
|
|
@ -1967,7 +1967,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)
|
||||
|
|
@ -1996,7 +2004,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)
|
||||
|
|
|
|||
|
|
@ -181,6 +181,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"},
|
||||
|
|
@ -249,6 +250,9 @@ static const struct {
|
|||
{META_VECTOR3, "vector3_t"},
|
||||
{META_MATRIX, "matrix_t"},
|
||||
{META_QUATERNION, "quaternion_t"},
|
||||
{META_VOICE, "kartvoice_t"},
|
||||
{META_VOICE_ARRAY, "kartvoice_t.array[]"},
|
||||
{META_VOICE_ARRAY1, "kartvoice_t.array[1]"},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
@ -2611,6 +2615,7 @@ static int lib_rGetNameByColor(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// S_SOUND
|
||||
////////////
|
||||
static int GetValidSoundOrigin(lua_State *L, void **origin)
|
||||
|
|
@ -3679,12 +3684,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*"
|
||||
|
|
@ -122,6 +123,11 @@ extern lua_State *gL;
|
|||
#define META_MATRIX "MATRIX_T"
|
||||
#define META_QUATERNION "QUATERNION_T"
|
||||
|
||||
#define META_VOICE "KARTVOICE_T*"
|
||||
#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);
|
||||
|
||||
int LUA_EnumLib(lua_State *L);
|
||||
|
|
@ -147,6 +153,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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -894,11 +899,19 @@ 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);
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -389,6 +389,7 @@ enum player_e
|
|||
player_flashpal,
|
||||
player_skincolor,
|
||||
player_skin,
|
||||
player_voice_id,
|
||||
player_availabilities,
|
||||
player_score,
|
||||
player_kartspeed,
|
||||
|
|
@ -606,6 +607,7 @@ static const char *const player_opt[] = {
|
|||
"flashpal",
|
||||
"skincolor",
|
||||
"skin",
|
||||
"voice_id",
|
||||
"availabilities",
|
||||
"score",
|
||||
"kartspeed",
|
||||
|
|
@ -1177,6 +1179,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;
|
||||
|
|
@ -1502,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:
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ static lua_CFunction liblist[] = {
|
|||
LUA_VectorLib, // vectors
|
||||
LUA_MatrixLib, // matrices
|
||||
LUA_QuaternionLib, // quaternions
|
||||
LUA_VoiceLib, // kartvoice_t, skinvoices[]
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ enum skin {
|
|||
skin_highresscale,
|
||||
skin_rivals,
|
||||
skin_soundsid,
|
||||
skin_voices,
|
||||
skin_sprites
|
||||
};
|
||||
static const char *const skin_opt[] = {
|
||||
|
|
@ -60,6 +61,7 @@ static const char *const skin_opt[] = {
|
|||
"highresscale",
|
||||
"rivals",
|
||||
"soundsid",
|
||||
"voices",
|
||||
"sprites",
|
||||
NULL
|
||||
};
|
||||
|
|
@ -139,7 +141,13 @@ 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);
|
||||
if (!lua_compatmode)
|
||||
return UNIMPLEMENTED; // close enough
|
||||
LUA_PushUserdata(L, &skin->soundsid[0], META_SOUNDSID);
|
||||
break;
|
||||
case skin_voices:
|
||||
// ...uuuuuuughhhhh...
|
||||
LUA_PushUserdata(L, &skin->soundsid[1], META_SKINVOICES);
|
||||
break;
|
||||
case skin_sprites:
|
||||
LUA_PushUserdata(L, skin->sprites, META_SKINSPRITES);
|
||||
|
|
@ -241,11 +249,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));
|
||||
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]);
|
||||
skin_t *s = (skin_t*)((char*)soundsid - offsetof(skin_t, soundsid[0]));
|
||||
|
||||
INT32 i = luaL_checkinteger(L, 2);
|
||||
|
||||
if (i < 0 || i >= NUMSKINSOUNDS)
|
||||
return luaL_error(L, "%s cannot be %s", LUA_QL("skinsound_t"), va("%u", i));
|
||||
|
||||
lua_pushinteger(L, R_GetLegacySkinSound(&s->voices[0], sfx_kwin + i));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -256,6 +269,28 @@ static int soundsid_num(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
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]));
|
||||
|
||||
INT32 i = luaL_checkinteger(L, 2);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// #voice -> MAXSKINVOICES
|
||||
static int skinvoices_num(lua_State *L)
|
||||
{
|
||||
lua_pushinteger(L, MAXSKINVOICES);
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum spritesopt {
|
||||
numframes = 0
|
||||
};
|
||||
|
|
@ -320,6 +355,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");
|
||||
|
|
@ -333,6 +369,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);
|
||||
|
|
|
|||
232
src/lua_voicelib.c
Normal file
232
src/lua_voicelib.c
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// 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_voicelib.c
|
||||
/// \brief voice structure library for Lua scripting
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "fastcmp.h"
|
||||
#include "r_skins.h"
|
||||
#include "sounds.h"
|
||||
#include "deh_tables.h"
|
||||
|
||||
#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
|
||||
{
|
||||
voicevars_id = 0,
|
||||
voicevars_name,
|
||||
voicevars_win,
|
||||
voicevars_lose,
|
||||
voicevars_pain,
|
||||
voicevars_attack,
|
||||
voicevars_boost,
|
||||
voicevars_overtake,
|
||||
voicevars_hitem,
|
||||
voicevars_power
|
||||
};
|
||||
|
||||
static const char *const voicevars_opt[] = {
|
||||
"id",
|
||||
"name",
|
||||
"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 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:
|
||||
return RNOGET;
|
||||
case voicevars_name:
|
||||
lua_pushstring(L, voice->name);
|
||||
break;
|
||||
case voicevars_win:
|
||||
LUA_PushUserdata(L, &voice->win, META_VOICE_ARRAY1);
|
||||
break;
|
||||
case voicevars_lose:
|
||||
LUA_PushUserdata(L, &voice->lose, META_VOICE_ARRAY1);
|
||||
break;
|
||||
case voicevars_pain:
|
||||
LUA_PushUserdata(L, voice->pain, META_VOICE_ARRAY);
|
||||
break;
|
||||
case voicevars_attack:
|
||||
LUA_PushUserdata(L, voice->attack, META_VOICE_ARRAY);
|
||||
break;
|
||||
case voicevars_boost:
|
||||
LUA_PushUserdata(L, voice->boost, META_VOICE_ARRAY);
|
||||
break;
|
||||
case voicevars_overtake:
|
||||
LUA_PushUserdata(L, &voice->overtake, META_VOICE_ARRAY1);
|
||||
break;
|
||||
case voicevars_hitem:
|
||||
LUA_PushUserdata(L, &voice->hitem, META_VOICE_ARRAY1);
|
||||
break;
|
||||
case voicevars_power:
|
||||
LUA_PushUserdata(L, &voice->power, META_VOICE_ARRAY1);
|
||||
break;
|
||||
default:
|
||||
return RNOFIELD;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
// voices are always valid, only added, never removed
|
||||
I_Assert(voice != NULL);
|
||||
|
||||
lua_pushinteger(L, voice->id);
|
||||
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-1);
|
||||
|
||||
lua_pushinteger(L, voice_array[i]);
|
||||
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 (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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 (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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
35
src/p_mobj.c
35
src/p_mobj.c
|
|
@ -12386,9 +12386,17 @@ 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)
|
||||
{
|
||||
// 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.
|
||||
mobj->voice = &skins[p->skin].voices[p->voice_id];
|
||||
}
|
||||
|
||||
mobj->health = 1;
|
||||
p->playerstate = PST_LIVE;
|
||||
|
||||
|
|
@ -14967,6 +14975,31 @@ 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 (P_MobjWasRemoved(mo))
|
||||
{
|
||||
// Nonexistent or NULL object.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mo->voice)
|
||||
{
|
||||
return mo->voice;
|
||||
}
|
||||
|
||||
#ifdef PARANOIA
|
||||
// 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,
|
||||
P_MobjTypeName(mo)
|
||||
);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Thing IDs / tags
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -1847,6 +1848,7 @@ enum mobj_diff_t
|
|||
MD3_BAKEDOFFSET,
|
||||
MD3_EXTVAL3,
|
||||
MD3_LIFETIME,
|
||||
MD3_VOICE,
|
||||
|
||||
MD__MAX
|
||||
};
|
||||
|
|
@ -2009,6 +2011,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)
|
||||
|
|
@ -2221,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);
|
||||
|
|
@ -2334,6 +2337,21 @@ 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) && mobj->skin)
|
||||
{
|
||||
if (save->write)
|
||||
WRITEUINT16(save->p, (UINT16)(mobj->voice - &((skin_t *)(mobj->skin))->voices[0]));
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
// Reset some non-synch values
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
10
src/p_user.c
10
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->voices[0]);
|
||||
|
||||
if (playervoice)
|
||||
{
|
||||
S_StartSound(player->mo, (losing ? playervoice->lose : playervoice->win));
|
||||
}
|
||||
}
|
||||
|
||||
// See Y_StartIntermission timer handling
|
||||
|
|
|
|||
368
src/r_skins.c
368
src/r_skins.c
|
|
@ -170,9 +170,72 @@ UINT8 P_KartFrameToSprite2(skin_t *skin, UINT8 inframe, UINT8 *outframe)
|
|||
return P_GetSkinSprite2(skin, spr2, NULL);
|
||||
}
|
||||
|
||||
static void Sk_SetDefaultValue(skin_t *skin)
|
||||
// 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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 sfx_kwin: // Win quote
|
||||
return voice->win;
|
||||
case sfx_klose: // Lose quote
|
||||
return voice->lose;
|
||||
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 sfx_khitem: // Hit confirm taunt
|
||||
return voice->hitem;
|
||||
case sfx_kgloat: // Power item taunt
|
||||
return voice->power;
|
||||
default:
|
||||
return sfx_thok;
|
||||
}
|
||||
}
|
||||
|
||||
static void Sk_SetDefaultValue(skin_t *skin, kartvoice_t *skin_voice)
|
||||
{
|
||||
INT32 i;
|
||||
//
|
||||
// set default skin values
|
||||
//
|
||||
|
|
@ -200,9 +263,20 @@ 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;
|
||||
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));
|
||||
|
||||
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);
|
||||
|
|
@ -540,7 +614,8 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
|||
|
||||
if (player->mo)
|
||||
{
|
||||
player->mo->skin = skin;
|
||||
R_ApplySkin(player->mo, skin);
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -564,6 +639,94 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
|||
SetPlayerSkinByNum(playernum, 0); // not found, put in the default skin
|
||||
}
|
||||
|
||||
// Gets the corresponding voice ID for a given voice's name.
|
||||
// Returns MAXSKINVOICES if nothing is found.
|
||||
INT32 R_FindIDForVoice(skin_t *skin, const char* voicename)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
// Scan through our allocated skin voices for the wanted voice.
|
||||
|
||||
// First pass: Check the actual name.
|
||||
for (i = 0; i < skin->numvoices; i++)
|
||||
{
|
||||
if (strnicmp(voicename, skin->voices[i].name, VOICENAMESIZE) == 0)
|
||||
{
|
||||
// Found a voice.
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: check the display name.
|
||||
for (i = 0; i < skin->numvoices; i++)
|
||||
{
|
||||
// Whatever. Go, my code duplication!
|
||||
if (strnicmp(voicename, skin->voices[i].realname, VOICENAMESIZE) == 0)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
INT32 voxid = R_FindIDForVoice(&skins[players[playernum].skin], voicename);
|
||||
|
||||
if (voxid == MAXSKINVOICES)
|
||||
{
|
||||
// Couldn't find a voice. Default to the skin's default.
|
||||
voxid = 0;
|
||||
}
|
||||
|
||||
SetPlayerVoiceByNum(playernum, voxid);
|
||||
}
|
||||
|
||||
// network code calls this when a 'voice change' is received
|
||||
void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum)
|
||||
{
|
||||
player_t* player = &players[playernum];
|
||||
|
||||
if (player->voice_id == voicenum)
|
||||
{
|
||||
// Hey... this voice is the same as before!
|
||||
return;
|
||||
}
|
||||
|
||||
player->voice_id = voicenum;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/** \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
|
||||
//
|
||||
|
|
@ -776,10 +939,18 @@ 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 = &skin->voices[0];
|
||||
Sk_SetDefaultValue(skin, skin_voice);
|
||||
skin->wadnum = wadnum;
|
||||
strcpy(skin->name, "sonic");
|
||||
|
||||
// Add voice
|
||||
if (R_FindIDForVoice(skin, "default") == MAXSKINVOICES)
|
||||
{
|
||||
// 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;
|
||||
|
|
@ -793,6 +964,8 @@ static void R_IHateThatHedgehog(UINT16 wadnum)
|
|||
#endif
|
||||
}
|
||||
|
||||
static kartvoice_t *allocvoice;
|
||||
|
||||
// returns whether found appropriate property
|
||||
static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
|
||||
{
|
||||
|
|
@ -912,44 +1085,149 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
|
|||
GETPATCH(facemmap)
|
||||
#undef GETPATCH
|
||||
|
||||
else // let's check if it's a sound, otherwise error out
|
||||
else if (!stricmp(stoken, "voicename"))
|
||||
{
|
||||
boolean found = false;
|
||||
sfxenum_t i;
|
||||
size_t stokenadjust;
|
||||
// Change the current voice.
|
||||
INT32 vox_id = R_FindIDForVoice(skin, value);
|
||||
|
||||
// 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 (vox_id == MAXSKINVOICES)
|
||||
{
|
||||
if (!S_sfx[i].name)
|
||||
continue;
|
||||
if (S_sfx[i].skinsound != -1
|
||||
&& !stricmp(S_sfx[i].name,
|
||||
stoken + stokenadjust))
|
||||
if (!stricmp(stoken, "default"))
|
||||
{
|
||||
skin->soundsid[S_sfx[i].skinsound] =
|
||||
S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].flags, true);
|
||||
found = true;
|
||||
// "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.
|
||||
}
|
||||
return found;
|
||||
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 (!strnicmp(stoken, "voice", 5))
|
||||
{
|
||||
// (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;
|
||||
|
||||
sfxenum_t *voicearray;
|
||||
size_t voicearraylen = R_ParseVoiceSoundKey(allocvoice, stoken+5, &voicearray);
|
||||
if (voicearraylen == 0)
|
||||
return false;
|
||||
|
||||
for (i = 0; i <= len; i++)
|
||||
{
|
||||
if (value[i] == ',' || i == len)
|
||||
{
|
||||
// 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;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
voicename[pos] = value[i];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
else // let's check if it's a legacy skinsound, otherwise error out
|
||||
{
|
||||
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;
|
||||
|
||||
static const char *tonewname[] = {
|
||||
"WIN",
|
||||
"LOSE",
|
||||
"PAIN",
|
||||
"PAIN",
|
||||
"ATTACK",
|
||||
"ATTACK",
|
||||
"BOOST",
|
||||
"BOOST",
|
||||
"SLOW",
|
||||
"HITEM",
|
||||
"GLOAT",
|
||||
};
|
||||
|
||||
sfxenum_t *voicearray;
|
||||
size_t len = R_ParseVoiceSoundKey(allocvoice, tonewname[i - sfx_kwin], &voicearray);
|
||||
if (len == 0)
|
||||
return false;
|
||||
|
||||
// grab the flags now before messing with the pointer
|
||||
INT32 flags = R_GetVoiceArraySoundFlags(allocvoice, voicearray);
|
||||
|
||||
if (i == sfx_khurt2 || i == sfx_kattk2 || i == sfx_kbost2)
|
||||
voicearray++;
|
||||
|
||||
*voicearray = S_AddSoundFx(value+2, true);
|
||||
if (*voicearray != sfx_None)
|
||||
S_sfx[*voicearray].flags = flags;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocates a voice onto the dehacked list, and iterates the provided output pointer
|
||||
// (should it exist).
|
||||
//
|
||||
INT32 R_AllocKartVoice(skin_t *skin, const char* name, INT32 *out)
|
||||
{
|
||||
*out = R_FindIDForVoice(skin, name);
|
||||
if (*out != MAXSKINVOICES)
|
||||
return -1; // already allocated
|
||||
|
||||
if (skin->numvoices == MAXSKINVOICES - 1) {
|
||||
CONS_Alert(CONS_WARNING, "Ran out of free voice slots!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strlcpy(skin->voices[skin->numvoices].name, name, sizeof(skin->voices[skin->numvoices].name));
|
||||
strlwr(skin->voices[skin->numvoices].name);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//
|
||||
// Find skin sprites, sounds & optional status bar face, & add them
|
||||
//
|
||||
|
|
@ -962,6 +1240,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 +1270,10 @@ void R_AddSkins(UINT16 wadnum)
|
|||
|
||||
// set defaults
|
||||
skin = &skins[numskins];
|
||||
Sk_SetDefaultValue(skin);
|
||||
skin_voice = &skin->voices[0];
|
||||
allocvoice = skin_voice;
|
||||
|
||||
Sk_SetDefaultValue(skin, skin_voice);
|
||||
skin->wadnum = wadnum;
|
||||
realname = false;
|
||||
// parse
|
||||
|
|
@ -1018,7 +1300,9 @@ void R_AddSkins(UINT16 wadnum)
|
|||
INT32 skinnum = R_SkinAvailable(value);
|
||||
strlwr(value);
|
||||
if (skinnum == -1)
|
||||
{
|
||||
STRBUFCPY(skin->name, value);
|
||||
}
|
||||
// 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 +1315,11 @@ 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);
|
||||
}
|
||||
Z_Free(value2);
|
||||
}
|
||||
|
||||
|
|
@ -1057,6 +1343,13 @@ void R_AddSkins(UINT16 wadnum)
|
|||
}
|
||||
free(buf2);
|
||||
|
||||
// Add voice
|
||||
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);
|
||||
}
|
||||
|
||||
// Add sprites
|
||||
R_LoadSkinSprites(wadnum, &lump, &lastlump, skin);
|
||||
//ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere
|
||||
|
|
@ -1153,7 +1446,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);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ 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"
|
||||
|
|
@ -36,6 +38,29 @@ extern "C" {
|
|||
|
||||
extern consvar_t cv_skinselectstyle, cv_skinselectsort;
|
||||
|
||||
#define MAXSKINVOICES 16 // 16 voices * 4096 skins = 65536 voices in total :^)
|
||||
|
||||
// Player voice struct
|
||||
struct kartvoice_t
|
||||
{
|
||||
UINT32 id;
|
||||
|
||||
// The voice's name.
|
||||
char name[VOICENAMESIZE+1];
|
||||
|
||||
// Display name, for things such as menus
|
||||
char realname[VOICENAMESIZE+1];
|
||||
|
||||
sfxenum_t win;
|
||||
sfxenum_t lose;
|
||||
sfxenum_t pain[VOICEARRAYSIZE];
|
||||
sfxenum_t attack[VOICEARRAYSIZE];
|
||||
sfxenum_t boost[VOICEARRAYSIZE];
|
||||
sfxenum_t overtake;
|
||||
sfxenum_t hitem;
|
||||
sfxenum_t power;
|
||||
};
|
||||
|
||||
/// The skin_t struct
|
||||
struct skin_t
|
||||
{
|
||||
|
|
@ -63,8 +88,16 @@ 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
|
||||
// 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 voices[MAXSKINVOICES];
|
||||
|
||||
UINT16 numvoices;
|
||||
|
||||
// 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.
|
||||
UINT8 soundsid[2];
|
||||
|
||||
// contains super versions too
|
||||
spritedef_t sprites[NUMPLAYERSPRITES*2];
|
||||
|
|
@ -111,10 +144,18 @@ 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);
|
||||
|
||||
INT32 R_AllocKartVoice(skin_t *skin, const char* name, INT32 *out);
|
||||
INT32 R_FindIDForVoice(skin_t *skin, const char *voicename);
|
||||
void SetPlayerVoice(INT32 playernum, const char *voicename);
|
||||
void SetPlayerVoiceByNum(INT32 playernum, UINT16 voicenum);
|
||||
sfxenum_t R_GetLegacySkinSound(const kartvoice_t *voice, sfxenum_t sound);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -603,19 +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 the sound in the skin table
|
||||
sfx_id = ((skin_t *)origin->skin)->soundsid[sfx->skinsound];
|
||||
sfx = &S_sfx[sfx_id];
|
||||
}
|
||||
|
||||
// Initialize sound parameters
|
||||
pitch = NORM_PITCH;
|
||||
priority = NORM_PRIORITY;
|
||||
|
|
@ -1274,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;
|
||||
}
|
||||
|
||||
|
|
|
|||
56
src/sounds.c
56
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]);
|
||||
|
|
|
|||
29
src/sounds.h
29
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
|
||||
|
|
|
|||
|
|
@ -376,6 +376,7 @@ TYPEDEF (visffloor_t);
|
|||
TYPEDEF (portal_t);
|
||||
|
||||
// r_skins.h
|
||||
TYPEDEF (kartvoice_t);
|
||||
TYPEDEF (skin_t);
|
||||
|
||||
// r_splats.h
|
||||
|
|
|
|||
Loading…
Reference in a new issue