]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/triggers/target/music.qc
trigger_music can now be controlled with relays
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / target / music.qc
1 #include "music.qh"
2 #if defined(CSQC)
3 #elif defined(MENUQC)
4 #elif defined(SVQC)
5     #include <common/constants.qh>
6     #include <common/net_linked.qh>
7     #include <server/constants.qh>
8     #include <server/defs.qh>
9 #endif
10
11 REGISTER_NET_TEMP(TE_CSQC_TARGET_MUSIC)
12 REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_MUSIC)
13
14 #ifdef SVQC
15
16 IntrusiveList g_targetmusic_list;
17 STATIC_INIT(g_targetmusic_list)
18 {
19         g_targetmusic_list = IL_NEW();
20 }
21
22 // values:
23 //   volume
24 //   noise
25 //   targetname
26 //   lifetime
27 //   fade_time
28 //   fade_rate
29 // when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0)
30 // when targetname is not set, THIS ONE is default
31 void target_music_sendto(entity this, int to, bool is)
32 {
33         WriteHeader(to, TE_CSQC_TARGET_MUSIC);
34         WriteShort(to, etof(this));
35         WriteByte(to, this.volume * 255.0 * is);
36         WriteByte(to, this.fade_time * 16.0);
37         WriteByte(to, this.fade_rate * 16.0);
38         WriteByte(to, this.lifetime);
39         WriteString(to, this.noise);
40 }
41 void target_music_reset(entity this)
42 {
43         if (this.targetname == "")
44         {
45                 target_music_sendto(this, MSG_ALL, true);
46         }
47 }
48 void target_music_kill()
49 {
50         IL_EACH(g_targetmusic_list, true,
51         {
52                 it.volume = 0;
53         if (it.targetname == "")
54             target_music_sendto(it, MSG_ALL, true);
55         else
56             target_music_sendto(it, MSG_ALL, false);
57         });
58 }
59 void target_music_use(entity this, entity actor, entity trigger)
60 {
61         if(!actor)
62                 return;
63         if(IS_REAL_CLIENT(actor))
64         {
65                 msg_entity = actor;
66                 target_music_sendto(this, MSG_ONE, true);
67         }
68         FOREACH_CLIENT(IS_SPEC(it) && it.enemy == actor, {
69                 msg_entity = it;
70                 target_music_sendto(this, MSG_ONE, true);
71         });
72 }
73 spawnfunc(target_music)
74 {
75         this.use = target_music_use;
76         this.reset = target_music_reset;
77         if(!this.volume)
78                 this.volume = 1;
79         IL_PUSH(g_targetmusic_list, this);
80         if(this.targetname == "")
81                 target_music_sendto(this, MSG_INIT, true);
82         else
83                 target_music_sendto(this, MSG_INIT, false);
84 }
85 void TargetMusic_RestoreGame()
86 {
87         IL_EACH(g_targetmusic_list, true,
88         {
89                 if(it.targetname == "")
90                         target_music_sendto(it, MSG_INIT, true);
91                 else
92                         target_music_sendto(it, MSG_INIT, false);
93         });
94 }
95 // values:
96 //   volume
97 //   noise
98 //   targetname
99 //   fade_time
100 // spawnflags:
101 //   START_DISABLED
102 // can be disabled/enabled for everyone with relays
103 bool trigger_music_SendEntity(entity this, entity to, int sendflags)
104 {
105         WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
106         WriteByte(MSG_ENTITY, sendflags);
107         if(sendflags & SF_MUSIC_ORIGIN)
108         {
109                 WriteVector(MSG_ENTITY, this.origin);
110         }
111         if(sendflags & SF_TRIGGER_INIT)
112         {
113                 if(this.model != "null")
114                 {
115                         WriteShort(MSG_ENTITY, this.modelindex);
116                         WriteVector(MSG_ENTITY, this.mins);
117                         WriteVector(MSG_ENTITY, this.maxs);
118                 }
119                 else
120                 {
121                         WriteShort(MSG_ENTITY, 0);
122                         WriteVector(MSG_ENTITY, this.maxs);
123                 }
124                 WriteByte(MSG_ENTITY, this.volume * 255.0);
125                 WriteByte(MSG_ENTITY, this.fade_time * 16.0);
126                 WriteByte(MSG_ENTITY, this.fade_rate * 16.0);
127                 WriteString(MSG_ENTITY, this.noise);
128         }
129         if(sendflags & SF_TRIGGER_UPDATE)
130         {
131                 WriteByte(MSG_ENTITY, this.active);
132         }
133         return true;
134 }
135 void trigger_music_reset(entity this)
136 {
137         if(this.spawnflags & START_DISABLED)
138         {
139                 this.setactive(this, ACTIVE_NOT);
140         }
141         else
142         {
143                 this.setactive(this, ACTIVE_ACTIVE);
144         }
145 }
146
147 spawnfunc(trigger_music)
148 {
149         if(this.model != "")
150         {
151                 _setmodel(this, this.model);
152         }
153         if(!this.volume)
154         {
155                 this.volume = 1;
156         }
157         if(!this.modelindex)
158         {
159                 setorigin(this, this.origin + this.mins);
160                 setsize(this, '0 0 0', this.maxs - this.mins);
161         }
162
163         this.setactive = generic_netlinked_setactive;
164         this.use = generic_netlinked_legacy_use; // backwards compatibility
165         this.reset = trigger_music_reset;
166         this.reset(this);
167
168         Net_LinkEntity(this, false, 0, trigger_music_SendEntity);
169 }
170 #elif defined(CSQC)
171
172 entity TargetMusic_list;
173 STATIC_INIT(TargetMusic_list)
174 {
175         TargetMusic_list = LL_NEW();
176 }
177
178 void TargetMusic_Advance()
179 {
180         // run AFTER all the thinks!
181         entity best = music_default;
182         if (music_target && time < music_target.lifetime)
183         {
184                 best = music_target;
185         }
186         if (music_trigger)
187         {
188                 best = music_trigger;
189         }
190         LL_EACH(TargetMusic_list, it.noise, {
191                 const float vol0 = (getsoundtime(it, CH_BGM_SINGLE) >= 0) ? it.lastvol : -1;
192                 if (it == best)
193                 {
194                         // increase volume
195                         it.state = (it.fade_time > 0) ? bound(0, it.state + frametime / it.fade_time, 1) : 1;
196                 }
197                 else
198                 {
199                         // decrease volume
200                         it.state = (it.fade_rate > 0) ? bound(0, it.state - frametime / it.fade_rate, 1) : 0;
201                 }
202                 const float vol = it.state * it.volume * autocvar_bgmvolume;
203                 if (vol != vol0)
204                 {
205                         if(vol0 < 0)
206                                 sound7(it, CH_BGM_SINGLE, it.noise, vol, ATTEN_NONE, 0, BIT(4)); // restart
207                         else
208                                 sound7(it, CH_BGM_SINGLE, "", vol, ATTEN_NONE, 0, BIT(4));
209                         it.lastvol = vol;
210                 }
211         });
212         music_trigger = NULL;
213         bgmtime = (best) ? getsoundtime(best, CH_BGM_SINGLE) : gettime(GETTIME_CDTRACK);
214 }
215
216 NET_HANDLE(TE_CSQC_TARGET_MUSIC, bool isNew)
217 {
218         Net_TargetMusic();
219         return true;
220 }
221
222 void Net_TargetMusic()
223 {
224         const int id = ReadShort();
225         const float vol = ReadByte() / 255.0;
226         const float fai = ReadByte() / 16.0;
227         const float fao = ReadByte() / 16.0;
228         const float tim = ReadByte();
229         const string noi = ReadString();
230
231         entity e = NULL;
232         LL_EACH(TargetMusic_list, it.count == id, { e = it; break; });
233         if (!e)
234         {
235                 LL_PUSH(TargetMusic_list, e = new_pure(TargetMusic));
236                 e.count = id;
237         }
238         if(e.noise != noi)
239         {
240                 if(e.noise)
241                         strunzone(e.noise);
242                 e.noise = strzone(noi);
243                 precache_sound(e.noise);
244                 _sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE);
245                 if(getsoundtime(e, CH_BGM_SINGLE) < 0)
246                 {
247                         LOG_TRACEF("Cannot initialize sound %s", e.noise);
248                         strunzone(e.noise);
249                         e.noise = string_null;
250                 }
251         }
252         e.volume = vol;
253         e.fade_time = fai;
254         e.fade_rate = fao;
255         if(vol > 0)
256         {
257                 if(tim == 0)
258                 {
259                         music_default = e;
260                         if(!music_disabled)
261                         {
262                                 e.state = 2;
263                                 cvar_settemp("music_playlist_index", "-1"); // don't use playlists
264                                 localcmd("cd stop\n"); // just in case
265                                 music_disabled = 1;
266                         }
267                 }
268                 else
269                 {
270                         music_target = e;
271                         e.lifetime = time + tim;
272                 }
273         }
274 }
275
276 void Ent_TriggerMusic_Think(entity this)
277 {
278         if(this.active == ACTIVE_NOT)
279         {
280                 return;
281         }
282         vector org = (csqcplayer) ? csqcplayer.origin : view_origin;
283         if(WarpZoneLib_BoxTouchesBrush(org + STAT(PL_MIN), org + STAT(PL_MAX), this, NULL))
284         {
285                 music_trigger = this;
286         }
287 }
288
289 void Ent_TriggerMusic_Remove(entity this)
290 {
291         if(this.noise)
292                 strunzone(this.noise);
293         this.noise = string_null;
294 }
295
296 NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew)
297 {
298         int sendflags = ReadByte();
299         if(sendflags & SF_MUSIC_ORIGIN)
300         {
301                 this.origin = ReadVector();
302         }
303         if(sendflags & SF_TRIGGER_INIT)
304         {
305                 this.modelindex = ReadShort();
306                 if(this.modelindex)
307                 {
308                         this.mins = ReadVector();
309                         this.maxs = ReadVector();
310                 }
311                 else
312                 {
313                         this.mins    = '0 0 0';
314                         this.maxs = ReadVector();
315                 }
316
317                 this.volume = ReadByte() / 255.0;
318                 this.fade_time = ReadByte() / 16.0;
319                 this.fade_rate = ReadByte() / 16.0;
320                 string s = this.noise;
321                 if(this.noise)
322                         strunzone(this.noise);
323                 this.noise = strzone(ReadString());
324                 if(this.noise != s)
325                 {
326                         precache_sound(this.noise);
327                         sound7(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE, 0, BIT(4));
328                         if(getsoundtime(this, CH_BGM_SINGLE) < 0)
329                         {
330                                 LOG_WARNF("Cannot initialize sound %s", this.noise);
331                                 strunzone(this.noise);
332                                 this.noise = string_null;
333                         }
334                 }
335         }
336         if(sendflags & SF_TRIGGER_UPDATE)
337         {
338                 this.active = ReadByte();
339         }
340
341         setorigin(this, this.origin);
342         setsize(this, this.mins, this.maxs);
343         this.draw = Ent_TriggerMusic_Think;
344         if(isnew)
345         {
346                 LL_PUSH(TargetMusic_list, this);
347                 IL_PUSH(g_drawables, this);
348         }
349         return true;
350 }
351
352 #endif