1 #include "pointparticles.qh"
2 REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES)
5 // NOTE: also contains func_sparks
7 bool pointparticles_SendEntity(entity this, entity to, float sendflags)
9 WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
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
22 WriteByte(MSG_ENTITY, sendflags);
23 if(sendflags & SF_TRIGGER_UPDATE)
25 if(this.active == ACTIVE_ACTIVE)
26 WriteCoord(MSG_ENTITY, this.impulse);
28 WriteCoord(MSG_ENTITY, 0); // off
30 if(sendflags & SF_TRIGGER_RESET)
32 WriteVector(MSG_ENTITY, this.origin);
34 if(sendflags & SF_TRIGGER_INIT)
36 if(this.model != "null")
38 WriteShort(MSG_ENTITY, this.modelindex);
39 if(sendflags & SF_POINTPARTICLES_BOUNDS)
41 WriteVector(MSG_ENTITY, this.mins);
42 WriteVector(MSG_ENTITY, this.maxs);
47 WriteShort(MSG_ENTITY, 0);
48 if(sendflags & SF_POINTPARTICLES_BOUNDS)
50 WriteVector(MSG_ENTITY, this.maxs);
53 WriteShort(MSG_ENTITY, this.cnt);
54 WriteString(MSG_ENTITY, this.mdl);
55 if(sendflags & SF_POINTPARTICLES_MOVING)
57 WriteShort(MSG_ENTITY, compressShortVector(this.velocity));
58 WriteShort(MSG_ENTITY, compressShortVector(this.movedir));
60 if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT)
62 WriteShort(MSG_ENTITY, this.waterlevel * 16.0);
63 WriteByte(MSG_ENTITY, this.count * 16.0);
65 WriteString(MSG_ENTITY, this.noise);
68 WriteByte(MSG_ENTITY, floor(this.atten * 64));
69 WriteByte(MSG_ENTITY, floor(this.volume * 255));
71 WriteString(MSG_ENTITY, this.bgmscript);
72 if(this.bgmscript != "")
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));
83 void pointparticles_think(entity this)
85 if(this.origin != this.oldorigin)
87 this.SendFlags |= SF_TRIGGER_RESET;
88 this.oldorigin = this.origin;
90 this.nextthink = time;
93 spawnfunc(func_pointparticles)
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
99 if(!this.bgmscriptsustain) this.bgmscriptsustain = 1;
100 else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0;
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;
110 setorigin(this, this.origin + this.mins);
111 setsize(this, '0 0 0', this.maxs - this.mins);
113 //if(!this.cnt) this.cnt = _particleeffectnum(this.mdl);
114 this.setactive = generic_netlinked_setactive;
116 Net_LinkEntity(this, (this.spawnflags & PARTICLES_VISCULLING), 0, pointparticles_SendEntity);
120 // backwards compatibility
121 this.use = generic_netlinked_legacy_use;
123 this.reset = generic_netlinked_reset;
125 setthink(this, pointparticles_think);
126 this.nextthink = time;
129 spawnfunc(func_sparks)
131 // this.cnt is the amount of sparks that one burst will spawn
133 this.cnt = 25.0; // nice default value
136 // this.wait is the probability that a sparkthink will spawn a spark shower
137 // range: 0 - 1, but 0 makes little sense, so...
138 if(this.wait < 0.05) {
139 this.wait = 0.25; // nice default value
142 this.count = this.cnt;
145 this.velocity = '0 0 -1';
146 this.mdl = "TE_SPARK";
147 this.impulse = 10 * this.wait; // by default 2.5/sec
149 this.cnt = 0; // use mdl
151 spawnfunc_func_pointparticles(this);
155 .int dphitcontentsmask;
157 entityclass(PointParticles);
158 class(PointParticles) .int cnt; // effect number
159 class(PointParticles) .vector velocity; // particle velocity
160 class(PointParticles) .float waterlevel; // direction jitter
161 class(PointParticles) .int count; // count multiplier
162 class(PointParticles) .int impulse; // density
163 class(PointParticles) .string noise; // sound
164 class(PointParticles) .float atten;
165 class(PointParticles) .float volume;
166 class(PointParticles) .int absolute; // 1 = count per second is absolute, ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = only spawn at toggle
167 class(PointParticles) .vector movedir; // trace direction
168 class(PointParticles) .float glow_color; // palette index
170 const int ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = 2;
172 void Draw_PointParticles(entity this)
179 sz = this.maxs - this.mins;
180 n = doBGMScript(this);
181 if(this.absolute == ABSOLUTE_ONLY_SPAWN_AT_TOGGLE)
184 n = this.just_toggled ? this.impulse : 0;
186 n = this.impulse * drawframetime;
190 n *= this.impulse * drawframetime;
191 if(this.just_toggled)
198 for(i = random(); i <= n && fail <= 64*n; ++i)
201 p.x += random() * sz.x;
202 p.y += random() * sz.y;
203 p.z += random() * sz.z;
204 if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL))
206 if(this.movedir != '0 0 0')
208 traceline(p, p + normalize(this.movedir) * 4096, 0, NULL);
214 eff_num = _particleeffectnum(this.mdl);
215 __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count);
223 eff_num = _particleeffectnum(this.mdl);
224 __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count);
229 _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten);
231 this.just_toggled = 0;
233 else if(this.absolute)
242 void Ent_PointParticles_Remove(entity this)
245 strunzone(this.noise);
246 this.noise = string_null;
248 strunzone(this.bgmscript);
249 this.bgmscript = string_null;
252 this.mdl = string_null;
255 NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
259 int sendflags = ReadByte();
260 if(sendflags & SF_TRIGGER_UPDATE)
262 i = ReadCoord(); // density (<0: point, >0: volume)
263 if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed
264 this.just_toggled = 1;
267 if(sendflags & SF_TRIGGER_RESET)
269 this.origin = ReadVector();
271 if(sendflags & SF_TRIGGER_INIT)
273 this.modelindex = ReadShort();
274 if(sendflags & SF_POINTPARTICLES_BOUNDS)
278 this.mins = ReadVector();
279 this.maxs = ReadVector();
284 this.maxs = ReadVector();
289 this.mins = this.maxs = '0 0 0';
292 this.cnt = ReadShort(); // effect number
293 this.mdl = strzone(ReadString()); // effect string
295 if(sendflags & SF_POINTPARTICLES_MOVING)
297 this.velocity = decompressShortVector(ReadShort());
298 this.movedir = decompressShortVector(ReadShort());
302 this.velocity = this.movedir = '0 0 0';
304 if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT)
306 this.waterlevel = ReadShort() / 16.0;
307 this.count = ReadByte() / 16.0;
315 strunzone(this.noise);
317 strunzone(this.bgmscript);
318 this.noise = strzone(ReadString());
321 this.atten = ReadByte() / 64.0;
322 this.volume = ReadByte() / 255.0;
324 this.bgmscript = strzone(ReadString());
325 if(this.bgmscript != "")
327 this.bgmscriptattack = ReadByte() / 64.0;
328 this.bgmscriptdecay = ReadByte() / 64.0;
329 this.bgmscriptsustain = ReadByte() / 255.0;
330 this.bgmscriptrelease = ReadByte() / 64.0;
332 BGMScript_InitEntity(this);
337 if(sendflags & SF_TRIGGER_UPDATE)
339 this.absolute = (this.impulse >= 0);
342 v = this.maxs - this.mins;
343 this.impulse *= -v.x * v.y * v.z / (64**3); // relative: particles per 64^3 cube
347 if(sendflags & SF_POINTPARTICLES_IMPULSE)
348 this.absolute = ABSOLUTE_ONLY_SPAWN_AT_TOGGLE;
350 setorigin(this, this.origin);
351 setsize(this, this.mins, this.maxs);
352 this.solid = SOLID_NOT;
353 this.draw = Draw_PointParticles;
354 if (isnew) IL_PUSH(g_drawables, this);
355 this.entremove = Ent_PointParticles_Remove;