UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cl_actor.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "../client.h"
27 #include "cl_actor.h"
28 #include "../cgame/cl_game.h"
29 #include "cl_hud.h"
30 #include "cl_parse.h"
31 #include "cl_particle.h"
32 #include "cl_view.h"
33 #include "../cl_screen.h"
34 #include "../ui/ui_main.h"
35 #include "../ui/ui_popup.h"
36 #include "../ui/node/ui_node_container.h"
37 #include "../renderer/r_entity.h"
38 #include "../renderer/r_mesh.h"
39 #include "../../common/routing.h"
40 #include "../../common/grid.h"
41 
47 
48 /* public */
62 static int mousePosTargettingAlign = 0;
63 
64 static le_t* mouseActor;
67 
73 void MSG_Write_PA (player_action_t playerAction, int entnum, ...)
74 {
75  va_list ap;
76  dbuffer msg;
77 
78  va_start(ap, entnum);
79  NET_WriteFormat(&msg, "bbs", clc_action, playerAction, entnum);
80  NET_vWriteFormat(&msg, pa_format[playerAction], ap);
81  va_end(ap);
83 }
84 
85 /*
86 ==============================================================
87 ACTOR MENU UPDATING
88 ==============================================================
89 */
90 
91 void CL_ActorSetFireDef (le_t* actor, const fireDef_t* fd)
92 {
93  if (actor->fd != fd)
95  actor->fd = fd;
96 }
97 
102 int CL_ActorMoveMode (const le_t* le)
103 {
104  assert(le);
105  if (!LE_IsCrouched(le))
106  return WALKTYPE_WALKING;
107 
108  /* Is the player using autostand? */
109  if (!cl_autostand->integer)
111 
112  /* ...and if this is a long walk... */
116 
118 }
119 
125 int CL_ActorGetNumber (const le_t* le)
126 {
127  assert(le);
128 
129  for (int actorIdx = 0; actorIdx < cl.numTeamList; actorIdx++) {
130  if (cl.teamList[actorIdx] == le)
131  return actorIdx;
132  }
133  return -1;
134 }
135 
142 {
143  for (int i = 0; i < cl.numTeamList; ++i) {
144  if (cl.teamList[i] && cl.teamList[i]->ucn == chr->ucn)
145  return cl.teamList[i];
146  }
147  return nullptr;
148 }
149 
156 {
157  const linkedList_t* chrList = cl.chrList;
158 
159  LIST_Foreach(chrList, character_t, chr) {
160  if (chr->ucn == le->ucn)
161  return chr;
162  }
163 
164  return nullptr;
165 }
166 
172 {
173  const character_t* chr = CL_ActorGetChr(shooter);
174  if (chr == nullptr)
175  return nullptr;
176 
177  const FiremodeSettings& fmSetting = chr->RFmode;
178  const Item* weapon = shooter->getHandItem(fmSetting.getHand());
179  if (weapon == nullptr)
180  return nullptr;
181 
182  const fireDef_t* fdArray = weapon->getFiredefs();
183  if (fdArray == nullptr)
184  return nullptr;
185 
186  const int fmIdx = fmSetting.getFmIdx();
187  if (fmIdx < 0 || fmIdx >= MAX_FIREDEFS_PER_WEAPON)
188  return nullptr;
189 
190  const fireDef_t* fd = &fdArray[fmIdx];
191  return fd;
192 }
193 
199 bool CL_ActorIsReactionFireOutOfRange (const le_t* shooter, const le_t* target)
200 {
201  const float distance = VectorDist(shooter->origin, target->origin);
202  const fireDef_t* fd = CL_ActorGetReactionFireFireDef(shooter);
203  const bool outOfRange = fd->range < distance;
204  return outOfRange;
205 }
206 
215 {
216  if (!le)
217  return -1;
218 
219  const character_t* chr = CL_ActorGetChr(le);
220  if (!chr) {
221  Com_DPrintf(DEBUG_CLIENT, "CL_ActorReservedTUs: No character found for le.\n");
222  return -1;
223  }
224 
225  const int reservedReaction = std::max(0, chr->reservedTus.reaction);
226  const int reservedCrouch = std::max(0, chr->reservedTus.crouch);
227  const int reservedShot = std::max(0, chr->reservedTus.shot);
228 
229  switch (type) {
230  case RES_ALL:
231  /* A summary of ALL TUs that are reserved. */
232  return reservedReaction + reservedCrouch + reservedShot;
233  case RES_ALL_ACTIVE: {
234  /* A summary of ALL TUs that are reserved depending on their "status". */
235  /* Only use reaction-value if we have RF activated. */
236  if ((le->state & STATE_REACTION))
237  return reservedReaction + reservedShot + reservedCrouch;
238  else
239  return reservedShot + reservedCrouch;
240  }
241  case RES_REACTION:
242  return reservedReaction;
243  case RES_CROUCH:
244  return reservedCrouch;
245  case RES_SHOT:
246  return reservedShot;
247  default:
248  Com_DPrintf(DEBUG_CLIENT, "CL_ActorReservedTUs: Bad type given: %i\n", type);
249  return -1;
250  }
251 }
252 
259 int CL_ActorUsableTUs (const le_t* le)
260 {
261  if (!le)
262  return -1;
263 
264  return le->TU - CL_ActorReservedTUs(le, RES_ALL_ACTIVE);
265 }
266 
273 void CL_ActorReserveTUs (const le_t* le, const reservation_types_t type, const int tus)
274 {
275  assert(type != RES_REACTION);
276 
277  if (!le || tus < 0)
278  return;
279 
280  character_t* chr = CL_ActorGetChr(le);
281  if (chr) {
282  chrReservations_t res = chr->reservedTus;
283 
284  if (type == RES_CROUCH)
285  res.crouch = tus;
286  else if (type == RES_SHOT)
287  res.shot = tus;
288 
290  }
291 }
292 
300 {
301  float mod = 0;
302 
303  if (le) {
304  const character_t* chr = CL_ActorGetChr(le);
305  if (!chr)
306  return 0;
307  const BodyData* bodyTemplate = chr->teamDef->bodyTemplate;
308  for (int bodyPart = 0; bodyPart < bodyTemplate->numBodyParts(); ++bodyPart) {
309  const int threshold = le->maxHP * bodyTemplate->woundThreshold(bodyPart);
310  const int injury = (le->wounds.woundLevel[bodyPart] + le->wounds.treatmentLevel[bodyPart] * 0.5);
311  if (injury > threshold)
312  mod += 2 * bodyTemplate->penalty(bodyPart, type) * injury / le->maxHP;
313  }
314 
315  switch (type) {
316  case MODIFIER_REACTION:
318  break;
319  case MODIFIER_ACCURACY:
320  case MODIFIER_SHOOTING:
321  ++mod;
322  break;
323  case MODIFIER_MOVEMENT:
324  mod = ceil(mod);
325  break;
326  default:
327  Com_Printf("CL_ActorInjuryPenalty: Unused modifier type %i\n", type);
328  mod = 0;
329  break;
330  }
331  }
332  return mod;
333 }
334 
342 int CL_ActorTimeForFireDef (const le_t* le, const fireDef_t* fd, bool reaction)
343 {
344  if (!fd)
345  return -1;
346 
347  return fd->time * CL_ActorInjuryModifier(le, reaction ? MODIFIER_REACTION : MODIFIER_SHOOTING);
348 }
349 
350 /*
351 ==============================================================
352 ACTOR SELECTION AND TEAM LIST
353 ==============================================================
354 */
355 
363 {
364  /* test team */
365  if (!le || le->team != cls.team || le->pnum != cl.pnum || LE_IsDead(le))
366  return;
367 
368  /* check list for that actor */
369  int actorIdx = CL_ActorGetNumber(le);
370 
371  /* add it */
372  if (actorIdx == -1) {
373  /* check list length */
374  const size_t size = lengthof(cl.teamList);
375  if (cl.numTeamList >= size) {
376  Com_Printf("Too many actors on the teamlist!\n");
377  return;
378  }
379  cl.teamList[cl.numTeamList] = le;
380  UI_ExecuteConfunc("hudenable %i", cl.numTeamList);
381  cl.numTeamList++;
382  if (cl.numTeamList == 1)
384  } else {
385  UI_ExecuteConfunc("hudenable %i", actorIdx);
386  }
387 }
388 
390 {
391  cls.i.destroyInventory(&le->inv);
392 }
393 
401 {
402  if (!le)
403  return;
404 
405  for (int i = 0; i < cl.numTeamList; i++) {
406  if (cl.teamList[i] == le) {
407  if (!LE_IsStunned(le)) {
408  CL_ActorCleanup(le);
409  /* remove from list */
410  cl.teamList[i] = nullptr;
411  }
412 
413  /* disable hud button */
414  UI_ExecuteConfunc("huddisable %i", i);
415 
416  break;
417  }
418  }
419 
420  /* check selection */
421  if (LE_IsSelected(le)) {
422  int i;
423  for (i = 0; i < cl.numTeamList; i++) {
424  le_t* tl = cl.teamList[i];
425  if (tl && CL_ActorSelect(tl))
426  break;
427  }
428 
429  if (i == cl.numTeamList)
430  CL_ActorSelect(nullptr);
431  }
432 }
433 
441 {
442  /* test team */
443  if (!le) {
444  if (selActor)
445  selActor->flags &= ~LE_SELECTED;
446  selActor = nullptr;
447  ui_inventory = nullptr;
448  return false;
449  }
450 
451  if (le->pnum != cl.pnum || LE_IsDead(le) || !le->inuse)
452  return false;
453 
454  if (LE_IsSelected(le)) {
456  return true;
457  }
458 
459  if (selActor)
460  selActor->flags &= ~LE_SELECTED;
461 
463  selActor = le;
464  selActor->flags |= LE_SELECTED;
465  ui_inventory = &selActor->inv;
466 
467  if (le->state & RF_IRGOGGLESSHOT)
469  else
471 
472  if (le->clientAction != nullptr)
473  UI_ExecuteConfunc("enable_clientaction");
474  else
475  UI_ExecuteConfunc("disable_clientaction");
476 
477  const int actorIdx = CL_ActorGetNumber(le);
478  if (actorIdx == -1)
479  return false;
480 
481  /* console commands, update cvars */
482  Cvar_ForceSet("cl_selected", va("%i", actorIdx));
483 
484  const character_t* chr = CL_ActorGetChr(le);
485  if (!chr)
486  Com_Error(ERR_DROP, "No character given for local entity!");
487 
489 
491 
492  return true;
493 }
494 
506 bool CL_ActorSelectList (int num)
507 {
508  /* check if actor exists */
509  if (num >= cl.numTeamList || num < 0)
510  return false;
511 
512  /* select actor */
513  le_t* le = cl.teamList[num];
514  if (!le || !CL_ActorSelect(le))
515  return false;
516 
517  /* center view (if wanted) */
518  LE_CenterView(le);
519  Cvar_SetValue("cl_worldlevel", le->pos[2]);
520 
521  return true;
522 }
523 
528 {
529  int selIndex = -1;
530  const int num = cl.numTeamList;
531 
532  /* find index of currently selected actor */
533  for (int i = 0; i < num; i++) {
534  const le_t* le = cl.teamList[i];
535  if (le && le->inuse && LE_IsSelected(le) && !LE_IsDead(le)) {
536  selIndex = i;
537  break;
538  }
539  }
540  if (selIndex < 0)
541  return false; /* no one selected? */
542 
543  /* cycle round */
544  int i = selIndex;
545  while (true) {
546  i = (i + 1) % num;
547  if (i == selIndex)
548  break;
549  if (CL_ActorSelectList(i))
550  return true;
551  }
552  return false;
553 }
554 
559 {
560  int selIndex = -1;
561  const int num = cl.numTeamList;
562 
563  /* find index of currently selected actor */
564  for (int i = 0; i < num; i++) {
565  const le_t* le = cl.teamList[i];
566  if (le && le->inuse && LE_IsSelected(le) && !LE_IsDead(le)) {
567  selIndex = i;
568  break;
569  }
570  }
571  if (selIndex < 0)
572  return false; /* no one selected? */
573 
574  /* cycle round */
575  int i = selIndex;
576  while (true) {
577  /* i = (i - 1) % num; */
578  i--;
579  if (i < 0)
580  i = num - 1;
581 
582  if (i == selIndex)
583  break;
584  if (CL_ActorSelectList(i))
585  return true;
586  }
587  return false;
588 }
589 
590 
591 /*
592 ==============================================================
593 ACTOR MOVEMENT AND SHOOTING
594 ==============================================================
595 */
596 
603 
612 static void CL_BuildForbiddenList (void)
613 {
614  le_t* le = nullptr;
615 
616  forbiddenList.reset();
617 
618  while ((le = LE_GetNextInUse(le))) {
619  if (LE_IsInvisible(le))
620  continue;
621  /* Dead ugv will stop walking, too. */
622  if (le->type == ET_ACTOR2x2 || (!LE_IsStunned(le) && LE_IsLivingAndVisibleActor(le))) {
623  forbiddenList.add(le->pos, (byte*)&le->fieldSize);
624  }
625  }
626 }
627 
628 #ifdef DEBUG
629 
635 static void CL_DisplayBlockedPaths_f (void)
636 {
637  le_t* le = nullptr;
638 
639  while ((le = LE_GetNextInUse(le))) {
640  vec3_t s;
641  switch (le->type) {
642  case ET_ACTOR:
643  case ET_ACTOR2x2:
644  /* draw blocking cursor at le->pos */
645  if (!LE_IsDead(le))
646  Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, s);
647  break;
648  case ET_DOOR:
649  case ET_BREAKABLE:
650  case ET_ROTATING:
651  VectorCopy(le->origin, s);
652  break;
653  default:
654  continue;
655  }
656 
657  ptl_t* ptl = CL_ParticleSpawn("blocked_field", 0, s, nullptr, nullptr);
658  ptl->rounds = 2;
659  ptl->roundsCnt = 2;
660  ptl->life = 10000;
661  ptl->t = 0;
662  if (le->fieldSize == ACTOR_SIZE_2x2) {
663  /* If this actor blocks 4 fields draw them as well. */
664  for (int j = 0; j < 3; j++) {
665  ptl_t* ptl2 = CL_ParticleSpawn("blocked_field", 0, s, nullptr, nullptr);
666  ptl2->rounds = ptl->rounds;
667  ptl2->roundsCnt = ptl->roundsCnt;
668  ptl2->life = ptl->life;
669  ptl2->t = ptl->t;
670  }
671  }
672  }
673 }
674 #endif
675 
683 {
685  if (le && LE_IsSelected(le)) {
686  Grid_CalcPathing(cl.mapData->routing, le->fieldSize, &cl.pathMap, le->pos, MAX_ROUTE_TUS, &forbiddenList);
688  }
689 }
690 
695 {
696  le_t* closest = nullptr;
697  le_t* le = nullptr;
698  while ((le = LE_GetNextInUse(le))) {
699  if (le->team != team || !LE_IsLivingAndVisibleActor(le))
700  continue;
701 
702  if (closest == nullptr || VectorDist(le->origin, origin) < VectorDist(closest->origin, origin))
703  closest = le;
704  }
705  return closest;
706 }
707 
713 int CL_ActorCheckAction (const le_t* le)
714 {
715  if (!le)
716  return false;
717 
718  if (le->isMoving())
719  return false;
720 
721  if (!cls.isOurRound()) {
722  HUD_DisplayMessage(_("It is not your turn!"));
723  return false;
724  }
725 
726  return true;
727 }
728 
737 static byte CL_ActorMoveLength (const le_t* le, const pos3_t to)
738 {
739  const bool useAutostand = LE_IsCrouched(le) && cl_autostand->integer
741  const int autostandTU = useAutostand ? 2 * TU_CROUCH : 0;
742  byte crouchingState = LE_IsCrouched(le) && !useAutostand ? 1 : 0;
743  const int length = Grid_MoveLength(&cl.pathMap, to, crouchingState, false);
744 
745  if (!length || length == ROUTING_NOT_REACHABLE)
746  return length;
747 
748  pos3_t pos;
749  VectorCopy(to, pos);
750  int dvec, numSteps = 0;
751  while ((dvec = Grid_MoveNext(&cl.pathMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
752  ++numSteps;
753  PosSubDV(pos, crouchingState, dvec); /* We are going backwards to the origin. */
754  }
755 
756  return std::min(ROUTING_NOT_REACHABLE, length + static_cast<int>(numSteps
757  * CL_ActorInjuryModifier(le, MODIFIER_MOVEMENT)) + autostandTU);
758 }
759 
765 {
767 }
768 
776 static bool CL_ActorTraceMove (const pos3_t to)
777 {
778  if (!selActor)
779  return false;
780 
781  byte length = CL_ActorMoveLength(selActor, to);
782  if (!length || length >= ROUTING_NOT_REACHABLE)
783  return false;
784 
785 
786  vec3_t oldVec;
787  Grid_PosToVec(cl.mapData->routing, selActor->fieldSize, to, oldVec);
788  pos3_t pos;
789  VectorCopy(to, pos);
790 
791  int dvec;
792  byte crouchingState = LE_IsCrouched(selActor) ? 1 : 0;
793  while ((dvec = Grid_MoveNext(&cl.pathMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
794  length = CL_ActorMoveLength(selActor, pos);
795  PosSubDV(pos, crouchingState, dvec); /* We are going backwards to the origin. */
796  vec3_t vec;
797  Grid_PosToVec(cl.mapData->routing, selActor->fieldSize, pos, vec);
798  if (length > CL_ActorUsableTUs(selActor))
799  CL_ParticleSpawn("longRangeTracer", 0, vec, oldVec);
800  else if (crouchingState)
801  CL_ParticleSpawn("crawlTracer", 0, vec, oldVec);
802  else
803  CL_ParticleSpawn("moveTracer", 0, vec, oldVec);
804  VectorCopy(vec, oldVec);
805  }
806  return true;
807 }
808 
816 static void CL_ActorMaximumMove (const pos3_t to, const le_t* le, pos3_t pos)
817 {
818  const byte length = CL_ActorMoveLength(le, to);
819  if (!length || length >= ROUTING_NOT_REACHABLE)
820  return;
821 
822  VectorCopy(to, pos);
823 
824  int dvec;
825  byte crouchingState = le && LE_IsCrouched(le) ? 1 : 0;
826  const int tus = CL_ActorUsableTUs(le);
827  while ((dvec = Grid_MoveNext(&cl.pathMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
828  const byte length2 = CL_ActorMoveLength(le, pos);
829  if (length2 <= tus)
830  return;
831  PosSubDV(pos, crouchingState, dvec); /* We are going backwards to the origin. */
832  }
833 }
834 
835 void CL_ActorSetMode (le_t* actor, actorModes_t actorMode)
836 {
837  actor->actorMode = actorMode;
838 }
839 
847 void CL_ActorStartMove (le_t* le, const pos3_t to)
848 {
849  if (IN_GetMouseSpace() != MS_WORLD)
850  return;
851 
852  if (!CL_ActorCheckAction(le))
853  return;
854 
855  byte length = CL_ActorMoveLength(le, to);
856 
857  if (!length || length >= ROUTING_NOT_REACHABLE) {
858  /* move not valid, don't even care to send */
859  return;
860  }
861 
862  /* Get the last position we can walk to with the usable TUs. */
863  pos3_t toReal;
864  CL_ActorMaximumMove(to, le, toReal);
865 
866  /* Get the cost of the new position just in case. */
867  length = CL_ActorMoveLength(le, toReal);
868 
869  if (CL_ActorUsableTUs(le) < length) {
870  /* We do not have enough _usable_ TUs to move so don't even try to send. */
871  /* This includes a check for reserved TUs (which isn't done on the server!) */
872  return;
873  }
874 
875  /* change mode to move now */
876  CL_ActorSetMode(le, M_MOVE);
877 
878  /* move seems to be possible; send request to server */
879  MSG_Write_PA(PA_MOVE, le->entnum, toReal);
880 }
881 
882 
888 void CL_ActorShoot (const le_t* le, const pos3_t at)
889 {
890  if (IN_GetMouseSpace() != MS_WORLD)
891  return;
892 
893  if (!CL_ActorCheckAction(le))
894  return;
895 
896  int type;
897  if (IS_MODE_FIRE_RIGHT(le->actorMode)) {
898  type = ST_RIGHT;
899  } else if (IS_MODE_FIRE_LEFT(le->actorMode)) {
900  type = ST_LEFT;
901  } else if (IS_MODE_FIRE_HEADGEAR(le->actorMode)) {
902  type = ST_HEADGEAR;
903  } else
904  return;
905 
907 }
908 
916 int CL_ActorGetContainerForReload (Item** ammoItem, const Inventory* inv, const objDef_t* weapon)
917 {
918  int tu = 100;
919  containerIndex_t bestContainer = NONE;
920 
921  /* also search the linked ground floor tile (temp container) */
922  for (containerIndex_t container = 0; container < CID_MAX; container++) {
923  if (INVDEF(container)->out >= tu)
924  continue;
925  /* Once we've found at least one clip, there's no point
926  * searching other containers if it would take longer
927  * to retrieve the ammo from them than the one
928  * we've already found. */
929  for (Item* item = inv->getContainer2(container); item; item = item->getNext()) {
930  const objDef_t* od = item->def();
931  if (!od->isLoadableInWeapon(weapon) || !GAME_ItemIsUseable(od))
932  continue;
933  tu = INVDEF(container)->out;
934  bestContainer = container;
935  *ammoItem = item;
936  break;
937  }
938  }
939  return bestContainer;
940 }
941 
948 void CL_ActorReload (le_t* le, containerIndex_t containerID)
949 {
950  if (!CL_ActorCheckAction(le))
951  return;
952 
953  /* check weapon */
954  Inventory* inv = &le->inv;
955 
956  const objDef_t* weapon;
957  if (inv->getContainer2(containerID)) {
958  weapon = inv->getContainer2(containerID)->def();
959  } else if (containerID == CID_LEFT && inv->getContainer2(CID_RIGHT)->isHeldTwoHanded()) {
960  /* Check for two-handed weapon */
961  containerID = CID_RIGHT;
962  weapon = inv->getContainer2(containerID)->def();
963  } else {
964  /* no weapon in the reloadable containers found */
965  return;
966  }
967 
968  if (!weapon)
969  return;
970 
971  /* return if the weapon is not reloadable */
972  if (!weapon->isReloadable())
973  return;
974 
975  if (!GAME_ItemIsUseable(weapon)) {
976  HUD_DisplayMessage(_("You cannot reload this unknown item."));
977  return;
978  }
979 
980  Item* ic;
981  const containerIndex_t bestContainer = CL_ActorGetContainerForReload(&ic, inv, weapon);
982  /* send request */
983  if (bestContainer != NONE) {
984  int x, y;
985 
986  ic->getFirstShapePosition(&x, &y);
987  x += ic->getX();
988  y += ic->getY();
989 
990  CL_ActorInvMove(le, bestContainer, x, y, containerID, 0, 0);
991  }
992 }
993 
1004 void CL_ActorInvMove (const le_t* le, containerIndex_t fromContainer, int fromX, int fromY, containerIndex_t toContainer, int toX, int toY)
1005 {
1006  assert(CL_BattlescapeRunning());
1007  assert(le);
1008  assert(LE_IsActor(le));
1009 
1010  const invDef_t* fromPtr = INVDEF(fromContainer);
1011  const Item* item = le->inv.getItemAtPos(fromPtr, fromX, fromY);
1012 
1013  if (item != nullptr) {
1014  const character_t* chr = CL_ActorGetChr(le);
1015  if (!le->inv.canHoldItemWeight(fromContainer, toContainer, *item, GAME_GetChrMaxLoad(chr))) {
1016  UI_Popup(_("Warning"), _("This soldier can not carry anything else."));
1017  return;
1018  }
1019  MSG_Write_PA(PA_INVMOVE, le->entnum, fromContainer, fromX, fromY, toContainer, toX, toY);
1020  }
1021 }
1022 
1027 static void CL_ActorUse (const le_t* le)
1028 {
1029  if (!CL_ActorCheckAction(le))
1030  return;
1031 
1032  assert(le->clientAction);
1033 
1035  Com_DPrintf(DEBUG_CLIENT, "CL_ActorUse: Use door number: %i (actor %i).\n", le->clientAction->entnum, le->entnum);
1036 }
1037 
1041 static void CL_ActorUse_f (void)
1042 {
1043  le_t* actor = selActor;
1044 
1045  if (!CL_ActorCheckAction(actor))
1046  return;
1047 
1048  /* no client action */
1049  if (actor->clientAction == nullptr) {
1050  Com_DPrintf(DEBUG_CLIENT, "CL_ActorUse_f: No client_action set for actor with entnum %i.\n", actor->entnum);
1051  return;
1052  }
1053 
1054  if (LE_IsDoor(actor->clientAction)) {
1055  /* Check if we should even try to send this command (no TUs left or). */
1056  if (CL_ActorUsableTUs(actor) >= TU_DOOR_ACTION)
1057  CL_ActorUse(actor);
1058  }
1059 }
1060 
1067 {
1068  return IS_MODE_FIRE_RIGHT(mode) || IS_MODE_FIRE_LEFT(mode) || IS_MODE_FIRE_HEADGEAR(mode);
1069 }
1070 
1075 {
1076  if (IN_GetMouseSpace() != MS_WORLD)
1077  return;
1078 
1079  if (!CL_ActorCheckAction(selActor))
1080  return;
1081 
1082  if (CL_ActorUsableTUs(selActor) < TU_TURN) {
1083  /* Cannot turn because of not enough usable TUs. */
1084  return;
1085  }
1086 
1087  /* check for fire-modes, and cancel them */
1088  if (CL_ActorFireModeActivated(selActor->actorMode)) {
1090  return; /* and return without turning */
1091  }
1092 
1093  /* calculate dvec */
1094  vec3_t directionVector;
1095  VectorSubtract(mousePos, selActor->pos, directionVector);
1096  const dvec_t dvec = AngleToDV((int) (atan2(directionVector[1], directionVector[0]) * todeg));
1097 
1098  /* send message to server */
1099  MSG_Write_PA(PA_TURN, selActor->entnum, dvec);
1100 }
1101 
1105 static void CL_ActorStandCrouch_f (void)
1106 {
1107  if (!CL_ActorCheckAction(selActor))
1108  return;
1109 
1110  /* In case of standing up also check the headroom */
1111  if (LE_IsCrouched(selActor) && !RT_CanActorStandHere(cl.mapData->routing, selActor->fieldSize, selActor->pos))
1112  return;
1113 
1114  /* Check if we should even try to send this command (no TUs left or). */
1115  if (CL_ActorUsableTUs(selActor) >= TU_CROUCH || CL_ActorReservedTUs(selActor, RES_CROUCH) >= TU_CROUCH) {
1116  /* send a request to toggle crouch to the server */
1118  }
1119 }
1120 
1124 static void CL_ActorUseHeadgear_f (void)
1125 {
1126  const mouseSpace_t tmpMouseSpace = IN_GetMouseSpace();
1127 
1128  /* this can be executed by a click on a hud button
1129  * but we need MS_WORLD mouse space to let the shooting
1130  * function work */
1132 
1133  if (!CL_ActorCheckAction(selActor))
1134  return;
1135 
1136  Item* headgear = selActor->inv.getHeadgear();
1137  if (!headgear)
1138  return;
1139 
1140  CL_ActorSetMode(selActor, M_FIRE_HEADGEAR);
1142  selActor->currentSelectedFiremode = 0;
1143  CL_ActorShoot(selActor, selActor->pos);
1144  CL_ActorSetMode(selActor, M_MOVE);
1145 
1146  /* restore old mouse space */
1147  IN_SetMouseSpace(tmpMouseSpace);
1148 }
1149 
1150 /*
1151 ==============================================================
1152 MOUSE INPUT
1153 ==============================================================
1154 */
1155 
1161 static void CL_ActorMoveMouse (void)
1162 {
1163  /* Don't display the cursor if it's above the currently selected level.
1164  * The 2nd part of the if is an attempt to display it anyway when we eg. climb a hill.
1165  * But there are too many situations inside buildings that match the criteria (eg. actorclip around chair).
1166  * So disabled for now.*/
1167  if (mousePos[2] > cl_worldlevel->integer/* && !RT_AllCellsBelowAreFilled(cl.mapData->map, fieldSize, pos)*/)
1168  return;
1169 
1170  if (selActor->actorMode == M_PEND_MOVE) {
1171  if (VectorCompare(mousePos, selActor->mousePendPos)) {
1172  /* Pending move and clicked the same spot (i.e. 2 clicks on the same place) */
1173  CL_ActorStartMove(selActor, mousePos);
1174  } else {
1175  /* Clicked different spot. */
1176  VectorCopy(mousePos, selActor->mousePendPos);
1177  }
1178  } else {
1179  /* either we want to confirm every move, or it's not our round and we prepare the
1180  * movement for the next round */
1181  if (confirm_actions->integer || !cls.isOurRound()) {
1182  /* Set our mode to pending move. */
1183  VectorCopy(mousePos, selActor->mousePendPos);
1184 
1185  CL_ActorSetMode(selActor, M_PEND_MOVE);
1186  } else {
1187  /* Just move there */
1188  CL_ActorStartMove(selActor, mousePos);
1189  }
1190  }
1191 }
1192 
1198 {
1199  if (IN_GetMouseSpace() != MS_WORLD || !selActor)
1200  return;
1201 
1202  switch (selActor->actorMode) {
1203  case M_MOVE:
1204  case M_PEND_MOVE:
1205  /* Try and select another team member */
1206  if (mouseActor && !LE_IsSelected(mouseActor) && CL_ActorSelect(mouseActor)) {
1207  /* Succeeded so go back into move mode. */
1208  CL_ActorSetMode(selActor, M_MOVE);
1209  } else if (interactEntity) {
1210  CL_ActorUse(selActor);
1211  } else {
1213  }
1214  break;
1215  case M_PEND_FIRE_R:
1216  case M_PEND_FIRE_L:
1217  if (VectorCompare(mousePos, selActor->mousePendPos)) {
1218  /* Pending shot and clicked the same spot (i.e. 2 clicks on the same place) */
1219  CL_ActorShoot(selActor, mousePos);
1220 
1221  /* We switch back to aiming mode. */
1222  if (selActor->actorMode == M_PEND_FIRE_R)
1223  CL_ActorSetMode(selActor, M_FIRE_R);
1224  else
1225  CL_ActorSetMode(selActor, M_FIRE_L);
1226  } else {
1227  /* Clicked different spot. */
1228  VectorCopy(mousePos, selActor->mousePendPos);
1229  }
1230  break;
1231  case M_FIRE_R:
1232  if (mouseActor && LE_IsSelected(mouseActor))
1233  break;
1234 
1235  /* We either switch to "pending" fire-mode or fire the gun. */
1236  if (confirm_actions->integer == 1) {
1237  CL_ActorSetMode(selActor, M_PEND_FIRE_R);
1238  VectorCopy(mousePos, selActor->mousePendPos);
1239  } else {
1240  CL_ActorShoot(selActor, mousePos);
1241  }
1242  break;
1243  case M_FIRE_L:
1244  if (mouseActor && LE_IsSelected(mouseActor))
1245  break;
1246 
1247  /* We either switch to "pending" fire-mode or fire the gun. */
1248  if (confirm_actions->integer == 1) {
1249  CL_ActorSetMode(selActor, M_PEND_FIRE_L);
1250  VectorCopy(mousePos, selActor->mousePendPos);
1251  } else {
1252  CL_ActorShoot(selActor, mousePos);
1253  }
1254  break;
1255  default:
1256  break;
1257  }
1258 }
1259 
1260 
1267 {
1268  if (!selActor || IN_GetMouseSpace() != MS_WORLD)
1269  return;
1270 
1271  if (CL_ActorFireModeActivated(selActor->actorMode)) {
1272  CL_ActorSetMode(selActor, M_MOVE);
1273  }
1274 }
1275 
1276 /*
1277 ==============================================================
1278 MOUSE SCANNING
1279 ==============================================================
1280 */
1281 
1290 void CL_GetWorldCoordsUnderMouse (vec3_t groundIntersection, vec3_t upperTracePoint, vec3_t lowerTracePoint)
1291 {
1292  /* TODO: Move this to cl_battlescape.cpp? This function is not directly related to actors. */
1293 
1294  /* get cursor position as a -1 to +1 range for projection */
1295  float cur[2];
1296  cur[0] = (mousePosX * viddef.rx - viddef.viewWidth * 0.5 - viddef.x) / (viddef.viewWidth * 0.5);
1297  cur[1] = (mousePosY * viddef.ry - viddef.viewHeight * 0.5 - viddef.y) / (viddef.viewHeight * 0.5);
1298 
1299  /* get trace vectors */
1300  vec3_t from, forward, right, up;
1301  VectorCopy(cl.cam.camorg, from);
1302  VectorCopy(cl.cam.axis[0], forward);
1303  VectorCopy(cl.cam.axis[1], right);
1304  VectorCopy(cl.cam.axis[2], up);
1305 
1306  float frustumSlope[2];
1307  const float projectionDistance = 2048.0f;
1308  if (cl_isometric->integer)
1309  frustumSlope[0] = 10.0 * refdef.fieldOfViewX;
1310  else
1311  frustumSlope[0] = tan(refdef.fieldOfViewX * (M_PI / 360.0)) * projectionDistance;
1312  frustumSlope[1] = frustumSlope[0] * ((float)viddef.viewHeight / (float)viddef.viewWidth);
1313 
1314  /* transform cursor position into perspective space */
1315  vec3_t stop;
1316  VectorMA(from, projectionDistance, forward, stop);
1317  VectorMA(stop, cur[0] * frustumSlope[0], right, stop);
1318  VectorMA(stop, cur[1] * -frustumSlope[1], up, stop);
1319 
1320  /* in isometric mode the camera position has to be calculated from the cursor position so that the trace goes in the right direction */
1321  if (cl_isometric->integer)
1322  VectorMA(stop, -projectionDistance * 2, forward, from);
1323 
1324  /* set stop point to the intersection of the trace line with the desired plane */
1325  /* description of maths used:
1326  * The equation for the plane can be written:
1327  * mapNormal dot (end - P3) = 0
1328  * where mapNormal is the vector normal to the plane,
1329  * P3 is any point on the plane and
1330  * end is the point where the line intersects the plane
1331  * All points on the line can be calculated using:
1332  * P1 + u*(P2 - P1)
1333  * where P1 and P2 are points that define the line and
1334  * u is some scalar
1335  * The intersection of the line and plane occurs when:
1336  * mapNormal dot (P1 + u*(P2 - P1)) == mapNormal dot P3
1337  * The intersection therefore occurs when:
1338  * u = (mapNormal dot (P3 - P1))/(mapNormal dot (P2 - P1))
1339  * Note: in the code below from & stop represent P1 and P2 respectively
1340  */
1341  vec3_t mapNormal, P3, P2minusP1;
1343  VectorSet(mapNormal, 0., 0., 1.);
1344  VectorSubtract(stop, from, P2minusP1);
1345  const float nDotP2minusP1 = DotProduct(mapNormal, P2minusP1);
1346 
1347  /* calculate intersection directly if angle is not parallel to the map plane */
1348  vec3_t end;
1349  if (nDotP2minusP1 > 0.01 || nDotP2minusP1 < -0.01) {
1350  vec3_t dir, P3minusP1;
1351 
1352  VectorSubtract(P3, from, P3minusP1);
1353  const float u = DotProduct(mapNormal, P3minusP1) / nDotP2minusP1;
1354  VectorScale(P2minusP1, (vec_t)u, dir);
1355  VectorAdd(from, dir, end);
1356  } else { /* otherwise do a full trace */
1358  }
1359 
1360  if (groundIntersection)
1361  VectorCopy(end, groundIntersection);
1362  if (upperTracePoint)
1363  VectorCopy(from, upperTracePoint);
1364  if (lowerTracePoint)
1365  VectorCopy(stop, lowerTracePoint);
1366 }
1367 
1374 {
1375  vec3_t from, stop, end;
1376  CL_GetWorldCoordsUnderMouse(end, from, stop);
1377  pos3_t testPos;
1378  VecToPos(end, testPos);
1379  /* hack to prevent cursor from getting stuck on the top of an invisible
1380  * playerclip surface (in most cases anyway) */
1381  vec3_t pA;
1382  PosToVec(testPos, pA);
1383  /* ensure that the cursor is in the world, if this is not done, the tracer box is
1384  * drawn in the void on the first level and the menu key bindings might get executed
1385  * this could result in different problems like the zooming issue (where you can't zoom
1386  * in again, because in_zoomout->state is not reseted). */
1387  if (CL_OutsideMap(pA, MAP_SIZE_OFFSET))
1388  return false;
1389 
1390  vec3_t pB;
1391  VectorCopy(pA, pB);
1392  pA[2] += UNIT_HEIGHT;
1393  pB[2] -= UNIT_HEIGHT;
1397  pos_t restingLevel = Grid_Fall(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), testPos);
1398  vec3_t pC;
1400  VecToPos(pC, testPos);
1401  /* VecToPos strictly rounds the values down, while routing will round floors up to the next QUANT.
1402  * This makes a huge diffence when calculating the z-level:
1403  * without compensation, z of 61-63 will belong to the level below. */
1404  testPos[2] = ModelFloorToQuant(pC[2] / CELL_HEIGHT);
1405 
1406  restingLevel = std::min(restingLevel, Grid_Fall(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), testPos));
1407 
1408  /* if grid below intersection level, start a trace from the intersection */
1409  if (restingLevel < cl_worldlevel->integer) {
1410  VectorCopy(end, from);
1411  from[2] -= CURSOR_OFFSET;
1413  VecToPos(end, testPos);
1414  restingLevel = Grid_Fall(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), testPos);
1415  }
1416 
1417  /* test if the selected grid is out of the world */
1418  if (restingLevel >= PATHFINDING_HEIGHT)
1419  return false;
1420 
1421  /* Set truePos- test pos is under the cursor. */
1422  VectorCopy(testPos, truePos);
1424 
1425  /* Set mousePos to the position that the actor will move to. */
1426  testPos[2] = restingLevel;
1427  VectorCopy(testPos, mousePos);
1428 
1429  le_t* interactLe = CL_BattlescapeSearchAtGridPos(mousePos, false, selActor);
1430  if (interactLe != nullptr && LE_IsActor(interactLe)) {
1431  mouseActor = interactLe;
1432  interactEntity = nullptr;
1433  } else if (selActor != nullptr && selActor->clientAction == interactLe) {
1434  interactEntity = interactLe;
1435  mouseActor = nullptr;
1436  } else {
1437  interactEntity = nullptr;
1438  mouseActor = nullptr;
1439  }
1440 
1441  if (interactEntity != nullptr) {
1442  SCR_ChangeCursor(2);
1443  } else {
1444  SCR_ChangeCursor(1);
1445  }
1446 
1447  /* calculate move length */
1448  if (selActor && !VectorCompare(mousePos, mouseLastPos)) {
1450  CL_ActorResetMoveLength(selActor);
1451  }
1452 
1453  return true;
1454 }
1455 
1460 {
1461  CL_GetWorldCoordsUnderMouse(mouseDraggingPos, nullptr, nullptr);
1462 }
1463 
1468 {
1469  /* TODO: the movement is snapping to the cell center, and is clunky - make it smooth */
1470  /* Difference between last and currently selected cell, we'll move camera by that difference */
1471  vec3_t currentMousePos;
1472 
1473  CL_GetWorldCoordsUnderMouse(currentMousePos, nullptr, nullptr);
1474  if (fabs(currentMousePos[0] - mouseDraggingPos[0]) + fabs(currentMousePos[1] - mouseDraggingPos[1]) < 0.5f)
1475  return;
1476 
1477  vec3_t mousePosDiff;
1478  VectorSubtract(mouseDraggingPos, currentMousePos, mousePosDiff);
1479  VectorMA(cl.cam.origin, 0.2f, mousePosDiff, cl.cam.origin); /* Move camera slowly to the dest point, to prevent shaking */
1480  Cvar_SetValue("cl_worldlevel", truePos[2]); /* Do not change world level */
1481 }
1482 
1483 /*
1484 ==============================================================
1485 ACTOR GRAPHICS
1486 ==============================================================
1487 */
1488 
1495 static inline bool CL_AddActorWeapon (int objID)
1496 {
1497  if (objID != NONE) {
1498  const objDef_t* od = INVSH_GetItemByIDX(objID);
1499  if (od->isVirtual)
1500  return false;
1501  return true;
1502  }
1503  return false;
1504 }
1505 
1515 bool CL_AddActor (le_t* le, entity_t* ent)
1516 {
1517  if (!cl_showactors->integer)
1518  return false;
1519 
1520  const bool hasTagHead = R_GetTagIndexByName(le->model1, "tag_head") != -1;
1521 
1522  if (LE_IsStunned(le)) {
1523  if (!le->ptl) {
1524  int levelflags = 0;
1525  for (int i = le->pos[2]; i < PATHFINDING_HEIGHT; ++i)
1526  levelflags |= (1 << i);
1527  le->ptl = CL_ParticleSpawn("stunnedactor", levelflags, le->origin);
1528  }
1529  } else if (!LE_IsDead(le)) {
1530  /* add the weapons to the actor's hands */
1531  const bool addLeftHandWeapon = CL_AddActorWeapon(le->left);
1532  const bool addRightHandWeapon = CL_AddActorWeapon(le->right);
1533  const int delta = hasTagHead ? 2 : 1;
1534  /* add left hand weapon */
1535  if (addLeftHandWeapon) {
1536  entity_t leftHand(RF_NONE);
1537 
1538  leftHand.model = cls.modelPool[le->left];
1539  if (!leftHand.model)
1540  Com_Error(ERR_DROP, "Actor model for left hand weapon wasn't found!");
1541 
1542  /* point to the body ent which will be added last */
1543  leftHand.tagent = R_GetFreeEntity() + delta + addRightHandWeapon;
1544  leftHand.tagname = "tag_lweapon";
1545 
1546  R_AddEntity(&leftHand);
1547  }
1548 
1549  /* add right hand weapon */
1550  if (addRightHandWeapon) {
1551  entity_t rightHand(RF_NONE);
1552 
1553  rightHand.alpha = le->alpha;
1554  rightHand.model = cls.modelPool[le->right];
1555  if (!rightHand.model)
1556  Com_Error(ERR_DROP, "Actor model for right hand weapon wasn't found!");
1557 
1558  /* point to the body ent which will be added last */
1559  rightHand.tagent = R_GetFreeEntity() + delta;
1560  rightHand.tagname = "tag_rweapon";
1561 
1562  R_AddEntity(&rightHand);
1563  }
1564  }
1565 
1566  if (hasTagHead) {
1567  /* add head */
1568  entity_t head(RF_NONE);
1569 
1570  head.alpha = le->alpha;
1571  head.model = le->model2;
1572  if (!head.model)
1573  Com_Error(ERR_DROP, "Actor model wasn't found!");
1574  head.skinnum = le->headSkin;
1575 
1576  /* point to the body ent which will be added last */
1577  head.tagent = R_GetFreeEntity() + 1;
1578  head.tagname = "tag_head";
1579 
1580  if (le->team != cls.team)
1581  head.flags |= RF_IRGOGGLES;
1582 
1583  R_AddEntity(&head);
1584  }
1585 
1589  if (LE_IsStunned(le) && le->HP <= le->maxHP / 2)
1590  ent->flags |= RF_BLOOD;
1591  else if (LE_IsDead(le))
1592  ent->flags |= RF_BLOOD;
1593  else
1594  ent->flags |= RF_SHADOW;
1595 
1596  ent->flags |= RF_ACTOR;
1597  /* actors are highlighted if some other actor uses ir goggles */
1598  if (le->team != cls.team)
1599  ent->flags |= RF_IRGOGGLES;
1600 
1601  if (!LE_IsDead(le) && !LE_IsStunned(le)) {
1602  if (LE_IsSelected(le))
1603  ent->flags |= RF_SELECTED;
1604  if (le->team == cls.team) {
1605  if (le->pnum == cl.pnum)
1606  ent->flags |= RF_MEMBER;
1607  if (le->pnum != cl.pnum)
1608  ent->flags |= RF_ALLIED;
1609  } else {
1610  ent->flags |= RF_OPPONENT;
1611  }
1612  if (le->team == TEAM_CIVILIAN)
1613  ent->flags |= RF_NEUTRAL;
1614  }
1615 
1616  if (ent->flags & RF_BLOOD) {
1617  const char* deathTextureName;
1618  assert(le->teamDef != nullptr);
1619  deathTextureName = le->teamDef->deathTextureName;
1620  ent->texture = R_FindImage(deathTextureName, it_effect);
1621  }
1622 
1623  return true;
1624 }
1625 
1626 /*
1627 ==============================================================
1628 TARGETING GRAPHICS
1629 ==============================================================
1630 */
1631 
1637 static void CL_TargetingRadius (const vec3_t center, const float radius)
1638 {
1639  ptl_t* particle = CL_ParticleSpawn("circle", 0, center);
1640  if (particle != nullptr)
1641  particle->size[0] = radius;
1642 }
1643 
1654 static void CL_TargetingStraight (const pos3_t fromPos, actorSizeEnum_t fromActorSize, const pos3_t toPos)
1655 {
1656  if (!selActor || !selActor->fd)
1657  return;
1658 
1659  /* search for an actor at target */
1660  le_t* target = CL_BattlescapeSearchAtGridPos(toPos, true, nullptr);
1661 
1662  /* Determine the target's size. */
1663  const actorSizeEnum_t toActorSize = target
1664  ? target->fieldSize
1666 
1667  vec3_t start, end;
1668  Grid_PosToVec(cl.mapData->routing, fromActorSize, fromPos, start);
1669  Grid_PosToVec(cl.mapData->routing, toActorSize, toPos, end);
1671  end[2] -= mousePosTargettingAlign;
1672 
1673  /* calculate direction */
1674  vec3_t dir;
1675  VectorSubtract(end, start, dir);
1676 
1677  /* Calculate shot origin */
1678  selActor->fd->getShotOrigin(start, dir, LE_IsCrouched(selActor), start);
1679  VectorSubtract(end, start, dir);
1680  VectorNormalize(dir);
1681 
1682  /* calculate 'out of range point' if there is one */
1683  bool crossNo;
1684  vec3_t mid;
1685  if (VectorDistSqr(start, end) > selActor->fd->range * selActor->fd->range) {
1686  VectorMA(start, selActor->fd->range, dir, mid);
1687  crossNo = true;
1688  } else {
1689  VectorCopy(end, mid);
1690  crossNo = false;
1691  }
1692 
1693  vec3_t temp;
1694  VectorMA(start, UNIT_SIZE * 1.4, dir, temp);
1695  /* switch up to top level, this is needed to make sure our trace doesn't go through ceilings ... */
1698  trace_t tr = CL_Trace(Line(start, temp), AABB::EMPTY, selActor, nullptr, MASK_SHOT, cl.mapMaxLevel - 1);
1699  if (tr.le && (tr.le->team == cls.team || LE_IsCivilian(tr.le)) && LE_IsCrouched(tr.le))
1700  VectorMA(start, UNIT_SIZE * 1.4, dir, temp);
1701  else
1702  VectorCopy(start, temp);
1703 
1706  tr = CL_Trace(Line(temp, mid), AABB::EMPTY, selActor, target, MASK_SHOT, cl.mapMaxLevel - 1);
1707 
1708  if (tr.fraction < 1.0 && (!tr.le || (!LE_IsInvisible(tr.le) && !VectorCompare(tr.le->pos, toPos)))) {
1709  const float d = VectorDist(temp, mid);
1710  VectorMA(start, tr.fraction * d, dir, mid);
1711  crossNo = true;
1712  }
1713 
1714  /* spawn particles */
1715  CL_ParticleSpawn("inRangeTracer", 0, start, mid);
1716  if (crossNo) {
1717  CL_ParticleSpawn("longRangeTracer", 0, mid, end);
1718  CL_ParticleSpawn("cross_no", 0, end);
1719  } else {
1720  CL_ParticleSpawn("cross", 0, end);
1721  }
1722 
1723  if (selActor->fd->splrad > 0.0) {
1724  Grid_PosToVec(cl.mapData->routing, toActorSize, toPos, end);
1725  CL_TargetingRadius(end, selActor->fd->splrad);
1726  }
1727 }
1728 
1729 #define GRENADE_PARTITIONS 20
1730 
1738 static void CL_TargetingGrenade (const pos3_t fromPos, actorSizeEnum_t fromActorSize, const pos3_t toPos)
1739 {
1740  if (!selActor || !selActor->fd || Vector2Compare(fromPos, toPos))
1741  return;
1742 
1743  /* search for an actor at target */
1744  le_t* target = CL_BattlescapeSearchAtGridPos(toPos, true, nullptr);
1745 
1746  /* Determine the target's size. */
1747  actorSizeEnum_t toActorSize = target
1748  ? target->fieldSize
1750 
1751  /* get vectors, paint cross */
1752  vec3_t from, at, cross;
1753  Grid_PosToVec(cl.mapData->routing, fromActorSize, fromPos, from);
1754  Grid_PosToVec(cl.mapData->routing, toActorSize, toPos, at);
1755 
1756  /* prefer to aim grenades at the ground */
1757  at[2] -= GROUND_DELTA;
1759  at[2] -= mousePosTargettingAlign;
1760  VectorCopy(at, cross);
1761 
1762  /* Calculate shot origin */
1763  vec3_t ds;
1764  VectorSubtract(at, from, ds);
1765  selActor->fd->getShotOrigin(from, ds, LE_IsCrouched(selActor), from);
1766 
1767  /* calculate parabola */
1768  vec3_t v0;
1769  float dt = Com_GrenadeTarget(from, at, selActor->fd->range, selActor->fd->launched, selActor->fd->rolled, v0);
1770  if (!dt) {
1771  CL_ParticleSpawn("cross_no", 0, cross);
1772  return;
1773  }
1774 
1775  dt /= GRENADE_PARTITIONS;
1776  VectorSubtract(at, from, ds);
1777  VectorScale(ds, 1.0 / GRENADE_PARTITIONS, ds);
1778  ds[2] = 0;
1779 
1780  /* paint */
1781  float vz = v0[2];
1782 
1783  bool obstructed = false;
1784  for (int i = 0; i < GRENADE_PARTITIONS; i++) {
1785  vec3_t next;
1786  VectorAdd(from, ds, next);
1787  next[2] += dt * (vz - 0.5 * GRAVITY * dt);
1788  vz -= GRAVITY * dt;
1789  VectorScale(ds, i + 1.0, at);
1790 
1791  /* trace for obstacles. Switch up to top level, to make sure our trace
1792  * doesn't go through ceilings ... */
1793  const trace_t tr = CL_Trace(Line(from, next), AABB::EMPTY, selActor, target, MASK_SHOT, cl.mapMaxLevel - 1);
1794 
1795  /* something was hit */
1796  if (tr.fraction < 1.0 && (!tr.le || (!LE_IsInvisible(tr.le) && !VectorCompare(tr.le->pos, toPos)))) {
1797  obstructed = true;
1798  }
1799 
1800  /* draw particles */
1803  if (obstructed || VectorLength(at) > selActor->fd->range)
1804  CL_ParticleSpawn("longRangeTracer", 0, from, next);
1805  else
1806  CL_ParticleSpawn("inRangeTracer", 0, from, next);
1807  VectorCopy(next, from);
1808  }
1809  /* draw targeting cross */
1810  if (obstructed || VectorLength(at) > selActor->fd->range)
1811  CL_ParticleSpawn("cross_no", 0, cross);
1812  else
1813  CL_ParticleSpawn("cross", 0, cross);
1814 
1815  if (selActor->fd->splrad > 0.0) {
1816  Grid_PosToVec(cl.mapData->routing, toActorSize, toPos, at);
1817  CL_TargetingRadius(at, selActor->fd->splrad);
1818  }
1819 }
1820 
1826 #define BoxOffset(aSize, target) (target[0]=(aSize-1)*(UNIT_SIZE+BOX_DELTA_WIDTH), target[1]=(aSize-1)*(UNIT_SIZE+BOX_DELTA_LENGTH), target[2]=0)
1827 
1833 static void CL_AddTargetingBox (pos3_t pos, bool pendBox)
1834 {
1835  if (!cl_showactors->integer)
1836  return;
1837 
1838  entity_t cursor(RF_BOX);
1839 
1840  /* Paint the green box if move is possible ...
1841  * OR paint a dark blue one if move is impossible or the
1842  * soldier does not have enough TimeUnits left. */
1843  if (selActor && selActor->actorMoveLength < ROUTING_NOT_REACHABLE
1844  && selActor->actorMoveLength <= CL_ActorUsableTUs(selActor))
1845  VectorSet(cursor.color, 0, 1, 0); /* Green */
1846  else
1847  VectorSet(cursor.color, 0.6, 0.68, 1); /* Light Blue */
1848 
1849  /* color */
1850  /* if the mouse is over an actor, but not the selected one */
1851  actorSizeEnum_t actorSize = ACTOR_SIZE_NORMAL;
1852  if (mouseActor && !LE_IsSelected(mouseActor)) {
1853  actorSize = mouseActor->fieldSize;
1854  cursor.alpha = 0.6 + 0.2 * sin((float) cl.time / 80);
1855  /* Paint the box red if the soldiers under the cursor is
1856  * not in our team and no civilian either. */
1857  if (mouseActor->team != cls.team) {
1858  switch (mouseActor->team) {
1859  case TEAM_CIVILIAN:
1860  /* Civilians are yellow */
1861  VectorSet(cursor.color, 1, 1, 0); /* Yellow */
1862  break;
1863  default:
1864  if (LE_IsAlien(mouseActor)) {
1865  if (GAME_TeamIsKnown(mouseActor->teamDef))
1867  else
1868  UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, _("Unknown alien race"));
1869  } else {
1870  /* multiplayer names */
1871  /* see CL_ParseClientinfo */
1873  }
1874  /* Aliens (and players not in our team [multiplayer]) are red */
1875  VectorSet(cursor.color, 1, 0, 0); /* Red */
1876  break;
1877  }
1878  } else {
1879  /* coop multiplayer games */
1880  if (mouseActor->pnum != cl.pnum) {
1882  } else {
1883  /* we know the names of our own actors */
1884  character_t* chr = CL_ActorGetChr(mouseActor);
1885  assert(chr);
1887  }
1888  /* Paint a light blue box if on our team */
1889  VectorSet(cursor.color, 0.2, 0.3, 1); /* Light Blue */
1890  }
1891  } else {
1892  /* either no actor under the cursor or the selected one */
1893  cursor.alpha = 0.3;
1894  if (selActor) { /* there should always be an actor selected, but who knows */
1895  actorSize = selActor->fieldSize;
1896  }
1897  }
1898 
1899  /* Now calculate the size of the cursor box, depending on the actor. */
1900  /* For some strange reason we use origin and oldorigin instead of the ent's min/max, respectively */
1901  Grid_PosToVec(cl.mapData->routing, ACTOR_SIZE_NORMAL, pos, cursor.origin); /* center of the (lower left) cell */
1902  VectorAdd(cursor.origin, halfBoxSize, cursor.oldorigin);
1903  VectorSubtract(cursor.origin, halfBoxSize, cursor.origin);
1904  if (actorSize > ACTOR_SIZE_NORMAL) {
1905  vec_t inc = UNIT_SIZE * (actorSize - 1);
1906  vec3_t increase = { inc, inc, 0};
1907  VectorAdd(cursor.oldorigin, increase, cursor.oldorigin);
1908  }
1909 
1910  /* if pendBox is true then ignore all the previous color considerations and use cyan */
1911  if (pendBox) {
1912  VectorSet(cursor.color, 0, 1, 1); /* Cyan */
1913  cursor.alpha = 0.15;
1914  }
1915 
1916  /* add it */
1917  R_AddEntity(&cursor);
1918 }
1919 
1925 {
1926  /* no firedef selected */
1927  if (!selActor || !selActor->fd)
1928  return;
1929  if (!CL_ActorFireModeActivated(selActor->actorMode))
1930  return;
1931 
1932  /* user defined height align */
1933  int align = GROUND_DELTA;
1934  if (Cmd_Argc() == 2) {
1935  align = atoi(Cmd_Argv(1));
1936  } else {
1937  static int currentPos = 0;
1938  switch (currentPos) {
1939  case 0:
1940  if (selActor->fd->gravity)
1941  align = -align;
1942  currentPos = 1; /* next level */
1943  break;
1944  case 1:
1945  /* only allow to align to lower z level if the actor is
1946  * standing at a higher z-level */
1947  if (selActor->fd->gravity)
1948  align = -(2 * align);
1949  else
1950  align = -align;
1951  currentPos = 2;
1952  break;
1953  case 2:
1954  /* the static var is not reseted on weaponswitch or actorswitch */
1955  if (selActor->fd->gravity) {
1956  align = 0;
1957  currentPos = 0; /* next level */
1958  } else {
1959  align = -(2 * align);
1960  currentPos = 3; /* next level */
1961  }
1962  break;
1963  case 3:
1964  align = 0;
1965  currentPos = 0; /* back to start */
1966  break;
1967  }
1968  }
1969  mousePosTargettingAlign = align;
1970 }
1971 
1981 void CL_AddTargeting (void)
1982 {
1983  if (IN_GetMouseSpace() != MS_WORLD || !selActor)
1984  return;
1985 
1986  switch (selActor->actorMode) {
1987  case M_MOVE:
1988  case M_PEND_MOVE:
1989  /* Don't display the cursor if it's above the currently selected level.
1990  * The 2nd part of the if is an attempt to display it anyway when we eg. climb a hill.
1991  * But there are too many situations inside buildings that match the criteria (eg. actorclip around chair).
1992  * So disabled for now.*/
1993  if (mousePos[2] > cl_worldlevel->integer/* && !RT_AllCellsBelowAreFilled(cl.mapData->map, fieldSize, pos)*/)
1994  return;
1995 
1996  /* Display Move-cursor. */
1997  CL_AddTargetingBox(mousePos, false);
1998 
1999  if (selActor->actorMode == M_PEND_MOVE) {
2000  /* Also display a box for the pending move if we have one. */
2001  CL_AddTargetingBox(selActor->mousePendPos, true);
2002  if (!CL_ActorTraceMove(selActor->mousePendPos))
2003  CL_ActorSetMode(selActor, M_MOVE);
2004  }
2005  break;
2006  case M_FIRE_R:
2007  case M_FIRE_L:
2008  if (!selActor->fd)
2009  return;
2010 
2011  if (!selActor->fd->gravity)
2012  CL_TargetingStraight(selActor->pos, selActor->fieldSize, mousePos);
2013  else
2014  CL_TargetingGrenade(selActor->pos, selActor->fieldSize, mousePos);
2015  break;
2016  case M_PEND_FIRE_R:
2017  case M_PEND_FIRE_L:
2018  if (!selActor->fd)
2019  return;
2020 
2021  /* Draw cursor at mousepointer */
2022  CL_AddTargetingBox(mousePos, false);
2023 
2024  /* Draw (pending) Cursor at target */
2025  CL_AddTargetingBox(selActor->mousePendPos, true);
2026 
2027  if (!selActor->fd->gravity)
2028  CL_TargetingStraight(selActor->pos, selActor->fieldSize, selActor->mousePendPos);
2029  else
2030  CL_TargetingGrenade(selActor->pos, selActor->fieldSize, selActor->mousePendPos);
2031  break;
2032  default:
2033  break;
2034  }
2035 }
2036 
2038 
2043 static bool CL_AddPathingBox (pos3_t pos, bool addUnreachableCells)
2044 {
2045  const int TUneed = CL_ActorMoveLength(selActor, pos);
2046  const int TUhave = CL_ActorUsableTUs(selActor);
2047  if (!addUnreachableCells && TUhave < TUneed)
2048  return false;
2049 
2050  entity_t ent(RF_PATH);
2051 
2052  Grid_PosToVec(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), pos, ent.origin);
2053  VectorSubtract(ent.origin, boxShift, ent.origin);
2054 
2055  /* The floor relative to this cell */
2056  const int base = Grid_Floor(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), pos);
2057 
2058  /* Paint the box green if it is reachable,
2059  * yellow if it can be entered but is too far,
2060  * or red if it cannot be entered ever. */
2061  if (base < -QuantToModel(PATHFINDING_MAX_FALL)) {
2062  VectorSet(ent.color, 0.0, 0.0, 0.0); /* Can't enter - black */
2063  } else {
2064  /* Can reach - green
2065  * Passable but unreachable - yellow
2066  * Not passable - red */
2067  VectorSet(ent.color, (TUneed > TUhave), (TUneed != ROUTING_NOT_REACHABLE), 0);
2068  }
2069 
2070  /* Set the box height to the ceiling value of the cell. */
2071  /* The total opening size */
2072  const int height = 2 + std::min(TUneed * (UNIT_HEIGHT - 2) / ROUTING_NOT_REACHABLE, 16);
2073  ent.oldorigin[2] = height;
2074  ent.oldorigin[0] = TUneed;
2075  ent.oldorigin[1] = TUhave;
2076 
2077  ent.alpha = 0.25;
2078 
2079  /* add it */
2080  R_AddEntity(&ent);
2081  return true;
2082 }
2083 
2089 void CL_AddPathing (void)
2090 {
2091  if (selActor == nullptr) {
2092  return;
2093  }
2094 
2095  pos3_t pos;
2096  pos[2] = cl_worldlevel->integer;
2097  for (pos[1] = std::max(mousePos[1] - 8, 0); pos[1] <= std::min(mousePos[1] + 8, PATHFINDING_WIDTH - 1); pos[1]++) {
2098  for (pos[0] = std::max(mousePos[0] - 8, 0); pos[0] <= std::min(mousePos[0] + 8, PATHFINDING_WIDTH - 1); pos[0]++) {
2099  CL_AddPathingBox(pos, true);
2100  }
2101  }
2102 }
2103 
2110 {
2111  if (selActor == nullptr) {
2112  return;
2113  }
2114 
2115  pos3_t pos;
2116  pos[2] = cl_worldlevel->integer;
2117  int i = 0;
2118  for (int y = 0; y <= PATHFINDING_WIDTH; y++) {
2119  for (int x = 0; x <= PATHFINDING_WIDTH; x++) {
2120  pos[0] = (pos_t)x;
2121  pos[1] = (pos_t)y;
2122  i += CL_AddPathingBox(pos, false);
2123  if (i > 1024)
2124  return;
2125  }
2126  }
2127 }
2128 
2134 void CL_ActorPlaySound (const le_t* le, actorSound_t soundType)
2135 {
2136  const char* actorSound = le->teamDef->getActorSound(le->gender, soundType);
2137  if (actorSound) {
2139  Com_DPrintf(DEBUG_SOUND|DEBUG_CLIENT, "CL_PlayActorSound: ActorSound: '%s'\n", actorSound);
2140  }
2141  }
2142 }
2143 
2147 static void CL_AddArrow (vec3_t from, vec3_t to, float red, float green, float blue)
2148 {
2149  /* Com_Printf("Adding arrow (%f, %f, %f) to (%f, %f, %f).\n", from[0], from[1], from[2], to[0], to[1], to[2]); */
2150 
2151  entity_t ent(RF_ARROW);
2152 
2153  VectorCopy(from, ent.origin);
2154  VectorCopy(to, ent.oldorigin);
2155  VectorSet(ent.color, red, green, blue);
2156 
2157  ent.alpha = 0.25;
2158 
2159  /* add it */
2160  R_AddEntity(&ent);
2161 }
2162 
2167 {
2168  vec3_t base, start;
2169 
2171  VectorCopy(base, start);
2172  base[2] -= QUANT;
2173  start[2] += QUANT;
2174  CL_AddArrow(base, start, 0.0, 0.0, 0.0);
2175 }
2176 
2181 {
2182  vec3_t base, start;
2183 
2185  VectorCopy(base, start);
2186  CL_AddArrow(base, start, 0.0, 0.0, 0.0);
2187 }
2188 
2189 #ifdef DEBUG
2190 
2193 static void CL_DumpMoveMark_f (void)
2194 {
2195  const int temp = developer->integer;
2196 
2197  if (!selActor)
2198  return;
2199 
2202 
2203  CL_ActorConditionalMoveCalc(selActor);
2204  developer->integer = temp;
2205 }
2206 
2211 static void CL_DumpTUs_f (void)
2212 {
2213  if (!selActor)
2214  return;
2215 
2216  const int crouchingState = LE_IsCrouched(selActor) ? 1 : 0;
2217  pos3_t pos;
2218  VectorCopy(selActor->pos, pos);
2219 
2220  Com_Printf("TUs around (%i, %i, %i).\n", pos[0], pos[1], pos[2]);
2221 
2222  for (int y = std::max(0, pos[1] - 8); y <= std::min(PATHFINDING_WIDTH, pos[1] + 8); y++) {
2223  for (int x = std::max(0, pos[0] - 8); x <= std::min(PATHFINDING_WIDTH, pos[0] + 8); x++) {
2224  pos3_t loc;
2225  VectorSet(loc, x, y, pos[2]);
2226  Com_Printf("%3i ", Grid_MoveLength(&cl.pathMap, loc, crouchingState, false));
2227  }
2228  Com_Printf("\n");
2229  }
2230  Com_Printf("TUs at (%i, %i, %i) = %i\n", pos[0], pos[1], pos[2], Grid_MoveLength(&cl.pathMap, pos, crouchingState, false));
2231 }
2232 
2233 static void CL_DebugPath_f (void)
2234 {
2235  if (IN_GetMouseSpace() != MS_WORLD)
2236  return;
2237 
2238  const actorSizeEnum_t actorSize = ACTOR_SIZE_NORMAL;
2239  const pos_t x = mousePos[0];
2240  const pos_t y = mousePos[1];
2241  const pos_t z = mousePos[2];
2242 
2243 #if 0
2244  int dir = 3;
2245  RT_DebugSpecial(cl.mapTiles, cl.mapData->routing, actorSize, x, y, dir, cl.leInlineModelList);
2246 
2247  bool found = Grid_FindPath(cl.mapData->routing, actorSize, &cl.pathMap, selActor->pos, mousePos, 0, 600, nullptr);
2248  if (found)
2249  Com_Printf("found the path !\n");
2250  {
2251 // pos3_t boxmin = {134,128,0};
2252 // pos3_t boxmax = {136,130,1};
2253 // GridBox myBox(boxmin, boxmax);
2254 // Grid_RecalcBoxRouting(cl.mapTiles, cl.mapData->routing, myBox, cl.leInlineModelList);
2255  }
2256  int dir = 0;
2257  long time = Sys_Milliseconds();
2259  time = (Sys_Milliseconds() - time);
2260  Com_Printf("RT_UpdateConnectionColumn needed %i milliseconds\n", (int)time);
2261 #endif
2262 
2263  RT_DebugPathDisplay(cl.mapData->routing, actorSize, x, y, z);
2264  cl.mapTiles->printTilesAt(x, y);
2265 
2266  GridBox mbox;
2268  const int xW = mbox.getMaxX() - mbox.getMinX();
2269  const int yW = mbox.getMaxY() - mbox.getMinY();
2270  const int zW = mbox.getMaxZ() - mbox.getMinZ();
2271  Com_Printf("Statistics:\nWorldsize(x/y/z) %i/%i/%i\n", xW, yW, zW);
2272  int numCells = xW * yW * zW;
2273  Com_Printf("number of Cells: %i\n", numCells);
2274  Com_Printf("Base Coords (x/y/z) %i/%i/%i\n", mbox.getMinX(), mbox.getMinY(), mbox.getMinZ());
2275 }
2276 #endif
2277 
2278 
2282 static void CL_ActorNext_f (void)
2283 {
2284  if (CL_BattlescapeRunning()) {
2286  }
2287 }
2288 
2292 static void CL_ActorPrev_f (void)
2293 {
2294  if (CL_BattlescapeRunning()) {
2296  }
2297 }
2298 
2302 static void CL_ActorSelect_f (void)
2303 {
2304  /* check syntax */
2305  if (Cmd_Argc() < 2) {
2306  Com_Printf("Usage: %s <num>\n", Cmd_Argv(0));
2307  return;
2308  }
2309 
2310  /* check whether we are connected (tactical mission) */
2311  if (CL_BattlescapeRunning()) {
2312  const int num = atoi(Cmd_Argv(1));
2313  CL_ActorSelectList(num);
2314  }
2315 }
2316 
2320 static void CL_ActorUpdate_f (void)
2321 {
2322  const int num = cl_selected->integer;
2323 
2324  /* We are in the base or multiplayer inventory */
2325  int i = 0;
2327  if (i++ != num)
2328  continue;
2330  }
2331 }
2332 
2339 static bool CL_ActorVis (const le_t* le, const le_t* check)
2340 {
2341  vec3_t from;
2342 
2343  VectorCopy(le->origin, from);
2344 
2345  /* start on eye height */
2346  vec3_t test;
2347  VectorCopy(check->origin, test);
2348  float delta;
2349  if (LE_IsDead(check)) {
2350  test[2] += PLAYER_DEAD;
2351  delta = 0;
2352  } else if (LE_IsCrouched(check)) {
2353  test[2] += PLAYER_CROUCH - 2;
2354  delta = (PLAYER_CROUCH - PLAYER_MIN) / 2 - 2;
2355  } else {
2356  test[2] += PLAYER_STAND;
2357  delta = (PLAYER_STAND - PLAYER_MIN) / 2 - 2;
2358  }
2359 
2360  /* side shifting -> better checks */
2361  vec3_t dir;
2362  dir[0] = from[1] - check->origin[1];
2363  dir[1] = check->origin[0] - from[0];
2364  dir[2] = 0;
2365  VectorNormalize(dir);
2366  VectorMA(test, -7, dir, test);
2367 
2368  /* do 3 tests */
2369  for (int i = 0; i < 3; i++) {
2370  const trace_t tr = CL_Trace(Line(from, test), AABB::EMPTY, le, nullptr, MASK_SOLID, cl_worldlevel->integer);
2371  /* trace didn't reach the target - something was hit before */
2372  if (tr.fraction < 1.0) {
2373  /* look further down or stop */
2374  if (!delta)
2375  return false;
2376  VectorMA(test, 7, dir, test);
2377  test[2] -= delta;
2378  continue;
2379  }
2380 
2381  return true;
2382  }
2383 
2384  return false;
2385 }
2386 
2392 {
2393  static int lastAlien = 0;
2394 
2395  if (!selActor)
2396  return;
2397 
2398  if (lastAlien >= cl.numLEs)
2399  lastAlien = 0;
2400 
2401  int i = lastAlien;
2402  do {
2403  if (++i >= cl.numLEs)
2404  i = 0;
2405  const le_t* le = &cl.LEs[i];
2406  if (le->inuse && LE_IsLivingAndVisibleActor(le) && le->team != cls.team
2407  && !LE_IsCivilian(le)) {
2408  if (CL_ActorVis(selActor, le)) {
2409  lastAlien = i;
2411  CL_ParticleSpawn("fadeTracer", 0, selActor->origin, le->origin);
2412  return;
2413  }
2414  }
2415  } while (i != lastAlien);
2416 }
2417 
2422 static void CL_NextAlien_f (void)
2423 {
2424  if (cl.numLEs <= 0)
2425  return;
2426 
2427  int lastAlien = Cvar_GetInteger("ui_lastalien");
2428  lastAlien = std::max(0, std::min(cl.numLEs - 1, lastAlien));
2429 
2430  int i = lastAlien;
2431  do {
2432  if (++i >= cl.numLEs)
2433  i = 0;
2434  const le_t* le = &cl.LEs[i];
2435  if (le->inuse && LE_IsLivingAndVisibleActor(le) && le->team != cls.team
2436  && le->team != TEAM_CIVILIAN) {
2437  lastAlien = i;
2439  Cvar_SetValue("ui_lastalien", lastAlien);
2440  return;
2441  }
2442  } while (i != lastAlien);
2443 }
2444 
2449 static void CL_PrevAlien_f (void)
2450 {
2451  if (cl.numLEs <= 0)
2452  return;
2453 
2454  int lastAlien = Cvar_GetInteger("ui_lastalien");
2455  lastAlien = std::max(0, std::min(cl.numLEs - 1, lastAlien));
2456 
2457  int i = lastAlien;
2458  do {
2459  if (--i < 0)
2460  i = cl.numLEs - 1;
2461  const le_t* le = &cl.LEs[i];
2462  if (le->inuse && LE_IsLivingAndVisibleActor(le) && le->team != cls.team
2463  && le->team != TEAM_CIVILIAN) {
2464  lastAlien = i;
2466  Cvar_SetValue("ui_lastalien", lastAlien);
2467  return;
2468  }
2469  } while (i != lastAlien);
2470 }
2471 
2476 static void CL_ActorConfirmAction (le_t* le)
2477 {
2478  if (le->team != cl.actTeam)
2479  return;
2480 
2481  /* might be a friendly player controlled actor */
2482  if (le->pnum != cl.pnum)
2483  return;
2484 
2485  switch (le->actorMode) {
2486  case M_PEND_MOVE:
2488  break;
2489  case M_PEND_FIRE_R:
2490  case M_PEND_FIRE_L:
2491  CL_ActorShoot(le, le->mousePendPos);
2492  break;
2493  default:
2494  break;
2495  }
2496 }
2497 
2504 static void CL_ActorConfirmAction_f (void)
2505 {
2506  static int time = 0;
2507 
2508  if (time - cl.time < 1000) {
2509  le_t* le = nullptr;
2510  while ((le = LE_GetNextInUse(le))) {
2511  if (LE_IsLivingActor(le) && !LE_IsStunned(le) && le->team == cls.team)
2513  }
2514  } else {
2515  time = cl.time;
2516  if (!selActor)
2517  return;
2518  CL_ActorConfirmAction(selActor);
2519  }
2520 }
2521 
2523 {
2524  cl_autostand = Cvar_Get("cl_autostand","1", CVAR_USERINFO | CVAR_ARCHIVE, "Prevent accidental wasting of TUs by allowing the actor to automatically stand up before starting long walks.");
2525  confirm_actions = Cvar_Get("confirm_actions", "0", CVAR_ARCHIVE, "Confirm all actions in tactical mode");
2526  cl_showactors = Cvar_Get("cl_showactors", "1", 0, "Show actors on the battlefield");
2527  Cmd_AddCommand("actor_next", CL_ActorNext_f, N_("Toggle to next living actor"));
2528  Cmd_AddCommand("actor_prev", CL_ActorPrev_f, N_("Toggle to previous living actor"));
2529  Cmd_AddCommand("actor_select", CL_ActorSelect_f, N_("Select an actor from list"));
2530  Cmd_AddCommand("actor_updatecurrent", CL_ActorUpdate_f, N_("Update an actor"));
2531  Cmd_AddCommand("actor_standcrouch", CL_ActorStandCrouch_f, N_("Toggle stand/crouch."));
2532  Cmd_AddCommand("actor_useheadgear", CL_ActorUseHeadgear_f, N_("Toggle the headgear"));
2533  Cmd_AddCommand("actor_use", CL_ActorUse_f, N_("Use"));
2534  Cmd_AddCommand("actor_confirmaction", CL_ActorConfirmAction_f, N_("Confirm the current action"));
2535  Cmd_AddCommand("actor_nextalien", CL_NextAlienVisibleFromActor_f, N_("Toggle to the next alien in sight of the selected actor."));
2536 
2537  Cmd_AddCommand("nextalien", CL_NextAlien_f, N_("Toggle camera to the next visible alien."));
2538  Cmd_AddCommand("prevalien", CL_PrevAlien_f, N_("Toggle camera to the previous visible alien."));
2539 
2540 #ifdef DEBUG
2541  Cmd_AddCommand("debug_path", CL_DebugPath_f, "Display routing data for current mouse position.");
2542  Cmd_AddCommand("debug_drawblocked", CL_DisplayBlockedPaths_f, "Draw a marker for all blocked map-positions.");
2543  Cmd_AddCommand("debug_movemark", CL_DumpMoveMark_f, "Trigger Step::isPossible in every direction at the current truePos.");
2544  Cmd_AddCommand("debug_tus", CL_DumpTUs_f, "Show a table of the TUs that would be used by the current actor to move relative to his current location.");
2545  Cmd_AddCommand("debug_actorinvlist", nullptr, "Show the inventory list of all actors.");
2546 #endif /* DEBUG */
2547 }
#define LE_IsStunned(le)
void CL_AddPathing(void)
Adds a pathing marker to the current floor when we render the world.
Definition: cl_actor.cpp:2089
#define ST_RIGHT
The right hand should be used for shooting.
Definition: q_shared.h:211
static void CL_TargetingGrenade(const pos3_t fromPos, actorSizeEnum_t fromActorSize, const pos3_t toPos)
Shows targeting for a grenade.
Definition: cl_actor.cpp:1738
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition: mathlib.cpp:434
bool Grid_ShouldUseAutostand(const pathing_t *path, const pos3_t toPos)
Checks if a crouched actor could save TUs by standing up, walking and crouching again.
Definition: grid.cpp:818
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition: cmd.cpp:516
static void CL_ActorNext_f(void)
Switch to the next living soldier.
Definition: cl_actor.cpp:2282
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition: cmd.cpp:744
#define PLAYER_WIDTH
Definition: q_sizes.h:10
#define VectorCopy(src, dest)
Definition: vector.h:51
void CL_UpdateCharacterValues(const character_t *chr)
Definition: cl_team.cpp:218
void destroyInventory(Inventory *const inv)
Destroys inventory.
Definition: inventory.cpp:521
#define CVAR_USERINFO
Definition: cvar.h:41
int ucn
le_t * CL_BattlescapeSearchAtGridPos(const pos3_t pos, bool includingStunned, const le_t *actor)
Searches a local entity at the given position.
#define ACTOR_SIZE_2x2
Definition: defines.h:303
pos_t getMinZ() const
Definition: mathlib.h:180
teamDef_t * teamDef
void CL_AddTargeting(void)
Adds a target cursor when we render the world.
Definition: cl_actor.cpp:1981
int viewHeight
Definition: cl_video.h:76
#define VectorSet(v, x, y, z)
Definition: vector.h:59
void CL_BattlescapeMouseDragging(void)
Scroll battlescape touchscreen-style, by clicking and dragging away.
Definition: cl_actor.cpp:1467
bool CL_ActorMouseTrace(void)
Battlescape cursor positioning.
Definition: cl_actor.cpp:1373
void CL_ActorInvMove(const le_t *le, containerIndex_t fromContainer, int fromX, int fromY, containerIndex_t toContainer, int toX, int toY)
Sends an inventory move event to the server.
Definition: cl_actor.cpp:1004
#define LE_IsCrouched(le)
static void CL_ActorConfirmAction_f(void)
Executes "pending" actions such as walking and firing.
Definition: cl_actor.cpp:2504
int roundsCnt
Definition: cl_renderer.h:149
void MSG_Write_PA(player_action_t playerAction, int entnum,...)
Writes player action with its data.
Definition: cl_actor.cpp:73
int Grid_MoveNext(const pathing_t *path, const pos3_t toPos, byte crouchingState)
Get the direction to use to move to a position (used to reconstruct the path)
Definition: grid.cpp:719
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
static int mousePosTargettingAlign
If you want to change the z level of targeting and shooting, use this value. Negative and positive of...
Definition: cl_actor.cpp:62
void getShotOrigin(const vec3_t from, const vec3_t dir, bool crouching, vec3_t shotOrigin) const
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
static const vec3_t halfBoxSize
field marker box
Definition: cl_actor.cpp:1825
void CL_InitBattlescapeMouseDragging(void)
Scroll battlescape touchscreen-style, by clicking and dragging away.
Definition: cl_actor.cpp:1459
vec3_t origin
Definition: bspfile.h:47
pos_t getMaxY() const
Definition: mathlib.h:186
void CL_ActorTurnMouse(void)
Turns the actor around without moving.
Definition: cl_actor.cpp:1074
linkedList_t * chrList
this is a fire definition for our weapons/ammo
Definition: inv_shared.h:110
InventoryInterface i
Definition: client.h:101
void UI_RegisterText(int dataId, const char *text)
share a text with a data id
Definition: ui_data.cpp:115
int pnum
int entnum
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
pos_t getMaxX() const
Definition: mathlib.h:183
void CL_ActorShoot(const le_t *le, const pos3_t at)
Shoot with actor.
Definition: cl_actor.cpp:888
void LE_CenterView(const le_t *le)
Center the camera on the local entity's origin.
#define RF_IRGOGGLESSHOT
Definition: r_entity.h:52
const teamDef_t * teamDef
Definition: chr_shared.h:394
int gender
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
pos3_t pos
void CL_ActorRemoveFromTeamList(le_t *le)
Removes an actor (from your team) from the team list.
Definition: cl_actor.cpp:400
bool isOurRound() const
Definition: client.h:106
#define _(String)
Definition: cl_shared.h:43
float alpha
void CL_AddActorPathing(void)
Adds an actor pathing marker to the current floor when we render the world.
Definition: cl_actor.cpp:2109
void ACTOR_InitStartup(void)
Definition: cl_actor.cpp:2522
Item * getContainer2(const containerIndex_t idx) const
Definition: inv_shared.h:546
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.
pos_t Grid_Fall(const Routing &routing, const actorSizeEnum_t actorSize, const pos3_t pos)
Calculated the new height level when something falls down from a certain position.
Definition: grid.cpp:783
voidpf uLong int origin
Definition: ioapi.h:45
#define STATE_REACTION
Definition: q_shared.h:272
float fieldOfViewX
Definition: cl_renderer.h:171
int32_t actorSizeEnum_t
Definition: ufotypes.h:77
void Grid_CalcPathing(const Routing &routing, const actorSizeEnum_t actorSize, pathing_t *path, const pos3_t from, int maxTUs, forbiddenList_t *fb_list)
Recalculate the pathing table for the given actor(-position)
Definition: grid.cpp:497
le_t * CL_ActorGetClosest(const vec3_t origin, int team)
Returns the actor that is closest to the given origin.
Definition: cl_actor.cpp:694
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
Definition: inv_shared.cpp:576
static le_t * mouseActor
Definition: cl_actor.cpp:64
struct le_s * le
Definition: tracing.h:66
#define LE_IsInvisible(le)
float vec_t
Definition: ufotypes.h:37
#define TU_CROUCH
Definition: defines.h:72
#define LE_IsCivilian(le)
void CL_GetWorldCoordsUnderMouse(vec3_t groundIntersection, vec3_t upperTracePoint, vec3_t lowerTracePoint)
Get battlescape cell position under mouse cursor.
Definition: cl_actor.cpp:1290
#define RF_MEMBER
Definition: r_entity.h:44
float rx
Definition: cl_video.h:71
static void CL_TargetingStraight(const pos3_t fromPos, actorSizeEnum_t fromActorSize, const pos3_t toPos)
Draws line to target.
Definition: cl_actor.cpp:1654
#define RF_OPPONENT
Definition: r_entity.h:51
void CL_ActorActionMouse(void)
initiates action with mouse.
Definition: cl_actor.cpp:1266
void CL_ViewCenterAtGridPosition(const pos3_t pos)
Centers the camera on a given grid field.
Definition: cl_view.cpp:289
void CL_DisplayObstructionArrows(void)
Useful for debugging pathfinding.
Definition: cl_actor.cpp:2180
#define AngleToDV(x)
Definition: mathlib.h:257
const objDef_t * def(void) const
Definition: inv_shared.h:469
viddef_t viddef
Definition: cl_video.cpp:34
#define IS_MODE_FIRE_LEFT(x)
#define VectorDist(a, b)
Definition: vector.h:69
#define UNIT_HEIGHT
Definition: defines.h:122
void CL_ActorStartMove(le_t *le, const pos3_t to)
Starts moving actor.
Definition: cl_actor.cpp:847
struct le_s * clientAction
#define LE_IsAlien(le)
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
bool CL_OutsideMap(const vec3_t position, const float delta)
Checks whether give position is still inside the map borders.
character_t * CL_ActorGetChr(const le_t *le)
Returns the character information for an actor in the teamlist.
Definition: cl_actor.cpp:155
char deathTextureName[MAX_VAR]
Definition: chr_shared.h:332
float CL_ActorInjuryModifier(const le_t *le, const modifier_types_t type)
Returns the actor injury modifier of the specified type.
Definition: cl_actor.cpp:299
#define TEAM_CIVILIAN
Definition: q_shared.h:61
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
int getFmIdx() const
Definition: chr_shared.h:150
Item * getHeadgear() const
Definition: inv_shared.cpp:965
bool CM_EntTestLineDM(mapTiles_t *mapTiles, const Line &trLine, vec3_t hit, const int levelmask, const char **entlist)
Checks traces against the world and all inline models, gives the hit position back.
Definition: cmodel.cpp:230
vec3_t origin
bool isReloadable() const
Definition: inv_shared.h:352
vec3_t maxs
Definition: aabb.h:258
actorModes_t actorMode
bool CL_ActorSelectPrev(void)
selects the previous actor
Definition: cl_actor.cpp:558
bool CL_ActorIsReactionFireOutOfRange(const le_t *shooter, const le_t *target)
Definition: cl_actor.cpp:199
#define RF_BLOOD
Definition: r_entity.h:42
const char * leInlineModelList[MAX_EDICTS+1]
Item * getNext() const
Definition: inv_shared.h:451
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
le_t LEs[MAX_EDICTS]
#define STATE_CROUCHED
Definition: q_shared.h:263
int integer
Definition: cvar.h:81
bool LE_IsActor(const le_t *le)
Checks whether the given le is a living actor.
#define VectorScale(in, scale, out)
Definition: vector.h:79
void CL_ActorAddToTeamList(le_t *le)
Adds the actor to the team list.
Definition: cl_actor.cpp:362
void CL_ActorSelectMouse(void)
Selects an actor using the mouse.
Definition: cl_actor.cpp:1197
#define CVAR_ARCHIVE
Definition: cvar.h:40
int GAME_GetChrMaxLoad(const character_t *chr)
Returns the max weight the given character can carry.
Definition: cl_game.cpp:1768
#define CELL_HEIGHT
A cell's height in QUANT sized units.
Definition: defines.h:296
static bool CL_AddPathingBox(pos3_t pos, bool addUnreachableCells)
create a targeting box at the given position
Definition: cl_actor.cpp:2043
#define PATHFINDING_MAX_FALL
Definition: defines.h:309
int Cvar_GetInteger(const char *varName)
Returns the int value of a cvar.
Definition: cvar.cpp:194
#define MAP_SIZE_OFFSET
Definition: defines.h:388
float fraction
Definition: tracing.h:58
static void CL_PrevAlien_f(void)
Cycles between visible aliens in reverse direction.
Definition: cl_actor.cpp:2449
actorModes_t
Actor actions.
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.
#define SOUND_ATTN_IDLE
Definition: common.h:187
#define TL_FLAG_ACTORCLIP
Definition: defines.h:359
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
static void CL_NextAlien_f(void)
Cycles between visible aliens.
Definition: cl_actor.cpp:2422
actorSizeEnum_t fieldSize
int CL_ActorGetNumber(const le_t *le)
Returns the number of the actor in the teamlist.
Definition: cl_actor.cpp:125
pos_t Grid_MoveLength(const pathing_t *path, const pos3_t to, byte crouchingState, bool stored)
Return the needed TUs to walk to a given position.
Definition: grid.cpp:698
image_t * R_FindImage(const char *pname, imagetype_t type)
Finds or loads the given image.
Definition: r_image.cpp:603
const char * pa_format[]
Player action format strings for netchannel transfer.
Definition: q_shared.cpp:34
#define CID_LEFT
Definition: inv_shared.h:48
mapTiles_t * mapTiles
client_static_t cls
Definition: cl_main.cpp:83
item instance data, with linked list capability
Definition: inv_shared.h:402
void getFirstShapePosition(int *const x, int *const y) const
Calculates the first "true" bit in the shape and returns its position in the item.
Definition: inv_shared.cpp:557
#define todeg
Definition: mathlib.h:51
static const vec3_t boxShift
Definition: cl_actor.cpp:2037
#define PLAYER_DEAD
Definition: q_sizes.h:8
#define PATHFINDING_WIDTH
absolute max
Definition: defines.h:292
#define RF_PATH
Definition: r_entity.h:37
static void CL_TargetingRadius(const vec3_t center, const float radius)
Show weapon radius.
Definition: cl_actor.cpp:1637
int flags
void CL_ActorReserveTUs(const le_t *le, const reservation_types_t type, const int tus)
Replace the reserved TUs for a certain type.
Definition: cl_actor.cpp:273
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
static void CL_ActorUse(const le_t *le)
Uses the current selected entity in the battlescape. Can e.g. open the selected door.
Definition: cl_actor.cpp:1027
inventory definition with all its containers
Definition: inv_shared.h:525
void reset()
Definition: grid.h:51
int right
void CL_ActorPlaySound(const le_t *le, actorSound_t soundType)
Plays various sounds on actor action.
Definition: cl_actor.cpp:2134
void NET_vWriteFormat(dbuffer *buf, const char *format, va_list ap)
Writes to buffer according to format; version without syntactic sugar for variable arguments...
Definition: netpack.cpp:149
cvar_t * cl_worldlevel
Definition: cl_hud.cpp:46
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
#define ERR_DROP
Definition: common.h:211
bool launched
Definition: inv_shared.h:137
int treatmentLevel[BODYPART_MAXTYPE]
Definition: chr_shared.h:352
#define DEBUG_CLIENT
Definition: defines.h:59
int x
Definition: cl_video.h:76
vec3_t origin
Definition: cl_camera.h:31
#define Vector2Compare(a, b)
Definition: vector.h:66
#define CID_RIGHT
Definition: inv_shared.h:47
#define PLAYER_MIN
Definition: q_sizes.h:9
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition: cvar.cpp:342
void CL_ActorResetMoveLength(le_t *le)
Recalculates the currently selected Actor's move length.
Definition: cl_actor.cpp:764
GLsizei size
Definition: r_gl.h:152
bool gravity
Definition: inv_shared.h:136
model_t * modelPool[MAX_OBJDEFS]
Definition: client.h:96
#define PosSubDV(p, crouch, dv)
Definition: mathlib.h:254
static pos3_t mouseLastPos
Definition: cl_actor.cpp:66
rendererData_t refdef
Definition: r_main.cpp:45
int mousePosY
Definition: cl_input.cpp:80
#define GRENADE_PARTITIONS
Definition: cl_actor.cpp:1729
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
#define RF_BOX
Definition: r_entity.h:36
#define IS_MODE_FIRE_RIGHT(x)
#define BOX_DELTA_LENGTH
Definition: q_sizes.h:27
entity_t * R_GetFreeEntity(void)
Get the next free entry in the entity list (the last one)
Definition: r_entity.cpp:684
#define M_PI
Definition: mathlib.h:34
static void CL_ActorMoveMouse(void)
handle select or action clicking in either move mode
Definition: cl_actor.cpp:1161
#define RF_SELECTED
Definition: r_entity.h:43
#define IS_MODE_FIRE_HEADGEAR(x)
static void CL_ActorStandCrouch_f(void)
Stands or crouches actor.
Definition: cl_actor.cpp:1105
#define ST_LEFT
The left hand should be used for shooting.
Definition: q_shared.h:221
pos_t getMinY() const
Definition: mathlib.h:177
#define ST_HEADGEAR
The headgear slot item should be used when shooting/using the item in the slot.
Definition: q_shared.h:231
static cvar_t * cl_autostand
Player preference: should the server make guys stand for long walks, to save TU.
Definition: cl_actor.cpp:45
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
Definition: cmd.cpp:505
#define ACTOR_GET_FIELDSIZE(actor)
Definition: cl_actor.h:48
pos_t getMinX() const
Definition: mathlib.h:174
#define LE_IsSelected(le)
pos3_t mousePendPos
#define MAX_FIREDEFS_PER_WEAPON
Definition: inv_shared.h:42
player_action_t
Definition: q_shared.h:189
#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
int CL_ActorReservedTUs(const le_t *le, const reservation_types_t type)
Returns the amount of reserved TUs for a certain type.
Definition: cl_actor.cpp:214
float Com_GrenadeTarget(const vec3_t from, const vec3_t at, float speed, bool launched, bool rolled, vec3_t v0)
Calculates parabola-type shot.
Definition: common.cpp:231
reservation_types_t
Definition: chr_shared.h:197
bool LE_IsLivingAndVisibleActor(const le_t *le)
Checks whether the given le is a living and visible actor.
#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
model_t * model1
static forbiddenList_t forbiddenList
A list of locations that cannot be moved to.
Definition: cl_actor.cpp:602
clientBattleScape_t cl
static void CL_ActorPrev_f(void)
Switch to the previous living soldier.
Definition: cl_actor.cpp:2292
int woundLevel[BODYPART_MAXTYPE]
Definition: chr_shared.h:351
#define RF_NEUTRAL
Definition: r_entity.h:49
bool CL_ActorSelect(le_t *le)
Selects an actor.
Definition: cl_actor.cpp:440
void CL_ActorReload(le_t *le, containerIndex_t containerID)
Reload weapon with actor.
Definition: cl_actor.cpp:948
mouseSpace_t
Definition: cl_input.h:31
static bool CL_ActorVis(const le_t *le, const le_t *check)
Definition: cl_actor.cpp:2339
static transfer_t tr
ptl_t * ptl
#define MASK_SOLID
Definition: defines.h:272
Definition: line.h:31
#define CURSOR_OFFSET
Definition: cl_actor.h:28
#define TU_TURN
Definition: defines.h:73
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
vec2_t size
Definition: cl_renderer.h:118
struct net_stream * netStream
Definition: client.h:74
#define GRAVITY
Definition: q_shared.h:276
#define RF_NONE
Definition: r_entity.h:34
#define INVDEF(containerID)
Definition: cl_shared.h:47
#define MAX_ROUTE_TUS
Definition: defines.h:83
void CL_ActorTargetAlign_f(void)
Targets to the ground when holding the assigned button.
Definition: cl_actor.cpp:1924
bool CL_ActorFireModeActivated(const actorModes_t mode)
Checks whether we are in fire mode or node.
Definition: cl_actor.cpp:1066
#define RDF_IRGOGGLES
Definition: cl_renderer.h:35
bool Grid_FindPath(const Routing &routing, const actorSizeEnum_t actorSize, pathing_t *path, const pos3_t from, const pos3_t targetPos, byte crouchingState, int maxTUs, forbiddenList_t *forbiddenList)
Tries to find a path from the given actor(-position) to a given target position.
Definition: grid.cpp:590
bool GAME_TeamIsKnown(const teamDef_t *teamDef)
Definition: cl_game.cpp:1630
const char * CL_PlayerGetName(unsigned int player)
Get the player name.
Definition: cl_parse.cpp:113
void NET_WriteFormat(dbuffer *buf, const char *format,...)
The user-friendly version of NET_WriteFormat that writes variable arguments to buffer according to fo...
Definition: netpack.cpp:207
int CL_ActorUsableTUs(const le_t *le)
Returns the amount of usable (overall-reserved) TUs for an actor.
Definition: cl_actor.cpp:259
pos_t pos3_t[3]
Definition: ufotypes.h:58
model_t * model2
#define RF_ALLIED
Definition: r_entity.h:45
cvar_t * cl_isometric
Definition: cl_input.cpp:77
le_t * CL_ActorGetFromCharacter(const character_t *chr)
Returns the local entity information for a character in the team list.
Definition: cl_actor.cpp:141
#define ModelFloorToQuant(x)
These macros are meant to correctly convert from model units to QUANT units and back.
Definition: routing.h:75
linkedList_t * chrDisplayList
List of currently displayed or equipable characters.
Definition: cl_team.cpp:38
void setFromMapBounds(const vec3_t mini, const vec3_t maxi)
Set the box correctly if the maxs value is the upper corner of a cell. VecToPos considers the upper b...
Definition: mathlib.h:163
bool CL_BattlescapeRunning(void)
Check whether we already have actors spawned on the battlefield.
void SCR_ChangeCursor(int cursor)
Definition: cl_screen.cpp:370
float woundThreshold(const short bodyPart) const
Definition: chr_shared.cpp:383
#define UNIT_SIZE
Definition: defines.h:121
#define MASK_SHOT
Definition: defines.h:275
static const vec4_t green
Definition: cp_geoscape.cpp:55
bool canHoldItemWeight(containerIndex_t from, containerIndex_t to, const Item &item, int maxWeight) const
Check that adding an item to the inventory won't exceed the max permitted weight. ...
Definition: inv_shared.cpp:919
cvar_t * developer
Definition: common.cpp:46
int CL_ActorCheckAction(const le_t *le)
Checks that an action is valid.
Definition: cl_actor.cpp:713
#define PLAYER_STAND
Definition: q_sizes.h:6
#define VectorCompare(a, b)
Definition: vector.h:63
a local entity
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
le_t * LE_GetNextInUse(le_t *lastLE)
Iterate through the entities that are in use.
Inventory inv
int32_t containerIndex_t
Definition: inv_shared.h:46
static vec3_t mouseDraggingPos
Definition: cl_actor.cpp:52
void RT_UpdateConnectionColumn(mapTiles_t *mapTiles, Routing &routing, const int actorSize, const int x, const int y, const int dir, const char **list, const int minZ, const int maxZ)
Routing Function to update the connection between two fields.
Definition: routing.cpp:1292
const fireDef_t * fd
bool isLoadableInWeapon(const objDef_s *weapon) const
Checks if an item can be used to reload a weapon.
Definition: inv_shared.cpp:356
int R_GetTagIndexByName(const model_t *mod, const char *tagName)
Searches the tag data for the given name.
Definition: r_mesh.cpp:247
cvar_t * cl_selected
Definition: cl_main.cpp:73
void IN_SetMouseSpace(mouseSpace_t mspace)
Definition: cl_input.cpp:1195
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition: defines.h:294
int CL_ActorMoveMode(const le_t *le)
Decide how the actor will walk, taking into account autostanding.
Definition: cl_actor.cpp:102
#define VectorAdd(a, b, dest)
Definition: vector.h:47
static void CL_ActorMaximumMove(const pos3_t to, const le_t *le, pos3_t pos)
Return the last position we can walk to with a defined amount of TUs.
Definition: cl_actor.cpp:816
entity_type_t type
char name[MAX_VAR]
Definition: chr_shared.h:299
static const vec4_t red
Definition: cp_geoscape.cpp:57
le_t * selActor
Definition: cl_actor.cpp:49
int HP
bool rolled
Definition: inv_shared.h:138
float range
Definition: inv_shared.h:152
FiremodeSettings RFmode
Definition: chr_shared.h:397
short numBodyParts(void) const
Definition: chr_shared.cpp:388
static void CL_ActorSelect_f(void)
Selects a soldier while we are on battlescape.
Definition: cl_actor.cpp:2302
#define QuantToModel(x)
Definition: routing.h:79
Inventory * ui_inventory
int CL_ActorTimeForFireDef(const le_t *le, const fireDef_t *fd, bool reaction)
Find the TUs needed for the given fireDef taking into account the actor wound penalties.
Definition: cl_actor.cpp:342
int Grid_Floor(const Routing &routing, const actorSizeEnum_t actorSize, const pos3_t pos)
Returns the height of the floor in a cell.
Definition: grid.cpp:754
QGL_EXTERN GLint i
Definition: r_gl.h:113
modifier_types_t
Definition: chr_shared.h:244
int viewWidth
Definition: cl_video.h:76
float life
Definition: cl_renderer.h:147
pos_t getMaxZ() const
Definition: mathlib.h:189
Item * getHandItem(actorHands_t hand) const
#define LE_IsDead(le)
bool CL_AddActor(le_t *le, entity_t *ent)
Adds an actor to the render entities with all it's models and items.
Definition: cl_actor.cpp:1515
#define RF_IRGOGGLES
Definition: r_entity.h:48
vec_t VectorNormalize(vec3_t v)
Calculate unit vector for a given vec3_t.
Definition: mathlib.cpp:745
#define GROUND_DELTA
Definition: defines.h:115
static void CL_BuildForbiddenList(void)
Builds a list of locations that cannot be moved to (client side).
Definition: cl_actor.cpp:612
const BodyData * bodyTemplate
Definition: chr_shared.h:339
bool isMoving() const
byte actorMoveLength
The TUs that the current selected actor needs to walk to the current grid position marked by the mous...
#define CID_MAX
Definition: inv_shared.h:57
Item * getItemAtPos(const invDef_t *container, const int x, const int y) const
Searches if there is an item at location (x,y) in a container.
Definition: inv_shared.cpp:844
static void CL_ActorConfirmAction(le_t *le)
Definition: cl_actor.cpp:2476
bool inuse
const char int mode
Definition: ioapi.h:41
#define RF_ARROW
Definition: r_entity.h:38
HUD related routines.
bool isHeldTwoHanded() const
Definition: inv_shared.h:476
#define DIST_EPSILON
Definition: defines.h:377
const char * getActorSound(int gender, actorSound_t soundType) const
Returns the actor sounds for a given category.
Definition: chr_shared.cpp:54
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
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
vec_t vec3_t[3]
Definition: ufotypes.h:39
inventory definition for our menus
Definition: inv_shared.h:371
float splrad
Definition: inv_shared.h:161
float t
Definition: cl_renderer.h:145
bool CL_ActorSelectNext(void)
selects the next actor
Definition: cl_actor.cpp:527
bool CL_ActorSelectList(int num)
Selects an actor from a list.
Definition: cl_actor.cpp:506
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
float penalty(const short bodyPart, const modifier_types_t type) const
Definition: chr_shared.cpp:373
void add(pos3_t pos, byte *entSize)
Definition: grid.h:44
int getX() const
Definition: inv_shared.h:454
static bool CL_AddActorWeapon(int objID)
Checks whether a weapon should be added to the entity's hand.
Definition: cl_actor.cpp:1495
vec3_t camorg
Definition: cl_camera.h:32
char name[MAX_VAR]
Definition: chr_shared.h:371
#define N_(String)
Definition: cl_shared.h:45
vec3_t mins
Definition: aabb.h:257
AABB mapBox
Definition: typedefs.h:351
cvar_t * Cvar_ForceSet(const char *varName, const char *value)
Will set the variable even if NOSET or LATCH.
Definition: cvar.cpp:604
int y
Definition: cl_video.h:76
#define lengthof(x)
Definition: shared.h:105
int TU
static cvar_t * confirm_actions
Confirm actions in tactical mode - valid values are 0, 1 and 2.
Definition: cl_actor.cpp:43
void CL_ActorSetMode(le_t *actor, actorModes_t actorMode)
Definition: cl_actor.cpp:835
#define DEBUG_SOUND
Definition: defines.h:63
actorSound_t
Types of actor sounds being issued by CL_ActorPlaySound().
Definition: chr_shared.h:207
#define ROUTING_UNREACHABLE
Definition: defines.h:284
pos3_t mousePos
Definition: cl_actor.cpp:51
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 NONE
Definition: defines.h:68
#define RF_SHADOW
Definition: r_entity.h:50
actorHands_t getHand() const
Definition: chr_shared.h:158
#define BOX_DELTA_HEIGHT
Definition: q_sizes.h:28
le_t * teamList[MAX_ACTIVETEAM]
static void CL_ActorUseHeadgear_f(void)
Toggles the headgear for the current selected player.
Definition: cl_actor.cpp:1124
bool GAME_ItemIsUseable(const objDef_t *od)
Definition: cl_game.cpp:1295
static void CL_ActorUse_f(void)
Hud callback to use the current selected entity.
Definition: cl_actor.cpp:1041
void printTilesAt(int x, int y)
Definition: tracing.cpp:513
int team
unsigned int headSkin
A list of locations that cannot be moved to.
Definition: grid.h:35
bool LE_IsLivingActor(const le_t *le)
Checks whether the given le is a living actor (but might be hidden)
#define LE_SELECTED
static void CL_AddTargetingBox(pos3_t pos, bool pendBox)
create a targeting box at the given position
Definition: cl_actor.cpp:1833
How many TUs (and of what type) did a player reserve for a unit?
Definition: chr_shared.h:179
float ry
Definition: cl_video.h:72
int mousePosX
Definition: cl_input.cpp:80
#define IN_GetMouseSpace()
Definition: cl_input.h:48
static void CL_ActorUpdate_f(void)
Update the skin of the current soldier.
Definition: cl_actor.cpp:2320
static void CL_AddArrow(vec3_t from, vec3_t to, float red, float green, float blue)
create an arrow between from and to with the specified color ratios
Definition: cl_actor.cpp:2147
static const AABB EMPTY
Definition: aabb.h:44
static byte CL_ActorMoveLength(const le_t *le, const pos3_t to)
Get the real move length (depends on crouch-state of the current actor).
Definition: cl_actor.cpp:737
#define ACTOR_SIZE_NORMAL
Definition: defines.h:302
int maxHP
#define TU_DOOR_ACTION
Definition: defines.h:78
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
#define ROUTING_NOT_REACHABLE
Definition: defines.h:283
static cvar_t * cl_showactors
Definition: cl_actor.cpp:46
void UI_Popup(const char *title, const char *text)
Popup on geoscape.
Definition: ui_popup.cpp:47
void Cvar_SetValue(const char *varName, float value)
Expands value to a string and calls Cvar_Set.
Definition: cvar.cpp:671
Routing routing
Definition: typedefs.h:341
uint8_t byte
Definition: ufotypes.h:34
bool RT_CanActorStandHere(const Routing &routing, const int actorSize, const pos3_t pos)
Check if an actor can stand(up) in the cell given by pos.
Definition: routing.cpp:237
bool isVirtual
Definition: inv_shared.h:284
int rounds
Definition: cl_renderer.h:148
#define VectorDistSqr(a, b)
Definition: vector.h:68
int CL_ActorGetContainerForReload(Item **ammoItem, const Inventory *inv, const objDef_t *weapon)
Searches the clip with the least TU usage to put it into the weapon.
Definition: cl_actor.cpp:916
fireDefIndex_t currentSelectedFiremode
static bool CL_ActorTraceMove(const pos3_t to)
Draws the way to walk when confirm actions is activated.
Definition: cl_actor.cpp:776
#define PLAYER_CROUCH
Definition: q_sizes.h:7
#define LE_IsDoor(le)
void CL_DisplayFloorArrows(void)
Useful for debugging pathfinding.
Definition: cl_actor.cpp:2166
void CL_ActorCleanup(le_t *le)
Definition: cl_actor.cpp:389
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
pos3_t truePos
Definition: cl_actor.cpp:50
int state
static le_t * interactEntity
Definition: cl_actor.cpp:65
void CL_ActorSetFireDef(le_t *actor, const fireDef_t *fd)
Definition: cl_actor.cpp:91
int getY() const
Definition: inv_shared.h:457
vec3_t axis[3]
Definition: cl_camera.h:36
static void CL_NextAlienVisibleFromActor_f(void)
Cycles between visible (to selected actor) aliens.
Definition: cl_actor.cpp:2391
#define RF_ACTOR
Definition: r_entity.h:46
byte pos_t
Definition: ufotypes.h:57
void HUD_DisplayMessage(const char *text)
Displays a message on the hud.
Definition: cl_hud.cpp:138
int Sys_Milliseconds(void)
Definition: unix_shared.cpp:41
#define SND_VOLUME_DEFAULT
Definition: s_main.h:42
#define QUANT
Definition: defines.h:126
const fireDef_t * CL_ActorGetReactionFireFireDef(const le_t *shooter)
Definition: cl_actor.cpp:171
void UI_ExecuteConfunc(const char *fmt,...)
Executes confunc - just to identify those confuncs in the code - in this frame.
Definition: ui_main.cpp:110
chrReservations_t reservedTus
Definition: chr_shared.h:396
int left
woundInfo_t wounds
void NET_WriteMsg(struct net_stream *s, dbuffer &buf)
Enqueue the buffer in the net stream for ONE client.
Definition: netpack.cpp:569
Describes a character with all its attributes.
Definition: chr_shared.h:369
#define BOX_DELTA_WIDTH
Definition: q_sizes.h:26