mirror of
https://github.com/1dot13/source.git
synced 2026-08-05 14:00:23 +02:00
AI suppression fire improvements.
CalcBestShot: - added standard check for valid opponent - use standard knowledge functions - blind soldier can only attack seen/heard personally - determine suppression fire (shooting at target nobody sees currently) - don't shoot if target location is seen and empty - no fire on unseen opponents with throwing knives - only enemy team can use blind suppression fire - only try to suppress alive and conscious human targets - additional check to limit shooting through walls - alt weapon holding scope mode is used only when ubAllowAlternativeWeaponHolding == 3 - hip firing allowed only for human bodytypes - take into account direction when checking stance - shoot heavy guns in standing stance only when using hip fire - penalize suppression fire Red AI suppression fire: - check valid target - check weapon/ammo requirements - check that soldier is not sniper - check friendly fire chance - reduce chance to shoot if target is beyond weapon range - check that we have spare ammo Improved code to prepare suppression fire: - change stance first of needed - allow shooting with aiming - reserve APs to hide if no cover or enemy is close - added debug messages git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8878 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
+203
-72
@@ -32,6 +32,7 @@
|
||||
#include "Isometric Utils.h"
|
||||
#include "Structure Wrap.h" // IsRoofPresentAtGridNo
|
||||
#include "Render Fun.h"
|
||||
#include "worldman.h"
|
||||
#endif
|
||||
|
||||
// anv: for enemy taunts
|
||||
@@ -170,15 +171,34 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
UINT32 uiLoop;
|
||||
INT32 iAttackValue, iThreatValue, iHitRate, iBestHitRate, iPercentBetter, iEstDamage, iTrueLastTarget;
|
||||
UINT16 usTrueState, usTurningCost, usRaiseGunCost;
|
||||
INT16 ubAimTime, ubMinAPcost, ubRawAPCost, sBestAPcost, ubChanceToHit, ubBestAimTime, ubChanceToGetThrough, ubBestChanceToGetThrough, ubFriendlyFireChance, ubBestFriendlyFireChance, ubBestChanceToHit, sStanceAPcost;
|
||||
INT16 ubAimTime;
|
||||
INT16 ubMinAPcost;
|
||||
INT16 ubRawAPCost;
|
||||
INT16 sBestAPcost;
|
||||
INT16 ubChanceToHit;
|
||||
INT16 ubBestAimTime;
|
||||
INT16 ubChanceToGetThrough;
|
||||
INT16 ubBestChanceToGetThrough;
|
||||
UINT8 ubFriendlyFireChance;
|
||||
UINT8 ubBestFriendlyFireChance;
|
||||
INT16 ubBestChanceToHit;
|
||||
INT16 sStanceAPcost;
|
||||
BOOLEAN fAddingTurningCost, fAddingRaiseGunCost;
|
||||
UINT8 ubMaxPossibleAimTime, ubStance, ubBestStance, ubChanceToReallyHit;
|
||||
INT8 bScopeMode;
|
||||
SOLDIERTYPE *pOpponent;
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"CalcBestShot");
|
||||
// sevenfm:
|
||||
BOOLEAN fSuppression = FALSE;
|
||||
BOOLEAN fReturnFire = FALSE;
|
||||
INT32 sTarget = NOWHERE;
|
||||
INT8 bLevel;
|
||||
|
||||
ubBestChanceToHit = ubBestAimTime = ubChanceToHit = ubBestChanceToGetThrough = ubBestFriendlyFireChance = ubChanceToReallyHit = 0;
|
||||
INT8 bKnowledge;
|
||||
INT8 bPersonalKnowledge;
|
||||
INT8 bPublicKnowledge;
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"CalcBestShot");
|
||||
|
||||
// sevenfm: initialize
|
||||
pBestShot->ubPossible = FALSE;
|
||||
@@ -187,6 +207,8 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
pBestShot->ubOpponent = NOBODY;
|
||||
pBestShot->ubFriendlyFireChance = 0;
|
||||
|
||||
ubBestChanceToHit = ubBestAimTime = ubChanceToHit = ubBestChanceToGetThrough = ubBestFriendlyFireChance = ubChanceToReallyHit = 0;
|
||||
|
||||
// sevenfm: set attacking hand and target
|
||||
pSoldier->ubAttackingHand = HANDPOS;
|
||||
pSoldier->ubTargetID = NOBODY;
|
||||
@@ -204,40 +226,131 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
||||
{
|
||||
pOpponent = MercSlots[ uiLoop ];
|
||||
fSuppression = FALSE;
|
||||
fReturnFire = FALSE;
|
||||
|
||||
// if this merc is inactive, at base, on assignment, or dead
|
||||
if (!pOpponent || !pOpponent->stats.bLife)
|
||||
continue; // next merc
|
||||
|
||||
// if this man is neutral / on the same side, he's not an opponent
|
||||
if ( CONSIDERED_NEUTRAL( pSoldier, pOpponent ) || (pSoldier->bSide == pOpponent->bSide))
|
||||
continue; // next merc
|
||||
if (!ValidOpponent(pSoldier, pOpponent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this opponent is not currently in sight (ignore known but unseen!)
|
||||
/*if ((pSoldier->aiData.bOppList[pOpponent->ubID] != SEEN_CURRENTLY && !shootUnseen)) //guys we can't see
|
||||
// determine return fire
|
||||
if (pSoldier->aiData.bUnderFire &&
|
||||
!pSoldier->bBlindedCounter &&
|
||||
pSoldier->ubPreviousAttackerID == pOpponent->ubID)
|
||||
{
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("CalcBestShot: soldier = %d, target = %d, skip guys we can't see, shootUnseen = %d, personal opplist = %d",pSoldier->ubID, pOpponent->ubID, shootUnseen, pSoldier->aiData.bOppList[pOpponent->ubID]));
|
||||
continue; // next opponent
|
||||
}*/
|
||||
if ((pSoldier->aiData.bOppList[pOpponent->ubID] != SEEN_CURRENTLY && gbPublicOpplist[pSoldier->bTeam][pOpponent->ubID] != SEEN_CURRENTLY)) // guys nobody sees
|
||||
fReturnFire = TRUE;
|
||||
}
|
||||
|
||||
bKnowledge = Knowledge(pSoldier, pOpponent->ubID);
|
||||
bPersonalKnowledge = PersonalKnowledge(pSoldier, pOpponent->ubID);
|
||||
bPublicKnowledge = PublicKnowledge(pSoldier->bTeam, pOpponent->ubID);
|
||||
|
||||
// check knowledge
|
||||
if (bKnowledge != SEEN_CURRENTLY &&
|
||||
bKnowledge != SEEN_THIS_TURN &&
|
||||
bKnowledge != SEEN_LAST_TURN &&
|
||||
bKnowledge != HEARD_THIS_TURN &&
|
||||
bKnowledge != HEARD_LAST_TURN &&
|
||||
!((bKnowledge == SEEN_2_TURNS_AGO || bKnowledge == SEEN_3_TURNS_AGO || bKnowledge == HEARD_2_TURNS_AGO) && Weapon[pSoldier->usAttackingWeapon].ubWeaponType == GUN_LMG))
|
||||
{
|
||||
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("CalcBestShot: soldier = %d, target = %d, skip guys nobody sees, public opplist = %d", pSoldier->ubID, pOpponent->ubID, gbPublicOpplist[pSoldier->bTeam][pOpponent->ubID]));
|
||||
continue; // next opponent
|
||||
}
|
||||
|
||||
// silversurfer: ignore empty vehicles
|
||||
// sevenfm: allow shooting at empty vehicles, but they have low priority
|
||||
//if ( pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle( pOpponent->bVehicleID ) == 0 )
|
||||
//continue;
|
||||
|
||||
// Special stuff for Carmen the bounty hunter
|
||||
if (pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpponent->ubProfile != SLAY)
|
||||
// sevenfm: blind soldier can only attack seen/heard personally
|
||||
if (pSoldier->bBlindedCounter > 0 &&
|
||||
bPersonalKnowledge != SEEN_THIS_TURN &&
|
||||
bPersonalKnowledge != HEARD_THIS_TURN)
|
||||
{
|
||||
continue; // next opponent
|
||||
}
|
||||
|
||||
// sevenfm: determine if we shoot on unseen target for suppression
|
||||
if (bPersonalKnowledge != SEEN_CURRENTLY &&
|
||||
bPublicKnowledge != SEEN_CURRENTLY &&
|
||||
!SoldierToSoldierLineOfSightTest(pSoldier, pOpponent, TRUE, CALC_FROM_ALL_DIRS))
|
||||
{
|
||||
fSuppression = TRUE;
|
||||
}
|
||||
|
||||
// determine enemy location
|
||||
if (fSuppression)
|
||||
{
|
||||
// personal/public knowledge
|
||||
sTarget = KnownLocation(pSoldier, pOpponent->ubID);
|
||||
bLevel = KnownLevel(pSoldier, pOpponent->ubID);
|
||||
// try to randomize location
|
||||
sTarget = RandomizeLocation(sTarget, bLevel, 1, pSoldier);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we know exact enemy location
|
||||
sTarget = pOpponent->sGridNo;
|
||||
bLevel = pOpponent->pathing.bLevel;
|
||||
}
|
||||
|
||||
// safety check
|
||||
if (TileIsOutOfBounds(sTarget))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip if we can see location and location is empty
|
||||
if (SoldierToVirtualSoldierLineOfSightTest(pSoldier, sTarget, bLevel, ANIM_PRONE, TRUE, CALC_FROM_ALL_DIRS) &&
|
||||
WhoIsThere2(sTarget, bLevel) == NOBODY)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// no fire on unseen opponents with throwing knives
|
||||
if ((Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE) &&
|
||||
bPersonalKnowledge != SEEN_CURRENTLY &&
|
||||
!SoldierToSoldierLineOfSightTest(pSoldier, pOpponent, TRUE, CALC_FROM_ALL_DIRS))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// sevenfm: only enemy team can use blind suppression fire
|
||||
if (fSuppression &&
|
||||
pSoldier->bTeam != ENEMY_TEAM)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// sevenfm: only try to suppress alive and conscious human targets
|
||||
if (fSuppression &&
|
||||
(pOpponent->stats.bLife < OKLIFE ||
|
||||
pOpponent->bCollapsed && pOpponent->bBreath == 0 ||
|
||||
//pOpponent->usAnimState == COWERING ||
|
||||
//pOpponent->usAnimState == COWERING_PRONE ||
|
||||
CoweringShockLevel(pOpponent) ||
|
||||
pOpponent->IsZombie() ||
|
||||
!IS_MERC_BODY_TYPE(pOpponent)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// shoot through wall check
|
||||
if( bPersonalKnowledge != SEEN_CURRENTLY &&
|
||||
!SoldierToSoldierLineOfSightTest(pSoldier, pOpponent, TRUE, CALC_FROM_ALL_DIRS) &&
|
||||
!SoldierToVirtualSoldierLineOfSightTest(pSoldier, sTarget, bLevel, ANIM_STAND, TRUE, NO_DISTANCE_LIMIT) &&
|
||||
!LocationToLocationLineOfSightTest(pSoldier->sGridNo, pSoldier->pathing.bLevel, sTarget, bLevel, TRUE, NO_DISTANCE_LIMIT) &&
|
||||
!fReturnFire &&
|
||||
!(InARoom(sTarget, NULL) && bLevel == 0 && Weapon[pSoldier->usAttackingWeapon].ubWeaponType == GUN_LMG) && // bPublicKnowledge == SEEN_CURRENTLY &&
|
||||
!(InARoom(sTarget, NULL) && bLevel == 0 && TeamPercentKilled(pSoldier->bTeam) > (100 - 20 * SoldierDifficultyLevel(pSoldier))) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef DEBUGATTACKS
|
||||
DebugAI( String( "%s sees %s at gridno %d\n",pSoldier->GetName(),ExtMen[pOpponent->ubID].GetName(),pOpponent->sGridNo ) );
|
||||
#endif
|
||||
ubMinAPcost = MinAPsToAttack(pSoldier, pOpponent->sGridNo, DONTADDTURNCOST, 0);// later will be decide if shoot is possible this here is just best guess so ignore turnover
|
||||
ubMinAPcost = MinAPsToAttack(pSoldier, sTarget, DONTADDTURNCOST, 0);
|
||||
// later will be decide if shoot is possible this here is just best guess so ignore turnover
|
||||
|
||||
// if we don't have enough APs left to shoot even a snap-shot at this guy
|
||||
if (ubMinAPcost > pSoldier->bActionPoints)
|
||||
@@ -274,7 +387,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
INT8 bDir;
|
||||
|
||||
// must make sure that structure data can be added in the direction of the target
|
||||
bDir = (INT8) GetDirectionToGridNoFromGridNo( pSoldier->sGridNo, pOpponent->sGridNo );
|
||||
bDir = (INT8)GetDirectionToGridNoFromGridNo(pSoldier->sGridNo, sTarget);
|
||||
|
||||
// ATE: Only if we have a levelnode...
|
||||
if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL )
|
||||
@@ -294,38 +407,37 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
}
|
||||
}
|
||||
|
||||
//if (pOpponent->sGridNo == pSoldier->sLastTarget)
|
||||
//{
|
||||
// // raw AP cost calculation included cost of changing target!
|
||||
// // Not unless we really needed to change targets!
|
||||
// //ubRawAPCost -= APBPConstants[AP_CHANGE_TARGET];
|
||||
//}
|
||||
|
||||
iBestHitRate = 0; // reset best hit rate to minimum
|
||||
|
||||
//dnl ch69 130913 Hoping to optimize
|
||||
// consider alternate holding mode and different scopes
|
||||
for(pSoldier->bScopeMode=(gGameExternalOptions.ubAllowAlternativeWeaponHolding?USE_ALT_WEAPON_HOLD:USE_BEST_SCOPE); pSoldier->bScopeMode<=(gGameExternalOptions.fScopeModes?NUM_SCOPE_MODES-1:USE_BEST_SCOPE); pSoldier->bScopeMode++)
|
||||
// sevenfm: alt weapon holding scope mode is used only when ubAllowAlternativeWeaponHolding == 3
|
||||
for(pSoldier->bScopeMode = (gGameExternalOptions.ubAllowAlternativeWeaponHolding == 3 ? USE_ALT_WEAPON_HOLD : USE_BEST_SCOPE);
|
||||
pSoldier->bScopeMode <= (gGameExternalOptions.fScopeModes ? NUM_SCOPE_MODES - 1 : USE_BEST_SCOPE);
|
||||
pSoldier->bScopeMode++)
|
||||
{
|
||||
//dnl ch71 180913 throwing knives cannot be use in fire from hip, also SANDRO said: if the gun is flagged as HeavyGun, then we can only fire it from hip, thus no scopes to use at all, not even iron sights
|
||||
if(pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD)
|
||||
{
|
||||
//dnl ch71 180913 throwing knives cannot be used in fire from hip
|
||||
if(Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Weapon[pSoldier->usAttackingWeapon].HeavyGun)
|
||||
|
||||
// sevenfm: hip firing allowed only for human bodytypes
|
||||
if(!IS_MERC_BODY_TYPE(pSoldier))
|
||||
continue;
|
||||
}
|
||||
|
||||
if(pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || (pSoldier->bScopeMode >= USE_BEST_SCOPE && ObjList[pSoldier->bScopeMode] != NULL))
|
||||
{
|
||||
usTrueState = pSoldier->usAnimState;// because is used in CalculateRaiseGunCost, CalcAimingLevelsAvailableWithAP, CalculateTurningCost
|
||||
iTrueLastTarget = pSoldier->sLastTarget;// because is used in MinAPsToShootOrStab
|
||||
usTrueState = pSoldier->usAnimState; // because is used in CalculateRaiseGunCost, CalcAimingLevelsAvailableWithAP, CalculateTurningCost
|
||||
iTrueLastTarget = pSoldier->sLastTarget; // because is used in MinAPsToShootOrStab
|
||||
|
||||
// --------- Standing ---------
|
||||
ubStance = ANIM_STAND;
|
||||
if(IsValidStance(pSoldier, ubStance))
|
||||
// sevenfm: take into account direction when checking stance
|
||||
// sevenfm: shoot heavy guns in standing stance only when using hip fire
|
||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, sTarget), ubStance) &&
|
||||
(pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || !Weapon[pSoldier->usAttackingWeapon].HeavyGun || !Item[pSoldier->usAttackingWeapon].twohanded || !gGameExternalOptions.ubAllowAlternativeWeaponHolding))
|
||||
{
|
||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||
if(sStanceAPcost)
|
||||
@@ -334,9 +446,9 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
pSoldier->usAnimState = STANDING;
|
||||
pSoldier->sLastTarget = NOWHERE;
|
||||
}
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, pOpponent->sGridNo, 0);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, sTarget, 0);
|
||||
if(fAddingTurningCost && fAddingRaiseGunCost)//dnl ch71 180913
|
||||
{
|
||||
if(usRaiseGunCost > usTurningCost)
|
||||
@@ -344,27 +456,31 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
else
|
||||
usRaiseGunCost = 0;
|
||||
}
|
||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
|
||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
||||
|
||||
if(pSoldier->bActionPoints - ubMinAPcost >= 0)
|
||||
{
|
||||
// calc next attack's minimum shooting cost (excludes readying & turning & raise gun)
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, pOpponent->sGridNo, pSoldier->bActionPoints-ubMinAPcost);
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, sTarget, pSoldier->bActionPoints - ubMinAPcost);
|
||||
|
||||
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||
gUnderFire.Clear();
|
||||
gUnderFire.Enable();
|
||||
ubChanceToGetThrough = AISoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
if (fSuppression)
|
||||
ubChanceToGetThrough = SoldierToLocationChanceToGetThrough(pSoldier, sTarget, bLevel, 3, NOBODY);
|
||||
else
|
||||
ubChanceToGetThrough = SoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||
gUnderFire.Disable();
|
||||
|
||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
||||
{
|
||||
for (ubAimTime = APBPConstants[AP_MIN_AIM_ATTACK]; ubAimTime <= ubMaxPossibleAimTime; ubAimTime++)
|
||||
for (ubAimTime = 0; ubAimTime <= ubMaxPossibleAimTime; ubAimTime++)
|
||||
{
|
||||
ubChanceToHit = AICalcChanceToHitGun(pSoldier, pOpponent->sGridNo, ubAimTime, AIM_SHOT_TORSO, pOpponent->pathing.bLevel, STANDING);
|
||||
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||
ubChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, ubAimTime, AIM_SHOT_TORSO, bLevel, STANDING);
|
||||
iHitRate = ubChanceToHit * (pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||
// sevenfm: take into account CTGT for every stance
|
||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough ||
|
||||
(Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE) && ubChanceToHit > ubBestChanceToHit)// rather take best chance for throwing knives
|
||||
@@ -385,12 +501,14 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
pSoldier->sLastTarget = iTrueLastTarget;
|
||||
}
|
||||
|
||||
// no crouched/prone if we are tank/using throwing knife/hip firing
|
||||
if ( pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || ARMED_VEHICLE( pSoldier ) || (Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE) )
|
||||
continue;
|
||||
|
||||
// --------- Crouched ---------
|
||||
ubStance = ANIM_CROUCH;
|
||||
if(IsValidStance(pSoldier, ubStance))
|
||||
// sevenfm: take into account direction
|
||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, sTarget), ubStance))
|
||||
{
|
||||
// change stance then turn
|
||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||
@@ -399,9 +517,9 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
pSoldier->usAnimState = CROUCHING;
|
||||
pSoldier->sLastTarget = NOWHERE;
|
||||
}
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, pOpponent->sGridNo, 0);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, sTarget, 0);
|
||||
if(fAddingTurningCost && fAddingRaiseGunCost)//dnl ch71 180913
|
||||
{
|
||||
if(usRaiseGunCost > usTurningCost)
|
||||
@@ -409,27 +527,30 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
else
|
||||
usRaiseGunCost = 0;
|
||||
}
|
||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
|
||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
||||
|
||||
if(pSoldier->bActionPoints - ubMinAPcost >= 0)
|
||||
{
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, pOpponent->sGridNo, pSoldier->bActionPoints-ubMinAPcost);
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, sTarget, pSoldier->bActionPoints - ubMinAPcost);
|
||||
|
||||
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||
gUnderFire.Clear();
|
||||
gUnderFire.Enable();
|
||||
ubChanceToGetThrough = AISoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
if (fSuppression)
|
||||
ubChanceToGetThrough = SoldierToLocationChanceToGetThrough(pSoldier, sTarget, bLevel, 3, NOBODY);
|
||||
else
|
||||
ubChanceToGetThrough = SoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||
gUnderFire.Disable();
|
||||
|
||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
||||
{
|
||||
for (ubAimTime = APBPConstants[AP_MIN_AIM_ATTACK]; ubAimTime <= ubMaxPossibleAimTime; ubAimTime++)
|
||||
for (ubAimTime = 0; ubAimTime <= ubMaxPossibleAimTime; ubAimTime++)
|
||||
{
|
||||
ubChanceToHit = AICalcChanceToHitGun(pSoldier, pOpponent->sGridNo, ubAimTime, AIM_SHOT_TORSO, pOpponent->pathing.bLevel, CROUCHING);
|
||||
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||
ubChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, ubAimTime, AIM_SHOT_TORSO, bLevel, CROUCHING);
|
||||
iHitRate = ubChanceToHit * (pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||
// sevenfm: take into account CTGT for every stance
|
||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough)
|
||||
{
|
||||
@@ -450,7 +571,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
}
|
||||
|
||||
// no prone stance if we have to change direction and stance at the same time
|
||||
if (pSoldier->ubDirection != AIDirection(pSoldier->sGridNo, pOpponent->sGridNo) &&
|
||||
if (pSoldier->ubDirection != AIDirection(pSoldier->sGridNo, sTarget) &&
|
||||
gAnimControl[pSoldier->usAnimState].ubEndHeight > ANIM_PRONE)
|
||||
{
|
||||
continue;
|
||||
@@ -458,8 +579,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
|
||||
// --------- Prone ---------
|
||||
ubStance = ANIM_PRONE;
|
||||
//if(IsValidStance(pSoldier, ubStance))
|
||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, pOpponent->sGridNo), ubStance))
|
||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, sTarget), ubStance))
|
||||
{
|
||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||
if (sStanceAPcost)
|
||||
@@ -467,30 +587,33 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
pSoldier->usAnimState = PRONE;
|
||||
pSoldier->sLastTarget = NOWHERE;
|
||||
}
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, pOpponent->sGridNo, 0);
|
||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, sTarget, 0);
|
||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
||||
|
||||
if(pSoldier->bActionPoints - ubMinAPcost >= 0)
|
||||
if (pSoldier->bActionPoints - ubMinAPcost >= 0)
|
||||
{
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, pOpponent->sGridNo, pSoldier->bActionPoints-ubMinAPcost);
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, sTarget, pSoldier->bActionPoints - ubMinAPcost);
|
||||
|
||||
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||
gUnderFire.Clear();
|
||||
gUnderFire.Enable();
|
||||
ubChanceToGetThrough = AISoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
if (fSuppression)
|
||||
ubChanceToGetThrough = SoldierToLocationChanceToGetThrough(pSoldier, sTarget, bLevel, 3, NOBODY);
|
||||
else
|
||||
ubChanceToGetThrough = SoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||
gUnderFire.Disable();
|
||||
|
||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
||||
{
|
||||
for (ubAimTime = APBPConstants[AP_MIN_AIM_ATTACK]; ubAimTime <= ubMaxPossibleAimTime; ubAimTime++)
|
||||
for (ubAimTime = 0; ubAimTime <= ubMaxPossibleAimTime; ubAimTime++)
|
||||
{
|
||||
ubChanceToHit = AICalcChanceToHitGun(pSoldier, pOpponent->sGridNo, ubAimTime, AIM_SHOT_TORSO, pOpponent->pathing.bLevel, PRONE);
|
||||
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||
ubChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, ubAimTime, AIM_SHOT_TORSO, bLevel, PRONE);
|
||||
iHitRate = ubChanceToHit * (pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||
// sevenfm: take into account CTGT for every stance
|
||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough)
|
||||
{
|
||||
@@ -524,14 +647,15 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
continue; // next opponent
|
||||
|
||||
// really limit knife throwing so it doesn't look wrong
|
||||
if ( Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_THROWING_KNIFE && (ubChanceToReallyHit < 25 || ( PythSpacesAway( pSoldier->sGridNo, pOpponent->sGridNo ) > CalcMaxTossRange( pSoldier, pSoldier->usAttackingWeapon, FALSE ) )))// Madd / 2 ) ) ) //dnl ch69 160913 was ubChanceToReallyHit < 30
|
||||
if (Item[pSoldier->usAttackingWeapon].usItemClass == IC_THROWING_KNIFE &&
|
||||
(ubChanceToReallyHit < 25 || (PythSpacesAway(pSoldier->sGridNo, sTarget) > CalcMaxTossRange(pSoldier, pSoldier->usAttackingWeapon, FALSE))))// Madd / 2 ) ) ) //dnl ch69 160913 was ubChanceToReallyHit < 30
|
||||
continue; // don't bother... next opponent
|
||||
|
||||
// calculate this opponent's threat value (factor in my cover from him)
|
||||
iThreatValue = CalcManThreatValue(pOpponent,pSoldier->sGridNo,TRUE,pSoldier);
|
||||
iThreatValue = CalcManThreatValue(pOpponent, pSoldier->sGridNo, TRUE, pSoldier);
|
||||
|
||||
// estimate the damage this shot would do to this opponent
|
||||
iEstDamage = EstimateShotDamage(pSoldier,pOpponent,ubBestChanceToHit);
|
||||
iEstDamage = EstimateShotDamage(pSoldier, pOpponent, ubBestChanceToHit);
|
||||
//NumMessage("SHOT EstDamage = ",iEstDamage);
|
||||
|
||||
// calculate the combined "attack value" for this opponent
|
||||
@@ -540,6 +664,13 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
iAttackValue = (iEstDamage * iBestHitRate * ubChanceToReallyHit * iThreatValue) / 1000;
|
||||
//NumMessage("SHOT AttackValue = ",iAttackValue / 1000);
|
||||
|
||||
// sevenfm: penalize suppression fire
|
||||
if (fSuppression)
|
||||
{
|
||||
// 25% penalty for shooting at invisible target
|
||||
iAttackValue = iAttackValue / 2;
|
||||
}
|
||||
|
||||
// special stuff for assassins to ignore militia more
|
||||
if ( pSoldier->IsAssassin() && pOpponent->bTeam == MILITIA_TEAM )
|
||||
{
|
||||
@@ -624,8 +755,8 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
pBestShot->ubOpponent = pOpponent->ubID;
|
||||
pBestShot->ubAimTime = ubBestAimTime;
|
||||
pBestShot->ubChanceToReallyHit = ubChanceToReallyHit;
|
||||
pBestShot->sTarget = pOpponent->sGridNo;
|
||||
pBestShot->bTargetLevel = pOpponent->pathing.bLevel;
|
||||
pBestShot->sTarget = sTarget;
|
||||
pBestShot->bTargetLevel = bLevel;
|
||||
pBestShot->iAttackValue = iAttackValue;
|
||||
pBestShot->ubAPCost = sBestAPcost;
|
||||
pBestShot->ubStance = ubBestStance;
|
||||
|
||||
Reference in New Issue
Block a user