// Can this tile be attacked by an enemy unit or city next turn? bool CvDangerPlotContents::IsUnderImmediateThreat(const CvUnit* pUnit) { if (!m_pPlot || !pUnit) return false; // Air units operate off of intercepts instead of units/cities that can attack them if (pUnit->getDomainType() == DOMAIN_AIR) { if (pUnit->GetBestInterceptor(*m_pPlot)) { return true; } } else { // Cities in range for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (pCity && pCity->getTeam() != pUnit->getTeam()) { return true; } } // Units in range for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if (pUnit && !pUnit->isDelayedDeath() && !pUnit->IsDead()) { return true; } } // Citadel etc in range if (GetDamageFromFeatures(pUnit->getOwner()) > 0) { return true; } } // Terrain damage is greater than heal rate int iMinimumDamage = m_bFlatPlotDamage ? m_pPlot->getTurnDamage(pUnit->ignoreTerrainDamage(), pUnit->ignoreFeatureDamage(), pUnit->extraTerrainDamage(), pUnit->extraFeatureDamage()) : 0; if (pUnit->canHeal(m_pPlot)) { iMinimumDamage -= pUnit->healRate(m_pPlot); } return (iMinimumDamage > 0); }
// Can this tile be attacked by an enemy unit or city next turn? bool CvDangerPlotContents::IsUnderImmediateThreat(PlayerTypes ePlayer) { if (!m_pPlot) return false; // Terrain damage if (m_bFlatPlotDamage) return true; // Cities in range for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (pCity && pCity->getTeam() != GET_PLAYER(ePlayer).getTeam()) { return true; } } // Units in range for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if (pUnit && !pUnit->isDelayedDeath() && !pUnit->IsDead()) { return true; } } // Citadel etc in range if (GetDamageFromFeatures(ePlayer) > 0) { return true; } return false; }
// Get the maximum damage unit could receive at this plot in the next turn (update this with CvUnitCombat changes!) int CvDangerPlotContents::GetDanger(const CvUnit* pUnit, AirActionType iAirAction) { if (!m_pPlot || !pUnit) return 0; // Air units only take damage from interceptions if (pUnit->getDomainType() == DOMAIN_AIR) return GetAirUnitDamage(pUnit, iAirAction); //simple caching for speedup SUnitStats unitStats(pUnit); if (unitStats==m_lastUnit) return m_lastResult; //otherwise calculate from scratch int iPlotDamage = 0; CvCity* pFriendlyCity = NULL; if ( m_pPlot->isFriendlyCity(*pUnit,true) ) pFriendlyCity = m_pPlot->getPlotCity(); // Civilians can be captured - unless they would need to be embarked on this plot if (!pUnit->IsCombatUnit() && pUnit->isNativeDomain(m_pPlot)) { // If plot contains an enemy unit, mark it as max danger if (m_pPlot->getBestDefender(NO_PLAYER, pUnit->getOwner(), NULL, true)) { return MAX_INT; } for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pAttacker = GET_PLAYER(it->first).getUnit(it->second); if ( pAttacker && !pAttacker->isDelayedDeath() && !pAttacker->IsDead()) { // If in a city and the city can be captured, we are in highest danger if (pFriendlyCity) { if (GetDanger(pFriendlyCity) + pFriendlyCity->getDamage() > pFriendlyCity->GetMaxHitPoints()) { return MAX_INT; } } // Look for a possible plot defender else { IDInfo* pUnitNode = m_pPlot->headUnitNode(); CvUnit* pBestDefender = NULL; while (pUnitNode != NULL) { pBestDefender = ::getUnit(*pUnitNode); pUnitNode = m_pPlot->nextUnitNode(pUnitNode); if (pBestDefender && pBestDefender->getOwner() == pUnit->getOwner()) { //fix endless recursion with stacked embarked civilians: defender must also be able to attack if (pBestDefender->IsCanDefend() && pBestDefender->IsCanAttack()) { if (pBestDefender != pUnit) { if (pBestDefender->isWaiting() || !(pBestDefender->canMove())) { break; } } } } pBestDefender = NULL; } // If there is a defender and it might be killed, high danger if (pBestDefender && (pBestDefender->isWaiting() || !pBestDefender->canMove())) { if (GetDanger(pBestDefender) > pBestDefender->GetCurrHitPoints()) { return INT_MAX; } } else if (pBestDefender==NULL) { //Civilian could be captured on this tile return MAX_INT; } } } } // Damage from features (citadel) iPlotDamage += GetDamageFromFeatures(pUnit->getOwner()); iPlotDamage += m_bFlatPlotDamage ? m_pPlot->getTurnDamage(pUnit->ignoreTerrainDamage(), pUnit->ignoreFeatureDamage(), pUnit->extraTerrainDamage(), pUnit->extraFeatureDamage()) : 0; // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (!pCity || pCity->getTeam() == pUnit->getTeam()) continue; iPlotDamage += pCity->rangeCombatDamage(pUnit, NULL, false, m_pPlot); } //update cache m_lastUnit = unitStats; m_lastResult = iPlotDamage; return iPlotDamage; } // Capturing a city with a garrisoned unit destroys the garrisoned unit if (pFriendlyCity) { int iCityDanger = GetDanger(pFriendlyCity, (pUnit->getDomainType() == DOMAIN_LAND ? pUnit : NULL)); if (iCityDanger + pFriendlyCity->getDamage() < pFriendlyCity->GetMaxHitPoints()) { if (pUnit->CanGarrison()) { // Reconstruct the amount of damage the garrison would absorb for the city int iUnitShare = (iCityDanger*2*pUnit->GetMaxHitPoints()) / pFriendlyCity->GetMaxHitPoints(); // Damage from features return iUnitShare + GetDamageFromFeatures(pUnit->getOwner()); } else return 0; } else { return MAX_INT; } } CvPlot* pAttackerPlot = NULL; CvUnit* pInterceptor = NULL; // Damage from units // EXTREMELY IMPORTANT THAT NO RNG IS USED FOR PREDICTION! // Otherwise a tooltip or similar can change the game state for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pAttacker = GET_PLAYER(it->first).getUnit(it->second); if (!pAttacker || pAttacker->isDelayedDeath() || pAttacker->IsDead()) continue; pAttackerPlot = NULL; if (pAttacker->plot() != m_pPlot) { if (pAttacker->IsCanAttackRanged()) { if (pAttacker->getDomainType() == DOMAIN_AIR) { pInterceptor = pAttacker->GetBestInterceptor(*m_pPlot, pUnit); int iInterceptDamage = 0; if (pInterceptor) { // Always assume interception is successful iInterceptDamage = pInterceptor->GetInterceptionDamage(pUnit, false); } iPlotDamage += pAttacker->GetAirCombatDamage(pUnit, NULL, false, iInterceptDamage, m_pPlot); } else { iPlotDamage += pAttacker->GetRangeCombatDamage(pUnit, NULL, false, 0, m_pPlot); } } else { if (plotDistance(m_iX, m_iY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pAttacker->plot(); } iPlotDamage += pAttacker->getCombatDamage( pAttacker->GetMaxAttackStrength(pAttackerPlot, m_pPlot, pUnit), pUnit->GetMaxDefenseStrength(m_pPlot, pAttacker), pAttacker->getDamage(), false, false, false); if (pAttacker->isRangedSupportFire()) { iPlotDamage += pAttacker->GetRangeCombatDamage(pUnit, NULL, false, 0, m_pPlot, pAttackerPlot); } } } } // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (!pCity || pCity->getTeam() == pUnit->getTeam()) continue; iPlotDamage += pCity->rangeCombatDamage(pUnit, NULL, false, m_pPlot); } // Damage from surrounding features (citadel) and the plot itself iPlotDamage += GetDamageFromFeatures(pUnit->getOwner()); iPlotDamage += m_bFlatPlotDamage ? m_pPlot->getTurnDamage(pUnit->ignoreTerrainDamage(), pUnit->ignoreFeatureDamage(), pUnit->extraTerrainDamage(), pUnit->extraFeatureDamage()) : 0; //update cache m_lastUnit = unitStats; m_lastResult = iPlotDamage; //done return iPlotDamage; }
int CvDangerPlotContents::GetAirUnitDamage(const CvUnit* pUnit, AirActionType iAirAction) { if (pUnit->getDomainType() != DOMAIN_AIR) return 0; if (iAirAction == AIR_ACTION_INTERCEPT) // Max damage from a potential air sweep against our intercept { int iBestAirSweepDamage = 0; int iCurrentAirSweepDamage = 0; for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pAttacker = GET_PLAYER(it->first).getUnit(it->second); if (!pAttacker || !pAttacker->canAirSweep() || pAttacker->isDelayedDeath() || pAttacker->IsDead()) { continue; } int iAttackerStrength = pAttacker->GetMaxRangedCombatStrength(pUnit, /*pCity*/ NULL, true, false); iAttackerStrength *= (100 + pAttacker->GetAirSweepCombatModifier()); iAttackerStrength /= 100; int iDefenderStrength = pUnit->GetMaxRangedCombatStrength(pUnit, /*pCity*/ NULL, false, false); iCurrentAirSweepDamage = pUnit->getCombatDamage(iDefenderStrength, iAttackerStrength, pUnit->getDamage(), /*bIncludeRand*/ false, /*bAttackerIsCity*/ false, /*bDefenderIsCity*/ false); // It's a slower to have this in the unit loop instead of after the best damage has been calculated, but it's also more accurate if (iCurrentAirSweepDamage >= pAttacker->GetCurrHitPoints()) { int iReceiverDamage = pAttacker->getCombatDamage(iAttackerStrength, iDefenderStrength, pAttacker->getDamage(), /*bIncludeRand*/ false, /*bAttackerIsCity*/ false, /*bDefenderIsCity*/ false); if (iReceiverDamage >= pUnit->GetCurrHitPoints()) { if (iReceiverDamage + pUnit->getDamage() > iCurrentAirSweepDamage + pAttacker->getDamage()) { iCurrentAirSweepDamage = pUnit->GetCurrHitPoints() - 1; } } } if (iCurrentAirSweepDamage > iBestAirSweepDamage) { iBestAirSweepDamage = iCurrentAirSweepDamage; } } return iBestAirSweepDamage; } else { CvUnit* pInterceptor = pUnit->GetBestInterceptor(*m_pPlot); if (pInterceptor) { // Air sweeps take modified damage from interceptors if (iAirAction == AIR_ACTION_SWEEP) { if (pInterceptor->getDomainType() != DOMAIN_AIR) { return (pInterceptor->GetInterceptionDamage(pUnit, false) * (100+GC.getAIR_SWEEP_INTERCEPTION_DAMAGE_MOD()))/100; } else { int iAttackerStrength = pUnit->GetMaxRangedCombatStrength(pInterceptor, /*pCity*/ NULL, true, false); iAttackerStrength *= (100 + pUnit->GetAirSweepCombatModifier()); iAttackerStrength /= 100; int iDefenderStrength = pInterceptor->GetMaxRangedCombatStrength(pUnit, /*pCity*/ NULL, false, false); int iReceiveDamage = pInterceptor->getCombatDamage(iDefenderStrength, iAttackerStrength, pInterceptor->getDamage(), /*bIncludeRand*/ false, /*bAttackerIsCity*/ false, /*bDefenderIsCity*/ false); if (iReceiveDamage >= pUnit->GetCurrHitPoints()) { int iDamageDealt = pUnit->getCombatDamage(iAttackerStrength, iDefenderStrength, pUnit->getDamage(), /*bIncludeRand*/ false, /*bAttackerIsCity*/ false, /*bDefenderIsCity*/ false); if (iDamageDealt >= pInterceptor->GetCurrHitPoints()) { if (iDamageDealt + pInterceptor->getDamage() > iReceiveDamage + pUnit->getDamage()) { iReceiveDamage = pUnit->GetCurrHitPoints() - 1; } } } return iReceiveDamage; } } else { // Always assume interception is successful return pInterceptor->GetInterceptionDamage(pUnit, false); } } } return 0; }
// Get the maximum damage unit could receive at this plot in the next turn int CvDangerPlotContents::GetDanger(PlayerTypes ePlayer) { if (!m_pPlot) return 0; // Damage from terrain - since we don't know the unit, just assume 20 int iPlotDamage = m_bFlatPlotDamage ? 20 : 0; // Damage from units CvPlot* pAttackerPlot = NULL; for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if ( !pUnit || pUnit->isDelayedDeath() || pUnit->IsDead()) { continue; } pAttackerPlot = NULL; if (pUnit->IsCanAttackRanged()) { if (pUnit->getDomainType() == DOMAIN_AIR) { iPlotDamage += pUnit->GetAirCombatDamage(NULL, NULL, false, 0, m_pPlot); } else { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, NULL, false, 0, m_pPlot); } } else { if (plotDistance(m_iX, m_iY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pUnit->plot(); } //we don't know the defender strength, so assume it's equal to attacker strength! iPlotDamage += pUnit->getCombatDamage( pUnit->GetMaxAttackStrength(pAttackerPlot, m_pPlot, NULL), pUnit->GetBaseCombatStrength()*100, pUnit->getDamage(), false, false, false); if (pUnit->isRangedSupportFire()) { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, NULL, false, 0, m_pPlot, pAttackerPlot); } } } // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (pCity && pCity->getTeam() != GET_PLAYER(ePlayer).getTeam()) iPlotDamage += pCity->rangeCombatDamage(NULL, NULL, false, m_pPlot); } // Damage from features iPlotDamage += GetDamageFromFeatures(ePlayer); return iPlotDamage; }
// Get the maximum damage city could receive this turn if it were in this plot int CvDangerPlotContents::GetDanger(CvCity* pCity, const CvUnit* pPretendGarrison) { if (!m_pPlot || !pCity) return 0; int iPlotDamage = 0; CvPlot* pCityPlot = pCity->plot(); const int iCityX = pCityPlot->getX(); const int iCityY = pCityPlot->getY(); CvCityGarrisonOverride guard(pCity,pPretendGarrison); CvPlot* pAttackerPlot = NULL; CvUnit* pInterceptor = NULL; // Damage from ranged units and melees that cannot capture for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if (!pUnit || pUnit->isDelayedDeath() || pUnit->IsDead()) { continue; } pAttackerPlot = NULL; if (pUnit->IsCanAttackRanged()) { if (pUnit->getDomainType() == DOMAIN_AIR) { pInterceptor = pUnit->GetBestInterceptor(*m_pPlot); int iInterceptDamage = 0; if (pInterceptor) { // Always assume interception is successful iInterceptDamage = pInterceptor->GetInterceptionDamage(pUnit, false); } iPlotDamage += pUnit->GetAirCombatDamage(NULL, pCity, false, iInterceptDamage, m_pPlot); } else { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, m_pPlot); } } else if (pUnit->isNoCapture()) { if (plotDistance(iCityX, iCityY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pUnit->plot(); } iPlotDamage += pUnit->getCombatDamage(pUnit->GetMaxAttackStrength(pAttackerPlot, pCityPlot, NULL), pCity->getStrengthValue(), pUnit->getDamage(), false, false, true); if (pUnit->isRangedSupportFire()) { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, pCityPlot); } } } // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (!pCity || pCity->getTeam() == pCity->getTeam()) { continue; } iPlotDamage += pCity->rangeCombatDamage(NULL, pCity, false, pCityPlot); } // Damage from melee units for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if (!pUnit || pUnit->isDelayedDeath() || pUnit->IsDead()) { continue; } pAttackerPlot = NULL; if (!pUnit->IsCanAttackRanged() && !pUnit->isNoCapture()) { if (plotDistance(iCityX, iCityY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pUnit->plot(); } iPlotDamage += pUnit->getCombatDamage(pUnit->GetMaxAttackStrength(pAttackerPlot, pCityPlot, NULL), pCity->getStrengthValue(), pUnit->getDamage(), false, false, true); if (pUnit->isRangedSupportFire()) { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, pCityPlot); } } } //if we have a garrison, split the damage CvUnit* pGarrison = pCity->GetGarrisonedUnit(); if (pGarrison) { int iUnitShare = (iPlotDamage*2*pGarrison->GetMaxHitPoints())/(pCity->GetMaxHitPoints()+2*pGarrison->GetMaxHitPoints()); iPlotDamage -= iUnitShare; } return iPlotDamage; }