UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cl_particle.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, 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
39 static cvar_t* cl_particleweather;
40 
42 typedef struct mapParticle_s {
43  char ptl[MAX_VAR];
44  const char* info;
47  int nextTime;
50 
51 typedef struct timedParticle_s {
52  char ptl[MAX_VAR];
58  bool children;
60  int max;
61  int n;
62  int dt;
63  int lastTime;
65 
68 
70 static int r_numParticlesArt;
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 
77 typedef enum pf_s {
85 } pf_t;
86 
88 static char const* const pf_strings[] = {
89  "init",
90  "run",
91  "think",
92  "round",
93  "physics"
94 };
96 
98 static 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 };
105 CASSERT(lengthof(pf_values) == PF_NUM_PTLFUNCS);
106 
108 typedef enum pc_s {
110 
117 
120 
122 } pc_t;
123 
125 static 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 };
138 CASSERT(lengthof(pc_strings) == PC_NUM_PTLCMDS);
139 
141 static 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 };
154 CASSERT(lengthof(pc_types) == PC_NUM_PTLCMDS);
155 
160 static 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 
216 static int numPtlDefs;
217 static int numPtlCmds;
218 
219 #define MAX_PCMD_DATA (MAX_PTLCMDS * 8)
220 
224 
225 static const int RSTACK = -(MAX_PCMD_DATA);
226 
227 #define MAX_STACK_DEPTH 8
228 #define MAX_STACK_DATA 512
229 
231 static void* stackPtr[MAX_STACK_DEPTH];
233 
242 static 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++) {
252  timedParticle_t* tp = &timedParticles[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 
276 void 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 
283  mapParticle_t* mp = &mapParticles[cl.numMapParticles++];
284  Q_strncpyz(mp->ptl, ptl, sizeof(mp->ptl));
285  VectorCopy(origin, mp->origin);
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 
298 static 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++) {
328  ptlArt_t* a = &r_particlesArt[i];
330  }
331 }
332 
339 static 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 
366  r_numParticlesArt++;
367 
368  return a;
369 }
370 
377 static 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 
386 static 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);
461  p->program = R_LoadProgram((char*) stackPtr[stackIdx], R_InitParticleProgram, R_UseParticleProgram);
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:
607  CL_ParticleFree(p);
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 
705 ptl_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 */
711  ptlDef_t* pd = CL_ParticleGet(name);
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) {
724  if (r_numParticles < MAX_PTLS)
725  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 */
761  CL_ParticleFunction(p, pd->init);
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) {
781  CL_ParticleFree(c);
782  }
783 }
784 
792 static 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)
841  CL_ParticleFree(p);
842  }
843 }
844 
845 typedef struct ptlTraceCache_s {
846  int count;
852 
856 static 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 
882 static 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) {
895  CL_ParticleFree(p);
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) {
900  CL_ParticleFree(p);
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) {
939  CL_ParticleFree(p);
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 */
957  CL_ParticleFunction(p, p->ctrl->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 
1023 static void CL_ParticleRunTimed (void)
1024 {
1025  const size_t length = lengthof(timedParticles);
1026 
1027  for (int i = 0; i < length; i++) {
1028  timedParticle_t* tp = &timedParticles[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 
1062 static 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 
1115 static void CL_RunMapParticles (void)
1116 {
1117  for (int i = 0; i < cl.numMapParticles; i++) {
1118  mapParticle_t* mp = &mapParticles[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 
1148 void CL_ParticleRun (void)
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 
1161 static 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;
1277  if (pc_types[i] & PTL_ONLY_ONE_TYPE)
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 
1355 void 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
1426 
1429 static 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 
1444 static 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 
1475 void PTL_InitStartup (void)
1476 {
1477  r_numParticles = 0;
1478  numPtlCmds = 0;
1479  numPtlDefs = 0;
1480 
1481  r_numParticlesArt = 0;
1482 
1483  OBJZERO(ptlDef);
1484  OBJZERO(ptlCmd);
1485  OBJZERO(r_particlesArt);
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 }
pc_s
particle commands - see pc_strings
#define Vector2NotEmpty(a)
Definition: vector.h:75
bool Q_strnull(const char *string)
Definition: shared.h:138
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition: cmd.cpp:516
struct ptl_s * children
Definition: cl_renderer.h:132
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition: cmd.cpp:744
#define VectorCopy(src, dest)
Definition: vector.h:51
CASSERT(lengthof(pf_strings)==PF_NUM_PTLFUNCS)
bool invis
Definition: cl_renderer.h:109
static const int RSTACK
#define V_VECS
Definition: cl_particle.cpp:72
void * userdata
Definition: r_program.h:62
float tps
Definition: cl_renderer.h:142
int levelFlags
Definition: cl_renderer.h:128
vec4_t color
Definition: cl_renderer.h:120
static char const *const pc_strings[]
particle commands - see pc_t
r_program_t * R_LoadProgram(const char *name, programInitFunc_t init, programUseFunc_t use)
Definition: r_program.cpp:684
#define PTL_ONLY_ONE_TYPE
Definition: cl_particle.cpp:73
int roundsCnt
Definition: cl_renderer.h:149
vec3_t omega
Definition: cl_renderer.h:155
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
vec3_t s
Definition: cl_renderer.h:121
float dt
Definition: cl_renderer.h:146
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
valueTypes_t
possible values for parsing functions
Definition: scripts.h:48
#define PTL_INTENSITY_TO_RADIUS
Definition: cl_particle.cpp:37
void R_InitParticleProgram(r_program_t *prog)
Definition: r_program.cpp:1012
float lightSustain
Definition: cl_renderer.h:127
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
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
static ptlArt_t r_particlesArt[MAX_PTL_ART]
Definition: cl_particle.cpp:69
static const vec3_t scale
#define MAX_PTL_ART
Definition: cl_renderer.h:43
void CL_AddMapParticle(const char *ptl, const vec3_t origin, const vec2_t wait, const char *info, int levelflags)
Spawns the map particle.
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.
voidpf uLong int origin
Definition: ioapi.h:45
#define MAX_STACK_DATA
int frame
Definition: cl_renderer.h:139
vec3_t endpos
Definition: tracing.h:59
ptlCmd_t * round
Definition: cl_renderer.h:86
Definition: aabb.h:42
static cvar_t * cl_particleweather
Definition: cl_particle.cpp:39
static void CL_Fading(vec4_t color, fade_t fade, float frac, bool onlyAlpha)
Color fade function.
#define VectorNegate(src, dest)
Definition: vector.h:58
TR_PLANE_TYPE plane
Definition: tracing.h:60
const image_t * R_FindPics(const char *name)
Searches for an image in the image array.
Definition: r_image.cpp:673
static timedParticle_t timedParticles[MAX_TIMEDPARTICLES]
Definition: cl_particle.cpp:67
float lightIntensity
Definition: cl_renderer.h:126
const image_t * image
Definition: cl_renderer.h:101
bool bounce
Definition: cl_renderer.h:165
static ptlDef_t ptlDef[MAX_PTLDEFS]
static const size_t pf_values[]
particle functions offsets - see pf_strings and pf_t
Definition: cl_particle.cpp:98
static byte pcmdData[MAX_PCMD_DATA]
static void CL_ParticleFunction(ptl_t *p, ptlCmd_t *cmd)
#define UNIT_HEIGHT
Definition: defines.h:122
bool inuse
Definition: cl_renderer.h:108
static mapParticle_t mapParticles[MAX_MAPPARTICLES]
Definition: cl_particle.cpp:66
static int numPtlCmds
char name[MAX_VAR]
Definition: r_program.h:56
void set(const AABB &other)
Copies the values from the given aabb.
Definition: aabb.h:60
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
static byte cmdStack[MAX_STACK_DATA]
fade_t thinkFade
Definition: cl_renderer.h:144
valueTypes_t type
Definition: scripts.h:170
vec3_t maxs
Definition: aabb.h:258
#define MAX_MAPPARTICLES
Definition: cl_particle.cpp:34
model_t * model
Definition: cl_renderer.h:102
static void CL_ParticleLoadArt(ptlArt_t *a)
Loads the image or model for a given particle art.
ptl_t r_particleArray[MAX_PTLS]
Definition: r_particle.cpp:29
r_program_t * program
Definition: cl_renderer.h:111
int skin
Definition: cl_renderer.h:99
union ptlArt_s::@8 art
int integer
Definition: cvar.h:81
#define VectorScale(in, scale, out)
Definition: vector.h:79
style_t style
Definition: cl_renderer.h:117
#define CVAR_ARCHIVE
Definition: cvar.h:40
float fraction
Definition: tracing.h:58
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.
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
const char * info
Definition: cl_particle.cpp:44
float fps
Definition: cl_renderer.h:140
client_static_t cls
Definition: cl_main.cpp:83
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 Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
struct ptl_s * next
Definition: cl_renderer.h:133
int startTime
Definition: cl_renderer.h:138
unsigned int key
Definition: cl_input.cpp:68
static int numPtlDefs
#define MAX_PTLS
Definition: cl_renderer.h:44
cvar_t * cl_worldlevel
Definition: cl_hud.cpp:46
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
#define ERR_DROP
Definition: common.h:211
float frametime
Definition: client.h:59
#define DEBUG_CLIENT
Definition: defines.h:59
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
void CL_ParticleRegisterArt(void)
GLsizei size
Definition: r_gl.h:152
#define MAX_STACK_DEPTH
#define OBJZERO(obj)
Definition: shared.h:178
float lastThink
Definition: cl_renderer.h:143
#define MAX_VAR
Definition: shared.h:36
#define Vector4Set(v, r, g, b, a)
Definition: vector.h:62
float lastFrame
Definition: cl_renderer.h:141
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
void CL_ParticleCheckRounds(void)
checks whether a particle is still active in the current round
void R_UseParticleProgram(r_program_t *prog)
Definition: r_program.cpp:1017
#define M_PI
Definition: mathlib.h:34
const char *const vt_names[]
possible values for parsing functions
Definition: scripts.cpp:310
struct ptl_s * parent
Definition: cl_renderer.h:134
map particles
Definition: cl_particle.cpp:42
enum pf_s pf_t
particle functions enums - see pf_strings and pf_values
int Com_SetValue(void *base, const void *set, valueTypes_t type, int ofs, size_t size)
Definition: scripts.cpp:1023
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
static int r_numParticlesArt
Definition: cl_particle.cpp:70
blend_t blend
Definition: cl_renderer.h:116
const char * Com_ValueToStr(const void *base, const valueTypes_t type, const int ofs)
Definition: scripts.cpp:1189
bool stayalive
Definition: cl_renderer.h:160
bool autohide
Definition: cl_renderer.h:157
static ptlCmd_t ptlCmd[MAX_PTLCMDS]
struct mapParticle_s mapParticle_t
map particles
clientBattleScape_t cl
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
char ptl[MAX_VAR]
Definition: cl_particle.cpp:43
ptlCmd_t * physics
Definition: cl_renderer.h:87
static transfer_t tr
#define MASK_SOLID
Definition: defines.h:272
Definition: line.h:31
static ptlArt_t * CL_ParticleGetArt(const char *name, int frame, artType_t type)
Register art (pics, models) for each particle.
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
ptlDef_t * CL_ParticleGet(const char *name)
vec2_t size
Definition: cl_renderer.h:118
Definition: scripts.h:64
model_t * R_FindModel(const char *name)
Tries to load a model.
Definition: r_model.cpp:203
#define VectorNotEmpty(a)
Definition: vector.h:72
#define MAX_PCMD_DATA
ptlCmd_t * run
Definition: cl_renderer.h:84
const char * string
Definition: scripts.h:169
byte type
Definition: cl_renderer.h:76
static void CL_ParticleRun2(ptl_t *p)
Prepares the particle rendering, calculate new position, velocity and all the other particle values t...
void R_AddSustainedLight(const vec3_t org, float radius, const vec3_t color, float sustain)
Definition: r_light.cpp:58
static void * CL_ParticleCommandGetDataLocation(ptl_t *p, const ptlCmd_t *cmd)
Determine the memory location where the command accesses and stores its data.
size_t ofs
Definition: scripts.h:171
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
#define VectorClear(a)
Definition: vector.h:55
static char const *const pf_strings[]
valid particle functions - see pf_t and pf_values
Definition: cl_particle.cpp:88
vec3_t lightColor
Definition: cl_renderer.h:125
int Com_EParseValue(void *base, const char *token, valueTypes_t type, int ofs, size_t size)
Definition: scripts.cpp:978
#define VectorAdd(a, b, dest)
Definition: vector.h:47
fade_t
Definition: scripts.h:136
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
Definition: scripts.h:49
ptlCmd_t * think
Definition: cl_renderer.h:85
vec3_t v
Definition: cl_renderer.h:153
enum artType_s artType_t
particle art type
ptlDef_t * ctrl
Definition: cl_renderer.h:137
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
QGL_EXTERN GLint i
Definition: r_gl.h:113
QGL_EXTERN GLuint GLchar GLuint * len
Definition: r_gl.h:99
static const unsigned int pc_types[PC_NUM_PTLCMDS]
particle commands parameter and types
Definition: scripts.h:50
float life
Definition: cl_renderer.h:147
vec3_t angles
Definition: cl_renderer.h:124
vec3_t oldV
Definition: cl_renderer.h:154
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
#define S_COLOR_YELLOW
Definition: common.h:220
void CL_ParseParticle(const char *name, const char **text)
Parses particle definitions from UFO-script files.
void CL_ParticleFree(ptl_t *p)
Free a particle and all it's children.
static const value_t pps[]
particle script values
HUD related routines.
static byte stackType[MAX_STACK_DEPTH]
static void * stackPtr[MAX_STACK_DEPTH]
#define MEMBER_SIZEOF(TYPE, MEMBER)
Definition: scripts.h:34
#define V_UNTYPED
Definition: cl_particle.cpp:74
float crand(void)
Return random values between -1 and 1.
Definition: mathlib.cpp:517
size_t size
Definition: scripts.h:172
bool startsolid
Definition: tracing.h:57
int frame
Definition: cl_renderer.h:98
artType_t type
Definition: cl_renderer.h:104
vec_t vec3_t[3]
Definition: ufotypes.h:39
vec_t vec2_t[2]
Definition: ufotypes.h:38
static trace_t PTL_Trace(ptl_t *ptl, const AABB &aabb)
Particle tracing with caching.
float t
Definition: cl_renderer.h:145
static void CL_ParticleRunTimed(void)
Called every frame and checks whether a timed particle should be spawned.
static void CL_ParsePtlCmds(const char *name, const char **text)
Definition: scripts.h:52
char ptl[MAX_VAR]
Definition: cl_particle.cpp:52
char name[MAX_VAR]
Definition: cl_renderer.h:82
struct ptlTraceCache_s ptlTraceCache_t
fade_t frameFade
Definition: cl_renderer.h:144
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:440
void PTL_InitStartup(void)
Clears particle data.
int r_numParticles
Definition: r_particle.cpp:30
vec3_t mins
Definition: aabb.h:257
#define lengthof(x)
Definition: shared.h:105
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
static void CL_RunMapParticles(void)
vec3_t a
Definition: cl_renderer.h:152
#define MAX_PTLDEFS
#define Q_streq(a, b)
Definition: shared.h:136
ptlCmd_t * init
Definition: cl_renderer.h:83
bool physics
Definition: cl_renderer.h:156
voidpf uLong offset
Definition: ioapi.h:45
ptlArt_t * pic
Definition: cl_renderer.h:113
#define MAX_TIMEDPARTICLES
Definition: cl_particle.cpp:35
uint8_t byte
Definition: ufotypes.h:34
static SDL_Joystick * stick
Definition: cl_joystick.cpp:33
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
pf_s
particle functions enums - see pf_strings and pf_values
Definition: cl_particle.cpp:77
byte cmd
Definition: cl_renderer.h:75
int rounds
Definition: cl_renderer.h:148
static void CL_ParseMapParticle(ptl_t *ptl, const char *es, bool afterwards)
Parses particle used on maps.
ptlArt_t * model
Definition: cl_renderer.h:114
#define MAX_PTLCMDS
struct timedParticle_s timedParticle_t
Definition: scripts.h:55
bool stick
Definition: cl_renderer.h:164
bool hitSolid
Definition: cl_renderer.h:163
bool weather
Definition: cl_renderer.h:161
char name[MAX_VAR]
Definition: cl_renderer.h:97
void CL_ParticleRun(void)
General system for particle running during the game.
vec3_t origin
Definition: cl_renderer.h:122
static byte * pcmdPos
int CL_Milliseconds(void)
Definition: cl_main.cpp:1208
int endFrame
Definition: cl_renderer.h:139
vec_t vec4_t[4]
Definition: ufotypes.h:40
enum pc_s pc_t
particle commands - see pc_strings