From 4ee6ff910ba31d6a2246eeceb0f9fe40d3874619 Mon Sep 17 00:00:00 2001 From: TimePath Date: Sun, 8 Nov 2015 18:27:32 +1100 Subject: [PATCH] GlobalSound: register player sounds --- qcsrc/common/effects/qc/globalsound.qc | 167 +++++++++++++++---------- qcsrc/common/effects/qc/globalsound.qh | 117 +++++++++++------ qcsrc/common/monsters/sv_monsters.qc | 2 +- qcsrc/server/command/cmd.qc | 10 +- qcsrc/server/defs.qh | 7 -- 5 files changed, 191 insertions(+), 112 deletions(-) diff --git a/qcsrc/common/effects/qc/globalsound.qc b/qcsrc/common/effects/qc/globalsound.qc index 0d21895ec..27a0ead22 100644 --- a/qcsrc/common/effects/qc/globalsound.qc +++ b/qcsrc/common/effects/qc/globalsound.qc @@ -7,6 +7,7 @@ #endif REGISTER_NET_TEMP(globalsound) + REGISTER_NET_TEMP(playersound) #ifdef SVQC /** @@ -29,6 +30,27 @@ WriteCoord(channel, o.y); WriteCoord(channel, o.z); } + + /** + * @param from the source entity, its position is sent + * @param ps the player sound def + * @param r a random number in 0..1 + */ + void playersound(int channel, entity from, entity ps, float r, int chan, float vol, float atten) + { + if (channel == MSG_ONE && !IS_REAL_CLIENT(msg_entity)) return; + WriteHeader(channel, playersound); + WriteByte(channel, ps.m_id); + WriteByte(channel, r * 255); + WriteByte(channel, etof(from)); + WriteByte(channel, fabs(chan)); + WriteByte(channel, floor(vol * 255)); + WriteByte(channel, floor(atten * 64)); + vector o = from.origin + 0.5 * (from.mins + from.maxs); + WriteCoord(channel, o.x); + WriteCoord(channel, o.y); + WriteCoord(channel, o.z); + } #endif string GlobalSound_sample(string pair, float r); @@ -48,7 +70,7 @@ o.x = ReadCoord(); o.y = ReadCoord(); o.z = ReadCoord(); - if (who == player_localnum + 1) + if (who == player_currententnum) { // client knows better, play at current position to unlag entity e = findfloat(world, entnum, who); @@ -64,6 +86,36 @@ return true; } + NET_HANDLE(playersound, bool isnew) + { + entity ps; ps = PlayerSounds_from(ReadByte()); + float r = ReadByte() / 255; + int who = ReadByte(); + entity e = findfloat(world, entnum, autocvar_cl_forceplayermodels ? player_currententnum : who); + string s = e.(ps.m_playersoundfld); // TODO: populate this field + string sample = GlobalSound_sample(s, r); + int chan = ReadByte(); + float vol = ReadByte() / 255; + float atten = ReadByte() / 64; + vector o; + o.x = ReadCoord(); + o.y = ReadCoord(); + o.z = ReadCoord(); + if (who == player_currententnum) + { + // client knows better, play at current position to unlag + sound7(e, chan, sample, vol, atten, 0, 0); + } + else + { + entity e = new(playersound); + e.origin = o; + sound8(e, o, chan, sample, vol, atten, 0, 0); + remove(e); // debug with: e.think = SUB_Remove; e.nextthink = time + 1; + } + return true; + } + #endif string GlobalSound_sample(string pair, float r) @@ -102,37 +154,35 @@ #ifdef SVQC - int GetVoiceMessageVoiceType(string type) + entity GetVoiceMessage(string type) { - if (type == "taunt") return VOICETYPE_TAUNT; - if (type == "teamshoot") return VOICETYPE_LASTATTACKER; - return VOICETYPE_TEAMRADIO; + FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == true, LAMBDA(return it)); + return NULL; } - .string GetVoiceMessageSampleField(string type) + entity GetPlayerSound(string type) + { + FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == false, LAMBDA(return it)); + return NULL; + } + + .string _GetPlayerSoundSampleField(string type, bool voice) { GetPlayerSoundSampleField_notFound = false; - switch (type) - { - #define X(m) case #m: return playersound_##m; - ALLVOICEMSGS(X) - #undef X - } + entity e = voice ? GetVoiceMessage(type) : GetPlayerSound(type); + if (e) return e.m_playersoundfld; GetPlayerSoundSampleField_notFound = true; - return playersound_taunt; + return playersound_taunt.m_playersoundfld; + } + + .string GetVoiceMessageSampleField(string type) + { + return _GetPlayerSoundSampleField(type, true); } .string GetPlayerSoundSampleField(string type) { - GetPlayerSoundSampleField_notFound = false; - switch (type) - { - #define X(m) case #m: return playersound_##m; - ALLPLAYERSOUNDS(X) - #undef X - } - GetPlayerSoundSampleField_notFound = true; - return playersound_taunt; + return _GetPlayerSoundSampleField(type, false); } string allvoicesamples; @@ -161,29 +211,27 @@ if (!allvoicesamples) { - #define X(m) allvoicesamples = strcat(allvoicesamples, " ", #m); - ALLVOICEMSGS(X) - #undef X + FOREACH(PlayerSounds, it.instanceOfVoiceMessage, LAMBDA( + allvoicesamples = strcat(allvoicesamples, " ", it.m_playersoundstr) + )); allvoicesamples = strzone(substring(allvoicesamples, 1, -1)); } } void ClearPlayerSounds(entity this) { - #define X(m) \ - if (this.playersound_##m) \ - { \ - strunzone(this.playersound_##m); \ - this.playersound_##m = string_null; \ + FOREACH(PlayerSounds, true, LAMBDA( + .string fld = it.m_playersoundfld; + if (this.(fld)) + { + strunzone(this.(fld)); + this.(fld) = string_null; } - ALLPLAYERSOUNDS(X) - ALLVOICEMSGS(X) - #undef X + )); } - bool LoadPlayerSounds(string f, bool strict) + bool LoadPlayerSounds(entity this, string f, bool strict) { - SELFPARAM(); int fh = fopen(f, FILE_READ); if (fh < 0) { @@ -208,8 +256,8 @@ } string file = argv(1); string variants = argv(2); - if (self.(field)) strunzone(self.(field)); - self.(field) = strzone(strcat(file, " ", variants)); + if (this.(field)) strunzone(this.(field)); + this.(field) = strzone(strcat(file, " ", variants)); } fclose(fh); return true; @@ -224,20 +272,25 @@ this.modelindex_for_playersound = this.modelindex; this.skin_for_playersound = this.skin; ClearPlayerSounds(this); - LoadPlayerSounds("sound/player/default.sounds", true); + LoadPlayerSounds(this, "sound/player/default.sounds", true); if (autocvar_g_debug_defaultsounds) return; - if (!LoadPlayerSounds(get_model_datafilename(this.model, this.skin, "sounds"), false)) - LoadPlayerSounds(get_model_datafilename( + if (!LoadPlayerSounds(this, get_model_datafilename(this.model, this.skin, "sounds"), false)) + LoadPlayerSounds(this, get_model_datafilename( this.model, 0, "sounds"), true); } - void _GlobalSound(entity gs, string sample, int chan, int voicetype, bool fake) + void _GlobalSound(entity gs, entity ps, string sample, int chan, int voicetype, bool fake) { SELFPARAM(); - if (gs == NULL && sample == "") return; + if (gs == NULL && ps == NULL && sample == "") return; float r = random(); + if (ps) // TODO: remove + { + sample = this.(ps.m_playersoundfld); + ps = NULL; + } if (sample != "") sample = GlobalSound_sample(sample, r); switch (voicetype) { @@ -252,6 +305,7 @@ { float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); + else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten); else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); } } @@ -260,6 +314,7 @@ if (IS_REAL_CLIENT(msg_entity)) { if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASE, ATTEN_NONE); + else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASE, ATTEN_NONE); else soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NONE); } break; @@ -271,6 +326,7 @@ { \ float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; \ if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); \ + else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten); \ else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \ } \ while (0) @@ -306,6 +362,7 @@ ATTEN_MAX) \ : ATTEN_NONE; \ if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); \ + else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten); \ else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \ } \ } \ @@ -331,11 +388,13 @@ if (fake) { if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASE, ATTEN_NORM); + else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASE, ATTEN_NORM); else soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NORM); } else { if (gs) globalsound(MSG_ALL, this, gs, r, chan, VOL_BASE, ATTEN_NORM); + else if (ps) playersound(MSG_ALL, this, ps, r, chan, VOL_BASE, ATTEN_NORM); else _sound(this, chan, sample, VOL_BASE, ATTEN_NORM); } break; @@ -348,29 +407,5 @@ } } - void PlayerSound(.string samplefield, int chan, float voicetype) - { - SELFPARAM(); - _GlobalSound(NULL, this.(samplefield), chan, voicetype, false); - } - - void VoiceMessage(string type, string msg) - { - SELFPARAM(); - var.string sample = GetVoiceMessageSampleField(type); - if (GetPlayerSoundSampleField_notFound) - { - sprint(this, sprintf("Invalid voice. Use one of: %s\n", allvoicesamples)); - return; - } - int voicetype = GetVoiceMessageVoiceType(type); - bool ownteam = (voicetype == VOICETYPE_TEAMRADIO); - int flood = Say(this, ownteam, world, msg, true); - bool fake; - if (IS_SPEC(this) || IS_OBSERVER(this) || flood < 0) fake = true; - else if (flood > 0) fake = false; - else return; - _GlobalSound(NULL, this.(sample), CH_VOICE, voicetype, fake); - } #endif #endif diff --git a/qcsrc/common/effects/qc/globalsound.qh b/qcsrc/common/effects/qc/globalsound.qh index 926e0ba04..d120c0555 100644 --- a/qcsrc/common/effects/qc/globalsound.qh +++ b/qcsrc/common/effects/qc/globalsound.qh @@ -2,31 +2,69 @@ #define GLOBALSOUND_H // player sounds, voice messages -// TODO implemented fall and falling -#define ALLPLAYERSOUNDS(X) \ - X(death) \ - X(drown) \ - X(fall) \ - X(falling) \ - X(gasp) \ - X(jump) \ - X(pain100) \ - X(pain25) \ - X(pain50) \ - X(pain75) -#define ALLVOICEMSGS(X) \ - X(attack) \ - X(attackinfive) \ - X(coverme) \ - X(defend) \ - X(freelance) \ - X(incoming) \ - X(meet) \ - X(needhelp) \ - X(seenflag) \ - X(taunt) \ - X(teamshoot) +.string m_playersoundstr; +..string m_playersoundfld; + +REGISTRY(PlayerSounds, BITS(8) - 1) +#define PlayerSounds_from(i) _PlayerSounds_from(i, NULL) +#define REGISTER_PLAYERSOUND(id) \ + .string _playersound_##id; \ + REGISTER(RegisterPlayerSounds, playersound, PlayerSounds, id, m_id, new(PlayerSound)) \ + { \ + make_pure(this); \ + this.m_playersoundstr = #id; \ + this.m_playersoundfld = _playersound_##id; \ + } +REGISTER_REGISTRY(RegisterPlayerSounds) +REGISTRY_SORT(PlayerSounds, 0) +STATIC_INIT(PlayerSounds_renumber) { FOREACH(PlayerSounds, true, LAMBDA(it.m_id = i)); } +REGISTRY_CHECK(PlayerSounds) + +// TODO implement fall and falling + +REGISTER_PLAYERSOUND(death) +REGISTER_PLAYERSOUND(drown) +REGISTER_PLAYERSOUND(fall) +REGISTER_PLAYERSOUND(falling) +REGISTER_PLAYERSOUND(gasp) +REGISTER_PLAYERSOUND(jump) +REGISTER_PLAYERSOUND(pain100) +REGISTER_PLAYERSOUND(pain25) +REGISTER_PLAYERSOUND(pain50) +REGISTER_PLAYERSOUND(pain75) + +.bool instanceOfVoiceMessage; +.int m_playersoundvt; +#define REGISTER_VOICEMSG(id, vt) \ + .string _playersound_##id; \ + REGISTER(RegisterPlayerSounds, playersound, PlayerSounds, id, m_id, new(VoiceMessage)) \ + { \ + make_pure(this); \ + this.instanceOfVoiceMessage = true; \ + this.m_playersoundstr = #id; \ + this.m_playersoundfld = _playersound_##id; \ + this.m_playersoundvt = vt; \ + } + +const int VOICETYPE_PLAYERSOUND = 10; +const int VOICETYPE_TEAMRADIO = 11; +const int VOICETYPE_LASTATTACKER = 12; +const int VOICETYPE_LASTATTACKER_ONLY = 13; +const int VOICETYPE_AUTOTAUNT = 14; +const int VOICETYPE_TAUNT = 15; + +REGISTER_VOICEMSG(attack, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(attackinfive, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(coverme, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(defend, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(freelance, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(incoming, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(meet, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(needhelp, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(seenflag, VOICETYPE_TEAMRADIO) +REGISTER_VOICEMSG(taunt, VOICETYPE_TAUNT) +REGISTER_VOICEMSG(teamshoot, VOICETYPE_LASTATTACKER) // reserved sound names for the future (some models lack sounds for them): // _VOICEMSG(flagcarriertakingdamage) @@ -67,24 +105,31 @@ REGISTER_GLOBALSOUND(FALL_METAL, "misc/metalhitground 4") #ifdef SVQC - #define X(m) .string playersound_##m; - ALLPLAYERSOUNDS(X) - ALLVOICEMSGS(X) - #undef X - bool GetPlayerSoundSampleField_notFound; - float GetVoiceMessageVoiceType(string type); .string GetVoiceMessageSampleField(string type); .string GetPlayerSoundSampleField(string type); void PrecachePlayerSounds(string f); void ClearPlayerSounds(entity this); - float LoadPlayerSounds(string f, bool strict); + float LoadPlayerSounds(entity this, string f, bool strict); void UpdatePlayerSounds(entity this); - #define FakeGlobalSound(sample, chan, voicetype) _GlobalSound(NULL, sample, chan, voicetype, true) - void _GlobalSound(entity gs, string sample, float chan, float voicetype, bool fake); - #define GlobalSound(def, chan, voicetype) _GlobalSound(def, string_null, chan, voicetype, false) - void PlayerSound(.string samplefield, float chan, float voicetype); - void VoiceMessage(string type, string msg); + void _GlobalSound(entity gs, entity ps, string sample, float chan, float voicetype, bool fake); + #define GlobalSound(def, chan, voicetype) _GlobalSound(def, NULL, string_null, chan, voicetype, false) + #define GlobalSound_string(def, chan, voicetype) _GlobalSound(NULL, NULL, def, chan, voicetype, false) + #define PlayerSound(def, chan, voicetype) _GlobalSound(NULL, def, string_null, chan, voicetype, false) + #define VoiceMessage(def, msg) \ + do \ + { \ + entity VM = def; \ + int voicetype = VM.m_playersoundvt; \ + bool ownteam = (voicetype == VOICETYPE_TEAMRADIO); \ + int flood = Say(this, ownteam, world, msg, true); \ + bool fake; \ + if (IS_SPEC(this) || IS_OBSERVER(this) || flood < 0) fake = true; \ + else if (flood > 0) fake = false; \ + else break; \ + _GlobalSound(NULL, VM, string_null, CH_VOICE, voicetype, fake); \ + } \ + while (0) #endif diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index 6c35de85f..702e8ee39 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -346,7 +346,7 @@ void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float if(delaytoo) if(time < self.msound_delay) return; // too early - _GlobalSound(NULL, self.(samplefield), chan, VOICETYPE_PLAYERSOUND, false); + GlobalSound_string(self.(samplefield), chan, VOICETYPE_PLAYERSOUND); self.msound_delay = time + sound_delay; } diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index ac21bc62d..6cac1c4f0 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -601,8 +601,14 @@ void ClientCommand_voice(float request, float argc, string command) { if (argv(1) != "") { - if (argc >= 3) VoiceMessage(argv(1), substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2))); - else VoiceMessage(argv(1), ""); + entity e = GetVoiceMessage(argv(1)); + if (!e) + { + sprint(this, sprintf("Invalid voice. Use one of: %s\n", allvoicesamples)); + return; + } + if (argc >= 3) VoiceMessage(e, substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2))); + else VoiceMessage(e, ""); return; } diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 094eb4531..16a7c779b 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -301,13 +301,6 @@ float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end) float next_pingtime; -const float VOICETYPE_PLAYERSOUND = 10; -const float VOICETYPE_TEAMRADIO = 11; -const float VOICETYPE_LASTATTACKER = 12; -const float VOICETYPE_LASTATTACKER_ONLY = 13; -const float VOICETYPE_AUTOTAUNT = 14; -const float VOICETYPE_TAUNT = 15; - // autotaunt system .float cvar_cl_autotaunt; .float cvar_cl_voice_directional; -- 2.39.2