UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
g_reaction.cpp
Go to the documentation of this file.
1 
54 /*
55 Copyright (C) 2002-2020 UFO: Alien Invasion.
56 
57 This program is free software; you can redistribute it and/or
58 modify it under the terms of the GNU General Public License
59 as published by the Free Software Foundation; either version 2
60 of the License, or (at your option) any later version.
61 
62 This program is distributed in the hope that it will be useful,
63 but WITHOUT ANY WARRANTY; without even the implied warranty of
64 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
65 
66 See the GNU General Public License for more details.
67 
68 You should have received a copy of the GNU General Public License
69 along with this program; if not, write to the Free Software
70 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
71 
72 */
73 
74 #include "g_reaction.h"
75 #include "g_actor.h"
76 #include "g_client.h"
77 #include "g_combat.h"
78 #include "g_edicts.h"
79 #include "g_match.h"
80 #include "g_vis.h"
81 
82 #define MAX_RF_TARGETS 10
83 #define MAX_RF_DATA 128
84 
85 #define DEBUG_RF 0
86 
89 {
90 public:
91  Edict const* target;
92  int triggerTUs; /* the amount of TUS of the target(!) at which the reaction takes place */
93 };
94 
95 #define RF_NO_ENTNUM -1
96 
99 {
100 public:
101  int entnum;
102  int count;
104 
106  OBJZERO(targets);
107  init();
108  }
109 
110  inline void init (void) {
111  entnum = RF_NO_ENTNUM;
112  count = 0;
113  }
114 
115  inline void reset (void) {
116  count = 0;
117  }
118 };
119 
122 {
123 public:
124  void init();
125  void add(const Edict* shooter, const Edict* target, const int tusForShot);
126  void remove(const Edict* shooter, const Edict* target);
127  bool hasExpired(const Edict* shooter, const Edict* target, const int tusTarget);
128  int getTriggerTUs(const Edict* shooter, const Edict* target);
129  void advance(const Edict* shooter, const int tusShot);
130  void reset();
131  void notifyClientMove(const Edict* target, int step, bool startMove);
132  void notifyClientOnStep(const Edict* target, int step);
133  void create(const Edict* shooter);
134  void destroy(const Edict* shooter);
135  void resetTargetList(const Edict* shooter);
136  void notifyClientOnShot(const Edict* target, int step);
137  void notifyClientRFAborted(const Edict* shooter, const Edict* target, int step);
138 
139 private:
141  ReactionFireTargetList* find (const Edict* shooter);
142 };
143 
145 
150 {
151  for (int i = 0; i < MAX_RF_DATA; i++) {
152  rfData[i].init();
153  }
154 }
155 
160 {
161  for (int i = 0; i < MAX_RF_DATA; i++) {
162  rfData[i].reset();
163  }
164 }
165 
166 void ReactionFireTargets::notifyClientOnStep (const Edict* target, int step)
167 {
168  for (int i = 0; i < MAX_RF_DATA; i++) {
169  ReactionFireTargetList* rfts = &rfData[i];
170  if (rfts->entnum == RF_NO_ENTNUM)
171  continue;
172  const Edict* shooter = G_EdictsGetByNum(rfts->entnum);
173  for (int j = 0; j < rfts->count; j++) {
174  ReactionFireTarget& t = rfts->targets[j];
175  if (t.target != target)
176  continue;
177  const int tus = std::max(0, target->TU - t.triggerTUs);
178  G_EventReactionFireTargetUpdate(*shooter, *target, tus, step);
179  }
180  }
181 }
182 
183 void ReactionFireTargets::notifyClientOnShot (const Edict* target, int tusTarget)
184 {
185  for (int i = 0; i < MAX_RF_DATA; i++) {
186  ReactionFireTargetList* rfts = &rfData[i];
187  if (rfts->entnum == RF_NO_ENTNUM)
188  continue;
189  const Edict* shooter = G_EdictsGetByNum(rfts->entnum);
190  for (int j = 0; j < rfts->count; j++) {
191  ReactionFireTarget& t = rfts->targets[j];
192  if (t.target != target)
193  continue;
194  const int tus = std::max(0, target->TU - tusTarget - t.triggerTUs);
195  G_EventReactionFireTargetUpdate(*shooter, *target, tus, MAX_ROUTE);
196  }
197  }
198 }
199 
200 void ReactionFireTargets::notifyClientMove (const Edict* target, int step, bool startMove)
201 {
202  for (int i = 0; i < MAX_RF_DATA; i++) {
203  ReactionFireTargetList* rfts = &rfData[i];
204  if (rfts->entnum == RF_NO_ENTNUM)
205  continue;
206  const Edict* shooter = G_EdictsGetByNum(rfts->entnum);
207  for (int j = 0; j < rfts->count; j++) {
208  if (rfts->targets[j].target != target)
209  continue;
210  if (startMove) {
211  const int tus = std::max(0, target->TU - rfts->targets[j].triggerTUs);
212  G_EventReactionFireAddTarget(*shooter, *target, tus, step);
213  } else {
214  G_EventReactionFireRemoveTarget(*shooter, *target, step);
215  }
216  }
217  }
218 }
219 
220 void ReactionFireTargets::notifyClientRFAborted (const Edict* shooter, const Edict* target, int step)
221 {
222  ReactionFireTargetList* rfts = find(shooter);
223  assert(rfts);
224 
225  for (int i = 0; i < rfts->count; i++) {
226  ReactionFireTarget& t = rfts->targets[i];
227  if (t.target != target)
228  continue;
229  G_EventReactionFireAbortShot(*shooter, *target, step);
230  }
231 }
232 
238 {
239  for (int i = 0; i < MAX_RF_DATA; i++) {
240  ReactionFireTargetList* rfts = &rfData[i];
241  if (rfts->entnum == shooter->getIdNum()) {
242  return rfts;
243  }
244  }
245  return nullptr;
246 }
247 
248 
253 void ReactionFireTargets::create (const Edict* shooter)
254 {
255  const ReactionFireTargetList* rfts = find(shooter);
256 
257  if (rfts)
258  gi.Error("Entity already has rfData");
259 
260  for (int i = 0; i < MAX_RF_DATA; i++) {
262  if (data.entnum != RF_NO_ENTNUM)
263  continue;
264  data.entnum = shooter->getIdNum();
265  return;
266  }
267 
268  gi.Error("Not enough rfData");
269 }
270 
275 void ReactionFireTargets::destroy (const Edict* shooter)
276 {
277  ReactionFireTargetList* rfts = find(shooter);
278 
279  if (!rfts) {
280  gi.DPrintf("Entity doesn't have rfData");
281  return;
282  }
283 
284  rfts->init();
285 }
286 
293 void ReactionFireTargets::add (const Edict* shooter, const Edict* target, const int tusForShot)
294 {
295  int i;
296  ReactionFireTargetList* rfts = find(shooter);
297 
298  assert(rfts);
299  assert(target);
300 
301  for (i = 0; i < rfts->count; i++) {
302  /* check if shooter already knows that target */
303  if (rfts->targets[i].target == target)
304  return;
305  }
306  if (i >= MAX_RF_TARGETS)
307  return;
308  rfts->targets[i].target = target;
309  rfts->targets[i].triggerTUs = target->TU - tusForShot;
310  rfts->count++;
311  G_EventReactionFireAddTarget(*shooter, *target, tusForShot, target->moveinfo.steps - 1);
312 #if DEBUG_RF
313  if (!(G_IsAlien(shooter) || G_IsCivilian(shooter)))
314  Com_Printf("S%i: added\n", shooter->number);
315 #endif
316 }
317 
323 void ReactionFireTargets::remove (const Edict* shooter, const Edict* target)
324 {
325  ReactionFireTargetList* rfts = find(shooter);
326 
327  assert(rfts);
328  assert(target);
329 
330  for (int i = 0; i < rfts->count; i++) {
331  ReactionFireTarget& t = rfts->targets[i];
332  if (t.target != target)
333  continue;
334 
335  /* not the last one? */
336  if (i != rfts->count - 1) {
337  t.target = rfts->targets[rfts->count - 1].target;
338  t.triggerTUs = rfts->targets[rfts->count - 1].triggerTUs;
339  }
340  rfts->count--;
341  G_EventReactionFireRemoveTarget(*shooter, *target, target->moveinfo.steps - 1);
342 #if DEBUG_RF
343  if (!(G_IsAlien(shooter) || G_IsCivilian(shooter)))
344  Com_Printf("S%i: removed\n", shooter->number);
345 #endif
346  }
347 }
348 
350 {
351  ReactionFireTargetList* rfts = find(shooter);
352  for (int i = rfts->count - 1; i >= 0; --i)
353  remove(shooter, rfts->targets[i].target);
354 
355  rfts->reset();
356 }
357 
364 int ReactionFireTargets::getTriggerTUs (const Edict* shooter, const Edict* target)
365 {
366  const ReactionFireTargetList* rfts = find(shooter);
367 
368  if (!rfts)
369  return -2; /* the shooter doesn't aim at anything */
370 
371  assert(target);
372 
373  for (int i = 0; i < rfts->count; i++) {
374  const ReactionFireTarget& t = rfts->targets[i];
375  if (t.target == target)
376  return t.triggerTUs;
377  }
378 
379  return -1; /* the shooter doesn't aim at this target */
380 }
381 
382 
389 bool ReactionFireTargets::hasExpired (const Edict* shooter, const Edict* target, const int tusTarget)
390 {
391  const ReactionFireTargetList* rfts = find(shooter);
392 
393  if (!rfts)
394  return false; /* the shooter doesn't aim at anything */
395 
396  assert(target);
397 
398  for (int i = 0; i < rfts->count; i++) {
399  const ReactionFireTarget& t = rfts->targets[i];
400  if (t.target == target)
401  return t.triggerTUs >= target->TU - tusTarget;
402  }
403 
404  return false; /* the shooter doesn't aim at this target */
405 }
406 
407 
413 void ReactionFireTargets::advance (const Edict* shooter, const int tusShot)
414 {
415  ReactionFireTargetList* rfts = find(shooter);
416  assert(rfts);
417 
418  for (int i = 0; i < rfts->count; i++) {
419  ReactionFireTarget& t = rfts->targets[i];
420  t.triggerTUs -= tusShot;
421  }
422 }
423 
428 {
429  rft.init();
430 }
431 
436 void G_ReactionFireTargetsCreate (const Edict* shooter)
437 {
438  rft.create(shooter);
439 }
440 
445 void G_ReactionFireTargetsDestroy (const Edict* shooter)
446 {
447  rft.destroy(shooter);
448 }
449 
451 {
452 private:
453  bool isEnemy(const Actor* shooter, const Edict* target) const;
454  bool canReact(Actor* shooter, const Edict* target) const;
455  bool canSee(const Actor* shooter, const Edict* target) const;
456  bool shoot(Actor* shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode);
457  bool isPossible(Actor* shooter, const Edict* target) const;
458 public:
459  void notifyClientOnStep(const Edict* target, int step);
460  bool checkExecution(const Edict* target, int step);
461  void updateAllTargets(const Edict* target);
462  bool tryToShoot(Actor* shooter, const Edict* target);
463  bool isInWeaponRange(const Actor* shooter, const Edict* target, const fireDef_t* fd) const;
464  const fireDef_t* getFireDef(const Actor* shooter) const;
465  void resetTargets(const Edict* shooter);
466  void notifyClientOnShot(const Edict* target, int tusTarget);
467 };
469 
475 const fireDef_t* ReactionFire::getFireDef (const Actor* shooter) const
476 {
477  const FiremodeSettings* fmSetting = &shooter->chr.RFmode;
478  if (!fmSetting->isSaneFiremode())
479  return nullptr;
480 
481  const Item* weapon = shooter->getHandItem(fmSetting->getHand());
482 
483  if (weapon && weapon->ammoDef() && weapon->isWeapon() && !weapon->mustReload()) {
484  const fireDef_t* fdArray = weapon->getFiredefs();
485  if (fdArray == nullptr)
486  return nullptr;
487 
488  const fireDefIndex_t fmIdx = fmSetting->getFmIdx();
489  return &fdArray[fmIdx];
490  }
491  return nullptr;
492 }
493 
494 bool ReactionFire::isInWeaponRange (const Actor* shooter, const Edict* target, const fireDef_t* fd) const
495 {
496  assert(fd);
497  return fd->range >= VectorDist(shooter->origin, target->origin);
498 }
499 
507 static int G_ReactionFireGetTUsForItem (const Actor* shooter, const Edict* target)
508 {
509  const fireDef_t* fd = rf.getFireDef(shooter);
510  if (!fd)
511  return -1;
512 
513  const int tus = G_ActorGetModifiedTimeForFiredef(shooter, fd, true);
514 
515  if (tus <= shooter->TU && rf.isInWeaponRange(shooter, target, fd)) {
516  return tus;
517  }
518 
519  return -1;
520 }
521 
526 static bool G_ActorHasWorkingFireModeSet (const Edict* actor)
527 {
528  const FiremodeSettings* fmSettings = &actor->chr.RFmode;
529  if (!fmSettings->isSaneFiremode()) /* just checks for valid values */
530  return false;
531 
532  const Item* weapon = actor->getHandItem(fmSettings->getHand());
533  if (!weapon)
534  return false;
535  const fireDef_t* fd = weapon->getFiredefs();
536  if (fd == nullptr)
537  return false;
538 
539  if (fd->obj->weapons[fd->weapFdsIdx] == fmSettings->getWeapon()
540  && fmSettings->getFmIdx() < fd->obj->numFiredefs[fd->weapFdsIdx]) {
541  return true;
542  }
543 
544  return false;
545 }
546 
556 {
557  actor->chr.RFmode.set(hand, fmIdx, od); /* FiremodeSettings */
558 
559  if (!G_ActorHasWorkingFireModeSet(actor)) {
560  /* Disable reaction fire if no valid firemode was found. */
561  G_ClientStateChange(actor->getPlayer(), actor, ~STATE_REACTION, false);
563  G_EventSendState(G_VisToPM(actor->visflags), *actor);
564  return;
565  }
566 
568 
569  /* If reaction fire is active, update the reserved TUs */
570  if (actor->isReaction()) {
572  }
573 }
574 
580 static bool G_ActorHasEnoughTUsReactionFire (const Edict* ent)
581 {
582  const int TUs = G_ActorGetTUForReactionFire(ent);
583  const chrReservations_t* res = &ent->chr.reservedTus;
584  return ent->TU - TUs >= res->shot + res->crouch;
585 }
586 
593 {
595  return true;
596 
598  const Item* item = ent->getHandItem(hand);
599  if (!item) {
600  hand = ACTOR_HAND_LEFT;
601  item = ent->getHandItem(hand);
602  }
603 
604  if (!item)
605  return false;
606 
607  const objDef_t* weapon = item->getReactionFireWeaponType();
608  if (!weapon)
609  return false;
610 
611  ent->chr.RFmode.set(hand, 0, weapon); /* no special firemode */
612 
614  return false;
615 
616  if (!G_IsAI(ent))
618 
619  return true;
620 }
621 
628 static bool G_ReactionFireCanBeEnabled (const Edict* ent)
629 {
630  /* check ent is a suitable shooter */
631  if (!ent->inuse || !G_IsLivingActor(ent))
632  return false;
633 
634  if (G_MatchIsRunning() && ent->getTeam() != level.activeTeam)
635  return false;
636 
637  /* actor may not carry weapons at all - so no further checking is needed */
638  if (!ent->chr.teamDef->weapons && !ent->chr.teamDef->onlyWeapon)
639  return false;
640 
641  if (!ent->chr.inv.holdsReactionFireWeapon()) {
642  G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("No reaction fire enabled weapon."));
643  return false;
644  }
645 
646  if (!G_ActorHasWorkingFireModeSet(ent)) {
647  G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("No fire mode selected for reaction fire."));
648  return false;
649  }
650 
652  G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("Not enough TUs left for activating reaction fire."));
653  return false;
654  }
655 
656  return true;
657 }
658 
666 {
668  const int TUs = G_ActorGetTUForReactionFire(ent);
669  /* Enable requested reaction fire. */
671  return true;
672  }
673 
675  return false;
676 }
677 
678 inline bool ReactionFire::isPossible (Actor* shooter, const Edict* target) const
679 {
680  return isEnemy(shooter, target) && canReact(shooter, target) && canSee(shooter, target);
681 }
682 
688 bool ReactionFire::isEnemy (const Actor* shooter, const Edict* target) const
689 {
690  /* an entity can't reaction fire at itself */
691  if (shooter == target)
692  return false;
693 
694  /* Don't react in your own turn */
695  if (shooter->getTeam() == level.activeTeam)
696  return false;
697 
698  if (G_IsDead(target))
699  return false;
700 
701  /* If reaction fire is triggered by a friendly unit
702  * and the shooter is still sane, don't shoot;
703  * well, if the shooter isn't sane anymore... */
704  if (G_IsCivilian(target) || target->isSameTeamAs(shooter))
705  if (!shooter->isShaken() || (float) shooter->morale / mor_shaken->value > frand())
706  return false;
707 
708  return true;
709 }
710 
716 bool ReactionFire::canReact (Actor* shooter, const Edict* target) const
717 {
718  /* shooter can't use RF if is in STATE_DAZED (flashbang impact) */
719  if (shooter->isDazed())
720  return false;
721 
722  /* check shooter has reaction fire enabled */
723  if (!shooter->isReaction())
724  return false;
725 
726  /* check shooter has weapon in RF hand */
727  if (!shooter->getHandItem(shooter->chr.RFmode.getHand())) {
728  /* print character info if this happens, for now */
729  gi.DPrintf("Reaction fire enabled but no weapon for hand (name=%s,entnum=%i,hand=%i,fmIdx=%i)\n",
730  shooter->chr.name, shooter->getIdNum(), shooter->chr.RFmode.getHand(), shooter->chr.RFmode.getFmIdx());
731  shooter->removeReaction();
732  return false;
733  }
734  return true;
735 }
736 
742 bool ReactionFire::canSee (const Actor* shooter, const Edict* target) const
743 {
744  if (!G_IsVisibleForTeam(target, shooter->getTeam()))
745  return false;
746 
747  /* check in range and visible */
748  const int spotDist = G_VisCheckDist(shooter);
749  if (VectorDistSqr(shooter->origin, target->origin) > spotDist * spotDist)
750  return false;
751 
752  const bool frustum = G_FrustumVis(shooter, target->origin);
753  if (!frustum)
754  return false;
755 
756  const float actorVis = G_ActorVis(shooter, target, true);
757  if (actorVis < ACTOR_VIS_10)
758  return false;
759 
760  return true;
761 }
762 
768 {
769  Actor* shooter = nullptr;
770 
771  /* check all possible shooters */
772  while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
773  /* check whether reaction fire is possible (friend/foe, LoS) */
774  if (isPossible(shooter, target)) {
775  const int TUs = G_ReactionFireGetTUsForItem(shooter, target);
776  if (TUs < 0)
777  continue; /* no suitable weapon */
778  rft.add(shooter, target, TUs);
779  } else {
780  rft.remove(shooter, target);
781  }
782  }
783 }
784 
785 void ReactionFire::resetTargets (const Edict* shooter)
786 {
787  rft.resetTargetList(shooter);
788 }
789 
799 bool ReactionFire::shoot (Actor* shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode)
800 {
801  const Item* weapon = nullptr;
802  if (IS_SHOT_RIGHT(type)) {
803  weapon = shooter->getRightHandItem();
804  if (!weapon)
805  return false;
806  } else {
807  weapon = shooter->getLeftHandItem();
808  if (!weapon)
809  return false;
810  }
811 
812  const fireDef_t* fdArray = weapon->getFiredefs();
813  if (!fdArray)
814  return false;
815 
816  /* Adjust the number of samples we take so that we don't end firing thousands of shots
817  * in case the fire mode is multi-shot */
818  const int shotsPerFD = fdArray[firemode].shots;
819  const int samples = std::max(1, 100 / shotsPerFD);
820  const Player& player = shooter->getPlayer();
821  shot_mock_t mock;
822  for (int i = 0; i < samples; ++i)
823  if (!G_ClientShoot(player, shooter, at, type, firemode, &mock, false, 0))
824  break;
825 
826  /* this is the max amount of friendly units that were hit during the mock calculation */
827  const int maxShots = samples * shotsPerFD;
828  int maxff;
829  if (shooter->isInsane())
830  maxff = maxShots;
831  else if (shooter->isRaged())
832  maxff = maxShots * 2 / 3;
833  else if (shooter->isPanicked())
834  maxff = maxShots / 3;
835  else if (shooter->isShaken())
836  maxff = maxShots / 6;
837  else
838  maxff = maxShots / 20;
839 
840  /* calculate the mock values - e.g. how many friendly units we would hit
841  * when opening the reaction fire */
842  const int ff = mock.friendCount + (G_IsAlien(shooter) ? 0 : mock.civilian);
843  const int hits = shooter->isInsane() ? ff + mock.enemyCount : mock.enemyCount;
844  if (ff <= maxff && hits > 0)
845  return G_ClientShoot(player, shooter, at, type, firemode, nullptr, false, 0);
846 
847  return false;
848 }
849 
856 bool ReactionFire::tryToShoot (Actor* shooter, const Edict* target)
857 {
858  /* check for valid target */
859  assert(target);
860 
861  /* shooter can't take a reaction shot if it's not possible - and check that
862  * the target is still alive */
863  if (!isPossible(shooter, target)) {
864  rft.remove(shooter, target);
865  return false;
866  }
867 
868  /* take the shot */
869  const actorHands_t hand = shooter->chr.RFmode.getHand();
872  const bool tookShot = rf.shoot(shooter, target->pos, type, shooter->chr.RFmode.getFmIdx());
873 
874  if (tookShot) {
875  /* clear any shakenness */
876  shooter->removeShaken();
877  }
878 
879  return tookShot;
880 }
881 
882 void ReactionFire::notifyClientOnShot (const Edict* target, int tusTarget)
883 {
884  rft.notifyClientOnShot(target, tusTarget);
885 }
886 
887 void ReactionFire::notifyClientOnStep (const Edict* target, int step)
888 {
889  rft.notifyClientOnStep(target, step);
890 }
891 
900 bool ReactionFire::checkExecution (const Edict* target, int step)
901 {
902  Actor* shooter = nullptr;
903  bool fired = false;
904 
905  /* check all possible shooters */
906  while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
907  const int tus = G_ReactionFireGetTUsForItem(shooter, target);
908  /* indicates an RF weapon is there */
909  if (tus <= 1)
910  continue;
911  if (!rft.hasExpired(shooter, target, 0))
912  continue;
913  if (!rf.tryToShoot(shooter, target)) {
914  G_ReactionFireNotifyClientRFAborted(shooter, target, step);
915  continue;
916  }
917  rft.advance(shooter, tus);
918  fired |= true;
919  }
920  return fired;
921 }
922 
923 #if DEBUG_RF
924 
928 static void G_ReactionFirePrintSituation (Edict* target)
929 {
930  if (!G_IsAlien(target))
931  return;
932 
933  Com_Printf("Alien %i at %i/%i/%i TU:%i\n", target->number, target->pos[0], target->pos[1], target->pos[2], target->TU);
934 
935  Actor* shooter = nullptr;
936  /* check all possible shooters */
937  while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
938  if (G_IsAlien(shooter) || G_IsCivilian(shooter))
939  continue;
940  char msgHdr[100];
941  Com_sprintf(msgHdr, sizeof(msgHdr), "S%i: at %i/%i/%i RF: ", shooter->number, shooter->pos[0], shooter->pos[1], shooter->pos[2]);
942  int ttus = rft.getTriggerTUs(shooter, target);
943  if (ttus == -2)
944  Com_Printf("%s not initialized\n", msgHdr);
945  if (ttus == -1)
946  Com_Printf("%s not aiming\n", msgHdr);
947  else if (rft.hasExpired(shooter, target, 0))
948  Com_Printf("expired\n", msgHdr);
949  else
950  Com_Printf("%s not yet: %i\n", msgHdr, ttus);
951  }
952 }
953 #endif
954 
962 bool G_ReactionFireOnMovement (Actor* target, int step)
963 {
964 #if DEBUG_RF
965  G_ReactionFirePrintSituation(target);
966 #endif
967  rf.notifyClientOnStep(target, step);
968 
969  /* Check to see whether this resolves any reaction fire */
970  const bool fired = rf.checkExecution(target, step);
971 
972  /* Check to see whether this triggers any reaction fire */
973  rf.updateAllTargets(target);
974 
975  return fired;
976 }
977 
978 static void G_ReactionFireNotifyClientStartShot (const Edict* target)
979 {
980  rft.notifyClientMove(target, MAX_ROUTE, true);
981 }
982 
983 static void G_ReactionFireNotifyClientEndShot (const Edict* target)
984 {
985  rft.notifyClientMove(target, MAX_ROUTE, false);
986 }
987 
994 void G_ReactionFirePreShot (const Actor* target, const int fdTime)
995 {
996  bool repeat = true;
997 
998  /* Check to see whether this triggers any reaction fire */
1000  rf.updateAllTargets(target);
1001  rf.notifyClientOnShot(target, fdTime);
1002 
1003  /* if any reaction fire occurs, we have to loop through all entities again to allow
1004  * multiple (fast) RF snap shots before a (slow) aimed shot from the target occurs. */
1005  while (repeat) {
1006  Actor* shooter = nullptr;
1007  repeat = false;
1008  /* check all ents to see who wins and who loses a draw */
1009  while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
1010  const int entTUs = G_ReactionFireGetTUsForItem(shooter, target);
1011  /* indicates an RF weapon is there */
1012  if (entTUs < 1)
1013  continue;
1014  if (!rft.hasExpired(shooter, target, fdTime))
1015  continue;
1016  if (!rf.tryToShoot(shooter, target)) {
1018  continue;
1019  }
1020  repeat = true;
1021  rft.advance(shooter, entTUs);
1022  }
1023  }
1024 }
1025 
1030 void G_ReactionFireOnDead (const Actor* target)
1031 {
1032  assert(G_IsDead(target));
1033  rf.updateAllTargets(target);
1034  rf.resetTargets(target);
1035 }
1036 
1043 {
1044  /* Check to see whether this resolves any reaction fire */
1045  rf.notifyClientOnShot(target, 0);
1046  rf.checkExecution(target, MAX_ROUTE);
1048 }
1049 
1055 {
1056  /* we explicitly do nothing at end of turn, just reset the table */
1057  rft.reset();
1058 }
1059 
1066 void G_ReactionFireReset (int team)
1067 {
1068  Actor* actor = nullptr;
1069 
1070  while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, team))) {
1071  actor->removeShaken();
1072  }
1073 }
1074 
1076 {
1077  /* note that this is sent _before_ the actual move event, so we can't use the step number */
1078  rft.notifyClientMove(target, MAX_ROUTE, true);
1079 }
1080 
1082 {
1083  rft.notifyClientMove(target, target->moveinfo.steps - 1, false);
1084 }
1085 
1086 void G_ReactionFireNotifyClientRFAborted (const Actor* shooter, const Edict* target, int step)
1087 {
1088  rft.notifyClientRFAborted(shooter, target, step);
1089 }
int number
Definition: g_edict.h:51
A table with all the relations between all shooters and all their targets.
Definition: g_reaction.cpp:121
const objDef_t * onlyWeapon
Definition: chr_shared.h:325
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition: g_client.cpp:206
void notifyClientRFAborted(const Edict *shooter, const Edict *target, int step)
Definition: g_reaction.cpp:220
void notifyClientOnShot(const Edict *target, int tusTarget)
Definition: g_reaction.cpp:882
void create(const Edict *shooter)
Create a table of reaction fire targets for the given edict.
Definition: g_reaction.cpp:253
int getIdNum() const
Definition: g_edict.h:231
bool mustReload() const
Definition: inv_shared.h:483
Reaction fire system.
void removeReaction()
Definition: g_edict.h:378
void G_ReactionFireNotifyClientEndMove(const Actor *target)
const objDef_t * getWeapon() const
Definition: chr_shared.h:154
bool isSameTeamAs(const Edict *other) const
Definition: g_edict.h:278
bool canSee(const Actor *shooter, const Edict *target) const
Check whether shooter can see his target well enough.
Definition: g_reaction.cpp:742
bool tryToShoot(Actor *shooter, const Edict *target)
Resolve the reaction fire for an entity, this checks that the entity can fire and then takes the shot...
Definition: g_reaction.cpp:856
void add(const Edict *shooter, const Edict *target, const int tusForShot)
Add a reaction fire target for the given shooter.
Definition: g_reaction.cpp:293
#define ACTOR_VIS_10
Definition: g_vis.h:63
int getTriggerTUs(const Edict *shooter, const Edict *target)
Check if the given shooter is ready to reaction fire at the given target.
Definition: g_reaction.cpp:364
const objDef_t * getReactionFireWeaponType() const
Checks whether this item is a reaction fire enabled weapon.
Definition: inv_shared.cpp:649
static void G_ReactionFireNotifyClientStartShot(const Edict *target)
Definition: g_reaction.cpp:978
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition: g_edicts.cpp:196
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
bool isInWeaponRange(const Actor *shooter, const Edict *target, const fireDef_t *fd) const
Definition: g_reaction.cpp:494
void G_EventReactionFireAbortShot(const Edict &shooter, const Edict &target, int step)
Definition: g_events.cpp:318
void resetTargetList(const Edict *shooter)
Definition: g_reaction.cpp:349
this is a fire definition for our weapons/ammo
Definition: inv_shared.h:110
void G_ReactionFireOnDead(const Actor *target)
Removes the given target from the reaction fire lists.
bool isRaged() const
Definition: g_edict.h:358
const teamDef_t * teamDef
Definition: chr_shared.h:394
int civilian
Definition: g_combat.h:34
#define _(String)
Definition: cl_shared.h:43
weaponFireDefIndex_t weapFdsIdx
Definition: inv_shared.h:126
void notifyClientMove(const Edict *target, int step, bool startMove)
Definition: g_reaction.cpp:200
void G_ClientStateChange(const Player &player, Actor *actor, int reqState, bool checkaction)
Changes the state of a player/soldier.
Definition: g_client.cpp:473
void set(const actorHands_t hand, const fireDefIndex_t fmIdx, const objDef_t *weapon)
Definition: chr_shared.h:166
A single relation between a shooter and his target.
Definition: g_reaction.cpp:88
#define STATE_REACTION
Definition: q_shared.h:272
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
bool isPanicked() const
Definition: g_edict.h:356
int activeTeam
Definition: g_local.h:101
character_t chr
Definition: g_edict.h:116
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
Definition: inv_shared.cpp:576
int enemyCount
Definition: g_combat.h:32
ReactionFireTargetList rfData[MAX_RF_DATA]
Definition: g_reaction.cpp:140
#define ST_LEFT_REACTION
The left hand reaction fire should be used for shooting.
Definition: q_shared.h:226
bool inuse
Definition: g_edict.h:47
float value
Definition: cvar.h:80
void G_ActorReserveTUs(Edict *ent, int resReaction, int resShot, int resCrouch)
Reserves TUs for different actor actions.
Definition: g_actor.cpp:136
#define VectorDist(a, b)
Definition: vector.h:69
void init()
Initialize the reaction fire table for all entities.
Definition: g_reaction.cpp:149
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
int getFmIdx() const
Definition: chr_shared.h:150
bool isSaneFiremode() const
Definition: chr_shared.h:146
moveinfo_t moveinfo
Definition: g_edict.h:160
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
bool holdsReactionFireWeapon() const
Checks if there is a weapon in the hands that can be used for reaction fire.
void G_ReactionFireSettingsUpdate(Actor *actor, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t *od)
Updates the reaction fire settings in case something was moved into a hand or from a hand that would ...
Definition: g_reaction.cpp:555
bool G_IsLivingActor(const Edict *ent)
Checks whether the given edict is a living actor.
Definition: g_actor.cpp:43
float G_ActorVis(const Edict *ent, const Edict *check, bool full)
calculate how much check is "visible" by ent
Definition: g_vis.cpp:100
Player & getPlayer() const
Definition: g_edict.h:265
void G_ReactionFireTargetsCreate(const Edict *shooter)
free function to create a table of reaction fire targets for the given edict.
Definition: g_reaction.cpp:436
const struct objDef_s * obj
Definition: inv_shared.h:125
bool isWeapon() const
Definition: inv_shared.h:486
const fireDef_t * getFireDef(const Actor *shooter) const
Get the fireDef for the RF settings of the shooter.
Definition: g_reaction.cpp:475
Item * getRightHandItem() const
Definition: g_edict.h:249
void G_EventSendState(playermask_t playerMask, const Edict &ent)
Definition: g_events.cpp:466
#define IS_SHOT_RIGHT(x)
Determine whether the selected shoot type is for the item in the right hand, either shooting or react...
Definition: q_shared.h:243
int getTeam() const
Definition: g_edict.h:269
bool G_MatchIsRunning(void)
Checks whether the game is running (active team and no intermission time)
Definition: g_match.cpp:320
item instance data, with linked list capability
Definition: inv_shared.h:402
Match related functions.
static bool G_ActorHasWorkingFireModeSet(const Edict *actor)
Checks if the currently selected firemode is usable with the defined weapon.
Definition: g_reaction.cpp:526
used in shot probability calculations (pseudo shots)
Definition: g_combat.h:31
static bool G_ActorHasEnoughTUsReactionFire(const Edict *ent)
Checks whether an actor has enough TUs left to activate reaction fire.
Definition: g_reaction.cpp:580
static ReactionFire rf
Definition: g_reaction.cpp:468
bool checkExecution(const Edict *target, int step)
Check all entities to see whether target has caused reaction fire to resolve.
Definition: g_reaction.cpp:900
fireDefIndex_t numFiredefs[MAX_WEAPONS_PER_OBJDEF]
Definition: inv_shared.h:315
int friendCount
Definition: g_combat.h:33
game_import_t gi
Definition: g_main.cpp:39
#define OBJZERO(obj)
Definition: shared.h:178
void G_ReactionFireOnEndTurn(void)
Called at the end of turn, all outstanding reaction fire is resolved.
int32_t shoot_types_t
Available shoot types - also see the ST_ constants.
Definition: q_shared.h:206
void G_EventReactionFireChange(const Edict &ent)
Definition: g_events.cpp:282
#define G_IsCivilian(ent)
Definition: g_local.h:148
bool canReact(Actor *shooter, const Edict *target) const
Check whether shooter can reaction fire at target at all.
Definition: g_reaction.cpp:716
int TU
Definition: g_edict.h:88
#define MAX_RF_TARGETS
Definition: g_reaction.cpp:82
#define RF_NO_ENTNUM
Definition: g_reaction.cpp:95
teammask_t visflags
Definition: g_edict.h:82
void updateAllTargets(const Edict *target)
Check whether 'target' has just triggered any new reaction fire.
Definition: g_reaction.cpp:767
All parts of the main game logic that are combat related.
#define ST_RIGHT_REACTION
The right hand reaction fire should be used for shooting.
Definition: q_shared.h:216
void removeShaken()
Definition: g_edict.h:375
int32_t fireDefIndex_t
Definition: inv_shared.h:78
A list of relations between a shooter and all his targets.
Definition: g_reaction.cpp:98
int G_ActorGetModifiedTimeForFiredef(const Edict *const ent, const fireDef_t *const fd, const bool reaction)
Definition: g_actor.cpp:764
void G_ReactionFireNotifyClientStartMove(const Actor *target)
pos3_t pos
Definition: g_edict.h:55
#define MAX_ROUTE
Definition: defines.h:84
actorHands_t
Definition: inv_shared.h:626
An Edict of type Actor.
Definition: g_edict.h:348
void advance(const Edict *shooter, const int tusShot)
Increase the triggertime for the next RF shot for all targets of the shooter (after a reaction fire)...
Definition: g_reaction.cpp:413
bool isInsane() const
Definition: g_edict.h:359
#define MAX_RF_DATA
Definition: g_reaction.cpp:83
static void G_ReactionFireNotifyClientEndShot(const Edict *target)
Definition: g_reaction.cpp:983
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition: g_client.cpp:186
void notifyClientOnShot(const Edict *target, int step)
Definition: g_reaction.cpp:183
ReactionFireTargetList * find(const Edict *shooter)
Find the given edict's table of reaction fire targets.
Definition: g_reaction.cpp:237
bool isPossible(Actor *shooter, const Edict *target) const
Definition: g_reaction.cpp:678
pos_t pos3_t[3]
Definition: ufotypes.h:58
void G_ReactionFireTargetsInit(void)
free function to initialize the reaction fire table for all entities.
Definition: g_reaction.cpp:427
void resetTargets(const Edict *shooter)
Definition: g_reaction.cpp:785
bool G_ReactionFireOnMovement(Actor *target, int step)
Called when 'target' moves, possibly triggering or resolving reaction fire.
Definition: g_reaction.cpp:962
void notifyClientOnStep(const Edict *target, int step)
Definition: g_reaction.cpp:887
bool isShaken() const
Definition: g_edict.h:354
Item * getHandItem(actorHands_t hand) const
Definition: g_edict.h:255
bool isDazed() const
Definition: g_edict.h:360
void G_ReactionFireTargetsDestroy(const Edict *shooter)
free function to destroy the table of reaction fire targets for the given edict.
Definition: g_reaction.cpp:445
void G_ReactionFirePreShot(const Actor *target, const int fdTime)
Called when 'target' is about to shoot, this forces a 'draw' to decide who gets the first shot...
Definition: g_reaction.cpp:994
bool shoot(Actor *shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode)
Perform the reaction fire shot.
Definition: g_reaction.cpp:799
float range
Definition: inv_shared.h:152
FiremodeSettings RFmode
Definition: chr_shared.h:397
static bool G_ReactionFireCanBeEnabled(const Edict *ent)
Checks whether the actor is allowed to activate reaction fire and will informs the player about the r...
Definition: g_reaction.cpp:628
#define G_IsAI(ent)
Definition: g_local.h:141
bool G_ClientShoot(const Player &player, Actor *actor, const pos3_t at, shoot_types_t shootType, fireDefIndex_t firemode, shot_mock_t *mock, bool allowReaction, int z_align)
Setup for shooting, either real or mock.
Definition: g_combat.cpp:1183
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition: g_edicts.cpp:216
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
QGL_EXTERN GLint i
Definition: r_gl.h:113
ReactionFireTarget targets[MAX_RF_TARGETS]
Definition: g_reaction.cpp:103
#define G_IsDead(ent)
Definition: g_actor.h:34
bool hasExpired(const Edict *shooter, const Edict *target, const int tusTarget)
Check if the given shooter is ready to reaction fire at the given target.
Definition: g_reaction.cpp:389
#define G_IsAlien(ent)
Definition: g_local.h:149
bool G_FrustumVis(const Edict *from, const vec3_t point)
Checks whether a point is "visible" from the edicts position.
Definition: g_vis.cpp:38
static int G_ReactionFireGetTUsForItem(const Actor *shooter, const Edict *target)
Get the weapon firing TUs of the item in the hand of the shooter.
Definition: g_reaction.cpp:507
functions to handle the storage and lifecycle of all edicts in the game module.
bool isEnemy(const Actor *shooter, const Edict *target) const
Check whether we want to shoot at the target.
Definition: g_reaction.cpp:688
vec3_t origin
Definition: g_edict.h:53
byte steps
Definition: g_local.h:278
bool isReaction() const
Definition: g_edict.h:357
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
Edict const * target
Definition: g_reaction.cpp:91
void G_EventReactionFireAddTarget(const Edict &shooter, const Edict &target, int tus, int step)
Definition: g_events.cpp:295
void reset()
Reset the target count in the reaction fire table for all entities.
Definition: g_reaction.cpp:159
Inventory inv
Definition: chr_shared.h:392
bool G_ReactionFireSettingsReserveTUs(Actor *ent)
Set the reaction fire TU reservation for an actor.
Definition: g_reaction.cpp:665
char name[MAX_VAR]
Definition: chr_shared.h:371
int morale
Definition: g_edict.h:91
void G_EventReactionFireTargetUpdate(const Edict &shooter, const Edict &target, int tus, int step)
Definition: g_events.cpp:310
#define G_IsVisibleForTeam(ent, team)
Definition: g_local.h:144
GLsizei const GLvoid * data
Definition: r_gl.h:152
Edict * G_EdictsGetByNum(const int num)
Get an entity by it's number.
Definition: g_edicts.cpp:83
actorHands_t getHand() const
Definition: chr_shared.h:158
void G_ReactionFirePostShot(Actor *target)
Called after 'target' has fired, this might trigger more reaction fire or resolve outstanding reactio...
cvar_t * mor_shaken
Definition: g_main.cpp:102
How many TUs (and of what type) did a player reserve for a unit?
Definition: chr_shared.h:179
void G_ReactionFireNotifyClientRFAborted(const Actor *shooter, const Edict *target, int step)
void notifyClientOnStep(const Edict *target, int step)
Definition: g_reaction.cpp:166
#define ST_NUM_SHOOT_TYPES
Amount of shoottypes available.
Definition: q_shared.h:236
Definition: g_edict.h:45
Item * getLeftHandItem() const
Definition: g_edict.h:252
bool weapons
Definition: chr_shared.h:324
#define VectorDistSqr(a, b)
Definition: vector.h:68
static bool G_ReactionFireSettingsSetDefault(Edict *ent)
Definition: g_reaction.cpp:592
void remove(const Edict *shooter, const Edict *target)
Remove a reaction fire target for the given shooter.
Definition: g_reaction.cpp:323
void destroy(const Edict *shooter)
Destroys the table of reaction fire targets for the given edict.
Definition: g_reaction.cpp:275
int G_VisCheckDist(const Edict *const ent)
Definition: g_vis.cpp:163
Interface for g_client.cpp.
const struct objDef_s * weapons[MAX_WEAPONS_PER_OBJDEF]
Definition: inv_shared.h:311
static ReactionFireTargets rft
Definition: g_reaction.cpp:144
void G_EventReactionFireRemoveTarget(const Edict &shooter, const Edict &target, int step)
Definition: g_events.cpp:303
void G_ReactionFireReset(int team)
Guess! Reset all "shaken" states on end of turn?
level_locals_t level
Definition: g_main.cpp:38
#define PRINT_HUD
Definition: defines.h:107
int G_ActorGetTUForReactionFire(const Edict *ent)
Calculates the amount of TUs that are needed for the current selected reaction fire mode...
Definition: g_actor.cpp:116
chrReservations_t reservedTus
Definition: chr_shared.h:396