// 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; }
// 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; }