diff --git a/GameSettings.cpp b/GameSettings.cpp index aafa0e5d0..5a2102dfb 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -2306,6 +2306,7 @@ void LoadGameExternalOptions() gGameExternalOptions.fAIPathTweaks = iniReader.ReadBoolean("Tactical AI Settings", "AI_PATH_TWEAKS", TRUE); gGameExternalOptions.fAIDecisionInfo = iniReader.ReadBoolean("Tactical AI Settings", "AI_DECISION_INFO", FALSE); gGameExternalOptions.fAIShootUnseen = iniReader.ReadBoolean("Tactical AI Settings", "AI_SHOOT_UNSEEN", FALSE); + gGameExternalOptions.fAISafeSuppression = iniReader.ReadBoolean("Tactical AI Settings", "AI_SAFE_SUPPRESSION", TRUE); } diff --git a/GameSettings.h b/GameSettings.h index b0d699db1..3bb1871d4 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1566,6 +1566,7 @@ typedef struct BOOLEAN fAIPathTweaks; BOOLEAN fAIDecisionInfo; BOOLEAN fAIShootUnseen; + BOOLEAN fAISafeSuppression; } GAME_EXTERNAL_OPTIONS; diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index 36c9bbc84..7d0222c65 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -6138,3 +6138,41 @@ UINT8 CountPublicKnownEnemies(SOLDIERTYPE *pSoldier) return ubNum; } + +// sevenfm: check if suppression is possible (count friends in the fire direction) +BOOLEAN CheckSuppressionDirection(SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bTargetLevel) +{ + SOLDIERTYPE * pFriend; + UINT8 ubShootingDir; + UINT32 uiLoop; + + CHECKF(pSoldier); + CHECKF(!TileIsOutOfBounds(sTargetGridNo)); + + ubShootingDir = AIDirection(pSoldier->sGridNo, sTargetGridNo); + + for (uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop) + { + pFriend = MercSlots[uiLoop]; + + if (pFriend && + pFriend != pSoldier && + pFriend->bActive && + pFriend->bVisible == TRUE && + pFriend->stats.bLife >= OKLIFE && + (pFriend->bSide == pSoldier->bSide || CONSIDERED_NEUTRAL(pSoldier, pFriend)) && + (pFriend->pathing.bLevel == pSoldier->pathing.bLevel || pFriend->pathing.bLevel == bTargetLevel) && + ubShootingDir == AIDirection(pSoldier->sGridNo, pFriend->sGridNo) && + PythSpacesAway(pSoldier->sGridNo, pFriend->sGridNo) > 1 && + PythSpacesAway(pSoldier->sGridNo, pFriend->sGridNo) < 2 * TACTICAL_RANGE && + (gAnimControl[pFriend->usAnimState].ubHeight == ANIM_STAND || gGameExternalOptions.fAllowTargetHeadAndLegIfProne) && + //!pFriend->IsCowering() && + AISoldierToSoldierChanceToGetThrough(pSoldier, pFriend) > 25) + //LocationToLocationLineOfSightTest(pSoldier->sGridNo, pSoldier->pathing.bLevel, pFriend->sGridNo, pFriend->pathing.bLevel, TRUE, NO_DISTANCE_LIMIT)) + { + return FALSE; + } + } + + return TRUE; +} diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index af5f1e0e4..597cde532 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -3006,6 +3006,8 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->aiData.bOrders != SNIPER && BestShot.ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE && !AICheckIsFlanking(pSoldier) && + (Chance(BestShot.ubChanceToReallyHit) || Chance(gGameExternalOptions.sSuppressionEffectiveness)) && + (!gGameExternalOptions.fAISafeSuppression || CheckSuppressionDirection(pSoldier, BestShot.sTarget, BestShot.bTargetLevel)) && !pSoldier->RetreatCounterValue() && // check cover (fAnyCover || // safe position @@ -6152,12 +6154,20 @@ L_NEWAIM: } pSoldier->bDoAutofire--; - if(!UsingNewCTHSystem() && pSoldier->bDoAutofire < 3 && pSoldier->aiData.bAimTime > 0 && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft >= 3)//dnl ch69 130913 let try increase autofire rate for aim cost + + //dnl ch69 130913 let try increase autofire rate for aim cost + if(!UsingNewCTHSystem() && + pSoldier->bDoAutofire < 3 && + pSoldier->aiData.bAimTime > 0 && + pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft >= 3 && + Chance(gGameExternalOptions.sSuppressionEffectiveness) && + (!gGameExternalOptions.fAISafeSuppression || CheckSuppressionDirection(pSoldier, BestShot.sTarget, BestShot.bTargetLevel))) { pSoldier->aiData.bAimTime--; sActualAimAP = CalcAPCostForAiming(pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime); goto L_NEWAIM; } + if (pSoldier->bDoAutofire > 0) { ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire, pSoldier ); @@ -6218,18 +6228,27 @@ L_NEWAIM: //dnl ch69 140913 return aiming for autofire with halfautofire fix pSoldier->bDoBurst = 1; INT16 ubHalfBurstAPs = 256; - if(pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft < 4) + if (pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft < 4) + { iChance = 0; + } else { - ubHalfBurstAPs = CalcAPsToAutofire(pSoldier->CalcActionPoints(), &pSoldier->inv[BestAttack.bWeaponIn], 4, pSoldier); - if(Weapon[pSoldier->inv[BestAttack.bWeaponIn].usItem].NoSemiAuto) - iChance = 35; + ubHalfBurstAPs = CalcAPsToAutofire(pSoldier->CalcActionPoints(), &pSoldier->inv[BestAttack.bWeaponIn], 2, pSoldier); + + if (!CheckSuppressionDirection(pSoldier, BestAttack.sTarget, BestAttack.bTargetLevel)) + iChance = 100; + else + iChance = BestAttack.ubChanceToReallyHit / 2; + + if (Weapon[pSoldier->inv[BestAttack.bWeaponIn].usItem].NoSemiAuto || pSoldier->aiData.bOppCnt > 1) + iChance += (100 - iChance) / 2; } - if((INT32)PreRandom(100) < iChance && pSoldier->bActionPoints > (2 * BestAttack.ubAPCost + ubHalfBurstAPs + sActualAimAP)) + + if(Chance(iChance) && pSoldier->bActionPoints >= (2 * BestAttack.ubAPCost + ubHalfBurstAPs + sActualAimAP)) { // Try short autofire to enhance chance of hitting - pSoldier->bDoAutofire = 4; + pSoldier->bDoAutofire = 2; BestAttack.ubAPCost += ubHalfBurstAPs + sActualAimAP; } else diff --git a/TacticalAI/ai.h b/TacticalAI/ai.h index 7bd8e8c81..3951c23ef 100644 --- a/TacticalAI/ai.h +++ b/TacticalAI/ai.h @@ -351,6 +351,8 @@ BOOLEAN CorpseMilitiaTeam(ROTTING_CORPSE *pCorpse); BOOLEAN NorthSpot(INT32 sSpot, INT8 bLevel); BOOLEAN SoldierAI(SOLDIERTYPE *pSoldier); +BOOLEAN CheckSuppressionDirection(SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bTargetLevel); + UINT8 AIDirection(INT32 sSpot1, INT32 sSpot2); BOOLEAN AIGunScoped(SOLDIERTYPE *pSoldier);