g_reaction.c

Go to the documentation of this file.
00001 
00006 /*
00007 Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 This program is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU General Public License
00011 as published by the Free Software Foundation; either version 2
00012 of the License, or (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018 See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 
00024 */
00025 
00026 #include "g_local.h"
00027 
00033 static int G_ReactionFireGetTUsForItem (const edict_t *ent, const edict_t *target, const invList_t *invList)
00034 {
00035     if (invList && invList->item.m && invList->item.t->weapon
00036      && (!invList->item.t->reload || invList->item.a > 0)) {
00037         const fireDef_t *fdArray = FIRESH_FiredefForWeapon(&invList->item);
00038         if (fdArray == NULL)
00039             return -1;
00040 
00041         if (ent->chr.RFmode.hand == ACTOR_HAND_RIGHT && ent->chr.RFmode.fmIdx >= 0
00042          && ent->chr.RFmode.fmIdx < MAX_FIREDEFS_PER_WEAPON) { /* If a RIGHT-hand firemode is selected and sane. */
00043             const fireDefIndex_t fmIdx = ent->chr.RFmode.fmIdx;
00044             const int reactionFire = G_PLAYER_FROM_ENT(ent)->reactionLeftover;
00045 
00046             if (fdArray[fmIdx].time + reactionFire <= ent->TU
00047              && fdArray[fmIdx].range > VectorDist(ent->origin, target->origin)) {
00048                 return fdArray[fmIdx].time + reactionFire;
00049             }
00050         }
00051     }
00052 
00053     return -1;
00054 }
00055 
00061 static qboolean G_ActorHasReactionFireEnabledWeapon (const edict_t *ent)
00062 {
00063     const objDef_t *weapon = INVSH_HasReactionFireEnabledWeapon(RIGHT(ent));
00064     if (weapon)
00065         return qtrue;
00066     return INVSH_HasReactionFireEnabledWeapon(LEFT(ent)) != NULL;
00067 }
00068 
00073 static qboolean G_ActorHasWorkingFireModeSet (const edict_t *actor)
00074 {
00075     const fireDef_t *fd;
00076     const chrFiremodeSettings_t *fmSettings = &actor->chr.RFmode;
00077     const invList_t* invList;
00078 
00079     if (!SANE_FIREMODE(fmSettings))
00080         return qfalse;
00081 
00082     invList = ACTOR_GET_INV(actor, fmSettings->hand);
00083     if (!invList)
00084         return qfalse;
00085     fd = FIRESH_FiredefForWeapon(&invList->item);
00086     if (fd == NULL)
00087         return qfalse;
00088 
00089     if (fd->obj->weapons[fd->weapFdsIdx] == fmSettings->weapon && fmSettings->fmIdx
00090             < fd->obj->numFiredefs[fd->weapFdsIdx]) {
00091         return qtrue;
00092     }
00093 
00094     return qfalse;
00095 }
00096 
00102 void G_ReactionFireUpdate (edict_t *ent, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t *od)
00103 {
00104     chrFiremodeSettings_t *fm = &ent->chr.RFmode;
00105     fm->fmIdx = fmIdx;
00106     fm->hand = hand;
00107     fm->weapon = od;
00108 
00109     if (!G_ActorHasWorkingFireModeSet(ent)) {
00110         /* Disable reaction fire if no valid firemode was found. */
00111         G_ClientStateChange(G_PLAYER_FROM_ENT(ent), ent, ~STATE_REACTION, qfalse);
00112         return;
00113     }
00114 
00115     G_EventReactionFireChange(ent);
00116 }
00117 
00123 static qboolean G_ActorHasEnoughTUsReactionFire (const edict_t *ent)
00124 {
00125     const int TUs = G_ActorGetTUForReactionFire(ent);
00126     const chrReservations_t *res = &ent->chr.reservedTus;
00127     return ent->TU - TUs >= res->shot + res->crouch;
00128 }
00129 
00135 qboolean G_ReactionFireSetDefault (edict_t *ent)
00136 {
00137     const objDef_t *weapon;
00138     const invList_t *invList;
00139     actorHands_t hand = ACTOR_HAND_RIGHT;
00140 
00141     if (G_ActorHasWorkingFireModeSet(ent))
00142         return qtrue;
00143 
00144     invList = ACTOR_GET_INV(ent, hand);
00145     if (!invList) {
00146         hand = ACTOR_HAND_LEFT;
00147         invList = ACTOR_GET_INV(ent, hand);
00148     }
00149 
00150     weapon = INVSH_HasReactionFireEnabledWeapon(invList);
00151     if (!weapon)
00152         return qfalse;
00153 
00154     ent->chr.RFmode.fmIdx = 0;
00155     ent->chr.RFmode.hand = hand;
00156     ent->chr.RFmode.weapon = weapon;
00157 
00158     if (!G_IsAI(ent))
00159         G_EventReactionFireChange(ent);
00160 
00161     return qtrue;
00162 }
00163 
00170 qboolean G_ReactionFireCanBeEnabled (const edict_t *ent)
00171 {
00172     /* check ent is a suitable shooter */
00173     if (!ent->inuse || !G_IsLivingActor(ent))
00174         return qfalse;
00175 
00176     if (G_MatchIsRunning() && ent->team != level.activeTeam)
00177         return qfalse;
00178 
00179     /* actor may not carry weapons at all - so no further checking is needed */
00180     if (!ent->chr.teamDef->weapons)
00181         return qfalse;
00182 
00183     if (!G_ActorHasReactionFireEnabledWeapon(ent)) {
00184         G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("No reaction fire enabled weapon.\n"));
00185         return qfalse;
00186     }
00187 
00188     if (!G_ActorHasWorkingFireModeSet(ent)) {
00189         G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("No fire mode selected for reaction fire.\n"));
00190         return qfalse;
00191     }
00192 
00193     if (!G_ActorHasEnoughTUsReactionFire(ent)) {
00194         G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("Not enough TUs left for activating reaction fire.\n"));
00195         return qfalse;
00196     }
00197 
00198     return qtrue;
00199 }
00200 
00207 static qboolean G_ReactionFireIsPossible (const edict_t *ent, const edict_t *target)
00208 {
00209     float actorVis;
00210     qboolean frustum;
00211 
00212     /* an entity can't reaction fire at itself */
00213     if (ent == target)
00214         return qfalse;
00215 
00216     /* Don't react in your own turn */
00217     if (ent->team == level.activeTeam)
00218         return qfalse;
00219 
00220     /* ent can't use RF if is in STATE_DAZED (flashbang impact) */
00221     if (G_IsDazed(ent))
00222         return qfalse;
00223 
00224     if (G_IsDead(target))
00225         return qfalse;
00226 
00227     /* check ent has reaction fire enabled */
00228     if (!G_IsShaken(ent) && !(ent->state & STATE_REACTION))
00229         return qfalse;
00230 
00231     if (!G_IsVisibleForTeam(target, ent->team))
00232         return qfalse;
00233 
00234     /* If reaction fire is triggered by a friendly unit
00235      * and the shooter is still sane, don't shoot;
00236      * well, if the shooter isn't sane anymore... */
00237     if (G_IsCivilian(target) || target->team == ent->team)
00238         if (!G_IsShaken(ent) || (float) ent->morale / mor_shaken->value > frand())
00239             return qfalse;
00240 
00241     /* check in range and visible */
00242     if (VectorDistSqr(ent->origin, target->origin) > MAX_SPOT_DIST * MAX_SPOT_DIST)
00243         return qfalse;
00244 
00245     frustum = G_FrustumVis(ent, target->origin);
00246     if (!frustum)
00247         return qfalse;
00248 
00249     actorVis = G_ActorVis(ent->origin, target, qtrue);
00250     if (actorVis <= 0.2)
00251         return qfalse;
00252 
00253     /* okay do it then */
00254     return qtrue;
00255 }
00256 
00263 static void G_ReactionFireSearchTarget (const edict_t *target)
00264 {
00265     edict_t *ent = NULL;
00266 
00267     /* check all possible shooters */
00268     while ((ent = G_EdictsGetNextLivingActor(ent))) {
00269         int tus;
00270 
00271         /* not if ent has reaction target already */
00272         if (ent->reactionTarget)
00273             continue;
00274 
00275         /* check whether reaction fire is possible */
00276         if (!G_ReactionFireIsPossible(ent, target))
00277             continue;
00278 
00279         /* see how quickly ent can fire (if it can fire at all) */
00280         tus = G_ReactionFireGetTUsForItem(ent, target, RIGHT(ent));
00281         if (tus < 0)
00282             continue;
00283 
00284         /* queue a reaction fire to take place */
00285         ent->reactionTarget = target;
00286         /* An enemy entering the line of fire of a soldier on reaction
00287          * fire should have the opportunity to spend time equal to the
00288          * sum of these values. */
00289         ent->reactionTUs = max(0, target->TU - (tus / 4.0));
00290         ent->reactionNoDraw = qfalse;
00291     }
00292 }
00293 
00304 static qboolean G_ReactionFireShoot (const player_t *player, edict_t *shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode)
00305 {
00306     const int minhit = 30;
00307     shot_mock_t mock;
00308     int ff, i;
00309     /* this is the max amount of friendly units that were hit during the mock calculation */
00310     int maxff;
00311 
00312     if (G_IsInsane(shooter))
00313         maxff = 100;
00314     else if (G_IsRaged(shooter))
00315         maxff = 60;
00316     else if (G_IsPaniced(shooter))
00317         maxff = 30;
00318     else if (G_IsShaken(shooter))
00319         maxff = 15;
00320     else
00321         maxff = 5;
00322 
00323     /* calculate the mock values - e.g. how many friendly units we would hit
00324      * when opening the reaction fire */
00325     memset(&mock, 0, sizeof(mock));
00326     for (i = 0; i < 100; i++)
00327         if (!G_ClientShoot(player, shooter, at, type, firemode, &mock, qfalse, 0))
00328             break;
00329 
00330     ff = mock.friendCount + (G_IsAlien(shooter) ? 0 : mock.civilian);
00331     if (ff <= maxff && mock.enemyCount >= minhit)
00332         return G_ClientShoot(player, shooter, at, type, firemode, NULL, qfalse, 0);
00333 
00334     return qfalse;
00335 }
00336 
00342 static qboolean G_ReactionFireTryToShoot (edict_t *ent)
00343 {
00344     qboolean tookShot;
00345 
00346     /* check whether this ent has a reaction fire queued */
00347     assert(ent->reactionTarget);
00348 
00349     /* ent can't take a reaction shot if it's not possible - and check that
00350      * the target is still alive */
00351     if (!G_ReactionFireIsPossible(ent, ent->reactionTarget)) {
00352         ent->reactionTarget = NULL;
00353         return qfalse;
00354     }
00355 
00356     /* take the shot */
00357     tookShot = G_ReactionFireShoot(G_PLAYER_FROM_ENT(ent), ent, ent->reactionTarget->pos, ST_RIGHT_REACTION, ent->chr.RFmode.fmIdx);
00358 
00359     if (tookShot) {
00360         /* clear any shakenness */
00361         G_RemoveShaken(ent);
00362 
00363         /* check whether further reaction fire is possible */
00364         if (G_ReactionFireIsPossible(ent, ent->reactionTarget)){
00365             /* see how quickly ent can fire (if it can fire at all) */
00366             const int tus = G_ReactionFireGetTUsForItem(ent, ent->reactionTarget, RIGHT(ent));
00367             if (tus >= 0) {
00368                 /* An enemy getting reaction shot gets more time before
00369                  * reaction fire is repeated. */
00370                 ent->reactionTUs = max(0, ent->reactionTarget->TU - tus);
00371             }
00372         }
00373     }
00374 
00375     return tookShot;
00376 }
00377 
00385 static qboolean G_ReactionFireCheckExecution (const edict_t *target)
00386 {
00387     edict_t *ent = NULL;
00388     qboolean fired = qfalse;
00389 
00390     /* check all possible shooters */
00391     while ((ent = G_EdictsGetNextLivingActor(ent))) {
00392         if (ent->reactionTarget) {
00393             const int reactionTargetTU = ent->reactionTarget->TU;
00394             const int reactionTU = ent->reactionTUs;
00395             const qboolean timeout = g_reaction_fair->integer == 0 || reactionTargetTU < reactionTU;
00396             /* check whether target has changed (i.e. the player is making a move with a
00397              * different entity) or whether target is out of time. */
00398             if (ent->reactionTarget != target || timeout)
00399                 fired |= G_ReactionFireTryToShoot(ent);
00400         }
00401     }
00402     return fired;
00403 }
00404 
00411 qboolean G_ReactionFireOnMovement (edict_t *target)
00412 {
00413     /* Check to see whether this resolves any reaction fire */
00414     const qboolean fired = G_ReactionFireCheckExecution(target);
00415 
00416     /* Check to see whether this triggers any reaction fire */
00417     G_ReactionFireSearchTarget(target);
00418 
00419     return fired;
00420 }
00421 
00428 void G_ReactionFirePreShot (const edict_t *target, const int fdTime)
00429 {
00430     edict_t *ent = NULL;
00431 
00432     /* Check to see whether this triggers any reaction fire */
00433     G_ReactionFireSearchTarget(target);
00434 
00435     /* check all ents to see who wins and who loses a draw */
00436     while ((ent = G_EdictsGetNextLivingActor(ent))) {
00437         int entTUs;
00438 
00439         if (!ent->reactionTarget)
00440             continue;
00441 
00442         /* check this ent hasn't already lost the draw */
00443         if (ent->reactionNoDraw)
00444             continue;
00445 
00446         /* can't reaction fire if no TUs to fire */
00447         entTUs = G_ReactionFireGetTUsForItem(ent, target, RIGHT(ent));
00448         if (entTUs < 0) {
00449             ent->reactionTarget = NULL;
00450             continue;
00451         }
00452 
00453         /* see who won */
00454         if (entTUs >= fdTime) {
00455             /* target wins, so delay ent */
00456             /* ent can't lose the TU battle again */
00457             ent->reactionNoDraw = qtrue;
00458         } else {
00459             /* ent wins so take the shot */
00460             G_ReactionFireTryToShoot(ent);
00461         }
00462     }
00463 }
00464 
00470 void G_ReactionFirePostShot (edict_t *target)
00471 {
00472     /* Check to see whether this resolves any reaction fire */
00473     G_ReactionFireCheckExecution(target);
00474 }
00475 
00480 void G_ReactionFireEndTurn (void)
00481 {
00482     edict_t *ent = NULL;
00483 
00484     /* resolve all outstanding reaction firing if possible */
00485     while ((ent = G_EdictsGetNextLivingActor(ent))) {
00486         if (!ent->reactionTarget)
00487             continue;
00488 
00489         G_ReactionFireTryToShoot(ent);
00490     }
00491 }
00492 
00499 void G_ReactionFireReset (int team)
00500 {
00501     edict_t *ent = NULL;
00502 
00503     while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team))) {
00506         G_RemoveShaken(ent);
00507         ent->reactionTarget = NULL;
00508         ent->reactionTUs = 0;
00509         ent->reactionNoDraw = qfalse;
00510 
00511         gi.AddEvent(G_TeamToPM(ent->team), EV_ACTOR_STATECHANGE);
00512         gi.WriteShort(ent->number);
00513         gi.WriteShort(ent->state);
00514     }
00515 }

Generated by  doxygen 1.6.2