#include "globalsound.qh" #ifdef IMPLEMENTATION #include "../../animdecide.qh" #ifdef SVQC #include "../../../server/cl_player.qh" #endif REGISTER_NET_TEMP(globalsound) #ifdef SVQC /** * @param from the source entity, its position is sent * @param gs the global sound def * @param r a random number in 0..1 */ void globalsound(int channel, entity from, entity gs, float r, int chan, float vol, float atten) { if (channel == MSG_ONE && !IS_REAL_CLIENT(msg_entity)) return; WriteHeader(channel, globalsound); WriteByte(channel, gs.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); #ifdef CSQC NET_HANDLE(globalsound, bool isnew) { entity gs = GlobalSounds_from(ReadByte()); float r = ReadByte() / 255; string sample = GlobalSound_sample(gs.m_globalsoundstr, r); int who = ReadByte(); 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_localnum + 1) { // client knows better, play at current position to unlag entity e = findfloat(world, entnum, who); sound7(e, chan, sample, vol, atten, 0, 0); } else { entity e = new(globalsound); 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) { int n; { string s = cdr(pair); if (s) n = stof(s); else n = 0; } string sample = car(pair); if (n > 0) sample = sprintf("%s%d.wav", sample, floor(r * n + 1)); // randomization else sample = sprintf("%s.wav", sample); return sample; } void PrecacheGlobalSound(string sample) { int n; { string s = cdr(sample); if (s) n = stof(s); else n = 0; } sample = car(sample); if (n > 0) { for (int i = 1; i <= n; ++i) precache_sound(sprintf("%s%d.wav", sample, i)); } else { precache_sound(sprintf("%s.wav", sample)); } } #ifdef SVQC int GetVoiceMessageVoiceType(string type) { if (type == "taunt") return VOICETYPE_TAUNT; if (type == "teamshoot") return VOICETYPE_LASTATTACKER; return VOICETYPE_TEAMRADIO; } .string GetVoiceMessageSampleField(string type) { GetPlayerSoundSampleField_notFound = false; switch (type) { #define X(m) case #m: return playersound_##m; ALLVOICEMSGS(X) #undef X } GetPlayerSoundSampleField_notFound = true; return playersound_taunt; } .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; } string allvoicesamples; void PrecachePlayerSounds(string f) { int fh = fopen(f, FILE_READ); if (fh < 0) { LOG_WARNINGF("Player sound file not found: %s\n", f); return; } for (string s; (s = fgets(fh)); ) { int n = tokenize_console(s); if (n != 3) { if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s); continue; } string file = argv(1); string variants = argv(2); PrecacheGlobalSound(strcat(file, " ", variants)); } fclose(fh); if (!allvoicesamples) { #define X(m) allvoicesamples = strcat(allvoicesamples, " ", #m); ALLVOICEMSGS(X) #undef X 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; \ } ALLPLAYERSOUNDS(X) ALLVOICEMSGS(X) #undef X } bool LoadPlayerSounds(string f, bool strict) { SELFPARAM(); int fh = fopen(f, FILE_READ); if (fh < 0) { if (strict) LOG_WARNINGF("Player sound file not found: %s\n", f); return false; } for (string s; (s = fgets(fh)); ) { int n = tokenize_console(s); if (n != 3) { if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s); continue; } string key = argv(0); var.string field = GetPlayerSoundSampleField(key); if (GetPlayerSoundSampleField_notFound) field = GetVoiceMessageSampleField(key); if (GetPlayerSoundSampleField_notFound) { LOG_TRACEF("Invalid sound info field: %s\n", key); continue; } string file = argv(1); string variants = argv(2); if (self.(field)) strunzone(self.(field)); self.(field) = strzone(strcat(file, " ", variants)); } fclose(fh); return true; } .int modelindex_for_playersound; .int skin_for_playersound; void UpdatePlayerSounds(entity this) { if (this.modelindex == this.modelindex_for_playersound && this.skin == this.skin_for_playersound) return; this.modelindex_for_playersound = this.modelindex; this.skin_for_playersound = this.skin; ClearPlayerSounds(this); LoadPlayerSounds("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( this.model, 0, "sounds"), true); } void _GlobalSound(entity gs, string sample, int chan, int voicetype, bool fake) { SELFPARAM(); if (gs == NULL && sample == "") return; float r = random(); if (sample != "") sample = GlobalSound_sample(sample, r); switch (voicetype) { case VOICETYPE_LASTATTACKER_ONLY: case VOICETYPE_LASTATTACKER: { if (!fake) { if (!this.pusher) break; msg_entity = this.pusher; if (IS_REAL_CLIENT(msg_entity)) { 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 soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); } } if (voicetype == VOICETYPE_LASTATTACKER_ONLY) break; msg_entity = this; if (IS_REAL_CLIENT(msg_entity)) { if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASE, ATTEN_NONE); else soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NONE); } break; } case VOICETYPE_TEAMRADIO: { #define X() \ do \ { \ 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 soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \ } \ while (0) if (fake) { msg_entity = this; X(); } else { FOR_EACH_REALCLIENT(msg_entity) { if (!teamplay || msg_entity.team == this.team) X(); } } #undef X break; } case VOICETYPE_AUTOTAUNT: case VOICETYPE_TAUNT: { if (voicetype == VOICETYPE_AUTOTAUNT) if (!sv_autotaunt) { break; }else {} else if (IS_PLAYER(this) && this.deadflag == DEAD_NO) animdecide_setaction(this, ANIMACTION_TAUNT, true); if (!sv_taunt) break; if (autocvar_sv_gentle) break; float tauntrand = 0; if (voicetype == VOICETYPE_AUTOTAUNT) tauntrand = random(); #define X() \ do \ { \ if (voicetype != VOICETYPE_AUTOTAUNT || tauntrand < msg_entity.cvar_cl_autotaunt) \ { \ float atten = (msg_entity.cvar_cl_voice_directional >= 1) \ ? bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, \ ATTEN_MAX) \ : ATTEN_NONE; \ if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); \ else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \ } \ } \ while (0) if (fake) { msg_entity = this; X(); } else { FOR_EACH_REALCLIENT(msg_entity) { X(); } } #undef X break; } case VOICETYPE_PLAYERSOUND: { msg_entity = this; if (fake) { if (gs) globalsound(MSG_ONE, this, gs, 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 _sound(this, chan, sample, VOL_BASE, ATTEN_NORM); } break; } default: { backtrace("Invalid voice type!"); break; } } } 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