1 /**
2 * @file
3 * @brief Reaction fire code
4 *
5 * Note that in a turn-based game, reaction fire is a design bug in the first place,
6 * causing several logic problems. But we want it and we need it, so we'll have to
7 * work around those problems.
8 *
9 * Imagine the following situations:
10 * One of your soldiers is standing next to a house by a street. There is an alien down
11 * the street with a gun in snap shot mode (8 TUs). The soldier steps out on street, trying
12 * to cross it (and entering the line of sight of that alien). After 4 more paces or spending
13 * 8 TUs, your soldier is shot by the alien. Sounds reasonable? Fine. That's the basic idea
14 * of reaction fire.
15 *
16 * Now assume you have 5 soldiers next to that house. They all step out on the street. Nothing
17 * happens because they all just entered the line of sight of the alien. The first soldier
18 * starts walking and gets shot after 4 paces like above. The second soldier will walk 7 paces
19 * unharmed and get shot after 8 paces and the third soldier will be shot after 12 paces/24 TUs.
20 * This is because when the alien shoots at one target, all his other targets will receive a
21 * bonus equal to the amount of TUs the alien used for his shot. Still sounds reasonable? Fine.
22 *
23 * A slight modification: only one of the 5 soldiers steps out and gets shot after 4 paces.
24 * Then the 2nd steps out and gets shot after 4 paces as well. Likewise the 3rd soldier.
25 * That's because the soldiers hidden behind the house are not among the targets of the alien
26 * and thus don't receive the bonus when the alien shoots,
27 *
28 * There is also a problem at end of turn. Imagine your sniper is set to react with an aimed
29 * shot (18 Tus). An alien steps into his line of sight and fires two snap shots, totaling
30 * 16 TUs. Then the alien decides to do nothing for the rest of his round. You would love to
31 * see your sniper do his aimed shot, right ? But reaction fire rules don't allow that.
32 * On the other hand: if you (were stupid enough to) move one of your soldiers with his last
33 * 2 TUs out from cover and into the sight of an alien on RF with all his TUs still available,
34 * your soldier will receive no RF, due to the same RF rules. You love that, right ?
35 *
36 * Reaction fire is involved in the following situations:
37 * 1. G_ReactionFireOnMovement()
38 * calls G_ReactionFireCheckExecution()
39 * calls G_ReactionFireTryToShoot()
40 * calls G_ReactionFireTargetsUpdateAll()
41 * 2. G_ClientShoot()
42 * calls G_ReactionFirePreShot()
43 * calls G_ReactionFireTargetsUpdateAll()
44 * calls G_ReactionFireTryToShoot()
45 * calls G_ReactionFirePostShot()
46 * calls G_ReactionFireCheckExecution()
47 * calls G_ReactionFireTryToShoot()
48 * 3. G_ClientEndRound()
49 * calls G_ReactionFireOnEndTurn()
50 * calls G_ReactionFireTryToShoot()
51 * calls G_ReactionFireReset()
52 */
53
54 /*
55 Copyright (C) 2002-2013 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
87 /** @brief A single relation between a shooter and his target. */
88 class ReactionFireTarget
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
97 /** @brief A list of relations between a shooter and all his targets. */
98 class ReactionFireTargetList
99 {
100 public:
101 int entnum;
102 int count;
103 ReactionFireTarget targets[MAX_RF_TARGETS];
104
ReactionFireTargetList()105 ReactionFireTargetList () {
106 init();
107 }
108
init(void)109 inline void init (void) {
110 entnum = RF_NO_ENTNUM;
111 count = 0;
112 }
113
reset(void)114 inline void reset (void) {
115 count = 0;
116 }
117 };
118
119 /** @brief A table with all the relations between all shooters and all their targets. */
120 class ReactionFireTargets
121 {
122 public:
123 void init();
124 void add(const Edict* shooter, const Edict* target, const int tusForShot);
125 void remove(const Edict* shooter, const Edict* target);
126 bool hasExpired(const Edict* shooter, const Edict* target, const int tusTarget);
127 int getTriggerTUs(const Edict* shooter, const Edict* target);
128 void advance(const Edict* shooter, const int tusShot);
129 void reset();
130 void notifyClientMove(const Edict* target, int step, bool startMove);
131 void notifyClientOnStep(const Edict* target, int step);
132 void create(const Edict* shooter);
133 void resetTargetList(const Edict* shooter);
134 void notifyClientOnShot(const Edict* target, int step);
135
136 private:
137 ReactionFireTargetList rfData[MAX_RF_DATA];
138 ReactionFireTargetList* find (const Edict* shooter);
139 };
140
141 static ReactionFireTargets rft;
142
143 /**
144 * @brief Initialize the reaction fire table for all entities.
145 */
init(void)146 void ReactionFireTargets::init (void)
147 {
148 for (int i = 0; i < MAX_RF_DATA; i++) {
149 rfData[i].init();
150 }
151 }
152
153 /**
154 * @brief Reset the target count in the reaction fire table for all entities.
155 */
reset(void)156 void ReactionFireTargets::reset (void)
157 {
158 for (int i = 0; i < MAX_RF_DATA; i++) {
159 rfData[i].reset();
160 }
161 }
162
notifyClientOnStep(const Edict * target,int step)163 void ReactionFireTargets::notifyClientOnStep (const Edict* target, int step)
164 {
165 for (int i = 0; i < MAX_RF_DATA; i++) {
166 ReactionFireTargetList *rfts = &rfData[i];
167 if (rfts->entnum == RF_NO_ENTNUM)
168 continue;
169 const Edict* shooter = G_EdictsGetByNum(rfts->entnum);
170 for (int j = 0; j < rfts->count; j++) {
171 ReactionFireTarget& t = rfts->targets[j];
172 if (t.target != target)
173 continue;
174 const int tus = std::max(0, target->TU - t.triggerTUs);
175 G_EventReactionFireTargetUpdate(*shooter, *target, tus, step);
176 }
177 }
178 }
179
notifyClientOnShot(const Edict * target,int tusTarget)180 void ReactionFireTargets::notifyClientOnShot (const Edict* target, int tusTarget)
181 {
182 for (int i = 0; i < MAX_RF_DATA; i++) {
183 ReactionFireTargetList* rfts = &rfData[i];
184 if (rfts->entnum == RF_NO_ENTNUM)
185 continue;
186 const Edict* shooter = G_EdictsGetByNum(rfts->entnum);
187 for (int j = 0; j < rfts->count; j++) {
188 ReactionFireTarget& t = rfts->targets[j];
189 if (t.target != target)
190 continue;
191 const int tus = std::max(0, target->TU - tusTarget - t.triggerTUs);
192 G_EventReactionFireTargetUpdate(*shooter, *target, tus, MAX_ROUTE);
193 }
194 }
195 }
196
notifyClientMove(const Edict * target,int step,bool startMove)197 void ReactionFireTargets::notifyClientMove (const Edict* target, int step, bool startMove)
198 {
199 for (int i = 0; i < MAX_RF_DATA; i++) {
200 ReactionFireTargetList *rfts = &rfData[i];
201 if (rfts->entnum == RF_NO_ENTNUM)
202 continue;
203 const Edict* shooter = G_EdictsGetByNum(rfts->entnum);
204 for (int j = 0; j < rfts->count; j++) {
205 if (rfts->targets[j].target != target)
206 continue;
207 if (startMove) {
208 const int tus = target->TU - rfts->targets[j].triggerTUs;
209 G_EventReactionFireAddTarget(*shooter, *target, tus, step);
210 } else {
211 G_EventReactionFireRemoveTarget(*shooter, *target, step);
212 }
213 }
214 }
215 }
216
217 /**
218 * @brief Find the given edict's table of reaction fire targets.
219 * @param[in] shooter The reaction firing actor
220 */
find(const Edict * shooter)221 ReactionFireTargetList* ReactionFireTargets::find (const Edict* shooter)
222 {
223 for (int i = 0; i < MAX_RF_DATA; i++) {
224 ReactionFireTargetList *rfts = &rfData[i];
225 if (rfts->entnum == shooter->number) {
226 return rfts;
227 }
228 }
229 return nullptr;
230 }
231
232
233 /**
234 * @brief Create a table of reaction fire targets for the given edict.
235 * @param[in] shooter The reaction firing actor
236 */
create(const Edict * shooter)237 void ReactionFireTargets::create (const Edict* shooter)
238 {
239 ReactionFireTargetList *rfts = find(shooter);
240
241 if (rfts)
242 gi.Error("Entity already has rfData");
243
244 for (int i = 0; i < MAX_RF_DATA; i++) {
245 ReactionFireTargetList& data = rfData[i];
246 if (data.entnum != RF_NO_ENTNUM)
247 continue;
248 data.entnum = shooter->number;
249 return;
250 }
251
252 gi.Error("Not enough rfData");
253 }
254
255 /**
256 * @brief Add a reaction fire target for the given shooter.
257 * @param[in] shooter The reaction firing actor
258 * @param[in] target The potential reaction fire victim
259 * @param[in] tusForShot The TUs needed for the shot
260 */
add(const Edict * shooter,const Edict * target,const int tusForShot)261 void ReactionFireTargets::add (const Edict* shooter, const Edict* target, const int tusForShot)
262 {
263 int i;
264 ReactionFireTargetList *rfts = find(shooter);
265
266 assert(rfts);
267 assert(target);
268
269 for (i = 0; i < rfts->count; i++) {
270 /* check if shooter already knows that target */
271 if (rfts->targets[i].target == target)
272 return;
273 }
274 if (i >= MAX_RF_TARGETS)
275 return;
276 rfts->targets[i].target = target;
277 rfts->targets[i].triggerTUs = target->TU - tusForShot;
278 rfts->count++;
279 G_EventReactionFireAddTarget(*shooter, *target, tusForShot, target->moveinfo.steps - 1);
280 #if DEBUG_RF
281 if (!(G_IsAlien(shooter) || G_IsCivilian(shooter)))
282 Com_Printf("S%i: added\n", shooter->number);
283 #endif
284 }
285
286 /**
287 * @brief Remove a reaction fire target for the given shooter.
288 * @param[in] shooter The reaction firing actor
289 * @param[in] target The potential reaction fire victim
290 */
remove(const Edict * shooter,const Edict * target)291 void ReactionFireTargets::remove (const Edict* shooter, const Edict* target)
292 {
293 ReactionFireTargetList *rfts = find(shooter);
294
295 assert(rfts);
296 assert(target);
297
298 for (int i = 0; i < rfts->count; i++) {
299 ReactionFireTarget &t = rfts->targets[i];
300 if (t.target != target)
301 continue;
302
303 /* not the last one? */
304 if (i != rfts->count - 1) {
305 t.target = rfts->targets[rfts->count - 1].target;
306 t.triggerTUs = rfts->targets[rfts->count - 1].triggerTUs;
307 }
308 rfts->count--;
309 G_EventReactionFireRemoveTarget(*shooter, *target, target->moveinfo.steps - 1);
310 #if DEBUG_RF
311 if (!(G_IsAlien(shooter) || G_IsCivilian(shooter)))
312 Com_Printf("S%i: removed\n", shooter->number);
313 #endif
314 }
315 }
316
resetTargetList(const Edict * shooter)317 void ReactionFireTargets::resetTargetList (const Edict* shooter)
318 {
319 ReactionFireTargetList* rfts = find(shooter);
320 for (int i = rfts->count - 1; i >= 0; --i)
321 remove(shooter, rfts->targets[i].target);
322
323 rfts->reset();
324 }
325
326 /**
327 * @brief Check if the given shooter is ready to reaction fire at the given target.
328 * @param[in] shooter The reaction firing actor
329 * @param[in] target The potential reaction fire victim
330 * @return The TUs the target will need to reach until the RF shot goes off.
331 */
getTriggerTUs(const Edict * shooter,const Edict * target)332 int ReactionFireTargets::getTriggerTUs (const Edict* shooter, const Edict* target)
333 {
334 const ReactionFireTargetList *rfts = find(shooter);
335
336 if (!rfts)
337 return -2; /* the shooter doesn't aim at anything */
338
339 assert(target);
340
341 for (int i = 0; i < rfts->count; i++) {
342 const ReactionFireTarget &t = rfts->targets[i];
343 if (t.target == target)
344 return t.triggerTUs;
345 }
346
347 return -1; /* the shooter doesn't aim at this target */
348 }
349
350
351 /**
352 * @brief Check if the given shooter is ready to reaction fire at the given target.
353 * @param[in] shooter The reaction firing actor
354 * @param[in] target The potential reaction fire victim
355 * @param[in] tusTarget The TUs the target will need for the shot, 0 for just moving
356 */
hasExpired(const Edict * shooter,const Edict * target,const int tusTarget)357 bool ReactionFireTargets::hasExpired (const Edict* shooter, const Edict* target, const int tusTarget)
358 {
359 const ReactionFireTargetList *rfts = find(shooter);
360
361 if (!rfts)
362 return false; /* the shooter doesn't aim at anything */
363
364 assert(target);
365
366 for (int i = 0; i < rfts->count; i++) {
367 const ReactionFireTarget &t = rfts->targets[i];
368 if (t.target == target)
369 return t.triggerTUs >= target->TU - tusTarget;
370 }
371
372 return false; /* the shooter doesn't aim at this target */
373 }
374
375
376 /**
377 * @brief Increase the triggertime for the next RF shot for all targets of the shooter (after a reaction fire).
378 * @param[in] shooter The reaction firing actor
379 * @param[in] tusShot The TUs the shooter will need for the next shot
380 */
advance(const Edict * shooter,const int tusShot)381 void ReactionFireTargets::advance (const Edict* shooter, const int tusShot)
382 {
383 ReactionFireTargetList *rfts = find(shooter);
384 assert(rfts);
385
386 for (int i = 0; i < rfts->count; i++) {
387 ReactionFireTarget &t = rfts->targets[i];
388 t.triggerTUs -= tusShot;
389 }
390 }
391
392 /**
393 * @brief free function to initialize the reaction fire table for all entities.
394 */
G_ReactionFireTargetsInit(void)395 void G_ReactionFireTargetsInit (void)
396 {
397 rft.init();
398 }
399
400 /**
401 * @brief free function to create a table of reaction fire targets for the given edict.
402 * @param[in] shooter The reaction firing actor
403 */
G_ReactionFireTargetsCreate(const Edict * shooter)404 void G_ReactionFireTargetsCreate (const Edict* shooter)
405 {
406 rft.create(shooter);
407 }
408
409 class ReactionFire
410 {
411 private:
412 bool isEnemy(const Edict* shooter, const Edict* target) const;
413 bool canReact(Edict* shooter, const Edict* target) const;
414 bool canSee(const Edict* shooter, const Edict* target) const;
415 bool shoot(Edict* shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode);
416 bool isPossible(Edict* shooter, const Edict* target) const;
417 public:
418 void notifyClientOnStep(const Edict* target, int step);
419 bool checkExecution(const Edict* target);
420 void updateAllTargets(const Edict* target);
421 bool tryToShoot(Edict* shooter, const Edict* target);
422 bool isInWeaponRange(const Edict* shooter, const Edict* target, const fireDef_t* fd) const;
423 const fireDef_t* getFireDef(const Edict* shooter) const;
424 void resetTargets(const Edict* shooter);
425 void notifyClientOnShot(const Edict* target, int tusTarget);
426 };
427 static ReactionFire rf;
428
429 /**
430 * @brief Get the fireDef for the RF settings of the shooter.
431 * @param[in] shooter The reaction firing actor
432 * @return nullptr if something is wrong
433 */
getFireDef(const Edict * shooter) const434 const fireDef_t* ReactionFire::getFireDef (const Edict* shooter) const
435 {
436 const FiremodeSettings *fmSetting = &shooter->chr.RFmode;
437 if (!fmSetting->isSaneFiremode())
438 return nullptr;
439
440 const Item* weapon = shooter->getHandItem(fmSetting->getHand());
441
442 if (weapon && weapon->ammoDef() && weapon->isWeapon() && !weapon->mustReload()) {
443 const fireDef_t* fdArray = weapon->getFiredefs();
444 if (fdArray == nullptr)
445 return nullptr;
446
447 const fireDefIndex_t fmIdx = fmSetting->getFmIdx();
448 return &fdArray[fmIdx];
449 }
450 return nullptr;
451 }
452
isInWeaponRange(const Edict * shooter,const Edict * target,const fireDef_t * fd) const453 bool ReactionFire::isInWeaponRange (const Edict* shooter, const Edict* target, const fireDef_t* fd) const
454 {
455 assert(fd);
456 return fd->range >= VectorDist(shooter->origin, target->origin);
457 }
458
459 /**
460 * @brief Get the weapon firing TUs of the item in the hand of the shooter.
461 * @return -1 if no firedef was found for the item or the reaction fire mode is not activated.
462 * @param[in] shooter The reaction firing actor
463 * @param[in] target The target to check reaction fire for (e.g. check whether the weapon that was marked for
464 * using in reaction fire situations can handle the distance between the shooter and the target)
465 */
G_ReactionFireGetTUsForItem(const Edict * shooter,const Edict * target)466 static int G_ReactionFireGetTUsForItem (const Edict* shooter, const Edict* target)
467 {
468 const fireDef_t* fd = rf.getFireDef(shooter);
469 if (!fd)
470 return -1;
471
472 const int tus = G_ActorGetModifiedTimeForFiredef(shooter, fd, true);
473
474 if (tus <= shooter->TU && rf.isInWeaponRange(shooter, target, fd)) {
475 return tus;
476 }
477
478 return -1;
479 }
480
481 /**
482 * @brief Checks if the currently selected firemode is usable with the defined weapon.
483 * @param[in] actor The actor to check the firemode for.
484 */
G_ActorHasWorkingFireModeSet(const Edict * actor)485 static bool G_ActorHasWorkingFireModeSet (const Edict* actor)
486 {
487 const FiremodeSettings *fmSettings = &actor->chr.RFmode;
488 if (!fmSettings->isSaneFiremode()) /* just checks for valid values */
489 return false;
490
491 const Item* weapon = actor->getHandItem(fmSettings->getHand());
492 if (!weapon)
493 return false;
494 const fireDef_t* fd = weapon->getFiredefs();
495 if (fd == nullptr)
496 return false;
497
498 if (fd->obj->weapons[fd->weapFdsIdx] == fmSettings->getWeapon()
499 && fmSettings->getFmIdx() < fd->obj->numFiredefs[fd->weapFdsIdx]) {
500 return true;
501 }
502
503 return false;
504 }
505
506 /**
507 * @brief Updates the reaction fire settings in case something was moved into a hand or from a hand
508 * that would make the current settings invalid
509 * @param[in,out] ent The actor edict to check the settings for
510 * @param[in] fmIdx The fire mode index that should be used for reaction fire
511 * @param[in] hand The hand that should be used for reaction fire
512 * @param[in] od The object/weapon for the reaction fire
513 */
G_ReactionFireSettingsUpdate(Edict * ent,fireDefIndex_t fmIdx,actorHands_t hand,const objDef_t * od)514 void G_ReactionFireSettingsUpdate (Edict* ent, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t* od)
515 {
516 ent->chr.RFmode.set(hand, fmIdx, od); /* FiremodeSettings */
517
518 if (!G_ActorHasWorkingFireModeSet(ent)) {
519 /* Disable reaction fire if no valid firemode was found. */
520 G_ClientStateChange(ent->getPlayer(), ent, ~STATE_REACTION, true);
521 return;
522 }
523
524 G_EventReactionFireChange(*ent);
525
526 /* If reaction fire is active, update the reserved TUs */
527 if (G_IsReaction(ent)) {
528 G_ReactionFireSettingsReserveTUs(ent);
529 }
530 }
531
532 /**
533 * @brief Checks whether an actor has enough TUs left to activate reaction fire.
534 * @param[in] ent The actors edict to check for TUs for
535 * @return @c true if the given actor has enough TUs left to activate reaction fire, @c false otherwise.
536 */
G_ActorHasEnoughTUsReactionFire(const Edict * ent)537 static bool G_ActorHasEnoughTUsReactionFire (const Edict* ent)
538 {
539 const int TUs = G_ActorGetTUForReactionFire(ent);
540 const chrReservations_t* res = &ent->chr.reservedTus;
541 return ent->TU - TUs >= res->shot + res->crouch;
542 }
543
544 /**
545 * @param ent The actor to set the reaction fire for
546 * @return @c true if the needed settings could have been made or settings are
547 * already valid, @c false otherwise.
548 */
G_ReactionFireSettingsSetDefault(Edict * ent)549 static bool G_ReactionFireSettingsSetDefault (Edict* ent)
550 {
551 if (G_ActorHasWorkingFireModeSet(ent))
552 return true;
553
554 actorHands_t hand = ACTOR_HAND_RIGHT;
555 const Item* item = ent->getHandItem(hand);
556 if (!item) {
557 hand = ACTOR_HAND_LEFT;
558 item = ent->getHandItem(hand);
559 }
560
561 if (!item)
562 return false;
563
564 const objDef_t* weapon = item->getReactionFireWeaponType();
565 if (!weapon)
566 return false;
567
568 ent->chr.RFmode.set(hand, 0, weapon); /* no special firemode */
569
570 if (!G_ActorHasWorkingFireModeSet(ent))
571 return false;
572
573 if (!G_IsAI(ent))
574 G_EventReactionFireChange(*ent);
575
576 return true;
577 }
578
579 /**
580 * @brief Checks whether the actor is allowed to activate reaction fire and will informs the player about
581 * the reason if this would not work.
582 * @param[in] ent The actor to check
583 * @return @c true if the actor is allowed to activate it, @c false otherwise
584 */
G_ReactionFireCanBeEnabled(const Edict * ent)585 static bool G_ReactionFireCanBeEnabled (const Edict* ent)
586 {
587 /* check ent is a suitable shooter */
588 if (!ent->inuse || !G_IsLivingActor(ent))
589 return false;
590
591 if (G_MatchIsRunning() && ent->team != level.activeTeam)
592 return false;
593
594 /* actor may not carry weapons at all - so no further checking is needed */
595 if (!ent->chr.teamDef->weapons)
596 return false;
597
598 if (!ent->chr.inv.holdsReactionFireWeapon()) {
599 G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("No reaction fire enabled weapon."));
600 return false;
601 }
602
603 if (!G_ActorHasWorkingFireModeSet(ent)) {
604 G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("No fire mode selected for reaction fire."));
605 return false;
606 }
607
608 if (!G_ActorHasEnoughTUsReactionFire(ent)) {
609 G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("Not enough TUs left for activating reaction fire."));
610 return false;
611 }
612
613 return true;
614 }
615
616 /**
617 * @brief Set the reaction fire TU reservation for an actor
618 * @param[in,out] ent The actor edict to set the TUs for
619 * @return @c true if TUs for reaction fire were reserved, @c false if the reservation was set
620 * back to @c 0
621 */
G_ReactionFireSettingsReserveTUs(Edict * ent)622 bool G_ReactionFireSettingsReserveTUs (Edict* ent)
623 {
624 if (G_ReactionFireSettingsSetDefault(ent) && G_ReactionFireCanBeEnabled(ent)) {
625 const int TUs = G_ActorGetTUForReactionFire(ent);
626 /* Enable requested reaction fire. */
627 G_ActorReserveTUs(ent, TUs, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
628 return true;
629 }
630
631 G_ActorReserveTUs(ent, 0, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
632 return false;
633 }
634
isPossible(Edict * shooter,const Edict * target) const635 inline bool ReactionFire::isPossible (Edict* shooter, const Edict* target) const
636 {
637 return isEnemy(shooter, target) && canReact(shooter, target) && canSee(shooter, target);
638 }
639
640 /**
641 * @brief Check whether we want to shoot at the target.
642 * @param[in] shooter The entity that might be firing
643 * @param[in] target The entity that might be fired at
644 */
isEnemy(const Edict * shooter,const Edict * target) const645 bool ReactionFire::isEnemy (const Edict* shooter, const Edict* target) const
646 {
647 /* an entity can't reaction fire at itself */
648 if (shooter == target)
649 return false;
650
651 /* Don't react in your own turn */
652 if (shooter->team == level.activeTeam)
653 return false;
654
655 if (G_IsDead(target))
656 return false;
657
658 /* If reaction fire is triggered by a friendly unit
659 * and the shooter is still sane, don't shoot;
660 * well, if the shooter isn't sane anymore... */
661 if (G_IsCivilian(target) || target->team == shooter->team)
662 if (!G_IsShaken(shooter) || (float) shooter->morale / mor_shaken->value > frand())
663 return false;
664
665 return true;
666 }
667
668 /**
669 * @brief Check whether shooter can reaction fire at target at all.
670 * @param[in] shooter The entity that might be firing
671 * @param[in] target The entity that might be fired at
672 */
canReact(Edict * shooter,const Edict * target) const673 bool ReactionFire::canReact (Edict* shooter, const Edict* target) const
674 {
675 /* shooter can't use RF if is in STATE_DAZED (flashbang impact) */
676 if (G_IsDazed(shooter))
677 return false;
678
679 /* check shooter has reaction fire enabled */
680 if (!G_IsReaction(shooter))
681 return false;
682
683 /* check shooter has weapon in RF hand */
684 if (!shooter->getHandItem(shooter->chr.RFmode.getHand())) {
685 /* print character info if this happens, for now */
686 gi.DPrintf("Reaction fire enabled but no weapon for hand (name=%s,entnum=%i,hand=%i,fmIdx=%i)\n",
687 shooter->chr.name, shooter->number, shooter->chr.RFmode.getHand(), shooter->chr.RFmode.getFmIdx());
688 G_RemoveReaction(shooter);
689 return false;
690 }
691 return true;
692 }
693
694 /**
695 * @brief Check whether shooter can see his target well enough
696 * @param[in] shooter The entity that might be firing
697 * @param[in] target The entity that might be fired at
698 */
canSee(const Edict * shooter,const Edict * target) const699 bool ReactionFire::canSee (const Edict* shooter, const Edict* target) const
700 {
701 if (!G_IsVisibleForTeam(target, shooter->team))
702 return false;
703
704 /* check in range and visible */
705 const int spotDist = G_VisCheckDist(shooter);
706 if (VectorDistSqr(shooter->origin, target->origin) > spotDist * spotDist)
707 return false;
708
709 const bool frustum = G_FrustumVis(shooter, target->origin);
710 if (!frustum)
711 return false;
712
713 const float actorVis = G_ActorVis(shooter->origin, shooter, target, true);
714 if (actorVis < 0.1)
715 return false;
716
717 return true;
718 }
719
720 /**
721 * @brief Check whether 'target' has just triggered any new reaction fire
722 * @param[in] target The entity triggering fire
723 */
updateAllTargets(const Edict * target)724 void ReactionFire::updateAllTargets (const Edict* target)
725 {
726 Edict* shooter = nullptr;
727
728 /* check all possible shooters */
729 while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
730 /* check whether reaction fire is possible (friend/foe, LoS) */
731 if (isPossible(shooter, target)) {
732 const int TUs = G_ReactionFireGetTUsForItem(shooter, target);
733 if (TUs < 0)
734 continue; /* no suitable weapon */
735 rft.add(shooter, target, TUs);
736 } else {
737 rft.remove(shooter, target);
738 }
739 }
740 }
741
resetTargets(const Edict * shooter)742 void ReactionFire::resetTargets (const Edict *shooter)
743 {
744 rft.resetTargetList(shooter);
745 }
746
747 /**
748 * @brief Perform the reaction fire shot
749 * @param[in] shooter The actor that is trying to shoot
750 * @param[in] at Position to fire on.
751 * @param[in] type What type of shot this is (left, right reaction-left etc...).
752 * @param[in] firemode The firemode index of the ammo for the used weapon (objDef.fd[][x]) .
753 * @return true if everything went ok (i.e. the shot(s) where fired ok), otherwise false.
754 * @sa G_ClientShoot
755 */
shoot(Edict * shooter,const pos3_t at,shoot_types_t type,fireDefIndex_t firemode)756 bool ReactionFire::shoot (Edict* shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode)
757 {
758 const int minhit = 30;
759 shot_mock_t mock;
760 int i;
761 const Player &player = shooter->getPlayer();
762 /* this is the max amount of friendly units that were hit during the mock calculation */
763 int maxff;
764
765 if (G_IsInsane(shooter))
766 maxff = 100;
767 else if (G_IsRaged(shooter))
768 maxff = 60;
769 else if (G_IsPanicked(shooter))
770 maxff = 30;
771 else if (G_IsShaken(shooter))
772 maxff = 15;
773 else
774 maxff = 5;
775
776 /* calculate the mock values - e.g. how many friendly units we would hit
777 * when opening the reaction fire */
778 for (i = 0; i < 100; i++)
779 if (!G_ClientShoot(player, shooter, at, type, firemode, &mock, false, 0))
780 break;
781
782 const int ff = mock.friendCount + (G_IsAlien(shooter) ? 0 : mock.civilian);
783 if (ff <= maxff && mock.enemyCount >= minhit)
784 return G_ClientShoot(player, shooter, at, type, firemode, nullptr, false, 0);
785
786 return false;
787 }
788
789 /**
790 * @brief Resolve the reaction fire for an entity, this checks that the entity can fire and then takes the shot
791 * @param[in] shooter The entity using reaction fire
792 * @param[in] target The victim of the reaction fire
793 * @return true if the entity fired, false otherwise
794 */
tryToShoot(Edict * shooter,const Edict * target)795 bool ReactionFire::tryToShoot (Edict* shooter, const Edict* target)
796 {
797 /* check for valid target */
798 assert(target);
799
800 /* shooter can't take a reaction shot if it's not possible - and check that
801 * the target is still alive */
802 if (!isPossible(shooter, target)) {
803 rft.remove(shooter, target);
804 return false;
805 }
806
807 /* take the shot */
808 const bool tookShot = rf.shoot(shooter, target->pos, ST_RIGHT_REACTION, shooter->chr.RFmode.getFmIdx());
809
810 if (tookShot) {
811 /* clear any shakenness */
812 G_RemoveShaken(shooter);
813 }
814
815 return tookShot;
816 }
817
notifyClientOnShot(const Edict * target,int tusTarget)818 void ReactionFire::notifyClientOnShot (const Edict* target, int tusTarget)
819 {
820 rft.notifyClientOnShot(target, tusTarget);
821 }
822
notifyClientOnStep(const Edict * target,int step)823 void ReactionFire::notifyClientOnStep (const Edict* target, int step)
824 {
825 rft.notifyClientOnStep(target, step);
826 }
827
828 /**
829 * @brief Check all entities to see whether target has caused reaction fire to resolve.
830 * @param[in] target The entity that might be resolving reaction fire
831 * @returns whether any entity fired (or would fire) upon target
832 * @sa G_ReactionFireOnMovement
833 * @sa G_ReactionFirePostShot
834 */
checkExecution(const Edict * target)835 bool ReactionFire::checkExecution (const Edict* target)
836 {
837 Edict* shooter = nullptr;
838 bool fired = false;
839
840 /* check all possible shooters */
841 while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
842 const int tus = G_ReactionFireGetTUsForItem(shooter, target);
843 /* indicates an RF weapon is there */
844 if (tus <= 1)
845 continue;
846 if (!rft.hasExpired(shooter, target, 0))
847 continue;
848 if (!rf.tryToShoot(shooter, target))
849 continue;
850 rft.advance(shooter, tus);
851 fired |= true;
852 }
853 return fired;
854 }
855
856 #if DEBUG_RF
857 /**
858 * @brief Prints some reaction fire data to the console
859 * @param[in] target The target entity
860 */
G_ReactionFirePrintSituation(Edict * target)861 static void G_ReactionFirePrintSituation (Edict* target)
862 {
863 if (!G_IsAlien(target))
864 return;
865
866 Com_Printf("Alien %i at %i/%i/%i TU:%i\n", target->number, target->pos[0], target->pos[1], target->pos[2], target->TU);
867
868 Edict* shooter = nullptr;
869 /* check all possible shooters */
870 while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
871 if (G_IsAlien(shooter) || G_IsCivilian(shooter))
872 continue;
873 char msgHdr[100];
874 Com_sprintf(msgHdr, sizeof(msgHdr), "S%i: at %i/%i/%i RF: ", shooter->number, shooter->pos[0], shooter->pos[1], shooter->pos[2]);
875 int ttus = rft.getTriggerTUs(shooter, target);
876 if (ttus == -2)
877 Com_Printf("%s not initialized\n", msgHdr);
878 if (ttus == -1)
879 Com_Printf("%s not aiming\n", msgHdr);
880 else if (rft.hasExpired(shooter, target, 0))
881 Com_Printf("expired\n", msgHdr);
882 else
883 Com_Printf("%s not yet: %i\n", msgHdr, ttus);
884 }
885 }
886 #endif
887
888 /**
889 * @brief Called when 'target' moves, possibly triggering or resolving reaction fire
890 * @param[in] target The target entity
891 * @return true If any shots were (or would be) taken
892 * @sa G_ClientMove
893 */
G_ReactionFireOnMovement(Edict * target,int step)894 bool G_ReactionFireOnMovement (Edict* target, int step)
895 {
896 #if DEBUG_RF
897 G_ReactionFirePrintSituation(target);
898 #endif
899 rf.notifyClientOnStep(target, step);
900
901 /* Check to see whether this resolves any reaction fire */
902 const bool fired = rf.checkExecution(target);
903
904 /* Check to see whether this triggers any reaction fire */
905 rf.updateAllTargets(target);
906
907 return fired;
908 }
909
G_ReactionFireNofityClientStartShot(const Edict * target)910 static void G_ReactionFireNofityClientStartShot (const Edict* target)
911 {
912 rft.notifyClientMove(target, MAX_ROUTE, true);
913 }
914
G_ReactionFireNofityClientEndShot(const Edict * target)915 static void G_ReactionFireNofityClientEndShot (const Edict* target)
916 {
917 rft.notifyClientMove(target, MAX_ROUTE, false);
918 }
919
920 /**
921 * @brief Called when 'target' is about to shoot, this forces a 'draw' to decide who gets the first shot
922 * @param[in] target The entity about to shoot
923 * @param[in] fdTime The TU of the shot
924 * @sa G_ClientShoot
925 */
G_ReactionFirePreShot(const Edict * target,const int fdTime)926 void G_ReactionFirePreShot (const Edict* target, const int fdTime)
927 {
928 bool repeat = true;
929
930 /* Check to see whether this triggers any reaction fire */
931 G_ReactionFireNofityClientStartShot(target);
932 rf.updateAllTargets(target);
933 rf.notifyClientOnShot(target, fdTime);
934
935 /* if any reaction fire occurs, we have to loop through all entities again to allow
936 * multiple (fast) RF snap shots before a (slow) aimed shot from the target occurs. */
937 while (repeat) {
938 Edict* shooter = nullptr;
939 repeat = false;
940 /* check all ents to see who wins and who loses a draw */
941 while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
942 const int entTUs = G_ReactionFireGetTUsForItem(shooter, target);
943 /* indicates an RF weapon is there */
944 if (entTUs <= 1)
945 continue;
946 if (!rft.hasExpired(shooter, target, fdTime))
947 continue;
948 if (!rf.tryToShoot(shooter, target))
949 continue;
950 repeat = true;
951 rft.advance(shooter, fdTime);
952 }
953 }
954 }
955
956 /**
957 * @brief Removes the given target from the reaction fire lists
958 * @param[in] target The target to remove from the lists
959 */
G_ReactionFireOnDead(const Edict * target)960 void G_ReactionFireOnDead (const Edict* target)
961 {
962 assert(G_IsDead(target));
963 rf.updateAllTargets(target);
964 rf.resetTargets(target);
965 }
966
967 /**
968 * @brief Called after 'target' has fired, this might trigger more reaction fire or resolve outstanding reaction fire (because target is out of time)
969 * @param[in] target The entity that has just fired
970 * @sa G_ClientShoot
971 */
G_ReactionFirePostShot(Edict * target)972 void G_ReactionFirePostShot (Edict* target)
973 {
974 /* Check to see whether this resolves any reaction fire */
975 rf.notifyClientOnShot(target, 0);
976 rf.checkExecution(target);
977 G_ReactionFireNofityClientEndShot(target);
978 }
979
980 /**
981 * @brief Called at the end of turn, all outstanding reaction fire is resolved
982 * @sa G_ClientEndRound
983 */
G_ReactionFireOnEndTurn(void)984 void G_ReactionFireOnEndTurn (void)
985 {
986 /* we explicitly do nothing at end of turn, just reset the table */
987 rft.reset();
988 }
989
990 /**
991 * @brief Guess! Reset all "shaken" states on end of turn?
992 * @note Normally called on end of turn.
993 * @sa G_ClientStateChange
994 * @param[in] team Index of team to loop through.
995 */
G_ReactionFireReset(int team)996 void G_ReactionFireReset (int team)
997 {
998 Edict* ent = nullptr;
999
1000 while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team))) {
1001 G_RemoveShaken(ent);
1002 }
1003 }
1004
G_ReactionFireNofityClientStartMove(const Edict * target)1005 void G_ReactionFireNofityClientStartMove (const Edict* target)
1006 {
1007 /* note that this is sent _before_ the actual move event, so we can't use the step number */
1008 rft.notifyClientMove(target, MAX_ROUTE, true);
1009 }
1010
G_ReactionFireNofityClientEndMove(const Edict * target)1011 void G_ReactionFireNofityClientEndMove (const Edict* target)
1012 {
1013 rft.notifyClientMove(target, target->moveinfo.steps - 1, false);
1014 }
1015