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(entity this)
107 if(this.spawnflags & 1)
113 spawnfunc(func_pointparticles)
115 if(this.model != "") _setmodel(this, this.model);
116 if(this.noise != "") precache_sound(this.noise);
118 if(!this.bgmscriptsustain) this.bgmscriptsustain = 1;
119 else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0;
121 if(!this.atten) this.atten = ATTEN_NORM;
122 else if(this.atten < 0) this.atten = 0;
123 if(!this.volume) this.volume = 1;
124 if(!this.count) this.count = 1;
125 if(!this.impulse) this.impulse = 1;
129 setorigin(this, this.origin + this.mins);
130 setsize(this, '0 0 0', this.maxs - this.mins);
132 if(!this.cnt) this.cnt = _particleeffectnum(this.mdl);
134 Net_LinkEntity(this, (this.spawnflags & 4), 0, pointparticles_SendEntity);
138 this.use = pointparticles_use;
139 this.reset = pointparticles_reset;
144 this.think = pointparticles_think;
145 this.nextthink = time;
148 spawnfunc(func_sparks)
150 // self.cnt is the amount of sparks that one burst will spawn
152 self.cnt = 25.0; // nice default value
155 // self.wait is the probability that a sparkthink will spawn a spark shower
156 // range: 0 - 1, but 0 makes little sense, so...
157 if(self.wait < 0.05) {
158 self.wait = 0.25; // nice default value
161 self.count = self.cnt;
164 self.velocity = '0 0 -1';
165 self.mdl = "TE_SPARK";
166 self.impulse = 10 * self.wait; // by default 2.5/sec
168 self.cnt = 0; // use mdl
170 spawnfunc_func_pointparticles(this);
174 .int dphitcontentsmask;
176 entityclass(PointParticles);
177 class(PointParticles) .int cnt; // effect number
178 class(PointParticles) .vector velocity; // particle velocity
179 class(PointParticles) .float waterlevel; // direction jitter
180 class(PointParticles) .int count; // count multiplier
181 class(PointParticles) .int impulse; // density
182 class(PointParticles) .string noise; // sound
183 class(PointParticles) .float atten;
184 class(PointParticles) .float volume;
185 class(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
186 class(PointParticles) .vector movedir; // trace direction
187 class(PointParticles) .float glow_color; // palette index
189 void Draw_PointParticles(entity this)
196 sz = self.maxs - self.mins;
197 n = doBGMScript(self);
198 if(self.absolute == 2)
201 n = self.just_toggled ? self.impulse : 0;
203 n = self.impulse * drawframetime;
207 n *= self.impulse * drawframetime;
208 if(self.just_toggled)
215 for(i = random(); i <= n && fail <= 64*n; ++i)
218 p.x += random() * sz.x;
219 p.y += random() * sz.y;
220 p.z += random() * sz.z;
221 if(WarpZoneLib_BoxTouchesBrush(p, p, self, world))
223 if(self.movedir != '0 0 0')
225 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
227 __pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
231 __pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
236 _sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten);
238 self.just_toggled = 0;
240 else if(self.absolute)
249 void Ent_PointParticles_Remove()
252 strunzone(self.noise);
253 self.noise = string_null;
255 strunzone(self.bgmscript);
256 self.bgmscript = string_null;
259 NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
266 i = ReadCoord(); // density (<0: point, >0: volume)
267 if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
268 self.just_toggled = 1;
273 self.origin_x = ReadCoord();
274 self.origin_y = ReadCoord();
275 self.origin_z = ReadCoord();
279 self.modelindex = ReadShort();
284 self.mins_x = ReadCoord();
285 self.mins_y = ReadCoord();
286 self.mins_z = ReadCoord();
287 self.maxs_x = ReadCoord();
288 self.maxs_y = ReadCoord();
289 self.maxs_z = ReadCoord();
294 self.maxs_x = ReadCoord();
295 self.maxs_y = ReadCoord();
296 self.maxs_z = ReadCoord();
301 self.mins = self.maxs = '0 0 0';
304 self.cnt = ReadShort(); // effect number
308 self.velocity = decompressShortVector(ReadShort());
309 self.movedir = decompressShortVector(ReadShort());
313 self.velocity = self.movedir = '0 0 0';
317 self.waterlevel = ReadShort() / 16.0;
318 self.count = ReadByte() / 16.0;
326 strunzone(self.noise);
328 strunzone(self.bgmscript);
329 self.noise = strzone(ReadString());
332 self.atten = ReadByte() / 64.0;
333 self.volume = ReadByte() / 255.0;
335 self.bgmscript = strzone(ReadString());
336 if(self.bgmscript != "")
338 self.bgmscriptattack = ReadByte() / 64.0;
339 self.bgmscriptdecay = ReadByte() / 64.0;
340 self.bgmscriptsustain = ReadByte() / 255.0;
341 self.bgmscriptrelease = ReadByte() / 64.0;
343 BGMScript_InitEntity(self);
350 self.absolute = (self.impulse >= 0);
353 v = self.maxs - self.mins;
354 self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
361 setorigin(self, self.origin);
362 setsize(self, self.mins, self.maxs);
363 self.solid = SOLID_NOT;
364 self.draw = Draw_PointParticles;
365 self.entremove = Ent_PointParticles_Remove;