UFO: Alien Invasion
Loading...
Searching...
No Matches
cl_particle.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24*/
25
26#include "../client.h"
27#include "cl_particle.h"
28#include "cl_localentity.h"
29#include "cl_hud.h"
30#include "../renderer/r_light.h"
32#include "../../shared/parse.h"
33
34#define MAX_MAPPARTICLES 1024
35#define MAX_TIMEDPARTICLES 16
36
37#define PTL_INTENSITY_TO_RADIUS 256
38
40
42typedef struct mapParticle_s {
43 char ptl[MAX_VAR];
44 const char* info;
50
51typedef struct timedParticle_s {
52 char ptl[MAX_VAR];
58 bool children;
60 int max;
61 int n;
62 int dt;
65
68
71
72#define V_VECS ((1 << V_FLOAT) | (1 << V_POS) | (1 << V_VECTOR) | (1 << V_COLOR))
73#define PTL_ONLY_ONE_TYPE (1<<31)
74#define V_UNTYPED 0x7FFF
75
86
88static char const* const pf_strings[] = {
89 "init",
90 "run",
91 "think",
92 "round",
93 "physics"
94};
96
98static const size_t pf_values[] = {
99 offsetof(ptlDef_t, init),
100 offsetof(ptlDef_t, run),
101 offsetof(ptlDef_t, think),
102 offsetof(ptlDef_t, round),
103 offsetof(ptlDef_t, physics)
104};
106
123
125static char const* const pc_strings[] = {
126 "end",
127
128 "push", "pop", "kpop",
129 "add", "sub",
130 "mul", "div",
131 "sin", "cos", "tan",
132 "rand", "crand",
133 "v2", "v3", "v4",
134
135 "kill",
136 "spawn", "nspawn", "tnspawn", "child"
137};
139
155
160static const value_t pps[] = {
161 {"image", V_STRING, offsetof(ptl_t, pic), 0},
162 {"model", V_STRING, offsetof(ptl_t, model), 0},
163 {"program", V_STRING, offsetof(ptl_t, program), 0},
164 {"skin", V_INT, offsetof(ptl_t, skin), MEMBER_SIZEOF(ptl_t, skin)},
165 {"blend", V_BLEND, offsetof(ptl_t, blend), MEMBER_SIZEOF(ptl_t, blend)},
166 {"style", V_STYLE, offsetof(ptl_t, style), MEMBER_SIZEOF(ptl_t, style)},
167 {"thinkfade", V_FADE, offsetof(ptl_t, thinkFade), MEMBER_SIZEOF(ptl_t, thinkFade)},
168 {"framefade", V_FADE, offsetof(ptl_t, frameFade), MEMBER_SIZEOF(ptl_t, frameFade)},
169 {"size", V_POS, offsetof(ptl_t, size), MEMBER_SIZEOF(ptl_t, size)},
170 {"scale", V_VECTOR, offsetof(ptl_t, scale), MEMBER_SIZEOF(ptl_t, scale)},
171 {"color", V_COLOR, offsetof(ptl_t, color), MEMBER_SIZEOF(ptl_t, color)},
172 {"a", V_VECTOR, offsetof(ptl_t, a), MEMBER_SIZEOF(ptl_t, a)},
173 {"v", V_VECTOR, offsetof(ptl_t, v), MEMBER_SIZEOF(ptl_t, v)},
174 {"s", V_VECTOR, offsetof(ptl_t, s), MEMBER_SIZEOF(ptl_t, s)},
175 {"offset", V_VECTOR, offsetof(ptl_t, offset), MEMBER_SIZEOF(ptl_t, offset)},
176 {"scroll_s", V_FLOAT, offsetof(ptl_t, scrollS), MEMBER_SIZEOF(ptl_t, scrollS)},
177 {"scroll_t", V_FLOAT, offsetof(ptl_t, scrollT), MEMBER_SIZEOF(ptl_t, scrollT)},
178
179 /* t and dt are not specified in particle definitions */
180 /* but they can be used as references */
181 {"t", V_FLOAT, offsetof(ptl_t, t), MEMBER_SIZEOF(ptl_t, t)},
182 {"dt", V_FLOAT, offsetof(ptl_t, dt), MEMBER_SIZEOF(ptl_t, dt)},
183
184 {"rounds", V_INT, offsetof(ptl_t, rounds), MEMBER_SIZEOF(ptl_t, rounds)},
185 {"angles", V_VECTOR, offsetof(ptl_t, angles), MEMBER_SIZEOF(ptl_t, angles)},
186 {"omega", V_VECTOR, offsetof(ptl_t, omega), MEMBER_SIZEOF(ptl_t, omega)},
187 {"life", V_FLOAT, offsetof(ptl_t, life), MEMBER_SIZEOF(ptl_t, life)},
188 {"tps", V_FLOAT, offsetof(ptl_t, tps), MEMBER_SIZEOF(ptl_t, tps)},
189 {"lastthink", V_FLOAT, offsetof(ptl_t, lastThink), MEMBER_SIZEOF(ptl_t, lastThink)},
190 {"frame", V_INT, offsetof(ptl_t, frame), MEMBER_SIZEOF(ptl_t, frame)},
191 {"endframe", V_INT, offsetof(ptl_t, endFrame), MEMBER_SIZEOF(ptl_t, endFrame)},
192 {"fps", V_FLOAT, offsetof(ptl_t, fps), MEMBER_SIZEOF(ptl_t, fps)},
193 {"lastframe", V_FLOAT, offsetof(ptl_t, lastFrame), MEMBER_SIZEOF(ptl_t, lastFrame)},
194 {"levelflags", V_INT, offsetof(ptl_t, levelFlags), MEMBER_SIZEOF(ptl_t, levelFlags)},
195 {"physics", V_BOOL, offsetof(ptl_t, physics), MEMBER_SIZEOF(ptl_t, physics)},
196 {"stick", V_BOOL, offsetof(ptl_t, stick), MEMBER_SIZEOF(ptl_t, stick)},
197 {"bounce", V_BOOL, offsetof(ptl_t, bounce), MEMBER_SIZEOF(ptl_t, bounce)},
198 {"autohide", V_BOOL, offsetof(ptl_t, autohide), MEMBER_SIZEOF(ptl_t, autohide)},
199 {"stayalive", V_BOOL, offsetof(ptl_t, stayalive), MEMBER_SIZEOF(ptl_t, stayalive)},
200 {"weather", V_BOOL, offsetof(ptl_t, weather), MEMBER_SIZEOF(ptl_t, weather)},
201 {"lightcolor", V_VECTOR, offsetof(ptl_t, lightColor), MEMBER_SIZEOF(ptl_t, lightColor)},
202 {"lightintensity", V_FLOAT, offsetof(ptl_t, lightIntensity), MEMBER_SIZEOF(ptl_t, lightIntensity)},
203 {"lightsustain", V_FLOAT, offsetof(ptl_t, lightSustain), MEMBER_SIZEOF(ptl_t, lightSustain)},
204
205 {nullptr, V_NULL, 0, 0}
206};
207
208/* =========================================================== */
209
210#define MAX_PTLDEFS 256
211#define MAX_PTLCMDS (MAX_PTLDEFS * 32)
212
215
216static int numPtlDefs;
217static int numPtlCmds;
218
219#define MAX_PCMD_DATA (MAX_PTLCMDS * 8)
220
223static byte* pcmdPos = pcmdData;
224
225static const int RSTACK = -(MAX_PCMD_DATA);
226
227#define MAX_STACK_DEPTH 8
228#define MAX_STACK_DATA 512
229
233
242static void CL_ParticleSpawnTimed (const char* name, ptl_t* parent, bool children, int deltaTime, int n)
243{
244 if (n <= 0)
245 Com_Error(ERR_DROP, "Timed particle should spawn particles");
246
247 if (deltaTime <= 0)
248 Com_Error(ERR_DROP, "Delta time for timed particle is invalid");
249
250 const size_t length = lengthof(timedParticles);
251 for (int i = 0; i < length; i++) {
253 if (tp->n != tp->max)
254 continue;
255 /* found a free slot */
256 Q_strncpyz(tp->ptl, name, sizeof(tp->ptl));
257 tp->levelFlags = parent->levelFlags;
258 tp->dt = deltaTime;
259 tp->n = 0;
260 tp->children = children;
261 tp->max = n;
262 tp->parent = parent;
263 return;
264 }
265 Com_Printf("Could not spawn timed particles due to overflow\n");
266}
267
276void CL_AddMapParticle (const char* ptl, const vec3_t origin, const vec2_t wait, const char* info, int levelflags)
277{
278 if (cl.numMapParticles >= MAX_MAPPARTICLES) {
279 Com_Printf("Too many map particles (don't add %s) - exceeded %i\n", ptl, MAX_MAPPARTICLES);
280 return;
281 }
282
283 mapParticle_t* mp = &mapParticles[cl.numMapParticles++];
284 Q_strncpyz(mp->ptl, ptl, sizeof(mp->ptl));
286 mp->info = info;
287 mp->levelflags = levelflags;
288 mp->wait[0] = wait[0] * 1000;
289 mp->wait[1] = wait[1] * 1000;
290 mp->nextTime = cl.time + wait[0] + wait[1] * frand() + 1;
291
292 Com_DPrintf(DEBUG_CLIENT, "Adding map particle %s (%i) with levelflags %i\n", ptl, cl.numMapParticles, levelflags);
293}
294
298static inline void CL_ParticleLoadArt (ptlArt_t* a)
299{
300 /* register the art */
301 switch (a->type) {
302 case ART_PIC:
303 {
304 const char* imageName;
305 /* only one image */
306 if (a->name[0] != '+')
307 imageName = a->name;
308 else /* load several frames */
309 imageName = va("%s%c%c", a->name + 1, a->frame / 10 + '0', a->frame % 10 + '0');
310 a->art.image = R_FindPics(imageName);
311 if (!a->art.image)
312 Com_Printf("CL_ParticleLoadArt: Could not load image: '%s'\n", imageName);
313 }
314 break;
315 case ART_MODEL:
317 a->art.model = R_FindModel(a->name);
318 break;
319 default:
320 Com_Error(ERR_DROP, "CL_ParticleLoadArt: Unknown art type\n");
321 }
322}
323
325{
326
327 for (int i = 0; i < r_numParticlesArt; i++) {
330 }
331}
332
339static ptlArt_t* CL_ParticleGetArt (const char* name, int frame, artType_t type)
340{
341 ptlArt_t* a;
342 int i;
343
344 /* search for the pic in the list */
345 for (i = 0, a = r_particlesArt; i < r_numParticlesArt; i++, a++)
346 if (a->type == type && (type == ART_PIC && a->frame == frame) && Q_streq(name, a->name))
347 break;
348
349 if (i < r_numParticlesArt)
350 return a;
351
352 if (i >= MAX_PTL_ART)
353 Com_Error(ERR_DROP, "CL_ParticleGetArt: MAX_PTL_ART overflow");
354
355 a->skin = 0;
356 a->type = type;
357 a->frame = frame;
358 Q_strncpyz(a->name, name, sizeof(a->name));
359
361
362 /* check for an error */
363 if (!a->art.image)
364 return nullptr;
365
367
368 return a;
369}
370
377static inline void* CL_ParticleCommandGetDataLocation (ptl_t* p, const ptlCmd_t* cmd)
378{
379 if (cmd->ref < 0)
380 /* a negative ref value is relative to the particle */
381 return (byte*)p - cmd->ref;
382 /* data is stored on the global command data hunk */
383 return (byte*)pcmdData + cmd->ref;
384}
385
386static void CL_ParticleFunction (ptl_t* p, ptlCmd_t* cmd)
387{
388 /* test for null cmd */
389 if (!cmd)
390 return;
391
392 ptrdiff_t e = 0;
393 /* run until finding PC_END */
394 for (int stackIdx = 0; cmd->cmd != PC_END; cmd++) {
395 int i, j, n;
396 void* cmdData;
397 float arg;
398 ptl_t* pnew;
399 if (cmd->ref > RSTACK)
400 cmdData = CL_ParticleCommandGetDataLocation(p, cmd);
401 else {
402 if (!stackIdx)
403 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
404
405 /* pop an element off the stack */
406 e = (byte*) stackPtr[--stackIdx] - cmdStack;
407
408 i = RSTACK - cmd->ref;
409 if (!i) {
410 /* normal stack reference */
411 cmdData = stackPtr[stackIdx];
412 cmd->type = stackType[stackIdx];
413 } else {
414 /* stack reference to element of vector */
415 if ((1 << stackType[stackIdx]) & V_VECS) {
416 cmd->type = V_FLOAT;
417 cmdData = (float*) stackPtr[stackIdx] + (i - 1);
418 } else {
419 Com_Error(ERR_DROP, "CL_ParticleFunction: can't get components of a non-vector type (particle %s)", p->ctrl->name);
420 }
421 }
422 }
423
424 int type;
425 switch (cmd->cmd) {
426 case PC_PUSH:
427 /* check for stack overflow */
428 if (stackIdx >= MAX_STACK_DEPTH)
429 Com_Error(ERR_DROP, "CL_ParticleFunction: stack overflow");
430
431 /* store the value in the stack */
432 stackPtr[stackIdx] = &cmdStack[e];
433 stackType[stackIdx] = cmd->type;
434 e += Com_SetValue(stackPtr[stackIdx++], cmdData, (valueTypes_t)cmd->type, 0, 0);
435 break;
436
437 case PC_POP:
438 case PC_KPOP:
439 /* check for stack underflow */
440 if (stackIdx == 0)
441 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
442
443 /* get pics and models */
444 if (offsetof(ptl_t, pic) == -cmd->ref) {
445 if (stackType[--stackIdx] != V_STRING)
446 Com_Error(ERR_DROP, "Bad type '%s' for pic (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
447 p->pic = CL_ParticleGetArt((char*) stackPtr[stackIdx], p->frame, ART_PIC);
448 e = (byte*) stackPtr[stackIdx] - cmdStack;
449 break;
450 }
451 if (offsetof(ptl_t, model) == -cmd->ref) {
452 if (stackType[--stackIdx] != V_STRING)
453 Com_Error(ERR_DROP, "Bad type '%s' for model (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
454 p->model = CL_ParticleGetArt((char*) stackPtr[stackIdx], p->frame, ART_MODEL);
455 e = (byte*) stackPtr[stackIdx] - cmdStack;
456 break;
457 }
458 if (offsetof(ptl_t, program) == -cmd->ref) {
459 if (stackType[--stackIdx] != V_STRING)
460 Com_Error(ERR_DROP, "Bad type '%s' for program (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
462 if (p->program)
463 p->program->userdata = p;
464 e = (byte*) stackPtr[stackIdx] - cmdStack;
465 break;
466 }
467
468 /* get different data */
469 if (cmd->cmd == PC_POP)
470 e -= Com_SetValue(cmdData, stackPtr[--stackIdx], (valueTypes_t)cmd->type, 0, 0);
471 else
472 Com_SetValue(cmdData, stackPtr[stackIdx - 1], (valueTypes_t)cmd->type, 0, 0);
473 break;
474
475 case PC_ADD:
476 case PC_SUB:
477 /* check for stack underflow */
478 if (stackIdx == 0)
479 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
480
481 type = stackType[stackIdx - 1];
482 if (!((1 << type) & V_VECS))
483 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for add (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
484
485 /* float based vector addition */
486 if (type != cmd->type)
487 Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for add/sub (particle %s)", p->ctrl->name);
488
489 n = type - V_FLOAT + 1;
490
491 for (i = 0; i < n; i++) {
492 if (cmd->cmd == PC_SUB)
493 arg = -(*((float*) cmdData + i));
494 else
495 arg = *((float*) cmdData + i);
496 *((float*) stackPtr[stackIdx - 1] + i) += arg;
497 }
498 break;
499
500 case PC_MUL:
501 case PC_DIV:
502 /* check for stack underflow */
503 if (stackIdx == 0)
504 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
505
506 type = stackType[stackIdx - 1];
507 if (!((1 << type) & V_VECS))
508 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for add (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
509
510 n = type - V_FLOAT + 1;
511
512 if (type > V_FLOAT && cmd->type > V_FLOAT) {
513 /* component wise multiplication */
514 if (type != cmd->type)
515 Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for mul/div (particle %s)", p->ctrl->name);
516
517 for (i = 0; i < n; i++) {
518 if (cmd->cmd == PC_DIV)
519 arg = 1.0 / (*((float*) cmdData + i));
520 else
521 arg = *((float*) cmdData + i);
522 *((float*) stackPtr[stackIdx - 1] + i) *= arg;
523 }
524 break;
525 }
526
527 if (cmd->type > V_FLOAT)
528 Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for mul/div (particle %s)", p->ctrl->name);
529
530 /* scalar multiplication with scalar in second argument */
531 if (cmd->cmd == PC_DIV)
532 arg = 1.0 / (*(float*) cmdData);
533 else
534 arg = *(float*) cmdData;
535 for (i = 0; i < n; i++)
536 *((float*) stackPtr[stackIdx - 1] + i) *= arg;
537
538 break;
539
540 case PC_SIN:
541 if (cmd->type != V_FLOAT)
542 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for sin (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
543 stackPtr[stackIdx] = &cmdStack[e];
544 stackType[stackIdx] = cmd->type;
545 *(float*) stackPtr[stackIdx++] = sin(*(float*) cmdData * (2 * M_PI));
546 e += sizeof(float);
547 break;
548
549 case PC_COS:
550 if (cmd->type != V_FLOAT)
551 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for cos (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
552 stackPtr[stackIdx] = &cmdStack[e];
553 stackType[stackIdx] = cmd->type;
554 *(float*) stackPtr[stackIdx++] = sin(*(float*) cmdData * (2 * M_PI));
555 e += sizeof(float);
556 break;
557
558 case PC_TAN:
559 if (cmd->type != V_FLOAT)
560 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for tan (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
561 stackPtr[stackIdx] = &cmdStack[e];
562 stackType[stackIdx] = cmd->type;
563 *(float*) stackPtr[stackIdx++] = sin(*(float*) cmdData * (2 * M_PI));
564 e += sizeof(float);
565 break;
566
567 case PC_RAND:
568 case PC_CRAND:
569 stackPtr[stackIdx] = &cmdStack[e];
570 stackType[stackIdx] = cmd->type;
571
572 n = cmd->type - V_FLOAT + 1;
573
574 if (cmd->cmd == PC_RAND)
575 for (i = 0; i < n; i++)
576 *((float*) stackPtr[stackIdx] + i) = *((float*) cmdData + i) * frand();
577 else
578 for (i = 0; i < n; i++)
579 *((float*) stackPtr[stackIdx] + i) = *((float*) cmdData + i) * crand();
580
581 e += n * sizeof(float);
582 stackIdx++;
583 break;
584
585 case PC_V2:
586 case PC_V3:
587 case PC_V4:
588 n = cmd->cmd - PC_V2 + 2;
589 j = 0;
590
591 if (stackIdx < n)
592 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
593
594 for (i = 0; i < n; i++) {
595 if (!((1 << stackType[--stackIdx]) & V_VECS))
596 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for vector creation (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name);
597 j += stackType[stackIdx] - V_FLOAT + 1;
598 }
599
600 if (j > 4)
601 Com_Error(ERR_DROP, "CL_ParticleFunction: created vector with dim > 4 (particle %s)", p->ctrl->name);
602
603 stackType[stackIdx++] = V_FLOAT + j - 1;
604 break;
605
606 case PC_KILL:
608 return;
609
610 case PC_SPAWN:
611 pnew = CL_ParticleSpawn((const char*) cmdData, p->levelFlags, p->s, p->v, p->a);
612 if (!pnew)
613 Com_DPrintf(DEBUG_CLIENT, "PC_SPAWN: Could not spawn child particle for '%s' (%s)\n", p->ctrl->name, (const char*) cmdData);
614 break;
615
616 case PC_TNSPAWN:
617 /* check for stack underflow */
618 if (stackIdx < 2)
619 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
620
621 /* pop elements off the stack */
622 /* amount of timed particles */
623 type = stackType[--stackIdx];
624 if (type != V_INT)
625 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for tnspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name);
626 n = *(int*) stackPtr[stackIdx];
627
628 /* delta time */
629 type = stackType[--stackIdx];
630 if (type != V_INT)
631 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for tnspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name);
632 i = *(int*) stackPtr[stackIdx];
633
635 CL_ParticleSpawnTimed((const char*) cmdData, p, true, i, n);
636
637 e -= 2 * sizeof(int);
638
639 break;
640
641 case PC_NSPAWN:
642 /* check for stack underflow */
643 if (stackIdx == 0)
644 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
645
646 type = stackType[--stackIdx];
647 if (type != V_INT)
648 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for nspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name);
649
650 n = *(int*) stackPtr[stackIdx];
651 e -= sizeof(int);
652
653 for (i = 0; i < n; i++) {
654 pnew = CL_ParticleSpawn((const char*) cmdData, p->levelFlags, p->s, p->v, p->a);
655 if (!pnew)
656 Com_DPrintf(DEBUG_CLIENT, "PC_NSPAWN: Could not spawn child particle for '%s'\n", p->ctrl->name);
657 }
658 break;
659
660 case PC_CHILD:
661 pnew = CL_ParticleSpawn((const char*)cmdData, p->levelFlags, p->s, p->v, p->a);
662 if (pnew) {
663 pnew->next = p->children;
664 pnew->parent = p;
665 p->children = pnew;
666 } else {
667 Com_DPrintf(DEBUG_CLIENT, "PC_CHILD: Could not spawn child particle for '%s'\n", p->ctrl->name);
668 }
669 break;
670
671 default:
672 Com_Error(ERR_DROP, "CL_ParticleFunction: unknown cmd type %i", cmd->type);
673 break;
674 }
675 }
676}
677
679{
680 if (Q_strnull(name))
681 return nullptr;
682
683 /* find the particle definition */
684 for (int i = 0; i < numPtlDefs; i++) {
685 ptlDef_t* pd = &ptlDef[i];
686 if (Q_streq(name, pd->name)) {
687 return pd;
688 }
689 }
690
691 return nullptr;
692}
693
705ptl_t* CL_ParticleSpawn (const char* name, int levelFlags, const vec3_t s, const vec3_t v, const vec3_t a)
706{
707 if (Q_strnull(name))
708 return nullptr;
709
710 /* find the particle definition */
712 if (pd == nullptr) {
713 Com_Printf("Particle definition \"%s\" not found\n", name);
714 return nullptr;
715 }
716
717 /* add the particle */
718 int i;
719 for (i = 0; i < r_numParticles; i++)
720 if (!r_particleArray[i].inuse)
721 break;
722
723 if (i == r_numParticles) {
726 else {
727 Com_DPrintf(DEBUG_CLIENT, "Too many particles (don't add %s) - exceeded %i\n", name, MAX_PTLS);
728 return nullptr;
729 }
730 }
731
732 /* allocate particle */
733 ptl_t* p = &r_particleArray[i];
734 OBJZERO(*p);
735
736 /* set basic values */
737 p->inuse = true;
738 p->startTime = cl.time;
739 p->ctrl = pd;
740 Vector4Set(p->color, 1.0f, 1.0f, 1.0f, 1.0f);
741
742 p->pic = nullptr;
743 p->model = nullptr;
744
745 /* copy location */
746 if (s) {
747 VectorCopy(s, p->origin);
748 VectorCopy(s, p->s);
749 }
750 /* copy velocity */
751 if (v)
752 VectorCopy(v, p->v);
753 /* copy acceleration */
754 if (a)
755 VectorCopy(a, p->a);
756
757 /* copy levelflags */
758 p->levelFlags = levelFlags;
759
760 /* run init function */
762 if (p->inuse && !p->tps && !p->life) {
763 Com_DPrintf(DEBUG_CLIENT, "Particle %s does not have a tps nor a life set - this is only valid for projectile particles\n",
764 name);
765 p->tps = 1;
766 }
767
768 return p;
769}
770
777{
778 p->inuse = false;
779 p->invis = true;
780 for (ptl_t* c = p->children; c; c = c->next) {
782 }
783}
784
792static void CL_Fading (vec4_t color, fade_t fade, float frac, bool onlyAlpha)
793{
794 switch (fade) {
795 case FADE_IN:
796 for (int i = onlyAlpha ? 3 : 0; i < 4; i++)
797 color[i] *= frac;
798 break;
799 case FADE_OUT:
800 for (int i = onlyAlpha ? 3 : 0; i < 4; i++)
801 color[i] *= (1.0 - frac);
802 break;
803 case FADE_SIN:
804 for (int i = onlyAlpha ? 3 : 0; i < 4; i++)
805 color[i] *= sin(frac * M_PI);
806 break;
807 case FADE_SAW:
808 if (frac < 0.5)
809 for (int i = onlyAlpha ? 3 : 0; i < 4; i++)
810 color[i] *= frac * 2;
811 else
812 for (int i = onlyAlpha ? 3 : 0; i < 4; i++)
813 color[i] *= (1.0 - frac) * 2;
814 break;
815 case FADE_NONE:
816 break;
817 case FADE_LAST:
818 break;
819 }
820}
821
828{
829 for (int i = 0; i < r_numParticles; i++) {
830 ptl_t* p = &r_particleArray[i];
831 if (!p->inuse)
832 continue;
833 /* run round function */
835
836 if (!p->rounds)
837 continue;
838
839 p->roundsCnt--;
840 if (p->roundsCnt <= 0)
842 }
843}
844
845typedef struct ptlTraceCache_s {
846 int count;
852
856static trace_t PTL_Trace (ptl_t* ptl, const AABB& aabb)
857{
858 static ptlTraceCache_t ptlCache;
859 const float epsilonPos = 3.0f;
860 const float epsilonBBox = 1.0f;
861
862 if (VectorCompareEps(ptlCache.start, ptl->origin, epsilonPos) && VectorCompareEps(ptlCache.end, ptl->s, epsilonPos)
863 && VectorCompareEps(ptlCache.pBox.mins, aabb.mins, epsilonBBox) && VectorCompareEps(ptlCache.pBox.maxs, aabb.maxs, epsilonBBox)) {
864 ptlCache.count++;
865 return ptlCache.trace;
866 }
867
868 VectorCopy(ptl->origin, ptlCache.start);
869 VectorCopy(ptl->s, ptlCache.end);
870 ptlCache.pBox.set(aabb);
871
872 ptlCache.trace = CL_Trace(Line(ptl->origin, ptl->s), aabb, nullptr, nullptr, MASK_SOLID, cl.mapMaxLevel - 1);
873 return ptlCache.trace;
874}
875
882static void CL_ParticleRun2 (ptl_t* p)
883{
884 /* advance time */
885 p->dt = cls.frametime;
886 p->t = (cl.time - p->startTime) * 0.001f;
887 p->lastThink += p->dt;
888 p->lastFrame += p->dt;
889
890 if (p->rounds && !p->roundsCnt)
891 p->roundsCnt = p->rounds;
892
893 /* test for end of life */
894 if (p->life && p->t >= p->life && !p->parent) {
896 return;
897 /* don't play the weather particles if a user don't want them there can
898 * be a lot of weather particles - which might slow the computer down */
899 } else if (p->weather && !cl_particleweather->integer) {
901 return;
902 }
903
904 /* kinematics */
905 if (p->style != STYLE_LINE) {
906 VectorMA(p->s, 0.5 * p->dt * p->dt, p->a, p->s);
907 VectorMA(p->s, p->dt, p->v, p->s);
908 VectorMA(p->v, p->dt, p->a, p->v);
909 VectorMA(p->angles, p->dt, p->omega, p->angles);
910 }
911
912 /* basic 'physics' for particles */
913 if (p->physics) {
914 const float size = std::max(p->size[0], p->size[1]);
915
916 if (p->hitSolid && p->bounce) {
917 VectorCopy(p->oldV, p->v);
918 VectorNegate(p->a, p->a);
919 p->hitSolid = false;
920 }
921
922 /* if the particle hit a solid already and is sticking to the surface, no further
923 * traces are needed */
924 if (p->hitSolid && p->stick)
925 return;
926
927 const AABB ptlbox(-size, -size, -size, size, size, size);
928 const trace_t& tr = PTL_Trace(p, ptlbox);
929
930 /* hit something solid */
931 if (tr.fraction < 1.0 || tr.startsolid) {
932 p->hitSolid = true;
933
934 /* now execute the physics handler */
935 if (p->ctrl->physics)
937 /* let them stay on the ground until they fade out or die */
938 if (!p->stayalive) {
940 return;
941 } else if (p->bounce) {
942 /* bounce */
943 vec3_t temp;
944 VectorCopy(p->v, p->oldV);
945 VectorScale(tr.plane.normal, -DotProduct(tr.plane.normal, p->v), temp);
946 VectorAdd(temp, p->v, temp);
947 VectorAdd(temp, temp, p->v);
948 VectorNegate(p->a, p->a);
949 } else {
950 VectorClear(p->v);
951 }
952 VectorCopy(tr.endpos, p->s);
953 }
954 }
955
956 /* run */
958
959 /* think */
960 while (p->tps && p->lastThink * p->tps >= 1) {
962 p->lastThink -= 1.0 / p->tps;
963 }
964
965 /* animate */
966 while (p->fps && p->lastFrame * p->fps >= 1) {
967 /* advance frame */
968 p->frame++;
969 if (p->frame > p->endFrame)
970 p->frame = 0;
971 p->lastFrame -= 1.0 / p->fps;
972
973 /* load next frame */
974 assert(p->pic);
975 p->pic = CL_ParticleGetArt(p->pic->name, p->frame, ART_PIC);
976 }
977
978 /* fading */
979 if (p->thinkFade || p->frameFade) {
980 const bool onlyAlpha = (p->blend == BLEND_BLEND);
981 if (!onlyAlpha)
982 Vector4Set(p->color, 1.0f, 1.0f, 1.0f, 1.0f);
983 else
984 p->color[3] = 1.0;
985
986 if (p->thinkFade)
987 CL_Fading(p->color, p->thinkFade, p->lastThink * p->tps, onlyAlpha);
988 if (p->frameFade)
989 CL_Fading(p->color, p->frameFade, p->lastFrame * p->fps, onlyAlpha);
990 }
991
992 /* this is useful for particles like weather effects that are on top of
993 * some other brushes in higher level but should be visible in lower ones */
994 if (p->autohide) {
995 const int z = (int)p->s[2] / UNIT_HEIGHT;
996 if (z > cl_worldlevel->integer) {
997 p->invis = true;
998 return;
999 } else if (z < 0) {
1000 CL_ParticleFree(p);
1001 return;
1002 }
1003 }
1004
1005 /* add light to the scene */
1006 if (VectorNotEmpty(p->lightColor)) {
1007 const float intensity = 0.5 + p->lightIntensity;
1008 if (p->lightSustain)
1010 else
1011 R_AddLight(p->s, intensity * PTL_INTENSITY_TO_RADIUS, p->lightColor);
1012 }
1013
1014 /* set the new origin */
1015 VectorCopy(p->s, p->origin);
1016
1017 p->invis = false;
1018}
1019
1023static void CL_ParticleRunTimed (void)
1024{
1025 const size_t length = lengthof(timedParticles);
1026
1027 for (int i = 0; i < length; i++) {
1029 if (!tp->parent || !tp->parent->inuse)
1030 continue;
1031 if (tp->n >= tp->max)
1032 continue;
1033 if (CL_Milliseconds() - tp->lastTime < tp->dt)
1034 continue;
1035
1036 if (!tp->n) {
1037 /* first spawn? - then copy the parent values. We have to
1038 * do this here and now earlier because projectile particles
1039 * get these values set after spawn. */
1040 VectorCopy(tp->parent->s, tp->s);
1041 VectorCopy(tp->parent->v, tp->v);
1042 VectorCopy(tp->parent->a, tp->a);
1043 }
1044 tp->n++;
1045 tp->lastTime = CL_Milliseconds();
1046 ptl_t* p = CL_ParticleSpawn(tp->ptl, tp->levelFlags, tp->s, tp->v, tp->a);
1047 if (p && tp->children) {
1048 p->next = tp->parent->children;
1049 p->parent = tp->parent;
1050 tp->parent->children = p;
1051 }
1052 }
1053}
1054
1062static void CL_ParseMapParticle (ptl_t* ptl, const char* es, bool afterwards)
1063{
1064 const char* token;
1065
1066 do {
1067 /* get keyname */
1068 token = Com_Parse(&es);
1069 if (token[0] == '}')
1070 break;
1071 if (!es)
1072 Com_Error(ERR_DROP, "CL_ParseMapParticle: EOF without closing brace");
1073
1074 char keyname[MAX_VAR];
1075 Q_strncpyz(keyname, token, sizeof(keyname));
1076
1077 /* parse value */
1078 token = Com_Parse(&es);
1079 if (!es)
1080 Com_Error(ERR_DROP, "CL_ParseMapParticle: EOF without closing brace");
1081
1082 if (token[0] == '}')
1083 Com_Error(ERR_DROP, "CL_ParseMapParticle: closing brace without data");
1084
1085 if (!afterwards && keyname[0] != '-')
1086 continue;
1087 if (afterwards && keyname[0] != '+')
1088 continue;
1089
1090 char* key = keyname + 1;
1091 const value_t* pp;
1092 for (pp = pps; pp->string; pp++) {
1093 if (Q_streq(key, pp->string)) {
1094 /* found a normal particle value */
1095 Com_EParseValue(ptl, token, pp->type, pp->ofs, pp->size);
1096 break;
1097 }
1098 }
1099
1100 if (!pp->string) {
1101 /* register art */
1102 if (Q_streq(key, "image"))
1103 ptl->pic = CL_ParticleGetArt(token, ptl->frame, ART_PIC);
1104 else if (Q_streq(key, "model"))
1105 ptl->model = CL_ParticleGetArt(token, ptl->frame, ART_MODEL);
1106 else if (Q_streq(key, "program")) {
1108 if (ptl->program)
1109 ptl->program->userdata = ptl;
1110 }
1111 }
1112 } while (token);
1113}
1114
1115static void CL_RunMapParticles (void)
1116{
1117 for (int i = 0; i < cl.numMapParticles; i++) {
1119 if (!mp->nextTime)
1120 continue;
1121 if (cl.time < mp->nextTime)
1122 continue;
1123 /* spawn a new particle */
1124 ptl_t* ptl = CL_ParticleSpawn(mp->ptl, mp->levelflags, mp->origin);
1125 if (!ptl) {
1126 Com_Printf(S_COLOR_YELLOW "Could not spawn particle '%s'\n", mp->ptl);
1127 mp->nextTime = 0;
1128 continue;
1129 }
1130
1131 /* init the particle */
1132 CL_ParseMapParticle(ptl, mp->info, false);
1133 CL_ParticleFunction(ptl, ptl->ctrl->init);
1134 CL_ParseMapParticle(ptl, mp->info, true);
1135
1136 /* prepare next spawning */
1137 if (Vector2NotEmpty(mp->wait))
1138 mp->nextTime += mp->wait[0] + mp->wait[1] * frand();
1139 else
1140 mp->nextTime = 0;
1141 }
1142}
1143
1149{
1151
1153
1154 for (int i = 0; i < r_numParticles; i++) {
1155 ptl_t* p = &r_particleArray[i];
1156 if (p->inuse)
1157 CL_ParticleRun2(p);
1158 }
1159}
1160
1161static void CL_ParsePtlCmds (const char* name, const char** text)
1162{
1163 /* get it's body */
1164 const char* token = Com_Parse(text);
1165
1166 if (!*text || *token != '{') {
1167 Com_Printf("CL_ParsePtlCmds: particle cmds \"%s\" without body ignored\n", name);
1168 return;
1169 }
1170
1171 const char* errhead = "CL_ParsePtlCmds: unexpected end of file";
1172 ptlCmd_t* pc;
1173 do {
1174 token = Com_EParse(text, errhead, name);
1175 if (!*text)
1176 break;
1177 if (*token == '}')
1178 break;
1179
1180 const value_t* pp;
1181 int i;
1182 for (i = 0; i < PC_NUM_PTLCMDS; i++)
1183 if (Q_streq(token, pc_strings[i])) {
1184 /* allocate an new cmd */
1185 if (numPtlCmds >= MAX_PTLCMDS)
1186 Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
1187 pc = &ptlCmd[numPtlCmds++];
1188 OBJZERO(*pc);
1189
1190 pc->cmd = i;
1191
1192 if (!pc_types[i])
1193 break;
1194
1195 /* get parameter type */
1196 token = Com_EParse(text, errhead, name);
1197 if (!*text)
1198 return;
1199
1200 /* operate on the top element on the stack */
1201 if (token[0] == '#') {
1202 pc->ref = RSTACK;
1203 if (token[1] == '.')
1204 pc->ref -= (token[2] - '0');
1205 break;
1206 }
1207
1208 if (token[0] == '*') {
1209 char baseComponentToken[MAX_VAR];
1210
1211 /* it's a variable reference */
1212 token++;
1213
1214 /* we maybe have to modify it */
1215 Q_strncpyz(baseComponentToken, token, sizeof(baseComponentToken));
1216
1217 /* check for component specifier */
1218 int len = strlen(baseComponentToken);
1219 /* it's possible to change only the second value of e.g. a vector
1220 * just defined e.g. 'size.2' to modify the second value of size */
1221 if (len >= 2 && baseComponentToken[len - 2] == '.') {
1222 baseComponentToken[len - 2] = 0;
1223 } else
1224 len = 0;
1225
1226 for (pp = pps; pp->string; pp++)
1227 if (Q_streq(baseComponentToken, pp->string))
1228 break;
1229
1230 if (!pp->string) {
1231 Com_Printf("CL_ParsePtlCmds: bad reference \"%s\" specified (particle %s)\n", token, name);
1232 numPtlCmds--;
1233 break;
1234 }
1235
1236 if ((pc_types[i] & PTL_ONLY_ONE_TYPE)) {
1237 if ((pc_types[i] & ~PTL_ONLY_ONE_TYPE) != pp->type) {
1238 Com_Printf("CL_ParsePtlCmds: bad type in var \"%s\" (PTL_ONLY_ONE_TYPE) specified (particle %s) (ptl type: %i (pc_type: %i), string: %s)\n", token, name, pp->type, pc_types[i], pc_strings[i]);
1239 numPtlCmds--;
1240 break;
1241 }
1242 } else if (pp->type >= V_NUM_TYPES || !((1 << pp->type) & pc_types[i])) {
1243 Com_Printf("CL_ParsePtlCmds: bad type in var \"%s\" specified (particle %s) (ptl type: %i (pc_type: %i), string: %s)\n", token, name, pp->type, pc_types[i], pc_strings[i]);
1244 numPtlCmds--;
1245 break;
1246 }
1247
1248 if (len) {
1249 /* get single component */
1250 if ((1 << pp->type) & V_VECS) {
1251 const int component = (baseComponentToken[len - 1] - '1');
1252 /* get the component we want to modify */
1253 if (component > 3) {
1254 Com_Printf("CL_ParsePtlCmds: bad component value - it's bigger than 3: %i (particle %s)\n", component, name);
1255 numPtlCmds--;
1256 break;
1257 }
1258 pc->type = V_FLOAT;
1259 /* go to component offset */
1260 pc->ref = -((int)pp->ofs) - component * sizeof(float);
1261 break;
1262 } else {
1263 Com_Printf("CL_ParsePtlCmds: can't get components of a non-vector type (particle %s)\n", name);
1264 numPtlCmds--;
1265 break;
1266 }
1267 }
1268
1269 /* set the values */
1270 pc->type = pp->type;
1271 pc->ref = -((int)pp->ofs);
1272 break;
1273 }
1274
1275 /* get the type */
1276 int j;
1278 /* extract the real type */
1280 else {
1281 for (j = 0; j < V_NUM_TYPES; j++)
1282 if (Q_streq(token, vt_names[j]))
1283 break;
1284
1285 if (j >= V_NUM_TYPES || !((1 << j) & pc_types[i])) {
1286 Com_Printf("CL_ParsePtlCmds: bad type \"%s\" specified (particle %s)\n", token, name);
1287 numPtlCmds--;
1288 break;
1289 }
1290
1291 /* get the value */
1292 token = Com_EParse(text, errhead, name);
1293 if (!*text)
1294 return;
1295 }
1296
1297 /* set the values */
1298 pc->type = j;
1299
1300 pcmdPos = (byte*) Com_AlignPtr(pcmdPos, (valueTypes_t)pc->type);
1301 pc->ref = (int) (pcmdPos - pcmdData);
1302 pcmdPos += Com_EParseValue(pcmdPos, token, (valueTypes_t)pc->type, 0, 0);
1303
1304/* Com_Printf("%s %s %i\n", vt_names[pc->type], token, pcmdPos - pc->ref, (char*)pc->ref); */
1305 break;
1306 }
1307
1308 if (i < PC_NUM_PTLCMDS)
1309 continue;
1310
1311 for (pp = pps; pp->string; pp++)
1312 if (Q_streq(token, pp->string)) {
1313 /* get parameter */
1314 token = Com_EParse(text, errhead, name);
1315 if (!*text)
1316 return;
1317
1318 /* translate set to a push and pop */
1319 if (numPtlCmds >= MAX_PTLCMDS - 1)
1320 Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
1321 pc = &ptlCmd[numPtlCmds++];
1322 pc->cmd = PC_PUSH;
1323 pc->type = pp->type;
1324
1325 pcmdPos = (byte*) Com_AlignPtr(pcmdPos, (valueTypes_t)pc->type);
1326 pc->ref = (int) (pcmdPos - pcmdData);
1327 pcmdPos += Com_EParseValue(pcmdPos, token, (valueTypes_t)pc->type, 0, 0);
1328
1329 pc = &ptlCmd[numPtlCmds++];
1330 pc->cmd = PC_POP;
1331 pc->type = pp->type;
1332 pc->ref = -((int)pp->ofs);
1333 break;
1334 }
1335
1336 if (!pp->string)
1337 Com_Printf("CL_ParsePtlCmds: unknown token \"%s\" ignored (particle %s)\n", token, name);
1338
1339 } while (*text);
1340
1341 /* terminate cmd chain */
1342 if (numPtlCmds >= MAX_PTLCMDS)
1343 Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
1344 pc = &ptlCmd[numPtlCmds++];
1345 OBJZERO(*pc);
1346}
1347
1355void CL_ParseParticle (const char* name, const char** text)
1356{
1357 int i;
1358
1359 /* search for particles with same name */
1360 for (i = 0; i < numPtlDefs; i++)
1361 if (Q_streq(name, ptlDef[i].name))
1362 break;
1363
1364 ptlDef_t* pd;
1365 if (i < numPtlDefs) {
1366 Com_Printf("CL_ParseParticle: particle def \"%s\" with same name found, reset first one\n", name);
1367 pd = &ptlDef[i];
1368 } else {
1369 if (numPtlDefs < MAX_PTLDEFS - 1) {
1370 /* initialize the new particle */
1371 pd = &ptlDef[numPtlDefs++];
1372 } else {
1373 Com_Printf("CL_ParseParticle: max particle definitions reached - skip the current one: '%s'\n", name);
1374 return;
1375 }
1376 }
1377 OBJZERO(*pd);
1378
1379 Q_strncpyz(pd->name, name, sizeof(pd->name));
1380
1381 /* get it's body */
1382 const char* token = Com_Parse(text);
1383
1384 if (!*text || *token != '{') {
1385 Com_Printf("CL_ParseParticle: particle def \"%s\" without body ignored\n", name);
1386 if (i == numPtlDefs)
1387 numPtlDefs--;
1388 return;
1389 }
1390
1391 const char* errhead = "CL_ParseParticle: unexpected end of file (particle ";
1392 do {
1393 token = Com_EParse(text, errhead, name);
1394 if (!*text)
1395 break;
1396 if (*token == '}')
1397 break;
1398
1399 for (i = 0; i < PF_NUM_PTLFUNCS; i++)
1400 if (Q_streq(token, pf_strings[i])) {
1401 /* allocate the first particle command */
1402 ptlCmd_t** pc;
1403
1404 pc = (ptlCmd_t**) ((byte*) pd + pf_values[i]);
1405 *pc = &ptlCmd[numPtlCmds];
1406
1407 /* parse the commands */
1408 CL_ParsePtlCmds(name, text);
1409 break;
1410 }
1411
1412 if (i == PF_NUM_PTLFUNCS)
1413 Com_Printf("CL_ParseParticle: unknown token \"%s\" ignored (particle %s)\n", token, name);
1414
1415 } while (*text);
1416
1417 /* check for an init function */
1418 if (!pd->init) {
1419 Com_Printf("CL_ParseParticle: particle definition %s without init function ignored\n", name);
1420 if (i == numPtlDefs)
1421 numPtlDefs--;
1422 }
1423}
1424
1425#ifdef DEBUG
1429static void PTL_DebugSpawnMarker_f (void)
1430{
1431 if (Cmd_Argc() < 4) {
1432 Com_Printf("Usage: %s <x> <y> <z>\n", Cmd_Argv(0));
1433 return;
1434 }
1435
1436 vec3_t worldOrigin;
1437 worldOrigin[0] = atof(Cmd_Argv(1));
1438 worldOrigin[1] = atof(Cmd_Argv(2));
1439 worldOrigin[2] = atof(Cmd_Argv(3));
1440
1441 CL_ParticleSpawn("debug_marker", 0, worldOrigin);
1442}
1443
1444static void PTL_DebugList_f (void)
1445{
1446 Com_Printf("%i particles\n", r_numParticles);
1447 for (int i = 0; i < r_numParticles; i++) {
1448 const ptl_t* p = &r_particleArray[i];
1449 const ptlDef_t* def = p->ctrl;
1450 if (!p->inuse)
1451 continue;
1452 Com_Printf("particle %i\n", i);
1453 Com_Printf(" name: %s\n", def->name);
1454 for (const value_t* pp = pps; pp->string; pp++) {
1455 const char* value = "";
1456 if (Q_streq(pp->string, "image") && p->pic) {
1457 value = p->pic->name;
1458 } else if (Q_streq(pp->string, "model") && p->model) {
1459 value = p->model->name;
1460 } else if (Q_streq(pp->string, "program") && p->program) {
1461 value = p->program->name;
1462 } else {
1463 value = Com_ValueToStr(p, pp->type, pp->ofs);
1464 }
1465 Com_Printf(" %s: %s\n", pp->string, value);
1466 }
1467 }
1468}
1469#endif
1470
1476{
1477 r_numParticles = 0;
1478 numPtlCmds = 0;
1479 numPtlDefs = 0;
1480
1482
1483 OBJZERO(ptlDef);
1484 OBJZERO(ptlCmd);
1486
1487 cl_particleweather = Cvar_Get("cl_particleweather", "1", CVAR_ARCHIVE, "Switch the weather particles on or off");
1488#ifdef DEBUG
1489 Cmd_AddCommand("debug_spawnmarker", PTL_DebugSpawnMarker_f, "Spawn a marker particle in the world at a given location");
1490 Cmd_AddCommand("debug_particlelist", PTL_DebugList_f);
1491#endif
1492}
clientBattleScape_t cl
cvar_t * cl_worldlevel
Definition cl_hud.cpp:46
HUD related routines.
unsigned int key
Definition cl_input.cpp:64
static SDL_Joystick * stick
trace_t CL_Trace(const Line &traceLine, const AABB &box, const le_t *passle, le_t *passle2, int contentmask, int worldLevel)
Moves the given mins/maxs volume through the world from start to end.
client_static_t cls
Definition cl_main.cpp:83
int CL_Milliseconds(void)
Definition cl_main.cpp:1207
void PTL_InitStartup(void)
Clears particle data.
ptlDef_t * CL_ParticleGet(const char *name)
static const unsigned int pc_types[PC_NUM_PTLCMDS]
particle commands parameter and types
static char const *const pf_strings[]
valid particle functions - see pf_t and pf_values
pc_t
particle commands - see pc_strings
@ PC_RAND
@ PC_CRAND
@ PC_KPOP
@ PC_NSPAWN
@ PC_V2
@ PC_TNSPAWN
@ PC_COS
@ PC_CHILD
@ PC_NUM_PTLCMDS
@ PC_END
@ PC_DIV
@ PC_MUL
@ PC_ADD
@ PC_POP
@ PC_SIN
@ PC_V4
@ PC_KILL
@ PC_SUB
@ PC_V3
@ PC_PUSH
@ PC_SPAWN
@ PC_TAN
static void * stackPtr[MAX_STACK_DEPTH]
static ptlArt_t r_particlesArt[MAX_PTL_ART]
#define MAX_MAPPARTICLES
void CL_ParticleFree(ptl_t *p)
Free a particle and all it's children.
static ptlDef_t ptlDef[MAX_PTLDEFS]
#define MAX_PTLCMDS
static byte pcmdData[MAX_PCMD_DATA]
void CL_ParticleRegisterArt(void)
static void CL_ParticleSpawnTimed(const char *name, ptl_t *parent, bool children, int deltaTime, int n)
Will spawn a n particles deltaTime ms after the parent was spawned.
void CL_ParseParticle(const char *name, const char **text)
Parses particle definitions from UFO-script files.
static void CL_ParticleRunTimed(void)
Called every frame and checks whether a timed particle should be spawned.
static void * CL_ParticleCommandGetDataLocation(ptl_t *p, const ptlCmd_t *cmd)
Determine the memory location where the command accesses and stores its data.
#define MAX_STACK_DATA
#define PTL_INTENSITY_TO_RADIUS
static const value_t pps[]
particle script values
static int numPtlCmds
#define MAX_TIMEDPARTICLES
static byte cmdStack[MAX_STACK_DATA]
#define MAX_STACK_DEPTH
pf_t
particle functions enums - see pf_strings and pf_values
@ PF_NUM_PTLFUNCS
@ PF_ROUND
@ PF_RUN
@ PF_PHYSICS
@ PF_THINK
@ PF_INIT
static void CL_ParseMapParticle(ptl_t *ptl, const char *es, bool afterwards)
Parses particle used on maps.
#define PTL_ONLY_ONE_TYPE
static mapParticle_t mapParticles[MAX_MAPPARTICLES]
static void CL_RunMapParticles(void)
#define V_VECS
ptl_t * CL_ParticleSpawn(const char *name, int levelFlags, const vec3_t s, const vec3_t v, const vec3_t a)
Spawn a new particle to the map.
static int numPtlDefs
static timedParticle_t timedParticles[MAX_TIMEDPARTICLES]
void CL_ParticleRun(void)
General system for particle running during the game.
static void CL_ParticleRun2(ptl_t *p)
Prepares the particle rendering, calculate new position, velocity and all the other particle values t...
#define V_UNTYPED
void CL_AddMapParticle(const char *ptl, const vec3_t origin, const vec2_t wait, const char *info, int levelflags)
Spawns the map particle.
static trace_t PTL_Trace(ptl_t *ptl, const AABB &aabb)
Particle tracing with caching.
static cvar_t * cl_particleweather
static void CL_ParticleLoadArt(ptlArt_t *a)
Loads the image or model for a given particle art.
static void CL_ParticleFunction(ptl_t *p, ptlCmd_t *cmd)
static char const *const pc_strings[]
particle commands - see pc_t
static void CL_ParsePtlCmds(const char *name, const char **text)
#define MAX_PCMD_DATA
static const size_t pf_values[]
particle functions offsets - see pf_strings and pf_t
static const int RSTACK
static ptlCmd_t ptlCmd[MAX_PTLCMDS]
#define MAX_PTLDEFS
static int r_numParticlesArt
static byte stackType[MAX_STACK_DEPTH]
void CL_ParticleCheckRounds(void)
checks whether a particle is still active in the current round
static void CL_Fading(vec4_t color, fade_t fade, float frac, bool onlyAlpha)
Color fade function.
static byte * pcmdPos
static ptlArt_t * CL_ParticleGetArt(const char *name, int frame, artType_t type)
Register art (pics, models) for each particle.
#define MAX_PTLS
Definition cl_renderer.h:44
#define MAX_PTL_ART
Definition cl_renderer.h:43
artType_t
particle art type
Definition cl_renderer.h:91
@ ART_MODEL
Definition cl_renderer.h:93
@ ART_PIC
Definition cl_renderer.h:92
Definition aabb.h:42
vec3_t maxs
Definition aabb.h:258
vec3_t mins
Definition aabb.h:257
void set(const AABB &other)
Copies the values from the given aabb.
Definition aabb.h:60
Definition line.h:31
Primary header for client.
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition cmd.cpp:516
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
Definition cmd.cpp:505
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition cmd.cpp:744
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition common.cpp:440
void Com_Error(int code, const char *fmt,...)
Definition common.cpp:459
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define ERR_DROP
Definition common.h:211
#define S_COLOR_YELLOW
Definition common.h:220
static transfer_t tr
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition cvar.cpp:342
#define CVAR_ARCHIVE
Definition cvar.h:40
#define DEBUG_CLIENT
Definition defines.h:59
#define UNIT_HEIGHT
Definition defines.h:122
#define MASK_SOLID
Definition defines.h:272
voidpf void uLong size
Definition ioapi.h:42
voidpf uLong int origin
Definition ioapi.h:45
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
voidpf uLong offset
Definition ioapi.h:45
void VectorMA(const vec3_t veca, const float scale, const vec3_t vecb, vec3_t outVector)
Sets vector_out (vc) to vevtor1 (va) + scale * vector2 (vb).
Definition mathlib.cpp:261
float crand(void)
Return random values between -1 and 1.
Definition mathlib.cpp:517
int VectorCompareEps(const vec3_t v1, const vec3_t v2, float epsilon)
Compare two vectors that may have an epsilon difference but still be the same vectors.
Definition mathlib.cpp:413
float frand(void)
Return random values between 0 and 1.
Definition mathlib.cpp:506
#define M_PI
Definition mathlib.h:34
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition parse.cpp:107
Shared parsing functions.
QGL_EXTERN GLuint GLchar GLuint * len
Definition r_gl.h:99
QGL_EXTERN int GLboolean GLfloat * v
Definition r_gl.h:120
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition r_gl.h:110
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
const image_t * R_FindPics(const char *name)
Searches for an image in the image array.
Definition r_image.cpp:673
void R_AddSustainedLight(const vec3_t org, float radius, const vec3_t color, float sustain)
Definition r_light.cpp:58
void R_AddLight(const vec3_t origin, float radius, const vec3_t color)
Create light to be rendered in the current frame (will be removed before the next).
Definition r_light.cpp:36
model_t * R_FindModel(const char *name)
Tries to load a model.
Definition r_model.cpp:203
int r_numParticles
ptl_t r_particleArray[MAX_PTLS]
Particle system header file.
void R_InitParticleProgram(r_program_t *prog)
void R_UseParticleProgram(r_program_t *prog)
r_program_t * R_LoadProgram(const char *name, programInitFunc_t init, programUseFunc_t use)
const char *const vt_names[]
possible values for parsing functions
Definition scripts.cpp:310
int Com_SetValue(void *base, const void *set, valueTypes_t type, int ofs, size_t size)
Definition scripts.cpp:1009
const char * Com_EParse(const char **text, const char *errhead, const char *errinfo, char *target, size_t size)
Parsing function that prints an error message when there is no text in the buffer.
Definition scripts.cpp:277
const char * Com_ValueToStr(const void *base, const valueTypes_t type, const int ofs)
Definition scripts.cpp:1171
void * Com_AlignPtr(const void *memory, valueTypes_t type)
Align a memory to use a natural address for the data type we will write.
Definition scripts.cpp:437
int Com_EParseValue(void *base, const char *token, valueTypes_t type, int ofs, size_t size)
Definition scripts.cpp:964
@ STYLE_LINE
Definition scripts.h:128
valueTypes_t
possible values for parsing functions
Definition scripts.h:48
@ V_BOOL
Definition scripts.h:50
@ V_FLOAT
Definition scripts.h:54
@ V_STYLE
Definition scripts.h:63
@ V_BLEND
Definition scripts.h:62
@ V_NULL
Definition scripts.h:49
@ V_STRING
Definition scripts.h:58
@ V_FADE
Definition scripts.h:64
@ V_INT
Definition scripts.h:52
@ V_VECTOR
Definition scripts.h:56
@ V_COLOR
Definition scripts.h:57
@ V_NUM_TYPES
Definition scripts.h:80
@ V_POS
Definition scripts.h:55
#define MEMBER_SIZEOF(TYPE, MEMBER)
Definition scripts.h:34
@ BLEND_BLEND
Definition scripts.h:116
fade_t
Definition scripts.h:135
@ FADE_IN
Definition scripts.h:137
@ FADE_SAW
Definition scripts.h:140
@ FADE_SIN
Definition scripts.h:139
@ FADE_OUT
Definition scripts.h:138
@ FADE_LAST
Definition scripts.h:142
@ FADE_NONE
Definition scripts.h:136
#define Q_streq(a, b)
Definition shared.h:136
bool Q_strnull(const char *string)
Definition shared.h:138
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
#define lengthof(x)
Definition shared.h:105
#define CASSERT(x)
Definition shared.h:107
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
map particles
char ptl[MAX_VAR]
const char * info
bool hitSolid
vec4_t color
bool physics
float fps
struct ptl_s * parent
float lightIntensity
ptlArt_t * model
vec2_t size
float t
bool stick
ptlArt_t * pic
vec3_t lightColor
vec3_t angles
vec3_t oldV
vec3_t a
float lastFrame
bool invis
int rounds
bool weather
fade_t frameFade
vec3_t s
struct ptl_s * next
blend_t blend
float dt
vec3_t v
int endFrame
bool autohide
bool stayalive
bool bounce
int levelFlags
bool inuse
float tps
int roundsCnt
int frame
ptlDef_t * ctrl
style_t style
struct ptl_s * children
float lightSustain
fade_t thinkFade
vec3_t origin
float lastThink
float life
r_program_t * program
vec3_t omega
int startTime
const image_t * image
char name[MAX_VAR]
Definition cl_renderer.h:97
union ptlArt_t::@003311107351012232215144266245032026252364056310 art
artType_t type
model_t * model
byte type
Definition cl_renderer.h:76
ptlCmd_t * run
Definition cl_renderer.h:84
ptlCmd_t * think
Definition cl_renderer.h:85
ptlCmd_t * round
Definition cl_renderer.h:86
ptlCmd_t * init
Definition cl_renderer.h:83
char name[MAX_VAR]
Definition cl_renderer.h:82
ptlCmd_t * physics
Definition cl_renderer.h:87
void * userdata
Definition r_program.h:62
char name[MAX_VAR]
Definition r_program.h:56
char ptl[MAX_VAR]
size_t ofs
Definition scripts.h:170
const char * string
Definition scripts.h:168
size_t size
Definition scripts.h:171
valueTypes_t type
Definition scripts.h:169
vec_t vec3_t[3]
Definition ufotypes.h:39
vec_t vec4_t[4]
Definition ufotypes.h:40
vec_t vec2_t[2]
Definition ufotypes.h:38
static const vec3_t scale
#define Vector4Set(v, r, g, b, a)
Definition vector.h:62
#define Vector2NotEmpty(a)
Definition vector.h:75
#define VectorClear(a)
Definition vector.h:55
#define VectorNegate(src, dest)
Definition vector.h:58
#define VectorNotEmpty(a)
Definition vector.h:72
#define VectorCopy(src, dest)
Definition vector.h:51
#define VectorAdd(a, b, dest)
Definition vector.h:47
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition vector.h:44
#define VectorScale(in, scale, out)
Definition vector.h:79