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