Add support for pitch shifting to the QC sound sending implementation, apply pitch...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / sounds / all.qc
1 #include "all.qh"
2 #ifdef SVQC
3
4 #include <server/utils.qh>
5
6 bool autocvar_bot_sound_monopoly;
7
8 .entity realowner;
9 bool sound_allowed(int to, entity e)
10 {
11         if(!e) return true; // save on a few checks
12         for ( ; ; )
13         {
14                 if (e.classname == "body") e = e.enemy;
15                 else if (e.realowner && e.realowner != e) e = e.realowner;
16                 else if (e.owner && e.owner != e) e = e.owner;
17                 else break;
18         }
19         // sounds to self may always pass
20         if (to == MSG_ONE && e == msg_entity) return true;
21         // sounds by players can be removed
22         if (autocvar_bot_sound_monopoly && IS_REAL_CLIENT(e)) return false;
23         // anything else may pass
24         return true;
25 }
26
27 /** hack: string precache_sound(string s) = #19; */
28 int precache_sound_index(string s) = #19;
29
30 const int SVC_SOUND = 6;
31 const int SVC_STOPSOUND = 16;
32
33 const int SND_VOLUME = BIT(0);
34 const int SND_ATTENUATION = BIT(1);
35 const int SND_LARGEENTITY = BIT(3);
36 const int SND_LARGESOUND = BIT(4);
37 const int SND_SPEEDUSHORT4000 = BIT(5);
38
39 void soundtoat(int to, entity e, vector o, int chan, string samp, float vol, float attenu, float _pitch)
40 {
41         if (!sound_allowed(to, e)) return;
42         int entno = etof(e);
43         int idx = precache_sound_index(samp);
44         attenu = floor(attenu * 64);
45         vol = floor(vol * 255);
46         int sflags = 0;
47         int speed4000 = floor((_pitch * 0.01) * 4000 + 0.5);
48         if (vol != 255) sflags |= SND_VOLUME;
49         if (attenu != 64) sflags |= SND_ATTENUATION;
50         if (entno >= 8192 || chan < 0 || chan > 7) sflags |= SND_LARGEENTITY;
51         if (idx >= 256) sflags |= SND_LARGESOUND;
52         if (speed4000 && speed4000 != 4000) sflags |= SND_SPEEDUSHORT4000;
53         WriteByte(to, SVC_SOUND);
54         WriteByte(to, sflags);
55         if (sflags & SND_VOLUME) WriteByte(to, vol);
56         if (sflags & SND_ATTENUATION) WriteByte(to, attenu);
57         if (sflags & SND_SPEEDUSHORT4000) WriteShort(to, speed4000);
58         if (sflags & SND_LARGEENTITY)
59         {
60                 WriteShort(to, entno);
61                 WriteByte(to, chan);
62         }
63         else
64         {
65                 WriteShort(to, (entno << 3) | chan);
66         }
67         if (sflags & SND_LARGESOUND) WriteShort(to, idx);
68         else WriteByte(to, idx);
69         WriteCoord(to, o.x);
70         WriteCoord(to, o.y);
71         WriteCoord(to, o.z);
72 }
73
74 void soundto(int _dest, entity e, int chan, string samp, float vol, float _atten, float _pitch)
75 {
76         if (!sound_allowed(_dest, e)) return;
77         vector o = e.origin + 0.5 * (e.mins + e.maxs);
78         soundtoat(_dest, e, o, chan, samp, vol, _atten, _pitch);
79 }
80 void soundat(entity e, vector o, int chan, string samp, float vol, float _atten)
81 {
82         soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten, 0);
83 }
84 void stopsoundto(int _dest, entity e, int chan)
85 {
86         if (!sound_allowed(_dest, e)) return;
87         int entno = etof(e);
88         if (entno >= 8192 || chan < 0 || chan > 7)
89         {
90                 int idx = precache_sound_index(SND(Null));
91                 int sflags = SND_LARGEENTITY;
92                 if (idx >= 256) sflags |= SND_LARGESOUND;
93                 WriteByte(_dest, SVC_SOUND);
94                 WriteByte(_dest, sflags);
95                 WriteShort(_dest, entno);
96                 WriteByte(_dest, chan);
97                 if (sflags & SND_LARGESOUND) WriteShort(_dest, idx);
98                 else WriteByte(_dest, idx);
99                 WriteCoord(_dest, e.origin.x);
100                 WriteCoord(_dest, e.origin.y);
101                 WriteCoord(_dest, e.origin.z);
102         }
103         else
104         {
105                 WriteByte(_dest, SVC_STOPSOUND);
106                 WriteShort(_dest, entno * 8 + chan);
107         }
108 }
109 void stopsound(entity e, int chan)
110 {
111         if (!sound_allowed(MSG_BROADCAST, e)) return;
112         stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
113         stopsoundto(MSG_ALL, e, chan);       // in case of packet loss
114 }
115
116 void play2(entity e, string filename)
117 {
118         msg_entity = e;
119         soundtoat(MSG_ONE, NULL, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE, 0);
120 }
121
122 .float spamtime;
123 /** use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame) */
124 float spamsound(entity e, int chan, Sound samp, float vol, float _atten)
125 {
126         if (!sound_allowed(MSG_BROADCAST, e)) return false;
127         if (time > e.spamtime)
128         {
129                 e.spamtime = time;
130                 sound(e, chan, samp, vol, _atten);
131                 return true;
132         }
133         return false;
134 }
135
136 void play2team(float t, string filename)
137 {
138         if (autocvar_bot_sound_monopoly) return;
139         FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.team == t, play2(it, filename));
140 }
141
142 void play2all(string samp)
143 {
144         if (autocvar_bot_sound_monopoly) return;
145         _sound(NULL, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
146 }
147
148 #endif