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