UFO: Alien Invasion
Loading...
Searching...
No Matches
g_reaction.cpp
Go to the documentation of this file.
1
53
54/*
55Copyright (C) 2002-2025 UFO: Alien Invasion.
56
57This program is free software; you can redistribute it and/or
58modify it under the terms of the GNU General Public License
59as published by the Free Software Foundation; either version 2
60of the License, or (at your option) any later version.
61
62This program is distributed in the hope that it will be useful,
63but WITHOUT ANY WARRANTY; without even the implied warranty of
64MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
65
66See the GNU General Public License for more details.
67
68You should have received a copy of the GNU General Public License
69along with this program; if not, write to the Free Software
70Foundation, 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{
90public:
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{
100public:
102 int count;
104
109
110 inline void init (void) {
112 count = 0;
113 }
114
115 inline void reset (void) {
116 count = 0;
117 }
118};
119
122{
123public:
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
139private:
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
166void ReactionFireTargets::notifyClientOnStep (const Edict* target, int step)
167{
168 for (int i = 0; i < MAX_RF_DATA; 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
183void ReactionFireTargets::notifyClientOnShot (const Edict* target, int tusTarget)
184{
185 for (int i = 0; i < MAX_RF_DATA; 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
200void ReactionFireTargets::notifyClientMove (const Edict* target, int step, bool startMove)
201{
202 for (int i = 0; i < MAX_RF_DATA; 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
220void 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++) {
241 if (rfts->entnum == shooter->getIdNum()) {
242 return rfts;
243 }
244 }
245 return nullptr;
246}
247
248
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
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
293void 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
323void 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
364int 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
389bool 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
413void 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
437{
438 rft.create(shooter);
439}
440
446{
447 rft.destroy(shooter);
448}
449
451{
452private:
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;
458public:
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
475const 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
494bool 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
507static 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
526static 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
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
628static 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
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
678inline bool ReactionFire::isPossible (Actor* shooter, const Edict* target) const
679{
680 return isEnemy(shooter, target) && canReact(shooter, target) && canSee(shooter, target);
681}
682
688bool 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
716bool 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
742bool 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
786{
787 rft.resetTargetList(shooter);
788}
789
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
856bool 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
882void ReactionFire::notifyClientOnShot (const Edict* target, int tusTarget)
883{
884 rft.notifyClientOnShot(target, tusTarget);
885}
886
887void ReactionFire::notifyClientOnStep (const Edict* target, int step)
888{
889 rft.notifyClientOnStep(target, step);
890}
891
900bool 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
928static 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
962bool 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
979{
980 rft.notifyClientMove(target, MAX_ROUTE, true);
981}
982
983static void G_ReactionFireNotifyClientEndShot (const Edict* target)
984{
985 rft.notifyClientMove(target, MAX_ROUTE, false);
986}
987
994void 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
1030void 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
1066void 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
1086void G_ReactionFireNotifyClientRFAborted (const Actor* shooter, const Edict* target, int step)
1087{
1088 rft.notifyClientRFAborted(shooter, target, step);
1089}
#define _(String)
Definition cl_shared.h:44
An Edict of type Actor.
Definition g_edict.h:348
bool isShaken() const
Definition g_edict.h:354
void removeShaken()
Definition g_edict.h:375
bool isPanicked() const
Definition g_edict.h:356
void removeReaction()
Definition g_edict.h:378
bool isInsane() const
Definition g_edict.h:359
bool isRaged() const
Definition g_edict.h:358
bool isReaction() const
Definition g_edict.h:357
bool isDazed() const
Definition g_edict.h:360
teammask_t visflags
Definition g_edict.h:82
int TU
Definition g_edict.h:88
bool isSameTeamAs(const Edict *other) const
Definition g_edict.h:278
character_t chr
Definition g_edict.h:116
int getIdNum() const
Definition g_edict.h:231
Item * getRightHandItem() const
Definition g_edict.h:249
vec3_t origin
Definition g_edict.h:53
pos3_t pos
Definition g_edict.h:55
moveinfo_t moveinfo
Definition g_edict.h:160
Item * getLeftHandItem() const
Definition g_edict.h:252
bool inuse
Definition g_edict.h:47
int getTeam() const
Definition g_edict.h:269
Player & getPlayer() const
Definition g_edict.h:265
Item * getHandItem(actorHands_t hand) const
Definition g_edict.h:255
int number
Definition g_edict.h:51
int morale
Definition g_edict.h:91
void set(const actorHands_t hand, const fireDefIndex_t fmIdx, const objDef_t *weapon)
Definition chr_shared.h:173
actorHands_t getHand() const
Definition chr_shared.h:165
const objDef_t * getWeapon() const
Definition chr_shared.h:161
int getFmIdx() const
Definition chr_shared.h:157
bool isSaneFiremode() const
Definition chr_shared.h:153
bool holdsReactionFireWeapon() const
Checks if there is a weapon in the hands that can be used for reaction fire.
item instance data, with linked list capability
Definition inv_shared.h:402
const objDef_t * ammoDef(void) const
Definition inv_shared.h:460
bool mustReload() const
Definition inv_shared.h:483
bool isWeapon() const
Definition inv_shared.h:486
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
const objDef_t * getReactionFireWeaponType() const
Checks whether this item is a reaction fire enabled weapon.
const fireDef_t * getFireDef(const Actor *shooter) const
Get the fireDef for the RF settings of the shooter.
void resetTargets(const Edict *shooter)
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...
void notifyClientOnStep(const Edict *target, int step)
void notifyClientOnShot(const Edict *target, int tusTarget)
bool isInWeaponRange(const Actor *shooter, const Edict *target, const fireDef_t *fd) const
bool canReact(Actor *shooter, const Edict *target) const
Check whether shooter can reaction fire at target at all.
bool isPossible(Actor *shooter, const Edict *target) const
bool isEnemy(const Actor *shooter, const Edict *target) const
Check whether we want to shoot at the target.
bool canSee(const Actor *shooter, const Edict *target) const
Check whether shooter can see his target well enough.
bool shoot(Actor *shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode)
Perform the reaction fire shot.
void updateAllTargets(const Edict *target)
Check whether 'target' has just triggered any new reaction fire.
bool checkExecution(const Edict *target, int step)
Check all entities to see whether target has caused reaction fire to resolve.
A single relation between a shooter and his target.
Edict const * target
A list of relations between a shooter and all his targets.
ReactionFireTarget targets[MAX_RF_TARGETS]
A table with all the relations between all shooters and all their targets.
void create(const Edict *shooter)
Create a table of reaction fire targets for the given edict.
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).
void notifyClientOnShot(const Edict *target, int step)
void remove(const Edict *shooter, const Edict *target)
Remove a reaction fire target for the given shooter.
int getTriggerTUs(const Edict *shooter, const Edict *target)
Check if the given shooter is ready to reaction fire at the given target.
void resetTargetList(const Edict *shooter)
void notifyClientOnStep(const Edict *target, int step)
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.
ReactionFireTargetList * find(const Edict *shooter)
Find the given edict's table of reaction fire targets.
ReactionFireTargetList rfData[MAX_RF_DATA]
void add(const Edict *shooter, const Edict *target, const int tusForShot)
Add a reaction fire target for the given shooter.
void destroy(const Edict *shooter)
Destroys the table of reaction fire targets for the given edict.
void notifyClientRFAborted(const Edict *shooter, const Edict *target, int step)
void reset()
Reset the target count in the reaction fire table for all entities.
void init()
Initialize the reaction fire table for all entities.
void notifyClientMove(const Edict *target, int step, bool startMove)
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define PRINT_HUD
Definition defines.h:107
#define MAX_ROUTE
Definition defines.h:84
void G_ActorReserveTUs(Edict *ent, int resReaction, int resShot, int resCrouch)
Reserves TUs for different actor actions.
Definition g_actor.cpp:136
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
int G_ActorGetModifiedTimeForFiredef(const Edict *const ent, const fireDef_t *const fd, const bool reaction)
Definition g_actor.cpp:764
bool G_IsLivingActor(const Edict *ent)
Checks whether the given edict is a living actor.
Definition g_actor.cpp:43
#define G_IsDead(ent)
Definition g_actor.h:34
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 G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition g_client.cpp:206
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition g_client.cpp:186
Interface for g_client.cpp.
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.
All parts of the main game logic that are combat related.
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition g_edicts.cpp:216
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition g_edicts.cpp:196
Edict * G_EdictsGetByNum(const int num)
Get an entity by it's number.
Definition g_edicts.cpp:83
functions to handle the storage and lifecycle of all edicts in the game module.
void G_EventReactionFireChange(const Edict &ent)
Definition g_events.cpp:282
void G_EventReactionFireRemoveTarget(const Edict &shooter, const Edict &target, int step)
Definition g_events.cpp:303
void G_EventSendState(playermask_t playerMask, const Edict &ent)
Definition g_events.cpp:466
void G_EventReactionFireTargetUpdate(const Edict &shooter, const Edict &target, int tus, int step)
Definition g_events.cpp:310
void G_EventReactionFireAbortShot(const Edict &shooter, const Edict &target, int step)
Definition g_events.cpp:318
void G_EventReactionFireAddTarget(const Edict &shooter, const Edict &target, int tus, int step)
Definition g_events.cpp:295
cvar_t * mor_shaken
Definition g_main.cpp:102
level_locals_t level
Definition g_main.cpp:38
#define G_IsAI(ent)
Definition g_local.h:141
#define G_IsVisibleForTeam(ent, team)
Definition g_local.h:144
game_import_t gi
Definition g_main.cpp:39
#define G_IsCivilian(ent)
Definition g_local.h:148
#define G_IsAlien(ent)
Definition g_local.h:149
bool G_MatchIsRunning(void)
Checks whether the game is running (active team and no intermission time).
Definition g_match.cpp:320
Match related functions.
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 ...
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...
void G_ReactionFireTargetsInit(void)
free function to initialize the reaction fire table for all entities.
bool G_ReactionFireOnMovement(Actor *target, int step)
Called when 'target' moves, possibly triggering or resolving reaction fire.
void G_ReactionFireNotifyClientEndMove(const Actor *target)
void G_ReactionFireTargetsCreate(const Edict *shooter)
free function to create a table of reaction fire targets for the given edict.
void G_ReactionFireNotifyClientRFAborted(const Actor *shooter, const Edict *target, int step)
static int G_ReactionFireGetTUsForItem(const Actor *shooter, const Edict *target)
Get the weapon firing TUs of the item in the hand of the shooter.
void G_ReactionFireNotifyClientStartMove(const Actor *target)
#define MAX_RF_TARGETS
static bool G_ActorHasEnoughTUsReactionFire(const Edict *ent)
Checks whether an actor has enough TUs left to activate reaction fire.
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.
void G_ReactionFireOnEndTurn(void)
Called at the end of turn, all outstanding reaction fire is resolved.
void G_ReactionFireTargetsDestroy(const Edict *shooter)
free function to destroy the table of reaction fire targets for the given edict.
static bool G_ActorHasWorkingFireModeSet(const Edict *actor)
Checks if the currently selected firemode is usable with the defined weapon.
static void G_ReactionFireNotifyClientStartShot(const Edict *target)
static bool G_ReactionFireSettingsSetDefault(Edict *ent)
static ReactionFireTargets rft
#define RF_NO_ENTNUM
#define MAX_RF_DATA
void G_ReactionFirePostShot(Actor *target)
Called after 'target' has fired, this might trigger more reaction fire or resolve outstanding reactio...
static void G_ReactionFireNotifyClientEndShot(const Edict *target)
void G_ReactionFireReset(int team)
Guess! Reset all "shaken" states on end of turn?
bool G_ReactionFireSettingsReserveTUs(Actor *ent)
Set the reaction fire TU reservation for an actor.
void G_ReactionFireOnDead(const Actor *target)
Removes the given target from the reaction fire lists.
static ReactionFire rf
Reaction fire system.
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
int G_VisCheckDist(const Edict *const ent)
Definition g_vis.cpp:163
float G_ActorVis(const Edict *ent, const Edict *check, bool full)
calculate how much check is "visible" by ent
Definition g_vis.cpp:100
#define ACTOR_VIS_10
Definition g_vis.h:63
int32_t fireDefIndex_t
Definition inv_shared.h:78
actorHands_t
Definition inv_shared.h:626
@ ACTOR_HAND_LEFT
Definition inv_shared.h:629
@ ACTOR_HAND_RIGHT
Definition inv_shared.h:628
float frand(void)
Return random values between 0 and 1.
Definition mathlib.cpp:506
#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
#define ST_RIGHT_REACTION
The right hand reaction fire should be used for shooting.
Definition q_shared.h:216
#define STATE_REACTION
Definition q_shared.h:272
#define ST_LEFT_REACTION
The left hand reaction fire should be used for shooting.
Definition q_shared.h:226
#define ST_NUM_SHOOT_TYPES
Amount of shoottypes available.
Definition q_shared.h:236
int32_t shoot_types_t
Available shoot types - also see the ST_ constants.
Definition q_shared.h:206
QGL_EXTERN GLsizei const GLvoid * data
Definition r_gl.h:89
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
#define OBJZERO(obj)
Definition shared.h:178
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
chrReservations_t reservedTus
Definition chr_shared.h:415
const teamDef_t * teamDef
Definition chr_shared.h:413
FiremodeSettings RFmode
Definition chr_shared.h:416
char name[MAX_VAR]
Definition chr_shared.h:390
Inventory inv
Definition chr_shared.h:411
How many TUs (and of what type) did a player reserve for a unit?
Definition chr_shared.h:186
this is a fire definition for our weapons/ammo
Definition inv_shared.h:110
const struct objDef_s * obj
Definition inv_shared.h:125
weaponFireDefIndex_t weapFdsIdx
Definition inv_shared.h:126
float range
Definition inv_shared.h:152
byte steps
Definition g_local.h:278
Defines all attributes of objects used in the inventory.
Definition inv_shared.h:264
used in shot probability calculations (pseudo shots)
Definition g_combat.h:31
int friendCount
Definition g_combat.h:33
int civilian
Definition g_combat.h:34
int enemyCount
Definition g_combat.h:32
bool weapons
Definition chr_shared.h:335
const objDef_t * onlyWeapon
Definition chr_shared.h:336
pos_t pos3_t[3]
Definition ufotypes.h:58
#define VectorDist(a, b)
Definition vector.h:69
#define VectorDistSqr(a, b)
Definition vector.h:68