]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/particles.qc
support settable attenuation for pointparticles
[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 glow_color; // palette color
46 .float impulse; // density
47 .string noise; // sound
48 .float atten;
49 .float volume;
50 .float absolute;
51 .vector movedir; // trace direction
52
53 void Draw_PointParticles()
54 {
55         float n, i, fail;
56         vector p;
57         vector sz;
58         vector o;
59         o = self.origin;
60         sz = self.maxs - self.mins;
61         n = self.impulse * drawframetime;
62         if(n == 0)
63                 return;
64         fail = 0;
65         for(i = random(); i <= n && fail <= 64*n; ++i)
66         {
67                 p = o + self.mins;
68                 p_x += random() * sz_x;
69                 p_y += random() * sz_y;
70                 p_z += random() * sz_z;
71                 if(PointInBrush(self, p))
72                 {
73                         if(self.movedir != '0 0 0')
74                         {
75                                 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
76                                 p = trace_endpos;
77                                 pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count, self.glow_color);
78                         }
79                         else
80                                 pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count, self.glow_color);
81                         if(self.noise != "")
82                         {
83                                 self.origin = p;
84                                 sound(self, CHAN_AUTO, self.noise, VOL_BASE * self.volume, self.atten);
85                         }
86                 }
87                 else if(self.absolute)
88                 {
89                         ++fail;
90                         --i;
91                 }
92         }
93         self.origin = o;
94 }
95
96 void Ent_PointParticles_Remove()
97 {
98         if(self.noise)
99                 strunzone(self.noise);
100         self.noise = string_null;
101 }
102
103 void Ent_PointParticles()
104 {
105         float f;
106         vector v;
107         f = ReadByte();
108         if(f & 2)
109         {
110                 self.impulse = ReadCoord(); // density (<0: point, >0: volume)
111         }
112         if(f & 4)
113         {
114                 self.origin_x = ReadCoord();
115                 self.origin_y = ReadCoord();
116                 self.origin_z = ReadCoord();
117         }
118         if(f & 1)
119         {
120                 self.modelindex = ReadShort();
121                 if(self.modelindex)
122                 {
123                         self.mins_x = ReadCoord();
124                         self.mins_y = ReadCoord();
125                         self.mins_z = ReadCoord();
126                         self.maxs_x = ReadCoord();
127                         self.maxs_y = ReadCoord();
128                         self.maxs_z = ReadCoord();
129                 }
130                 else
131                 {
132                         self.mins    = '0 0 0';
133                         self.maxs_x = ReadCoord();
134                         self.maxs_y = ReadCoord();
135                         self.maxs_z = ReadCoord();
136                 }
137
138                 self.cnt = ReadShort(); // effect number
139
140                 self.velocity = decompressShortVector(ReadShort());
141                 self.movedir = decompressShortVector(ReadShort());
142                 self.waterlevel = ReadCoord();
143                 self.count = ReadCoord();
144                 self.glow_color = ReadByte();
145                 if(self.noise)
146                         strunzone(self.noise);
147                 self.noise = strzone(ReadString());
148                 if(self.noise != "")
149                 {
150                         self.atten = ReadByte() / 64.0;
151                         self.volume = ReadByte() / 255.0;
152                 }
153         }
154
155         if(f & 2)
156         {
157                 self.absolute = (self.impulse >= 0);
158                 if(!self.absolute)
159                 {
160                         v = self.maxs - self.mins;
161                         self.impulse *= -v_x * v_y * v_z / 262144; // relative: particles per 64^3 cube
162                 }
163         }
164
165         setorigin(self, self.origin);
166         setsize(self, self.mins, self.maxs);
167         self.solid = SOLID_NOT;
168         self.draw = Draw_PointParticles;
169         self.entremove = Ent_PointParticles_Remove;
170 }
171
172 void Draw_Rain()
173 {
174     te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
175 }
176
177 void Draw_Snow()
178 {
179     te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
180 }
181
182 void Ent_RainOrSnow()
183 {
184         self.impulse = ReadByte(); // Rain, Snow, or Whatever
185         self.origin_x = ReadCoord();
186         self.origin_y = ReadCoord();
187         self.origin_z = ReadCoord();
188         self.maxs_x = ReadCoord();
189         self.maxs_y = ReadCoord();
190         self.maxs_z = ReadCoord();
191         self.velocity = decompressShortVector(ReadShort());
192         self.count = ReadShort() * 10;
193         self.glow_color = ReadByte(); // color
194
195         self.mins    = -0.5 * self.maxs;
196         self.maxs    =  0.5 * self.maxs;
197         self.origin  = self.origin - self.mins;
198
199         setorigin(self, self.origin);
200         setsize(self, self.mins, self.maxs);
201         self.solid = SOLID_NOT;
202         if(self.impulse)
203                 self.draw = Draw_Rain;
204         else
205                 self.draw = Draw_Snow;
206 }
207
208 entity zcurve;
209 void zcurveparticles(float effectnum, vector start, vector end, float end_dz, float speed, float depth)
210 {
211         // end_dz:
212         //   IF IT WERE A STRAIGHT LINE, it'd end end_dz above end
213
214         vector mid;
215         mid = (start + end) * 0.5;
216
217         end_dz *= 0.25;
218         mid_z += end_dz;
219
220         --depth;
221         if(depth < 0 || normalize(mid - start) * normalize(end - start) > 0.999999)
222         // TODO make this a variable threshold
223         // currently: 0.081 degrees
224         // 0.99999 would be 0.256 degrees and is visible
225         {
226                 zcurve.velocity = speed * normalize(end - start);
227                 trailparticles(zcurve, effectnum, start, end);
228         }
229         else
230         {
231                 zcurveparticles(effectnum, start, mid, end_dz, speed, depth);
232                 zcurveparticles(effectnum, mid, end, end_dz, speed, depth);
233         }
234 }
235
236 void Net_ReadZCurveParticles()
237 {
238         vector start, end;
239         float end_dz;
240         float effectnum, speed;
241
242         if(!zcurve)
243         {
244                 zcurve = spawn();
245                 zcurve.classname = "zcurve";
246         }
247
248         effectnum = ReadShort();
249
250         start_x = ReadCoord();
251         start_y = ReadCoord();
252         start_z = ReadCoord();
253         end_x = ReadCoord();
254         end_y = ReadCoord();
255         end_z = ReadCoord();
256         end_dz = ReadCoord();
257         speed = ReadShort() * 16;
258
259         zcurveparticles(effectnum, start, end, end_dz, speed, 5); // at most 32 segments
260 }
261
262 void Net_ReadNexgunBeamParticle()
263 {
264         vector shotorg, endpos;
265         shotorg_x = ReadCoord(); shotorg_y = ReadCoord(); shotorg_z = ReadCoord();
266         endpos_x = ReadCoord(); endpos_y = ReadCoord(); endpos_z = ReadCoord();
267         
268         pointparticles(particleeffectnum("nex_muzzleflash"), shotorg, normalize(endpos - shotorg) * 1000, 1);
269         
270         //draw either the old v2.3 beam or the new beam
271         if (cvar("cl_particles_oldnexbeam") && (getstati(STAT_ALLOW_OLDNEXBEAM) || isdemo()))
272                 trailparticles(world, particleeffectnum("TE_TEI_G3"), shotorg, endpos);
273         else
274                 trailparticles(world, particleeffectnum("nex_beam"), shotorg, endpos);
275 }