Xonotic
pointparticles.qc
Go to the documentation of this file.
1 #include "pointparticles.qh"
2 REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES)
3 
4 #ifdef SVQC
5 // NOTE: also contains func_sparks
6 
7 bool pointparticles_SendEntity(entity this, entity to, float sendflags)
8 {
9  WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
10 
11  // optional features to save space
12  sendflags = sendflags & 0x0F;
13  if(this.spawnflags & PARTICLES_IMPULSE)
14  sendflags |= SF_POINTPARTICLES_IMPULSE; // absolute count on toggle-on
15  if(this.movedir != '0 0 0' || this.velocity != '0 0 0')
16  sendflags |= SF_POINTPARTICLES_MOVING; // 4 bytes - saves CPU
17  if(this.waterlevel || this.count != 1)
18  sendflags |= SF_POINTPARTICLES_JITTER_AND_COUNT; // 4 bytes - obscure features almost never used
19  if(this.mins != '0 0 0' || this.maxs != '0 0 0')
20  sendflags |= SF_POINTPARTICLES_BOUNDS; // 14 bytes - saves lots of space
21 
22  WriteByte(MSG_ENTITY, sendflags);
23  if(sendflags & SF_TRIGGER_UPDATE)
24  {
25  if(this.active == ACTIVE_ACTIVE)
26  WriteCoord(MSG_ENTITY, this.impulse);
27  else
28  WriteCoord(MSG_ENTITY, 0); // off
29  }
30  if(sendflags & SF_TRIGGER_RESET)
31  {
32  WriteVector(MSG_ENTITY, this.origin);
33  }
34  if(sendflags & SF_TRIGGER_INIT)
35  {
36  if(this.model != "null")
37  {
38  WriteShort(MSG_ENTITY, this.modelindex);
39  if(sendflags & SF_POINTPARTICLES_BOUNDS)
40  {
41  WriteVector(MSG_ENTITY, this.mins);
42  WriteVector(MSG_ENTITY, this.maxs);
43  }
44  }
45  else
46  {
47  WriteShort(MSG_ENTITY, 0);
48  if(sendflags & SF_POINTPARTICLES_BOUNDS)
49  {
50  WriteVector(MSG_ENTITY, this.maxs);
51  }
52  }
53  WriteShort(MSG_ENTITY, this.cnt);
54  WriteString(MSG_ENTITY, this.mdl);
55  if(sendflags & SF_POINTPARTICLES_MOVING)
56  {
57  WriteShort(MSG_ENTITY, compressShortVector(this.velocity));
58  WriteShort(MSG_ENTITY, compressShortVector(this.movedir));
59  }
61  {
62  WriteShort(MSG_ENTITY, this.waterlevel * 16.0);
63  WriteByte(MSG_ENTITY, this.count * 16.0);
64  }
65  WriteString(MSG_ENTITY, this.noise);
66  if(this.noise != "")
67  {
68  WriteByte(MSG_ENTITY, floor(this.atten * 64));
69  WriteByte(MSG_ENTITY, floor(this.volume * 255));
70  }
71  WriteString(MSG_ENTITY, this.bgmscript);
72  if(this.bgmscript != "")
73  {
74  WriteByte(MSG_ENTITY, floor(this.bgmscriptattack * 64));
75  WriteByte(MSG_ENTITY, floor(this.bgmscriptdecay * 64));
76  WriteByte(MSG_ENTITY, floor(this.bgmscriptsustain * 255));
77  WriteByte(MSG_ENTITY, floor(this.bgmscriptrelease * 64));
78  }
79  }
80  return 1;
81 }
82 
83 void pointparticles_think(entity this)
84 {
85  if(this.origin != this.oldorigin)
86  {
87  this.SendFlags |= SF_TRIGGER_RESET;
88  this.oldorigin = this.origin;
89  }
90  this.nextthink = time;
91 }
92 
93 spawnfunc(func_pointparticles)
94 {
95  if(this.model != "") { precache_model(this.model); _setmodel(this, this.model); }
96  if(this.noise != "") precache_sound(this.noise);
97  if(this.mdl != "") this.cnt = 0; // use a good handler
98 
99  if(!this.bgmscriptsustain) this.bgmscriptsustain = 1;
100  else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0;
101 
102  if(!this.atten) this.atten = ATTEN_NORM;
103  else if(this.atten < 0) this.atten = 0;
104  if(!this.volume) this.volume = 1;
105  if(!this.count) this.count = 1;
106  if(!this.impulse) this.impulse = 1;
107 
108  if(!this.modelindex)
109  {
110  setorigin(this, this.origin + this.mins);
111  setsize(this, '0 0 0', this.maxs - this.mins);
112  }
113  //if(!this.cnt) this.cnt = _particleeffectnum(this.mdl);
114  this.setactive = generic_netlinked_setactive;
115 
116  Net_LinkEntity(this, (this.spawnflags & PARTICLES_VISCULLING), 0, pointparticles_SendEntity);
117 
118  if(this.targetname && this.targetname != "")
119  {
120  // backwards compatibility
121  this.use = generic_netlinked_legacy_use;
122  }
123  this.reset = generic_netlinked_reset;
124  this.reset(this);
125  setthink(this, pointparticles_think);
126  this.nextthink = time;
127 }
128 
129 spawnfunc(func_sparks)
130 {
131  if(this.count < 1) {
132  this.count = 25.0; // nice default value
133  }
134 
135  if(this.impulse < 0.5) {
136  this.impulse = 2.5; // nice default value
137  }
138 
139  this.mins = '0 0 0';
140  this.maxs = '0 0 0';
141  this.velocity = '0 0 -1';
142  this.mdl = "TE_SPARK";
143  this.cnt = 0; // use mdl
144 
145  spawnfunc_func_pointparticles(this);
146 }
147 #elif defined(CSQC)
148 
149 .int dphitcontentsmask;
150 
151 entityclass(PointParticles);
152 classfield(PointParticles) .int cnt; // effect number
153 classfield(PointParticles) .vector velocity; // particle velocity
154 classfield(PointParticles) .float waterlevel; // direction jitter
155 classfield(PointParticles) .int count; // count multiplier
156 classfield(PointParticles) .int impulse; // density
157 classfield(PointParticles) .string noise; // sound
158 classfield(PointParticles) .float atten;
159 classfield(PointParticles) .float volume;
160 classfield(PointParticles) .float absolute; // 1 = count per second is absolute, ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = only spawn at toggle
161 classfield(PointParticles) .vector movedir; // trace direction
162 classfield(PointParticles) .float glow_color; // palette index
163 
164 const int ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = 2;
165 
166 void Draw_PointParticles(entity this)
167 {
168  float n, i, fail;
169  vector p;
170  vector sz;
171  vector o;
172  o = this.origin;
173  sz = this.maxs - this.mins;
174  n = doBGMScript(this);
175  if(this.absolute == ABSOLUTE_ONLY_SPAWN_AT_TOGGLE)
176  {
177  if(n >= 0)
178  n = this.just_toggled ? this.impulse : 0;
179  else
180  n = this.impulse * drawframetime;
181  }
182  else
183  {
184  n *= this.impulse * drawframetime;
185  if(this.just_toggled)
186  if(n < 1)
187  n = 1;
188  }
189  if(n == 0)
190  return;
191  fail = 0;
192  for(i = random(); i <= n && fail <= 64*n; ++i)
193  {
194  p = o + this.mins;
195  p.x += random() * sz.x;
196  p.y += random() * sz.y;
197  p.z += random() * sz.z;
198  if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL))
199  {
200  if(this.movedir != '0 0 0')
201  {
202  traceline(p, p + normalize(this.movedir) * 4096, 0, NULL);
203  p = trace_endpos;
204  int eff_num;
205  if(this.cnt)
206  eff_num = this.cnt;
207  else
208  eff_num = _particleeffectnum(this.mdl);
209  __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count);
210  }
211  else
212  {
213  int eff_num;
214  if(this.cnt)
215  eff_num = this.cnt;
216  else
217  eff_num = _particleeffectnum(this.mdl);
218  __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count);
219  }
220  if(this.noise != "")
221  {
222  setorigin(this, p);
223  _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten);
224  }
225  this.just_toggled = 0;
226  }
227  else if(this.absolute)
228  {
229  ++fail;
230  --i;
231  }
232  }
233  setorigin(this, o);
234 }
235 
236 void Ent_PointParticles_Remove(entity this)
237 {
238  strfree(this.noise);
239  strfree(this.bgmscript);
240  strfree(this.mdl);
241 }
242 
243 NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
244 {
245  float i;
246  vector v;
247  int sendflags = ReadByte();
248  if(sendflags & SF_TRIGGER_UPDATE)
249  {
250  i = ReadCoord(); // density (<0: point, >0: volume)
251  if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed
252  this.just_toggled = 1;
253  this.impulse = i;
254  }
255  if(sendflags & SF_TRIGGER_RESET)
256  {
257  this.origin = ReadVector();
258  }
259  if(sendflags & SF_TRIGGER_INIT)
260  {
261  this.modelindex = ReadShort();
262  if(sendflags & SF_POINTPARTICLES_BOUNDS)
263  {
264  if(this.modelindex)
265  {
266  this.mins = ReadVector();
267  this.maxs = ReadVector();
268  }
269  else
270  {
271  this.mins = '0 0 0';
272  this.maxs = ReadVector();
273  }
274  }
275  else
276  {
277  this.mins = this.maxs = '0 0 0';
278  }
279 
280  this.cnt = ReadShort(); // effect number
281  this.mdl = strzone(ReadString()); // effect string
282 
283  if(sendflags & SF_POINTPARTICLES_MOVING)
284  {
285  this.velocity = decompressShortVector(ReadShort());
286  this.movedir = decompressShortVector(ReadShort());
287  }
288  else
289  {
290  this.velocity = this.movedir = '0 0 0';
291  }
292  if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT)
293  {
294  this.waterlevel = ReadShort() / 16.0;
295  this.count = ReadByte() / 16.0;
296  }
297  else
298  {
299  this.waterlevel = 0;
300  this.count = 1;
301  }
302  strcpy(this.noise, ReadString());
303  if(this.noise != "")
304  {
305  this.atten = ReadByte() / 64.0;
306  this.volume = ReadByte() / 255.0;
307  }
308  strcpy(this.bgmscript, ReadString());
309  if(this.bgmscript != "")
310  {
311  this.bgmscriptattack = ReadByte() / 64.0;
312  this.bgmscriptdecay = ReadByte() / 64.0;
313  this.bgmscriptsustain = ReadByte() / 255.0;
314  this.bgmscriptrelease = ReadByte() / 64.0;
315  }
316  BGMScript_InitEntity(this);
317  }
318 
319  return = true;
320 
321  if(sendflags & SF_TRIGGER_UPDATE)
322  {
323  this.absolute = (this.impulse >= 0);
324  if(!this.absolute)
325  {
326  v = this.maxs - this.mins;
327  this.impulse *= -v.x * v.y * v.z / (64**3); // relative: particles per 64^3 cube
328  }
329  }
330 
331  if(sendflags & SF_POINTPARTICLES_IMPULSE)
332  this.absolute = ABSOLUTE_ONLY_SPAWN_AT_TOGGLE;
333 
334  setorigin(this, this.origin);
335  setsize(this, this.mins, this.maxs);
336  this.solid = SOLID_NOT;
337  this.draw = Draw_PointParticles;
338  if (isnew) IL_PUSH(g_drawables, this);
339  this.entremove = Ent_PointParticles_Remove;
340 }
341 #endif
const float SOLID_NOT
Definition: csprogsdefs.qc:244
float compressShortVector(vector vec)
Definition: util.qc:406
#define REGISTER_NET_LINKED(id)
Definition: net.qh:67
const int SF_TRIGGER_INIT
Definition: defs.qh:22
const int SF_POINTPARTICLES_JITTER_AND_COUNT
string noise
Definition: progsdefs.qc:209
#define ReadString
float waterlevel
Definition: progsdefs.qc:181
float modelindex
Definition: csprogsdefs.qc:91
vector oldorigin
Definition: csprogsdefs.qc:102
entity() spawn
vector maxs
Definition: csprogsdefs.qc:113
#define classfield(name)
Definition: oo.qh:52
#define NET_HANDLE(id, param)
Definition: net.qh:12
const int SF_POINTPARTICLES_BOUNDS
float atten
Definition: triggers.qh:47
entity to
Definition: self.qh:96
spawnfunc(info_player_attacker)
Definition: sv_assault.qc:283
origin
Definition: ent_cs.qc:114
float impulse
Definition: progsdefs.qc:158
float spawnflags
Definition: progsdefs.qc:191
string model
Definition: csprogsdefs.qc:108
#define strcpy(this, s)
Definition: string.qh:49
vector movedir
Definition: progsdefs.qc:203
vector mins
Definition: csprogsdefs.qc:113
float cnt
Definition: powerups.qc:24
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
void BGMScript_InitEntity(entity e)
Definition: bgmscript.qc:116
float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig)
Definition: common.qc:138
const int SF_TRIGGER_RESET
Definition: defs.qh:24
const int ACTIVE_ACTIVE
Definition: defs.qh:37
#define NULL
Definition: post.qh:17
const int PARTICLES_IMPULSE
const float VOL_BASE
Definition: sound.qh:36
vector trace_endpos
Definition: csprogsdefs.qc:37
const int SF_POINTPARTICLES_MOVING
float glow_color
const float ATTEN_NORM
Definition: sound.qh:30
float nextthink
Definition: csprogsdefs.qc:121
vector decompressShortVector(int data)
Definition: util.qc:372
const int PARTICLES_VISCULLING
vector(float skel, float bonenum) _skel_get_boneabs_hidden
vector v
Definition: ent_cs.qc:116
float drawframetime
Definition: main.qh:92
float count
Definition: powerups.qc:22
#define _sound(e, c, s, v, a)
Definition: sound.qh:50
string targetname
Definition: progsdefs.qc:194
int active
Definition: defs.qh:34
entity int sendflags
Definition: self.qh:96
setorigin(ent, v)
float dphitcontentsmask
#define setthink(e, f)
vector trace_plane_normal
Definition: csprogsdefs.qc:38
const int CH_AMBIENT
Definition: sound.qh:24
const int SF_POINTPARTICLES_IMPULSE
#define strfree(this)
Definition: string.qh:56
#define use
Definition: csprogsdefs.qh:50
#define entityclass(...)
Definition: oo.qh:47
float doBGMScript(entity e)
Definition: bgmscript.qc:173
float time
Definition: csprogsdefs.qc:16
vector velocity
Definition: csprogsdefs.qc:103
IntrusiveList g_drawables
Definition: main.qh:77
float volume
Definition: triggers.qh:47
float solid
Definition: csprogsdefs.qc:99
const int SF_TRIGGER_UPDATE
Definition: defs.qh:23