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 .float bgmscriptdecay;
54 float pointparticles_scriptbuf;
55 float pointparticles_scriptbufsize;
56 float pointparticles_scriptbufloaded;
57 void PointparticlesScript_Init()
61 pointparticles_scriptbuf = pointparticles_scriptbufsize = 0;
62 pointparticles_scriptbufloaded = 1;
63 s = strcat("maps/", mi_shortname, ".bgs");
64 fh = fopen(s, FILE_READ);
67 pointparticles_scriptbuf = buf_create();
68 while((s = fgets(fh)))
70 bufstr_set(pointparticles_scriptbuf, pointparticles_scriptbufsize, s);
71 ++pointparticles_scriptbufsize;
78 .float scriptvelocity;
82 void PointparticlesScript_InitEntity(entity e)
86 if(!pointparticles_scriptbufloaded)
87 PointparticlesScript_Init();
93 for(i = 0; i < pointparticles_scriptbufsize; ++i)
95 tokenize_sane(bufstr_get(pointparticles_scriptbuf, i));
96 if(argv(0) == e.bgmscript)
99 e.scriptline = e.scriptline0 = i;
100 if(i >= pointparticles_scriptbufsize)
102 print("func_pointparticles: script does not define ", mychar, "\n");
108 float PointparticlesScript(entity e)
112 if(e.bgmscript == "")
115 if(cvar("bgmvolume") <= 0)
118 e.just_toggled = FALSE;
120 t = gettime(GETTIME_CDTRACK);
122 tokenize_sane(bufstr_get(pointparticles_scriptbuf, e.scriptline));
125 e.scriptline = e.scriptline0;
127 tokenize_sane(bufstr_get(pointparticles_scriptbuf, e.scriptline));
130 if(argv(0) != e.bgmscript)
132 // end of script, will revert to beginning later
134 else if(t >= stof(argv(1)))
136 // time code reached!
137 e.scriptvelocity = stof(argv(2));
138 if(e.scriptvelocity > 0)
139 e.just_toggled = e.scriptstate = TRUE;
141 e.just_toggled = e.scriptstate = FALSE;
143 e.scripttime = stof(argv(1));
148 if(e.bgmscriptdecay >= 1)
149 return e.just_toggled;
151 return pow(0.5, (t - e.scripttime) * (e.bgmscriptdecay / (1 - e.bgmscriptdecay))) * e.scriptvelocity;
157 void Draw_PointParticles()
164 sz = self.maxs - self.mins;
165 n = PointparticlesScript(self);
166 if(self.absolute == 2)
168 n = self.just_toggled ? self.impulse : 0;
172 n *= self.impulse * drawframetime;
173 if(self.just_toggled)
180 for(i = random(); i <= n && fail <= 64*n; ++i)
183 p_x += random() * sz_x;
184 p_y += random() * sz_y;
185 p_z += random() * sz_z;
186 if(PointInBrush(self, p))
188 if(self.movedir != '0 0 0')
190 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
192 pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
196 pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
201 sound(self, CHAN_AUTO, self.noise, VOL_BASE * self.volume, self.atten);
203 self.just_toggled = 0;
205 else if(self.absolute)
214 void Ent_PointParticles_Remove()
217 strunzone(self.noise);
218 self.noise = string_null;
220 strunzone(self.bgmscript);
221 self.bgmscript = string_null;
224 void Ent_PointParticles()
231 i = ReadCoord(); // density (<0: point, >0: volume)
232 if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
233 self.just_toggled = 1;
238 self.origin_x = ReadCoord();
239 self.origin_y = ReadCoord();
240 self.origin_z = ReadCoord();
244 self.modelindex = ReadShort();
249 self.mins_x = ReadCoord();
250 self.mins_y = ReadCoord();
251 self.mins_z = ReadCoord();
252 self.maxs_x = ReadCoord();
253 self.maxs_y = ReadCoord();
254 self.maxs_z = ReadCoord();
259 self.maxs_x = ReadCoord();
260 self.maxs_y = ReadCoord();
261 self.maxs_z = ReadCoord();
266 self.mins = self.maxs = '0 0 0';
269 self.cnt = ReadShort(); // effect number
273 self.velocity = decompressShortVector(ReadShort());
274 self.movedir = decompressShortVector(ReadShort());
278 self.velocity = self.movedir = '0 0 0';
282 self.waterlevel = ReadShort() / 16.0;
283 self.count = ReadByte() / 16.0;
291 strunzone(self.noise);
293 strunzone(self.bgmscript);
294 self.noise = strzone(ReadString());
297 self.atten = ReadByte() / 64.0;
298 self.volume = ReadByte() / 255.0;
300 self.bgmscript = strzone(ReadString());
301 if(self.bgmscript != "")
302 self.bgmscriptdecay = ReadByte() / 255.0;
303 PointparticlesScript_InitEntity(self);
308 self.absolute = (self.impulse >= 0);
311 v = self.maxs - self.mins;
312 self.impulse *= -v_x * v_y * v_z / 262144; // relative: particles per 64^3 cube
319 setorigin(self, self.origin);
320 setsize(self, self.mins, self.maxs);
321 self.solid = SOLID_NOT;
322 self.draw = Draw_PointParticles;
323 self.entremove = Ent_PointParticles_Remove;
326 .float glow_color; // palette index
329 te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
334 te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
337 void Ent_RainOrSnow()
339 self.impulse = ReadByte(); // Rain, Snow, or Whatever
340 self.origin_x = ReadCoord();
341 self.origin_y = ReadCoord();
342 self.origin_z = ReadCoord();
343 self.maxs_x = ReadCoord();
344 self.maxs_y = ReadCoord();
345 self.maxs_z = ReadCoord();
346 self.velocity = decompressShortVector(ReadShort());
347 self.count = ReadShort() * 10;
348 self.glow_color = ReadByte(); // color
350 self.mins = -0.5 * self.maxs;
351 self.maxs = 0.5 * self.maxs;
352 self.origin = self.origin - self.mins;
354 setorigin(self, self.origin);
355 setsize(self, self.mins, self.maxs);
356 self.solid = SOLID_NOT;
358 self.draw = Draw_Rain;
360 self.draw = Draw_Snow;
364 void zcurveparticles(float effectnum, vector start, vector end, float end_dz, float speed, float depth)
367 // IF IT WERE A STRAIGHT LINE, it'd end end_dz above end
370 mid = (start + end) * 0.5;
376 if(depth < 0 || normalize(mid - start) * normalize(end - start) > 0.999999)
377 // TODO make this a variable threshold
378 // currently: 0.081 degrees
379 // 0.99999 would be 0.256 degrees and is visible
381 zcurve.velocity = speed * normalize(end - start);
382 trailparticles(zcurve, effectnum, start, end);
386 zcurveparticles(effectnum, start, mid, end_dz, speed, depth);
387 zcurveparticles(effectnum, mid, end, end_dz, speed, depth);
391 void Net_ReadZCurveParticles()
395 float effectnum, speed;
400 zcurve.classname = "zcurve";
403 effectnum = ReadShort();
405 start_x = ReadCoord();
406 start_y = ReadCoord();
407 start_z = ReadCoord();
411 end_dz = ReadCoord();
412 speed = ReadShort() * 16;
414 zcurveparticles(effectnum, start, end, end_dz, speed, 5); // at most 32 segments
417 void Net_ReadNexgunBeamParticle()
419 vector shotorg, endpos;
420 shotorg_x = ReadCoord(); shotorg_y = ReadCoord(); shotorg_z = ReadCoord();
421 endpos_x = ReadCoord(); endpos_y = ReadCoord(); endpos_z = ReadCoord();
423 pointparticles(particleeffectnum("nex_muzzleflash"), shotorg, normalize(endpos - shotorg) * 1000, 1);
425 //draw either the old v2.3 beam or the new beam
426 if (cvar("cl_particles_oldnexbeam") && (getstati(STAT_ALLOW_OLDNEXBEAM) || isdemo()))
427 trailparticles(world, particleeffectnum("TE_TEI_G3"), shotorg, endpos);
429 trailparticles(world, particleeffectnum("nex_beam"), shotorg, endpos);