Xonotic
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 
7  REGISTER_NET_TEMP(globalsound)
8  REGISTER_NET_TEMP(playersound)
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
17 
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;
26  if (!autocvar_g_debug_globalsounds) {
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:
33  if(sound_allowed(MSG_BROADCAST, from))
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));
47  entcs_force_origin(from);
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;
61  if (!autocvar_g_debug_globalsounds) {
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:
70  if(sound_allowed(MSG_BROADCAST, from))
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));
84  entcs_force_origin(from);
85  vector o = from.origin + 0.5 * (from.mins + from.maxs);
86  WriteVector(channel, o);
87  }
88  #endif
89 
90  #ifdef CSQC
91 
92  NET_HANDLE(globalsound, bool isnew)
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 
119  NET_HANDLE(playersound, bool isnew)
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 
196  entity GetVoiceMessage(string type)
197  {
198  FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == true, return it);
199  return NULL;
200  }
201 
202  entity GetPlayerSound(string type)
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;
303  strcpy(this.model_for_playersound, this.model);
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
309  && autocvar_g_debug_globalsounds
310  #endif
311  ) return;
312  if (autocvar_g_debug_defaultsounds) 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  }
356  case VOICETYPE_TEAMRADIO:
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  }
377  case VOICETYPE_AUTOTAUNT:
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
string get_model_datafilename(string m, float sk, string fil)
Definition: util.qc:1245
skin
Definition: ent_cs.qc:143
entity GetPlayerSound(string type)
Definition: globalsound.qc:202
void PrecachePlayerSounds(string f)
Definition: globalsound.qc:222
#define X(team)
const int VOICETYPE_PLAYERSOUND
Definition: globalsound.qh:64
int skin_for_playersound
Definition: globalsound.qc:296
const float ATTEN_MIN
Definition: sound.qh:28
#define sound8(e, o, chan, samp, vol, atten, speed, sf)
because sound7 didn&#39;t have origin
Definition: sound.qh:66
const int VOICETYPE_TAUNT
Definition: globalsound.qh:69
entity() spawn
#define REGISTRY_GET(id, i)
Definition: registry.qh:43
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
const float ATTEN_NONE
Definition: sound.qh:27
ERASEABLE string cdr(string s)
returns all but first word
Definition: string.qh:249
REPLICATE(cvar_cl_casings, bool, "cl_casings")
#define CS_CVAR(this)
Definition: state.qh:51
const float FILE_READ
Definition: csprogsdefs.qc:231
#define NET_HANDLE(id, param)
Definition: net.qh:12
float atten
Definition: triggers.qh:47
void UpdatePlayerSounds(entity this)
Definition: globalsound.qc:300
const int VOICETYPE_AUTOTAUNT
Definition: globalsound.qh:68
string _GetPlayerSoundSampleField(string type, bool voice)
Definition: globalsound.qc:208
string classname
Definition: csprogsdefs.qc:107
ERASEABLE string car(string s)
returns first word
Definition: string.qh:240
#define player_currententnum
Definition: main.qh:169
string model
Definition: csprogsdefs.qc:108
entity pusher
Definition: teleporters.qh:13
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
#define strcpy(this, s)
Definition: string.qh:49
#define LOG_WARNF(...)
Definition: log.qh:67
string GetPlayerSoundSampleField(string type)
Definition: globalsound.qc:247
entity msg_entity
Definition: progsdefs.qc:63
string GetVoiceMessageSampleField(string type)
Definition: globalsound.qc:217
const int VOICETYPE_LASTATTACKER
Definition: globalsound.qh:66
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
bool GetPlayerSoundSampleField_notFound
Definition: globalsound.qh:123
void animdecide_setaction(entity e, float action, float restart)
Definition: animdecide.qc:338
#define NULL
Definition: post.qh:17
#define backtrace(msg)
Definition: log.qh:105
const float VOL_BASE
Definition: sound.qh:36
#define LOG_TRACEF(...)
Definition: log.qh:82
#define SAME_TEAM(a, b)
Definition: teams.qh:239
#define IS_DEAD(s)
Definition: utils.qh:26
const float ATTEN_NORM
Definition: sound.qh:30
void PrecacheGlobalSound(string sample)
Definition: globalsound.qc:176
float scale
Definition: projectile.qc:14
float GlobalSound_pitch(float _pitch)
Definition: globalsound.qc:164
bool LoadPlayerSounds(entity this, string f, bool strict)
Definition: globalsound.qc:263
vector(float skel, float bonenum) _skel_get_boneabs_hidden
#define tokenize_console
Definition: dpextensions.qh:24
void ClearPlayerSounds(entity this)
Definition: globalsound.qc:252
string GlobalSound_sample(string pair, float r)
Definition: globalsound.qc:150
const int ANIMACTION_TAUNT
Definition: animdecide.qh:146
bool autocvar_g_debug_defaultsounds
Definition: globalsound.qc:298
string model_for_playersound
Definition: globalsound.qc:295
#define REGISTER_NET_TEMP(id)
Definition: net.qh:33
#define strfree(this)
Definition: string.qh:56
entity GetVoiceMessage(string type)
Definition: globalsound.qc:196
#define FOREACH(list, cond, body)
Definition: iter.qh:19
float entnum
Definition: csprogsdefs.qc:94
const int VOICETYPE_LASTATTACKER_ONLY
Definition: globalsound.qh:67
const int VOICETYPE_TEAMRADIO
Definition: globalsound.qh:65
#define IS_PLAYER(v)
Definition: utils.qh:9