/** * @brief Called when 'target' is about to shoot, this forces a 'draw' to decide who gets the first shot * @param[in] target The entity about to shoot * @param[in] fdTime The TU of the shot * @sa G_ClientShoot */ void G_ReactionFirePreShot (const Edict *target, const int fdTime) { bool repeat = true; /* Check to see whether this triggers any reaction fire */ G_ReactionFireTargetsUpdateAll(target); /* if any reaction fire occurs, we have to loop through all entities again to allow * multiple (fast) RF snap shots before a (slow) aimed shot from the target occurs. */ while (repeat) { Edict *shooter = nullptr; repeat = false; /* check all ents to see who wins and who loses a draw */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { const int entTUs = G_ReactionFireGetTUsForItem(shooter, target); if (entTUs > 1) { /* indicates an RF weapon is there */ if (rft.hasExpired(shooter, target, fdTime)) { if (G_ReactionFireTryToShoot(shooter, target)) { repeat = true; rft.advance(shooter, fdTime); } } } } } }
/** * @brief Prints some reaction fire data to the console * @param[in] target The target entity */ static void G_ReactionFirePrintSituation (Edict* target) { if (!G_IsAlien(target)) return; Com_Printf("Alien %i at %i/%i/%i TU:%i\n", target->number, target->pos[0], target->pos[1], target->pos[2], target->TU); Actor* shooter = nullptr; /* check all possible shooters */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { if (G_IsAlien(shooter) || G_IsCivilian(shooter)) continue; char msgHdr[100]; Com_sprintf(msgHdr, sizeof(msgHdr), "S%i: at %i/%i/%i RF: ", shooter->number, shooter->pos[0], shooter->pos[1], shooter->pos[2]); int ttus = rft.getTriggerTUs(shooter, target); if (ttus == -2) Com_Printf("%s not initialized\n", msgHdr); if (ttus == -1) Com_Printf("%s not aiming\n", msgHdr); else if (rft.hasExpired(shooter, target, 0)) Com_Printf("expired\n", msgHdr); else Com_Printf("%s not yet: %i\n", msgHdr, ttus); } }
/** * @brief Called when 'target' is about to shoot, this forces a 'draw' to decide who gets the first shot * @param[in] target The entity about to shoot * @param[in] fdTime The TU of the shot * @sa G_ClientShoot */ void G_ReactionFirePreShot (const Actor* target, const int fdTime) { bool repeat = true; /* Check to see whether this triggers any reaction fire */ G_ReactionFireNotifyClientStartShot(target); rf.updateAllTargets(target); rf.notifyClientOnShot(target, fdTime); /* if any reaction fire occurs, we have to loop through all entities again to allow * multiple (fast) RF snap shots before a (slow) aimed shot from the target occurs. */ while (repeat) { Actor* shooter = nullptr; repeat = false; /* check all ents to see who wins and who loses a draw */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { const int entTUs = G_ReactionFireGetTUsForItem(shooter, target); /* indicates an RF weapon is there */ if (entTUs <= 1) continue; if (!rft.hasExpired(shooter, target, fdTime)) continue; if (!rf.tryToShoot(shooter, target)) { G_ReactionFireNotifyClientRFAborted(shooter, target, MAX_ROUTE); continue; } repeat = true; rft.advance(shooter, fdTime); } } }
/** * @brief Resolve the reaction fire for an entity, this checks that the entity can fire and then takes the shot * @param[in] shooter The entity using reaction fire * @param[in] target The victim of the reaction fire * @return true if the entity fired, false otherwise */ bool ReactionFire::tryToShoot (Actor* shooter, const Edict* target) { /* check for valid target */ assert(target); /* shooter can't take a reaction shot if it's not possible - and check that * the target is still alive */ if (!isPossible(shooter, target)) { rft.remove(shooter, target); return false; } /* take the shot */ const actorHands_t hand = shooter->chr.RFmode.getHand(); const shoot_types_t type = (hand == ACTOR_HAND_RIGHT ? ST_RIGHT_REACTION : (hand == ACTOR_HAND_LEFT ? ST_LEFT_REACTION : ST_NUM_SHOOT_TYPES)); const bool tookShot = rf.shoot(shooter, target->pos, type, shooter->chr.RFmode.getFmIdx()); if (tookShot) { /* clear any shakenness */ shooter->removeShaken(); } return tookShot; }
/** * @brief Check whether 'target' has just triggered any new reaction fire * @param[in] target The entity triggering fire */ static void G_ReactionFireTargetsUpdateAll (const edict_t *target) { edict_t *shooter = NULL; /* check all possible shooters */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { /* check whether reaction fire is possible (friend/foe, LoS */ if (G_ReactionFireIsPossible(shooter, target)) { const int TUs = G_ReactionFireGetTUsForItem(shooter, target, RIGHT(shooter)); if (TUs < 0) continue; /* no suitable weapon */ rft.add(shooter, target, TUs); } else { rft.remove(shooter, target); } } }
/** * @brief Check whether 'target' has just triggered any new reaction fire * @param[in] target The entity triggering fire */ void ReactionFire::updateAllTargets (const Edict* target) { Actor* shooter = nullptr; /* check all possible shooters */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { /* check whether reaction fire is possible (friend/foe, LoS) */ if (isPossible(shooter, target)) { const int TUs = G_ReactionFireGetTUsForItem(shooter, target); if (TUs < 0) continue; /* no suitable weapon */ rft.add(shooter, target, TUs); } else { rft.remove(shooter, target); } } }
/** * @brief Check all entities to see whether target has caused reaction fire to resolve. * @param[in] target The entity that might be resolving reaction fire * @returns whether any entity fired (or would fire) upon target * @sa G_ReactionFireOnMovement * @sa G_ReactionFirePostShot */ static bool G_ReactionFireCheckExecution (const Edict *target) { Edict *shooter = nullptr; bool fired = false; /* check all possible shooters */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { const int tus = G_ReactionFireGetTUsForItem(shooter, target); if (tus > 1) { /* indicates an RF weapon is there */ if (rft.hasExpired(shooter, target, 0)) { if (G_ReactionFireTryToShoot(shooter, target)) { rft.advance(shooter, tus); fired |= true; } } } } return fired; }
/** * @brief Check all entities to see whether target has caused reaction fire to resolve. * @param[in] target The entity that might be resolving reaction fire * @param[in] step The number of the step in the move we are checking reactions for * @returns whether any entity fired (or would fire) upon target * @sa G_ReactionFireOnMovement * @sa G_ReactionFirePostShot */ bool ReactionFire::checkExecution (const Edict* target, int step) { Actor* shooter = nullptr; bool fired = false; /* check all possible shooters */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { const int tus = G_ReactionFireGetTUsForItem(shooter, target); /* indicates an RF weapon is there */ if (tus <= 1) continue; if (!rft.hasExpired(shooter, target, 0)) continue; if (!rf.tryToShoot(shooter, target)) { G_ReactionFireNotifyClientRFAborted(shooter, target, step); continue; } rft.advance(shooter, tus); fired |= true; } return fired; }
/** * @brief Resolve the reaction fire for an entity, this checks that the entity can fire and then takes the shot * @param[in] shooter The entity using reaction fire * @param[in] target The victim of the reaction fire * @return true if the entity fired, false otherwise */ bool ReactionFire::tryToShoot (Actor* shooter, const Edict* target) { /* check for valid target */ assert(target); /* shooter can't take a reaction shot if it's not possible - and check that * the target is still alive */ if (!isPossible(shooter, target)) { rft.remove(shooter, target); return false; } /* take the shot */ const bool tookShot = rf.shoot(shooter, target->pos, ST_RIGHT_REACTION, shooter->chr.RFmode.getFmIdx()); if (tookShot) { /* clear any shakenness */ shooter->removeShaken(); } return tookShot; }
/** * @brief Resolve the reaction fire for an entity, this checks that the entity can fire and then takes the shot * @param[in] shooter The entity to resolve reaction fire for * @param[in] target The victim of the reaction fire * @return true if the entity fired (or would have fired if mock), false otherwise */ static bool G_ReactionFireTryToShoot (edict_t *shooter, const edict_t *target) { /* check for valid target */ assert(target); /* shooter can't take a reaction shot if it's not possible - and check that * the target is still alive */ if (!G_ReactionFireIsPossible(shooter, target)) { rft.remove(shooter, target); return false; } /* take the shot */ const bool tookShot = G_ReactionFireShoot(shooter, target->pos, ST_RIGHT_REACTION, shooter->chr.RFmode.fmIdx); if (tookShot) { /* clear any shakenness */ G_RemoveShaken(shooter); } return tookShot; }
static void G_ReactionFireNotifyClientStartShot (const Edict* target) { rft.notifyClientMove(target, MAX_ROUTE, true); }
/** * @brief free function to destroy the table of reaction fire targets for the given edict. * @param[in] shooter The reaction firing actor */ void G_ReactionFireTargetsDestroy (const Edict* shooter) { rft.destroy(shooter); }
void ReactionFire::notifyClientOnStep (const Edict* target, int step) { rft.notifyClientOnStep(target, step); }
void ReactionFire::notifyClientOnShot (const Edict* target, int tusTarget) { rft.notifyClientOnShot(target, tusTarget); }
/** * @brief Called at the end of turn, all outstanding reaction fire is resolved * @sa G_ClientEndRound */ void G_ReactionFireOnEndTurn (void) { /* we explicitly do nothing at end of turn, just reset the table */ rft.reset(); }
/** * @brief free function to initialize the reaction fire table for all entities. */ void G_ReactionFireTargetsInit (void) { rft.init(); }
void G_ReactionFireNotifyClientRFAborted (const Actor* shooter, const Edict* target, int step) { rft.notifyClientRFAborted(shooter, target, step); }
void G_ReactionFireNotifyClientEndMove (const Actor* target) { rft.notifyClientMove(target, target->moveinfo.steps - 1, false); }
void G_ReactionFireNotifyClientStartMove (const Actor* target) { /* note that this is sent _before_ the actual move event, so we can't use the step number */ rft.notifyClientMove(target, MAX_ROUTE, true); }
static void G_ReactionFireNotifyClientEndShot (const Edict* target) { rft.notifyClientMove(target, MAX_ROUTE, false); }
/** * @brief free function to create a table of reaction fire targets for the given edict. * @param[in] shooter The reaction firing actor */ void G_ReactionFireTargetsCreate (const Edict* shooter) { rft.create(shooter); }
void ReactionFire::resetTargets (const Edict* shooter) { rft.resetTargetList(shooter); }