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