1 vector PointInBrush_vec;
2 entity PointInBrush_brush;
3 .float dphitcontentsmask;
4 float PointInBrush_Recurse()
10 traceline(PointInBrush_vec, PointInBrush_vec, 0, world);
13 if(trace_ent == PointInBrush_brush)
19 f = PointInBrush_Recurse();
24 float PointInBrush(entity brush, vector point)
28 if not(brush.modelindex)
32 brush.solid = SOLID_BSP;
33 PointInBrush_vec = point;
34 PointInBrush_brush = brush;
35 f = PointInBrush_Recurse();
41 .float cnt; // effect number
42 .vector velocity; // particle velocity
43 .float waterlevel; // direction jitter
44 .float count; // count multiplier
45 .float impulse; // density
46 .string noise; // sound
49 .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
50 .vector movedir; // trace direction
52 void Draw_PointParticles()
59 sz = self.maxs - self.mins;
61 if(self.absolute == 2)
64 n = self.just_toggled ? self.impulse : 0;
66 n = self.impulse * drawframetime;
70 n *= self.impulse * drawframetime;
78 for(i = random(); i <= n && fail <= 64*n; ++i)
81 p_x += random() * sz_x;
82 p_y += random() * sz_y;
83 p_z += random() * sz_z;
84 if(PointInBrush(self, p))
86 if(self.movedir != '0 0 0')
88 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
90 pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
94 pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
99 sound(self, CHAN_AUTO, self.noise, VOL_BASE * self.volume, self.atten);
101 self.just_toggled = 0;
103 else if(self.absolute)
112 void Ent_PointParticles_Remove()
115 strunzone(self.noise);
116 self.noise = string_null;
118 strunzone(self.bgmscript);
119 self.bgmscript = string_null;
122 void Ent_PointParticles()
129 i = ReadCoord(); // density (<0: point, >0: volume)
130 if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
131 self.just_toggled = 1;
136 self.origin_x = ReadCoord();
137 self.origin_y = ReadCoord();
138 self.origin_z = ReadCoord();
142 self.modelindex = ReadShort();
147 self.mins_x = ReadCoord();
148 self.mins_y = ReadCoord();
149 self.mins_z = ReadCoord();
150 self.maxs_x = ReadCoord();
151 self.maxs_y = ReadCoord();
152 self.maxs_z = ReadCoord();
157 self.maxs_x = ReadCoord();
158 self.maxs_y = ReadCoord();
159 self.maxs_z = ReadCoord();
164 self.mins = self.maxs = '0 0 0';
167 self.cnt = ReadShort(); // effect number
171 self.velocity = decompressShortVector(ReadShort());
172 self.movedir = decompressShortVector(ReadShort());
176 self.velocity = self.movedir = '0 0 0';
180 self.waterlevel = ReadShort() / 16.0;
181 self.count = ReadByte() / 16.0;
189 strunzone(self.noise);
191 strunzone(self.bgmscript);
192 self.noise = strzone(ReadString());
195 self.atten = ReadByte() / 64.0;
196 self.volume = ReadByte() / 255.0;
198 self.bgmscript = strzone(ReadString());
199 if(self.bgmscript != "")
201 self.bgmscriptattack = ReadByte() / 64.0;
202 self.bgmscriptdecay = ReadByte() / 64.0;
203 self.bgmscriptsustain = ReadByte() / 255.0;
204 self.bgmscriptrelease = ReadByte() / 64.0;
206 BGMScript_InitEntity(self);
211 self.absolute = (self.impulse >= 0);
214 v = self.maxs - self.mins;
215 self.impulse *= -v_x * v_y * v_z / 262144; // relative: particles per 64^3 cube
222 setorigin(self, self.origin);
223 setsize(self, self.mins, self.maxs);
224 self.solid = SOLID_NOT;
225 self.draw = Draw_PointParticles;
226 self.entremove = Ent_PointParticles_Remove;
229 .float glow_color; // palette index
232 te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
237 te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
240 void Ent_RainOrSnow()
242 self.impulse = ReadByte(); // Rain, Snow, or Whatever
243 self.origin_x = ReadCoord();
244 self.origin_y = ReadCoord();
245 self.origin_z = ReadCoord();
246 self.maxs_x = ReadCoord();
247 self.maxs_y = ReadCoord();
248 self.maxs_z = ReadCoord();
249 self.velocity = decompressShortVector(ReadShort());
250 self.count = ReadShort() * 10;
251 self.glow_color = ReadByte(); // color
253 self.mins = -0.5 * self.maxs;
254 self.maxs = 0.5 * self.maxs;
255 self.origin = self.origin - self.mins;
257 setorigin(self, self.origin);
258 setsize(self, self.mins, self.maxs);
259 self.solid = SOLID_NOT;
261 self.draw = Draw_Rain;
263 self.draw = Draw_Snow;
267 void zcurveparticles(float effectnum, vector start, vector end, float end_dz, float speed, float depth)
270 // IF IT WERE A STRAIGHT LINE, it'd end end_dz above end
273 mid = (start + end) * 0.5;
279 if(depth < 0 || normalize(mid - start) * normalize(end - start) > 0.999999)
280 // TODO make this a variable threshold
281 // currently: 0.081 degrees
282 // 0.99999 would be 0.256 degrees and is visible
284 zcurve.velocity = speed * normalize(end - start);
285 trailparticles(zcurve, effectnum, start, end);
289 zcurveparticles(effectnum, start, mid, end_dz, speed, depth);
290 zcurveparticles(effectnum, mid, end, end_dz, speed, depth);
294 void Net_ReadZCurveParticles()
298 float effectnum, speed;
303 zcurve.classname = "zcurve";
306 effectnum = ReadShort();
308 start_x = ReadCoord();
309 start_y = ReadCoord();
310 start_z = ReadCoord();
314 end_dz = ReadCoord();
315 speed = ReadShort() * 16;
317 zcurveparticles(effectnum, start, end, end_dz, speed, 5); // at most 32 segments
320 void Net_ReadNexgunBeamParticle()
322 vector shotorg, endpos;
323 shotorg_x = ReadCoord(); shotorg_y = ReadCoord(); shotorg_z = ReadCoord();
324 endpos_x = ReadCoord(); endpos_y = ReadCoord(); endpos_z = ReadCoord();
326 pointparticles(particleeffectnum("nex_muzzleflash"), shotorg, normalize(endpos - shotorg) * 1000, 1);
328 //draw either the old v2.3 beam or the new beam
329 if (cvar("cl_particles_oldnexbeam") && (getstati(STAT_ALLOW_OLDNEXBEAM) || isdemo()))
330 trailparticles(world, particleeffectnum("TE_TEI_G3"), shotorg, endpos);
332 trailparticles(world, particleeffectnum("nex_beam"), shotorg, endpos);