]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mapobjects/target/music.qc
Merge branch 'master' into Mario/qc_camstuff
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mapobjects / 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                 strcpy(e.noise, noi);
241                 precache_sound(e.noise);
242                 _sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE);
243                 if(getsoundtime(e, CH_BGM_SINGLE) < 0)
244                 {
245                         LOG_TRACEF("Cannot initialize sound %s", e.noise);
246                         strfree(e.noise);
247                 }
248         }
249         e.volume = vol;
250         e.fade_time = fai;
251         e.fade_rate = fao;
252         if(vol > 0)
253         {
254                 if(tim == 0)
255                 {
256                         music_default = e;
257                         if(!music_disabled)
258                         {
259                                 e.state = 2;
260                                 cvar_settemp("music_playlist_index", "-1"); // don't use playlists
261                                 localcmd("cd stop\n"); // just in case
262                                 music_disabled = 1;
263                         }
264                 }
265                 else
266                 {
267                         music_target = e;
268                         e.lifetime = time + tim;
269                 }
270         }
271 }
272
273 void Ent_TriggerMusic_Think(entity this)
274 {
275         if(this.active == ACTIVE_NOT || intermission)
276         {
277                 return;
278         }
279         vector org = (csqcplayer) ? csqcplayer.origin : view_origin;
280         if(WarpZoneLib_BoxTouchesBrush(org + STAT(PL_MIN), org + STAT(PL_MAX), this, NULL))
281         {
282                 music_trigger = this;
283         }
284 }
285
286 void Ent_TriggerMusic_Remove(entity this)
287 {
288     strfree(this.noise);
289 }
290
291 NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew)
292 {
293         int sendflags = ReadByte();
294         if(sendflags & SF_MUSIC_ORIGIN)
295         {
296                 this.origin = ReadVector();
297         }
298         if(sendflags & SF_TRIGGER_INIT)
299         {
300                 this.modelindex = ReadShort();
301                 if(this.modelindex)
302                 {
303                         this.mins = ReadVector();
304                         this.maxs = ReadVector();
305                 }
306                 else
307                 {
308                         this.mins    = '0 0 0';
309                         this.maxs = ReadVector();
310                 }
311
312                 this.volume = ReadByte() / 255.0;
313                 this.fade_time = ReadByte() / 16.0;
314                 this.fade_rate = ReadByte() / 16.0;
315                 string s = this.noise;
316                 strcpy(this.noise, ReadString());
317                 if(this.noise != s)
318                 {
319                         precache_sound(this.noise);
320                         sound7(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE, 0, BIT(4));
321                         if(getsoundtime(this, CH_BGM_SINGLE) < 0)
322                         {
323                                 LOG_WARNF("Cannot initialize sound %s", this.noise);
324                                 strfree(this.noise);
325                         }
326                 }
327         }
328         if(sendflags & SF_TRIGGER_UPDATE)
329         {
330                 this.active = ReadByte();
331         }
332
333         setorigin(this, this.origin);
334         setsize(this, this.mins, this.maxs);
335         this.draw = Ent_TriggerMusic_Think;
336         if(isnew)
337         {
338                 LL_PUSH(TargetMusic_list, this);
339                 IL_PUSH(g_drawables, this);
340         }
341         return true;
342 }
343
344 #endif