Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
pointparticles.qc
Go to the documentation of this file.
1#include "pointparticles.qh"
2REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES)
3
4#ifdef SVQC
5// NOTE: also contains func_sparks
6
7bool pointparticles_SendEntity(entity this, entity to, float sendflags)
8{
9 WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
10
11 // optional features to save space
12 sendflags = sendflags & 0x0F;
14 sendflags |= SF_POINTPARTICLES_IMPULSE; // absolute count on toggle-on
15 if(this.movedir != '0 0 0' || this.velocity != '0 0 0')
16 sendflags |= SF_POINTPARTICLES_MOVING; // 4 bytes - saves CPU
17 if(this.waterlevel || this.count != 1)
18 sendflags |= SF_POINTPARTICLES_JITTER_AND_COUNT; // 4 bytes - obscure features almost never used
19 if(this.mins != '0 0 0' || this.maxs != '0 0 0')
20 sendflags |= SF_POINTPARTICLES_BOUNDS; // 14 bytes - saves lots of space
21
22 WriteByte(MSG_ENTITY, sendflags);
23 if(sendflags & SF_TRIGGER_UPDATE)
24 {
25 if(this.active == ACTIVE_ACTIVE)
27 else
28 WriteCoord(MSG_ENTITY, 0); // off
29 }
30 if(sendflags & SF_TRIGGER_RESET)
31 {
32 WriteVector(MSG_ENTITY, this.origin);
33 }
34 if(sendflags & SF_TRIGGER_INIT)
35 {
36 if(this.model != "null")
37 {
39 if(sendflags & SF_POINTPARTICLES_BOUNDS)
40 {
41 WriteVector(MSG_ENTITY, this.mins);
42 WriteVector(MSG_ENTITY, this.maxs);
43 }
44 }
45 else
46 {
48 if(sendflags & SF_POINTPARTICLES_BOUNDS)
49 {
50 WriteVector(MSG_ENTITY, this.maxs);
51 }
52 }
55 if(sendflags & SF_POINTPARTICLES_MOVING)
56 {
59 }
61 {
62 WriteShort(MSG_ENTITY, this.waterlevel * 16.0);
63 WriteByte(MSG_ENTITY, this.count * 16.0);
64 }
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
84{
85 if(this.origin != this.oldorigin)
86 {
88 this.oldorigin = this.origin;
89 }
90 this.nextthink = time;
91}
92
93spawnfunc(func_pointparticles)
94{
95 if(this.model != "") { precache_model(this.model); _setmodel(this, this.model); }
96 if(this.noise != "") precache_sound(this.noise);
97 if(this.mdl != "") this.cnt = 0; // use a good handler
98
99 if(!this.bgmscriptsustain) this.bgmscriptsustain = 1;
100 else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0;
101
102 if(!this.atten) this.atten = ATTEN_NORM;
103 else if(this.atten < 0) this.atten = 0;
104 if(!this.volume) this.volume = 1;
105 if(!this.count) this.count = 1;
106 if(!this.impulse) this.impulse = 1;
107
108 if(!this.modelindex)
109 {
110 setorigin(this, this.origin + this.mins);
111 setsize(this, '0 0 0', this.maxs - this.mins);
112 }
113 //if(!this.cnt) this.cnt = _particleeffectnum(this.mdl);
114 this.setactive = generic_netlinked_setactive;
115
117
118 if(this.targetname && this.targetname != "")
119 {
120 // backwards compatibility
122 }
123 this.reset = generic_netlinked_reset;
124 this.reset(this);
126 this.nextthink = time;
127}
128
129spawnfunc(func_sparks)
130{
131 if(this.count < 1) {
132 this.count = 25.0; // nice default value
133 }
134
135 if(this.impulse < 0.5) {
136 this.impulse = 2.5; // nice default value
137 }
138
139 this.mins = '0 0 0';
140 this.maxs = '0 0 0';
141 this.velocity = '0 0 -1';
142 this.mdl = "TE_SPARK";
143 this.cnt = 0; // use mdl
144
145 spawnfunc_func_pointparticles(this);
146}
147#elif defined(CSQC)
148
150
151entityclass(PointParticles);
152classfield(PointParticles) .int cnt; // effect number
153classfield(PointParticles) .vector velocity; // particle velocity
154classfield(PointParticles) .float waterlevel; // direction jitter
155classfield(PointParticles) .int count; // count multiplier
156classfield(PointParticles) .int impulse; // density
157classfield(PointParticles) .string noise; // sound
158classfield(PointParticles) .float atten;
159classfield(PointParticles) .float volume;
160classfield(PointParticles) .float absolute; // 1 = count per second is absolute, ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = only spawn at toggle
161classfield(PointParticles) .vector movedir; // trace direction
162classfield(PointParticles) .float glow_color; // palette index
163
164const int ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = 2;
165
166void Draw_PointParticles(entity this)
167{
168 float n, i, fail;
169 vector p;
170 vector sz;
171 vector o;
172 o = this.origin;
173 sz = this.maxs - this.mins;
174 n = doBGMScript(this);
175 if(this.absolute == ABSOLUTE_ONLY_SPAWN_AT_TOGGLE)
176 {
177 if(n >= 0)
178 n = this.just_toggled ? this.impulse : 0;
179 else
180 n = this.impulse * drawframetime;
181 }
182 else
183 {
184 n *= this.impulse * drawframetime;
185 if(this.just_toggled)
186 if(n < 1)
187 n = 1;
188 }
189 if(n == 0)
190 return;
191 fail = 0;
192 for(i = random(); i <= n && fail <= 64*n; ++i)
193 {
194 p = o + this.mins;
195 p.x += random() * sz.x;
196 p.y += random() * sz.y;
197 p.z += random() * sz.z;
198 if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL))
199 {
200 if(this.movedir != '0 0 0')
201 {
202 traceline(p, p + normalize(this.movedir) * 4096, 0, NULL);
203 p = trace_endpos;
204 int eff_num;
205 if(this.cnt)
206 eff_num = this.cnt;
207 else
208 eff_num = _particleeffectnum(this.mdl);
209 __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count);
210 }
211 else
212 {
213 int eff_num;
214 if(this.cnt)
215 eff_num = this.cnt;
216 else
217 eff_num = _particleeffectnum(this.mdl);
218 __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count);
219 }
220 if(this.noise != "")
221 {
222 setorigin(this, p);
223 _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten);
224 }
225 this.just_toggled = 0;
226 }
227 else if(this.absolute)
228 {
229 ++fail;
230 --i;
231 }
232 }
233 setorigin(this, o);
234}
235
236void Ent_PointParticles_Remove(entity this)
237{
238 strfree(this.noise);
239 strfree(this.bgmscript);
240 strfree(this.mdl);
241}
242
243NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
244{
245 float i;
246 vector v;
247 int sendflags = ReadByte();
248 if(sendflags & SF_TRIGGER_UPDATE)
249 {
250 i = ReadCoord(); // density (<0: point, >0: volume)
251 if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed
252 this.just_toggled = 1;
253 this.impulse = i;
254 }
255 if(sendflags & SF_TRIGGER_RESET)
256 {
257 this.origin = ReadVector();
258 }
259 if(sendflags & SF_TRIGGER_INIT)
260 {
261 this.modelindex = ReadShort();
262 if(sendflags & SF_POINTPARTICLES_BOUNDS)
263 {
264 if(this.modelindex)
265 {
266 this.mins = ReadVector();
267 this.maxs = ReadVector();
268 }
269 else
270 {
271 this.mins = '0 0 0';
272 this.maxs = ReadVector();
273 }
274 }
275 else
276 {
277 this.mins = this.maxs = '0 0 0';
278 }
279
280 this.cnt = ReadShort(); // effect number
281 this.mdl = strzone(ReadString()); // effect string
282
283 if(sendflags & SF_POINTPARTICLES_MOVING)
284 {
285 this.velocity = decompressShortVector(ReadShort());
286 this.movedir = decompressShortVector(ReadShort());
287 }
288 else
289 {
290 this.velocity = this.movedir = '0 0 0';
291 }
293 {
294 this.waterlevel = ReadShort() / 16.0;
295 this.count = ReadByte() / 16.0;
296 }
297 else
298 {
299 this.waterlevel = 0;
300 this.count = 1;
301 }
302 strcpy(this.noise, ReadString());
303 if(this.noise != "")
304 {
305 this.atten = ReadByte() / 64.0;
306 this.volume = ReadByte() / 255.0;
307 }
308 strcpy(this.bgmscript, ReadString());
309 if(this.bgmscript != "")
310 {
311 this.bgmscriptattack = ReadByte() / 64.0;
312 this.bgmscriptdecay = ReadByte() / 64.0;
313 this.bgmscriptsustain = ReadByte() / 255.0;
314 this.bgmscriptrelease = ReadByte() / 64.0;
315 }
317 }
318
319 return = true;
320
321 if(sendflags & SF_TRIGGER_UPDATE)
322 {
323 this.absolute = (this.impulse >= 0);
324 if(!this.absolute)
325 {
326 v = this.maxs - this.mins;
327 this.impulse *= -v.x * v.y * v.z / (64**3); // relative: particles per 64^3 cube
328 }
329 }
330
331 if(sendflags & SF_POINTPARTICLES_IMPULSE)
332 this.absolute = ABSOLUTE_ONLY_SPAWN_AT_TOGGLE;
333
334 setorigin(this, this.origin);
335 setsize(this, this.mins, this.maxs);
336 this.solid = SOLID_NOT;
337 this.draw = Draw_PointParticles;
338 if (isnew) IL_PUSH(g_drawables, this);
339 this.entremove = Ent_PointParticles_Remove;
340}
341#endif
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define ReadString
void BGMScript_InitEntity(entity e)
Definition bgmscript.qc:116
float doBGMScript(entity e)
Definition bgmscript.qc:173
float cnt
Definition powerups.qc:24
float count
Definition powerups.qc:22
IntrusiveList g_drawables
Definition main.qh:91
float drawframetime
Definition main.qh:108
int spawnflags
Definition ammo.qh:15
string mdl
Definition item.qh:89
float waterlevel
Definition player.qh:226
float compressShortVector(vector vec)
Definition util.qc:521
vector decompressShortVector(int data)
Definition util.qc:486
float modelindex
vector mins
vector velocity
const float SOLID_NOT
float time
vector trace_endpos
vector maxs
float nextthink
vector origin
vector oldorigin
vector trace_plane_normal
float dphitcontentsmask
#define use
const int SF_TRIGGER_RESET
Definition defs.qh:24
const int SF_TRIGGER_INIT
Definition defs.qh:22
int active
Definition defs.qh:34
const int ACTIVE_ACTIVE
Definition defs.qh:37
const int SF_TRIGGER_UPDATE
Definition defs.qh:23
float glow_color
model
Definition ent_cs.qc:139
solid
Definition ent_cs.qc:165
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
int SendFlags
Definition net.qh:118
#define NET_HANDLE(id, param)
Definition net.qh:15
const int MSG_ENTITY
Definition net.qh:115
#define ReadVector()
Definition net.qh:367
#define WriteHeader(to, id)
Definition net.qh:221
#define REGISTER_NET_LINKED(id)
Definition net.qh:55
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
int ReadByte()
float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig)
Definition common.qc:133
vector movedir
Definition viewloc.qh:18
void WriteString(string data, float dest, float desto)
float random(void)
float vlen(vector v)
void WriteShort(float data, float dest, float desto)
string precache_sound(string sample)
vector randomvec(void)
void WriteCoord(float data, float dest, float desto)
vector normalize(vector v)
void WriteByte(float data, float dest, float desto)
float floor(float f)
string strzone(string s)
#define entityclass(...)
Definition oo.qh:52
#define classfield(name)
Definition oo.qh:57
void pointparticles_think(entity this)
bool pointparticles_SendEntity(entity this, entity to, float sendflags)
const int PARTICLES_VISCULLING
const int SF_POINTPARTICLES_IMPULSE
const int PARTICLES_IMPULSE
const int SF_POINTPARTICLES_BOUNDS
const int SF_POINTPARTICLES_MOVING
const int SF_POINTPARTICLES_JITTER_AND_COUNT
#define NULL
Definition post.qh:14
float impulse
Definition progsdefs.qc:158
#define setthink(e, f)
vector
Definition self.qh:92
const float VOL_BASE
Definition sound.qh:36
const int CH_AMBIENT
Definition sound.qh:24
#define _sound(e, c, s, v, a)
Definition sound.qh:43
const float ATTEN_NORM
Definition sound.qh:30
#define spawnfunc(id)
Definition spawnfunc.qh:96
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
string noise
Definition subs.qh:83
void generic_netlinked_reset(entity this)
Definition triggers.qc:76
void generic_netlinked_legacy_use(entity this, entity actor, entity trigger)
Definition triggers.qc:98
void generic_netlinked_setactive(entity this, int act)
Definition triggers.qc:65
float volume
Definition triggers.qh:47
float atten
Definition triggers.qh:47
string targetname
Definition triggers.qh:56