Hooks
static bool BeforeCvTacticalAI_FindUnitsWithinStrikingDistance(CvTacticalAI* self, CvPlot* pTarget, int iNumTurnsAway, int iPreferredDamageLevel, bool bNoRangedUnits, bool bNavalOnly, bool bMustMoveThrough, bool bIncludeBlockedUnits, bool bWillPillage, bool bTargetUndefended, bool& result);static void AfterCvTacticalAI_FindUnitsWithinStrikingDistance(CvTacticalAI* self, CvPlot* pTarget, int iNumTurnsAway, int iPreferredDamageLevel, bool bNoRangedUnits, bool bNavalOnly, bool bMustMoveThrough, bool bIncludeBlockedUnits, bool bWillPillage, bool bTargetUndefended, bool& result);static bool ReplaceCvTacticalAI_FindUnitsWithinStrikingDistance(bool (*original)(CvTacticalAI*, CvPlot*, int, int, bool, bool, bool, bool, bool, bool), CvTacticalAI* self, CvPlot* pTarget, int iNumTurnsAway, int iPreferredDamageLevel, bool bNoRangedUnits, bool bNavalOnly, bool bMustMoveThrough, bool bIncludeBlockedUnits, bool bWillPillage, bool bTargetUndefended);CivRegisterBefore<CivHooks::CvTacticalAI_FindUnitsWithinStrikingDistance>(host, &BeforeCvTacticalAI_FindUnitsWithinStrikingDistance);CivRegisterAfter<CivHooks::CvTacticalAI_FindUnitsWithinStrikingDistance>(host, &AfterCvTacticalAI_FindUnitsWithinStrikingDistance);CivRegisterReplace<CivHooks::CvTacticalAI_FindUnitsWithinStrikingDistance>(host, &ReplaceCvTacticalAI_FindUnitsWithinStrikingDistance);
Code
9054/// Fills m_CurrentMoveUnits with all units within X turns of a target (returns TRUE if 1 or more found)9055bool CvTacticalAI::FindUnitsWithinStrikingDistance(CvPlot* pTarget, int iNumTurnsAway, int iPreferredDamageLevel, bool bNoRangedUnits, bool bNavalOnly, bool bMustMoveThrough, bool bIncludeBlockedUnits, bool bWillPillage, bool bTargetUndefended)9056{9057 list<int>::iterator it;9058 UnitHandle pLoopUnit;90599060 bool rtnValue = false;9061 m_CurrentMoveUnits.clear();90629063 bool bIsCityTarget = pTarget->getPlotCity() != NULL;9064#if defined(MOD_AI_SMART_V3)9065 bool bAirUnitsAdded = false;9066#endif90679068 // Loop through all units available to tactical AI this turn9069 for(it = m_CurrentTurnUnits.begin(); it != m_CurrentTurnUnits.end(); it++)9070 {9071 pLoopUnit = m_pPlayer->getUnit(*it);9072 if(pLoopUnit)9073 {9074 if(!bNavalOnly || pLoopUnit->getDomainType() == DOMAIN_SEA)9075 {9076 // don't use non-combat units9077 if(!pLoopUnit->IsCanAttack())9078 {9079 continue;9080 }90819082 if(pLoopUnit->isOutOfAttacks())9083 {9084 continue;9085 }90869087 if (!bIsCityTarget && pLoopUnit->IsCityAttackOnly())9088 {9089 continue;9090 }90919092 if (bWillPillage && !pLoopUnit->canPillage(pTarget))9093 {9094 continue;9095 }9096 9097#if defined(MOD_AI_SMART_V3)9098 //Don't pull melee units out of camps to attack.9099 if(MOD_AI_SMART_V3 && pLoopUnit->isBarbarian() && !pLoopUnit->isRanged() && (pLoopUnit->plot()->getImprovementType() == GC.getBARBARIAN_CAMP_IMPROVEMENT()))9100 {9101 continue;9102 }91039104 //AMS: To effectively skip all ranged units...9105 if (MOD_AI_SMART_V3 && bNoRangedUnits && pLoopUnit->IsCanAttackRanged())9106 {9107 continue;9108 }9109#endif91109111 int iAttackStrength = pLoopUnit->GetMaxAttackStrength(NULL, NULL, NULL);91129113 // Looking for damaged units? If so, recalculate attack strength9114 if (iPreferredDamageLevel > 0)9115 {9116 int iDamage = pLoopUnit->getDamage();9117 if (iDamage > iPreferredDamageLevel)9118 {9119 iAttackStrength *= 3;9120 }9121 }91229123 int iTurnsCalculated = -1; // If CanReachInXTurns does an actual pathfind, save the result so we don't just do the same one again.9124 if (bTargetUndefended && CanReachInXTurns(pLoopUnit, pTarget, iNumTurnsAway, false /*bIgnoreUnits*/, &iTurnsCalculated))9125 {9126 CvTacticalUnit unit;9127 unit.SetID(pLoopUnit->GetID());9128 unit.SetAttackStrength(iAttackStrength / 2);9129 unit.SetHealthPercent(pLoopUnit->GetCurrHitPoints(), pLoopUnit->GetMaxHitPoints());9130 m_CurrentMoveUnits.push_back(unit);9131 rtnValue = true;9132 }91339134 else if(!bNoRangedUnits && !bWillPillage && pLoopUnit->IsCanAttackRanged())9135 {9136 // Don't use air units for air strikes if at or below half health9137#if defined(MOD_UNITS_MAX_HP)9138 if (pLoopUnit->getDomainType() != DOMAIN_AIR || (pLoopUnit->getDamage() * 2) < pLoopUnit->GetMaxHitPoints())9139#else9140 if (pLoopUnit->getDomainType() != DOMAIN_AIR || (pLoopUnit->getDamage() * 2) < GC.getMAX_HIT_POINTS())9141#endif9142 {9143#if defined(MOD_AI_SMART_V3)9144 if (MOD_AI_SMART_V3)9145 {9146 // AMS: Are we in range or could be in range with movement?9147 int getDistance = plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), pTarget->getX(), pTarget->getY());9148 if(getDistance < pLoopUnit-> GetRangePlusMoveToshot())9149 {9150 // Will we do any damage9151 if(IsExpectedToDamageWithRangedAttack(pLoopUnit, pTarget))9152 {9153 CvTacticalUnit unit;9154 unit.SetID(pLoopUnit->GetID());9155 unit.SetAttackStrength(100 * pLoopUnit->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, true, true));9156 unit.SetHealthPercent(100, 100); // Don't take damage from bombarding, so show as fully healthy9157 m_CurrentMoveUnits.push_back(unit);9158 rtnValue = true;91599160 if (pLoopUnit->getDomainType() == DOMAIN_AIR)9161 {9162 bAirUnitsAdded = true;9163 }9164 }9165 }9166 }9167 else9168 {9169#endif9170 // Are we in range?9171 if(plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), pTarget->getX(), pTarget->getY()) <= pLoopUnit->GetRange())9172 {9173 // Do we have LOS to the target?9174 if(pLoopUnit->canEverRangeStrikeAt(pTarget->getX(), pTarget->getY()))9175 {9176 // Will we do any damage9177 if(IsExpectedToDamageWithRangedAttack(pLoopUnit, pTarget))9178 {9179 CvTacticalUnit unit;9180 unit.SetID(pLoopUnit->GetID());91819182 // Want ranged units to attack first, so inflate this9183 unit.SetAttackStrength(100 * pLoopUnit->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, true, true));9184 unit.SetHealthPercent(100, 100); // Don't take damage from bombarding, so show as fully healthy9185 m_CurrentMoveUnits.push_back(unit);9186 rtnValue = true;9187 }9188 }9189 }9190#if defined(MOD_AI_SMART_V3)9191 }9192#endif9193 }9194 }9195 else9196 {9197 if (bMustMoveThrough && (iTurnsCalculated == 0 || (iTurnsCalculated == -1 && CanReachInXTurns(pLoopUnit, pTarget, 0, false /*bIgnoreUnits*/, &iTurnsCalculated))))9198 {9199 CvTacticalUnit unit;9200 unit.SetID(pLoopUnit->GetID());9201 unit.SetAttackStrength(iAttackStrength);9202 unit.SetHealthPercent(pLoopUnit->GetCurrHitPoints(), pLoopUnit->GetMaxHitPoints());9203 m_CurrentMoveUnits.push_back(unit);9204 rtnValue = true;9205 }92069207 else if ( (iTurnsCalculated != -1 && iTurnsCalculated <= iNumTurnsAway) || (iTurnsCalculated == -1 && CanReachInXTurns(pLoopUnit, pTarget, iNumTurnsAway, false /*bIgnoreUnits*/)) )9208 {9209 CvTacticalUnit unit;9210 unit.SetID(pLoopUnit->GetID());9211 unit.SetAttackStrength(iAttackStrength);9212 unit.SetHealthPercent(pLoopUnit->GetCurrHitPoints(), pLoopUnit->GetMaxHitPoints());9213 m_CurrentMoveUnits.push_back(unit);9214 rtnValue = true;9215 }92169217 // Units that can make it when others get out of the way are also potentially useful, but give them half priority so they bring up the rear9218 else if (bIncludeBlockedUnits && CanReachInXTurns(pLoopUnit, pTarget, iNumTurnsAway, true /*bIgnoreUnits*/))9219 {9220 CvTacticalUnit unit;9221 unit.SetID(pLoopUnit->GetID());9222 unit.SetAttackStrength(iAttackStrength / 2);9223 unit.SetHealthPercent(pLoopUnit->GetCurrHitPoints(), pLoopUnit->GetMaxHitPoints());9224 m_CurrentMoveUnits.push_back(unit);9225 rtnValue = true;9226 }9227 }9228 }9229 }9230 }92319232#if defined(MOD_AI_SMART_V3)9233 if (MOD_AI_SMART_V3)9234 {9235 //AMS: As we have air units on the attack targets we should also check possible air sweeps9236 if (bAirUnitsAdded)9237 FindAirUnitsToAirSweep(pTarget);9238 else9239 m_CurrentAirUnits.clear();9240 }9241#endif92429243 // Now sort them in the order we'd like them to attack9244 std::stable_sort(m_CurrentMoveUnits.begin(), m_CurrentMoveUnits.end());92459246 return rtnValue;9247}