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 WriteHeader(channel, globalsound);
40 WriteByte(channel, gs.m_id);
41 WriteByte(channel, r * 255);
42 WriteByte(channel, etof(from));
43 WriteByte(channel, chan);
44 WriteByte(channel, floor(_vol * 255));
45 WriteByte(channel, floor(_atten * 64));
46 if(_pitch)
47 {
48 _pitch *= 0.01;
49 WriteShort(channel, floor(_pitch * 4000 + 0.5));
50 }
51 else
52 WriteShort(channel, 0);
54 vector o = from.origin + 0.5 * (from.mins + from.maxs);
55 WriteVector(channel, o);
56 }
57
63 void playersound(int channel, entity from, entity ps, float r, int chan, float _vol, float _atten, float _pitch)
64 {
65 //assert(IS_PLAYER(from), eprint(from));
66 if (channel == MSG_ONE && !IS_REAL_CLIENT(msg_entity)) return;
68 //UpdatePlayerSounds(from);
69 string s = from.(ps.m_playersoundfld);
70 string sample = GlobalSound_sample(s, r);
71 switch (channel) {
72 case MSG_ONE:
73 soundto(channel, from, chan, sample, _vol, _atten, _pitch);
74 break;
75 case MSG_ALL:
77 sound7(from, chan, sample, _vol, _atten, _pitch, 0);
78 break;
79 }
80 return;
81 }
82 WriteHeader(channel, playersound);
83 WriteByte(channel, ps.m_id);
84 WriteByte(channel, r * 255);
85 WriteByte(channel, etof(from));
86 WriteByte(channel, chan);
87 WriteByte(channel, floor(_vol * 255));
88 WriteByte(channel, floor(_atten * 64));
89 if(_pitch)
90 {
91 _pitch *= 0.01;
92 WriteShort(channel, floor(_pitch * 4000 + 0.5));
93 }
94 else
95 WriteShort(channel, 0);
97 vector o = from.origin + 0.5 * (from.mins + from.maxs);
98 WriteVector(channel, o);
99 }
100 #endif
101
102 #ifdef CSQC
103
105 {
106 entity gs = REGISTRY_GET(GlobalSounds, ReadByte());
107 float r = ReadByte() / 255;
108 string sample = GlobalSound_sample(gs.m_globalsoundstr, r);
109 int who = ReadByte();
110 entity e = entcs_receiver(who - 1);
111 int chan = ReadSByte();
112 float vol = ReadByte() / 255;
113 float atten = ReadByte() / 64;
114 float pitch = ReadShort();
115 if(pitch)
116 pitch /= 4000;
117 pitch *= 100;
118 vector o = ReadVector();
119 // TODO: is this really what we want to be doing? Footsteps that follow the player at head height?
120 if (who == player_currententnum) e = findfloat(NULL, entnum, who); // play at camera position for full volume
121 else if (e) e.origin = o;
122 if (e)
123 {
124 sound7(e, chan, sample, vol, atten, pitch, 0);
125 }
126 else
127 {
128 // Can this happen?
129 //LOG_WARNF("Missing entcs data for player %d", who);
130 sound8(e, o, chan, sample, vol, atten, pitch, 0);
131 }
132 return true;
133 }
134
136 {
137 entity ps = REGISTRY_GET(PlayerSounds, ReadByte());
138 float r = ReadByte() / 255;
139 int who = ReadByte();
140 entity e = entcs_receiver(who - 1);
142 string s = e.(ps.m_playersoundfld);
143 string sample = GlobalSound_sample(s, r);
144 int chan = ReadSByte();
145 float vol = ReadByte() / 255;
146 float atten = ReadByte() / 64;
147 float pitch = ReadShort();
148 if(pitch)
149 pitch /= 4000;
150 pitch *= 100;
151 vector o = ReadVector();
152 if (who == player_currententnum) e = findfloat(NULL, entnum, who); // play at camera position for full volume
153 else if (e) e.origin = o;
154 if (e)
155 {
156 // TODO: for non-visible players, origin should probably continue to be updated as long as the sound is playing
157 sound7(e, chan, sample, vol, atten, pitch, 0);
158 }
159 else
160 {
161 // Can this happen?
162 //LOG_WARNF("Missing entcs data for player %d", who);
163 sound8(e, o, chan, sample, vol, atten, 0, 0);
164 }
165 return true;
166 }
167
168 #endif
169
170 string GlobalSound_sample(string pair, float r)
171 {
172 int n;
173 {
174 string s = cdr(pair);
175 if (s) n = stof(s);
176 else n = 0;
177 }
178 string sample = car(pair);
179 if (n > 0) sample = sprintf("%s%d.wav", sample, floor(r * n + 1)); // randomization
180 else sample = sprintf("%s.wav", sample);
181 return sample;
182 }
183
184 float GlobalSound_pitch(float _pitch)
185 {
186 // customizable gradient function that crosses (0,a), (c,1) and asymptotically approaches b
187 float a = 1.5; // max pitch
188 float b = 0.75; // min pitch
189 float c = 100; // standard pitch (scale * 100)
190 float d = _pitch;
191 float pitch_shift = (b*d*(a-1) + a*c*(1-b)) / (d*(a-1) + c*(1-b));
192
193 return pitch_shift * 100;
194 }
195
196 void PrecacheGlobalSound(string sample)
197 {
198 int n;
199 {
200 string s = cdr(sample);
201 if (s) n = stof(s);
202 else n = 0;
203 }
204 sample = car(sample);
205 if (n > 0)
206 {
207 for (int i = 1; i <= n; ++i)
208 precache_sound(sprintf("%s%d.wav", sample, i));
209 }
210 else
211 {
212 precache_sound(sprintf("%s.wav", sample));
213 }
214 }
215
217 {
218 FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == true, return it);
219 return NULL;
220 }
221
223 {
224 FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == false, return it);
225 return NULL;
226 }
227
228 .string _GetPlayerSoundSampleField(string type, bool voice)
229 {
231 entity e = voice ? GetVoiceMessage(type) : GetPlayerSound(type);
232 if (e) return e.m_playersoundfld;
234 return playersound_taunt.m_playersoundfld;
235 }
236
237 .string GetVoiceMessageSampleField(string type)
238 {
239 return _GetPlayerSoundSampleField(type, true);
240 }
241
242 void PrecachePlayerSounds(string f)
243 {
244 int fh = fopen(f, FILE_READ);
245 if (fh < 0)
246 {
247 LOG_WARNF("Player sound file not found: %s", f);
248 return;
249 }
250 for (string s; (s = fgets(fh)); )
251 {
252 int n = tokenize_console(s);
253 if (n != 3)
254 {
255 if (n != 0) LOG_WARNF("Invalid sound info line: %s", s);
256 continue;
257 }
258 string file = argv(1);
259 string variants = argv(2);
260 PrecacheGlobalSound(strcat(file, " ", variants));
261 }
262 fclose(fh);
263 }
264
265 //#ifdef CSQC
266
267 .string GetPlayerSoundSampleField(string type)
268 {
269 return _GetPlayerSoundSampleField(type, false);
270 }
271
273 {
274 FOREACH(PlayerSounds, true, {
275 .string fld = it.m_playersoundfld;
276 if (this.(fld))
277 {
278 strfree(this.(fld));
279 }
280 });
281 }
282
283 bool LoadPlayerSounds(entity this, string f, bool strict)
284 {
285 int fh = fopen(f, FILE_READ);
286 if (fh < 0)
287 {
288 if (strict) LOG_WARNF("Player sound file not found: %s", f);
289 return false;
290 }
291 for (string s; (s = fgets(fh)); )
292 {
293 int n = tokenize_console(s);
294 if (n != 3)
295 {
296 if (n != 0) LOG_WARNF("Invalid sound info line: %s", s);
297 continue;
298 }
299 string key = argv(0);
300 var .string field = GetPlayerSoundSampleField(key);
303 {
304 LOG_TRACEF("Invalid sound info field in player sound file '%s': %s", f, key);
305 continue;
306 }
307 string file = argv(1);
308 string variants = argv(2);
309 strcpy(this.(field), strcat(file, " ", variants));
310 }
311 fclose(fh);
312 return true;
313 }
314
317
319
321 {
322 if (this.model == this.model_for_playersound && this.skin == this.skin_for_playersound) return;
324 this.skin_for_playersound = this.skin;
325 ClearPlayerSounds(this);
326 LoadPlayerSounds(this, "sound/player/default.sounds", true);
327 if (this.model == "null"
328 #ifdef SVQC
330 #endif
331 ) return;
333 if (LoadPlayerSounds(this, get_model_datafilename(this.model, this.skin, "sounds"), false)) return;
334 LoadPlayerSounds(this, get_model_datafilename(this.model, 0, "sounds"), true);
335 }
336
337 //#endif
338
339 #ifdef SVQC
340
341 void _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, float vol, int voicetype, bool fake, float pitchscale)
342 {
343 if (gs == NULL && ps == NULL && sample == "") return;
344 if(this.classname == "body") return;
345 float r = random();
346 // sv_q3compat_changehitbox model scaling should not cause pitch shifting
348 pitchscale *= this.scale;
349 float thepitch = (pitchscale == 1) ? 0 : GlobalSound_pitch(pitchscale * 100);
350 if (sample != "") sample = GlobalSound_sample(sample, r);
351 switch (voicetype)
352 {
355 {
356 if (!fake)
357 {
358 if (!this.pusher) break;
359 msg_entity = this.pusher;
361 {
362 float atten = (CS_CVAR(msg_entity).cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE;
363 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten, thepitch);
364 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten, thepitch);
365 else soundto(MSG_ONE, this, chan, sample, vol, atten, thepitch);
366 }
367 }
368 if (voicetype == VOICETYPE_LASTATTACKER_ONLY) break;
369 msg_entity = this;
371 {
372 if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASE, ATTEN_NONE, thepitch);
373 else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASE, ATTEN_NONE, thepitch);
374 else soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NONE, thepitch);
375 }
376 break;
377 }
379 {
380 #define X() \
381 MACRO_BEGIN \
382 float atten = (CS_CVAR(msg_entity).cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; \
383 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten, thepitch); \
384 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten, thepitch); \
385 else soundto(MSG_ONE, this, chan, sample, vol, atten, thepitch); \
386 MACRO_END
387
388 if (fake) { msg_entity = this; X(); }
389 else
390 {
391 FOREACH_CLIENT(IS_REAL_CLIENT(it) && SAME_TEAM(it, this), {
392 msg_entity = it;
393 X();
394 });
395 }
396 #undef X
397 break;
398 }
400 case VOICETYPE_TAUNT:
401 {
402 if (voicetype == VOICETYPE_AUTOTAUNT)
403 {
404 if (!autocvar_sv_autotaunt) break;
405 }
406 else if (IS_PLAYER(this) && !IS_DEAD(this))
408
409 if (!autocvar_sv_taunt) break;
410 if (autocvar_sv_gentle) break;
411 float tauntrand = 0;
412 if (voicetype == VOICETYPE_AUTOTAUNT) tauntrand = random();
413
414 #define X() \
415 MACRO_BEGIN \
416 if (voicetype != VOICETYPE_AUTOTAUNT || tauntrand < CS_CVAR(msg_entity).cvar_cl_autotaunt) \
417 { \
418 float atten = (CS_CVAR(msg_entity).cvar_cl_voice_directional >= 1) \
419 ? bound(ATTEN_MIN, CS_CVAR(msg_entity).cvar_cl_voice_directional_taunt_attenuation, \
420 ATTEN_MAX) \
421 : ATTEN_NONE; \
422 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten, thepitch); \
423 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten, thepitch); \
424 else soundto(MSG_ONE, this, chan, sample, vol, atten, thepitch); \
425 } \
426 MACRO_END
427 if (fake)
428 {
429 msg_entity = this;
430 X();
431 }
432 else
433 {
435 msg_entity = it;
436 X();
437 });
438 }
439 #undef X
440 break;
441 }
443 {
444 msg_entity = this;
445 if (fake)
446 {
447 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, ATTEN_NORM, thepitch);
448 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, ATTEN_NORM, thepitch);
449 else soundto(MSG_ONE, this, chan, sample, vol, ATTEN_NORM, thepitch);
450 }
451 else
452 {
453 if (gs) globalsound(MSG_ALL, this, gs, r, chan, vol, ATTEN_NORM, thepitch);
454 else if (ps) playersound(MSG_ALL, this, ps, r, chan, vol, ATTEN_NORM, thepitch);
455 else if (sound_allowed(MSG_BROADCAST, this)) sound7(this, chan, sample, vol, ATTEN_NORM, thepitch, 0);
456 }
457 break;
458 }
459 default:
460 {
461 backtrace("Invalid voice type!");
462 break;
463 }
464 }
465 }
466
467 #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:244
#define IS_PLAYER(s)
Definition player.qh:242
string get_model_datafilename(string m, float sk, string fil)
Definition util.qc:1487
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
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 _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, float vol, int voicetype, bool fake, float pitchscale)
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
float pitch
Definition halflife.qc:5
#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)
void WriteShort(float data, float dest, float desto)
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
q3compat
Definition quake3.qc:59
bool autocvar_sv_q3compat_changehitbox
Definition quake3.qh:7
#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:52