UFO: Alien Invasion
cl_particle.cpp
Go to the documentation of this file.
1
6/*
7Copyright (C) 2002-2022 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"
31#include "../renderer/r_particle.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
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
77typedef enum pf_s {
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
108typedef enum pc_s {
110
117
120
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
141static const unsigned int pc_types[PC_NUM_PTLCMDS] = {
142 0,
143
145 V_VECS, V_VECS,
146 V_VECS, V_VECS,
148 V_VECS, V_VECS,
149 0, 0, 0,
150
151 0,
153};
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{
279 Com_Printf("Too many map particles (don't add %s) - exceeded %i\n", ptl, MAX_MAPPARTICLES);
280 return;
281 }
282
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 */
1279 j = pc_types[i] & ~PTL_ONLY_ONE_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:68
static SDL_Joystick * stick
Definition: cl_joystick.cpp:33
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
Definition: cl_particle.cpp:88
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]
Definition: cl_particle.cpp:69
#define MAX_MAPPARTICLES
Definition: cl_particle.cpp:34
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
Definition: cl_particle.cpp:37
static const value_t pps[]
particle script values
static int numPtlCmds
#define MAX_TIMEDPARTICLES
Definition: cl_particle.cpp:35
static byte cmdStack[MAX_STACK_DATA]
#define MAX_STACK_DEPTH
pf_t
particle functions enums - see pf_strings and pf_values
Definition: cl_particle.cpp:77
@ PF_NUM_PTLFUNCS
Definition: cl_particle.cpp:84
@ PF_ROUND
Definition: cl_particle.cpp:81
@ PF_RUN
Definition: cl_particle.cpp:79
@ PF_PHYSICS
Definition: cl_particle.cpp:82
@ PF_THINK
Definition: cl_particle.cpp:80
@ PF_INIT
Definition: cl_particle.cpp:78
static void CL_ParseMapParticle(ptl_t *ptl, const char *es, bool afterwards)
Parses particle used on maps.
#define PTL_ONLY_ONE_TYPE
Definition: cl_particle.cpp:73
static mapParticle_t mapParticles[MAX_MAPPARTICLES]
Definition: cl_particle.cpp:66
CASSERT(lengthof(pf_strings)==PF_NUM_PTLFUNCS)
static void CL_RunMapParticles(void)
#define V_VECS
Definition: cl_particle.cpp:72
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]
Definition: cl_particle.cpp:67
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
Definition: cl_particle.cpp:74
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
Definition: cl_particle.cpp:39
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
Definition: cl_particle.cpp:98
static const int RSTACK
static ptlCmd_t ptlCmd[MAX_PTLCMDS]
#define MAX_PTLDEFS
static int r_numParticlesArt
Definition: cl_particle.cpp:70
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
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:398
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
#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
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
Definition: r_particle.cpp:30
ptl_t r_particleArray[MAX_PTLS]
Definition: r_particle.cpp:29
void R_InitParticleProgram(r_program_t *prog)
Definition: r_program.cpp:1012
void R_UseParticleProgram(r_program_t *prog)
Definition: r_program.cpp:1017
r_program_t * R_LoadProgram(const char *name, programInitFunc_t init, programUseFunc_t use)
Definition: r_program.cpp:684
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
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
float frametime
Definition: client.h:59
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
int integer
Definition: cvar.h:81
map particles
Definition: cl_particle.cpp:42
char ptl[MAX_VAR]
Definition: cl_particle.cpp:43
const char * info
Definition: cl_particle.cpp:44
bool hitSolid
Definition: cl_renderer.h:163
vec4_t color
Definition: cl_renderer.h:120
bool physics
Definition: cl_renderer.h:156
float fps
Definition: cl_renderer.h:140
struct ptl_s * parent
Definition: cl_renderer.h:134
float lightIntensity
Definition: cl_renderer.h:126
ptlArt_t * model
Definition: cl_renderer.h:114
vec2_t size
Definition: cl_renderer.h:118
float t
Definition: cl_renderer.h:145
bool stick
Definition: cl_renderer.h:164
ptlArt_t * pic
Definition: cl_renderer.h:113
vec3_t lightColor
Definition: cl_renderer.h:125
vec3_t angles
Definition: cl_renderer.h:124
vec3_t oldV
Definition: cl_renderer.h:154
vec3_t a
Definition: cl_renderer.h:152
float lastFrame
Definition: cl_renderer.h:141
bool invis
Definition: cl_renderer.h:109
int rounds
Definition: cl_renderer.h:148
bool weather
Definition: cl_renderer.h:161
fade_t frameFade
Definition: cl_renderer.h:144
vec3_t s
Definition: cl_renderer.h:121
struct ptl_s * next
Definition: cl_renderer.h:133
blend_t blend
Definition: cl_renderer.h:116
float dt
Definition: cl_renderer.h:146
vec3_t v
Definition: cl_renderer.h:153
int endFrame
Definition: cl_renderer.h:139
bool autohide
Definition: cl_renderer.h:157
bool stayalive
Definition: cl_renderer.h:160
bool bounce
Definition: cl_renderer.h:165
int levelFlags
Definition: cl_renderer.h:128
bool inuse
Definition: cl_renderer.h:108
float tps
Definition: cl_renderer.h:142
int roundsCnt
Definition: cl_renderer.h:149
int frame
Definition: cl_renderer.h:139
ptlDef_t * ctrl
Definition: cl_renderer.h:137
style_t style
Definition: cl_renderer.h:117
struct ptl_s * children
Definition: cl_renderer.h:132
float lightSustain
Definition: cl_renderer.h:127
fade_t thinkFade
Definition: cl_renderer.h:144
vec3_t origin
Definition: cl_renderer.h:122
float lastThink
Definition: cl_renderer.h:143
float life
Definition: cl_renderer.h:147
r_program_t * program
Definition: cl_renderer.h:111
vec3_t omega
Definition: cl_renderer.h:155
int startTime
Definition: cl_renderer.h:138
union ptlArt_t::@8 art
const image_t * image
Definition: cl_renderer.h:101
char name[MAX_VAR]
Definition: cl_renderer.h:97
artType_t type
Definition: cl_renderer.h:104
int frame
Definition: cl_renderer.h:98
int skin
Definition: cl_renderer.h:99
model_t * model
Definition: cl_renderer.h:102
byte cmd
Definition: cl_renderer.h:75
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]
Definition: cl_particle.cpp:52
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