mirror of
https://github.com/1dot13/source.git
synced 2026-09-02 14:36:06 +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:
@@ -5790,3 +5790,81 @@ UINT8 SectorCurfew(BOOLEAN fNight)
|
|||||||
|
|
||||||
return ubSectorData;
|
return ubSectorData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INT32 RandomizeLocation(INT32 sSpot, INT8 bLevel, UINT8 ubTimes, SOLDIERTYPE *pSightSoldier)
|
||||||
|
{
|
||||||
|
if (TileIsOutOfBounds(sSpot))
|
||||||
|
{
|
||||||
|
return NOWHERE;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 ubDirection;
|
||||||
|
UINT8 ubMovementCost;
|
||||||
|
INT32 sTempSpot;
|
||||||
|
INT32 sSpotArray[NUM_WORLD_DIRECTIONS + 1];
|
||||||
|
UINT8 ubSpots;
|
||||||
|
|
||||||
|
for (UINT8 ubCnt = 0; ubCnt < ubTimes; ubCnt++)
|
||||||
|
{
|
||||||
|
// store original location
|
||||||
|
ubSpots = 1;
|
||||||
|
sSpotArray[0] = sSpot;
|
||||||
|
|
||||||
|
// find adjacent locations
|
||||||
|
for (ubDirection = 0; ubDirection < NUM_WORLD_DIRECTIONS; ubDirection++)
|
||||||
|
{
|
||||||
|
sTempSpot = NewGridNo(sSpot, DirectionInc(ubDirection));
|
||||||
|
|
||||||
|
if (sTempSpot != sSpot)
|
||||||
|
{
|
||||||
|
ubMovementCost = gubWorldMovementCosts[sTempSpot][ubDirection][bLevel];
|
||||||
|
|
||||||
|
if (ubMovementCost < TRAVELCOST_BLOCKED &&
|
||||||
|
IsLocationSittableExcludingPeople(sTempSpot, bLevel) &&
|
||||||
|
(!pSightSoldier || SoldierToVirtualSoldierLineOfSightTest(pSightSoldier, sTempSpot, bLevel, ANIM_STAND, TRUE, NO_DISTANCE_LIMIT)))
|
||||||
|
{
|
||||||
|
sSpotArray[ubSpots] = sTempSpot;
|
||||||
|
ubSpots++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// find random location
|
||||||
|
sSpot = sSpotArray[Random(ubSpots)];
|
||||||
|
// stop if could not find any adjacent spot
|
||||||
|
if (ubSpots < 2)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sSpot;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT32 RandomizeOpponentLocation(INT32 sSpot, SOLDIERTYPE *pOpponent, INT16 sMaxDistance)
|
||||||
|
{
|
||||||
|
if (TileIsOutOfBounds(sSpot))
|
||||||
|
{
|
||||||
|
return NOWHERE;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT8 bXOffset, bYOffset;
|
||||||
|
INT32 sRandomSpot;
|
||||||
|
|
||||||
|
if (sMaxDistance > 0)
|
||||||
|
{
|
||||||
|
for (INT cnt = 0; cnt < min(sMaxDistance * 2, 100); cnt++)
|
||||||
|
{
|
||||||
|
bXOffset = Random(sMaxDistance * 2 + 1) - sMaxDistance;
|
||||||
|
bYOffset = Random(sMaxDistance * 2 + 1) - sMaxDistance;
|
||||||
|
|
||||||
|
sRandomSpot = sSpot + bXOffset + (MAXCOL * bYOffset);
|
||||||
|
|
||||||
|
if (!TileIsOutOfBounds(sRandomSpot) && NewOKDestination(pOpponent, sRandomSpot, FALSE, pOpponent->pathing.bLevel))
|
||||||
|
{
|
||||||
|
return sRandomSpot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sSpot;
|
||||||
|
}
|
||||||
+203
-72
@@ -32,6 +32,7 @@
|
|||||||
#include "Isometric Utils.h"
|
#include "Isometric Utils.h"
|
||||||
#include "Structure Wrap.h" // IsRoofPresentAtGridNo
|
#include "Structure Wrap.h" // IsRoofPresentAtGridNo
|
||||||
#include "Render Fun.h"
|
#include "Render Fun.h"
|
||||||
|
#include "worldman.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// anv: for enemy taunts
|
// anv: for enemy taunts
|
||||||
@@ -170,15 +171,34 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
UINT32 uiLoop;
|
UINT32 uiLoop;
|
||||||
INT32 iAttackValue, iThreatValue, iHitRate, iBestHitRate, iPercentBetter, iEstDamage, iTrueLastTarget;
|
INT32 iAttackValue, iThreatValue, iHitRate, iBestHitRate, iPercentBetter, iEstDamage, iTrueLastTarget;
|
||||||
UINT16 usTrueState, usTurningCost, usRaiseGunCost;
|
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;
|
BOOLEAN fAddingTurningCost, fAddingRaiseGunCost;
|
||||||
UINT8 ubMaxPossibleAimTime, ubStance, ubBestStance, ubChanceToReallyHit;
|
UINT8 ubMaxPossibleAimTime, ubStance, ubBestStance, ubChanceToReallyHit;
|
||||||
INT8 bScopeMode;
|
INT8 bScopeMode;
|
||||||
SOLDIERTYPE *pOpponent;
|
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
|
// sevenfm: initialize
|
||||||
pBestShot->ubPossible = FALSE;
|
pBestShot->ubPossible = FALSE;
|
||||||
@@ -187,6 +207,8 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
pBestShot->ubOpponent = NOBODY;
|
pBestShot->ubOpponent = NOBODY;
|
||||||
pBestShot->ubFriendlyFireChance = 0;
|
pBestShot->ubFriendlyFireChance = 0;
|
||||||
|
|
||||||
|
ubBestChanceToHit = ubBestAimTime = ubChanceToHit = ubBestChanceToGetThrough = ubBestFriendlyFireChance = ubChanceToReallyHit = 0;
|
||||||
|
|
||||||
// sevenfm: set attacking hand and target
|
// sevenfm: set attacking hand and target
|
||||||
pSoldier->ubAttackingHand = HANDPOS;
|
pSoldier->ubAttackingHand = HANDPOS;
|
||||||
pSoldier->ubTargetID = NOBODY;
|
pSoldier->ubTargetID = NOBODY;
|
||||||
@@ -204,40 +226,131 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
||||||
{
|
{
|
||||||
pOpponent = MercSlots[ uiLoop ];
|
pOpponent = MercSlots[ uiLoop ];
|
||||||
|
fSuppression = FALSE;
|
||||||
|
fReturnFire = FALSE;
|
||||||
|
|
||||||
// if this merc is inactive, at base, on assignment, or dead
|
// if this merc is inactive, at base, on assignment, or dead
|
||||||
if (!pOpponent || !pOpponent->stats.bLife)
|
if (!pOpponent || !pOpponent->stats.bLife)
|
||||||
continue; // next merc
|
continue; // next merc
|
||||||
|
|
||||||
// if this man is neutral / on the same side, he's not an opponent
|
if (!ValidOpponent(pSoldier, pOpponent))
|
||||||
if ( CONSIDERED_NEUTRAL( pSoldier, pOpponent ) || (pSoldier->bSide == pOpponent->bSide))
|
{
|
||||||
continue; // next merc
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// if this opponent is not currently in sight (ignore known but unseen!)
|
// determine return fire
|
||||||
/*if ((pSoldier->aiData.bOppList[pOpponent->ubID] != SEEN_CURRENTLY && !shootUnseen)) //guys we can't see
|
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]));
|
fReturnFire = TRUE;
|
||||||
continue; // next opponent
|
}
|
||||||
}*/
|
|
||||||
if ((pSoldier->aiData.bOppList[pOpponent->ubID] != SEEN_CURRENTLY && gbPublicOpplist[pSoldier->bTeam][pOpponent->ubID] != SEEN_CURRENTLY)) // guys nobody sees
|
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
|
continue; // next opponent
|
||||||
}
|
}
|
||||||
|
|
||||||
// silversurfer: ignore empty vehicles
|
// sevenfm: blind soldier can only attack seen/heard personally
|
||||||
// sevenfm: allow shooting at empty vehicles, but they have low priority
|
if (pSoldier->bBlindedCounter > 0 &&
|
||||||
//if ( pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle( pOpponent->bVehicleID ) == 0 )
|
bPersonalKnowledge != SEEN_THIS_TURN &&
|
||||||
//continue;
|
bPersonalKnowledge != HEARD_THIS_TURN)
|
||||||
|
{
|
||||||
// Special stuff for Carmen the bounty hunter
|
|
||||||
if (pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpponent->ubProfile != SLAY)
|
|
||||||
continue; // next opponent
|
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
|
#ifdef DEBUGATTACKS
|
||||||
DebugAI( String( "%s sees %s at gridno %d\n",pSoldier->GetName(),ExtMen[pOpponent->ubID].GetName(),pOpponent->sGridNo ) );
|
DebugAI( String( "%s sees %s at gridno %d\n",pSoldier->GetName(),ExtMen[pOpponent->ubID].GetName(),pOpponent->sGridNo ) );
|
||||||
#endif
|
#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 we don't have enough APs left to shoot even a snap-shot at this guy
|
||||||
if (ubMinAPcost > pSoldier->bActionPoints)
|
if (ubMinAPcost > pSoldier->bActionPoints)
|
||||||
@@ -274,7 +387,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
INT8 bDir;
|
INT8 bDir;
|
||||||
|
|
||||||
// must make sure that structure data can be added in the direction of the target
|
// 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...
|
// ATE: Only if we have a levelnode...
|
||||||
if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL )
|
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
|
iBestHitRate = 0; // reset best hit rate to minimum
|
||||||
|
|
||||||
//dnl ch69 130913 Hoping to optimize
|
//dnl ch69 130913 Hoping to optimize
|
||||||
// consider alternate holding mode and different scopes
|
// 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)
|
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)
|
if(Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
else
|
// sevenfm: hip firing allowed only for human bodytypes
|
||||||
{
|
if(!IS_MERC_BODY_TYPE(pSoldier))
|
||||||
if(Weapon[pSoldier->usAttackingWeapon].HeavyGun)
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || (pSoldier->bScopeMode >= USE_BEST_SCOPE && ObjList[pSoldier->bScopeMode] != NULL))
|
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
|
usTrueState = pSoldier->usAnimState; // because is used in CalculateRaiseGunCost, CalcAimingLevelsAvailableWithAP, CalculateTurningCost
|
||||||
iTrueLastTarget = pSoldier->sLastTarget;// because is used in MinAPsToShootOrStab
|
iTrueLastTarget = pSoldier->sLastTarget; // because is used in MinAPsToShootOrStab
|
||||||
|
|
||||||
// --------- Standing ---------
|
// --------- Standing ---------
|
||||||
ubStance = ANIM_STAND;
|
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);
|
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||||
if(sStanceAPcost)
|
if(sStanceAPcost)
|
||||||
@@ -334,9 +446,9 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
pSoldier->usAnimState = STANDING;
|
pSoldier->usAnimState = STANDING;
|
||||||
pSoldier->sLastTarget = NOWHERE;
|
pSoldier->sLastTarget = NOWHERE;
|
||||||
}
|
}
|
||||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
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(fAddingTurningCost && fAddingRaiseGunCost)//dnl ch71 180913
|
||||||
{
|
{
|
||||||
if(usRaiseGunCost > usTurningCost)
|
if(usRaiseGunCost > usTurningCost)
|
||||||
@@ -344,27 +456,31 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
else
|
else
|
||||||
usRaiseGunCost = 0;
|
usRaiseGunCost = 0;
|
||||||
}
|
}
|
||||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
|
ubRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||||
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
||||||
|
|
||||||
if(pSoldier->bActionPoints - ubMinAPcost >= 0)
|
if(pSoldier->bActionPoints - ubMinAPcost >= 0)
|
||||||
{
|
{
|
||||||
// calc next attack's minimum shooting cost (excludes readying & turning & raise gun)
|
// 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
|
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||||
gUnderFire.Clear();
|
gUnderFire.Clear();
|
||||||
gUnderFire.Enable();
|
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);
|
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||||
gUnderFire.Disable();
|
gUnderFire.Disable();
|
||||||
|
|
||||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
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);
|
ubChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, ubAimTime, AIM_SHOT_TORSO, bLevel, STANDING);
|
||||||
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
iHitRate = ubChanceToHit * (pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||||
// sevenfm: take into account CTGT for every stance
|
// sevenfm: take into account CTGT for every stance
|
||||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough ||
|
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough ||
|
||||||
(Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE) && ubChanceToHit > ubBestChanceToHit)// rather take best chance for throwing knives
|
(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;
|
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) )
|
if ( pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || ARMED_VEHICLE( pSoldier ) || (Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// --------- Crouched ---------
|
// --------- Crouched ---------
|
||||||
ubStance = ANIM_CROUCH;
|
ubStance = ANIM_CROUCH;
|
||||||
if(IsValidStance(pSoldier, ubStance))
|
// sevenfm: take into account direction
|
||||||
|
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, sTarget), ubStance))
|
||||||
{
|
{
|
||||||
// change stance then turn
|
// change stance then turn
|
||||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||||
@@ -399,9 +517,9 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
pSoldier->usAnimState = CROUCHING;
|
pSoldier->usAnimState = CROUCHING;
|
||||||
pSoldier->sLastTarget = NOWHERE;
|
pSoldier->sLastTarget = NOWHERE;
|
||||||
}
|
}
|
||||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
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(fAddingTurningCost && fAddingRaiseGunCost)//dnl ch71 180913
|
||||||
{
|
{
|
||||||
if(usRaiseGunCost > usTurningCost)
|
if(usRaiseGunCost > usTurningCost)
|
||||||
@@ -409,27 +527,30 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
else
|
else
|
||||||
usRaiseGunCost = 0;
|
usRaiseGunCost = 0;
|
||||||
}
|
}
|
||||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
|
ubRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||||
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
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
|
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||||
gUnderFire.Clear();
|
gUnderFire.Clear();
|
||||||
gUnderFire.Enable();
|
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);
|
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||||
gUnderFire.Disable();
|
gUnderFire.Disable();
|
||||||
|
|
||||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
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);
|
ubChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, ubAimTime, AIM_SHOT_TORSO, bLevel, CROUCHING);
|
||||||
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
iHitRate = ubChanceToHit * (pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||||
// sevenfm: take into account CTGT for every stance
|
// sevenfm: take into account CTGT for every stance
|
||||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough)
|
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
|
// 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)
|
gAnimControl[pSoldier->usAnimState].ubEndHeight > ANIM_PRONE)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -458,8 +579,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
|
|
||||||
// --------- Prone ---------
|
// --------- Prone ---------
|
||||||
ubStance = ANIM_PRONE;
|
ubStance = ANIM_PRONE;
|
||||||
//if(IsValidStance(pSoldier, ubStance))
|
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, sTarget), ubStance))
|
||||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, pOpponent->sGridNo), ubStance))
|
|
||||||
{
|
{
|
||||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||||
if (sStanceAPcost)
|
if (sStanceAPcost)
|
||||||
@@ -467,30 +587,33 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
pSoldier->usAnimState = PRONE;
|
pSoldier->usAnimState = PRONE;
|
||||||
pSoldier->sLastTarget = NOWHERE;
|
pSoldier->sLastTarget = NOWHERE;
|
||||||
}
|
}
|
||||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, pOpponent->sGridNo, 0);
|
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, sTarget, 0);
|
||||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
|
ubRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||||
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
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
|
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||||
gUnderFire.Clear();
|
gUnderFire.Clear();
|
||||||
gUnderFire.Enable();
|
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);
|
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||||
gUnderFire.Disable();
|
gUnderFire.Disable();
|
||||||
|
|
||||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
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);
|
ubChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, ubAimTime, AIM_SHOT_TORSO, bLevel, PRONE);
|
||||||
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
iHitRate = ubChanceToHit * (pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||||
// sevenfm: take into account CTGT for every stance
|
// sevenfm: take into account CTGT for every stance
|
||||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough)
|
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough)
|
||||||
{
|
{
|
||||||
@@ -524,14 +647,15 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
continue; // next opponent
|
continue; // next opponent
|
||||||
|
|
||||||
// really limit knife throwing so it doesn't look wrong
|
// 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
|
continue; // don't bother... next opponent
|
||||||
|
|
||||||
// calculate this opponent's threat value (factor in my cover from him)
|
// 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
|
// estimate the damage this shot would do to this opponent
|
||||||
iEstDamage = EstimateShotDamage(pSoldier,pOpponent,ubBestChanceToHit);
|
iEstDamage = EstimateShotDamage(pSoldier, pOpponent, ubBestChanceToHit);
|
||||||
//NumMessage("SHOT EstDamage = ",iEstDamage);
|
//NumMessage("SHOT EstDamage = ",iEstDamage);
|
||||||
|
|
||||||
// calculate the combined "attack value" for this opponent
|
// calculate the combined "attack value" for this opponent
|
||||||
@@ -540,6 +664,13 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
iAttackValue = (iEstDamage * iBestHitRate * ubChanceToReallyHit * iThreatValue) / 1000;
|
iAttackValue = (iEstDamage * iBestHitRate * ubChanceToReallyHit * iThreatValue) / 1000;
|
||||||
//NumMessage("SHOT AttackValue = ",iAttackValue / 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
|
// special stuff for assassins to ignore militia more
|
||||||
if ( pSoldier->IsAssassin() && pOpponent->bTeam == MILITIA_TEAM )
|
if ( pSoldier->IsAssassin() && pOpponent->bTeam == MILITIA_TEAM )
|
||||||
{
|
{
|
||||||
@@ -624,8 +755,8 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
|||||||
pBestShot->ubOpponent = pOpponent->ubID;
|
pBestShot->ubOpponent = pOpponent->ubID;
|
||||||
pBestShot->ubAimTime = ubBestAimTime;
|
pBestShot->ubAimTime = ubBestAimTime;
|
||||||
pBestShot->ubChanceToReallyHit = ubChanceToReallyHit;
|
pBestShot->ubChanceToReallyHit = ubChanceToReallyHit;
|
||||||
pBestShot->sTarget = pOpponent->sGridNo;
|
pBestShot->sTarget = sTarget;
|
||||||
pBestShot->bTargetLevel = pOpponent->pathing.bLevel;
|
pBestShot->bTargetLevel = bLevel;
|
||||||
pBestShot->iAttackValue = iAttackValue;
|
pBestShot->iAttackValue = iAttackValue;
|
||||||
pBestShot->ubAPCost = sBestAPcost;
|
pBestShot->ubAPCost = sBestAPcost;
|
||||||
pBestShot->ubStance = ubBestStance;
|
pBestShot->ubStance = ubBestStance;
|
||||||
|
|||||||
+96
-42
@@ -2910,7 +2910,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//SUPPRESSION FIRE
|
//SUPPRESSION FIRE
|
||||||
CheckIfShotPossible(pSoldier,&BestShot); //WarmSteel - No longer returns 0 when there IS actually a chance to hit.
|
CheckIfShotPossible(pSoldier, &BestShot); //WarmSteel - No longer returns 0 when there IS actually a chance to hit.
|
||||||
|
|
||||||
// sevenfm: check that we have a clip to reload
|
// sevenfm: check that we have a clip to reload
|
||||||
BOOLEAN fExtraClip = FALSE;
|
BOOLEAN fExtraClip = FALSE;
|
||||||
@@ -2929,77 +2929,131 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
|||||||
|
|
||||||
// CHRISL: Changed from a simple flag to two externalized values for more modder control over AI suppression
|
// CHRISL: Changed from a simple flag to two externalized values for more modder control over AI suppression
|
||||||
// WarmSteel - Don't *always* try to suppress when under 50 CTH
|
// WarmSteel - Don't *always* try to suppress when under 50 CTH
|
||||||
if( BestShot.bWeaponIn != -1
|
if (BestShot.ubPossible &&
|
||||||
&& BestShot.ubPossible
|
BestShot.bWeaponIn != -1 &&
|
||||||
&& GetMagSize(&pSoldier->inv[BestShot.bWeaponIn]) >= gGameExternalOptions.ubAISuppressionMinimumMagSize
|
// check valid target
|
||||||
&& pSoldier->inv[BestShot.bWeaponIn][0]->data.gun.ubGunShotsLeft >= gGameExternalOptions.ubAISuppressionMinimumAmmo
|
!TileIsOutOfBounds(BestShot.sTarget) &&
|
||||||
//&& BestShot.ubChanceToReallyHit < (INT16)(PreRandom(50))
|
BestShot.ubOpponent != NOBODY &&
|
||||||
//&& Menptr[BestShot.ubOpponent].pathing.bLevel == 0
|
MercPtrs[BestShot.ubOpponent] &&
|
||||||
&& pSoldier->aiData.bOrders != SNIPER &&
|
// check weapon/ammo requirements
|
||||||
BestShot.ubFriendlyFireChance < 5 &&
|
IsGunAutofireCapable(&pSoldier->inv[BestShot.bWeaponIn]) &&
|
||||||
!CoweringShockLevel(MercPtrs[BestShot.ubOpponent]) &&
|
GetMagSize(&pSoldier->inv[BestShot.bWeaponIn]) >= gGameExternalOptions.ubAISuppressionMinimumMagSize &&
|
||||||
|
pSoldier->inv[BestShot.bWeaponIn][0]->data.gun.ubGunShotsLeft >= gGameExternalOptions.ubAISuppressionMinimumAmmo &&
|
||||||
|
// check soldier and weapon
|
||||||
|
pSoldier->aiData.bOrders != SNIPER &&
|
||||||
|
BestShot.ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE &&
|
||||||
!AICheckIsFlanking(pSoldier) &&
|
!AICheckIsFlanking(pSoldier) &&
|
||||||
LocationToLocationLineOfSightTest( pSoldier->sGridNo, pSoldier->pathing.bLevel, MercPtrs[BestShot.ubOpponent]->sGridNo, MercPtrs[BestShot.ubOpponent]->pathing.bLevel, TRUE, NO_DISTANCE_LIMIT) &&
|
// reduce chance to shoot if target is beyond weapon range
|
||||||
//Weapon[pSoldier->inv[BestShot.bWeaponIn].usItem].ubWeaponType == GUN_LMG ) && //Weapon[usInHand].ubWeaponClass == MGCLASS
|
(AICheckIsMachinegunner(pSoldier) ||
|
||||||
(fExtraClip || pSoldier->inv[BestShot.bWeaponIn][0]->data.gun.ubGunShotsLeft > gGameExternalOptions.ubAISuppressionMinimumMagSize) )
|
ARMED_VEHICLE(pSoldier) ||
|
||||||
|
AnyCoverAtSpot(pSoldier, pSoldier->sGridNo) ||
|
||||||
|
pSoldier->aiData.bUnderFire && (pSoldier->ubPreviousAttackerID == BestShot.ubOpponent || pSoldier->ubNextToPreviousAttackerID == BestShot.ubOpponent || MercPtrs[BestShot.ubOpponent]->sLastTarget == pSoldier->sGridNo) || // return fire
|
||||||
|
Chance(100 * (GunRange(&pSoldier->inv[BestShot.bWeaponIn], pSoldier) / CELL_X_SIZE) / PythSpacesAway(pSoldier->sGridNo, BestShot.sTarget))) &&
|
||||||
|
// check that we have spare ammo
|
||||||
|
(fExtraClip || pSoldier->inv[BestShot.bWeaponIn][0]->data.gun.ubGunShotsLeft >= gGameExternalOptions.ubAISuppressionMinimumMagSize) )
|
||||||
{
|
{
|
||||||
// then do it!
|
// then do it!
|
||||||
|
|
||||||
// if necessary, swap the usItem from holster into the hand position
|
// if necessary, swap the usItem from holster into the hand position
|
||||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: suppression fire possible!");
|
DebugAI(AI_MSG_INFO, pSoldier, String("suppression fire possible! target %d level %d aim %d", BestShot.sTarget, BestShot.bTargetLevel, BestShot.ubAimTime));
|
||||||
|
|
||||||
if (BestShot.bWeaponIn != HANDPOS)
|
if (BestShot.bWeaponIn != HANDPOS)
|
||||||
{
|
{
|
||||||
RearrangePocket(pSoldier,HANDPOS,BestShot.bWeaponIn,FOREVER);
|
DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"));
|
||||||
|
RearrangePocket(pSoldier, HANDPOS, BestShot.bWeaponIn, FOREVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
pSoldier->aiData.usActionData = BestShot.sTarget;
|
pSoldier->bTargetLevel = BestShot.bTargetLevel;
|
||||||
pSoldier->bTargetLevel = BestShot.bTargetLevel;
|
pSoldier->aiData.bAimTime = BestShot.ubAimTime;
|
||||||
pSoldier->aiData.bAimTime = 0;
|
pSoldier->bDoAutofire = 0;
|
||||||
pSoldier->bDoAutofire = 0;
|
pSoldier->bDoBurst = 1;
|
||||||
pSoldier->bDoBurst = 1;
|
pSoldier->bScopeMode = BestShot.bScopeMode;
|
||||||
|
|
||||||
INT16 ubBurstAPs = 0;
|
INT16 ubBurstAPs = 0;
|
||||||
FLOAT dTotalRecoil = 0;
|
FLOAT dTotalRecoil = 0;
|
||||||
|
INT32 sActualAimAP;
|
||||||
|
UINT8 ubAutoPenalty;
|
||||||
|
INT16 sReserveAP = GetAPsProne(pSoldier, TRUE);
|
||||||
|
UINT8 ubMinAuto = 5;
|
||||||
|
|
||||||
if(UsingNewCTHSystem() == true)
|
if (BestShot.ubAimTime > 0 &&
|
||||||
|
!UsingNewCTHSystem() &&
|
||||||
|
Chance((100 - BestShot.ubChanceToReallyHit) * (100 - BestShot.ubChanceToReallyHit) / 100))
|
||||||
{
|
{
|
||||||
do
|
DebugAI(AI_MSG_INFO, pSoldier, String("set ubAimTime = 0 for OCTH suppression"));
|
||||||
{
|
BestShot.ubAimTime = 0;
|
||||||
pSoldier->bDoAutofire++;
|
|
||||||
dTotalRecoil += AICalcRecoilForShot( pSoldier, &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire );
|
|
||||||
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
|
|
||||||
}
|
|
||||||
while( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs && pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire
|
|
||||||
&& dTotalRecoil <= 10.0f );
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// reserve APs to hide if no cover or enemy is close
|
||||||
|
if (!AnyCoverAtSpot(pSoldier, pSoldier->sGridNo) || PythSpacesAway(pSoldier->sGridNo, BestShot.sTarget) < DAY_VISION_RANGE / 2)
|
||||||
|
{
|
||||||
|
sReserveAP = APBPConstants[AP_MINIMUM] / 2;
|
||||||
|
}
|
||||||
|
if (PythSpacesAway(pSoldier->sGridNo, BestShot.sTarget) > DAY_VISION_RANGE || AnyCoverAtSpot(pSoldier, pSoldier->sGridNo) || pSoldier->aiData.bUnderFire)
|
||||||
|
{
|
||||||
|
ubMinAuto *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
sActualAimAP = CalcAPCostForAiming(pSoldier, BestShot.sTarget, (INT8)pSoldier->aiData.bAimTime);
|
||||||
|
|
||||||
|
if (UsingNewCTHSystem() == true)
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
pSoldier->bDoAutofire++;
|
pSoldier->bDoAutofire++;
|
||||||
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
|
dTotalRecoil += AICalcRecoilForShot(pSoldier, &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire);
|
||||||
}
|
ubBurstAPs = CalcAPsToAutofire(pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier);
|
||||||
while( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs &&
|
} while (pSoldier->bActionPoints >= BestShot.ubAPCost + sActualAimAP + ubBurstAPs + sReserveAP &&
|
||||||
pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire &&
|
pSoldier->inv[pSoldier->ubAttackingHand][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire &&
|
||||||
GetAutoPenalty(&pSoldier->inv[ pSoldier->ubAttackingHand ], gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE)*pSoldier->bDoAutofire <= 80 );
|
pSoldier->bDoAutofire <= 30 &&
|
||||||
|
(dTotalRecoil <= 20.0f || pSoldier->bDoAutofire < ubMinAuto));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ubAutoPenalty = GetAutoPenalty(&pSoldier->inv[pSoldier->ubAttackingHand], gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_PRONE);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
pSoldier->bDoAutofire++;
|
||||||
|
ubBurstAPs = CalcAPsToAutofire(pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier);
|
||||||
|
} while (pSoldier->bActionPoints >= BestShot.ubAPCost + sActualAimAP + ubBurstAPs + sReserveAP &&
|
||||||
|
pSoldier->inv[pSoldier->ubAttackingHand][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire &&
|
||||||
|
pSoldier->bDoAutofire <= 30 &&
|
||||||
|
(ubAutoPenalty * pSoldier->bDoAutofire <= 80 || pSoldier->bDoAutofire < ubMinAuto));
|
||||||
}
|
}
|
||||||
|
|
||||||
pSoldier->bDoAutofire--;
|
pSoldier->bDoAutofire--;
|
||||||
|
|
||||||
// Make sure we decided to fire at least one shot!
|
// Make sure we decided to fire at least one shot!
|
||||||
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
|
ubBurstAPs = CalcAPsToAutofire(pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier);
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("autofire shots %d APcost %d burst AP %d aimtime %d reserve AP %d", pSoldier->bDoAutofire, BestShot.ubAPCost, ubBurstAPs, sActualAimAP, sReserveAP));
|
||||||
|
|
||||||
// minimum 5 bullets
|
// minimum 3 bullets
|
||||||
if (pSoldier->bDoAutofire >= 5 && pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs )
|
if (pSoldier->bDoAutofire >= 3 && pSoldier->bActionPoints >= BestShot.ubAPCost + sActualAimAP + ubBurstAPs + sReserveAP)
|
||||||
{
|
{
|
||||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[ MSG113_SUPPRESSIONFIRE ] );
|
if (gAnimControl[pSoldier->usAnimState].ubEndHeight != BestShot.ubStance &&
|
||||||
return( AI_ACTION_FIRE_GUN );
|
IsValidStance(pSoldier, BestShot.ubStance))
|
||||||
|
{
|
||||||
|
pSoldier->aiData.bNextAction = AI_ACTION_FIRE_GUN;
|
||||||
|
pSoldier->aiData.usNextActionData = BestShot.sTarget;
|
||||||
|
pSoldier->aiData.bNextTargetLevel = BestShot.bTargetLevel;
|
||||||
|
pSoldier->aiData.usActionData = BestShot.ubStance;
|
||||||
|
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("Change stance before shooting"));
|
||||||
|
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_SUPPRESSIONFIRE]);
|
||||||
|
return(AI_ACTION_CHANGE_STANCE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pSoldier->aiData.usActionData = BestShot.sTarget;
|
||||||
|
|
||||||
|
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_SUPPRESSIONFIRE]);
|
||||||
|
return(AI_ACTION_FIRE_GUN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pSoldier->bDoBurst = 0;
|
pSoldier->bDoBurst = 0;
|
||||||
pSoldier->bDoAutofire = 0;
|
pSoldier->bDoAutofire = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// suppression not possible, do something else
|
// suppression not possible, do something else
|
||||||
|
|||||||
+2
-3
@@ -330,15 +330,14 @@ UINT8 TerrainDensity(INT32 sSpot, INT8 bLevel, UINT8 ubDistance, BOOLEAN fGrass)
|
|||||||
BOOLEAN FindNearbyExplosiveStructure(INT32 sSpot, INT8 bLevel);
|
BOOLEAN FindNearbyExplosiveStructure(INT32 sSpot, INT8 bLevel);
|
||||||
INT16 DistanceToClosestActiveOpponent(SOLDIERTYPE *pSoldier, INT32 sSpot);
|
INT16 DistanceToClosestActiveOpponent(SOLDIERTYPE *pSoldier, INT32 sSpot);
|
||||||
BOOLEAN ValidOpponent(SOLDIERTYPE* pSoldier, SOLDIERTYPE* pOpponent);
|
BOOLEAN ValidOpponent(SOLDIERTYPE* pSoldier, SOLDIERTYPE* pOpponent);
|
||||||
|
|
||||||
UINT8 CountSeenEnemiesLastTurn( SOLDIERTYPE *pSoldier );
|
UINT8 CountSeenEnemiesLastTurn( SOLDIERTYPE *pSoldier );
|
||||||
|
|
||||||
BOOLEAN FindObstacleNearSpot(INT32 sSpot, INT8 bLevel);
|
BOOLEAN FindObstacleNearSpot(INT32 sSpot, INT8 bLevel);
|
||||||
|
|
||||||
BOOLEAN CheckNPCDestination(SOLDIERTYPE *pSoldier, INT32 sGridNo);
|
BOOLEAN CheckNPCDestination(SOLDIERTYPE *pSoldier, INT32 sGridNo);
|
||||||
UINT8 SpotDangerLevel(SOLDIERTYPE *pSoldier, INT32 sGridNo);
|
UINT8 SpotDangerLevel(SOLDIERTYPE *pSoldier, INT32 sGridNo);
|
||||||
BOOLEAN AllowDeepWaterFlanking(SOLDIERTYPE *pSoldier);
|
BOOLEAN AllowDeepWaterFlanking(SOLDIERTYPE *pSoldier);
|
||||||
BOOLEAN AICheckUnderground(void);
|
BOOLEAN AICheckUnderground(void);
|
||||||
|
INT32 RandomizeLocation(INT32 sSpot, INT8 bLevel, UINT8 ubTimes, SOLDIERTYPE *pSightSoldier);
|
||||||
|
INT32 RandomizeOpponentLocation(INT32 sSpot, SOLDIERTYPE *pOpponent, INT16 sMaxDistance);
|
||||||
|
|
||||||
BOOLEAN CorpseWarning(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel);
|
BOOLEAN CorpseWarning(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel);
|
||||||
BOOLEAN CorpseEnemyTeam(ROTTING_CORPSE *pCorpse);
|
BOOLEAN CorpseEnemyTeam(ROTTING_CORPSE *pCorpse);
|
||||||
|
|||||||
Reference in New Issue
Block a user