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