cl_particle.c

Go to the documentation of this file.
00001 
00006 /*
00007 Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 This program is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU General Public License
00011 as published by the Free Software Foundation; either version 2
00012 of the License, or (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018 See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
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     /* t and dt are not specified in particle definitions */
00177     /* but they can be used as references */
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             /* found a free slot */
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     /* register the art */
00302     switch (a->type) {
00303     case ART_PIC:
00304         {
00305             const char *imageName;
00306             /* only one image */
00307             if (a->name[0] != '+')
00308                 imageName = a->name;
00309             else /* load several frames */
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     /* search for the pic in the list */
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     /* check for an error */
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         /* a negative ref value is relative to the particle */
00395         return (byte*)p - cmd->ref;
00396     else
00397         /* data is stored on the global command data hunk */
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     /* test for null cmd */
00412     if (!cmd)
00413         return;
00414 
00415     /* run until finding PC_END */
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             /* pop an element off the stack */
00424             e = (byte *) stackPtr[--stackIdx] - cmdStack;
00425 
00426             i = RSTACK - cmd->ref;
00427             if (!i) {
00428                 /* normal stack reference */
00429                 cmdData = stackPtr[stackIdx];
00430                 cmd->type = stackType[stackIdx];
00431             } else {
00432                 /* stack reference to element of vector */
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             /* check for stack overflow */
00445             if (stackIdx >= MAX_STACK_DEPTH)
00446                 Com_Error(ERR_DROP, "CL_ParticleFunction: stack overflow");
00447 
00448             /* store the value in the stack */
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             /* check for stack underflow */
00457             if (stackIdx == 0)
00458                 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00459 
00460             /* get pics and models */
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             /* get different data */
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             /* check for stack underflow */
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             /* float based vector addition */
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             /* check for stack underflow */
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                 /* component wise multiplication */
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             /* scalar multiplication with scalar in second argument */
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             /* check for stack underflow */
00635             if (stackIdx < 2)
00636                 Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow");
00637 
00638             /* pop elements off the stack */
00639             /* amount of timed particles */
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             /* delta time */
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             /* check for stack underflow */
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     /* find the particle definition */
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     /* add the particle */
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     /* allocate particle */
00742     p = &r_particles[i];
00743     memset(p, 0, sizeof(*p));
00744 
00745     /* set basic values */
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     /* copy location */
00755     if (s) {
00756         VectorCopy(s, p->origin);
00757         VectorCopy(s, p->s);
00758     }
00759     /* copy velocity */
00760     if (v)
00761         VectorCopy(v, p->v);
00762     /* copy acceleration */
00763     if (a)
00764         VectorCopy(a, p->a);
00765 
00766     /* copy levelflags */
00767     p->levelFlags = levelFlags;
00768 
00769     /* run init function */
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             /* run round function */
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     /* advance time */
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     /* test for end of life */
00892     if (p->life && p->t >= p->life && !p->parent) {
00893         CL_ParticleFree(p);
00894         return;
00895     /* don't play the weather particles if a user don't want them
00896      * there can be a lot of weather particles - which might slow the computer
00897      * down - so i made them switchable */
00900     } else if (p->weather && !cl_particleweather->integer) {
00901         CL_ParticleFree(p);
00902         return;
00903     }
00904 
00905     /* kinematics */
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     /* run */
00914     CL_ParticleFunction(p, p->ctrl->run);
00915 
00916     /* think */
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     /* animate */
00923     while (p->fps && p->lastFrame * p->fps >= 1) {
00924         /* advance frame */
00925         p->frame++;
00926         if (p->frame > p->endFrame)
00927             p->frame = 0;
00928         p->lastFrame -= 1.0 / p->fps;
00929 
00930         /* load next frame */
00931         assert(p->pic);
00932         p->pic = CL_ParticleGetArt(p->pic->name, p->frame, ART_PIC);
00933     }
00934 
00935     /* fading */
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     /* this is useful for particles like weather effects that are on top of
00950      * some other brushes in higher level but should be visible in lower ones */
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     /* basic 'physics' for particles */
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         /* hit something solid */
00970         if (tr.fraction < 1.0 || tr.startsolid) {
00971             /* now execute the physics handler */
00972             if (p->ctrl->physics)
00973                 CL_ParticleFunction(p, p->ctrl->physics);
00974             /* let them stay on the ground until they fade out or die */
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     /* add light to the scene */
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                 /* first spawn? - then copy the parent values. We have to
01017                  * do this here and now earlier because projectile particles
01018                  * get these values set after spawn. */
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         /* get keyname */
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         /* parse value */
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                 /* found a normal particle value */
01095                 Com_EParseValue(ptl, token, pp->type, pp->ofs, pp->size);
01096                 break;
01097             }
01098         }
01099 
01100         if (!pp->string) {
01101             /* register art */
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             /* spawn a new particle */
01128             ptl = CL_ParticleSpawn(mp->ptl, mp->levelflags, mp->origin, NULL, NULL);
01129             if (!ptl) {
01130                 mp->nextTime = 0;
01131                 continue;
01132             }
01133 
01134             /* init the particle */
01135             CL_ParseMapParticle(ptl, mp->info, qfalse);
01136             CL_ParticleFunction(ptl, ptl->ctrl->init);
01137             CL_ParseMapParticle(ptl, mp->info, qtrue);
01138 
01139             /* prepare next spawning */
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     /* get it's body */
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                 /* allocate an new cmd */
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                 /* get parameter type */
01185                 token = Com_EParse(text, errhead, name);
01186                 if (!*text)
01187                     return;
01188 
01189                 /* operate on the top element on the stack */
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                     /* it's a variable reference */
01202                     token++;
01203 
01204                     /* we maybe have to modify it */
01205                     Q_strncpyz(baseComponentToken, token, sizeof(baseComponentToken));
01206 
01207                     /* check for component specifier */
01208                     len = strlen(baseComponentToken);
01209                     /* it's possible to change only the second value of e.g. a vector
01210                      * just defined e.g. 'size.2' to modify the second value of size */
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                         /* get single component */
01240                         if ((1 << pp->type) & V_VECS) {
01241                             const int component = (baseComponentToken[len - 1] - '1');
01242                             /* get the component we want to modify */
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                             /* go to component offset */
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                     /* set the values */
01260                     pc->type = pp->type;
01261                     pc->ref = -((int)pp->ofs);
01262                     break;
01263                 }
01264 
01265                 /* get the type */
01266                 if (pc_types[i] & PTL_ONLY_ONE_TYPE)
01267                     /* extract the real type */
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                     /* get the value */
01281                     token = Com_EParse(text, errhead, name);
01282                     if (!*text)
01283                         return;
01284                 }
01285 
01286                 /* set the values */
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 /*              Com_Printf("%s %s %i\n", vt_names[pc->type], token, pcmdPos - pc->ref, (char *)pc->ref); */
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                 /* get parameter */
01303                 token = Com_EParse(text, errhead, name);
01304                 if (!*text)
01305                     return;
01306 
01307                 /* translate set to a push and pop */
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     /* terminalize cmd chain */
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     /* search for particles with same name */
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             /* initialize the new particle */
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     /* get it's body */
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                 /* allocate the first particle command */
01394                 ptlCmd_t **pc;
01395 
01396                 pc = (ptlCmd_t **) ((byte *) pd + pf_values[i]);
01397                 *pc = &ptlCmd[numPtlCmds];
01398 
01399                 /* parse the commands */
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     /* check for an init function */
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 }

Generated by  doxygen 1.6.2