]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/effects/qc/globalsound.qc
Merge branch 'master' into Mirio/balance
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / effects / qc / globalsound.qc
1 #include "globalsound.qh"
2
3 #include <common/ent_cs.qh>
4
5 #ifdef IMPLEMENTATION
6         #include <common/animdecide.qh>
7
8         #ifdef SVQC
9                 #include <server/player.qh>
10         #endif
11
12         REGISTER_NET_TEMP(globalsound)
13         REGISTER_NET_TEMP(playersound)
14
15         string GlobalSound_sample(string pair, float r);
16
17         #ifdef SVQC
18                 /**
19                  * @param from the source entity, its position is sent
20                  * @param gs the global sound def
21                  * @param r a random number in 0..1
22                  */
23                 void globalsound(int channel, entity from, entity gs, float r, int chan, float _vol, float _atten)
24                 {
25                         //assert(IS_PLAYER(from), eprint(from));
26                         if (channel == MSG_ONE && !IS_REAL_CLIENT(msg_entity)) return;
27                         if (!autocvar_g_debug_globalsounds) {
28                                 string sample = GlobalSound_sample(gs.m_globalsoundstr, r);
29                                 switch (channel) {
30                                         case MSG_ONE:
31                                                 soundto(channel, from, chan, sample, _vol, _atten);
32                                                 break;
33                                         case MSG_ALL:
34                                                 _sound(from, chan, sample, _vol, _atten);
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                         entcs_force_origin(from);
47                         vector o = from.origin + 0.5 * (from.mins + from.maxs);
48                         WriteCoord(channel, o.x);
49                         WriteCoord(channel, o.y);
50                         WriteCoord(channel, o.z);
51                 }
52
53                 /**
54                 * @param from the source entity, its position is sent
55                 * @param ps the player sound def
56                 * @param r a random number in 0..1
57                 */
58                 void playersound(int channel, entity from, entity ps, float r, int chan, float _vol, float _atten)
59                 {
60                         //assert(IS_PLAYER(from), eprint(from));
61                         if (channel == MSG_ONE && !IS_REAL_CLIENT(msg_entity)) return;
62                         if (!autocvar_g_debug_globalsounds) {
63                                 //UpdatePlayerSounds(from);
64                                 string s = from.(ps.m_playersoundfld);
65                                 string sample = GlobalSound_sample(s, r);
66                                 switch (channel) {
67                                         case MSG_ONE:
68                                                 soundto(channel, from, chan, sample, _vol, _atten);
69                                                 break;
70                                         case MSG_ALL:
71                                                 _sound(from, chan, sample, _vol, _atten);
72                                                 break;
73                                 }
74                                 return;
75                         }
76                         WriteHeader(channel, playersound);
77                         WriteByte(channel, ps.m_id);
78                         WriteByte(channel, r * 255);
79                         WriteByte(channel, etof(from));
80                         WriteByte(channel, chan);
81                         WriteByte(channel, floor(_vol * 255));
82                         WriteByte(channel, floor(_atten * 64));
83                         entcs_force_origin(from);
84                         vector o = from.origin + 0.5 * (from.mins + from.maxs);
85                         WriteCoord(channel, o.x);
86                         WriteCoord(channel, o.y);
87                         WriteCoord(channel, o.z);
88                 }
89         #endif
90
91         #ifdef CSQC
92
93                 NET_HANDLE(globalsound, bool isnew)
94                 {
95                         entity gs = GlobalSounds_from(ReadByte());
96                         float r = ReadByte() / 255;
97                         string sample = GlobalSound_sample(gs.m_globalsoundstr, r);
98                         int who = ReadByte();
99                         entity e = entcs_receiver(who - 1);
100                         int chan = ReadSByte();
101                         float vol = ReadByte() / 255;
102                         float atten = ReadByte() / 64;
103                         vector o;
104                         o.x = ReadCoord();
105                         o.y = ReadCoord();
106                         o.z = ReadCoord();
107                         // TODO: is this really what we want to be doing? Footsteps that follow the player at head height?
108                         if (who == player_currententnum) e = findfloat(NULL, entnum, who);  // play at camera position for full volume
109                         else if (e) e.origin = o;
110                         if (e)
111                         {
112                                 sound7(e, chan, sample, vol, atten, 0, 0);
113                         }
114                         else
115                         {
116                                 // Can this happen?
117                                 LOG_WARNF("Missing entcs data for player %d", who);
118                                 sound8(e, o, chan, sample, vol, atten, 0, 0);
119                         }
120                         return true;
121                 }
122
123                 NET_HANDLE(playersound, bool isnew)
124                 {
125                         entity ps = PlayerSounds_from(ReadByte());
126                         float r = ReadByte() / 255;
127                         int who = ReadByte();
128                         entity e = entcs_receiver(who - 1);
129                         UpdatePlayerSounds(e);
130                         string s = e.(ps.m_playersoundfld);
131                         string sample = GlobalSound_sample(s, r);
132                         int chan = ReadSByte();
133                         float vol = ReadByte() / 255;
134                         float atten = ReadByte() / 64;
135                         vector o;
136                         o.x = ReadCoord();
137                         o.y = ReadCoord();
138                         o.z = ReadCoord();
139                         if (who == player_currententnum) e = findfloat(NULL, entnum, who);  // play at camera position for full volume
140                         else if (e) e.origin = o;
141                         if (e)
142                         {
143                                 // TODO: for non-visible players, origin should probably continue to be updated as long as the sound is playing
144                                 sound7(e, chan, sample, vol, atten, 0, 0);
145                         }
146                         else
147                         {
148                                 // Can this happen?
149                                 LOG_WARNF("Missing entcs data for player %d", who);
150                                 sound8(e, o, chan, sample, vol, atten, 0, 0);
151                         }
152                         return true;
153                 }
154
155         #endif
156
157         string GlobalSound_sample(string pair, float r)
158         {
159                 int n;
160                 {
161                         string s = cdr(pair);
162                         if (s) n = stof(s);
163                         else n = 0;
164                 }
165                 string sample = car(pair);
166                 if (n > 0) sample = sprintf("%s%d.wav", sample, floor(r * n + 1));  // randomization
167                 else sample = sprintf("%s.wav", sample);
168                 return sample;
169         }
170
171         void PrecacheGlobalSound(string sample)
172         {
173                 int n;
174                 {
175                         string s = cdr(sample);
176                         if (s) n = stof(s);
177                         else n = 0;
178                 }
179                 sample = car(sample);
180                 if (n > 0)
181                 {
182                         for (int i = 1; i <= n; ++i)
183                                 precache_sound(sprintf("%s%d.wav", sample, i));
184                 }
185                 else
186                 {
187                         precache_sound(sprintf("%s.wav", sample));
188                 }
189         }
190
191         entity GetVoiceMessage(string type)
192         {
193                 FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == true, return it);
194                 return NULL;
195         }
196
197         entity GetPlayerSound(string type)
198         {
199                 FOREACH(PlayerSounds, it.m_playersoundstr == type && it.instanceOfVoiceMessage == false, return it);
200                 return NULL;
201         }
202
203         .string _GetPlayerSoundSampleField(string type, bool voice)
204         {
205                 GetPlayerSoundSampleField_notFound = false;
206                 entity e = voice ? GetVoiceMessage(type) : GetPlayerSound(type);
207                 if (e) return e.m_playersoundfld;
208                 GetPlayerSoundSampleField_notFound = true;
209                 return playersound_taunt.m_playersoundfld;
210         }
211
212         .string GetVoiceMessageSampleField(string type)
213         {
214                 return _GetPlayerSoundSampleField(type, true);
215         }
216
217         void PrecachePlayerSounds(string f)
218         {
219                 int fh = fopen(f, FILE_READ);
220                 if (fh < 0)
221                 {
222                         LOG_WARNF("Player sound file not found: %s", f);
223                         return;
224                 }
225                 for (string s; (s = fgets(fh)); )
226                 {
227                         int n = tokenize_console(s);
228                         if (n != 3)
229                         {
230                                 if (n != 0) LOG_WARNF("Invalid sound info line: %s", s);
231                                 continue;
232                         }
233                         string file = argv(1);
234                         string variants = argv(2);
235                         PrecacheGlobalSound(strcat(file, " ", variants));
236                 }
237                 fclose(fh);
238         }
239
240         //#ifdef CSQC
241
242                 .string GetPlayerSoundSampleField(string type)
243                 {
244                         return _GetPlayerSoundSampleField(type, false);
245                 }
246
247                 void ClearPlayerSounds(entity this)
248                 {
249                         FOREACH(PlayerSounds, true, {
250                                 .string fld = it.m_playersoundfld;
251                                 if (this.(fld))
252                                 {
253                                         strunzone(this.(fld));
254                                         this.(fld) = string_null;
255                                 }
256                         });
257                 }
258
259                 bool LoadPlayerSounds(entity this, string f, bool strict)
260                 {
261                         int fh = fopen(f, FILE_READ);
262                         if (fh < 0)
263                         {
264                                 if (strict) LOG_WARNF("Player sound file not found: %s", f);
265                                 return false;
266                         }
267                         for (string s; (s = fgets(fh)); )
268                         {
269                                 int n = tokenize_console(s);
270                                 if (n != 3)
271                                 {
272                                         if (n != 0) LOG_WARNF("Invalid sound info line: %s", s);
273                                         continue;
274                                 }
275                                 string key = argv(0);
276                                 var.string field = GetPlayerSoundSampleField(key);
277                                 if (GetPlayerSoundSampleField_notFound) field = GetVoiceMessageSampleField(key);
278                                 if (GetPlayerSoundSampleField_notFound)
279                                 {
280                                         LOG_TRACEF("Invalid sound info field: %s", key);
281                                         continue;
282                                 }
283                                 string file = argv(1);
284                                 string variants = argv(2);
285                                 if (this.(field)) strunzone(this.(field));
286                                 this.(field) = strzone(strcat(file, " ", variants));
287                         }
288                         fclose(fh);
289                         return true;
290                 }
291
292                 .string model_for_playersound;
293                 .int skin_for_playersound;
294
295                 bool autocvar_g_debug_defaultsounds;
296
297                 void UpdatePlayerSounds(entity this)
298                 {
299                         if (this.model == this.model_for_playersound && this.skin == this.skin_for_playersound) return;
300                         if (this.model_for_playersound) strunzone(this.model_for_playersound);
301                         this.model_for_playersound = strzone(this.model);
302                         this.skin_for_playersound = this.skin;
303                         ClearPlayerSounds(this);
304                         LoadPlayerSounds(this, "sound/player/default.sounds", true);
305                         if (this.model == "null"
306                         #ifdef SVQC
307                             && autocvar_g_debug_globalsounds
308                         #endif
309                          ) return;
310                         if (autocvar_g_debug_defaultsounds) return;
311                         if (LoadPlayerSounds(this, get_model_datafilename(this.model, this.skin, "sounds"), false)) return;
312                         LoadPlayerSounds(this, get_model_datafilename(this.model, 0, "sounds"), true);
313                 }
314
315         //#endif
316
317         #ifdef SVQC
318
319                 void _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, float vol, int voicetype, bool fake)
320                 {
321                         if (gs == NULL && ps == NULL && sample == "") return;
322                         if(this.classname == "body") return;
323                         float r = random();
324                         if (sample != "") sample = GlobalSound_sample(sample, r);
325                         switch (voicetype)
326                         {
327                                 case VOICETYPE_LASTATTACKER_ONLY:
328                                 case VOICETYPE_LASTATTACKER:
329                                 {
330                                         if (!fake)
331                                         {
332                                                 if (!this.pusher) break;
333                                                 msg_entity = this.pusher;
334                                                 if (IS_REAL_CLIENT(msg_entity))
335                                                 {
336                                                         float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE;
337                                                         if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten);
338                                                         else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten);
339                                                         else soundto(MSG_ONE, this, chan, sample, vol, atten);
340                                                 }
341                                         }
342                                         if (voicetype == VOICETYPE_LASTATTACKER_ONLY) break;
343                                         msg_entity = this;
344                                         if (IS_REAL_CLIENT(msg_entity))
345                                         {
346                                                 if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASE, ATTEN_NONE);
347                                                 else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASE, ATTEN_NONE);
348                                                 else soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NONE);
349                                         }
350                                         break;
351                                 }
352                                 case VOICETYPE_TEAMRADIO:
353                                 {
354                                         #define X() \
355                                                 MACRO_BEGIN \
356                                                 { \
357                                                         float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; \
358                                                         if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten); \
359                                                         else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten); \
360                                                         else soundto(MSG_ONE, this, chan, sample, vol, atten); \
361                                                 } MACRO_END
362
363                                         if (fake) { msg_entity = this; X(); }
364                                         else
365                                         {
366                                                 FOREACH_CLIENT(IS_REAL_CLIENT(it) && SAME_TEAM(it, this), {
367                                                         msg_entity = it;
368                                                         X();
369                                                 });
370                                         }
371                 #undef X
372                                         break;
373                                 }
374                                 case VOICETYPE_AUTOTAUNT:
375                                 case VOICETYPE_TAUNT:
376                                 {
377                                         if (voicetype == VOICETYPE_AUTOTAUNT) if (!sv_autotaunt) { break; }else {}
378                                         else if (IS_PLAYER(this) && !IS_DEAD(this)) animdecide_setaction(this, ANIMACTION_TAUNT,
379                                                         true);
380                                         if (!sv_taunt) break;
381                                         if (autocvar_sv_gentle) break;
382                                         float tauntrand = 0;
383                                         if (voicetype == VOICETYPE_AUTOTAUNT) tauntrand = random();
384                                         #define X() \
385                                                 MACRO_BEGIN \
386                                                 { \
387                                                         if (voicetype != VOICETYPE_AUTOTAUNT || tauntrand < msg_entity.cvar_cl_autotaunt) \
388                                                         { \
389                                                                 float atten = (msg_entity.cvar_cl_voice_directional >= 1) \
390                                                                     ? bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, \
391                                                                         ATTEN_MAX) \
392                                                                         : ATTEN_NONE; \
393                                                                 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten); \
394                                                                 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten); \
395                                                                 else soundto(MSG_ONE, this, chan, sample, vol, atten); \
396                                                         } \
397                                                 } MACRO_END
398                                         if (fake)
399                                         {
400                                                 msg_entity = this;
401                                                 X();
402                                         }
403                                         else
404                                         {
405                                                 FOREACH_CLIENT(IS_REAL_CLIENT(it), {
406                                                         msg_entity = it;
407                                                         X();
408                                                 });
409                                         }
410                 #undef X
411                                         break;
412                                 }
413                                 case VOICETYPE_PLAYERSOUND:
414                                 {
415                                         msg_entity = this;
416                                         if (fake)
417                                         {
418                                                 if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, ATTEN_NORM);
419                                                 else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, ATTEN_NORM);
420                                                 else soundto(MSG_ONE, this, chan, sample, vol, ATTEN_NORM);
421                                         }
422                                         else
423                                         {
424                                                 if (gs) globalsound(MSG_ALL, this, gs, r, chan, vol, ATTEN_NORM);
425                                                 else if (ps) playersound(MSG_ALL, this, ps, r, chan, vol, ATTEN_NORM);
426                                                 else _sound(this, chan, sample, vol, ATTEN_NORM);
427                                         }
428                                         break;
429                                 }
430                                 default:
431                                 {
432                                         backtrace("Invalid voice type!");
433                                         break;
434                                 }
435                         }
436                 }
437
438         #endif
439 #endif