UFO: Alien Invasion
cl_localentity.cpp
Go to the documentation of this file.
1
6/*
7Copyright (C) 2002-2022 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"
33#include "../renderer/r_mesh_anim.h"
34#include "../renderer/r_draw.h"
35#include "../../common/tracing.h"
36#include "../../common/grid.h"
37#include "../../shared/moveclip.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] == '*')
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
115 if (!model) {
116 return;
117 }
118 AABB absBox(model->cbmBox);
119 absBox.shift(model->origin);
120 GridBox rerouteBox(absBox);
121
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
145void CL_RecalcRouting (const le_t* le)
146{
148
150
152}
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{
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. */
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. */
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);
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);
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 */
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
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;
1492 le.flags &= ~LE_REMOVE_NEXT_FRAME;
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))
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
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{
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.
Definition: cl_actor.cpp:2134
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.
Definition: cl_camera.cpp:285
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
void init()
Definition: inv_shared.cpp:703
void emptyContainer(Inventory *const inv, const containerIndex_t container)
Clears the linked list of a container - removes all items from this container.
Definition: inventory.cpp:501
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
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:398
int Com_ServerState(void)
Check whether we are the server or have a singleplayer tactical mission.
Definition: common.cpp:538
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
#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
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
Definition: inv_shared.cpp:266
int INVSH_ShapeSize(const uint32_t shape)
Counts the used bits in a shape (item shape).
Definition: inv_shared.cpp:435
#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:333
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:314
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
int frame
Definition: r_entity.h:55
int32_t headnode
Definition: typedefs.h:29
vec3_t shift
Definition: typedefs.h:28
AABB cbmBox
Definition: typedefs.h:27
int tile
Definition: typedefs.h:31
vec3_t origin
Definition: typedefs.h:28
char name[MAX_QPATH]
Definition: typedefs.h:38
vec3_t origin
Definition: cl_camera.h:31
float frametime
Definition: client.h:59
connstate_t state
Definition: client.h:55
model_t * modelPool[MAX_OBJDEFS]
Definition: client.h:96
bool isOurRound() const
Definition: client.h:106
InventoryInterface i
Definition: client.h:101
mapTiles_t * mapTiles
le_t LEs[MAX_EDICTS]
const char * leInlineModelList[MAX_EDICTS+1]
model_t * model_draw[MAX_MODELS]
const struct cBspModel_s * model_clip[MAX_MODELS]
localModel_t LMs[MAX_LOCALMODELS]
int damStunGas
Definition: q_shared.h:533
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
int integer
Definition: cvar.h:81
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
int bounce
Definition: inv_shared.h:149
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 left
int contents
float angles[3]
int startTime
int STUN
void resetFloor()
ptl_t * ptl
actorModes_t actorMode
float volume
int morale
vec3_t origin
int thinkDelay
int dir
actorSizeEnum_t fieldSize
dvec_t dvtab[MAX_ROUTE]
leStep_t * stepList
int speed[MAX_ROUTE]
int maxMorale
int maxTU
int HP
const struct le_s * ref3
int pnum
int angle
void init()
float alpha
localEntitiyAddFunc_t addFunc
int gender
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
int right
byte actorMoveLength
The TUs that the current selected actor needs to walk to the current grid position marked by the mous...
int team
unsigned int headSkin
teamDef_t * teamDef
int TU
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
int ucn
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]
Routing routing
Definition: typedefs.h:341
TR_TILE_TYPE mapTiles[MAX_MAPTILES]
Definition: tracing.h:79
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
Definition: cl_renderer.h:118
vec3_t angles
Definition: cl_renderer.h:124
vec3_t s
Definition: cl_renderer.h:121
vec3_t v
Definition: cl_renderer.h:153
ptlDef_t * ctrl
Definition: cl_renderer.h:137
vec3_t omega
Definition: cl_renderer.h:155
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
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