]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/particles.qc
more pointparticles goodies
[divverent/nexuiz.git] / data / qcsrc / client / particles.qc
1 vector PointInBrush_vec;
2 entity PointInBrush_brush;
3 .float dphitcontentsmask;
4 float PointInBrush_Recurse()
5 {
6         float s;
7         entity se;
8         float f;
9
10         traceline(PointInBrush_vec, PointInBrush_vec, 0, world);
11         if not(trace_ent)
12                 return 0;
13         if(trace_ent == PointInBrush_brush)
14                 return 1;
15
16         se = trace_ent;
17         s = se.solid;
18         se.solid = SOLID_NOT;
19         f = PointInBrush_Recurse();
20         se.solid = s;
21
22         return f;
23 }
24 float PointInBrush(entity brush, vector point)
25 {
26         float f, s;
27
28         if not(brush.modelindex)
29                 return 1;
30
31         s = brush.solid;
32         brush.solid = SOLID_BSP;
33         PointInBrush_vec = point;
34         PointInBrush_brush = brush;
35         f = PointInBrush_Recurse();
36         brush.solid = s;
37
38         return f;
39 }
40
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
47 .float atten;
48 .float volume;
49 .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
50 .vector movedir; // trace direction
51 .string bgmscript;
52 .float bgmscriptdecay;
53
54 float pointparticles_scriptbuf;
55 float pointparticles_scriptbufsize;
56 float pointparticles_scriptbufloaded;
57 void PointparticlesScript_Init()
58 {
59         string s;
60         float fh;
61         pointparticles_scriptbuf = pointparticles_scriptbufsize = 0;
62         pointparticles_scriptbufloaded = 1;
63         s = strcat("maps/", mi_shortname, ".bgs");
64         fh = fopen(s, FILE_READ);
65         if(fh < 0)
66                 return;
67         pointparticles_scriptbuf = buf_create();
68         while((s = fgets(fh)))
69         {
70                 bufstr_set(pointparticles_scriptbuf, pointparticles_scriptbufsize, s);
71                 ++pointparticles_scriptbufsize;
72         }
73         fclose(fh);
74 }
75
76 .float scriptline;
77 .float scriptline0;
78 .float scriptvelocity;
79 .float scripttime;
80 .float scriptstate;
81 .float just_toggled;
82 void PointparticlesScript_InitEntity(entity e)
83 {
84         if(e.bgmscript != "")
85         {
86                 if(!pointparticles_scriptbufloaded)
87                         PointparticlesScript_Init();
88
89                 string mychar;
90                 float i;
91
92                 e.scriptline0 = -1;
93                 for(i = 0; i < pointparticles_scriptbufsize; ++i)
94                 {
95                         tokenize_sane(bufstr_get(pointparticles_scriptbuf, i));
96                         if(argv(0) == e.bgmscript)
97                                 break;
98                 }
99                 e.scriptline = e.scriptline0 = i;
100                 if(i >= pointparticles_scriptbufsize)
101                 {
102                         print("func_pointparticles: script does not define ", mychar, "\n");
103                         e.bgmscript = "";
104                 }
105         }
106 }
107
108 float PointparticlesScript(entity e)
109 {
110         float t;
111
112         if(e.bgmscript == "")
113                 return 1;
114         
115         if(cvar("bgmvolume") <= 0)
116                 return 0.5;
117
118         e.just_toggled = FALSE;
119
120         t = gettime(GETTIME_CDTRACK);
121
122         tokenize_sane(bufstr_get(pointparticles_scriptbuf, e.scriptline));
123         if(t < e.scripttime)
124         {
125                 e.scriptline = e.scriptline0;
126                 e.scripttime = 0;
127                 tokenize_sane(bufstr_get(pointparticles_scriptbuf, e.scriptline));
128         }
129
130         if(argv(0) != e.bgmscript)
131         {
132                 // end of script, will revert to beginning later
133         }
134         else if(t >= stof(argv(1)))
135         {
136                 // time code reached!
137                 e.scriptvelocity = stof(argv(2));
138                 if(e.scriptvelocity > 0)
139                         e.just_toggled = e.scriptstate = TRUE;
140                 else
141                         e.just_toggled = e.scriptstate = FALSE;
142                 e.scriptline += 1;
143                 e.scripttime = stof(argv(1));
144         }
145
146         if(e.scriptstate)
147         {
148                 if(e.bgmscriptdecay >= 1)
149                         return e.just_toggled;
150                 else
151                         return pow(0.5, (t - e.scripttime) * (e.bgmscriptdecay / (1 - e.bgmscriptdecay))) * e.scriptvelocity;
152         }
153         else
154                 return 0;
155 }
156
157 void Draw_PointParticles()
158 {
159         float n, i, fail;
160         vector p;
161         vector sz;
162         vector o;
163         o = self.origin;
164         sz = self.maxs - self.mins;
165         n = PointparticlesScript(self);
166         if(self.absolute == 2)
167         {
168                 n = self.just_toggled ? self.impulse : 0;
169         }
170         else
171         {
172                 n *= self.impulse * drawframetime;
173                 if(self.just_toggled)
174                         if(n < 1)
175                                 n = 1;
176         }
177         if(n == 0)
178                 return;
179         fail = 0;
180         for(i = random(); i <= n && fail <= 64*n; ++i)
181         {
182                 p = o + self.mins;
183                 p_x += random() * sz_x;
184                 p_y += random() * sz_y;
185                 p_z += random() * sz_z;
186                 if(PointInBrush(self, p))
187                 {
188                         if(self.movedir != '0 0 0')
189                         {
190                                 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
191                                 p = trace_endpos;
192                                 pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
193                         }
194                         else
195                         {
196                                 pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
197                         }
198                         if(self.noise != "")
199                         {
200                                 self.origin = p;
201                                 sound(self, CHAN_AUTO, self.noise, VOL_BASE * self.volume, self.atten);
202                         }
203                         self.just_toggled = 0;
204                 }
205                 else if(self.absolute)
206                 {
207                         ++fail;
208                         --i;
209                 }
210         }
211         self.origin = o;
212 }
213
214 void Ent_PointParticles_Remove()
215 {
216         if(self.noise)
217                 strunzone(self.noise);
218         self.noise = string_null;
219         if(self.bgmscript)
220                 strunzone(self.bgmscript);
221         self.bgmscript = string_null;
222 }
223
224 void Ent_PointParticles()
225 {
226         float f, i;
227         vector v;
228         f = ReadByte();
229         if(f & 2)
230         {
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;
234                 self.impulse = i;
235         }
236         if(f & 4)
237         {
238                 self.origin_x = ReadCoord();
239                 self.origin_y = ReadCoord();
240                 self.origin_z = ReadCoord();
241         }
242         if(f & 1)
243         {
244                 self.modelindex = ReadShort();
245                 if(f & 0x80)
246                 {
247                         if(self.modelindex)
248                         {
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();
255                         }
256                         else
257                         {
258                                 self.mins    = '0 0 0';
259                                 self.maxs_x = ReadCoord();
260                                 self.maxs_y = ReadCoord();
261                                 self.maxs_z = ReadCoord();
262                         }
263                 }
264                 else
265                 {
266                         self.mins = self.maxs = '0 0 0';
267                 }
268
269                 self.cnt = ReadShort(); // effect number
270
271                 if(f & 0x20)
272                 {
273                         self.velocity = decompressShortVector(ReadShort());
274                         self.movedir = decompressShortVector(ReadShort());
275                 }
276                 else
277                 {
278                         self.velocity = self.movedir = '0 0 0';
279                 }
280                 if(f & 0x40)
281                 {
282                         self.waterlevel = ReadShort() / 16.0;
283                         self.count = ReadByte() / 16.0;
284                 }
285                 else
286                 {
287                         self.waterlevel = 0;
288                         self.count = 1;
289                 }
290                 if(self.noise)
291                         strunzone(self.noise);
292                 if(self.bgmscript)
293                         strunzone(self.bgmscript);
294                 self.noise = strzone(ReadString());
295                 if(self.noise != "")
296                 {
297                         self.atten = ReadByte() / 64.0;
298                         self.volume = ReadByte() / 255.0;
299                 }
300                 self.bgmscript = strzone(ReadString());
301                 if(self.bgmscript != "")
302                         self.bgmscriptdecay = ReadByte() / 255.0;
303                 PointparticlesScript_InitEntity(self);
304         }
305
306         if(f & 2)
307         {
308                 self.absolute = (self.impulse >= 0);
309                 if(!self.absolute)
310                 {
311                         v = self.maxs - self.mins;
312                         self.impulse *= -v_x * v_y * v_z / 262144; // relative: particles per 64^3 cube
313                 }
314         }
315
316         if(f & 0x10)
317                 self.absolute = 2;
318
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;
324 }
325
326 .float glow_color; // palette index
327 void Draw_Rain()
328 {
329     te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
330 }
331
332 void Draw_Snow()
333 {
334     te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
335 }
336
337 void Ent_RainOrSnow()
338 {
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
349
350         self.mins    = -0.5 * self.maxs;
351         self.maxs    =  0.5 * self.maxs;
352         self.origin  = self.origin - self.mins;
353
354         setorigin(self, self.origin);
355         setsize(self, self.mins, self.maxs);
356         self.solid = SOLID_NOT;
357         if(self.impulse)
358                 self.draw = Draw_Rain;
359         else
360                 self.draw = Draw_Snow;
361 }
362
363 entity zcurve;
364 void zcurveparticles(float effectnum, vector start, vector end, float end_dz, float speed, float depth)
365 {
366         // end_dz:
367         //   IF IT WERE A STRAIGHT LINE, it'd end end_dz above end
368
369         vector mid;
370         mid = (start + end) * 0.5;
371
372         end_dz *= 0.25;
373         mid_z += end_dz;
374
375         --depth;
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
380         {
381                 zcurve.velocity = speed * normalize(end - start);
382                 trailparticles(zcurve, effectnum, start, end);
383         }
384         else
385         {
386                 zcurveparticles(effectnum, start, mid, end_dz, speed, depth);
387                 zcurveparticles(effectnum, mid, end, end_dz, speed, depth);
388         }
389 }
390
391 void Net_ReadZCurveParticles()
392 {
393         vector start, end;
394         float end_dz;
395         float effectnum, speed;
396
397         if(!zcurve)
398         {
399                 zcurve = spawn();
400                 zcurve.classname = "zcurve";
401         }
402
403         effectnum = ReadShort();
404
405         start_x = ReadCoord();
406         start_y = ReadCoord();
407         start_z = ReadCoord();
408         end_x = ReadCoord();
409         end_y = ReadCoord();
410         end_z = ReadCoord();
411         end_dz = ReadCoord();
412         speed = ReadShort() * 16;
413
414         zcurveparticles(effectnum, start, end, end_dz, speed, 5); // at most 32 segments
415 }
416
417 void Net_ReadNexgunBeamParticle()
418 {
419         vector shotorg, endpos;
420         shotorg_x = ReadCoord(); shotorg_y = ReadCoord(); shotorg_z = ReadCoord();
421         endpos_x = ReadCoord(); endpos_y = ReadCoord(); endpos_z = ReadCoord();
422         
423         pointparticles(particleeffectnum("nex_muzzleflash"), shotorg, normalize(endpos - shotorg) * 1000, 1);
424         
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);
428         else
429                 trailparticles(world, particleeffectnum("nex_beam"), shotorg, endpos);
430 }