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