UFO: Alien Invasion
Loading...
Searching...
No Matches
cl_localentity.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24*/
25
26#include "../client.h"
27#include "cl_localentity.h"
28#include "../sound/s_main.h"
29#include "../sound/s_sample.h"
30#include "cl_particle.h"
31#include "cl_actor.h"
32#include "cl_hud.h"
34#include "../renderer/r_draw.h"
36#include "../../common/grid.h"
38
42
44{
45 inuse = false;
48 gender = 0;
52 clientAction = nullptr;
53 inlineModelName[0] = '\0';
55 model1 = model2 = nullptr;
56 think = nullptr;
57 stepList = nullptr;
58 particleID = ref1 = ref2 = nullptr;
59 ptl = nullptr;
60 ref3 = nullptr;
61 teamDef = nullptr;
62 fd = nullptr;
63 addFunc = nullptr;
64 type = ET_NULL;
68
81 OBJZERO(as);
83
84 inv.init();
85}
86
87/*===========================================================================
88Local Model (LM) handling
89=========================================================================== */
90
91static inline void LE_GenerateInlineModelList (void)
92{
93 le_t* le = nullptr;
94 int i = 0;
95
96 while ((le = LE_GetNextInUse(le))) {
97 if (le->model1 && le->inlineModelName[0] == '*')
98 cl.leInlineModelList[i++] = le->inlineModelName;
99 }
100 cl.leInlineModelList[i] = nullptr;
101}
102
103static void CL_GridRecalcRouting (const le_t* le)
104{
105 /* We ALWAYS check against a model, even if it isn't in use.
106 * An unused model is NOT included in the inline list, so it doesn't get
107 * traced against. */
108 if (!le->model1 || le->inlineModelName[0] != '*')
109 return;
110
111 if (Com_ServerState())
112 return;
113
114 const cBspModel_t* model = CM_InlineModel(cl.mapTiles, le->inlineModelName);
115 if (!model) {
116 return;
117 }
118 AABB absBox(model->cbmBox);
119 absBox.shift(model->origin);
120 GridBox rerouteBox(absBox);
121
122 Grid_RecalcRouting(cl.mapTiles, cl.mapData->routing, le->inlineModelName, rerouteBox, cl.leInlineModelList);
123}
124
129{
130 const double start = time(nullptr); /* stopwatch */
131
133
134 int i = 0;
135 for (const le_t* le = cl.LEs; i < cl.numLEs; i++, le++)
137
138 Com_Printf("Rerouted for %i LEs in %5.2fs\n", i, time(nullptr) - start);
139}
140
153
154static void LM_AddToSceneOrder (bool parents)
155{
156 for (int i = 0; i < cl.numLMs; i++) {
157 localModel_t& lm = cl.LMs[i];
158 if (!lm.inuse)
159 continue;
160
161 /* check for visibility */
162 if (!((1 << cl_worldlevel->integer) & lm.levelflags))
163 continue;
164
165 /* if we want to render the parents and this is a child (has a parent assigned)
166 * then skip it */
167 if (parents && lm.parent)
168 continue;
169
170 /* if we want to render the children and this is a parent (no further parent
171 * assigned), then skip it. */
172 if (!parents && lm.parent == nullptr)
173 continue;
174
175 /* set entity values */
176 entity_t ent(RF_NONE);
177 assert(lm.model);
178 ent.model = lm.model;
179 ent.skinnum = lm.skin;
180 ent.lighting = &lm.lighting;
181 ent.setScale(lm.scale);
182
183 if (lm.parent) {
185 ent.tagent = R_GetEntity(lm.parent->renderEntityNum);
186 if (ent.tagent == nullptr)
187 Com_Error(ERR_DROP, "Invalid parent entity num for local model (%s/%s): %i",
188 lm.model->name, lm.id, lm.parent->renderEntityNum);
189 ent.tagname = lm.tagname;
190 } else {
191 R_EntitySetOrigin(&ent, lm.origin);
192 VectorCopy(lm.origin, ent.oldorigin);
193 VectorCopy(lm.angles, ent.angles);
194
195 if (lm.animname[0] != '\0') {
196 ent.as = lm.as;
197 /* do animation */
198 R_AnimRun(&lm.as, ent.model, cls.frametime * 1000);
199 } else {
200 ent.as.frame = lm.frame;
201 }
202 }
203
204 /* renderflags like RF_PULSE */
205 ent.flags = lm.renderFlags;
206
207 /* add it to the scene */
208 lm.renderEntityNum = R_AddEntity(&ent);
209 }
210}
211
218void LM_AddToScene (void)
219{
220 LM_AddToSceneOrder(true);
221 LM_AddToSceneOrder(false);
222}
223
227static inline localModel_t* LM_Find (int entnum)
228{
229 for (int i = 0; i < cl.numLMs; i++)
230 if (cl.LMs[i].entnum == entnum)
231 return &cl.LMs[i];
232
233 return nullptr;
234}
235
240{
241 le_t* floorItem = LE_Find(ET_ITEM, le->pos);
242 if (floorItem)
243 le->setFloor(floorItem);
244 else
245 le->resetFloor();
246}
247
253bool LE_IsActor (const le_t* le)
254{
255 assert(le);
256 return le->type == ET_ACTOR || le->type == ET_ACTOR2x2 || le->type == ET_ACTORHIDDEN;
257}
258
265bool LE_IsLivingActor (const le_t* le)
266{
267 assert(le);
268 return LE_IsActor(le) && (LE_IsStunned(le) || !LE_IsDead(le));
269}
270
278{
279 assert(le);
280 if (LE_IsInvisible(le))
281 return false;
282
283 assert(le->type != ET_ACTORHIDDEN);
284
285 return LE_IsLivingActor(le);
286}
287
292void LM_Register (void)
293{
294 for (int i = 0; i < cl.numLMs; i++) {
295 localModel_t& lm = cl.LMs[i];
296
297 /* register the model */
298 lm.model = R_FindModel(lm.name);
299 if (lm.animname[0]) {
300 R_AnimChange(&lm.as, lm.model, lm.animname);
301 if (!lm.as.change)
302 Com_Printf("LM_Register: Could not change anim of %s to '%s'\n",
303 lm.name, lm.animname);
304 }
305 if (!lm.model)
306 lm.inuse = false;
307 }
308}
309
311{
312 le->think = think;
313}
314
315localModel_t* LM_GetByID (const char* id)
316{
317 if (Q_strnull(id))
318 return nullptr;
319
320 for (int i = 0; i < cl.numLMs; i++) {
321 localModel_t* lm = &cl.LMs[i];
322 if (Q_streq(lm->id, id))
323 return lm;
324 }
325 return nullptr;
326}
327
341localModel_t* LM_AddModel (const char* model, const vec3_t origin, const vec3_t angles, int entnum, int levelflags, int renderFlags, const vec3_t scale)
342{
343 if (cl.numLMs >= MAX_LOCALMODELS)
344 Com_Error(ERR_DROP, "Too many local models\n");
345
346 /* check whether there is already a model with that number */
347 if (LM_Find(entnum))
348 Com_Error(ERR_DROP, "Already a local model with the same id (%i) loaded\n", entnum);
349
350 localModel_t* lm = &cl.LMs[cl.numLMs++];
351 OBJZERO(*lm);
352 Q_strncpyz(lm->name, model, sizeof(lm->name));
354 VectorCopy(angles, lm->angles);
355 lm->entnum = entnum;
356 lm->levelflags = levelflags;
357 lm->renderFlags = renderFlags;
358 lm->inuse = true;
359 lm->setScale(scale);
360
361 return lm;
362}
363
364/*===========================================================================
365LE thinking
366=========================================================================== */
367
372{
373 if (le->inuse && le->think) {
374 le->think(le);
375 }
376}
377
387void LE_Think (void)
388{
389 if (cls.state != ca_active)
390 return;
391
392 le_t* le = nullptr;
393 while ((le = LE_GetNext(le))) {
394 LE_ExecuteThink(le);
395 /* do animation - even for invisible entities */
396 R_AnimRun(&le->as, le->model1, cls.frametime * 1000);
397 }
398}
399
400void LM_Think (void)
401{
402 for (int i = 0; i < cl.numLMs; i++) {
403 localModel_t& lm = cl.LMs[i];
404 if (lm.think)
405 lm.think(&lm);
406 }
407}
408
409
410/*===========================================================================
411 LE think functions
412=========================================================================== */
413
423const char* LE_GetAnim (const char* anim, int right, int left, int state)
424{
425 if (!anim)
426 return "";
427
428 static char retAnim[MAX_VAR];
429 char* mod = retAnim;
430 size_t length = sizeof(retAnim);
431
432 /* add crouched flag */
433 if (state & STATE_CROUCHED) {
434 *mod++ = 'c';
435 length--;
436 }
437
438 /* determine relevant data */
439 char animationIndex;
440 char const* type;
441 if (right == NONE) {
442 animationIndex = '0';
443 if (left == NONE)
444 type = "item";
445 else {
447 /* left hand grenades look OK with default anim; others don't */
448 if (!Q_streq(type, "grenade")) {
449 type = "pistol_d";
450 }
451 }
452 } else {
453 const objDef_t* od = INVSH_GetItemByIDX(right);
454 animationIndex = od->animationIndex;
455 type = od->type;
456 if (left != NONE && Q_streq(od->type, "pistol") && Q_streq(INVSH_GetItemByIDX(left)->type, "pistol")) {
457 type = "pistol_d";
458 }
459 }
460
461 if (Q_strstart(anim, "stand") || Q_strstart(anim, "walk")) {
462 Com_sprintf(mod, length, "%s%c", anim, animationIndex);
463 } else {
464 Com_sprintf(mod, length, "%s_%s", anim, type);
465 }
466
467 return retAnim;
468}
469
479{
480 /* hidden actors don't have models assigned, thus we can not change the
481 * animation for any model */
482 if (!LE_IsInvisible(le)) {
483 if (LE_IsDead(le))
484 R_AnimChange(&le->as, le->model1, va("dead%i", LE_GetAnimationIndexForDeath(le)));
485 else if (LE_IsPanicked(le))
486 R_AnimChange(&le->as, le->model1, "panic0");
487 else
488 R_AnimChange(&le->as, le->model1, LE_GetAnim("stand", le->right, le->left, le->state));
489 }
490
491 le->pathPos = le->pathLength = 0;
492 if (le->stepList != nullptr) {
493 leStep_t* step = le->stepList->next;
494 Mem_Free(le->stepList);
495 le->stepList = step;
496 if (step != nullptr) {
497 le->stepIndex--;
498 } else if (le->stepIndex != 0) {
499 Com_Error(ERR_DROP, "stepindex for entnum %i is out of sync (%i should be 0)\n", le->entnum, le->stepIndex);
500 }
501 }
502
503 /* keep this animation until something happens */
504 LE_SetThink(le, nullptr);
505}
506
514static void LE_PlaySoundFileForContents (le_t* le, int contents)
515{
516 /* only play those water sounds when an actor jumps into the water - but not
517 * if he enters carefully in crouched mode */
518 if (!LE_IsCrouched(le)) {
519 if (contents & CONTENTS_WATER) {
520 /* were we already in the water? */
522 /* play water moving sound */
524 } else {
525 /* play water entering sound */
527 }
528 return;
529 }
530
532 /* play water leaving sound */
534 }
535 }
536}
537
544static void LE_PlaySoundFileAndParticleForSurface (le_t* le, const char* textureName)
545{
546 const terrainType_t* t = Com_GetTerrainType(textureName);
547 if (!t)
548 return;
549
550 /* origin might not be up-to-date here - but pos should be */
552 PosToVec(le->pos, origin);
553
556 if (t->particle) {
557 /* check whether actor is visible */
560 }
561 if (t->footstepSound) {
562 Com_DPrintf(DEBUG_SOUND, "LE_PlaySoundFileAndParticleForSurface: volume %.2f\n", t->footstepVolume);
564 }
565}
566
570int LE_ActorGetStepTime (const le_t* le, const pos3_t pos, const pos3_t oldPos, const int dir, const int speed)
571{
572 if (dir != DIRECTION_FALL) {
573 return (((dir & (CORE_DIRECTIONS - 1)) >= BASE_DIRECTIONS ? UNIT_SIZE * 1.41 : UNIT_SIZE) * 1000 / speed);
574 } else {
575 vec3_t start, dest;
576 /* This needs to account for the distance of the fall. */
577 Grid_PosToVec(cl.mapData->routing, le->fieldSize, oldPos, start);
578 Grid_PosToVec(cl.mapData->routing, le->fieldSize, pos, dest);
579 /* 1/1000th of a second per model unit in height change */
580 return (start[2] - dest[2]);
581 }
582}
583
584static void LE_PlayFootStepSound (le_t* le)
585{
586 if (Q_strvalid(le->teamDef->footstepSound)) {
588 return;
589 }
590 /* walking in water will not play the normal footstep sounds */
591 if (!le->pathContents[le->pathPos]) {
592 vec3_t from, to;
593
594 /* prepare trace vectors */
595 PosToVec(le->pos, from);
596 VectorCopy(from, to);
597 /* we should really hit the ground with this */
598 to[2] -= UNIT_HEIGHT;
599
600 const trace_t trace = CL_Trace(Line(from, to), AABB::EMPTY, nullptr, nullptr, MASK_SOLID, cl_worldlevel->integer);
601 if (trace.surface)
603 } else
605}
606
607static void LE_DoPathMove (le_t* le)
608{
609 /* next part */
610 const dvec_t dvec = le->dvtab[le->pathPos];
611 const byte dir = getDVdir(dvec);
612 const byte crouchingState = LE_IsCrouched(le) ? 1 : 0;
613 /* newCrouchingState needs to be set to the current crouching state
614 * and is possibly updated by PosAddDV. */
615 byte newCrouchingState = crouchingState;
616 PosAddDV(le->pos, newCrouchingState, dvec);
617
619
620 /* only change the direction if the actor moves horizontally. */
621 if (dir < CORE_DIRECTIONS || dir >= FLYING_DIRECTIONS)
622 le->angle = dir & (CORE_DIRECTIONS - 1);
623 le->angles[YAW] = directionAngles[le->angle];
624 le->startTime = le->endTime;
625 /* check for straight movement or diagonal movement */
626 assert(le->speed[le->pathPos]);
627 le->endTime += LE_ActorGetStepTime(le, le->pos, le->oldPos, dir, le->speed[le->pathPos]);
628
630 le->pathPos++;
631}
632
637{
638 /* Verify the current position */
639 if (!VectorCompare(le->pos, le->newPos))
640 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)",
641 le->pos[0], le->pos[1], le->pos[2], le->newPos[0], le->newPos[1], le->newPos[2], le->pathPos, le->pathLength, le->team);
642
644 /* if the moving actor was not the selected one, */
645 /* recalc the pathing table for the selected one, too. */
646 if (!LE_IsSelected(le)) {
648 }
649
651
653 LE_ExecuteThink(le);
654 LE_Unlock(le);
655}
656
663static void LE_ActorBodyHit (const le_t* le, const vec3_t impact, int normal)
664{
665 if (le->teamDef) {
666 /* Spawn "hit_particle" if defined in teamDef. */
667 if (le->teamDef->hitParticle[0] != '\0')
668 CL_ParticleSpawn(le->teamDef->hitParticle, 0, impact, bytedirs[normal]);
669 }
670}
671
677static void LET_PathMove (le_t* le)
678{
679 /* check for start of the next step */
680 if (cl.time < le->startTime)
681 return;
682
683 /* move ahead */
684 while (cl.time >= le->endTime) {
685 /* Ensure that we are displayed where we are supposed to be, in case the last frame came too quickly. */
686 Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin);
687
688 /* Record the last position of movement calculations. */
689 VectorCopy(le->pos, le->oldPos);
690
691 if (le->pathPos < le->pathLength) {
692 LE_DoPathMove(le);
693 } else {
695 return;
696 }
697 }
698
699 /* interpolate the position */
700 vec3_t start, dest, delta;
701 Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->oldPos, start);
702 Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, dest);
703 VectorSubtract(dest, start, delta);
704
705 const float frac = (float) (cl.time - le->startTime) / (float) (le->endTime - le->startTime);
706
707 /* calculate the new interpolated actor origin in the world */
708 VectorMA(start, frac, delta, le->origin);
709}
710
717{
718 /* center view (if wanted) */
719 if (!cls.isOurRound() && le->team != TEAM_CIVILIAN)
720 LE_CenterView(le);
721
722 /* initial animation or animation change */
723 R_AnimChange(&le->as, le->model1, LE_GetAnim("walk", le->right, le->left, le->state));
724 if (!le->as.change)
725 Com_Printf("LET_StartPathMove: Could not change anim of le: %i, team: %i, pnum: %i\n",
726 le->entnum, le->team, le->pnum);
727
729 LE_ExecuteThink(le);
730}
731
738{
739 VectorCopy(le->newPos, le->pos);
741 LE_ExecuteThink(le);
742 LE_Unlock(le);
743}
744
748static void LET_Projectile (le_t* le)
749{
750 if (cl.time >= le->endTime) {
751 vec3_t impact;
752 VectorCopy(le->origin, impact);
753 CL_ParticleFree(le->ptl);
754 /* don't run the think function again */
755 le->inuse = false;
756 if (Q_strvalid(le->ref1)) {
757 VectorCopy(le->ptl->s, impact);
758 le->ptl = CL_ParticleSpawn(le->ref1, 0, impact, bytedirs[le->angle]);
759 VecToAngles(bytedirs[le->state], le->ptl->angles);
760 }
761 if (Q_strvalid(le->ref2)) {
763 }
764 if (le->ref3) {
765 /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */
768 if (le->fd->obj->dmgtype != csi.damStunGas)
769 LE_ActorBodyHit(le->ref3, impact, le->angle);
771 }
772 } else if (CL_OutsideMap(le->ptl->s, UNIT_SIZE * 10)) {
773 le->endTime = cl.time;
774 CL_ParticleFree(le->ptl);
775 /* don't run the think function again */
776 le->inuse = false;
777 }
778}
779
780/*===========================================================================
781 LE Special Effects
782=========================================================================== */
783
784void LE_AddProjectile (const fireDef_t* fd, int flags, const vec3_t muzzle, const vec3_t impact, int normal, le_t* leVictim)
785{
786 /* add le */
787 le_t* le = LE_Add(0);
788 if (!le)
789 return;
790 LE_SetInvisible(le);
791 /* bind particle */
792 le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle);
793 if (!le->ptl) {
794 le->inuse = false;
795 return;
796 }
797
798 /* calculate parameters */
799 vec3_t delta;
800 VectorSubtract(impact, muzzle, delta);
801 const float dist = VectorLength(delta);
802
803 VecToAngles(delta, le->ptl->angles);
804 /* direction - bytedirs index */
805 le->angle = normal;
806 le->fd = fd;
807
808 /* infinite speed projectile? */
809 if (!fd->speed) {
810 le->inuse = false;
811 le->ptl->size[0] = dist;
812 VectorMA(muzzle, 0.5, delta, le->ptl->s);
813 if ((flags & (SF_IMPACT | SF_BODY)) || (fd->splrad && !fd->bounce)) {
814 ptl_t* ptl = nullptr;
815 const float* dir = bytedirs[le->angle];
816 if (flags & SF_BODY) {
817 if (fd->hitBodySound != nullptr) {
819 }
820 if (fd->hitBody != nullptr)
821 ptl = CL_ParticleSpawn(fd->hitBody, 0, impact, dir);
822
823 /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */
826 if (leVictim) {
827 if (fd->obj->dmgtype != csi.damStunGas)
828 LE_ActorBodyHit(leVictim, impact, le->angle);
829 if (fd->damage[0] >= 0)
830 CL_ActorPlaySound(leVictim, SND_HURT);
831 }
832 } else {
833 if (fd->impactSound != nullptr) {
835 }
836 if (fd->impact != nullptr)
837 ptl = CL_ParticleSpawn(fd->impact, 0, impact, dir);
838 }
839 if (ptl)
840 VecToAngles(dir, ptl->angles);
841 }
842 return;
843 }
844 /* particle properties */
845 VectorScale(delta, fd->speed / dist, le->ptl->v);
846 le->endTime = cl.time + 1000 * dist / fd->speed;
847
848 /* think function */
849 if (flags & SF_BODY) {
850 le->ref1 = fd->hitBody;
851 le->ref2 = fd->hitBodySound;
852 le->ref3 = leVictim;
853 } else if ((flags & SF_IMPACT) || (fd->splrad && !fd->bounce)) {
854 le->ref1 = fd->impact;
855 le->ref2 = fd->impactSound;
856 } else {
857 le->ref1 = nullptr;
858 if (flags & SF_BOUNCING)
859 le->ref2 = fd->bounceSound;
860 }
861
863 LE_ExecuteThink(le);
864}
865
873static const objDef_t* LE_BiggestItem (const Item* ic)
874{
875 assert(ic);
876 const objDef_t* max;
877 int maxSize = 0;
878
879 for (max = ic->def(); ic; ic = ic->getNext()) {
880 const int size = INVSH_ShapeSize(ic->def()->shape);
881 if (size > maxSize) {
882 max = ic->def();
883 maxSize = size;
884 }
885 }
886
887 /* there must be an item in the Item */
888 assert(max);
889 return max;
890}
891
897{
898 assert(LE_IsItem(le));
899
900 /* search owners (there can be many, some of them dead) */
901 le_t* actor = nullptr;
902 while ((actor = LE_GetNextInUse(actor))) {
903 if ((actor->type == ET_ACTOR || actor->type == ET_ACTOR2x2)
904 && VectorCompare(actor->pos, le->pos)) {
905 if (le->getFloorContainer())
906 actor->setFloor(le);
907 }
908 }
909
910 /* the le is an ET_ITEM entity, this entity is there to render dropped items
911 * if there are no items in the floor container, this entity can be
912 * deactivated */
913 Item* floorCont = le->getFloorContainer();
914 if (floorCont) {
915 const objDef_t* biggest = LE_BiggestItem(floorCont);
916 le->model1 = cls.modelPool[biggest->idx];
917 if (!le->model1)
918 Com_Error(ERR_DROP, "Model for item %s is not precached in the cls.model_weapons array",
919 biggest->id);
920 Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin);
921 VectorSubtract(le->origin, biggest->center, le->origin);
922 le->angles[ROLL] = 90;
923 /*le->angles[YAW] = 10*(int)(le->origin[0] + le->origin[1] + le->origin[2]) % 360; */
924 le->origin[2] -= GROUND_DELTA;
925 } else {
926 /* If no items in floor inventory, don't draw this le - the container is
927 * maybe empty because an actor picked up the last items here */
929 }
930}
931
940void LE_AddGrenade (const fireDef_t* fd, int flags, const vec3_t muzzle, const vec3_t v0, int dt, le_t* leVictim)
941{
942 /* add le */
943 le_t* le = LE_Add(0);
944 if (!le)
945 return;
946 LE_SetInvisible(le);
947
948 /* bind particle */
949 vec3_t accel;
950 VectorSet(accel, 0, 0, -GRAVITY);
951 le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle, v0, accel);
952 if (!le->ptl) {
953 le->inuse = false;
954 return;
955 }
956 /* particle properties */
957 VectorSet(le->ptl->angles, 360 * crand(), 360 * crand(), 360 * crand());
958 VectorSet(le->ptl->omega, 500 * crand(), 500 * crand(), 500 * crand());
959
960 /* think function */
961 if (flags & SF_BODY) {
962 le->ref1 = fd->hitBody;
963 le->ref2 = fd->hitBodySound;
964 le->ref3 = leVictim;
965 } else if ((flags & SF_IMPACT) || (fd->splrad && !fd->bounce)) {
966 le->ref1 = fd->impact;
967 le->ref2 = fd->impactSound;
968 } else {
969 le->ref1 = nullptr;
970 if (flags & SF_BOUNCING)
971 le->ref2 = fd->bounceSound;
972 }
973
974 le->endTime = cl.time + dt;
975 /* direction - bytedirs index (0,0,1) */
976 le->angle = 5;
977 le->fd = fd;
979 LE_ExecuteThink(le);
980}
981
987{
988 switch (le->type) {
989 case ET_ROTATING:
990 case ET_DOOR:
991 /* These cause the model to render correctly */
992 le->aabb.set(ent->eBox);
993 VectorCopy(ent->origin, le->origin);
994 VectorCopy(ent->angles, le->angles);
995 break;
996 case ET_DOOR_SLIDING:
997 VectorCopy(le->origin, ent->origin);
998 break;
999 case ET_BREAKABLE:
1000 break;
1001 case ET_TRIGGER_RESCUE: {
1002 const int drawFlags = cl_map_draw_rescue_zone->integer;
1003
1004 if (!((1 << cl_worldlevel->integer) & le->levelflags))
1005 return false;
1006
1007 ent->flags = 0; /* Do not draw anything at all, if drawFlags set to 0 */
1008 enum { DRAW_TEXTURE = 0x1, DRAW_CIRCLES = 0x2 };
1009 ent->model = nullptr;
1010 ent->alpha = 0.3f;
1011 VectorSet(ent->color, 0.5f, 1.0f, 0.0f);
1012 if ((drawFlags & DRAW_TEXTURE) && ent->texture == nullptr) {
1013 ent->flags = RF_BOX;
1014 ent->texture = R_FindPics("sfx/misc/rescue");
1015 VectorSet(ent->color, 1, 1, 1);
1016 }
1017 ent->eBox.set(le->aabb);
1018
1019 if (!(drawFlags & DRAW_CIRCLES))
1020 return false;
1021
1022 /* The triggerbox seems to be 'off-by-one'. The '- UNIT_SIZE' compensates for that. */
1023 for (vec_t x = le->aabb.getMinX(); x < le->aabb.getMaxX() - UNIT_SIZE; x += UNIT_SIZE) {
1024 for (vec_t y = le->aabb.getMinY(); y < le->aabb.getMaxY() - UNIT_SIZE; y += UNIT_SIZE) {
1025 const vec3_t center = {x + UNIT_SIZE / 2, y + UNIT_SIZE / 2, le->aabb.getMinZ()};
1026
1027 entity_t circle(RF_PATH);
1028 VectorCopy(center, circle.origin);
1029 circle.oldorigin[0] = circle.oldorigin[1] = circle.oldorigin[2] = UNIT_SIZE / 2.0f;
1030 VectorCopy(ent->color, circle.color);
1031 circle.alpha = ent->alpha;
1032
1033 R_AddEntity(&circle);
1034 }
1035 }
1036
1037 /* no other rendering entities should be added for the local entity */
1038 return false;
1039 }
1040 default:
1041 break;
1042 }
1043
1044 return true;
1045}
1046
1048{
1049 const int delay = cl.time - le->thinkDelay;
1050
1051 /* Updating model faster than 1000 times per second seems to be pretty much pointless */
1052 if (delay < 1)
1053 return;
1054
1055 if (le->type == ET_ROTATING) {
1056 const float angle = le->angles[le->angle] + 0.001 * delay * le->rotationSpeed; /* delay is in msecs, speed in degrees per second */
1057 le->angles[le->angle] = (angle >= 360.0 ? angle - 360.0 : angle);
1058 }
1059
1060 le->thinkDelay = cl.time;
1061}
1062
1063void LMT_Init (localModel_t* localModel)
1064{
1065 if (localModel->target[0] != '\0') {
1066 localModel->parent = LM_GetByID(localModel->target);
1067 if (!localModel->parent)
1068 Com_Error(ERR_DROP, "Could not find local model entity with the id: '%s'.", localModel->target);
1069 }
1070
1071 /* no longer needed */
1072 localModel->think = nullptr;
1073}
1074
1081void LET_RotateDoor (le_t* le, int speed)
1082{
1084 const int angle = speed > 0 ? DOOR_ROTATION_ANGLE : -DOOR_ROTATION_ANGLE;
1085 if (le->dir & DOOR_OPEN_REVERSE)
1086 le->angles[le->dir & 3] -= angle;
1087 else
1088 le->angles[le->dir & 3] += angle;
1089
1091 CL_RecalcRouting(le);
1092
1093 /* reset the think function as the movement finished */
1094 LE_SetThink(le, nullptr);
1095}
1096
1110void LET_SlideDoor (le_t* le, int speed)
1111{
1112 vec3_t moveAngles, moveDir;
1113
1114 /* get the movement angle vector */
1115 GET_SLIDING_DOOR_SHIFT_VECTOR(le->dir, speed, moveAngles);
1116
1117 /* this origin is only an offset to the absolute mins/maxs for rendering */
1118 VectorAdd(le->origin, moveAngles, le->origin);
1119
1120 /* get the direction vector from the movement angles that were set on the entity */
1121 AngleVectors(moveAngles, moveDir, nullptr, nullptr);
1122 moveDir[0] = fabsf(moveDir[0]);
1123 moveDir[1] = fabsf(moveDir[1]);
1124 moveDir[2] = fabsf(moveDir[2]);
1125 /* calculate the distance from the movement angles and the entity size */
1126 const int distance = DotProduct(moveDir, le->size);
1127
1128 bool endPos = false;
1129 if (speed > 0) {
1130 /* check whether the distance the door may slide is slided already
1131 * - if so, stop the movement of the door */
1132 if (fabs(le->origin[le->dir & 3]) >= distance)
1133 endPos = true;
1134 } else {
1135 /* the sliding door has not origin set - except when it is opened. This door type is no
1136 * origin brush based bmodel entity. So whenever the origin vector is not the zero vector,
1137 * the door is opened. */
1138 if (VectorEmpty(le->origin))
1139 endPos = true;
1140 }
1141
1142 if (endPos) {
1143 vec3_t distanceVec;
1144 /* the door finished its move - either close or open, so make sure to recalc the routing
1145 * data and set the mins/maxs for the inline brush model */
1146 cBspModel_t* model = CM_InlineModel(cl.mapTiles, le->inlineModelName);
1147
1148 assert(model);
1149
1150 /* we need the angles vector normalized */
1151 GET_SLIDING_DOOR_SHIFT_VECTOR(le->dir, (speed < 0) ? -1 : 1, moveAngles);
1152
1153 /* the bounding box of the door is updated in one step - here is no lerping needed */
1154 VectorMul(distance, moveAngles, distanceVec);
1155
1156 model->cbmBox.shift(distanceVec);
1157 CL_RecalcRouting(le);
1158
1159 /* reset the think function as the movement finished */
1160 LE_SetThink(le, nullptr);
1161 } else
1162 le->thinkDelay = 1000;
1163}
1164
1169void LE_AddAmbientSound (const char* sound, const vec3_t origin, int levelflags, float volume, float attenuation)
1170{
1171 if (strstr(sound, "sound/"))
1172 sound += 6;
1173
1174 int sampleIdx = S_LoadSampleIdx(sound);
1175 if (!sampleIdx)
1176 return;
1177
1178 le_t* le = LE_Add(0);
1179 if (!le) {
1180 Com_Printf("Could not add ambient sound entity\n");
1181 return;
1182 }
1183 le->type = ET_SOUND;
1184 le->sampleIdx = sampleIdx;
1185 VectorCopy(origin, le->origin);
1186 LE_SetInvisible(le);
1187 le->levelflags = levelflags;
1188 le->attenuation = attenuation;
1189
1190 if (volume < 0.0f || volume > 1.0f) {
1192 Com_Printf("Invalid volume for local entity given - only values between 0.0 and 1.0 are valid\n");
1193 } else {
1194 le->volume = volume;
1195 }
1196
1197 Com_DPrintf(DEBUG_SOUND, "Add ambient sound '%s' with volume %f\n", sound, volume);
1198}
1199
1200/*===========================================================================
1201 LE Management functions
1202=========================================================================== */
1203
1209le_t* LE_Add (int entnum)
1210{
1211 le_t* le = nullptr;
1212
1213 while ((le = LE_GetNext(le))) {
1214 if (!le->inuse)
1215 /* found a free LE */
1216 break;
1217 }
1218
1219 /* list full, try to make list longer */
1220 if (!le) {
1221 if (cl.numLEs >= MAX_EDICTS) {
1222 /* no free LEs */
1223 Com_Error(ERR_DROP, "Too many LEs");
1224 }
1225
1226 /* list isn't too long */
1227 le = &cl.LEs[cl.numLEs];
1228 cl.numLEs++;
1229 }
1230
1231 /* initialize the new LE */
1232 le->init();
1233 le->inuse = true;
1234 le->entnum = entnum;
1236 return le;
1237}
1238
1239void _LE_NotFoundError (int entnum, int type, const char* file, const int line)
1240{
1241 Cmd_ExecuteString("debug_listle");
1242 Cmd_ExecuteString("debug_listedicts");
1243 if (type >= 0) {
1244 Com_Error(ERR_DROP, "LE_NotFoundError: Could not get LE with entnum %i of type: %i (%s:%i)\n", entnum, type, file, line);
1245 } else {
1246 Com_Error(ERR_DROP, "LE_NotFoundError: Could not get LE with entnum %i (%s:%i)\n", entnum, file, line);
1247 }
1248}
1249
1257void LE_CenterView (const le_t* le)
1258{
1259 if (!cl_centerview->integer)
1260 return;
1261
1262 assert(le);
1263 if (le->team == cls.team) {
1264 const float minDistToMove = 4.0f * UNIT_SIZE;
1265 const float dist = Vector2Dist(cl.cam.origin, le->origin);
1266 if (dist < minDistToMove) {
1267 pos3_t currentCamPos;
1268 VecToPos(cl.cam.origin, currentCamPos);
1269 if (le->pos[2] != currentCamPos[2])
1270 Cvar_SetValue("cl_worldlevel", le->pos[2]);
1271 return;
1272 }
1273
1274 VectorCopy(le->origin, cl.cam.origin);
1275 } else {
1276 pos3_t pos;
1277 VecToPos(cl.cam.origin, pos);
1278 CL_CheckCameraRoute(pos, le->pos);
1279 }
1280}
1281
1287le_t* LE_Get (int entnum)
1288{
1289 le_t* le = nullptr;
1290
1291 if (entnum == SKIP_LOCAL_ENTITY)
1292 return nullptr;
1293
1294 while ((le = LE_GetNextInUse(le))) {
1295 if (le->entnum == entnum)
1296 /* found the LE */
1297 return le;
1298 }
1299
1300 /* didn't find it */
1301 return nullptr;
1302}
1303
1309bool LE_IsLocked (int entnum)
1310{
1311 le_t* le = LE_Get(entnum);
1312 return (le != nullptr && (le->flags & LE_LOCKED));
1313}
1314
1322void LE_Lock (le_t* le)
1323{
1324 if (le->flags & LE_LOCKED)
1325 Com_Error(ERR_DROP, "LE_Lock: Trying to lock %i which is already locked\n", le->entnum);
1326
1327 le->flags |= LE_LOCKED;
1328}
1329
1341void LE_Unlock (le_t* le)
1342{
1343 if (!(le->flags & LE_LOCKED))
1344 Com_Error(ERR_DROP, "LE_Unlock: Trying to unlock %i which is already unlocked\n", le->entnum);
1345
1346 le->flags &= ~LE_LOCKED;
1347}
1348
1354{
1355 le_t* le = nullptr;
1356
1357 while ((le = LE_GetNextInUse(le))) {
1358 if (VectorCompare(le->pos, pos))
1359 return le;
1360 }
1361
1362 /* didn't find it */
1363 return nullptr;
1364}
1365
1371{
1372 if (!cl.numLEs)
1373 return nullptr;
1374
1375 if (!lastLE)
1376 return cl.LEs;
1377
1378 le_t* endOfLEs = &cl.LEs[cl.numLEs];
1379
1380 assert(lastLE >= cl.LEs);
1381 assert(lastLE < endOfLEs);
1382
1383 le_t* le = lastLE;
1384
1385 le++;
1386 if (le >= endOfLEs)
1387 return nullptr;
1388 else
1389 return le;
1390}
1391
1399{
1400 le_t* le = lastLE;
1401
1402 while ((le = LE_GetNext(le))) {
1403 if (le->inuse)
1404 break;
1405 }
1406 return le;
1407}
1408
1416le_t* LE_FindRadius (le_t* from, const vec3_t org, float rad, entity_type_t type)
1417{
1418 le_t* le = from;
1419
1420 while ((le = LE_GetNextInUse(le))) {
1421 if (type != ET_NULL && le->type != type)
1422 continue;
1423 vec3_t eorg;
1424 vec3_t leCenter;
1425 le->aabb.getCenter(leCenter);
1426 for (int j = 0; j < 3; j++)
1427 eorg[j] = org[j] - (le->origin[j] + leCenter[j]);
1428 if (VectorLength(eorg) > rad)
1429 continue;
1430 return le;
1431 }
1432
1433 return nullptr;
1434}
1435
1442{
1443 le_t* le = nullptr;
1444
1445 while ((le = LE_GetNextInUse(le))) {
1446 if (le->type == type && VectorCompare(le->pos, pos))
1447 /* found the LE */
1448 return le;
1449 }
1450
1451 /* didn't find it */
1452 return nullptr;
1453}
1454
1462static inline bool LE_IsOriginBrush (const le_t* const le)
1463{
1464 return (le->type == ET_DOOR || le->type == ET_ROTATING);
1465}
1466
1470static void LE_AddEdictHighlight (const le_t* le)
1471{
1472 const cBspModel_t* model = LE_GetClipModel(le);
1473
1474 entity_t ent(RF_BOX);
1475 VectorSet(ent.color, 1, 1, 1);
1476 ent.alpha = (sin(cl.time * 6.28) + 1.0) / 2.0;
1477 CalculateMinsMaxs(le->angles, model->cbmBox, le->origin, ent.eBox);
1478 R_AddEntity(&ent);
1479}
1480
1486void LE_AddToScene (void)
1487{
1488 for (int i = 0; i < cl.numLEs; i++) {
1489 le_t& le = cl.LEs[i];
1490 if (le.flags & LE_REMOVE_NEXT_FRAME) {
1491 le.inuse = false;
1493 }
1494 if (le.inuse && !LE_IsInvisible(&le)) {
1495 if (le.flags & LE_CHECK_LEVELFLAGS) {
1496 if (!((1 << cl_worldlevel->integer) & le.levelflags))
1497 continue;
1498 } else if (le.flags & LE_ALWAYS_VISIBLE) {
1499 /* show them always */
1500 } else if (le.pos[2] > cl_worldlevel->integer)
1501 continue;
1502
1503 entity_t ent(RF_NONE);
1504 ent.alpha = le.alpha;
1505
1506 VectorCopy(le.angles, ent.angles);
1507 ent.model = le.model1;
1508 ent.skinnum = le.bodySkin;
1509 ent.lighting = &le.lighting;
1510
1511 switch (le.contents) {
1512 /* Only breakables do not use their origin; func_doors and func_rotating do!!!
1513 * But none of them have animations. */
1514 case CONTENTS_SOLID:
1515 case CONTENTS_DETAIL: /* they use mins/maxs */
1516 break;
1517 default:
1518 /* set entity values */
1519 R_EntitySetOrigin(&ent, le.origin);
1520 VectorCopy(le.origin, ent.oldorigin);
1521 /* store animation values */
1522 ent.as = le.as;
1523 break;
1524 }
1525
1526 if (LE_IsOriginBrush(&le)) {
1527 ent.isOriginBrushModel = true;
1528 R_EntitySetOrigin(&ent, le.origin);
1529 VectorCopy(le.origin, ent.oldorigin);
1530 }
1531
1532 if (LE_IsSelected(&le) && le.clientAction != nullptr) {
1533 const le_t* action = le.clientAction;
1534 if (action->inuse && action->type > ET_NULL && action->type < ET_MAX)
1535 LE_AddEdictHighlight(action);
1536 }
1537
1538 /* call add function */
1539 /* if it returns false, don't draw */
1540 if (le.addFunc)
1541 if (!le.addFunc(&le, &ent))
1542 continue;
1543
1544 /* add it to the scene */
1545 R_AddEntity(&ent);
1546
1547 if (cl_le_debug->integer)
1548 CL_ParticleSpawn("cross", 0, le.origin);
1549 }
1550 }
1551}
1552
1557void LE_Cleanup (void)
1558{
1559 Com_DPrintf(DEBUG_CLIENT, "LE_Cleanup: Clearing up to %i unused LE inventories\n", cl.numLEs);
1560 for (int i = cl.numLEs - 1; i >= 0; i--) {
1561 le_t* le = &cl.LEs[i];
1562 if (!le->inuse)
1563 continue;
1564 if (LE_IsActor(le))
1565 CL_ActorCleanup(le);
1566 else if (LE_IsItem(le))
1567 cls.i.emptyContainer(&le->inv, CID_FLOOR);
1568
1569 le->inuse = false;
1570 }
1571}
1572
1573#ifdef DEBUG
1577void LE_List_f (void)
1578{
1579 Com_Printf("number | entnum | type | inuse | invis | pnum | team | size | HP | state | level | model/ptl\n");
1580 for (int i = 0; i < cl.numLEs; i++) {
1581 le_t& le = cl.LEs[i];
1582 Com_Printf("#%5i | #%5i | %4i | %5i | %5i | %4i | %4i | %4i | %3i | %5i | %5i | ",
1583 i, le.entnum, le.type, le.inuse, LE_IsInvisible(&le), le.pnum, le.team,
1584 le.fieldSize, le.HP, le.state, le.levelflags);
1585 if (le.type == ET_PARTICLE) {
1586 if (le.ptl)
1587 Com_Printf("%s\n", le.ptl->ctrl->name);
1588 else
1589 Com_Printf("no ptl\n");
1590 } else if (le.model1)
1591 Com_Printf("%s\n", le.model1->name);
1592 else
1593 Com_Printf("no mdl\n");
1594 }
1595}
1596
1600void LM_List_f (void)
1601{
1602 Com_Printf("number | entnum | skin | frame | lvlflg | renderflags | origin | name\n");
1603 for (int i = 0; i < cl.numLMs; i++) {
1604 localModel_t& lm = cl.LMs[i];
1605 Com_Printf("#%5i | #%5i | #%3i | #%4i | %6i | %11i | %5.0f:%5.0f:%3.0f | %s\n",
1606 i, lm.entnum, lm.skin, lm.frame, lm.levelflags, lm.renderFlags,
1607 lm.origin[0], lm.origin[1], lm.origin[2], lm.name);
1608 }
1609}
1610
1611#endif
1612
1613/*===========================================================================
1614 LE Tracing
1615=========================================================================== */
1616
1618class MoveClipCL : public MoveClip
1619{
1620public:
1623};
1624
1626{
1627 const cBspModel_t* model;
1628 const unsigned int index = le->modelnum1;
1629 if (index > lengthof(cl.model_clip))
1630 Com_Error(ERR_DROP, "Clip model index out of bounds");
1631 model = cl.model_clip[index];
1632 if (!model)
1633 Com_Error(ERR_DROP, "LE_GetClipModel: Could not find inline model %u", index);
1634 return model;
1635}
1636
1638{
1639 if (index == 0 || index > lengthof(cl.model_draw))
1640 Com_Error(ERR_DROP, "Draw model index out of bounds");
1641 model_t* model = cl.model_draw[index];
1642 if (!model)
1643 Com_Error(ERR_DROP, "LE_GetDrawModel: Could not find model %u", index);
1644 return model;
1645}
1646
1659static int32_t CL_HullForEntity (const le_t* le, int* tile, vec3_t rmaShift, vec3_t angles)
1660{
1661 /* special case for bmodels */
1662 if (le->contents & CONTENTS_SOLID) {
1663 const cBspModel_t* model = LE_GetClipModel(le);
1664 /* special value for bmodel */
1665 if (!model)
1666 Com_Error(ERR_DROP, "CL_HullForEntity: Error - le with nullptr bmodel (%i)\n", le->type);
1667 *tile = model->tile;
1668 VectorCopy(le->angles, angles);
1669 VectorCopy(model->shift, rmaShift);
1670 return model->headnode;
1671 } else {
1672 /* might intersect, so do an exact clip */
1673 *tile = 0;
1674 VectorCopy(vec3_origin, angles);
1675 VectorCopy(vec3_origin, rmaShift);
1676 return CM_HeadnodeForBox(cl.mapTiles->mapTiles[*tile], le->aabb);
1677 }
1678}
1679
1685static void CL_ClipMoveToLEs (MoveClipCL* clip)
1686{
1687 if (clip->trace.allsolid)
1688 return;
1689
1690 le_t* le = nullptr;
1691 while ((le = LE_GetNextInUse(le))) {
1692 int tile = 0;
1693
1694 if (!(le->contents & clip->contentmask))
1695 continue;
1696 if (le == clip->passle || le == clip->passle2)
1697 continue;
1698
1699 vec3_t angles, shift;
1700 const int32_t headnode = CL_HullForEntity(le, &tile, shift, angles);
1701 assert(headnode < MAX_MAP_NODES);
1702
1703 vec3_t origin;
1704 VectorCopy(le->origin, origin);
1705
1706 trace_t trace = CM_HintedTransformedBoxTrace(cl.mapTiles->mapTiles[tile], clip->moveLine, clip->objBox,
1707 headnode, clip->contentmask, 0, origin, angles, shift, 1.0);
1708
1709 if (trace.fraction < clip->trace.fraction) {
1710 /* make sure we keep a startsolid from a previous trace */
1711 const bool oldStart = clip->trace.startsolid;
1712 trace.le = le;
1713 clip->trace = trace;
1714 clip->trace.startsolid |= oldStart;
1715 /* if true, plane is not valid */
1716 } else if (trace.allsolid) {
1717 trace.le = le;
1718 clip->trace = trace;
1719 /* if true, the initial point was in a solid area */
1720 } else if (trace.startsolid) {
1721 trace.le = le;
1722 clip->trace.startsolid = true;
1723 }
1724 }
1725}
1726
1739trace_t CL_Trace (const Line& traceLine, const AABB& box, const le_t* passle, le_t* passle2, int contentmask, int worldLevel)
1740{
1741 if (cl_trace_debug->integer)
1743
1744 /* clip to world */
1745 MoveClipCL clip;
1746 clip.trace = CM_CompleteBoxTrace(cl.mapTiles, traceLine, box, (1 << (worldLevel + 1)) - 1, contentmask, 0);
1747 clip.trace.le = nullptr;
1748 if (clip.trace.fraction == 0)
1749 return clip.trace; /* blocked by the world */
1750
1751 clip.contentmask = contentmask;
1752 clip.moveLine.set(traceLine);
1753 clip.objBox.set(box);
1754 clip.passle = passle;
1755 clip.passle2 = passle2;
1756
1757 /* create the bounding box of the entire move */
1758 clip.calcBounds();
1759
1760 /* clip to other solid entities */
1761 CL_ClipMoveToLEs(&clip);
1762
1763 return clip.trace;
1764}
@ SND_HURT
Definition chr_shared.h:220
void CL_ActorPlaySound(const le_t *le, actorSound_t soundType)
Plays various sounds on actor action.
void CL_ActorConditionalMoveCalc(le_t *le)
Recalculate forbidden list, available moves and actor's move length for the current selected actor.
Definition cl_actor.cpp:682
le_t * selActor
Definition cl_actor.cpp:49
void CL_ActorCleanup(le_t *le)
Definition cl_actor.cpp:389
bool CL_OutsideMap(const vec3_t position, const float delta)
Checks whether give position is still inside the map borders.
clientBattleScape_t cl
void CL_CheckCameraRoute(const pos3_t from, const pos3_t target)
Only moves the camera to the given target location if its not yet close enough.
cvar_t * cl_centerview
Definition cl_camera.cpp:69
cvar_t * cl_worldlevel
Definition cl_hud.cpp:46
HUD related routines.
void LET_StartPathMove(le_t *le)
Change the actors animation to walking.
static void LET_PathMove(le_t *le)
Move the actor along the path to the given location.
le_t * LE_GetNextInUse(le_t *lastLE)
Iterate through the entities that are in use.
void LE_Cleanup(void)
Cleanup unused LE inventories that the server sent to the client also free some unused LE memory.
void LE_CenterView(const le_t *le)
Center the camera on the local entity's origin.
static void CL_GridRecalcRouting(const le_t *le)
void LET_RotateDoor(le_t *le, int speed)
Rotates a door in the given speed.
void LE_AddAmbientSound(const char *sound, const vec3_t origin, int levelflags, float volume, float attenuation)
Adds ambient sounds from misc_sound entities.
static void LE_PlaySoundFileAndParticleForSurface(le_t *le, const char *textureName)
Plays step sounds and draw particles for different terrain types.
le_t * LE_GetNext(le_t *lastLE)
Iterate through the list of entities.
trace_t CL_Trace(const Line &traceLine, const AABB &box, const le_t *passle, le_t *passle2, int contentmask, int worldLevel)
Moves the given mins/maxs volume through the world from start to end.
void LE_AddGrenade(const fireDef_t *fd, int flags, const vec3_t muzzle, const vec3_t v0, int dt, le_t *leVictim)
void LM_AddToScene(void)
Add the local models to the scene.
void CL_RecalcRouting(const le_t *le)
void LM_Register(void)
Register misc_models.
cvar_t * cl_trace_debug
static void CL_ClipMoveToLEs(MoveClipCL *clip)
Clip against solid entities.
void LM_Think(void)
localModel_t * LM_GetByID(const char *id)
void LMT_Init(localModel_t *localModel)
void LE_Unlock(le_t *le)
Unlocks a previously locked le_t struct.
static void LE_PlayFootStepSound(le_t *le)
bool LE_IsLivingAndVisibleActor(const le_t *le)
Checks whether the given le is a living and visible actor.
void _LE_NotFoundError(int entnum, int type, const char *file, const int line)
static localModel_t * LM_Find(int entnum)
Checks whether a local model with the same entity number is already registered.
static void LE_ActorBodyHit(const le_t *le, const vec3_t impact, int normal)
Spawns particle effects for a hit actor.
bool LE_BrushModelAction(le_t *le, entity_t *ent)
Add function for brush models.
static bool LE_IsOriginBrush(const le_t *const le)
void CL_CompleteRecalcRouting(void)
void LET_HiddenMove(le_t *le)
Handle move for invisible actors.
static void LE_GenerateInlineModelList(void)
le_t * LE_FindRadius(le_t *from, const vec3_t org, float rad, entity_type_t type)
Returns entities that have origins within a spherical area.
static const objDef_t * LE_BiggestItem(const Item *ic)
Returns the index of the biggest item in the inventory list.
bool LE_IsLivingActor(const le_t *le)
Checks whether the given le is a living actor (but might be hidden).
void LET_BrushModel(le_t *le)
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)
Prepares local (not known or handled by the server) models to the map, which will be added later in L...
static void LE_AddEdictHighlight(const le_t *le)
Adds a box that highlights the current active door.
le_t * LE_GetFromPos(const pos3_t pos)
Searches a local entity on a given grid field.
const cBspModel_t * LE_GetClipModel(const le_t *le)
void LE_DoEndPathMove(le_t *le)
Ends the move of an actor.
model_t * LE_GetDrawModel(unsigned int index)
static void LE_DoPathMove(le_t *le)
cvar_t * cl_map_draw_rescue_zone
le_t * LE_Add(int entnum)
Add a new local entity to the scene.
int LE_ActorGetStepTime(const le_t *le, const pos3_t pos, const pos3_t oldPos, const int dir, const int speed)
void LE_SetThink(le_t *le, localEntityThinkFunc_t think)
void LET_SlideDoor(le_t *le, int speed)
Slides a door.
void LE_LinkFloorContainer(le_t *le)
link any floor container into the actor temp floor container
static void LET_Projectile(le_t *le)
static void LM_AddToSceneOrder(bool parents)
void LE_PlaceItem(le_t *le)
void LE_AddToScene(void)
void LET_StartIdle(le_t *le)
Change the animation of an actor to the idle animation (which can be panic, dead or stand).
static void LE_PlaySoundFileForContents(le_t *le, int contents)
Plays sound of content for moving actor.
void LE_AddProjectile(const fireDef_t *fd, int flags, const vec3_t muzzle, const vec3_t impact, int normal, le_t *leVictim)
void LE_Lock(le_t *le)
Markes a le_t struct as locked. Should be called at the beginning of an event handler on this le_t,...
static int32_t CL_HullForEntity(const le_t *le, int *tile, vec3_t rmaShift, vec3_t angles)
Returns a headnode that can be used for testing or clipping an object of mins/maxs size....
le_t * LE_Find(entity_type_t type, const pos3_t pos)
Searches a local entity on a given grid field.
void LE_ExecuteThink(le_t *le)
Call think function for the given local entity if its still in use.
bool LE_IsLocked(int entnum)
Checks if a given le_t structure is locked, i.e., used by another event at this time.
void LE_Think(void)
Calls the le think function and updates the animation. The animation updated even if the particular l...
cvar_t * cl_le_debug
le_t * LE_Get(int entnum)
Searches all local entities for the one with the searched entnum.
bool LE_IsActor(const le_t *le)
Checks whether the given le is a living actor.
const char * LE_GetAnim(const char *anim, int right, int left, int state)
Get the correct animation for the given actor state and weapons.
void(* localEntityThinkFunc_t)(struct le_s *le)
#define MAX_LOCALMODELS
#define LE_IsDead(le)
#define LE_IsPanicked(le)
#define LE_GetAnimationIndexForDeath(le)
Valid indices from 1 - MAX_DEATH.
#define LE_IsCrouched(le)
#define LE_REMOVE_NEXT_FRAME
#define LE_IsInvisible(le)
#define LE_ALWAYS_VISIBLE
#define LE_IsItem(le)
#define LE_IsSelected(le)
#define LE_SetInvisible(le)
#define LE_LOCKED
@ M_MOVE
#define LE_CHECK_LEVELFLAGS
#define LE_IsStunned(le)
client_static_t cls
Definition cl_main.cpp:83
void CL_ParticleFree(ptl_t *p)
Free a particle and all it's children.
ptl_t * CL_ParticleSpawn(const char *name, int levelFlags, const vec3_t s, const vec3_t v, const vec3_t a)
Spawn a new particle to the map.
@ ca_active
Definition cl_shared.h:80
Definition aabb.h:42
static const AABB EMPTY
Definition aabb.h:44
void getCenter(vec3_t center) const
Calculates the center of the bounding box.
Definition aabb.h:155
float getMinX() const
Definition aabb.h:119
float getMinZ() const
Definition aabb.h:125
void set(const AABB &other)
Copies the values from the given aabb.
Definition aabb.h:60
void shift(const vec3_t shiftVec)
shove the whole box by the given vector
Definition aabb.h:246
float getMinY() const
Definition aabb.h:122
item instance data, with linked list capability
Definition inv_shared.h:402
const objDef_t * def(void) const
Definition inv_shared.h:469
Item * getNext() const
Definition inv_shared.h:451
Definition line.h:31
void set(const Line &other)
Copies the values from the given Line.
Definition line.h:47
Client side moveclip.
const le_t * passle
const le_t * passle2
The bounding box of a moving object.
Definition moveclip.h:34
AABB objBox
Definition moveclip.h:37
void calcBounds()
Create the bounding box for the entire move.
Definition moveclip.h:48
Line moveLine
Definition moveclip.h:38
int contentmask
Definition moveclip.h:39
Primary header for client.
void Cmd_ExecuteString(const char *text,...)
A complete command line has been parsed, so try to execute it.
Definition cmd.cpp:1007
trace_t CM_HintedTransformedBoxTrace(MapTile &tile, const Line &traceLine, const AABB &traceBox, const int headnode, const int contentmask, const int brushrejects, const vec3_t origin, const vec3_t angles, const vec3_t rmaShift, const float fraction)
Handles offseting and rotation of the end points for moving and rotating entities.
Definition cmodel.cpp:84
trace_t CM_CompleteBoxTrace(mapTiles_t *mapTiles, const Line &trLine, const AABB &box, int levelmask, int brushmask, int brushreject)
Traces all submodels in all tiles. Used by ufo and ufo_ded.
Definition cmodel.cpp:283
int32_t CM_HeadnodeForBox(MapTile &tile, const AABB &box)
To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being c...
Definition cmodel.cpp:151
cBspModel_t * CM_SetInlineModelOrientation(mapTiles_t *mapTiles, const char *name, const vec3_t origin, const vec3_t angles)
This function updates a model's orientation.
Definition bsp.cpp:963
cBspModel_t * CM_InlineModel(const mapTiles_t *mapTiles, const char *name)
Searches all inline models and return the cBspModel_t pointer for the given modelnumber or -name.
Definition bsp.cpp:929
csi_t csi
Definition common.cpp:39
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition common.cpp:440
int Com_ServerState(void)
Check whether we are the server or have a singleplayer tactical mission.
Definition common.cpp:578
void Com_Error(int code, const char *fmt,...)
Definition common.cpp:459
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define SOUND_ATTN_STATIC
Definition common.h:188
#define SOUND_ATTN_NORM
Definition common.h:186
#define SOUND_ATTN_IDLE
Definition common.h:187
#define ERR_DROP
Definition common.h:211
void Cvar_SetValue(const char *varName, float value)
Expands value to a string and calls Cvar_Set.
Definition cvar.cpp:671
#define MAX_EDICTS
Definition defines.h:99
#define CONTENTS_DETAIL
Definition defines.h:251
#define DEBUG_CLIENT
Definition defines.h:59
#define NONE
Definition defines.h:68
#define DEBUG_SOUND
Definition defines.h:63
#define UNIT_HEIGHT
Definition defines.h:122
#define UNIT_SIZE
Definition defines.h:121
#define DIRECTION_FALL
Definition defines.h:335
#define CONTENTS_SOLID
Definition defines.h:223
#define MAX_MAP_NODES
Definition defines.h:140
#define ACTOR_SIZE_NORMAL
Definition defines.h:302
#define GROUND_DELTA
Definition defines.h:115
#define ACTOR_SIZE_INVALID
Definition defines.h:301
#define CONTENTS_WATER
Definition defines.h:226
#define MASK_SOLID
Definition defines.h:272
void Grid_PosToVec(const Routing &routing, const actorSizeEnum_t actorSize, const pos3_t pos, vec3_t vec)
Converts a grid position to world coordinates.
Definition grid.cpp:832
void Grid_RecalcRouting(mapTiles_t *mapTiles, Routing &routing, const char *name, const GridBox &box, const char **list)
This function recalculates the routing surrounding the entity name.
Definition grid.cpp:922
Battlescape grid functions.
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
int INVSH_ShapeSize(const uint32_t shape)
Counts the used bits in a shape (item shape).
#define CID_FLOOR
Definition inv_shared.h:55
voidpf void uLong size
Definition ioapi.h:42
voidpf uLong int origin
Definition ioapi.h:45
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition mathlib.cpp:434
void VectorMA(const vec3_t veca, const float scale, const vec3_t vecb, vec3_t outVector)
Sets vector_out (vc) to vevtor1 (va) + scale * vector2 (vb).
Definition mathlib.cpp:261
void CalculateMinsMaxs(const vec3_t angles, const AABB &relBox, const vec3_t origin, AABB &absBox)
Calculates the bounding box in absolute coordinates, also for rotating objects. WARNING: do not use t...
Definition mathlib.cpp:546
const vec3_t vec3_origin
Definition mathlib.cpp:35
const float directionAngles[CORE_DIRECTIONS]
Definition mathlib.cpp:105
float crand(void)
Return random values between -1 and 1.
Definition mathlib.cpp:517
void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Create the rotation matrix in order to rotate something.
Definition mathlib.cpp:631
void VecToAngles(const vec3_t value1, vec3_t angles)
Converts a vector to an angle vector.
Definition mathlib.cpp:934
#define PosAddDV(p, crouch, dv)
Definition mathlib.h:253
#define PosToVec(p, v)
Pos boundary size is +/- 128 - to get into the positive area we add the possible max negative value a...
Definition mathlib.h:110
#define FLYING_DIRECTIONS
Definition mathlib.h:89
#define CORE_DIRECTIONS
Definition mathlib.h:88
short dvec_t
The direction vector tells us where the actor came from (in his previous step). The pathing table hol...
Definition mathlib.h:236
#define BASE_DIRECTIONS
Number of direct connected fields for a position.
Definition mathlib.h:84
#define getDVdir(dv)
Definition mathlib.h:249
#define YAW
Definition mathlib.h:55
#define VecToPos(v, p)
Map boundary is +/- MAX_WORLD_WIDTH - to get into the positive area we add the possible max negative ...
Definition mathlib.h:100
#define ROLL
Definition mathlib.h:56
#define Mem_Free(ptr)
Definition mem.h:35
const vec3_t bytedirs[]
Definition netpack.cpp:27
#define DOOR_ROTATION_ANGLE
Definition q_shared.h:187
#define SKIP_LOCAL_ENTITY
Definition q_shared.h:255
#define SF_BODY
Definition q_shared.h:249
#define GRAVITY
Definition q_shared.h:276
entity_type_t
Definition q_shared.h:145
@ ET_ACTOR
Definition q_shared.h:148
@ ET_ACTOR2x2
Definition q_shared.h:160
@ ET_TRIGGER_RESCUE
Definition q_shared.h:154
@ ET_ROTATING
Definition q_shared.h:158
@ ET_ACTORHIDDEN
Definition q_shared.h:163
@ ET_PARTICLE
Definition q_shared.h:164
@ ET_BREAKABLE
Definition q_shared.h:150
@ ET_NULL
Definition q_shared.h:146
@ ET_DOOR
Definition q_shared.h:156
@ ET_DOOR_SLIDING
Definition q_shared.h:157
@ ET_SOUND
Definition q_shared.h:165
@ ET_MAX
Definition q_shared.h:173
@ ET_ITEM
Definition q_shared.h:149
#define STATE_CROUCHED
Definition q_shared.h:263
#define SF_IMPACT
Definition q_shared.h:248
#define DOOR_OPEN_REVERSE
Definition q_shared.h:293
#define SF_BOUNCING
Definition q_shared.h:250
#define GET_SLIDING_DOOR_SHIFT_VECTOR(dir, speed, vecout)
Definition q_shared.h:294
#define TEAM_CIVILIAN
Definition q_shared.h:61
void R_DrawBoundingBoxBatched(const AABB &absbox)
Definition r_draw.cpp:670
int R_AddEntity(const entity_t *ent)
Adds a copy of the specified entity to the list of all known render entities.
Definition r_entity.cpp:706
entity_t * R_GetEntity(int id)
Returns a specific entity from the list.
Definition r_entity.cpp:694
void R_EntitySetOrigin(entity_t *ent, const vec3_t origin)
setter for entity origin
Definition r_entity.cpp:47
#define RF_PATH
Definition r_entity.h:37
#define RF_BOX
Definition r_entity.h:36
#define RF_NONE
Definition r_entity.h:34
QGL_EXTERN GLenum GLuint * dest
Definition r_gl.h:101
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition r_gl.h:110
QGL_EXTERN GLuint index
Definition r_gl.h:110
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
const image_t * R_FindPics(const char *name)
Searches for an image in the image array.
Definition r_image.cpp:673
void R_AnimRun(animState_t *as, const model_t *mod, int msec)
Run the animation of the given model.
void R_AnimChange(animState_t *as, const model_t *mod, const char *name)
Changes the animation for md2 models.
model_t * R_FindModel(const char *name)
Tries to load a model.
Definition r_model.cpp:203
static ipos3_t shift
The shift array is used for random map assemblies (RMA) to shift the mins/maxs and stuff like that.
void S_PlayStdSample(const stdsound_t sId, const vec3_t origin, float attenuation, float volume)
plays one of the precached samples
Definition s_main.cpp:326
bool S_LoadAndPlaySample(const char *s, const vec3_t origin, float attenuation, float volume)
does what the name implies in just one function to avoid exposing s_sample_t
Definition s_main.cpp:307
Specifies sound API?
int S_LoadSampleIdx(const char *soundFile)
Loads and registers a sound file for later use.
Definition s_sample.cpp:105
@ SOUND_WATER_IN
Definition s_main.h:35
@ SOUND_WATER_OUT
Definition s_main.h:36
@ SOUND_WATER_MOVE
Definition s_main.h:37
#define SND_VOLUME_DEFAULT
Definition s_main.h:42
#define SND_VOLUME_WEAPONS
Definition s_main.h:43
const terrainType_t * Com_GetTerrainType(const char *textureName)
Searches the terrain definition if given.
Definition scripts.cpp:3067
#define SND_VOLUME_FOOTSTEPS
Definition scripts.h:212
#define Q_strvalid(string)
Definition shared.h:141
#define Q_streq(a, b)
Definition shared.h:136
bool Q_strnull(const char *string)
Definition shared.h:138
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
#define lengthof(x)
Definition shared.h:105
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
char const * Q_strstart(char const *str, char const *start)
Matches the start of a string.
Definition shared.cpp:587
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
byte change
Definition r_entity.h:65
int32_t headnode
Definition typedefs.h:29
vec3_t shift
Definition typedefs.h:28
AABB cbmBox
Definition typedefs.h:27
vec3_t origin
Definition typedefs.h:28
char name[MAX_QPATH]
Definition typedefs.h:38
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
float alpha
Definition r_entity.h:111
int flags
Definition r_entity.h:112
lighting_t * lighting
Definition r_entity.h:125
vec3_t angles
Definition r_entity.h:98
struct model_s * model
Definition r_entity.h:97
void setScale(const vec3_t scale_)
Definition r_entity.h:154
const char * tagname
Definition r_entity.h:107
vec3_t origin
Definition r_entity.h:101
vec3_t color
Definition r_entity.h:100
int skinnum
Definition r_entity.h:110
animState_t as
Definition r_entity.h:117
const image_t * texture
Definition r_entity.h:123
struct entity_s * tagent
Definition r_entity.h:106
vec3_t oldorigin
Definition r_entity.h:102
AABB eBox
Definition r_entity.h:103
bool isOriginBrushModel
Definition r_entity.h:115
this is a fire definition for our weapons/ammo
Definition inv_shared.h:110
const struct objDef_s * obj
Definition inv_shared.h:125
vec2_t damage
Definition inv_shared.h:158
float splrad
Definition inv_shared.h:161
float speed
Definition inv_shared.h:143
const char * projectile
Definition inv_shared.h:112
const char * hitBodySound
Definition inv_shared.h:116
const char * bounceSound
Definition inv_shared.h:118
const char * impact
Definition inv_shared.h:113
const char * impactSound
Definition inv_shared.h:114
float impactAttenuation
Definition inv_shared.h:121
const char * hitBody
Definition inv_shared.h:115
a local entity
const char * ref1
int state
lighting_t lighting
int slidingSpeed
int flags
int positionContents
int sampleIdx
fireDefIndex_t currentSelectedFiremode
int contents
float angles[3]
int startTime
void resetFloor()
ptl_t * ptl
actorModes_t actorMode
float volume
int morale
vec3_t origin
int thinkDelay
actorSizeEnum_t fieldSize
dvec_t dvtab[MAX_ROUTE]
leStep_t * stepList
int speed[MAX_ROUTE]
int maxMorale
int maxTU
const struct le_s * ref3
int angle
void init()
float alpha
localEntitiyAddFunc_t addFunc
unsigned int modelnum1
vec3_t oldOrigin
pos3_t pos
animState_t as
localEntityThinkFunc_t think
int pathContents[MAX_ROUTE]
const fireDef_t * fd
int pathPos
int stepIndex
const char * particleID
model_t * model1
float rotationSpeed
byte actorMoveLength
The TUs that the current selected actor needs to walk to the current grid position marked by the mous...
unsigned int headSkin
teamDef_t * teamDef
pos3_t mousePendPos
int pathLength
struct le_s * clientAction
int entnum
model_t * model2
const char * ref2
AABB aabb
vec3_t size
float attenuation
pos3_t oldPos
int maxHP
unsigned int modelnum2
void setFloor(le_s *other)
Item * getFloorContainer() const
char inlineModelName[8]
bool inuse
pos3_t newPos
unsigned int bodySkin
entity_type_t type
int headgear
Inventory inv
int levelflags
int endTime
woundInfo_t wounds
struct leStep_s * next
local models
model_t * model
lighting_t lighting
void(* think)(struct localModel_s *localModel)
char id[MAX_VAR]
void setScale(const vec3_t scale_)
char target[MAX_VAR]
animState_t as
struct localModel_s * parent
char animname[MAX_QPATH]
char name[MAX_QPATH]
char tagname[MAX_VAR]
char name[MAX_QPATH]
Definition r_model.h:44
Defines all attributes of objects used in the inventory.
Definition inv_shared.h:264
const char * type
Definition inv_shared.h:271
uint32_t shape
Definition inv_shared.h:273
char animationIndex
Definition inv_shared.h:327
vec3_t center
Definition inv_shared.h:276
const char * id
Definition inv_shared.h:268
vec2_t size
vec3_t angles
vec3_t s
vec3_t v
ptlDef_t * ctrl
vec3_t omega
char name[MAX_VAR]
Definition cl_renderer.h:82
char hitParticle[MAX_VAR]
Definition chr_shared.h:342
char footstepSound[MAX_VAR]
Definition chr_shared.h:312
Different terrain definitions for footsteps and particles.
Definition scripts.h:215
const char * footstepSound
Definition scripts.h:217
float footstepVolume
Definition scripts.h:220
const char * particle
Definition scripts.h:218
cBspSurface_t * surface
Definition tracing.h:61
float fraction
Definition tracing.h:58
bool startsolid
Definition tracing.h:57
struct le_s * le
Definition tracing.h:66
bool allsolid
Definition tracing.h:56
Tracing functions.
pos_t pos3_t[3]
Definition ufotypes.h:58
float vec_t
Definition ufotypes.h:37
vec_t vec3_t[3]
Definition ufotypes.h:39
static const vec3_t scale
static int oldPos
#define VectorMul(scalar, b, dest)
Definition vector.h:48
#define VectorClear(a)
Definition vector.h:55
#define VectorSubtract(a, b, dest)
Definition vector.h:45
#define VectorCopy(src, dest)
Definition vector.h:51
#define VectorEmpty(a)
Definition vector.h:73
#define Vector2Dist(a, b)
Definition vector.h:70
#define VectorCompare(a, b)
Definition vector.h:63
#define VectorAdd(a, b, dest)
Definition vector.h:47
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition vector.h:44
#define VectorSet(v, x, y, z)
Definition vector.h:59
#define VectorScale(in, scale, out)
Definition vector.h:79