Xonotic
music.qc
Go to the documentation of this file.
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 <common/weapons/_all.qh>
8  #include <common/stats.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
#define IL_EACH(this, cond, body)
#define REGISTER_NET_LINKED(id)
Definition: net.qh:67
const int SF_TRIGGER_INIT
Definition: defs.qh:22
string noise
Definition: progsdefs.qc:209
#define ReadString
#define LL_EACH(list, cond, body)
Definition: linkedlist.qh:73
float modelindex
Definition: csprogsdefs.qc:91
float cvar_settemp(string tmp_cvar, string tmp_value)
Definition: util.qc:696
#define IL_NEW()
fade_rate
Definition: projectile.qh:14
entity() spawn
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
const float ATTEN_NONE
Definition: sound.qh:27
const int CH_BGM_SINGLE
Definition: sound.qh:23
vector view_origin
Definition: main.qh:93
float intermission
Definition: csprogsdefs.qc:148
vector maxs
Definition: csprogsdefs.qc:113
#define NET_HANDLE(id, param)
Definition: net.qh:12
#define STATIC_INIT(func)
during worldspawn
Definition: static.qh:32
entity to
Definition: self.qh:96
limitations: NULL cannot be present elements can only be present once a maximum of IL_MAX lists can e...
spawnfunc(info_player_attacker)
Definition: sv_assault.qc:283
origin
Definition: ent_cs.qc:114
float spawnflags
Definition: progsdefs.qc:191
string model
Definition: csprogsdefs.qc:108
float GETTIME_CDTRACK
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
entity LL_PUSH(LinkedList this, entity e)
Push to tail.
Definition: linkedlist.qh:21
#define strcpy(this, s)
Definition: string.qh:49
#define LOG_WARNF(...)
Definition: log.qh:67
#define IS_SPEC(v)
Definition: utils.qh:10
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition: bits.qh:8
float lifetime
Definition: powerups.qc:23
const int START_DISABLED
Definition: defs.qh:7
entity msg_entity
Definition: progsdefs.qc:63
vector mins
Definition: csprogsdefs.qc:113
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig)
Definition: common.qc:138
const int ACTIVE_ACTIVE
Definition: defs.qh:37
#define NULL
Definition: post.qh:17
float autocvar_bgmvolume
Definition: bgmscript.qh:5
#define LOG_TRACEF(...)
Definition: log.qh:82
vector(float skel, float bonenum) _skel_get_boneabs_hidden
float fade_time
Definition: common.qh:22
#define _sound(e, c, s, v, a)
Definition: sound.qh:50
string targetname
Definition: progsdefs.qc:194
int active
Definition: defs.qh:34
const int SF_MUSIC_ORIGIN
Definition: music.qh:5
#define new_pure(class)
purely logical entities (.origin doesn&#39;t work)
Definition: oo.qh:62
entity int sendflags
Definition: self.qh:96
setorigin(ent, v)
#define REGISTER_NET_TEMP(id)
Definition: net.qh:33
#define strfree(this)
Definition: string.qh:56
entity csqcplayer
Definition: cl_player.qh:26
#define use
Definition: csprogsdefs.qh:50
best
Definition: all.qh:77
if(IS_DEAD(this))
Definition: impulse.qc:92
const int ACTIVE_NOT
Definition: defs.qh:36
float bgmtime
Definition: bgmscript.qh:9
float time
Definition: csprogsdefs.qc:16
IntrusiveList g_drawables
Definition: main.qh:77
#define LL_NEW()
Definition: linkedlist.qh:14
float volume
Definition: triggers.qh:47
const int SF_TRIGGER_UPDATE
Definition: defs.qh:23