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 &= 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 = this.origin;
172 sz = this.maxs - this.mins;
173 n = doBGMScript(this);
174 if(this.absolute == ABSOLUTE_ONLY_SPAWN_AT_TOGGLE)
175 {
176 if(n >= 0)
177 n = this.just_toggled ? this.impulse : 0;
178 else
179 n = this.impulse * drawframetime;
180 }
181 else
182 {
183 n *= this.impulse * drawframetime;
184 if(this.just_toggled)
185 if(n < 1)
186 n = 1;
187 }
188 if(n == 0)
189 return;
190 fail = 0;
191 for(i = random(); i <= n && fail <= 64*n; ++i)
192 {
193 p = o + this.mins;
194 p.x += random() * sz.x;
195 p.y += random() * sz.y;
196 p.z += random() * sz.z;
197 if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL))
198 {
199 if(this.movedir != '0 0 0')
200 {
201 traceline(p, p + normalize(this.movedir) * 4096, 0, NULL);
202 p = trace_endpos;
203 int eff_num;
204 if(this.cnt)
205 eff_num = this.cnt;
206 else
207 eff_num = _particleeffectnum(this.mdl);
208 __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count);
209 }
210 else
211 {
212 int eff_num;
213 if(this.cnt)
214 eff_num = this.cnt;
215 else
216 eff_num = _particleeffectnum(this.mdl);
217 __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count);
218 }
219 if(this.noise != "")
220 {
221 setorigin(this, p);
222 _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten);
223 }
224 this.just_toggled = 0;
225 }
226 else if(this.absolute)
227 {
228 ++fail;
229 --i;
230 }
231 }
232 setorigin(this, o);
233}
234
235void Ent_PointParticles_Remove(entity this)
236{
237 strfree(this.noise);
238 strfree(this.bgmscript);
239 strfree(this.mdl);
240}
241
242NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
243{
244 float i;
245 vector v;
246 int sendflags = ReadByte();
247 if(sendflags & SF_TRIGGER_UPDATE)
248 {
249 i = ReadCoord(); // density (<0: point, >0: volume)
250 if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed
251 this.just_toggled = 1;
252 this.impulse = i;
253 }
254 if(sendflags & SF_TRIGGER_RESET)
255 {
256 this.origin = ReadVector();
257 }
258 if(sendflags & SF_TRIGGER_INIT)
259 {
260 this.modelindex = ReadShort();
261 if(sendflags & SF_POINTPARTICLES_BOUNDS)
262 {
263 if(this.modelindex)
264 {
265 this.mins = ReadVector();
266 this.maxs = ReadVector();
267 }
268 else
269 {
270 this.mins = '0 0 0';
271 this.maxs = ReadVector();
272 }
273 }
274 else
275 {
276 this.mins = this.maxs = '0 0 0';
277 }
278
279 this.cnt = ReadShort(); // effect number
280 this.mdl = strzone(ReadString()); // effect string
281
282 if(sendflags & SF_POINTPARTICLES_MOVING)
283 {
284 this.velocity = decompressShortVector(ReadShort());
285 this.movedir = decompressShortVector(ReadShort());
286 }
287 else
288 {
289 this.velocity = this.movedir = '0 0 0';
290 }
292 {
293 this.waterlevel = ReadShort() / 16.0;
294 this.count = ReadByte() / 16.0;
295 }
296 else
297 {
298 this.waterlevel = 0;
299 this.count = 1;
300 }
301 strcpy(this.noise, ReadString());
302 if(this.noise != "")
303 {
304 this.atten = ReadByte() / 64.0;
305 this.volume = ReadByte() / 255.0;
306 }
307 strcpy(this.bgmscript, ReadString());
308 if(this.bgmscript != "")
309 {
310 this.bgmscriptattack = ReadByte() / 64.0;
311 this.bgmscriptdecay = ReadByte() / 64.0;
312 this.bgmscriptsustain = ReadByte() / 255.0;
313 this.bgmscriptrelease = ReadByte() / 64.0;
314 }
316 }
317
318 return = true;
319
320 if(sendflags & SF_TRIGGER_UPDATE)
321 {
322 this.absolute = (this.impulse >= 0);
323 if(!this.absolute)
324 {
325 v = this.maxs - this.mins;
326 this.impulse *= -v.x * v.y * v.z / (64**3); // relative: particles per 64^3 cube
327 }
328 }
329
330 if(sendflags & SF_POINTPARTICLES_IMPULSE)
331 this.absolute = ABSOLUTE_ONLY_SPAWN_AT_TOGGLE;
332
333 setorigin(this, this.origin);
334 setsize(this, this.mins, this.maxs);
335 this.solid = SOLID_NOT;
336 this.draw = Draw_PointParticles;
337 if (isnew) IL_PUSH(g_drawables, this);
338 this.entremove = Ent_PointParticles_Remove;
339}
340#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:225
float compressShortVector(vector vec)
Definition util.qc:519
vector decompressShortVector(int data)
Definition util.qc:484
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:159
#define NET_HANDLE(id, param)
Definition net.qh:15
const int MSG_ENTITY
Definition net.qh:156
#define ReadVector()
Definition net.qh:350
#define WriteHeader(to, id)
Definition net.qh:265
#define REGISTER_NET_LINKED(id)
Definition net.qh:91
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
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