00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "../client.h"
00027 #include "cl_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
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
00068
00069
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
00082
00083
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
00101 if (!((1 << cl_worldlevel->integer) & lm->levelflags))
00102 continue;
00103
00104
00105
00106 if (parents && lm->parent)
00107 continue;
00108
00109
00110
00111 if (!parents && lm->parent == NULL)
00112 continue;
00113
00114
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
00136 R_AnimRun(&lm->as, ent.model, cls.frametime * 1000);
00137 } else {
00138 ent.as.frame = lm->frame;
00139 }
00140 }
00141
00142
00143 ent.flags = lm->renderFlags;
00144
00145
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
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
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
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
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
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
00378 if (state & STATE_CROUCHED) {
00379 *mod++ = 'c';
00380 length--;
00381 }
00382
00383
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
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
00426
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
00439 LE_SetThink(le, NULL);
00440 }
00441
00449 static void LE_PlaySoundFileForContents (le_t* le, int contents)
00450 {
00451
00452
00453 if (le->state & ~STATE_CROUCHED) {
00454 if (contents & CONTENTS_WATER) {
00455
00456 if (le->positionContents & CONTENTS_WATER) {
00457
00458 S_PlaySample(le->origin, cls.soundPool[SOUND_WATER_OUT], SOUND_ATTN_IDLE, SND_VOLUME_FOOTSTEPS);
00459 } else {
00460
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
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
00489 PosToVec(le->pos, origin);
00490
00493 if (t->particle) {
00494
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
00515 Grid_PosToVec(cl.mapData->map, le->fieldSize, oldPos, start);
00516 Grid_PosToVec(cl.mapData->map, le->fieldSize, pos, dest);
00517
00518 return (start[2] - dest[2]);
00519 }
00520 }
00521
00522 static void LE_PlayFootStepSound (le_t *le)
00523 {
00524
00525 if (!le->pathContents[le->pathPos]) {
00526 trace_t trace;
00527 vec3_t from, to;
00528
00529
00530 PosToVec(le->pos, from);
00531 VectorCopy(from, to);
00532
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
00545 const byte fulldv = le->path[le->pathPos];
00546 const byte dir = getDVdir(fulldv);
00547 const byte crouchingState = LE_IsCrouched(le) ? 1 : 0;
00548
00549
00550 byte newCrouchingState = crouchingState;
00551 PosAddDV(le->pos, newCrouchingState, fulldv);
00552
00553 LE_PlayFootStepSound(le);
00554
00555
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
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
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
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
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
00618 if (cl.time < le->startTime)
00619 return;
00620
00621
00622 while (cl.time >= le->endTime) {
00623
00624 Grid_PosToVec(cl.mapData->map, le->fieldSize, le->pos, le->origin);
00625
00626
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
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
00645 VectorMA(start, frac, delta, le->origin);
00646 }
00647
00653 void LET_StartPathMove (le_t *le)
00654 {
00655
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
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
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
00697 le->inuse = qfalse;
00698 }
00699 }
00700
00701
00702
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
00712 le = LE_Add(0);
00713 if (!le)
00714 return;
00715 le->invis = !cl_leshowinvis->integer;
00716
00717 le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle, NULL, NULL);
00718 if (!le->ptl) {
00719 le->inuse = qfalse;
00720 return;
00721 }
00722
00723
00724 VectorSubtract(impact, muzzle, delta);
00725 dist = VectorLength(delta);
00726
00727 VecToAngles(delta, le->ptl->angles);
00728
00729 le->dir = normal;
00730 le->fd = fd;
00731
00732
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
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
00770 VectorScale(delta, fd->speed / dist, le->ptl->v);
00771 le->endTime = cl.time + 1000 * dist / fd->speed;
00772
00773
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
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
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
00836
00837
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
00848 le->origin[2] -= GROUND_DELTA;
00849 } else {
00850
00851
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
00869 le = LE_Add(0);
00870 if (!le)
00871 return;
00872 le->invis = !cl_leshowinvis->integer;
00873
00874
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
00882 VectorSet(le->ptl->angles, 360 * crand(), 360 * crand(), 360 * crand());
00883 VectorSet(le->ptl->omega, 500 * crand(), 500 * crand(), 500 * crand());
00884
00885
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
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
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
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
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
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
01012 break;
01013 }
01014
01015
01016 if (!le) {
01017 if (cl.numLEs >= MAX_EDICTS) {
01018
01019 Com_Error(ERR_DROP, "Too many LEs");
01020 }
01021
01022
01023 le = &cl.LEs[cl.numLEs];
01024 cl.numLEs++;
01025 }
01026
01027
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
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
01074 return le;
01075 }
01076
01077
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
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
01225 return le;
01226 }
01227
01228
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
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
01283
01284 case CONTENTS_SOLID:
01285 case CONTENTS_DETAIL:
01286 break;
01287 default:
01288
01289 R_EntitySetOrigin(&ent, le->origin);
01290 VectorCopy(le->origin, ent.oldorigin);
01291
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
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
01316
01317 if (le->addFunc)
01318 if (!le->addFunc(le, &ent))
01319 continue;
01320
01321
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
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
01424 if (le->contents & CONTENTS_SOLID) {
01425 const cBspModel_t *model = cl.model_clip[le->modelnum1];
01426
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
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
01479 oldStart = clip->trace.startsolid;
01480 trace.le = le;
01481 clip->trace = trace;
01482 clip->trace.startsolid |= oldStart;
01483
01484 } else if (trace.allsolid) {
01485 trace.le = le;
01486 clip->trace = trace;
01487
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
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;
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
01556 CL_TraceBounds(start, mins, maxs, end, clip.boxmins, clip.boxmaxs);
01557
01558
01559 CL_ClipMoveToLEs(&clip);
01560
01561 return clip.trace;
01562 }