1 REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES)
4 // NOTE: also contains func_sparks
6 bool pointparticles_SendEntity(entity this, entity to, float fl)
8 WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
10 // optional features to save space
12 if(self.spawnflags & 2)
13 fl |= 0x10; // absolute count on toggle-on
14 if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
15 fl |= 0x20; // 4 bytes - saves CPU
16 if(self.waterlevel || self.count != 1)
17 fl |= 0x40; // 4 bytes - obscure features almost never used
18 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
19 fl |= 0x80; // 14 bytes - saves lots of space
21 WriteByte(MSG_ENTITY, fl);
25 WriteCoord(MSG_ENTITY, self.impulse);
27 WriteCoord(MSG_ENTITY, 0); // off
31 WriteCoord(MSG_ENTITY, self.origin_x);
32 WriteCoord(MSG_ENTITY, self.origin_y);
33 WriteCoord(MSG_ENTITY, self.origin_z);
37 if(self.model != "null")
39 WriteShort(MSG_ENTITY, self.modelindex);
42 WriteCoord(MSG_ENTITY, self.mins_x);
43 WriteCoord(MSG_ENTITY, self.mins_y);
44 WriteCoord(MSG_ENTITY, self.mins_z);
45 WriteCoord(MSG_ENTITY, self.maxs_x);
46 WriteCoord(MSG_ENTITY, self.maxs_y);
47 WriteCoord(MSG_ENTITY, self.maxs_z);
52 WriteShort(MSG_ENTITY, 0);
55 WriteCoord(MSG_ENTITY, self.maxs_x);
56 WriteCoord(MSG_ENTITY, self.maxs_y);
57 WriteCoord(MSG_ENTITY, self.maxs_z);
60 WriteShort(MSG_ENTITY, self.cnt);
63 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
64 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
68 WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
69 WriteByte(MSG_ENTITY, self.count * 16.0);
71 WriteString(MSG_ENTITY, self.noise);
74 WriteByte(MSG_ENTITY, floor(self.atten * 64));
75 WriteByte(MSG_ENTITY, floor(self.volume * 255));
77 WriteString(MSG_ENTITY, self.bgmscript);
78 if(self.bgmscript != "")
80 WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
81 WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
82 WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
83 WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
89 void pointparticles_use()
91 self.state = !self.state;
95 void pointparticles_think()
97 if(self.origin != self.oldorigin)
100 self.oldorigin = self.origin;
102 self.nextthink = time;
105 void pointparticles_reset()
107 if(self.spawnflags & 1)
113 spawnfunc(func_pointparticles)
116 _setmodel(self, self.model);
118 precache_sound (self.noise);
120 if(!self.bgmscriptsustain)
121 self.bgmscriptsustain = 1;
122 else if(self.bgmscriptsustain < 0)
123 self.bgmscriptsustain = 0;
126 self.atten = ATTEN_NORM;
127 else if(self.atten < 0)
138 setorigin(self, self.origin + self.mins);
139 setsize(self, '0 0 0', self.maxs - self.mins);
142 self.cnt = _particleeffectnum(self.mdl);
144 Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity);
148 self.use = pointparticles_use;
149 self.reset = pointparticles_reset;
154 self.think = pointparticles_think;
155 self.nextthink = time;
158 spawnfunc(func_sparks)
160 // self.cnt is the amount of sparks that one burst will spawn
162 self.cnt = 25.0; // nice default value
165 // self.wait is the probability that a sparkthink will spawn a spark shower
166 // range: 0 - 1, but 0 makes little sense, so...
167 if(self.wait < 0.05) {
168 self.wait = 0.25; // nice default value
171 self.count = self.cnt;
174 self.velocity = '0 0 -1';
175 self.mdl = "TE_SPARK";
176 self.impulse = 10 * self.wait; // by default 2.5/sec
178 self.cnt = 0; // use mdl
180 spawnfunc_func_pointparticles(this);
184 .int dphitcontentsmask;
186 entityclass(PointParticles);
187 class(PointParticles) .int cnt; // effect number
188 class(PointParticles) .vector velocity; // particle velocity
189 class(PointParticles) .float waterlevel; // direction jitter
190 class(PointParticles) .int count; // count multiplier
191 class(PointParticles) .int impulse; // density
192 class(PointParticles) .string noise; // sound
193 class(PointParticles) .float atten;
194 class(PointParticles) .float volume;
195 class(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
196 class(PointParticles) .vector movedir; // trace direction
197 class(PointParticles) .float glow_color; // palette index
199 void Draw_PointParticles(entity this)
206 sz = self.maxs - self.mins;
207 n = doBGMScript(self);
208 if(self.absolute == 2)
211 n = self.just_toggled ? self.impulse : 0;
213 n = self.impulse * drawframetime;
217 n *= self.impulse * drawframetime;
218 if(self.just_toggled)
225 for(i = random(); i <= n && fail <= 64*n; ++i)
228 p.x += random() * sz.x;
229 p.y += random() * sz.y;
230 p.z += random() * sz.z;
231 if(WarpZoneLib_BoxTouchesBrush(p, p, self, world))
233 if(self.movedir != '0 0 0')
235 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
237 __pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
241 __pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
246 _sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten);
248 self.just_toggled = 0;
250 else if(self.absolute)
259 void Ent_PointParticles_Remove()
262 strunzone(self.noise);
263 self.noise = string_null;
265 strunzone(self.bgmscript);
266 self.bgmscript = string_null;
269 NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
276 i = ReadCoord(); // density (<0: point, >0: volume)
277 if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
278 self.just_toggled = 1;
283 self.origin_x = ReadCoord();
284 self.origin_y = ReadCoord();
285 self.origin_z = ReadCoord();
289 self.modelindex = ReadShort();
294 self.mins_x = ReadCoord();
295 self.mins_y = ReadCoord();
296 self.mins_z = ReadCoord();
297 self.maxs_x = ReadCoord();
298 self.maxs_y = ReadCoord();
299 self.maxs_z = ReadCoord();
304 self.maxs_x = ReadCoord();
305 self.maxs_y = ReadCoord();
306 self.maxs_z = ReadCoord();
311 self.mins = self.maxs = '0 0 0';
314 self.cnt = ReadShort(); // effect number
318 self.velocity = decompressShortVector(ReadShort());
319 self.movedir = decompressShortVector(ReadShort());
323 self.velocity = self.movedir = '0 0 0';
327 self.waterlevel = ReadShort() / 16.0;
328 self.count = ReadByte() / 16.0;
336 strunzone(self.noise);
338 strunzone(self.bgmscript);
339 self.noise = strzone(ReadString());
342 self.atten = ReadByte() / 64.0;
343 self.volume = ReadByte() / 255.0;
345 self.bgmscript = strzone(ReadString());
346 if(self.bgmscript != "")
348 self.bgmscriptattack = ReadByte() / 64.0;
349 self.bgmscriptdecay = ReadByte() / 64.0;
350 self.bgmscriptsustain = ReadByte() / 255.0;
351 self.bgmscriptrelease = ReadByte() / 64.0;
353 BGMScript_InitEntity(self);
360 self.absolute = (self.impulse >= 0);
363 v = self.maxs - self.mins;
364 self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
371 setorigin(self, self.origin);
372 setsize(self, self.mins, self.maxs);
373 self.solid = SOLID_NOT;
374 self.draw = Draw_PointParticles;
375 self.entremove = Ent_PointParticles_Remove;