00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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) {
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
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
00173 if (!ent->inuse || !G_IsLivingActor(ent))
00174 return qfalse;
00175
00176 if (G_MatchIsRunning() && ent->team != level.activeTeam)
00177 return qfalse;
00178
00179
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
00213 if (ent == target)
00214 return qfalse;
00215
00216
00217 if (ent->team == level.activeTeam)
00218 return qfalse;
00219
00220
00221 if (G_IsDazed(ent))
00222 return qfalse;
00223
00224 if (G_IsDead(target))
00225 return qfalse;
00226
00227
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
00235
00236
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
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
00254 return qtrue;
00255 }
00256
00263 static void G_ReactionFireSearchTarget (const edict_t *target)
00264 {
00265 edict_t *ent = NULL;
00266
00267
00268 while ((ent = G_EdictsGetNextLivingActor(ent))) {
00269 int tus;
00270
00271
00272 if (ent->reactionTarget)
00273 continue;
00274
00275
00276 if (!G_ReactionFireIsPossible(ent, target))
00277 continue;
00278
00279
00280 tus = G_ReactionFireGetTUsForItem(ent, target, RIGHT(ent));
00281 if (tus < 0)
00282 continue;
00283
00284
00285 ent->reactionTarget = target;
00286
00287
00288
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
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
00324
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
00347 assert(ent->reactionTarget);
00348
00349
00350
00351 if (!G_ReactionFireIsPossible(ent, ent->reactionTarget)) {
00352 ent->reactionTarget = NULL;
00353 return qfalse;
00354 }
00355
00356
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
00361 G_RemoveShaken(ent);
00362
00363
00364 if (G_ReactionFireIsPossible(ent, ent->reactionTarget)){
00365
00366 const int tus = G_ReactionFireGetTUsForItem(ent, ent->reactionTarget, RIGHT(ent));
00367 if (tus >= 0) {
00368
00369
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
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
00397
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
00414 const qboolean fired = G_ReactionFireCheckExecution(target);
00415
00416
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
00433 G_ReactionFireSearchTarget(target);
00434
00435
00436 while ((ent = G_EdictsGetNextLivingActor(ent))) {
00437 int entTUs;
00438
00439 if (!ent->reactionTarget)
00440 continue;
00441
00442
00443 if (ent->reactionNoDraw)
00444 continue;
00445
00446
00447 entTUs = G_ReactionFireGetTUsForItem(ent, target, RIGHT(ent));
00448 if (entTUs < 0) {
00449 ent->reactionTarget = NULL;
00450 continue;
00451 }
00452
00453
00454 if (entTUs >= fdTime) {
00455
00456
00457 ent->reactionNoDraw = qtrue;
00458 } else {
00459
00460 G_ReactionFireTryToShoot(ent);
00461 }
00462 }
00463 }
00464
00470 void G_ReactionFirePostShot (edict_t *target)
00471 {
00472
00473 G_ReactionFireCheckExecution(target);
00474 }
00475
00480 void G_ReactionFireEndTurn (void)
00481 {
00482 edict_t *ent = NULL;
00483
00484
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 }