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 fl)
9 WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
11 // optional features to save space
13 if(this.spawnflags & 2)
14 fl |= 0x10; // absolute count on toggle-on
15 if(this.movedir != '0 0 0' || this.velocity != '0 0 0')
16 fl |= 0x20; // 4 bytes - saves CPU
17 if(this.waterlevel || this.count != 1)
18 fl |= 0x40; // 4 bytes - obscure features almost never used
19 if(this.mins != '0 0 0' || this.maxs != '0 0 0')
20 fl |= 0x80; // 14 bytes - saves lots of space
22 WriteByte(MSG_ENTITY, fl);
26 WriteCoord(MSG_ENTITY, this.impulse);
28 WriteCoord(MSG_ENTITY, 0); // off
32 WriteCoord(MSG_ENTITY, this.origin_x);
33 WriteCoord(MSG_ENTITY, this.origin_y);
34 WriteCoord(MSG_ENTITY, this.origin_z);
38 if(this.model != "null")
40 WriteShort(MSG_ENTITY, this.modelindex);
43 WriteCoord(MSG_ENTITY, this.mins_x);
44 WriteCoord(MSG_ENTITY, this.mins_y);
45 WriteCoord(MSG_ENTITY, this.mins_z);
46 WriteCoord(MSG_ENTITY, this.maxs_x);
47 WriteCoord(MSG_ENTITY, this.maxs_y);
48 WriteCoord(MSG_ENTITY, this.maxs_z);
53 WriteShort(MSG_ENTITY, 0);
56 WriteCoord(MSG_ENTITY, this.maxs_x);
57 WriteCoord(MSG_ENTITY, this.maxs_y);
58 WriteCoord(MSG_ENTITY, this.maxs_z);
61 WriteShort(MSG_ENTITY, this.cnt);
62 WriteString(MSG_ENTITY, this.mdl);
65 WriteShort(MSG_ENTITY, compressShortVector(this.velocity));
66 WriteShort(MSG_ENTITY, compressShortVector(this.movedir));
70 WriteShort(MSG_ENTITY, this.waterlevel * 16.0);
71 WriteByte(MSG_ENTITY, this.count * 16.0);
73 WriteString(MSG_ENTITY, this.noise);
76 WriteByte(MSG_ENTITY, floor(this.atten * 64));
77 WriteByte(MSG_ENTITY, floor(this.volume * 255));
79 WriteString(MSG_ENTITY, this.bgmscript);
80 if(this.bgmscript != "")
82 WriteByte(MSG_ENTITY, floor(this.bgmscriptattack * 64));
83 WriteByte(MSG_ENTITY, floor(this.bgmscriptdecay * 64));
84 WriteByte(MSG_ENTITY, floor(this.bgmscriptsustain * 255));
85 WriteByte(MSG_ENTITY, floor(this.bgmscriptrelease * 64));
91 void pointparticles_use(entity this, entity actor, entity trigger)
93 this.state = !this.state;
97 void pointparticles_think(entity this)
99 if(this.origin != this.oldorigin)
102 this.oldorigin = this.origin;
104 this.nextthink = time;
107 void pointparticles_reset(entity this)
109 if(this.spawnflags & 1)
115 spawnfunc(func_pointparticles)
117 if(this.model != "") { precache_model(this.model); _setmodel(this, this.model); }
118 if(this.noise != "") precache_sound(this.noise);
119 if(this.mdl != "") this.cnt = 0; // use a good handler
121 if(!this.bgmscriptsustain) this.bgmscriptsustain = 1;
122 else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0;
124 if(!this.atten) this.atten = ATTEN_NORM;
125 else if(this.atten < 0) this.atten = 0;
126 if(!this.volume) this.volume = 1;
127 if(!this.count) this.count = 1;
128 if(!this.impulse) this.impulse = 1;
132 setorigin(this, this.origin + this.mins);
133 setsize(this, '0 0 0', this.maxs - this.mins);
135 //if(!this.cnt) this.cnt = _particleeffectnum(this.mdl);
137 Net_LinkEntity(this, (this.spawnflags & 4), 0, pointparticles_SendEntity);
141 this.use = pointparticles_use;
142 this.reset = pointparticles_reset;
147 setthink(this, pointparticles_think);
148 this.nextthink = time;
151 spawnfunc(func_sparks)
153 // this.cnt is the amount of sparks that one burst will spawn
155 this.cnt = 25.0; // nice default value
158 // this.wait is the probability that a sparkthink will spawn a spark shower
159 // range: 0 - 1, but 0 makes little sense, so...
160 if(this.wait < 0.05) {
161 this.wait = 0.25; // nice default value
164 this.count = this.cnt;
167 this.velocity = '0 0 -1';
168 this.mdl = "TE_SPARK";
169 this.impulse = 10 * this.wait; // by default 2.5/sec
171 this.cnt = 0; // use mdl
173 spawnfunc_func_pointparticles(this);
177 .int dphitcontentsmask;
179 entityclass(PointParticles);
180 class(PointParticles) .int cnt; // effect number
181 class(PointParticles) .vector velocity; // particle velocity
182 class(PointParticles) .float waterlevel; // direction jitter
183 class(PointParticles) .int count; // count multiplier
184 class(PointParticles) .int impulse; // density
185 class(PointParticles) .string noise; // sound
186 class(PointParticles) .float atten;
187 class(PointParticles) .float volume;
188 class(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
189 class(PointParticles) .vector movedir; // trace direction
190 class(PointParticles) .float glow_color; // palette index
192 void Draw_PointParticles(entity this)
199 sz = this.maxs - this.mins;
200 n = doBGMScript(this);
201 if(this.absolute == 2)
204 n = this.just_toggled ? this.impulse : 0;
206 n = this.impulse * drawframetime;
210 n *= this.impulse * drawframetime;
211 if(this.just_toggled)
218 for(i = random(); i <= n && fail <= 64*n; ++i)
221 p.x += random() * sz.x;
222 p.y += random() * sz.y;
223 p.z += random() * sz.z;
224 if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL))
226 if(this.movedir != '0 0 0')
228 traceline(p, p + normalize(this.movedir) * 4096, 0, NULL);
234 eff_num = _particleeffectnum(this.mdl);
235 __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count);
243 eff_num = _particleeffectnum(this.mdl);
244 __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count);
249 _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten);
251 this.just_toggled = 0;
253 else if(this.absolute)
262 void Ent_PointParticles_Remove(entity this)
265 strunzone(this.noise);
266 this.noise = string_null;
268 strunzone(this.bgmscript);
269 this.bgmscript = string_null;
272 this.mdl = string_null;
275 NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
282 i = ReadCoord(); // density (<0: point, >0: volume)
283 if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed
284 this.just_toggled = 1;
289 this.origin_x = ReadCoord();
290 this.origin_y = ReadCoord();
291 this.origin_z = ReadCoord();
295 this.modelindex = ReadShort();
300 this.mins_x = ReadCoord();
301 this.mins_y = ReadCoord();
302 this.mins_z = ReadCoord();
303 this.maxs_x = ReadCoord();
304 this.maxs_y = ReadCoord();
305 this.maxs_z = ReadCoord();
310 this.maxs_x = ReadCoord();
311 this.maxs_y = ReadCoord();
312 this.maxs_z = ReadCoord();
317 this.mins = this.maxs = '0 0 0';
320 this.cnt = ReadShort(); // effect number
321 this.mdl = strzone(ReadString()); // effect string
325 this.velocity = decompressShortVector(ReadShort());
326 this.movedir = decompressShortVector(ReadShort());
330 this.velocity = this.movedir = '0 0 0';
334 this.waterlevel = ReadShort() / 16.0;
335 this.count = ReadByte() / 16.0;
343 strunzone(this.noise);
345 strunzone(this.bgmscript);
346 this.noise = strzone(ReadString());
349 this.atten = ReadByte() / 64.0;
350 this.volume = ReadByte() / 255.0;
352 this.bgmscript = strzone(ReadString());
353 if(this.bgmscript != "")
355 this.bgmscriptattack = ReadByte() / 64.0;
356 this.bgmscriptdecay = ReadByte() / 64.0;
357 this.bgmscriptsustain = ReadByte() / 255.0;
358 this.bgmscriptrelease = ReadByte() / 64.0;
360 BGMScript_InitEntity(this);
367 this.absolute = (this.impulse >= 0);
370 v = this.maxs - this.mins;
371 this.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
378 setorigin(this, this.origin);
379 setsize(this, this.mins, this.maxs);
380 this.solid = SOLID_NOT;
381 this.draw = Draw_PointParticles;
382 if (isnew) IL_PUSH(g_drawables, this);
383 this.entremove = Ent_PointParticles_Remove;