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