00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "../client.h"
00027 #include "cl_particle.h"
00028 #include "cl_localentity.h"
00029 #include "cl_view.h"
00030 #include "cl_parse.h"
00031 #include "cl_hud.h"
00032 #include "../renderer/r_light.h"
00033 #include "../renderer/r_particle.h"
00034 #include "../../shared/parse.h"
00035
00036 #define MAX_MAPPARTICLES 1024
00037 #define MAX_TIMEDPARTICLES 16
00038
00039 static cvar_t *cl_particleweather;
00040
00042 typedef struct mapParticle_s {
00043 char ptl[MAX_VAR];
00044 const char *info;
00045 vec3_t origin;
00046 vec2_t wait;
00047 int nextTime;
00048 int levelflags;
00049 } mapParticle_t;
00050
00051 typedef struct timedParticle_s {
00052 char ptl[MAX_VAR];
00053 vec3_t origin;
00054 vec3_t s;
00055 vec3_t a;
00056 vec3_t v;
00057 ptl_t *parent;
00058 qboolean children;
00059 int levelFlags;
00060 int max;
00061 int n;
00062 int dt;
00063 int lastTime;
00064 } timedParticle_t;
00065
00066 static mapParticle_t mapParticles[MAX_MAPPARTICLES];
00067 static timedParticle_t timedParticles[MAX_TIMEDPARTICLES];
00068
00069 #define V_VECS ((1 << V_FLOAT) | (1 << V_POS) | (1 << V_VECTOR) | (1 << V_COLOR))
00070 #define PTL_ONLY_ONE_TYPE (1<<31)
00071 #define V_UNTYPED 0x7FFF
00072
00074 typedef enum pf_s {
00075 PF_INIT,
00076 PF_RUN,
00077 PF_THINK,
00078 PF_ROUND,
00079 PF_PHYSICS,
00081 PF_NUM_PTLFUNCS
00082 } pf_t;
00083
00085 static const char *pf_strings[] = {
00086 "init",
00087 "run",
00088 "think",
00089 "round",
00090 "physics"
00091 };
00092 CASSERT(lengthof(pf_strings) == PF_NUM_PTLFUNCS);
00093
00095 static const size_t pf_values[] = {
00096 offsetof(ptlDef_t, init),
00097 offsetof(ptlDef_t, run),
00098 offsetof(ptlDef_t, think),
00099 offsetof(ptlDef_t, round),
00100 offsetof(ptlDef_t, physics)
00101 };
00102 CASSERT(lengthof(pf_values) == PF_NUM_PTLFUNCS);
00103
00105 typedef enum pc_s {
00106 PC_END,
00107
00108 PC_PUSH, PC_POP, PC_KPOP,
00109 PC_ADD, PC_SUB,
00110 PC_MUL, PC_DIV,
00111 PC_SIN, PC_COS, PC_TAN,
00112 PC_RAND, PC_CRAND,
00113 PC_V2, PC_V3, PC_V4,
00114
00115 PC_KILL,
00116 PC_SPAWN, PC_NSPAWN, PC_TNSPAWN, PC_CHILD,
00117
00118 PC_NUM_PTLCMDS
00119 } pc_t;
00120
00122 static const char *pc_strings[PC_NUM_PTLCMDS] = {
00123 "end",
00124
00125 "push", "pop", "kpop",
00126 "add", "sub",
00127 "mul", "div",
00128 "sin", "cos", "tan",
00129 "rand", "crand",
00130 "v2", "v3", "v4",
00131
00132 "kill",
00133 "spawn", "nspawn", "tnspawn", "child"
00134 };
00135 CASSERT(lengthof(pc_strings) == PC_NUM_PTLCMDS);
00136
00138 static const int pc_types[PC_NUM_PTLCMDS] = {
00139 0,
00140
00141 V_UNTYPED, V_UNTYPED, V_UNTYPED,
00142 V_VECS, V_VECS,
00143 V_VECS, V_VECS,
00144 PTL_ONLY_ONE_TYPE | V_FLOAT, PTL_ONLY_ONE_TYPE | V_FLOAT, PTL_ONLY_ONE_TYPE | V_FLOAT,
00145 V_VECS, V_VECS,
00146 0, 0, 0,
00147
00148 0,
00149 PTL_ONLY_ONE_TYPE | V_STRING, PTL_ONLY_ONE_TYPE | V_STRING, PTL_ONLY_ONE_TYPE | V_STRING, PTL_ONLY_ONE_TYPE | V_STRING
00150 };
00151 CASSERT(lengthof(pc_types) == PC_NUM_PTLCMDS);
00152
00157 static const value_t pps[] = {
00158 {"image", V_STRING, offsetof(ptl_t, pic), 0},
00159 {"model", V_STRING, offsetof(ptl_t, model), 0},
00160 {"program", V_STRING, offsetof(ptl_t, program), 0},
00161 {"skin", V_INT, offsetof(ptl_t, skin), MEMBER_SIZEOF(ptl_t, skin)},
00162 {"blend", V_BLEND, offsetof(ptl_t, blend), MEMBER_SIZEOF(ptl_t, blend)},
00163 {"style", V_STYLE, offsetof(ptl_t, style), MEMBER_SIZEOF(ptl_t, style)},
00164 {"thinkfade", V_FADE, offsetof(ptl_t, thinkFade), MEMBER_SIZEOF(ptl_t, thinkFade)},
00165 {"framefade", V_FADE, offsetof(ptl_t, frameFade), MEMBER_SIZEOF(ptl_t, frameFade)},
00166 {"size", V_POS, offsetof(ptl_t, size), MEMBER_SIZEOF(ptl_t, size)},
00167 {"scale", V_VECTOR, offsetof(ptl_t, scale), MEMBER_SIZEOF(ptl_t, scale)},
00168 {"color", V_COLOR, offsetof(ptl_t, color), MEMBER_SIZEOF(ptl_t, color)},
00169 {"a", V_VECTOR, offsetof(ptl_t, a), MEMBER_SIZEOF(ptl_t, a)},
00170 {"v", V_VECTOR, offsetof(ptl_t, v), MEMBER_SIZEOF(ptl_t, v)},
00171 {"s", V_VECTOR, offsetof(ptl_t, s), MEMBER_SIZEOF(ptl_t, s)},
00172 {"offset", V_VECTOR, offsetof(ptl_t, offset), MEMBER_SIZEOF(ptl_t, offset)},
00173 {"scroll_s", V_FLOAT, offsetof(ptl_t, scrollS), MEMBER_SIZEOF(ptl_t, scrollS)},
00174 {"scroll_t", V_FLOAT, offsetof(ptl_t, scrollT), MEMBER_SIZEOF(ptl_t, scrollT)},
00175
00176
00177
00178 {"t", V_FLOAT, offsetof(ptl_t, t), MEMBER_SIZEOF(ptl_t, t)},
00179 {"dt", V_FLOAT, offsetof(ptl_t, dt), MEMBER_SIZEOF(ptl_t, dt)},
00180
00181 {"rounds", V_INT, offsetof(ptl_t, rounds), MEMBER_SIZEOF(ptl_t, rounds)},
00182 {"angles", V_VECTOR, offsetof(ptl_t, angles), MEMBER_SIZEOF(ptl_t, angles)},
00183 {"omega", V_VECTOR, offsetof(ptl_t, omega), MEMBER_SIZEOF(ptl_t, omega)},
00184 {"life", V_FLOAT, offsetof(ptl_t, life), MEMBER_SIZEOF(ptl_t, life)},
00185 {"tps", V_FLOAT, offsetof(ptl_t, tps), MEMBER_SIZEOF(ptl_t, tps)},
00186 {"lastthink", V_FLOAT, offsetof(ptl_t, lastThink), MEMBER_SIZEOF(ptl_t, lastThink)},
00187 {"frame", V_INT, offsetof(ptl_t, frame), MEMBER_SIZEOF(ptl_t, frame)},
00188 {"endframe", V_INT, offsetof(ptl_t, endFrame), MEMBER_SIZEOF(ptl_t, endFrame)},
00189 {"fps", V_FLOAT, offsetof(ptl_t, fps), MEMBER_SIZEOF(ptl_t, fps)},
00190 {"lastframe", V_FLOAT, offsetof(ptl_t, lastFrame), MEMBER_SIZEOF(ptl_t, lastFrame)},
00191 {"levelflags", V_INT, offsetof(ptl_t, levelFlags), MEMBER_SIZEOF(ptl_t, levelFlags)},
00192 {"physics", V_BOOL, offsetof(ptl_t, physics), MEMBER_SIZEOF(ptl_t, physics)},
00193 {"autohide", V_BOOL, offsetof(ptl_t, autohide), MEMBER_SIZEOF(ptl_t, autohide)},
00194 {"stayalive", V_BOOL, offsetof(ptl_t, stayalive), MEMBER_SIZEOF(ptl_t, stayalive)},
00195 {"weather", V_BOOL, offsetof(ptl_t, weather), MEMBER_SIZEOF(ptl_t, weather)},
00196 {"lightcolor", V_VECTOR, offsetof(ptl_t, lightColor), MEMBER_SIZEOF(ptl_t, lightColor)},
00197 {"lightintensity", V_FLOAT, offsetof(ptl_t, lightIntensity), MEMBER_SIZEOF(ptl_t, lightIntensity)},
00198 {"lightsustain", V_FLOAT, offsetof(ptl_t, lightSustain), MEMBER_SIZEOF(ptl_t, lightSustain)},
00199
00200 {NULL, 0, 0, 0}
00201 };
00202
00203
00204
00205 #define MAX_PTLDEFS 256
00206 #define MAX_PTLCMDS (MAX_PTLDEFS * 32)
00207
00208 static ptlDef_t ptlDef[MAX_PTLDEFS];
00209 static ptlCmd_t ptlCmd[MAX_PTLCMDS];
00210
00211 static int numPtlDefs;
00212 static int numPtlCmds;
00213
00214 #define MAX_PCMD_DATA (MAX_PTLCMDS * 8)
00215
00217 static byte pcmdData[MAX_PCMD_DATA];
00218 static byte *pcmdPos = pcmdData;
00219
00220 static const int RSTACK = -(MAX_PCMD_DATA);
00221
00222 #define MAX_STACK_DEPTH 8
00223 #define MAX_STACK_DATA 512
00224
00225 static byte cmdStack[MAX_STACK_DATA];
00226 static void *stackPtr[MAX_STACK_DEPTH];
00227 static byte stackType[MAX_STACK_DEPTH];
00228
00237 static void CL_ParticleSpawnTimed (const char *name, ptl_t *parent, qboolean children, int deltaTime, int n)
00238 {
00239 const size_t length = lengthof(timedParticles);
00240 int i;
00241
00242 if (n <= 0)
00243 Com_Error(ERR_DROP, "Timed particle should spawn particles");
00244
00245 if (deltaTime <= 0)
00246 Com_Error(ERR_DROP, "Delta time for timed particle is invalid");
00247
00248 for (i = 0; i < length; i++) {
00249 timedParticle_t *tp = &timedParticles[i];
00250 if (tp->n == tp->max) {
00251
00252 Q_strncpyz(tp->ptl, name, sizeof(tp->ptl));
00253 tp->levelFlags = parent->levelFlags;
00254 tp->dt = deltaTime;
00255 tp->n = 0;
00256 tp->children = children;
00257 tp->max = n;
00258 tp->parent = parent;
00259 return;
00260 }
00261 }
00262 Com_Printf("Could not spawn timed particles due to overflow\n");
00263 }
00264
00273 void CL_AddMapParticle (const char *ptl, const vec3_t origin, const vec2_t wait, const char *info, int levelflags)
00274 {
00275 mapParticle_t *mp;
00276
00277 mp = &mapParticles[cl.numMapParticles];
00278
00279 if (cl.numMapParticles >= MAX_MAPPARTICLES) {
00280 Com_Printf("Too many map particles (don't add %s) - exceeded %i\n", ptl, MAX_MAPPARTICLES);
00281 return;
00282 }
00283 cl.numMapParticles++;
00284
00285 Q_strncpyz(mp->ptl, ptl, sizeof(mp->ptl));
00286 VectorCopy(origin, mp->origin);
00287 mp->info = info;
00288 mp->levelflags = levelflags;
00289 mp->wait[0] = wait[0] * 1000;
00290 mp->wait[1] = wait[1] * 1000;
00291 mp->nextTime = cl.time + wait[0] + wait[1] * frand() + 1;
00292
00293 Com_DPrintf(DEBUG_CLIENT, "Adding map particle %s (%i) with levelflags %i\n", ptl, cl.numMapParticles, levelflags);
00294 }
00295
00299 static inline void CL_ParticleLoadArt (ptlArt_t *a)
00300 {
00301
00302 switch (a->type) {
00303 case ART_PIC:
00304 {
00305 const char *imageName;
00306
00307 if (a->name[0] != '+')
00308 imageName = a->name;
00309 else
00310 imageName = va("%s%c%c", a->name + 1, a->frame / 10 + '0', a->frame % 10 + '0');
00311 a->art.image = R_RegisterImage(imageName);
00312 if (!a->art.image)
00313 Com_Printf("CL_ParticleLoadArt: Could not load image: '%s'\n", imageName);
00314 }
00315 break;
00316 case ART_MODEL:
00318 a->art.model = R_RegisterModelShort(a->name);
00319 break;
00320 default:
00321 Com_Error(ERR_DROP, "CL_ParticleLoadArt: Unknown art type\n");
00322 }
00323 }
00324
00325 void CL_ParticleRegisterArt (void)
00326 {
00327 ptlArt_t *a;
00328 int i;
00329
00330 for (i = 0, a = r_particlesArt; i < r_numParticlesArt; i++, a++)
00331 CL_ParticleLoadArt(a);
00332 }
00333
00341 static ptlArt_t *CL_ParticleGetArt (const char *name, int frame, byte type)
00342 {
00343 ptlArt_t *a;
00344 int i;
00345
00346
00347 for (i = 0, a = r_particlesArt; i < r_numParticlesArt; i++, a++)
00348 if (a->type == type && (type == ART_PIC && a->frame == frame) && !strcmp(name, a->name))
00349 break;
00350
00351 if (i < r_numParticlesArt)
00352 return a;
00353
00354 if (i >= MAX_PTL_ART)
00355 Com_Error(ERR_DROP, "CL_ParticleGetArt: MAX_PTL_ART overflow");
00356
00357 a->skin = 0;
00358 a->type = type;
00359 a->frame = frame;
00360 Q_strncpyz(a->name, name, sizeof(a->name));
00361
00362 CL_ParticleLoadArt(a);
00363
00364
00365 if (!a->art.image)
00366 return NULL;
00367
00368 r_numParticlesArt++;
00369
00370 return a;
00371 }
00372
00376 void PTL_InitStartup (void)
00377 {
00378 r_numParticles = 0;
00379 numPtlCmds = 0;
00380 numPtlDefs = 0;
00381
00382 r_numParticlesArt = 0;
00383 }
00384
00391 static inline void *CL_ParticleCommandGetDataLocation (ptl_t *p, const ptlCmd_t *cmd)
00392 {
00393 if (cmd->ref < 0)
00394
00395 return (byte*)p - cmd->ref;
00396 else
00397
00398 return (byte*)pcmdData + cmd->ref;
00399 }
00400
00401 static void CL_ParticleFunction (ptl_t * p, ptlCmd_t * cmd)
00402 {
00403 int stackIdx;
00404 ptrdiff_t e;
00405 int type;
00406 int i, j, n;
00407 void *cmdData;
00408 float arg;
00409 ptl_t *pnew;
00410
00411
00412 if (!cmd)
00413 return;
00414
00415
00416 for (stackIdx = 0, e = 0; cmd->cmd != PC_END; cmd++) {
00417 if (cmd->ref > RSTACK)
00418 cmdData = CL_ParticleCommandGetDataLocation(p, cmd);
00419 else {
00420 if (!stackIdx)
00421 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00422
00423
00424 e = (byte *) stackPtr[--stackIdx] - cmdStack;
00425
00426 i = RSTACK - cmd->ref;
00427 if (!i) {
00428
00429 cmdData = stackPtr[stackIdx];
00430 cmd->type = stackType[stackIdx];
00431 } else {
00432
00433 if ((1 << stackType[stackIdx]) & V_VECS) {
00434 cmd->type = V_FLOAT;
00435 cmdData = (float *) stackPtr[stackIdx] + (i - 1);
00436 } else {
00437 Com_Error(ERR_DROP, "CL_ParticleFunction: can't get components of a non-vector type (particle %s)", p->ctrl->name);
00438 }
00439 }
00440 }
00441
00442 switch (cmd->cmd) {
00443 case PC_PUSH:
00444
00445 if (stackIdx >= MAX_STACK_DEPTH)
00446 Com_Error(ERR_DROP, "CL_ParticleFunction: stack overflow");
00447
00448
00449 stackPtr[stackIdx] = &cmdStack[e];
00450 stackType[stackIdx] = cmd->type;
00451 e += Com_SetValue(stackPtr[stackIdx++], cmdData, cmd->type, 0, 0);
00452 break;
00453
00454 case PC_POP:
00455 case PC_KPOP:
00456
00457 if (stackIdx == 0)
00458 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00459
00460
00461 if (offsetof(ptl_t, pic) == -cmd->ref) {
00462 if (stackType[--stackIdx] != V_STRING)
00463 Com_Error(ERR_DROP, "Bad type '%s' for pic (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
00464 p->pic = CL_ParticleGetArt((char *) stackPtr[stackIdx], p->frame, ART_PIC);
00465 e = (byte *) stackPtr[stackIdx] - cmdStack;
00466 break;
00467 }
00468 if (offsetof(ptl_t, model) == -cmd->ref) {
00469 if (stackType[--stackIdx] != V_STRING)
00470 Com_Error(ERR_DROP, "Bad type '%s' for model (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
00471 p->model = CL_ParticleGetArt((char *) stackPtr[stackIdx], p->frame, ART_MODEL);
00472 e = (byte *) stackPtr[stackIdx] - cmdStack;
00473 break;
00474 }
00475 if (offsetof(ptl_t, program) == -cmd->ref) {
00476 if (stackType[--stackIdx] != V_STRING)
00477 Com_Error(ERR_DROP, "Bad type '%s' for program (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
00478 p->program = R_LoadProgram((char *) stackPtr[stackIdx], R_InitParticleProgram, R_UseParticleProgram);
00479 if (p->program)
00480 p->program->userdata = p;
00481 e = (byte *) stackPtr[stackIdx] - cmdStack;
00482 break;
00483 }
00484
00485
00486 if (cmd->cmd == PC_POP)
00487 e -= Com_SetValue(cmdData, stackPtr[--stackIdx], cmd->type, 0, 0);
00488 else
00489 Com_SetValue(cmdData, stackPtr[stackIdx - 1], cmd->type, 0, 0);
00490 break;
00491
00492 case PC_ADD:
00493 case PC_SUB:
00494
00495 if (stackIdx == 0)
00496 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00497
00498 type = stackType[stackIdx - 1];
00499 if (!((1 << type) & V_VECS))
00500 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for add (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
00501
00502
00503 if (type != cmd->type)
00504 Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for add/sub (particle %s)", p->ctrl->name);
00505
00506 n = type - V_FLOAT + 1;
00507
00508 for (i = 0; i < n; i++) {
00509 if (cmd->cmd == PC_SUB)
00510 arg = -(*((float *) cmdData + i));
00511 else
00512 arg = *((float *) cmdData + i);
00513 *((float *) stackPtr[stackIdx - 1] + i) += arg;
00514 }
00515 break;
00516
00517 case PC_MUL:
00518 case PC_DIV:
00519
00520 if (stackIdx == 0)
00521 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00522
00523 type = stackType[stackIdx - 1];
00524 if (!((1 << type) & V_VECS))
00525 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for add (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
00526
00527 n = type - V_FLOAT + 1;
00528
00529 if (type > V_FLOAT && cmd->type > V_FLOAT) {
00530
00531 if (type != cmd->type)
00532 Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for mul/div (particle %s)", p->ctrl->name);
00533
00534 for (i = 0; i < n; i++) {
00535 if (cmd->cmd == PC_DIV)
00536 arg = 1.0 / (*((float *) cmdData + i));
00537 else
00538 arg = *((float *) cmdData + i);
00539 *((float *) stackPtr[stackIdx - 1] + i) *= arg;
00540 }
00541 break;
00542 }
00543
00544 if (cmd->type > V_FLOAT)
00545 Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for mul/div (particle %s)", p->ctrl->name);
00546
00547
00548 if (cmd->cmd == PC_DIV)
00549 arg = 1.0 / (*(float *) cmdData);
00550 else
00551 arg = *(float *) cmdData;
00552 for (i = 0; i < n; i++)
00553 *((float *) stackPtr[stackIdx - 1] + i) *= arg;
00554
00555 break;
00556
00557 case PC_SIN:
00558 if (cmd->type != V_FLOAT)
00559 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for sin (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
00560 stackPtr[stackIdx] = &cmdStack[e];
00561 stackType[stackIdx] = cmd->type;
00562 *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI));
00563 e += sizeof(float);
00564 break;
00565
00566 case PC_COS:
00567 if (cmd->type != V_FLOAT)
00568 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for cos (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
00569 stackPtr[stackIdx] = &cmdStack[e];
00570 stackType[stackIdx] = cmd->type;
00571 *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI));
00572 e += sizeof(float);
00573 break;
00574
00575 case PC_TAN:
00576 if (cmd->type != V_FLOAT)
00577 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for tan (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name);
00578 stackPtr[stackIdx] = &cmdStack[e];
00579 stackType[stackIdx] = cmd->type;
00580 *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI));
00581 e += sizeof(float);
00582 break;
00583
00584 case PC_RAND:
00585 case PC_CRAND:
00586 stackPtr[stackIdx] = &cmdStack[e];
00587 stackType[stackIdx] = cmd->type;
00588
00589 n = cmd->type - V_FLOAT + 1;
00590
00591 if (cmd->cmd == PC_RAND)
00592 for (i = 0; i < n; i++)
00593 *((float *) stackPtr[stackIdx] + i) = *((float *) cmdData + i) * frand();
00594 else
00595 for (i = 0; i < n; i++)
00596 *((float *) stackPtr[stackIdx] + i) = *((float *) cmdData + i) * crand();
00597
00598 e += n * sizeof(float);
00599 stackIdx++;
00600 break;
00601
00602 case PC_V2:
00603 case PC_V3:
00604 case PC_V4:
00605 n = cmd->cmd - PC_V2 + 2;
00606 j = 0;
00607
00608 if (stackIdx < n)
00609 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00610
00611 for (i = 0; i < n; i++) {
00612 if (!((1 << stackType[--stackIdx]) & V_VECS))
00613 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for vector creation (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name);
00614 j += stackType[stackIdx] - V_FLOAT + 1;
00615 }
00616
00617 if (j > 4)
00618 Com_Error(ERR_DROP, "CL_ParticleFunction: created vector with dim > 4 (particle %s)", p->ctrl->name);
00619
00620 stackType[stackIdx++] = V_FLOAT + j - 1;
00621 break;
00622
00623 case PC_KILL:
00624 CL_ParticleFree(p);
00625 return;
00626
00627 case PC_SPAWN:
00628 pnew = CL_ParticleSpawn((const char *) cmdData, p->levelFlags, p->s, p->v, p->a);
00629 if (!pnew)
00630 Com_Printf("PC_SPAWN: Could not spawn child particle for '%s'\n", p->ctrl->name);
00631 break;
00632
00633 case PC_TNSPAWN:
00634
00635 if (stackIdx < 2)
00636 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00637
00638
00639
00640 type = stackType[--stackIdx];
00641 if (type != V_INT)
00642 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for tnspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name);
00643 n = *(int *) stackPtr[stackIdx];
00644
00645
00646 type = stackType[--stackIdx];
00647 if (type != V_INT)
00648 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for tnspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name);
00649 i = *(int *) stackPtr[stackIdx];
00650
00652 CL_ParticleSpawnTimed((const char *) cmdData, p, qtrue, i, n);
00653
00654 e -= 2 * sizeof(int);
00655
00656 break;
00657
00658 case PC_NSPAWN:
00659
00660 if (stackIdx == 0)
00661 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00662
00663 type = stackType[--stackIdx];
00664 if (type != V_INT)
00665 Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for nspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name);
00666
00667 n = *(int *) stackPtr[stackIdx];
00668 e -= sizeof(int);
00669
00670 for (i = 0; i < n; i++) {
00671 pnew = CL_ParticleSpawn((const char *) cmdData, p->levelFlags, p->s, p->v, p->a);
00672 if (!pnew)
00673 Com_Printf("PC_NSPAWN: Could not spawn child particle for '%s'\n", p->ctrl->name);
00674 }
00675 break;
00676
00677 case PC_CHILD:
00678 pnew = CL_ParticleSpawn((const char *)cmdData, p->levelFlags, p->s, p->v, p->a);
00679 if (pnew) {
00680 pnew->next = p->children;
00681 pnew->parent = p;
00682 p->children = pnew;
00683 } else {
00684 Com_Printf("PC_CHILD: Could not spawn child particle for '%s'\n", p->ctrl->name);
00685 }
00686 break;
00687
00688 default:
00689 Com_Error(ERR_DROP, "CL_ParticleFunction: unknown cmd type %i", cmd->type);
00690 break;
00691 }
00692 }
00693 }
00694
00706 ptl_t *CL_ParticleSpawn (const char *name, int levelFlags, const vec3_t s, const vec3_t v, const vec3_t a)
00707 {
00708 ptlDef_t *pd;
00709 ptl_t *p;
00710 int i;
00711
00712 if (!name || strlen(name) <= 0)
00713 return NULL;
00714
00715
00716 for (i = 0; i < numPtlDefs; i++)
00717 if (!strcmp(name, ptlDef[i].name))
00718 break;
00719
00720 if (i == numPtlDefs) {
00721 Com_Printf("Particle definition \"%s\" not found\n", name);
00722 return NULL;
00723 }
00724
00725 pd = &ptlDef[i];
00726
00727
00728 for (i = 0; i < r_numParticles; i++)
00729 if (!r_particles[i].inuse)
00730 break;
00731
00732 if (i == r_numParticles) {
00733 if (r_numParticles < MAX_PTLS)
00734 r_numParticles++;
00735 else {
00736 Com_DPrintf(DEBUG_CLIENT, "Too many particles (don't add %s) - exceeded %i\n", name, MAX_PTLS);
00737 return NULL;
00738 }
00739 }
00740
00741
00742 p = &r_particles[i];
00743 memset(p, 0, sizeof(*p));
00744
00745
00746 p->inuse = qtrue;
00747 p->startTime = cl.time;
00748 p->ctrl = pd;
00749 Vector4Set(p->color, 1.0f, 1.0f, 1.0f, 1.0f);
00750
00751 p->pic = NULL;
00752 p->model = NULL;
00753
00754
00755 if (s) {
00756 VectorCopy(s, p->origin);
00757 VectorCopy(s, p->s);
00758 }
00759
00760 if (v)
00761 VectorCopy(v, p->v);
00762
00763 if (a)
00764 VectorCopy(a, p->a);
00765
00766
00767 p->levelFlags = levelFlags;
00768
00769
00770 CL_ParticleFunction(p, pd->init);
00771 if (p->inuse && !p->tps && !p->life) {
00772 Com_DPrintf(DEBUG_CLIENT, "Particle %s does not have a tps nor a life set - this is only valid for projectile particles\n",
00773 name);
00774 p->tps = 1;
00775 }
00776
00777 return p;
00778 }
00779
00785 void CL_ParticleVisible (ptl_t *p, qboolean hide)
00786 {
00787 ptl_t *c;
00788
00789 p->invis = hide;
00790
00791 for (c = p->children; c; c = c->next) {
00792 CL_ParticleVisible(c, hide);
00793 }
00794 }
00795
00801 void CL_ParticleFree (ptl_t *p)
00802 {
00803 ptl_t *c;
00804
00805 p->inuse = qfalse;
00806 p->invis = qtrue;
00807 for (c = p->children; c; c = c->next) {
00808 CL_ParticleFree(c);
00809 }
00810 }
00811
00819 static void CL_Fading (vec4_t color, fade_t fade, float frac, qboolean onlyAlpha)
00820 {
00821 int i;
00822
00823 switch (fade) {
00824 case FADE_IN:
00825 for (i = onlyAlpha ? 3 : 0; i < 4; i++)
00826 color[i] *= frac;
00827 break;
00828 case FADE_OUT:
00829 for (i = onlyAlpha ? 3 : 0; i < 4; i++)
00830 color[i] *= (1.0 - frac);
00831 break;
00832 case FADE_SIN:
00833 for (i = onlyAlpha ? 3 : 0; i < 4; i++)
00834 color[i] *= sin(frac * M_PI);
00835 break;
00836 case FADE_SAW:
00837 if (frac < 0.5)
00838 for (i = onlyAlpha ? 3 : 0; i < 4; i++)
00839 color[i] *= frac * 2;
00840 else
00841 for (i = onlyAlpha ? 3 : 0; i < 4; i++)
00842 color[i] *= (1.0 - frac) * 2;
00843 break;
00844 case FADE_NONE:
00845 break;
00846 case FADE_LAST:
00847 break;
00848 }
00849 }
00850
00856 void CL_ParticleCheckRounds (void)
00857 {
00858 ptl_t *p;
00859 int i;
00860
00861 for (i = 0, p = r_particles; i < r_numParticles; i++, p++)
00862 if (p->inuse) {
00863
00864 CL_ParticleFunction(p, p->ctrl->round);
00865
00866 if (p->rounds) {
00867 p->roundsCnt--;
00868 if (p->roundsCnt <= 0)
00869 CL_ParticleFree(p);
00870 }
00871 }
00872 }
00873
00880 static void CL_ParticleRun2 (ptl_t *p)
00881 {
00882
00883 p->dt = cls.frametime;
00884 p->t = (cl.time - p->startTime) * 0.001f;
00885 p->lastThink += p->dt;
00886 p->lastFrame += p->dt;
00887
00888 if (p->rounds && !p->roundsCnt)
00889 p->roundsCnt = p->rounds;
00890
00891
00892 if (p->life && p->t >= p->life && !p->parent) {
00893 CL_ParticleFree(p);
00894 return;
00895
00896
00897
00900 } else if (p->weather && !cl_particleweather->integer) {
00901 CL_ParticleFree(p);
00902 return;
00903 }
00904
00905
00906 if (p->style != STYLE_LINE) {
00907 VectorMA(p->s, 0.5 * p->dt * p->dt, p->a, p->s);
00908 VectorMA(p->s, p->dt, p->v, p->s);
00909 VectorMA(p->v, p->dt, p->a, p->v);
00910 VectorMA(p->angles, p->dt, p->omega, p->angles);
00911 }
00912
00913
00914 CL_ParticleFunction(p, p->ctrl->run);
00915
00916
00917 while (p->tps && p->lastThink * p->tps >= 1) {
00918 CL_ParticleFunction(p, p->ctrl->think);
00919 p->lastThink -= 1.0 / p->tps;
00920 }
00921
00922
00923 while (p->fps && p->lastFrame * p->fps >= 1) {
00924
00925 p->frame++;
00926 if (p->frame > p->endFrame)
00927 p->frame = 0;
00928 p->lastFrame -= 1.0 / p->fps;
00929
00930
00931 assert(p->pic);
00932 p->pic = CL_ParticleGetArt(p->pic->name, p->frame, ART_PIC);
00933 }
00934
00935
00936 if (p->thinkFade || p->frameFade) {
00937 const qboolean onlyAlpha = (p->blend == BLEND_BLEND);
00938 if (!onlyAlpha)
00939 Vector4Set(p->color, 1.0f, 1.0f, 1.0f, 1.0f);
00940 else
00941 p->color[3] = 1.0;
00942
00943 if (p->thinkFade)
00944 CL_Fading(p->color, p->thinkFade, p->lastThink * p->tps, onlyAlpha);
00945 if (p->frameFade)
00946 CL_Fading(p->color, p->frameFade, p->lastFrame * p->fps, onlyAlpha);
00947 }
00948
00949
00950
00951 if (p->autohide) {
00952 const int z = (int)p->s[2] / UNIT_HEIGHT;
00953 if (z > cl_worldlevel->integer) {
00954 p->invis = qtrue;
00955 return;
00956 } else if (z < 0) {
00957 CL_ParticleFree(p);
00958 return;
00959 }
00960 }
00961
00962
00963 if (p->physics) {
00965 trace_t tr;
00966
00967 tr = CL_Trace(p->origin, p->s, vec3_origin, vec3_origin, NULL, NULL, MASK_SOLID, cl.mapMaxLevel - 1);
00968
00969
00970 if (tr.fraction < 1.0 || tr.startsolid) {
00971
00972 if (p->ctrl->physics)
00973 CL_ParticleFunction(p, p->ctrl->physics);
00974
00975 if (!p->stayalive) {
00976 CL_ParticleFree(p);
00977 } else {
00978 VectorCopy(vec3_origin, p->v);
00979 VectorCopy(tr.endpos, p->s);
00980 }
00981 return;
00982 }
00983 }
00984
00985
00986 if (VectorNotEmpty(p->lightColor)) {
00987 const float intensity = 0.5 + p->lightIntensity;
00988 if (p->lightSustain)
00989 R_AddSustainedLight(p->s, intensity, p->lightColor, p->lightSustain);
00990 else
00991 R_AddLight(p->s, intensity, p->lightColor);
00992 }
00993
00994 p->invis = qfalse;
00995 }
00996
01000 static void CL_ParticleRunTimed (void)
01001 {
01002 int i;
01003 const size_t length = lengthof(timedParticles);
01004
01005 for (i = 0; i < length; i++) {
01006 timedParticle_t *tp = &timedParticles[i];
01007 if (!tp->parent || !tp->parent->inuse)
01008 continue;
01009 if (tp->n >= tp->max)
01010 continue;
01011 if (CL_Milliseconds() - tp->lastTime < tp->dt)
01012 continue;
01013 {
01014 ptl_t *p;
01015 if (!tp->n) {
01016
01017
01018
01019 VectorCopy(tp->parent->s, tp->s);
01020 VectorCopy(tp->parent->v, tp->v);
01021 VectorCopy(tp->parent->a, tp->a);
01022 }
01023 tp->n++;
01024 tp->lastTime = CL_Milliseconds();
01025 p = CL_ParticleSpawn(tp->ptl, tp->levelFlags, tp->s, tp->v, tp->a);
01026 if (p && tp->children) {
01027 p->next = tp->parent->children;
01028 p->parent = tp->parent;
01029 tp->parent->children = p;
01030 }
01031 }
01032 }
01033 }
01034
01039 void CL_ParticleRun (void)
01040 {
01041 ptl_t *p;
01042 int i;
01043
01044 if (cls.state != ca_active)
01045 return;
01046
01047 CL_ParticleRunTimed();
01048
01049 for (i = 0, p = r_particles; i < r_numParticles; i++, p++)
01050 if (p->inuse)
01051 CL_ParticleRun2(p);
01052 }
01053
01061 static void CL_ParseMapParticle (ptl_t * ptl, const char *es, qboolean afterwards)
01062 {
01063 char keyname[MAX_VAR];
01064 const char *token;
01065 char *key;
01066 const value_t *pp;
01067
01068 key = keyname + 1;
01069 do {
01070
01071 token = Com_Parse(&es);
01072 if (token[0] == '}')
01073 break;
01074 if (!es)
01075 Com_Error(ERR_DROP, "CL_ParseMapParticle: EOF without closing brace");
01076
01077 Q_strncpyz(keyname, token, sizeof(keyname));
01078
01079
01080 token = Com_Parse(&es);
01081 if (!es)
01082 Com_Error(ERR_DROP, "CL_ParseMapParticle: EOF without closing brace");
01083
01084 if (token[0] == '}')
01085 Com_Error(ERR_DROP, "CL_ParseMapParticle: closing brace without data");
01086
01087 if (!afterwards && keyname[0] != '-')
01088 continue;
01089 if (afterwards && keyname[0] != '+')
01090 continue;
01091
01092 for (pp = pps; pp->string; pp++) {
01093 if (!strcmp(key, pp->string)) {
01094
01095 Com_EParseValue(ptl, token, pp->type, pp->ofs, pp->size);
01096 break;
01097 }
01098 }
01099
01100 if (!pp->string) {
01101
01102 if (!strcmp(key, "image"))
01103 ptl->pic = CL_ParticleGetArt(token, ptl->frame, ART_PIC);
01104 else if (!strcmp(key, "model"))
01105 ptl->model = CL_ParticleGetArt(token, ptl->frame, ART_MODEL);
01106 else if (!strcmp(key, "program")) {
01107 ptl->program = R_LoadProgram(token, R_InitParticleProgram, R_UseParticleProgram);
01108 if (ptl->program)
01109 ptl->program->userdata = ptl;
01110 }
01111 }
01112 } while (token);
01113 }
01114
01115
01116 void CL_RunMapParticles (void)
01117 {
01118 mapParticle_t *mp;
01119 ptl_t *ptl;
01120 int i;
01121
01122 if (cls.state != ca_active)
01123 return;
01124
01125 for (i = 0, mp = mapParticles; i < cl.numMapParticles; i++, mp++)
01126 if (mp->nextTime && cl.time >= mp->nextTime) {
01127
01128 ptl = CL_ParticleSpawn(mp->ptl, mp->levelflags, mp->origin, NULL, NULL);
01129 if (!ptl) {
01130 mp->nextTime = 0;
01131 continue;
01132 }
01133
01134
01135 CL_ParseMapParticle(ptl, mp->info, qfalse);
01136 CL_ParticleFunction(ptl, ptl->ctrl->init);
01137 CL_ParseMapParticle(ptl, mp->info, qtrue);
01138
01139
01140 if (mp->wait[0] || mp->wait[1])
01141 mp->nextTime += mp->wait[0] + mp->wait[1] * frand();
01142 else
01143 mp->nextTime = 0;
01144 }
01145 }
01146
01147
01148 static void CL_ParsePtlCmds (const char *name, const char **text)
01149 {
01150 ptlCmd_t *pc;
01151 const value_t *pp;
01152 const char *errhead = "CL_ParsePtlCmds: unexpected end of file";
01153 const char *token;
01154 int i, j;
01155
01156
01157 token = Com_Parse(text);
01158
01159 if (!*text || *token != '{') {
01160 Com_Printf("CL_ParsePtlCmds: particle cmds \"%s\" without body ignored\n", name);
01161 return;
01162 }
01163
01164 do {
01165 token = Com_EParse(text, errhead, name);
01166 if (!*text)
01167 break;
01168 if (*token == '}')
01169 break;
01170
01171 for (i = 0; i < PC_NUM_PTLCMDS; i++)
01172 if (!strcmp(token, pc_strings[i])) {
01173
01174 if (numPtlCmds >= MAX_PTLCMDS)
01175 Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
01176 pc = &ptlCmd[numPtlCmds++];
01177 memset(pc, 0, sizeof(*pc));
01178
01179 pc->cmd = i;
01180
01181 if (!pc_types[i])
01182 break;
01183
01184
01185 token = Com_EParse(text, errhead, name);
01186 if (!*text)
01187 return;
01188
01189
01190 if (token[0] == '#') {
01191 pc->ref = RSTACK;
01192 if (token[1] == '.')
01193 pc->ref -= (token[2] - '0');
01194 break;
01195 }
01196
01197 if (token[0] == '*') {
01198 int len;
01199 char baseComponentToken[MAX_VAR];
01200
01201
01202 token++;
01203
01204
01205 Q_strncpyz(baseComponentToken, token, sizeof(baseComponentToken));
01206
01207
01208 len = strlen(baseComponentToken);
01209
01210
01211 if (len >= 2 && baseComponentToken[len - 2] == '.') {
01212 baseComponentToken[len - 2] = 0;
01213 } else
01214 len = 0;
01215
01216 for (pp = pps; pp->string; pp++)
01217 if (!strcmp(baseComponentToken, pp->string))
01218 break;
01219
01220 if (!pp->string) {
01221 Com_Printf("CL_ParsePtlCmds: bad reference \"%s\" specified (particle %s)\n", token, name);
01222 numPtlCmds--;
01223 break;
01224 }
01225
01226 if ((pc_types[i] & PTL_ONLY_ONE_TYPE)) {
01227 if ((pc_types[i] & ~PTL_ONLY_ONE_TYPE) != pp->type) {
01228 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]);
01229 numPtlCmds--;
01230 break;
01231 }
01232 } else if (pp->type >= V_NUM_TYPES || !((1 << pp->type) & pc_types[i])) {
01233 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]);
01234 numPtlCmds--;
01235 break;
01236 }
01237
01238 if (len) {
01239
01240 if ((1 << pp->type) & V_VECS) {
01241 const int component = (baseComponentToken[len - 1] - '1');
01242
01243 if (component > 3) {
01244 Com_Printf("CL_ParsePtlCmds: bad component value - it's bigger than 3: %i (particle %s)\n", component, name);
01245 numPtlCmds--;
01246 break;
01247 }
01248 pc->type = V_FLOAT;
01249
01250 pc->ref = -((int)pp->ofs) - component * sizeof(float);
01251 break;
01252 } else {
01253 Com_Printf("CL_ParsePtlCmds: can't get components of a non-vector type (particle %s)\n", name);
01254 numPtlCmds--;
01255 break;
01256 }
01257 }
01258
01259
01260 pc->type = pp->type;
01261 pc->ref = -((int)pp->ofs);
01262 break;
01263 }
01264
01265
01266 if (pc_types[i] & PTL_ONLY_ONE_TYPE)
01267
01268 j = pc_types[i] & ~PTL_ONLY_ONE_TYPE;
01269 else {
01270 for (j = 0; j < V_NUM_TYPES; j++)
01271 if (!strcmp(token, vt_names[j]))
01272 break;
01273
01274 if (j >= V_NUM_TYPES || !((1 << j) & pc_types[i])) {
01275 Com_Printf("CL_ParsePtlCmds: bad type \"%s\" specified (particle %s)\n", token, name);
01276 numPtlCmds--;
01277 break;
01278 }
01279
01280
01281 token = Com_EParse(text, errhead, name);
01282 if (!*text)
01283 return;
01284 }
01285
01286
01287 pc->type = j;
01288
01289 pcmdPos = (byte *)Com_AlignPtr(pcmdPos, pc->type);
01290 pc->ref = (int) (pcmdPos - pcmdData);
01291 pcmdPos += Com_EParseValue(pcmdPos, token, pc->type, 0, 0);
01292
01293
01294 break;
01295 }
01296
01297 if (i < PC_NUM_PTLCMDS)
01298 continue;
01299
01300 for (pp = pps; pp->string; pp++)
01301 if (!strcmp(token, pp->string)) {
01302
01303 token = Com_EParse(text, errhead, name);
01304 if (!*text)
01305 return;
01306
01307
01308 if (numPtlCmds >= MAX_PTLCMDS)
01309 Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
01310 pc = &ptlCmd[numPtlCmds++];
01311 pc->cmd = PC_PUSH;
01312 pc->type = pp->type;
01313
01314 pcmdPos = (byte *)Com_AlignPtr(pcmdPos, pc->type);
01315 pc->ref = (int) (pcmdPos - pcmdData);
01316 pcmdPos += Com_EParseValue(pcmdPos, token, pc->type, 0, 0);
01317
01318 pc = &ptlCmd[numPtlCmds++];
01319 pc->cmd = PC_POP;
01320 pc->type = pp->type;
01321 pc->ref = -((int)pp->ofs);
01322 break;
01323 }
01324
01325 if (!pp->string)
01326 Com_Printf("CL_ParsePtlCmds: unknown token \"%s\" ignored (particle %s)\n", token, name);
01327
01328 } while (*text);
01329
01330
01331 if (numPtlCmds >= MAX_PTLCMDS)
01332 Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
01333 pc = &ptlCmd[numPtlCmds++];
01334 memset(pc, 0, sizeof(*pc));
01335 }
01336
01344 int CL_ParseParticle (const char *name, const char **text)
01345 {
01346 const char *errhead = "CL_ParseParticle: unexpected end of file (particle ";
01347 ptlDef_t *pd;
01348 const char *token;
01349 int i, pos;
01350
01351
01352 for (i = 0; i < numPtlDefs; i++)
01353 if (!strcmp(name, ptlDef[i].name))
01354 break;
01355
01356 if (i < numPtlDefs) {
01357 Com_Printf("CL_ParseParticle: particle def \"%s\" with same name found, reset first one\n", name);
01358 pos = i;
01359 pd = &ptlDef[i];
01360 } else {
01361 if (numPtlDefs < MAX_PTLDEFS - 1) {
01362
01363 pos = numPtlDefs;
01364 pd = &ptlDef[numPtlDefs++];
01365 } else {
01366 Com_Printf("CL_ParseParticle: max particle definitions reached - skip the current one: '%s'\n", name);
01367 return -1;
01368 }
01369 }
01370 memset(pd, 0, sizeof(*pd));
01371
01372 Q_strncpyz(pd->name, name, sizeof(pd->name));
01373
01374
01375 token = Com_Parse(text);
01376
01377 if (!*text || *token != '{') {
01378 Com_Printf("CL_ParseParticle: particle def \"%s\" without body ignored\n", name);
01379 if (i == numPtlDefs)
01380 numPtlDefs--;
01381 return -1;
01382 }
01383
01384 do {
01385 token = Com_EParse(text, errhead, name);
01386 if (!*text)
01387 break;
01388 if (*token == '}')
01389 break;
01390
01391 for (i = 0; i < PF_NUM_PTLFUNCS; i++)
01392 if (!strcmp(token, pf_strings[i])) {
01393
01394 ptlCmd_t **pc;
01395
01396 pc = (ptlCmd_t **) ((byte *) pd + pf_values[i]);
01397 *pc = &ptlCmd[numPtlCmds];
01398
01399
01400 CL_ParsePtlCmds(name, text);
01401 break;
01402 }
01403
01404 if (i == PF_NUM_PTLFUNCS)
01405 Com_Printf("CL_ParseParticle: unknown token \"%s\" ignored (particle %s)\n", token, name);
01406
01407 } while (*text);
01408
01409
01410 if (!pd->init) {
01411 Com_Printf("CL_ParseParticle: particle definition %s without init function ignored\n", name);
01412 if (i == numPtlDefs)
01413 numPtlDefs--;
01414 return -1;
01415 }
01416 return pos;
01417 }
01418
01422 static void PTL_DebugSpawnMarker_f (void)
01423 {
01424 vec3_t worldOrigin;
01425
01426 if (Cmd_Argc() < 4) {
01427 Com_Printf("Usage: %s <x> <y> <z>\n", Cmd_Argv(0));
01428 return;
01429 }
01430
01431 worldOrigin[0] = atof(Cmd_Argv(1));
01432 worldOrigin[1] = atof(Cmd_Argv(2));
01433 worldOrigin[2] = atof(Cmd_Argv(3));
01434
01435 CL_ParticleSpawn("debug_marker", 0, worldOrigin, NULL, NULL);
01436 }
01437
01438 static void PTL_DebugList_f (void)
01439 {
01440 int i;
01441
01442 Com_Printf("%i particles\n", r_numParticles);
01443 for (i = 0; i < r_numParticles; i++) {
01444 const ptl_t *p = &r_particles[i];
01445 const ptlDef_t *def = p->ctrl;
01446 const value_t *pp = pps;
01447 if (!p->inuse)
01448 continue;
01449 Com_Printf("particle %i\n", i);
01450 Com_Printf(" name: %s\n", def->name);
01451 for (pp = pps; pp->string; pp++) {
01452 const char* value = "";
01453 if (!strcmp(pp->string, "image") && p->pic) {
01454 value = p->pic->name;
01455 } else if (!strcmp(pp->string, "model") && p->model) {
01456 value = p->model->name;
01457 } else if (!strcmp(pp->string, "program") && p->program) {
01458 value = p->program->name;
01459 } else {
01460 value = Com_ValueToStr(p, pp->type, pp->ofs);
01461 }
01462 Com_Printf(" %s: %s\n", pp->string, value);
01463 }
01464 }
01465 }
01466
01470 void CL_InitParticles (void)
01471 {
01472 cl_particleweather = Cvar_Get("cl_particleweather", "0", CVAR_ARCHIVE | CVAR_LATCH, "Switch the weather particles on or off");
01473 Cmd_AddCommand("debug_spawnmarker", PTL_DebugSpawnMarker_f, "Spawn a marker particle in the world at a given location");
01474 Cmd_AddCommand("debug_particlelist", PTL_DebugList_f, NULL);
01475 }