Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
globalsound.qc
Go to the documentation of this file.
1#include "globalsound.qh"
2
3#include <common/ent_cs.qh>
4
5 #include <common/animdecide.qh>
6
9
10 #ifdef GAMEQC
11 REPLICATE(cvar_cl_autotaunt, float, "cl_autotaunt");
12 REPLICATE(cvar_cl_voice_directional, int, "cl_voice_directional");
13 REPLICATE(cvar_cl_voice_directional_taunt_attenuation, float, "cl_voice_directional_taunt_attenuation");
14 #endif
15
16 #ifdef SVQC
22 void globalsound(int channel, entity from, entity gs, float r, int chan, float _vol, float _atten, float _pitch)
23 {
24 //assert(IS_PLAYER(from), eprint(from));
25 if (channel == MSG_ONE && !IS_REAL_CLIENT(msg_entity)) return;
27 string sample = GlobalSound_sample(gs.m_globalsoundstr, r);
28 switch (channel) {
29 case MSG_ONE:
30 soundto(channel, from, chan, sample, _vol, _atten, _pitch);
31 break;
32 case MSG_ALL:
34 sound7(from, chan, sample, _vol, _atten, _pitch, 0);
35 break;
36 }
37 return;
38 }
39 // FIXME: pitch not implemented
40 WriteHeader(channel, globalsound);
41 WriteByte(channel, gs.m_id);
42 WriteByte(channel, r * 255);
43 WriteByte(channel, etof(from));
44 WriteByte(channel, chan);
45 WriteByte(channel, floor(_vol * 255));
46 WriteByte(channel, floor(_atten * 64));
48 vector o = from.origin + 0.5 * (from.mins + from.maxs);
49 WriteVector(channel, o);
50 }
51
57 void playersound(int channel, entity from, entity ps, float r, int chan, float _vol, float _atten, float _pitch)
58 {
59 //assert(IS_PLAYER(from), eprint(from));
60 if (channel == MSG_ONE && !IS_REAL_CLIENT(msg_entity)) return;
62 //UpdatePlayerSounds(from);
63 string s = from.(ps.m_playersoundfld);
64 string sample = GlobalSound_sample(s, r);
65 switch (channel) {
66 case MSG_ONE:
67 soundto(channel, from, chan, sample, _vol, _atten, _pitch);
68 break;
69 case MSG_ALL:
71 sound7(from, chan, sample, _vol, _atten, _pitch, 0);
72 break;
73 }
74 return;
75 }
76 // FIXME: pitch not implemented
77 WriteHeader(channel, playersound);
78 WriteByte(channel, ps.m_id);
79 WriteByte(channel, r * 255);
80 WriteByte(channel, etof(from));
81 WriteByte(channel, chan);
82 WriteByte(channel, floor(_vol * 255));
83 WriteByte(channel, floor(_atten * 64));
85 vector o = from.origin + 0.5 * (from.mins + from.maxs);
86 WriteVector(channel, o);
87 }
88 #endif
89
90 #ifdef CSQC
91
93 {
94 entity gs = REGISTRY_GET(GlobalSounds, ReadByte());
95 float r = ReadByte() / 255;
96 string sample = GlobalSound_sample(gs.m_globalsoundstr, r);
97 int who = ReadByte();
98 entity e = entcs_receiver(who - 1);
99 int chan = ReadSByte();
100 float vol = ReadByte() / 255;
101 float atten = ReadByte() / 64;
102 vector o = ReadVector();
103 // TODO: is this really what we want to be doing? Footsteps that follow the player at head height?
104 if (who == player_currententnum) e = findfloat(NULL, entnum, who); // play at camera position for full volume
105 else if (e) e.origin = o;
106 if (e)
107 {
108 sound7(e, chan, sample, vol, atten, 0, 0);
109 }
110 else
111 {
112 // Can this happen?
113 LOG_WARNF("Missing entcs data for player %d", who);
114 sound8(e, o, chan, sample, vol, atten, 0, 0);
115 }
116 return true;
117 }
118
120 {
121 entity ps = REGISTRY_GET(PlayerSounds, ReadByte());
122 float r = ReadByte() / 255;
123 int who = ReadByte();
124 entity e = entcs_receiver(who - 1);
126 string s = e.(ps.m_playersoundfld);
127 string sample = GlobalSound_sample(s, r);
128 int chan = ReadSByte();
129 float vol = ReadByte() / 255;
130 float atten = ReadByte() / 64;
131 vector o = ReadVector();
132 if (who == player_currententnum) e = findfloat(NULL, entnum, who); // play at camera position for full volume
133 else if (e) e.origin = o;
134 if (e)
135 {
136 // TODO: for non-visible players, origin should probably continue to be updated as long as the sound is playing
137 sound7(e, chan, sample, vol, atten, 0, 0);
138 }
139 else
140 {
141 // Can this happen?
142 LOG_WARNF("Missing entcs data for player %d", who);
143 sound8(e, o, chan, sample, vol, atten, 0, 0);
144 }
145 return true;
146 }
147
148 #endif
149
150 string GlobalSound_sample(string pair, float r)
151 {
152 int n;
153 {
154 string s = cdr(pair);
155 if (s) n = stof(s);
156 else n = 0;
157 }
158 string sample = car(pair);
159 if (n > 0) sample = sprintf("%s%d.wav", sample, floor(r * n + 1)); // randomization
160 else sample = sprintf("%s.wav", sample);
161 return sample;
162 }
163
164 float GlobalSound_pitch(float _pitch)
165 {
166 // customizable gradient function that crosses (0,a), (c,1) and asymptotically approaches b
167 float a = 1.5; // max pitch
168 float b = 0.75; // min pitch
169 float c = 100; // standard pitch (scale * 100)
170 float d = _pitch;
171 float pitch_shift = (b*d*(a-1) + a*c*(1-b)) / (d*(a-1) + c*(1-b));
172
173 return pitch_shift * 100;
174 }
175
176 void PrecacheGlobalSound(string sample)
177 {
178 int n;
179 {
180 string s = cdr(sample);
181 if (s) n = stof(s);
182 else n = 0;
183 }
184 sample = car(sample);
185 if (n > 0)
186 {
187 for (int i = 1; i <= n; ++i)
188 precache_sound(sprintf("%s%d.wav", sample, i));
189 }
190 else
191 {
192 precache_sound(sprintf("%s.wav", sample));
193 }
194 }
195
197 {
198 FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == true, return it);
199 return NULL;
200 }
201
203 {
204 FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == false, return it);
205 return NULL;
206 }
207
208 .string _GetPlayerSoundSampleField(string type, bool voice)
209 {
211 entity e = voice ? GetVoiceMessage(type) : GetPlayerSound(type);
212 if (e) return e.m_playersoundfld;
214 return playersound_taunt.m_playersoundfld;
215 }
216
217 .string GetVoiceMessageSampleField(string type)
218 {
219 return _GetPlayerSoundSampleField(type, true);
220 }
221
222 void PrecachePlayerSounds(string f)
223 {
224 int fh = fopen(f, FILE_READ);
225 if (fh < 0)
226 {
227 LOG_WARNF("Player sound file not found: %s", f);
228 return;
229 }
230 for (string s; (s = fgets(fh)); )
231 {
232 int n = tokenize_console(s);
233 if (n != 3)
234 {
235 if (n != 0) LOG_WARNF("Invalid sound info line: %s", s);
236 continue;
237 }
238 string file = argv(1);
239 string variants = argv(2);
240 PrecacheGlobalSound(strcat(file, " ", variants));
241 }
242 fclose(fh);
243 }
244
245 //#ifdef CSQC
246
247 .string GetPlayerSoundSampleField(string type)
248 {
249 return _GetPlayerSoundSampleField(type, false);
250 }
251
253 {
254 FOREACH(PlayerSounds, true, {
255 .string fld = it.m_playersoundfld;
256 if (this.(fld))
257 {
258 strfree(this.(fld));
259 }
260 });
261 }
262
263 bool LoadPlayerSounds(entity this, string f, bool strict)
264 {
265 int fh = fopen(f, FILE_READ);
266 if (fh < 0)
267 {
268 if (strict) LOG_WARNF("Player sound file not found: %s", f);
269 return false;
270 }
271 for (string s; (s = fgets(fh)); )
272 {
273 int n = tokenize_console(s);
274 if (n != 3)
275 {
276 if (n != 0) LOG_WARNF("Invalid sound info line: %s", s);
277 continue;
278 }
279 string key = argv(0);
280 var .string field = GetPlayerSoundSampleField(key);
283 {
284 LOG_TRACEF("Invalid sound info field in player sound file '%s': %s", f, key);
285 continue;
286 }
287 string file = argv(1);
288 string variants = argv(2);
289 strcpy(this.(field), strcat(file, " ", variants));
290 }
291 fclose(fh);
292 return true;
293 }
294
297
299
301 {
302 if (this.model == this.model_for_playersound && this.skin == this.skin_for_playersound) return;
304 this.skin_for_playersound = this.skin;
305 ClearPlayerSounds(this);
306 LoadPlayerSounds(this, "sound/player/default.sounds", true);
307 if (this.model == "null"
308 #ifdef SVQC
310 #endif
311 ) return;
313 if (LoadPlayerSounds(this, get_model_datafilename(this.model, this.skin, "sounds"), false)) return;
314 LoadPlayerSounds(this, get_model_datafilename(this.model, 0, "sounds"), true);
315 }
316
317 //#endif
318
319 #ifdef SVQC
320
321 void _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, float vol, int voicetype, bool fake)
322 {
323 if (gs == NULL && ps == NULL && sample == "") return;
324 if(this.classname == "body") return;
325 float r = random();
326 float myscale = ((this.scale) ? this.scale : 1); // safety net
327 float thepitch = ((myscale == 1) ? 0 : GlobalSound_pitch(myscale * 100));
328 if (sample != "") sample = GlobalSound_sample(sample, r);
329 switch (voicetype)
330 {
333 {
334 if (!fake)
335 {
336 if (!this.pusher) break;
337 msg_entity = this.pusher;
339 {
340 float atten = (CS_CVAR(msg_entity).cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE;
341 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten, thepitch);
342 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten, thepitch);
343 else soundto(MSG_ONE, this, chan, sample, vol, atten, thepitch);
344 }
345 }
346 if (voicetype == VOICETYPE_LASTATTACKER_ONLY) break;
347 msg_entity = this;
349 {
350 if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASE, ATTEN_NONE, thepitch);
351 else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASE, ATTEN_NONE, thepitch);
352 else soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NONE, thepitch);
353 }
354 break;
355 }
357 {
358 #define X() \
359 MACRO_BEGIN \
360 float atten = (CS_CVAR(msg_entity).cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; \
361 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten, thepitch); \
362 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten, thepitch); \
363 else soundto(MSG_ONE, this, chan, sample, vol, atten, thepitch); \
364 MACRO_END
365
366 if (fake) { msg_entity = this; X(); }
367 else
368 {
369 FOREACH_CLIENT(IS_REAL_CLIENT(it) && SAME_TEAM(it, this), {
370 msg_entity = it;
371 X();
372 });
373 }
374 #undef X
375 break;
376 }
378 case VOICETYPE_TAUNT:
379 {
380 if (voicetype == VOICETYPE_AUTOTAUNT)
381 {
382 if (!autocvar_sv_autotaunt) break;
383 }
384 else if (IS_PLAYER(this) && !IS_DEAD(this))
386
387 if (!autocvar_sv_taunt) break;
388 if (autocvar_sv_gentle) break;
389 float tauntrand = 0;
390 if (voicetype == VOICETYPE_AUTOTAUNT) tauntrand = random();
391
392 #define X() \
393 MACRO_BEGIN \
394 if (voicetype != VOICETYPE_AUTOTAUNT || tauntrand < CS_CVAR(msg_entity).cvar_cl_autotaunt) \
395 { \
396 float atten = (CS_CVAR(msg_entity).cvar_cl_voice_directional >= 1) \
397 ? bound(ATTEN_MIN, CS_CVAR(msg_entity).cvar_cl_voice_directional_taunt_attenuation, \
398 ATTEN_MAX) \
399 : ATTEN_NONE; \
400 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten, thepitch); \
401 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten, thepitch); \
402 else soundto(MSG_ONE, this, chan, sample, vol, atten, thepitch); \
403 } \
404 MACRO_END
405 if (fake)
406 {
407 msg_entity = this;
408 X();
409 }
410 else
411 {
413 msg_entity = it;
414 X();
415 });
416 }
417 #undef X
418 break;
419 }
421 {
422 msg_entity = this;
423 if (fake)
424 {
425 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, ATTEN_NORM, thepitch);
426 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, ATTEN_NORM, thepitch);
427 else soundto(MSG_ONE, this, chan, sample, vol, ATTEN_NORM, thepitch);
428 }
429 else
430 {
431 if (gs) globalsound(MSG_ALL, this, gs, r, chan, vol, ATTEN_NORM, thepitch);
432 else if (ps) playersound(MSG_ALL, this, ps, r, chan, vol, ATTEN_NORM, thepitch);
433 else if (sound_allowed(MSG_BROADCAST, this)) sound7(this, chan, sample, vol, ATTEN_NORM, thepitch, 0);
434 }
435 break;
436 }
437 default:
438 {
439 backtrace("Invalid voice type!");
440 break;
441 }
442 }
443 }
444
445 #endif
void animdecide_setaction(entity e, float action, float restart)
const int ANIMACTION_TAUNT
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define player_currententnum
Definition main.qh:200
#define IS_DEAD(s)
Definition player.qh:245
#define IS_PLAYER(s)
Definition player.qh:243
string get_model_datafilename(string m, float sk, string fil)
Definition util.qc:1381
string classname
const float FILE_READ
float skin
float entnum
#define tokenize_console
model
Definition ent_cs.qc:139
void entcs_force_origin(entity player)
Definition ent_cs.qc:36
#define entcs_receiver(...)
Definition ent_cs.qh:65
void _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, float vol, int voicetype, bool fake)
entity GetVoiceMessage(string type)
void PrecacheGlobalSound(string sample)
void UpdatePlayerSounds(entity this)
bool LoadPlayerSounds(entity this, string f, bool strict)
string _GetPlayerSoundSampleField(string type, bool voice)
entity GetPlayerSound(string type)
int skin_for_playersound
string GetVoiceMessageSampleField(string type)
string GetPlayerSoundSampleField(string type)
void globalsound(int channel, entity from, entity gs, float r, int chan, float _vol, float _atten, float _pitch)
void playersound(int channel, entity from, entity ps, float r, int chan, float _vol, float _atten, float _pitch)
bool autocvar_g_debug_defaultsounds
float GlobalSound_pitch(float _pitch)
#define X()
void ClearPlayerSounds(entity this)
string GlobalSound_sample(string pair, float r)
string model_for_playersound
void PrecachePlayerSounds(string f)
const int VOICETYPE_LASTATTACKER
bool GetPlayerSoundSampleField_notFound
const int VOICETYPE_LASTATTACKER_ONLY
bool autocvar_sv_taunt
bool autocvar_g_debug_globalsounds
Use new sound handling.
Definition globalsound.qh:9
const int VOICETYPE_TAUNT
const int VOICETYPE_PLAYERSOUND
const int VOICETYPE_AUTOTAUNT
bool autocvar_sv_autotaunt
const int VOICETYPE_TEAMRADIO
#define FOREACH(list, cond, body)
Definition iter.qh:19
entity pusher
Definition laser.qc:57
#define NET_HANDLE(id, param)
Definition net.qh:15
#define ReadVector()
Definition net.qh:367
#define WriteHeader(to, id)
Definition net.qh:221
#define ReadSByte()
Definition net.qh:365
int ReadByte()
#define REGISTER_NET_TEMP(id)
Definition net.qh:28
#define REPLICATE(...)
Replicates a client cvar into a server field.
Definition replicate.qh:23
#define LOG_WARNF(...)
Definition log.qh:62
#define LOG_TRACEF(...)
Definition log.qh:77
#define backtrace(msg)
Definition log.qh:99
string fgets(float fhandle)
void fclose(float fhandle)
float MSG_ONE
Definition menudefs.qc:56
float stof(string val,...)
float fopen(string filename, float mode)
float random(void)
string precache_sound(string sample)
void WriteByte(float data, float dest, float desto)
float floor(float f)
float MSG_BROADCAST
Definition menudefs.qc:55
string argv(float n)
entity findfloat(entity start,.float field, float match)
float MSG_ALL
Definition menudefs.qc:57
#define etof(e)
Definition misc.qh:25
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NULL
Definition post.qh:14
entity msg_entity
Definition progsdefs.qc:63
float scale
Definition projectile.qc:14
#define REGISTRY_GET(id, i)
Definition registry.qh:43
vector
Definition self.qh:92
const float VOL_BASE
Definition sound.qh:36
const float ATTEN_MIN
Definition sound.qh:28
const float ATTEN_NONE
Definition sound.qh:27
const float ATTEN_NORM
Definition sound.qh:30
#define sound8(e, o, chan, samp, vol, atten, speed, sf)
because sound7 didn't have origin
Definition sound.qh:66
void soundto(int _dest, entity e, int chan, string samp, float vol, float _atten, float _pitch)
Definition all.qc:74
bool sound_allowed(int to, entity e)
Definition all.qc:9
#define CS_CVAR(this)
Definition state.qh:51
ERASEABLE string car(string s)
returns first word
Definition string.qh:259
ERASEABLE string cdr(string s)
returns all but first word
Definition string.qh:268
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
#define SAME_TEAM(a, b)
Definition teams.qh:241
float atten
Definition triggers.qh:47
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50