REALLY fix it this time.
[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
52 void Draw_PointParticles()
53 {
54         float n, i, fail;
55         vector p;
56         vector sz;
57         vector o;
58         o = self.origin;
59         sz = self.maxs - self.mins;
60         n = BGMScript(self);
61         if(self.absolute == 2)
62         {
63                 if(n >= 0)
64                         n = self.just_toggled ? self.impulse : 0;
65                 else
66                         n = self.impulse * drawframetime;
67         }
68         else
69         {
70                 n *= self.impulse * drawframetime;
71                 if(self.just_toggled)
72                         if(n < 1)
73                                 n = 1;
74         }
75         if(n == 0)
76                 return;
77         fail = 0;
78         for(i = random(); i <= n && fail <= 64*n; ++i)
79         {
80                 p = o + self.mins;
81                 p_x += random() * sz_x;
82                 p_y += random() * sz_y;
83                 p_z += random() * sz_z;
84                 if(PointInBrush(self, p))
85                 {
86                         if(self.movedir != '0 0 0')
87                         {
88                                 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
89                                 p = trace_endpos;
90                                 pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
91                         }
92                         else
93                         {
94                                 pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
95                         }
96                         if(self.noise != "")
97                         {
98                                 self.origin = p;
99                                 sound(self, CHAN_AUTO, self.noise, VOL_BASE * self.volume, self.atten);
100                         }
101                         self.just_toggled = 0;
102                 }
103                 else if(self.absolute)
104                 {
105                         ++fail;
106                         --i;
107                 }
108         }
109         self.origin = o;
110 }
111
112 void Ent_PointParticles_Remove()
113 {
114         if(self.noise)
115                 strunzone(self.noise);
116         self.noise = string_null;
117         if(self.bgmscript)
118                 strunzone(self.bgmscript);
119         self.bgmscript = string_null;
120 }
121
122 void Ent_PointParticles()
123 {
124         float f, i;
125         vector v;
126         f = ReadByte();
127         if(f & 2)
128         {
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;
132                 self.impulse = i;
133         }
134         if(f & 4)
135         {
136                 self.origin_x = ReadCoord();
137                 self.origin_y = ReadCoord();
138                 self.origin_z = ReadCoord();
139         }
140         if(f & 1)
141         {
142                 self.modelindex = ReadShort();
143                 if(f & 0x80)
144                 {
145                         if(self.modelindex)
146                         {
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();
153                         }
154                         else
155                         {
156                                 self.mins    = '0 0 0';
157                                 self.maxs_x = ReadCoord();
158                                 self.maxs_y = ReadCoord();
159                                 self.maxs_z = ReadCoord();
160                         }
161                 }
162                 else
163                 {
164                         self.mins = self.maxs = '0 0 0';
165                 }
166
167                 self.cnt = ReadShort(); // effect number
168
169                 if(f & 0x20)
170                 {
171                         self.velocity = decompressShortVector(ReadShort());
172                         self.movedir = decompressShortVector(ReadShort());
173                 }
174                 else
175                 {
176                         self.velocity = self.movedir = '0 0 0';
177                 }
178                 if(f & 0x40)
179                 {
180                         self.waterlevel = ReadShort() / 16.0;
181                         self.count = ReadByte() / 16.0;
182                 }
183                 else
184                 {
185                         self.waterlevel = 0;
186                         self.count = 1;
187                 }
188                 if(self.noise)
189                         strunzone(self.noise);
190                 if(self.bgmscript)
191                         strunzone(self.bgmscript);
192                 self.noise = strzone(ReadString());
193                 if(self.noise != "")
194                 {
195                         self.atten = ReadByte() / 64.0;
196                         self.volume = ReadByte() / 255.0;
197                 }
198                 self.bgmscript = strzone(ReadString());
199                 if(self.bgmscript != "")
200                 {
201                         self.bgmscriptattack = ReadByte() / 64.0;
202                         self.bgmscriptdecay = ReadByte() / 64.0;
203                         self.bgmscriptsustain = ReadByte() / 255.0;
204                         self.bgmscriptrelease = ReadByte() / 64.0;
205                 }
206                 BGMScript_InitEntity(self);
207         }
208
209         if(f & 2)
210         {
211                 self.absolute = (self.impulse >= 0);
212                 if(!self.absolute)
213                 {
214                         v = self.maxs - self.mins;
215                         self.impulse *= -v_x * v_y * v_z / 262144; // relative: particles per 64^3 cube
216                 }
217         }
218
219         if(f & 0x10)
220                 self.absolute = 2;
221
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;
227 }
228
229 .float glow_color; // palette index
230 void Draw_Rain()
231 {
232     te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
233 }
234
235 void Draw_Snow()
236 {
237     te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
238 }
239
240 void Ent_RainOrSnow()
241 {
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
252
253         self.mins    = -0.5 * self.maxs;
254         self.maxs    =  0.5 * self.maxs;
255         self.origin  = self.origin - self.mins;
256
257         setorigin(self, self.origin);
258         setsize(self, self.mins, self.maxs);
259         self.solid = SOLID_NOT;
260         if(self.impulse)
261                 self.draw = Draw_Rain;
262         else
263                 self.draw = Draw_Snow;
264 }
265
266 entity zcurve;
267 void zcurveparticles(float effectnum, vector start, vector end, float end_dz, float speed, float depth)
268 {
269         // end_dz:
270         //   IF IT WERE A STRAIGHT LINE, it'd end end_dz above end
271
272         vector mid;
273         mid = (start + end) * 0.5;
274
275         end_dz *= 0.25;
276         mid_z += end_dz;
277
278         --depth;
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
283         {
284                 zcurve.velocity = speed * normalize(end - start);
285                 trailparticles(zcurve, effectnum, start, end);
286         }
287         else
288         {
289                 zcurveparticles(effectnum, start, mid, end_dz, speed, depth);
290                 zcurveparticles(effectnum, mid, end, end_dz, speed, depth);
291         }
292 }
293
294 void Net_ReadZCurveParticles()
295 {
296         vector start, end;
297         float end_dz;
298         float effectnum, speed;
299
300         if(!zcurve)
301         {
302                 zcurve = spawn();
303                 zcurve.classname = "zcurve";
304         }
305
306         effectnum = ReadShort();
307
308         start_x = ReadCoord();
309         start_y = ReadCoord();
310         start_z = ReadCoord();
311
312         do
313         {
314                 end_x = ReadCoord();
315                 end_y = ReadCoord();
316                 end_z = ReadCoord();
317                 end_dz = ReadCoord();
318                 speed = ReadShort();
319                 zcurveparticles(effectnum, start, end, end_dz, 16 * (speed & 0x7FFF), 5); // at most 32 segments
320         }
321         while(!(speed & 0x8000));
322 }
323
324 void Net_ReadNexgunBeamParticle()
325 {
326         vector shotorg, endpos;
327         shotorg_x = ReadCoord(); shotorg_y = ReadCoord(); shotorg_z = ReadCoord();
328         endpos_x = ReadCoord(); endpos_y = ReadCoord(); endpos_z = ReadCoord();
329         
330         pointparticles(particleeffectnum("nex_muzzleflash"), shotorg, normalize(endpos - shotorg) * 1000, 1);
331         
332         //draw either the old v2.3 beam or the new beam
333         if (cvar("cl_particles_oldnexbeam") && (getstati(STAT_ALLOW_OLDNEXBEAM) || isdemo()))
334                 trailparticles(world, particleeffectnum("TE_TEI_G3"), shotorg, endpos);
335         else
336                 trailparticles(world, particleeffectnum("nex_beam"), shotorg, endpos);
337 }