/** * @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 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_t *target, const int fdTime) { edict_t *shooter = NULL; 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) { repeat = false; /* check all ents to see who wins and who loses a draw */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { int entTUs = G_ReactionFireGetTUsForItem(shooter, target, RIGHT(shooter)); if (entTUs > 1) { /* indicates an RF weapon is there */ if (G_ReactionFireTargetsExpired(shooter, target, fdTime)) { if (G_ReactionFireTryToShoot(shooter, target)) { repeat = true; G_ReactionFireTargetsAdvance(shooter, target, fdTime); } } } } } }
/** * @brief Resolve the reaction fire for an entity, this checks that the entity can fire and then takes the shot * @param[in] ent The entity to resolve reaction fire for * @return true if the entity fired (or would have fired if mock), false otherwise */ static qboolean G_ReactionFireTryToShoot (edict_t *ent) { qboolean tookShot; /* check whether this ent has a reaction fire queued */ assert(ent->reactionTarget); /* ent can't take a reaction shot if it's not possible - and check that * the target is still alive */ if (!G_ReactionFireIsPossible(ent, ent->reactionTarget)) { ent->reactionTarget = NULL; return qfalse; } /* take the shot */ tookShot = G_ReactionFireShoot(G_PLAYER_FROM_ENT(ent), ent, ent->reactionTarget->pos, ST_RIGHT_REACTION, ent->chr.RFmode.fmIdx); if (tookShot) { /* clear any shakenness */ G_RemoveShaken(ent); /* check whether further reaction fire is possible */ if (G_ReactionFireIsPossible(ent, ent->reactionTarget)){ /* see how quickly ent can fire (if it can fire at all) */ const int tus = G_ReactionFireGetTUsForItem(ent, ent->reactionTarget, RIGHT(ent)); if (tus >= 0) { /* An enemy getting reaction shot gets more time before * reaction fire is repeated. */ ent->reactionTUs = max(0, ent->reactionTarget->TU - tus); } } } return tookShot; }
/** * @brief Check whether 'target' has just triggered any new reaction fire * @param[in] target The entity triggering fire * @sa G_CanReactionFire * @sa G_GetFiringTUs */ static void G_ReactionFireSearchTarget (const edict_t *target) { edict_t *ent = NULL; /* check all possible shooters */ while ((ent = G_EdictsGetNextLivingActor(ent))) { int tus; /* not if ent has reaction target already */ if (ent->reactionTarget) continue; /* check whether reaction fire is possible */ if (!G_ReactionFireIsPossible(ent, target)) continue; /* see how quickly ent can fire (if it can fire at all) */ tus = G_ReactionFireGetTUsForItem(ent, target, RIGHT(ent)); if (tus < 0) continue; /* queue a reaction fire to take place */ ent->reactionTarget = target; /* An enemy entering the line of fire of a soldier on reaction * fire should have the opportunity to spend time equal to the * sum of these values. */ ent->reactionTUs = max(0, target->TU - (tus / 4.0)); ent->reactionNoDraw = qfalse; } }
/** * @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 */ G_ReactionFireTargetsAdd(shooter, target, TUs); } else { G_ReactionFireTargetsRemove(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_t *target) { edict_t *shooter = NULL; bool fired = false; /* check all possible shooters */ while ((shooter = G_EdictsGetNextLivingActor(shooter))) { const int tus = G_ReactionFireGetTUsForItem(shooter, target, RIGHT(shooter)); if (tus > 1) { /* indicates an RF weapon is there */ if (G_ReactionFireTargetsExpired(shooter, target, 0)) { if (G_ReactionFireTryToShoot(shooter, target)) { G_ReactionFireTargetsAdvance(shooter, target, 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 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 shoot * @sa G_ClientShoot */ void G_ReactionFirePreShot (const edict_t *target, const int fdTime) { edict_t *ent = NULL; /* Check to see whether this triggers any reaction fire */ G_ReactionFireSearchTarget(target); /* check all ents to see who wins and who loses a draw */ while ((ent = G_EdictsGetNextLivingActor(ent))) { int entTUs; if (!ent->reactionTarget) continue; /* check this ent hasn't already lost the draw */ if (ent->reactionNoDraw) continue; /* can't reaction fire if no TUs to fire */ entTUs = G_ReactionFireGetTUsForItem(ent, target, RIGHT(ent)); if (entTUs < 0) { ent->reactionTarget = NULL; continue; } /* see who won */ if (entTUs >= fdTime) { /* target wins, so delay ent */ /* ent can't lose the TU battle again */ ent->reactionNoDraw = qtrue; } else { /* ent wins so take the shot */ G_ReactionFireTryToShoot(ent); } } }