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
16 if (this.movedir != '0 0 0' || this.velocity != '0 0 0') {
17 fl |= 0x20; // 4 bytes - saves CPU
19 if (this.waterlevel || this.count != 1) {
20 fl |= 0x40; // 4 bytes - obscure features almost never used
22 if (this.mins != '0 0 0' || this.maxs != '0 0 0') {
23 fl |= 0x80; // 14 bytes - saves lots of space
25 WriteByte(MSG_ENTITY, fl);
28 WriteCoord(MSG_ENTITY, this.impulse);
30 WriteCoord(MSG_ENTITY, 0); // off
34 WriteCoord(MSG_ENTITY, this.origin_x);
35 WriteCoord(MSG_ENTITY, this.origin_y);
36 WriteCoord(MSG_ENTITY, this.origin_z);
39 if (this.model != "null") {
40 WriteShort(MSG_ENTITY, this.modelindex);
42 WriteCoord(MSG_ENTITY, this.mins_x);
43 WriteCoord(MSG_ENTITY, this.mins_y);
44 WriteCoord(MSG_ENTITY, this.mins_z);
45 WriteCoord(MSG_ENTITY, this.maxs_x);
46 WriteCoord(MSG_ENTITY, this.maxs_y);
47 WriteCoord(MSG_ENTITY, this.maxs_z);
50 WriteShort(MSG_ENTITY, 0);
52 WriteCoord(MSG_ENTITY, this.maxs_x);
53 WriteCoord(MSG_ENTITY, this.maxs_y);
54 WriteCoord(MSG_ENTITY, this.maxs_z);
57 WriteShort(MSG_ENTITY, this.cnt);
58 WriteString(MSG_ENTITY, this.mdl);
60 WriteShort(MSG_ENTITY, compressShortVector(this.velocity));
61 WriteShort(MSG_ENTITY, compressShortVector(this.movedir));
64 WriteShort(MSG_ENTITY, this.waterlevel * 16.0);
65 WriteByte(MSG_ENTITY, this.count * 16.0);
67 WriteString(MSG_ENTITY, this.noise);
68 if (this.noise != "") {
69 WriteByte(MSG_ENTITY, floor(this.atten * 64));
70 WriteByte(MSG_ENTITY, floor(this.volume * 255));
72 WriteString(MSG_ENTITY, this.bgmscript);
73 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_use(entity this, entity actor, entity trigger)
85 this.state = !this.state;
89 void pointparticles_think(entity this)
91 if (this.origin != this.oldorigin) {
93 this.oldorigin = this.origin;
95 this.nextthink = time;
98 void pointparticles_reset(entity this)
100 if (this.spawnflags & 1) {
107 spawnfunc(func_pointparticles)
109 if (this.model != "") { precache_model(this.model); _setmodel(this, this.model); }
110 if (this.noise != "") { precache_sound(this.noise); }
111 if (this.mdl != "") {
112 this.cnt = 0; // use a good handler
114 if (!this.bgmscriptsustain) { this.bgmscriptsustain = 1; } else if (this.bgmscriptsustain < 0) {
115 this.bgmscriptsustain = 0;
118 if (!this.atten) { this.atten = ATTEN_NORM; } else if (this.atten < 0) {
121 if (!this.volume) { this.volume = 1; }
122 if (!this.count) { this.count = 1; }
123 if (!this.impulse) { this.impulse = 1; }
125 if (!this.modelindex) {
126 setorigin(this, this.origin + this.mins);
127 setsize(this, '0 0 0', this.maxs - this.mins);
129 // if(!this.cnt) this.cnt = _particleeffectnum(this.mdl);
131 Net_LinkEntity(this, (this.spawnflags & 4), 0, pointparticles_SendEntity);
134 this.use = pointparticles_use;
135 this.reset = pointparticles_reset;
140 setthink(this, pointparticles_think);
141 this.nextthink = time;
144 spawnfunc(func_sparks)
146 // this.cnt is the amount of sparks that one burst will spawn
148 this.cnt = 25.0; // nice default value
151 // this.wait is the probability that a sparkthink will spawn a spark shower
152 // range: 0 - 1, but 0 makes little sense, so...
153 if (this.wait < 0.05) {
154 this.wait = 0.25; // nice default value
157 this.count = this.cnt;
160 this.velocity = '0 0 -1';
161 this.mdl = "TE_SPARK";
162 this.impulse = 10 * this.wait; // by default 2.5/sec
164 this.cnt = 0; // use mdl
166 spawnfunc_func_pointparticles(this);
170 .int dphitcontentsmask;
172 entityclass(PointParticles);
173 class(PointParticles).int cnt; // effect number
174 class(PointParticles).vector velocity; // particle velocity
175 class(PointParticles).float waterlevel; // direction jitter
176 class(PointParticles).int count; // count multiplier
177 class(PointParticles).int impulse; // density
178 class(PointParticles).string noise; // sound
179 class(PointParticles).float atten;
180 class(PointParticles).float volume;
181 class(PointParticles).float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
182 class(PointParticles).vector movedir; // trace direction
183 class(PointParticles).float glow_color; // palette index
185 void Draw_PointParticles(entity this)
192 sz = this.maxs - this.mins;
193 n = doBGMScript(this);
194 if (this.absolute == 2) {
196 n = this.just_toggled ? this.impulse : 0;
198 n = this.impulse * drawframetime;
201 n *= this.impulse * drawframetime;
202 if (this.just_toggled) {
212 for (i = random(); i <= n && fail <= 64 * n; ++i) {
214 p.x += random() * sz.x;
215 p.y += random() * sz.y;
216 p.z += random() * sz.z;
217 if (WarpZoneLib_BoxTouchesBrush(p, p, this, NULL)) {
218 if (this.movedir != '0 0 0') {
219 traceline(p, p + normalize(this.movedir) * 4096, 0, NULL);
225 eff_num = _particleeffectnum(this.mdl);
227 __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count);
233 eff_num = _particleeffectnum(this.mdl);
235 __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count);
237 if (this.noise != "") {
239 _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten);
241 this.just_toggled = 0;
242 } else if (this.absolute) {
250 void Ent_PointParticles_Remove(entity this)
253 strunzone(this.noise);
255 this.noise = string_null;
256 if (this.bgmscript) {
257 strunzone(this.bgmscript);
259 this.bgmscript = string_null;
263 this.mdl = string_null;
266 NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
272 i = ReadCoord(); // density (<0: point, >0: volume)
273 if (i && !this.impulse && (this.cnt || this.mdl)) { // this.cnt check is so it only happens if the ent already existed
274 this.just_toggled = 1;
279 this.origin_x = ReadCoord();
280 this.origin_y = ReadCoord();
281 this.origin_z = ReadCoord();
284 this.modelindex = ReadShort();
286 if (this.modelindex) {
287 this.mins_x = ReadCoord();
288 this.mins_y = ReadCoord();
289 this.mins_z = ReadCoord();
290 this.maxs_x = ReadCoord();
291 this.maxs_y = ReadCoord();
292 this.maxs_z = ReadCoord();
295 this.maxs_x = ReadCoord();
296 this.maxs_y = ReadCoord();
297 this.maxs_z = ReadCoord();
300 this.mins = this.maxs = '0 0 0';
303 this.cnt = ReadShort(); // effect number
304 this.mdl = strzone(ReadString()); // effect string
307 this.velocity = decompressShortVector(ReadShort());
308 this.movedir = decompressShortVector(ReadShort());
310 this.velocity = this.movedir = '0 0 0';
313 this.waterlevel = ReadShort() / 16.0;
314 this.count = ReadByte() / 16.0;
320 strunzone(this.noise);
322 if (this.bgmscript) {
323 strunzone(this.bgmscript);
325 this.noise = strzone(ReadString());
326 if (this.noise != "") {
327 this.atten = ReadByte() / 64.0;
328 this.volume = ReadByte() / 255.0;
330 this.bgmscript = strzone(ReadString());
331 if (this.bgmscript != "") {
332 this.bgmscriptattack = ReadByte() / 64.0;
333 this.bgmscriptdecay = ReadByte() / 64.0;
334 this.bgmscriptsustain = ReadByte() / 255.0;
335 this.bgmscriptrelease = ReadByte() / 64.0;
337 BGMScript_InitEntity(this);
343 this.absolute = (this.impulse >= 0);
344 if (!this.absolute) {
345 v = this.maxs - this.mins;
346 this.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
354 setorigin(this, this.origin);
355 setsize(this, this.mins, this.maxs);
356 this.solid = SOLID_NOT;
357 this.draw = Draw_PointParticles;
358 if (isnew) { IL_PUSH(g_drawables, this); }
359 this.entremove = Ent_PointParticles_Remove;