Cleanup
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / func / pointparticles.qc
1 #ifdef CSQC
2         #include "../../../client/particles.qh"
3 #endif
4
5 #ifdef SVQC
6 // NOTE: also contains func_sparks
7
8 bool pointparticles_SendEntity(entity this, entity to, float fl)
9 {
10         WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
11
12         // optional features to save space
13         fl = fl & 0x0F;
14         if(self.spawnflags & 2)
15                 fl |= 0x10; // absolute count on toggle-on
16         if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
17                 fl |= 0x20; // 4 bytes - saves CPU
18         if(self.waterlevel || self.count != 1)
19                 fl |= 0x40; // 4 bytes - obscure features almost never used
20         if(self.mins != '0 0 0' || self.maxs != '0 0 0')
21                 fl |= 0x80; // 14 bytes - saves lots of space
22
23         WriteByte(MSG_ENTITY, fl);
24         if(fl & 2)
25         {
26                 if(self.state)
27                         WriteCoord(MSG_ENTITY, self.impulse);
28                 else
29                         WriteCoord(MSG_ENTITY, 0); // off
30         }
31         if(fl & 4)
32         {
33                 WriteCoord(MSG_ENTITY, self.origin_x);
34                 WriteCoord(MSG_ENTITY, self.origin_y);
35                 WriteCoord(MSG_ENTITY, self.origin_z);
36         }
37         if(fl & 1)
38         {
39                 if(self.model != "null")
40                 {
41                         WriteShort(MSG_ENTITY, self.modelindex);
42                         if(fl & 0x80)
43                         {
44                                 WriteCoord(MSG_ENTITY, self.mins_x);
45                                 WriteCoord(MSG_ENTITY, self.mins_y);
46                                 WriteCoord(MSG_ENTITY, self.mins_z);
47                                 WriteCoord(MSG_ENTITY, self.maxs_x);
48                                 WriteCoord(MSG_ENTITY, self.maxs_y);
49                                 WriteCoord(MSG_ENTITY, self.maxs_z);
50                         }
51                 }
52                 else
53                 {
54                         WriteShort(MSG_ENTITY, 0);
55                         if(fl & 0x80)
56                         {
57                                 WriteCoord(MSG_ENTITY, self.maxs_x);
58                                 WriteCoord(MSG_ENTITY, self.maxs_y);
59                                 WriteCoord(MSG_ENTITY, self.maxs_z);
60                         }
61                 }
62                 WriteShort(MSG_ENTITY, self.cnt);
63                 if(fl & 0x20)
64                 {
65                         WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
66                         WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
67                 }
68                 if(fl & 0x40)
69                 {
70                         WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
71                         WriteByte(MSG_ENTITY, self.count * 16.0);
72                 }
73                 WriteString(MSG_ENTITY, self.noise);
74                 if(self.noise != "")
75                 {
76                         WriteByte(MSG_ENTITY, floor(self.atten * 64));
77                         WriteByte(MSG_ENTITY, floor(self.volume * 255));
78                 }
79                 WriteString(MSG_ENTITY, self.bgmscript);
80                 if(self.bgmscript != "")
81                 {
82                         WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
83                         WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
84                         WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
85                         WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
86                 }
87         }
88         return 1;
89 }
90
91 void pointparticles_use()
92 {SELFPARAM();
93         self.state = !self.state;
94         self.SendFlags |= 2;
95 }
96
97 void pointparticles_think()
98 {SELFPARAM();
99         if(self.origin != self.oldorigin)
100         {
101                 self.SendFlags |= 4;
102                 self.oldorigin = self.origin;
103         }
104         self.nextthink = time;
105 }
106
107 void pointparticles_reset()
108 {SELFPARAM();
109         if(self.spawnflags & 1)
110                 self.state = 1;
111         else
112                 self.state = 0;
113 }
114
115 spawnfunc(func_pointparticles)
116 {
117         if(self.model != "")
118                 _setmodel(self, self.model);
119         if(self.noise != "")
120                 precache_sound (self.noise);
121
122         if(!self.bgmscriptsustain)
123                 self.bgmscriptsustain = 1;
124         else if(self.bgmscriptsustain < 0)
125                 self.bgmscriptsustain = 0;
126
127         if(!self.atten)
128                 self.atten = ATTEN_NORM;
129         else if(self.atten < 0)
130                 self.atten = 0;
131         if(!self.volume)
132                 self.volume = 1;
133         if(!self.count)
134                 self.count = 1;
135         if(!self.impulse)
136                 self.impulse = 1;
137
138         if(!self.modelindex)
139         {
140                 setorigin(self, self.origin + self.mins);
141                 setsize(self, '0 0 0', self.maxs - self.mins);
142         }
143         if(!self.cnt)
144                 self.cnt = _particleeffectnum(self.mdl);
145
146         Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity);
147
148         IFTARGETED
149         {
150                 self.use = pointparticles_use;
151                 self.reset = pointparticles_reset;
152                 self.reset();
153         }
154         else
155                 self.state = 1;
156         self.think = pointparticles_think;
157         self.nextthink = time;
158 }
159
160 spawnfunc(func_sparks)
161 {
162         // self.cnt is the amount of sparks that one burst will spawn
163         if(self.cnt < 1) {
164                 self.cnt = 25.0; // nice default value
165         }
166
167         // self.wait is the probability that a sparkthink will spawn a spark shower
168         // range: 0 - 1, but 0 makes little sense, so...
169         if(self.wait < 0.05) {
170                 self.wait = 0.25; // nice default value
171         }
172
173         self.count = self.cnt;
174         self.mins = '0 0 0';
175         self.maxs = '0 0 0';
176         self.velocity = '0 0 -1';
177         self.mdl = "TE_SPARK";
178         self.impulse = 10 * self.wait; // by default 2.5/sec
179         self.wait = 0;
180         self.cnt = 0; // use mdl
181
182         spawnfunc_func_pointparticles(this);
183 }
184 #elif defined(CSQC)
185
186 void Draw_PointParticles(entity this)
187 {
188         float n, i, fail;
189         vector p;
190         vector sz;
191         vector o;
192         o = self.origin;
193         sz = self.maxs - self.mins;
194         n = doBGMScript(self);
195         if(self.absolute == 2)
196         {
197                 if(n >= 0)
198                         n = self.just_toggled ? self.impulse : 0;
199                 else
200                         n = self.impulse * drawframetime;
201         }
202         else
203         {
204                 n *= self.impulse * drawframetime;
205                 if(self.just_toggled)
206                         if(n < 1)
207                                 n = 1;
208         }
209         if(n == 0)
210                 return;
211         fail = 0;
212         for(i = random(); i <= n && fail <= 64*n; ++i)
213         {
214                 p = o + self.mins;
215                 p.x += random() * sz.x;
216                 p.y += random() * sz.y;
217                 p.z += random() * sz.z;
218                 if(WarpZoneLib_BoxTouchesBrush(p, p, self, world))
219                 {
220                         if(self.movedir != '0 0 0')
221                         {
222                                 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
223                                 p = trace_endpos;
224                                 __pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
225                         }
226                         else
227                         {
228                                 __pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
229                         }
230                         if(self.noise != "")
231                         {
232                                 setorigin(self, p);
233                                 _sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten);
234                         }
235                         self.just_toggled = 0;
236                 }
237                 else if(self.absolute)
238                 {
239                         ++fail;
240                         --i;
241                 }
242         }
243         setorigin(self, o);
244 }
245
246 void Ent_PointParticles_Remove()
247 {SELFPARAM();
248         if(self.noise)
249                 strunzone(self.noise);
250         self.noise = string_null;
251         if(self.bgmscript)
252                 strunzone(self.bgmscript);
253         self.bgmscript = string_null;
254 }
255
256 NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
257 {
258         float i;
259         vector v;
260         int f = ReadByte();
261         if(f & 2)
262         {
263                 i = ReadCoord(); // density (<0: point, >0: volume)
264                 if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
265                         self.just_toggled = 1;
266                 self.impulse = i;
267         }
268         if(f & 4)
269         {
270                 self.origin_x = ReadCoord();
271                 self.origin_y = ReadCoord();
272                 self.origin_z = ReadCoord();
273         }
274         if(f & 1)
275         {
276                 self.modelindex = ReadShort();
277                 if(f & 0x80)
278                 {
279                         if(self.modelindex)
280                         {
281                                 self.mins_x = ReadCoord();
282                                 self.mins_y = ReadCoord();
283                                 self.mins_z = ReadCoord();
284                                 self.maxs_x = ReadCoord();
285                                 self.maxs_y = ReadCoord();
286                                 self.maxs_z = ReadCoord();
287                         }
288                         else
289                         {
290                                 self.mins    = '0 0 0';
291                                 self.maxs_x = ReadCoord();
292                                 self.maxs_y = ReadCoord();
293                                 self.maxs_z = ReadCoord();
294                         }
295                 }
296                 else
297                 {
298                         self.mins = self.maxs = '0 0 0';
299                 }
300
301                 self.cnt = ReadShort(); // effect number
302
303                 if(f & 0x20)
304                 {
305                         self.velocity = decompressShortVector(ReadShort());
306                         self.movedir = decompressShortVector(ReadShort());
307                 }
308                 else
309                 {
310                         self.velocity = self.movedir = '0 0 0';
311                 }
312                 if(f & 0x40)
313                 {
314                         self.waterlevel = ReadShort() / 16.0;
315                         self.count = ReadByte() / 16.0;
316                 }
317                 else
318                 {
319                         self.waterlevel = 0;
320                         self.count = 1;
321                 }
322                 if(self.noise)
323                         strunzone(self.noise);
324                 if(self.bgmscript)
325                         strunzone(self.bgmscript);
326                 self.noise = strzone(ReadString());
327                 if(self.noise != "")
328                 {
329                         self.atten = ReadByte() / 64.0;
330                         self.volume = ReadByte() / 255.0;
331                 }
332                 self.bgmscript = strzone(ReadString());
333                 if(self.bgmscript != "")
334                 {
335                         self.bgmscriptattack = ReadByte() / 64.0;
336                         self.bgmscriptdecay = ReadByte() / 64.0;
337                         self.bgmscriptsustain = ReadByte() / 255.0;
338                         self.bgmscriptrelease = ReadByte() / 64.0;
339                 }
340                 BGMScript_InitEntity(self);
341         }
342
343         return = true;
344
345         if(f & 2)
346         {
347                 self.absolute = (self.impulse >= 0);
348                 if(!self.absolute)
349                 {
350                         v = self.maxs - self.mins;
351                         self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
352                 }
353         }
354
355         if(f & 0x10)
356                 self.absolute = 2;
357
358         setorigin(self, self.origin);
359         setsize(self, self.mins, self.maxs);
360         self.solid = SOLID_NOT;
361         self.draw = Draw_PointParticles;
362         self.entremove = Ent_PointParticles_Remove;
363 }
364 #endif