2 // NOTE: also contains func_sparks
4 bool pointparticles_SendEntity(entity this, entity to, float fl)
6 WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
8 // optional features to save space
10 if(self.spawnflags & 2)
11 fl |= 0x10; // absolute count on toggle-on
12 if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
13 fl |= 0x20; // 4 bytes - saves CPU
14 if(self.waterlevel || self.count != 1)
15 fl |= 0x40; // 4 bytes - obscure features almost never used
16 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
17 fl |= 0x80; // 14 bytes - saves lots of space
19 WriteByte(MSG_ENTITY, fl);
23 WriteCoord(MSG_ENTITY, self.impulse);
25 WriteCoord(MSG_ENTITY, 0); // off
29 WriteCoord(MSG_ENTITY, self.origin_x);
30 WriteCoord(MSG_ENTITY, self.origin_y);
31 WriteCoord(MSG_ENTITY, self.origin_z);
35 if(self.model != "null")
37 WriteShort(MSG_ENTITY, self.modelindex);
40 WriteCoord(MSG_ENTITY, self.mins_x);
41 WriteCoord(MSG_ENTITY, self.mins_y);
42 WriteCoord(MSG_ENTITY, self.mins_z);
43 WriteCoord(MSG_ENTITY, self.maxs_x);
44 WriteCoord(MSG_ENTITY, self.maxs_y);
45 WriteCoord(MSG_ENTITY, self.maxs_z);
50 WriteShort(MSG_ENTITY, 0);
53 WriteCoord(MSG_ENTITY, self.maxs_x);
54 WriteCoord(MSG_ENTITY, self.maxs_y);
55 WriteCoord(MSG_ENTITY, self.maxs_z);
58 WriteShort(MSG_ENTITY, self.cnt);
61 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
62 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
66 WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
67 WriteByte(MSG_ENTITY, self.count * 16.0);
69 WriteString(MSG_ENTITY, self.noise);
72 WriteByte(MSG_ENTITY, floor(self.atten * 64));
73 WriteByte(MSG_ENTITY, floor(self.volume * 255));
75 WriteString(MSG_ENTITY, self.bgmscript);
76 if(self.bgmscript != "")
78 WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
79 WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
80 WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
81 WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
87 void pointparticles_use()
89 self.state = !self.state;
93 void pointparticles_think()
95 if(self.origin != self.oldorigin)
98 self.oldorigin = self.origin;
100 self.nextthink = time;
103 void pointparticles_reset()
105 if(self.spawnflags & 1)
111 spawnfunc(func_pointparticles)
114 _setmodel(self, self.model);
116 precache_sound (self.noise);
118 if(!self.bgmscriptsustain)
119 self.bgmscriptsustain = 1;
120 else if(self.bgmscriptsustain < 0)
121 self.bgmscriptsustain = 0;
124 self.atten = ATTEN_NORM;
125 else if(self.atten < 0)
136 setorigin(self, self.origin + self.mins);
137 setsize(self, '0 0 0', self.maxs - self.mins);
140 self.cnt = _particleeffectnum(self.mdl);
142 Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity);
146 self.use = pointparticles_use;
147 self.reset = pointparticles_reset;
152 self.think = pointparticles_think;
153 self.nextthink = time;
156 spawnfunc(func_sparks)
158 // self.cnt is the amount of sparks that one burst will spawn
160 self.cnt = 25.0; // nice default value
163 // self.wait is the probability that a sparkthink will spawn a spark shower
164 // range: 0 - 1, but 0 makes little sense, so...
165 if(self.wait < 0.05) {
166 self.wait = 0.25; // nice default value
169 self.count = self.cnt;
172 self.velocity = '0 0 -1';
173 self.mdl = "TE_SPARK";
174 self.impulse = 10 * self.wait; // by default 2.5/sec
176 self.cnt = 0; // use mdl
178 spawnfunc_func_pointparticles(this);
182 .int dphitcontentsmask;
184 entityclass(PointParticles);
185 class(PointParticles) .int cnt; // effect number
186 class(PointParticles) .vector velocity; // particle velocity
187 class(PointParticles) .float waterlevel; // direction jitter
188 class(PointParticles) .int count; // count multiplier
189 class(PointParticles) .int impulse; // density
190 class(PointParticles) .string noise; // sound
191 class(PointParticles) .float atten;
192 class(PointParticles) .float volume;
193 class(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
194 class(PointParticles) .vector movedir; // trace direction
195 class(PointParticles) .float glow_color; // palette index
197 void Draw_PointParticles(entity this)
204 sz = self.maxs - self.mins;
205 n = doBGMScript(self);
206 if(self.absolute == 2)
209 n = self.just_toggled ? self.impulse : 0;
211 n = self.impulse * drawframetime;
215 n *= self.impulse * drawframetime;
216 if(self.just_toggled)
223 for(i = random(); i <= n && fail <= 64*n; ++i)
226 p.x += random() * sz.x;
227 p.y += random() * sz.y;
228 p.z += random() * sz.z;
229 if(WarpZoneLib_BoxTouchesBrush(p, p, self, world))
231 if(self.movedir != '0 0 0')
233 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
235 __pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
239 __pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
244 _sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten);
246 self.just_toggled = 0;
248 else if(self.absolute)
257 void Ent_PointParticles_Remove()
260 strunzone(self.noise);
261 self.noise = string_null;
263 strunzone(self.bgmscript);
264 self.bgmscript = string_null;
267 NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
274 i = ReadCoord(); // density (<0: point, >0: volume)
275 if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
276 self.just_toggled = 1;
281 self.origin_x = ReadCoord();
282 self.origin_y = ReadCoord();
283 self.origin_z = ReadCoord();
287 self.modelindex = ReadShort();
292 self.mins_x = ReadCoord();
293 self.mins_y = ReadCoord();
294 self.mins_z = ReadCoord();
295 self.maxs_x = ReadCoord();
296 self.maxs_y = ReadCoord();
297 self.maxs_z = ReadCoord();
302 self.maxs_x = ReadCoord();
303 self.maxs_y = ReadCoord();
304 self.maxs_z = ReadCoord();
309 self.mins = self.maxs = '0 0 0';
312 self.cnt = ReadShort(); // effect number
316 self.velocity = decompressShortVector(ReadShort());
317 self.movedir = decompressShortVector(ReadShort());
321 self.velocity = self.movedir = '0 0 0';
325 self.waterlevel = ReadShort() / 16.0;
326 self.count = ReadByte() / 16.0;
334 strunzone(self.noise);
336 strunzone(self.bgmscript);
337 self.noise = strzone(ReadString());
340 self.atten = ReadByte() / 64.0;
341 self.volume = ReadByte() / 255.0;
343 self.bgmscript = strzone(ReadString());
344 if(self.bgmscript != "")
346 self.bgmscriptattack = ReadByte() / 64.0;
347 self.bgmscriptdecay = ReadByte() / 64.0;
348 self.bgmscriptsustain = ReadByte() / 255.0;
349 self.bgmscriptrelease = ReadByte() / 64.0;
351 BGMScript_InitEntity(self);
358 self.absolute = (self.impulse >= 0);
361 v = self.maxs - self.mins;
362 self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
369 setorigin(self, self.origin);
370 setsize(self, self.mins, self.maxs);
371 self.solid = SOLID_NOT;
372 self.draw = Draw_PointParticles;
373 self.entremove = Ent_PointParticles_Remove;