/** * @brief Called after 'target' has fired, this might trigger more reaction fire or resolve outstanding reaction fire (because target is out of time) * @param[in] target The entity that has just fired * @sa G_ClientShoot */ void G_ReactionFirePostShot (Actor* target) { /* Check to see whether this resolves any reaction fire */ rf.notifyClientOnShot(target, 0); rf.checkExecution(target, MAX_ROUTE); G_ReactionFireNotifyClientEndShot(target); }
/** * @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 Get the weapon firing TUs of the item in the hand of the shooter. * @return -1 if no firedef was found for the item or the reaction fire mode is not activated. * @param[in] shooter The reaction firing actor * @param[in] target The target to check reaction fire for (e.g. check whether the weapon that was marked for * using in reaction fire situations can handle the distance between the shooter and the target) */ static int G_ReactionFireGetTUsForItem (const Actor* shooter, const Edict* target) { const fireDef_t* fd = rf.getFireDef(shooter); if (!fd) return -1; const int tus = G_ActorGetModifiedTimeForFiredef(shooter, fd, true); if (tus <= shooter->TU && rf.isInWeaponRange(shooter, target, fd)) { return tus; } return -1; }
/** * @brief Called when 'target' moves, possibly triggering or resolving reaction fire * @param[in] target The target entity * @param[in] step The number of the step in the move we are checking reactions for * @return true If any shots were (or would be) taken * @sa G_ClientMove */ bool G_ReactionFireOnMovement (Actor* target, int step) { #if DEBUG_RF G_ReactionFirePrintSituation(target); #endif rf.notifyClientOnStep(target, step); /* Check to see whether this resolves any reaction fire */ const bool fired = rf.checkExecution(target, step); /* Check to see whether this triggers any reaction fire */ rf.updateAllTargets(target); 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 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 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 Removes the given target from the reaction fire lists * @param[in] target The target to remove from the lists */ void G_ReactionFireOnDead (const Actor* target) { assert(G_IsDead(target)); rf.updateAllTargets(target); rf.resetTargets(target); }