cl_localentity.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_localentity.h"
00028 #include "../sound/s_main.h"
00029 #include "../sound/s_mix.h"
00030 #include "cl_particle.h"
00031 #include "cl_actor.h"
00032 #include "cl_parse.h"
00033 #include "cl_hud.h"
00034 #include "../renderer/r_mesh_anim.h"
00035 #include "../../common/tracing.h"
00036 #include "../../common/grid.h"
00037 
00038 cvar_t *cl_le_debug;
00039 
00040 /*===========================================================================
00041 Local Model (LM) handling
00042 =========================================================================== */
00043 
00044 static inline void LE_GenerateInlineModelList (void)
00045 {
00046     le_t *le = NULL;
00047     int i = 0;
00048 
00049     while ((le = LE_GetNextInUse(le))) {
00050         if (le->model1 && le->inlineModelName[0] == '*')
00051             cl.leInlineModelList[i++] = le->inlineModelName;
00052     }
00053     cl.leInlineModelList[i] = NULL;
00054 }
00055 
00059 void CL_CompleteRecalcRouting (void)
00060 {
00061     le_t* le;
00062     int i;
00063 
00064     LE_GenerateInlineModelList();
00065 
00066     for (i = 0, le = cl.LEs; i < cl.numLEs; i++, le++)
00067         /* We ALWAYS check against a model, even if it isn't in use.
00068          * An unused model is NOT included in the inline list, so it doesn't get
00069          * traced against. */
00070         if (le->model1 && le->inlineModelName[0] == '*')
00071             Grid_RecalcRouting(cl.mapTiles, cl.mapData->map, le->inlineModelName, cl.leInlineModelList);
00072 }
00073 
00078 void CL_RecalcRouting (const le_t* le)
00079 {
00080     LE_GenerateInlineModelList();
00081     /* We ALWAYS check against a model, even if it isn't in use.
00082      * An unused model is NOT included in the inline list, so it doesn't get
00083      * traced against. */
00084     if (le->model1 && le->inlineModelName[0] == '*')
00085         Grid_RecalcRouting(cl.mapTiles, cl.mapData->map, le->inlineModelName, cl.leInlineModelList);
00086 
00087     CL_ActorConditionalMoveCalc(selActor);
00088 }
00089 
00090 static void LM_AddToSceneOrder (qboolean parents)
00091 {
00092     localModel_t *lm;
00093     entity_t ent;
00094     int i;
00095 
00096     for (i = 0, lm = cl.LMs; i < cl.numLMs; i++, lm++) {
00097         if (!lm->inuse)
00098             continue;
00099 
00100         /* check for visibility */
00101         if (!((1 << cl_worldlevel->integer) & lm->levelflags))
00102             continue;
00103 
00104         /* if we want to render the parents an this is a child (has a parent assigned)
00105          * then skip it */
00106         if (parents && lm->parent)
00107             continue;
00108 
00109         /* if we want to render the children and this is a parent (no further parent
00110          * assigned, then skip it. */
00111         if (!parents && lm->parent == NULL)
00112             continue;
00113 
00114         /* set entity values */
00115         memset(&ent, 0, sizeof(ent));
00116         assert(lm->model);
00117         ent.model = lm->model;
00118         ent.skinnum = lm->skin;
00119         VectorCopy(lm->scale, ent.scale);
00120 
00121         if (lm->parent) {
00123             ent.tagent = R_GetEntity(lm->parent->renderEntityNum);
00124             if (ent.tagent == NULL)
00125                 Com_Error(ERR_DROP, "Invalid entity num for local model (%s/%s): %i",
00126                         lm->model->name, lm->id, lm->parent->renderEntityNum);
00127             ent.tagname = lm->tagname;
00128         } else {
00129             R_EntitySetOrigin(&ent, lm->origin);
00130             VectorCopy(lm->origin, ent.oldorigin);
00131             VectorCopy(lm->angles, ent.angles);
00132 
00133             if (lm->animname[0] != '\0') {
00134                 ent.as = lm->as;
00135                 /* do animation */
00136                 R_AnimRun(&lm->as, ent.model, cls.frametime * 1000);
00137             } else {
00138                 ent.as.frame = lm->frame;
00139             }
00140         }
00141 
00142         /* renderflags like RF_PULSE */
00143         ent.flags = lm->renderFlags;
00144 
00145         /* add it to the scene */
00146         lm->renderEntityNum = R_AddEntity(&ent);
00147     }
00148 }
00149 
00156 void LM_AddToScene (void)
00157 {
00158     LM_AddToSceneOrder(qtrue);
00159     LM_AddToSceneOrder(qfalse);
00160 }
00161 
00165 static inline localModel_t *LM_Find (int entnum)
00166 {
00167     int i;
00168 
00169     for (i = 0; i < cl.numLMs; i++)
00170         if (cl.LMs[i].entnum == entnum)
00171             return &cl.LMs[i];
00172 
00173     return NULL;
00174 }
00175 
00181 qboolean LE_IsActor (const le_t *le)
00182 {
00183     assert(le);
00184     return le->type == ET_ACTOR || le->type == ET_ACTOR2x2 || le->type == ET_ACTORHIDDEN;
00185 }
00186 
00193 qboolean LE_IsLivingActor (const le_t *le)
00194 {
00195     assert(le);
00196     return LE_IsActor(le) && (LE_IsStunned(le) || !LE_IsDead(le));
00197 }
00198 
00205 qboolean LE_IsLivingAndVisibleActor (const le_t *le)
00206 {
00207     assert(le);
00208     if (le->invis)
00209         return qfalse;
00210 
00211     assert(le->type != ET_ACTORHIDDEN);
00212 
00213     return LE_IsLivingActor(le);
00214 }
00215 
00220 void LM_Register (void)
00221 {
00222     localModel_t *lm;
00223     int i;
00224 
00225     for (i = 0, lm = cl.LMs; i < cl.numLMs; i++, lm++) {
00226         /* register the model */
00227         lm->model = R_RegisterModelShort(lm->name);
00228         if (lm->animname[0]) {
00229             R_AnimChange(&lm->as, lm->model, lm->animname);
00230             if (!lm->as.change)
00231                 Com_Printf("LM_Register: Could not change anim of %s to '%s'\n",
00232                         lm->name, lm->animname);
00233         }
00234         if (!lm->model)
00235             lm->inuse = qfalse;
00236     }
00237 }
00238 
00239 void LE_SetThink (le_t *le, void (*think) (le_t *le))
00240 {
00241     Com_DPrintf(DEBUG_EVENTSYS, "LE_SetThink: Set think function for le %i to %p\n",
00242             le->entnum, think);
00243     le->think = think;
00244 }
00245 
00246 localModel_t *LM_GetByID (const char *id)
00247 {
00248     int i;
00249 
00250     if (id == NULL || id[0] == '\0')
00251         return NULL;
00252 
00253     for (i = 0; i < cl.numLMs; i++) {
00254         if (!strcmp(cl.LMs[i].id, id))
00255             return &cl.LMs[i];
00256     }
00257     return NULL;
00258 }
00259 
00273 localModel_t *LM_AddModel (const char *model, const vec3_t origin, const vec3_t angles, int entnum, int levelflags, int renderFlags, const vec3_t scale)
00274 {
00275     localModel_t *lm;
00276 
00277     lm = &cl.LMs[cl.numLMs++];
00278 
00279     if (cl.numLMs >= MAX_LOCALMODELS)
00280         Com_Error(ERR_DROP, "Too many local models\n");
00281 
00282     memset(lm, 0, sizeof(*lm));
00283     Q_strncpyz(lm->name, model, sizeof(lm->name));
00284     VectorCopy(origin, lm->origin);
00285     VectorCopy(angles, lm->angles);
00286     /* check whether there is already a model with that number */
00287     if (LM_Find(entnum))
00288         Com_Error(ERR_DROP, "Already a local model with the same id (%i) loaded\n", entnum);
00289     lm->entnum = entnum;
00290     lm->levelflags = levelflags;
00291     lm->renderFlags = renderFlags;
00292     lm->inuse = qtrue;
00293     VectorCopy(scale, lm->scale);
00294 
00295     return lm;
00296 }
00297 
00298 /*===========================================================================
00299 LE thinking
00300 =========================================================================== */
00301 
00305 void LE_ExecuteThink (le_t *le)
00306 {
00307     if (le->inuse && le->think) {
00308         Com_DPrintf(DEBUG_EVENTSYS, "LE_ExecuteThink: Execute think function %p for le %i\n",
00309                     le->think, le->entnum);
00310         le->think(le);
00311     }
00312 }
00313 
00323 void LE_Think (void)
00324 {
00325     le_t *le = NULL;
00326 
00327     if (cls.state != ca_active)
00328         return;
00329 
00330     while ((le = LE_GetNext(le))) {
00331         LE_ExecuteThink(le);
00332         /* do animation - even for invisible entities */
00333         R_AnimRun(&le->as, le->model1, cls.frametime * 1000);
00334     }
00335 }
00336 
00337 void LM_Think (void)
00338 {
00339     int i;
00340     localModel_t *lm;
00341 
00342     for (i = 0, lm = cl.LMs; i < cl.numLMs; i++, lm++) {
00343         if (lm->think)
00344             lm->think(lm);
00345     }
00346 }
00347 
00348 
00349 /*===========================================================================
00350  LE think functions
00351 =========================================================================== */
00352 
00353 static char retAnim[MAX_VAR];
00354 
00364 const char *LE_GetAnim (const char *anim, int right, int left, int state)
00365 {
00366     char *mod;
00367     qboolean akimbo;
00368     char animationIndex;
00369     const char *type;
00370     size_t length = sizeof(retAnim);
00371 
00372     if (!anim)
00373         return "";
00374 
00375     mod = retAnim;
00376 
00377     /* add crouched flag */
00378     if (state & STATE_CROUCHED) {
00379         *mod++ = 'c';
00380         length--;
00381     }
00382 
00383     /* determine relevant data */
00384     akimbo = qfalse;
00385     if (right == NONE) {
00386         animationIndex = '0';
00387         if (left == NONE)
00388             type = "item";
00389         else {
00390             type = INVSH_GetItemByIDX(left)->type;
00391             /* left hand grenades look OK with default anim; others don't */
00392             if (strcmp(type, "grenade"))
00393                 akimbo = qtrue;
00394         }
00395     } else {
00396         const objDef_t *od = INVSH_GetItemByIDX(right);
00397         animationIndex = od->animationIndex;
00398         type = od->type;
00399         if (left != NONE && !strcmp(od->type, "pistol") && !strcmp(INVSH_GetItemByIDX(left)->type, "pistol"))
00400             akimbo = qtrue;
00401     }
00402 
00403     if (!strncmp(anim, "stand", 5) || !strncmp(anim, "walk", 4)) {
00404         Q_strncpyz(mod, anim, length);
00405         mod += strlen(anim);
00406         *mod++ = animationIndex;
00407         *mod++ = 0;
00408     } else {
00409         Com_sprintf(mod, length, "%s_%s", anim, akimbo ? "pistol_d" : type);
00410     }
00411 
00412     return retAnim;
00413 }
00414 
00423 void LET_StartIdle (le_t * le)
00424 {
00425     /* hidden actors don't have models assigned, thus we can not change the
00426      * animation for any model */
00427     if (le->type != ET_ACTORHIDDEN) {
00428         if (LE_IsDead(le))
00429             R_AnimChange(&le->as, le->model1, va("dead%i", LE_GetAnimationIndexForDeath(le)));
00430         else if (LE_IsPaniced(le))
00431             R_AnimChange(&le->as, le->model1, "panic0");
00432         else
00433             R_AnimChange(&le->as, le->model1, LE_GetAnim("stand", le->right, le->left, le->state));
00434     }
00435 
00436     le->pathPos = le->pathLength = 0;
00437 
00438     /* keep this animation until something happens */
00439     LE_SetThink(le, NULL);
00440 }
00441 
00449 static void LE_PlaySoundFileForContents (le_t* le, int contents)
00450 {
00451     /* only play those water sounds when an actor jumps into the water - but not
00452      * if he enters carefully in crouched mode */
00453     if (le->state & ~STATE_CROUCHED) {
00454         if (contents & CONTENTS_WATER) {
00455             /* were we already in the water? */
00456             if (le->positionContents & CONTENTS_WATER) {
00457                 /* play water moving sound */
00458                 S_PlaySample(le->origin, cls.soundPool[SOUND_WATER_OUT], SOUND_ATTN_IDLE, SND_VOLUME_FOOTSTEPS);
00459             } else {
00460                 /* play water entering sound */
00461                 S_PlaySample(le->origin, cls.soundPool[SOUND_WATER_IN], SOUND_ATTN_IDLE, SND_VOLUME_FOOTSTEPS);
00462             }
00463             return;
00464         }
00465 
00466         if (le->positionContents & CONTENTS_WATER) {
00467             /* play water leaving sound */
00468             S_PlaySample(le->origin, cls.soundPool[SOUND_WATER_MOVE], SOUND_ATTN_IDLE, SND_VOLUME_FOOTSTEPS);
00469         }
00470     }
00471 }
00472 
00479 static void LE_PlaySoundFileAndParticleForSurface (le_t* le, const char *textureName)
00480 {
00481     const terrainType_t *t;
00482     vec3_t origin;
00483 
00484     t = Com_GetTerrainType(textureName);
00485     if (!t)
00486         return;
00487 
00488     /* origin might not be up-to-date here - but pos should be */
00489     PosToVec(le->pos, origin);
00490 
00493     if (t->particle) {
00494         /* check whether actor is visible */
00495         if (!LE_IsStunned(le) && LE_IsLivingAndVisibleActor(le))
00496             CL_ParticleSpawn(t->particle, 0, origin, NULL, NULL);
00497     }
00498     if (t->footStepSound) {
00499         s_sample_t *sample = S_LoadSample(t->footStepSound);
00500         Com_DPrintf(DEBUG_SOUND, "LE_PlaySoundFileAndParticleForSurface: volume %.2f\n", t->footStepVolume);
00501         S_PlaySample(origin, sample, SOUND_ATTN_STATIC, t->footStepVolume);
00502     }
00503 }
00504 
00508 int LE_ActorGetStepTime (const le_t *le, const pos3_t pos, const pos3_t oldPos, const int dir, const int speed)
00509 {
00510     if (dir != DIRECTION_FALL) {
00511         return (((dir & (CORE_DIRECTIONS - 1)) >= BASE_DIRECTIONS ? UNIT_SIZE * 1.41 : UNIT_SIZE) * 1000 / speed);
00512     } else {
00513         vec3_t start, dest;
00514         /* This needs to account for the distance of the fall. */
00515         Grid_PosToVec(cl.mapData->map, le->fieldSize, oldPos, start);
00516         Grid_PosToVec(cl.mapData->map, le->fieldSize, pos, dest);
00517         /* 1/1000th of a second per model unit in height change */
00518         return (start[2] - dest[2]);
00519     }
00520 }
00521 
00522 static void LE_PlayFootStepSound (le_t *le)
00523 {
00524     /* walking in water will not play the normal footstep sounds */
00525     if (!le->pathContents[le->pathPos]) {
00526         trace_t trace;
00527         vec3_t from, to;
00528 
00529         /* prepare trace vectors */
00530         PosToVec(le->pos, from);
00531         VectorCopy(from, to);
00532         /* we should really hit the ground with this */
00533         to[2] -= UNIT_HEIGHT;
00534 
00535         trace = CL_Trace(from, to, vec3_origin, vec3_origin, NULL, NULL, MASK_SOLID, cl_worldlevel->integer);
00536         if (trace.surface)
00537             LE_PlaySoundFileAndParticleForSurface(le, trace.surface->name);
00538     } else
00539         LE_PlaySoundFileForContents(le, le->pathContents[le->pathPos]);
00540 }
00541 
00542 static void LE_DoPathMove (le_t *le)
00543 {
00544     /* next part */
00545     const byte fulldv = le->path[le->pathPos];
00546     const byte dir = getDVdir(fulldv);
00547     const byte crouchingState = LE_IsCrouched(le) ? 1 : 0;
00548     /* newCrouchingState needs to be set to the current crouching state
00549      * and is possibly updated by PosAddDV. */
00550     byte newCrouchingState = crouchingState;
00551     PosAddDV(le->pos, newCrouchingState, fulldv);
00552 
00553     LE_PlayFootStepSound(le);
00554 
00555     /* only change the direction if the actor moves horizontally. */
00556     if (dir < CORE_DIRECTIONS || dir >= FLYING_DIRECTIONS)
00557         le->dir = dir & (CORE_DIRECTIONS - 1);
00558     le->angles[YAW] = directionAngles[le->dir];
00559     le->startTime = le->endTime;
00560     /* check for straight movement or diagonal movement */
00561     assert(le->speed[le->pathPos]);
00562     le->endTime += LE_ActorGetStepTime(le, le->pos, le->oldPos, dir, le->speed[le->pathPos]);
00563 
00564     le->positionContents = le->pathContents[le->pathPos];
00565     le->pathPos++;
00566 }
00567 
00571 void LE_DoEndPathMove (le_t *le)
00572 {
00573     le_t *floor;
00574 
00575     /* Verify the current position */
00576     if (!VectorCompare(le->pos, le->newPos))
00577         Com_Error(ERR_DROP, "LE_DoEndPathMove: Actor movement is out of sync: %i:%i:%i should be %i:%i:%i (step %i of %i) (team %i)",
00578                 le->pos[0], le->pos[1], le->pos[2], le->newPos[0], le->newPos[1], le->newPos[2], le->pathPos, le->pathLength, le->team);
00579 
00580     CL_ActorConditionalMoveCalc(le);
00581 
00582     /* link any floor container into the actor temp floor container */
00583     floor = LE_Find(ET_ITEM, le->pos);
00584     if (floor)
00585         FLOOR(le) = FLOOR(floor);
00586 
00587     LE_SetThink(le, LET_StartIdle);
00588     LE_ExecuteThink(le);
00589     LE_Unlock(le);
00590 }
00591 
00598 static void LE_ActorBodyHit (const le_t * le, const vec3_t impact, int normal)
00599 {
00600     if (le->teamDef) {
00601         /* Spawn "hit_particle" if defined in teamDef. */
00602         if (le->teamDef->hitParticle[0] != '\0')
00603             CL_ParticleSpawn(le->teamDef->hitParticle, 0, impact, bytedirs[normal], NULL);
00604     }
00605 }
00606 
00612 static void LET_PathMove (le_t * le)
00613 {
00614     float frac;
00615     vec3_t start, dest, delta;
00616 
00617     /* check for start of the next step */
00618     if (cl.time < le->startTime)
00619         return;
00620 
00621     /* move ahead */
00622     while (cl.time >= le->endTime) {
00623         /* Ensure that we are displayed where we are supposed to be, in case the last frame came too quickly. */
00624         Grid_PosToVec(cl.mapData->map, le->fieldSize, le->pos, le->origin);
00625 
00626         /* Record the last position of movement calculations. */
00627         VectorCopy(le->pos, le->oldPos);
00628 
00629         if (le->pathPos < le->pathLength) {
00630             LE_DoPathMove(le);
00631         } else {
00632             LE_DoEndPathMove(le);
00633             return;
00634         }
00635     }
00636 
00637     /* interpolate the position */
00638     Grid_PosToVec(cl.mapData->map, le->fieldSize, le->oldPos, start);
00639     Grid_PosToVec(cl.mapData->map, le->fieldSize, le->pos, dest);
00640     VectorSubtract(dest, start, delta);
00641 
00642     frac = (float) (cl.time - le->startTime) / (float) (le->endTime - le->startTime);
00643 
00644     /* calculate the new interpolated actor origin in the world */
00645     VectorMA(start, frac, delta, le->origin);
00646 }
00647 
00653 void LET_StartPathMove (le_t *le)
00654 {
00655     /* initial animation or animation change */
00656     R_AnimChange(&le->as, le->model1, LE_GetAnim("walk", le->right, le->left, le->state));
00657     if (!le->as.change)
00658         Com_Printf("LET_StartPathMove: Could not change anim of le: %i, team: %i, pnum: %i\n",
00659             le->entnum, le->team, le->pnum);
00660 
00661     LE_SetThink(le, LET_PathMove);
00662     LE_ExecuteThink(le);
00663 }
00664 
00668 static void LET_Projectile (le_t * le)
00669 {
00670     if (cl.time >= le->endTime) {
00671         vec3_t impact;
00672         VectorCopy(le->origin, impact);
00673         CL_ParticleFree(le->ptl);
00674         /* don't run the think function again */
00675         le->inuse = qfalse;
00676         if (le->ref1 && le->ref1[0] != '\0') {
00677             VectorCopy(le->ptl->s, impact);
00678             le->ptl = CL_ParticleSpawn(le->ref1, 0, impact, bytedirs[le->dir], NULL);
00679             VecToAngles(bytedirs[le->state], le->ptl->angles);
00680         }
00681         if (le->ref2 && le->ref2[0] != '\0') {
00682             s_sample_t *sample = S_LoadSample(le->ref2);
00683             S_PlaySample(impact, sample, le->fd->impactAttenuation, SND_VOLUME_WEAPONS);
00684         }
00685         if (le->ref3) {
00686             /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */
00689             if (le->fd->obj->dmgtype != csi.damStunGas)
00690                 LE_ActorBodyHit(le->ref3, impact, le->dir);
00691             CL_ActorPlaySound(le->ref3, SND_HURT);
00692         }
00693     } else if (CL_OutsideMap(le->ptl->s, UNIT_SIZE * 10)) {
00694         le->endTime = cl.time;
00695         CL_ParticleFree(le->ptl);
00696         /* don't run the think function again */
00697         le->inuse = qfalse;
00698     }
00699 }
00700 
00701 /*===========================================================================
00702  LE Special Effects
00703 =========================================================================== */
00704 
00705 void LE_AddProjectile (const fireDef_t *fd, int flags, const vec3_t muzzle, const vec3_t impact, int normal, le_t *leVictim)
00706 {
00707     le_t *le;
00708     vec3_t delta;
00709     float dist;
00710 
00711     /* add le */
00712     le = LE_Add(0);
00713     if (!le)
00714         return;
00715     le->invis = !cl_leshowinvis->integer;
00716     /* bind particle */
00717     le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle, NULL, NULL);
00718     if (!le->ptl) {
00719         le->inuse = qfalse;
00720         return;
00721     }
00722 
00723     /* calculate parameters */
00724     VectorSubtract(impact, muzzle, delta);
00725     dist = VectorLength(delta);
00726 
00727     VecToAngles(delta, le->ptl->angles);
00728     /* direction - bytedirs index */
00729     le->dir = normal;
00730     le->fd = fd;
00731 
00732     /* infinite speed projectile? */
00733     if (!fd->speed) {
00734         le->inuse = qfalse;
00735         le->ptl->size[0] = dist;
00736         VectorMA(muzzle, 0.5, delta, le->ptl->s);
00737         if (flags & (SF_IMPACT | SF_BODY) || (fd->splrad && !fd->bounce)) {
00738             ptl_t *ptl = NULL;
00739             const float *dir = bytedirs[le->dir];
00740             if (flags & SF_BODY) {
00741                 if (fd->hitBodySound[0]) {
00742                     s_sample_t *sample = S_LoadSample(fd->hitBodySound);
00743                     S_PlaySample(le->origin, sample, le->fd->impactAttenuation, SND_VOLUME_WEAPONS);
00744                 }
00745                 if (fd->hitBody[0])
00746                     ptl = CL_ParticleSpawn(fd->hitBody, 0, impact, dir, NULL);
00747 
00748                 /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */
00751                 if (leVictim) {
00752                     if (fd->obj->dmgtype != csi.damStunGas)
00753                         LE_ActorBodyHit(leVictim, impact, le->dir);
00754                     CL_ActorPlaySound(leVictim, SND_HURT);
00755                 }
00756             } else {
00757                 if (fd->impactSound[0]) {
00758                     s_sample_t *sample = S_LoadSample(fd->impactSound);
00759                     S_PlaySample(le->origin, sample, le->fd->impactAttenuation, SND_VOLUME_WEAPONS);
00760                 }
00761                 if (fd->impact[0])
00762                     ptl = CL_ParticleSpawn(fd->impact, 0, impact, dir, NULL);
00763             }
00764             if (ptl)
00765                 VecToAngles(dir, ptl->angles);
00766         }
00767         return;
00768     }
00769     /* particle properties */
00770     VectorScale(delta, fd->speed / dist, le->ptl->v);
00771     le->endTime = cl.time + 1000 * dist / fd->speed;
00772 
00773     /* think function */
00774     if (flags & SF_BODY) {
00775         le->ref1 = fd->hitBody;
00776         le->ref2 = fd->hitBodySound;
00777         le->ref3 = leVictim;
00778     } else if (flags & SF_IMPACT || (fd->splrad && !fd->bounce)) {
00779         le->ref1 = fd->impact;
00780         le->ref2 = fd->impactSound;
00781     } else {
00782         le->ref1 = NULL;
00783         if (flags & SF_BOUNCING)
00784             le->ref2 = fd->bounceSound;
00785     }
00786 
00787     LE_SetThink(le, LET_Projectile);
00788     LE_ExecuteThink(le);
00789 }
00790 
00798 static objDef_t *LE_BiggestItem (const invList_t *ic)
00799 {
00800     objDef_t *max;
00801     int maxSize = 0;
00802 
00803     for (max = ic->item.t; ic; ic = ic->next) {
00804         const int size = INVSH_ShapeSize(ic->item.t->shape);
00805         if (size > maxSize) {
00806             max = ic->item.t;
00807             maxSize = size;
00808         }
00809     }
00810 
00811     /* there must be an item in the invList_t */
00812     assert(max);
00813     return max;
00814 }
00815 
00820 void LE_PlaceItem (le_t *le)
00821 {
00822     le_t *actor = NULL;
00823 
00824     assert(LE_IsItem(le));
00825 
00826     /* search owners (there can be many, some of them dead) */
00827     while ((actor = LE_GetNextInUse(actor))) {
00828         if ((actor->type == ET_ACTOR || actor->type == ET_ACTOR2x2)
00829          && VectorCompare(actor->pos, le->pos)) {
00830             if (FLOOR(le))
00831                 FLOOR(actor) = FLOOR(le);
00832         }
00833     }
00834 
00835     /* the le is an ET_ITEM entity, this entity is there to render dropped items
00836      * if there are no items in the floor container, this entity can be
00837      * deactivated */
00838     if (FLOOR(le)) {
00839         const objDef_t *biggest = LE_BiggestItem(FLOOR(le));
00840         le->model1 = cls.modelPool[biggest->idx];
00841         if (!le->model1)
00842             Com_Error(ERR_DROP, "Model for item %s is not precached in the cls.model_weapons array",
00843                 biggest->id);
00844         Grid_PosToVec(cl.mapData->map, le->fieldSize, le->pos, le->origin);
00845         VectorSubtract(le->origin, biggest->center, le->origin);
00846         le->angles[ROLL] = 90;
00847         /*le->angles[YAW] = 10*(int)(le->origin[0] + le->origin[1] + le->origin[2]) % 360; */
00848         le->origin[2] -= GROUND_DELTA;
00849     } else {
00850         /* If no items in floor inventory, don't draw this le - the container is
00851          * maybe empty because an actor picked up the last items here */
00852         le->removeNextFrame = qtrue;
00853     }
00854 }
00855 
00863 void LE_AddGrenade (const fireDef_t *fd, int flags, const vec3_t muzzle, const vec3_t v0, int dt, le_t* leVictim)
00864 {
00865     le_t *le;
00866     vec3_t accel;
00867 
00868     /* add le */
00869     le = LE_Add(0);
00870     if (!le)
00871         return;
00872     le->invis = !cl_leshowinvis->integer;
00873 
00874     /* bind particle */
00875     VectorSet(accel, 0, 0, -GRAVITY);
00876     le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle, v0, accel);
00877     if (!le->ptl) {
00878         le->inuse = qfalse;
00879         return;
00880     }
00881     /* particle properties */
00882     VectorSet(le->ptl->angles, 360 * crand(), 360 * crand(), 360 * crand());
00883     VectorSet(le->ptl->omega, 500 * crand(), 500 * crand(), 500 * crand());
00884 
00885     /* think function */
00886     if (flags & SF_BODY) {
00887         le->ref1 = fd->hitBody;
00888         le->ref2 = fd->hitBodySound;
00889         le->ref3 = leVictim;
00890     } else if (flags & SF_IMPACT || (fd->splrad && !fd->bounce)) {
00891         le->ref1 = fd->impact;
00892         le->ref2 = fd->impactSound;
00893     } else {
00894         le->ref1 = NULL;
00895         if (flags & SF_BOUNCING)
00896             le->ref2 = fd->bounceSound;
00897     }
00898 
00899     le->endTime = cl.time + dt;
00900     /* direction - bytedirs index (0,0,1) */
00901     le->dir = 5;
00902     le->fd = fd;
00903     assert(fd);
00904     LE_SetThink(le, LET_Projectile);
00905     LE_ExecuteThink(le);
00906 }
00907 
00912 qboolean LE_BrushModelAction (le_t * le, entity_t * ent)
00913 {
00914     switch (le->type) {
00915     case ET_ROTATING:
00916     case ET_DOOR:
00917         /* These cause the model to render correctly */
00918         VectorCopy(ent->mins, le->mins);
00919         VectorCopy(ent->maxs, le->maxs);
00920         VectorCopy(*R_EntityGetOrigin(ent), le->origin);
00921         VectorCopy(ent->angles, le->angles);
00922         break;
00923     case ET_BREAKABLE:
00924         break;
00925     default:
00926         break;
00927     }
00928 
00929     return qtrue;
00930 }
00931 
00932 void LET_BrushModel (le_t *le)
00933 {
00934     /* the speed is e.g. used to determine how fast a rotation will be */
00935     if (cl.time - le->thinkDelay < le->speed[0]) {
00936         le->thinkDelay = cl.time;
00937         return;
00938     }
00939 
00940     if (le->type == ET_ROTATING) {
00941         const float angle = le->angles[le->dir] + (1.0 / le->rotationSpeed);
00942         le->angles[le->dir] = (angle >= 360.0 ? angle - 360.0 : angle);
00943     }
00944 }
00945 
00946 void LMT_Init (localModel_t* localModel)
00947 {
00948     if (localModel->target[0] != '\0') {
00949         localModel->parent = LM_GetByID(localModel->target);
00950         if (!localModel->parent)
00951             Com_Error(ERR_DROP, "Could not find local model entity with the id: '%s'.", localModel->target);
00952     }
00953 
00954     /* no longer needed */
00955     localModel->think = NULL;
00956 }
00957 
00962 void LE_AddAmbientSound (const char *sound, const vec3_t origin, int levelflags, float volume, float attenuation)
00963 {
00964     le_t* le;
00965     s_sample_t* sample;
00966 
00967     if (strstr(sound, "sound/"))
00968         sound += 6;
00969 
00970     sample = S_LoadSample(sound);
00971     if (!sample)
00972         return;
00973 
00974     le = LE_Add(0);
00975     if (!le) {
00976         Com_Printf("Could not add ambient sound entity\n");
00977         return;
00978     }
00979     le->type = ET_SOUND;
00980     le->sample = sample;
00981     VectorCopy(origin, le->origin);
00982     le->invis = !cl_leshowinvis->integer;
00983     le->levelflags = levelflags;
00984     le->attenuation = attenuation;
00985 
00986     if (volume < 0.0f || volume > 1.0f) {
00987         le->volume = SND_VOLUME_DEFAULT;
00988         Com_Printf("Invalid volume for local entity given - only values between 0.0 and 1.0 are valid\n");
00989     } else {
00990         le->volume = volume;
00991     }
00992 
00993     Com_DPrintf(DEBUG_SOUND, "Add ambient sound '%s' with volume %f\n", sound, volume);
00994 }
00995 
00996 /*===========================================================================
00997  LE Management functions
00998 =========================================================================== */
00999 
01005 le_t *LE_Add (int entnum)
01006 {
01007     le_t *le = NULL;
01008 
01009     while ((le = LE_GetNext(le))) {
01010         if (!le->inuse)
01011             /* found a free LE */
01012             break;
01013     }
01014 
01015     /* list full, try to make list longer */
01016     if (!le) {
01017         if (cl.numLEs >= MAX_EDICTS) {
01018             /* no free LEs */
01019             Com_Error(ERR_DROP, "Too many LEs");
01020         }
01021 
01022         /* list isn't too long */
01023         le = &cl.LEs[cl.numLEs];
01024         cl.numLEs++;
01025     }
01026 
01027     /* initialize the new LE */
01028     memset(le, 0, sizeof(*le));
01029     le->inuse = qtrue;
01030     le->entnum = entnum;
01031     le->fieldSize = ACTOR_SIZE_NORMAL;
01032     return le;
01033 }
01034 
01035 void _LE_NotFoundError (const int entnum, const char *file, const int line)
01036 {
01037     Cmd_ExecuteString("debug_listle");
01038     Com_Error(ERR_DROP, "LE_NotFoundError: Could not get LE with entnum %i (%s:%i)\n", entnum, file, line);
01039 }
01040 
01048 void LE_CenterView (const le_t *le)
01049 {
01050     /* if (cl_centerview->integer == 1 && cl.actTeam != cls.team) */
01051     if (!cl_centerview->integer)
01052         return;
01053 
01054     assert(le);
01055     Cvar_SetValue("cl_worldlevel", le->pos[2]);
01056     VectorCopy(le->origin, cl.cam.origin);
01057 }
01058 
01064 le_t *LE_Get (int entnum)
01065 {
01066     le_t *le = NULL;
01067 
01068     if (entnum == SKIP_LOCAL_ENTITY)
01069         return NULL;
01070 
01071     while ((le = LE_GetNextInUse(le))) {
01072         if (le->entnum == entnum)
01073             /* found the LE */
01074             return le;
01075     }
01076 
01077     /* didn't find it */
01078     return NULL;
01079 }
01080 
01086 qboolean LE_IsLocked (int entnum)
01087 {
01088     le_t *le = LE_Get(entnum);
01089     return (le != NULL && le->locked);
01090 }
01091 
01099 void LE_Lock (le_t *le)
01100 {
01101     if (le->locked)
01102         Com_Error(ERR_DROP, "LE_Lock: Trying to lock %i which is already locked\n", le->entnum);
01103 
01104     le->locked = qtrue;
01105 }
01106 
01118 void LE_Unlock (le_t *le)
01119 {
01120     if (!le->locked)
01121         Com_Error(ERR_DROP, "LE_Unlock: Trying to unlock %i which is already unlocked\n", le->entnum);
01122 
01123     le->locked = qfalse;
01124 }
01125 
01130 le_t *LE_GetFromPos (const pos3_t pos)
01131 {
01132     le_t *le = NULL;
01133 
01134     while ((le = LE_GetNextInUse(le))) {
01135         if (VectorCompare(le->pos, pos))
01136             return le;
01137     }
01138 
01139     /* didn't find it */
01140     return NULL;
01141 }
01142 
01147 le_t* LE_GetNext (le_t* lastLE)
01148 {
01149     le_t* endOfLEs = &cl.LEs[cl.numLEs];
01150     le_t* le;
01151 
01152     if (!cl.numLEs)
01153         return NULL;
01154 
01155     if (!lastLE)
01156         return cl.LEs;
01157 
01158     assert(lastLE >= cl.LEs);
01159     assert(lastLE < endOfLEs);
01160 
01161     le = lastLE;
01162 
01163     le++;
01164     if (le >= endOfLEs)
01165         return NULL;
01166     else
01167         return le;
01168 }
01169 
01176 le_t* LE_GetNextInUse (le_t* lastLE)
01177 {
01178     le_t* le = lastLE;
01179 
01180     while ((le = LE_GetNext(le))) {
01181         if (le->inuse)
01182             break;
01183     }
01184     return le;
01185 }
01186 
01194 le_t *LE_FindRadius (le_t *from, const vec3_t org, float rad, entity_type_t type)
01195 {
01196     le_t *le = from;
01197 
01198     while ((le = LE_GetNextInUse(le))) {
01199         int j;
01200         vec3_t eorg;
01201         for (j = 0; j < 3; j++)
01202             eorg[j] = org[j] - (le->origin[j] + (le->mins[j] + le->maxs[j]) * 0.5);
01203         if (VectorLength(eorg) > rad)
01204             continue;
01205         if (type != ET_NULL && le->type != type)
01206             continue;
01207         return le;
01208     }
01209 
01210     return NULL;
01211 }
01212 
01218 le_t *LE_Find (int type, const pos3_t pos)
01219 {
01220     le_t *le = NULL;
01221 
01222     while ((le = LE_GetNextInUse(le))) {
01223         if (le->type == type && VectorCompare(le->pos, pos))
01224             /* found the LE */
01225             return le;
01226     }
01227 
01228     /* didn't find it */
01229     return NULL;
01230 }
01231 
01233 #define ModelOffset(i, target) (target[0]=(i-1)*(UNIT_SIZE+BOX_DELTA_WIDTH)/2, target[1]=(i-1)*(UNIT_SIZE+BOX_DELTA_LENGTH)/2, target[2]=0)
01234 
01242 static inline qboolean LE_IsOriginBrush (const le_t *const le)
01243 {
01244     return (le->type == ET_DOOR || le->type == ET_ROTATING);
01245 }
01246 
01252 void LE_AddToScene (void)
01253 {
01254     le_t *le;
01255     entity_t ent;
01256     vec3_t modelOffset;
01257     int i;
01258 
01259     for (i = 0, le = cl.LEs; i < cl.numLEs; i++, le++) {
01260         if (le->removeNextFrame) {
01261             le->inuse = qfalse;
01262             le->removeNextFrame = qfalse;
01263         }
01264         if (le->inuse && !le->invis) {
01265             if (le->contents & CONTENTS_SOLID) {
01266                 if (!((1 << cl_worldlevel->integer) & le->levelflags))
01267                     continue;
01268             } else if (le->contents & CONTENTS_DETAIL) {
01269                 /* show them always */
01270             } else if (le->pos[2] > cl_worldlevel->integer)
01271                 continue;
01272 
01273             memset(&ent, 0, sizeof(ent));
01274 
01275             ent.alpha = le->alpha;
01276 
01277             VectorCopy(le->angles, ent.angles);
01278             ent.model = le->model1;
01279             ent.skinnum = le->skinnum;
01280 
01281             switch (le->contents) {
01282             /* Only breakables do not use their origin; func_doors and func_rotating do!!!
01283              * But none of them have animations. */
01284             case CONTENTS_SOLID:
01285             case CONTENTS_DETAIL: /* they use mins/maxs */
01286                 break;
01287             default:
01288                 /* set entity values */
01289                 R_EntitySetOrigin(&ent, le->origin);
01290                 VectorCopy(le->origin, ent.oldorigin);
01291                 /* store animation values */
01292                 ent.as = le->as;
01293                 break;
01294             }
01295 
01296             if (LE_IsOriginBrush(le)) {
01297                 ent.isOriginBrushModel = qtrue;
01298                 VectorCopy(le->angles, ent.angles);
01299                 R_EntitySetOrigin(&ent, le->origin);
01300                 VectorCopy(le->origin, ent.oldorigin);
01301             }
01302 
01303             /* Offset the model to be inside the cursor box */
01304             switch (le->fieldSize) {
01305             case ACTOR_SIZE_NORMAL:
01306             case ACTOR_SIZE_2x2:
01307                 ModelOffset(le->fieldSize, modelOffset);
01308                 R_EntityAddToOrigin(&ent, modelOffset);
01309                 VectorAdd(ent.oldorigin, modelOffset, ent.oldorigin);
01310                 break;
01311             default:
01312                 break;
01313             }
01314 
01315             /* call add function */
01316             /* if it returns false, don't draw */
01317             if (le->addFunc)
01318                 if (!le->addFunc(le, &ent))
01319                     continue;
01320 
01321             /* add it to the scene */
01322             R_AddEntity(&ent);
01323 
01324             if (cl_le_debug->integer)
01325                 CL_ParticleSpawn("cross", 0, le->origin, NULL, NULL);
01326         }
01327     }
01328 }
01329 
01334 void LE_Cleanup (void)
01335 {
01336     int i;
01337     le_t *le;
01338 
01339     Com_DPrintf(DEBUG_CLIENT, "LE_Cleanup: Clearing up to %i unused LE inventories\n", cl.numLEs);
01340     for (i = cl.numLEs - 1, le = &cl.LEs[cl.numLEs - 1]; i >= 0; i--, le--) {
01341         if (!le->inuse)
01342             continue;
01343         if (LE_IsActor(le))
01344             CL_ActorCleanup(le);
01345         else if (LE_IsItem(le))
01346             cls.i.EmptyContainer(&cls.i, &le->i, INVDEF(csi.idFloor));
01347 
01348         le->inuse = qfalse;
01349     }
01350 }
01351 
01352 #ifdef DEBUG
01353 
01356 void LE_List_f (void)
01357 {
01358     int i;
01359     le_t *le;
01360 
01361     Com_Printf("number | entnum | type | inuse | invis | pnum | team | size |  HP | state | level | model/ptl\n");
01362     for (i = 0, le = cl.LEs; i < cl.numLEs; i++, le++) {
01363         Com_Printf("#%5i | #%5i | %4i | %5i | %5i | %4i | %4i | %4i | %3i | %5i | %5i | ",
01364             i, le->entnum, le->type, le->inuse, le->invis, le->pnum, le->team,
01365             le->fieldSize, le->HP, le->state, le->levelflags);
01366         if (le->type == ET_PARTICLE) {
01367             if (le->ptl)
01368                 Com_Printf("%s\n", le->ptl->ctrl->name);
01369             else
01370                 Com_Printf("no ptl\n");
01371         } else if (le->model1)
01372             Com_Printf("%s\n", le->model1->name);
01373         else
01374             Com_Printf("no mdl\n");
01375     }
01376 }
01377 
01381 void LM_List_f (void)
01382 {
01383     int i;
01384     localModel_t *lm;
01385 
01386     Com_Printf("number | entnum | skin | frame | lvlflg | renderflags | origin          | name\n");
01387     for (i = 0, lm = cl.LMs; i < cl.numLMs; i++, lm++) {
01388         Com_Printf("#%5i | #%5i | #%3i | #%4i | %6i | %11i | %5.0f:%5.0f:%3.0f | %s\n",
01389             i, lm->entnum, lm->skin, lm->frame, lm->levelflags, lm->renderFlags,
01390             lm->origin[0], lm->origin[1], lm->origin[2], lm->name);
01391     }
01392 }
01393 
01394 #endif
01395 
01396 /*===========================================================================
01397  LE Tracing
01398 =========================================================================== */
01399 
01401 typedef struct {
01402     vec3_t boxmins, boxmaxs;    
01403     const float *mins, *maxs;   
01404     const float *start, *end;
01405     trace_t trace;
01406     const le_t *passle, *passle2;       
01407     int contentmask;            
01408 } moveclip_t;
01409 
01421 static int32_t CL_HullForEntity (const le_t *le, int *tile, vec3_t rmaShift, vec3_t angles)
01422 {
01423     /* special case for bmodels */
01424     if (le->contents & CONTENTS_SOLID) {
01425         const cBspModel_t *model = cl.model_clip[le->modelnum1];
01426         /* special value for bmodel */
01427         assert(le->modelnum1 < MAX_MODELS);
01428         if (!model)
01429             Com_Error(ERR_DROP, "CL_HullForEntity: Error - le with NULL bmodel (%i)\n", le->type);
01430         *tile = model->tile;
01431         VectorCopy(le->angles, angles);
01432         VectorCopy(model->shift, rmaShift);
01433         return model->headnode;
01434     } else {
01435         /* might intersect, so do an exact clip */
01436         *tile = 0;
01437         VectorCopy(vec3_origin, angles);
01438         VectorCopy(vec3_origin, rmaShift);
01439         return CM_HeadnodeForBox(&(cl.mapTiles->mapTiles[*tile]), le->mins, le->maxs);
01440     }
01441 }
01442 
01448 static void CL_ClipMoveToLEs (moveclip_t * clip)
01449 {
01450     le_t *le = NULL;
01451 
01452     if (clip->trace.allsolid)
01453         return;
01454 
01455     while ((le = LE_GetNextInUse(le))) {
01456         int tile = 0;
01457         trace_t trace;
01458         int32_t headnode;
01459         vec3_t angles;
01460         vec3_t origin, shift;
01461 
01462         if (!(le->contents & clip->contentmask))
01463             continue;
01464         if (le == clip->passle || le == clip->passle2)
01465             continue;
01466 
01467         headnode = CL_HullForEntity(le, &tile, shift, angles);
01468         assert(headnode < MAX_MAP_NODES);
01469 
01470         VectorCopy(le->origin, origin);
01471 
01472         trace = CM_HintedTransformedBoxTrace(&(cl.mapTiles->mapTiles[tile]), clip->start, clip->end, clip->mins, clip->maxs,
01473                 headnode, clip->contentmask, 0, origin, angles, shift, 1.0);
01474 
01475         if (trace.fraction < clip->trace.fraction) {
01476             qboolean oldStart;
01477 
01478             /* make sure we keep a startsolid from a previous trace */
01479             oldStart = clip->trace.startsolid;
01480             trace.le = le;
01481             clip->trace = trace;
01482             clip->trace.startsolid |= oldStart;
01483         /* if true, plane is not valid */
01484         } else if (trace.allsolid) {
01485             trace.le = le;
01486             clip->trace = trace;
01487         /* if true, the initial point was in a solid area */
01488         } else if (trace.startsolid) {
01489             trace.le = le;
01490             clip->trace.startsolid = qtrue;
01491         }
01492     }
01493 }
01494 
01495 
01507 static inline void CL_TraceBounds (const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
01508 {
01509     int i;
01510 
01511     for (i = 0; i < 3; i++) {
01512         if (end[i] > start[i]) {
01513             boxmins[i] = start[i] + mins[i] - 1;
01514             boxmaxs[i] = end[i] + maxs[i] + 1;
01515         } else {
01516             boxmins[i] = end[i] + mins[i] - 1;
01517             boxmaxs[i] = start[i] + maxs[i] + 1;
01518         }
01519     }
01520 }
01521 
01537 trace_t CL_Trace (const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, const le_t * passle, le_t * passle2, int contentmask, int worldLevel)
01538 {
01539     moveclip_t clip;
01540 
01541     /* clip to world */
01542     clip.trace = CM_CompleteBoxTrace(cl.mapTiles, start, end, mins, maxs, (1 << (worldLevel + 1)) - 1, contentmask, 0);
01543     clip.trace.le = NULL;
01544     if (clip.trace.fraction == 0)
01545         return clip.trace;      /* blocked by the world */
01546 
01547     clip.contentmask = contentmask;
01548     clip.start = start;
01549     clip.end = end;
01550     clip.mins = mins;
01551     clip.maxs = maxs;
01552     clip.passle = passle;
01553     clip.passle2 = passle2;
01554 
01555     /* create the bounding box of the entire move */
01556     CL_TraceBounds(start, mins, maxs, end, clip.boxmins, clip.boxmaxs);
01557 
01558     /* clip to other solid entities */
01559     CL_ClipMoveToLEs(&clip);
01560 
01561     return clip.trace;
01562 }

Generated by  doxygen 1.6.2