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