// 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; }
/// Add data for this cell into dominance zone information void CvTacticalAnalysisMap::AddToDominanceZones(int iIndex, CvTacticalAnalysisCell* pCell) { TeamTypes ourTeam = GET_PLAYER(m_ePlayer).getTeam(); CvPlot* pPlot = GC.getMap().plotByIndex(iIndex); // Compute zone data for this cell CvTacticalDominanceZone newZone; newZone.SetAreaID(pPlot->getArea()); newZone.SetWater(pPlot->isWater()); int iCityDistance = GC.getGame().GetClosestCityDistanceInTurns(pPlot); CvCity* pCity = GC.getGame().GetClosestCityByEstimatedTurns(pPlot); PlayerTypes eOwnerPlayer = NO_PLAYER; TeamTypes eOwnerTeam = NO_TEAM; //for plots far away from a city, check the owner if (iCityDistance>2) { eOwnerTeam = pPlot->getTeam(); eOwnerPlayer = pPlot->getOwner(); //there is almost always a closest city, but we're not always interested if (pCity && eOwnerPlayer!=pCity->getOwner()) pCity = NULL; } else if (pCity) //look at the city { eOwnerTeam = pCity->getTeam(); eOwnerPlayer = pCity->getOwner(); } newZone.SetOwner(eOwnerPlayer); newZone.SetZoneCity(pCity); newZone.Extend(pPlot); if(eOwnerTeam==NO_TEAM) { newZone.SetTerritoryType(TACTICAL_TERRITORY_NO_OWNER); } else if(eOwnerTeam == ourTeam) { newZone.SetTerritoryType(TACTICAL_TERRITORY_FRIENDLY); } else if(GET_TEAM(ourTeam).isAtWar(eOwnerTeam)) { newZone.SetTerritoryType(TACTICAL_TERRITORY_ENEMY); } else { newZone.SetTerritoryType(TACTICAL_TERRITORY_NEUTRAL); } // Now see if we already have a matching zone CvTacticalDominanceZone* pZone = MergeWithExistingZone(&newZone); if(!pZone) { // Data populated, now add to vector newZone.SetDominanceZoneID(m_DominanceZones.size()); m_DominanceZones.push_back(newZone); pZone = &m_DominanceZones[m_DominanceZones.size() - 1]; } // Set zone for this cell pCell->SetDominanceZone(pZone->GetDominanceZoneID()); pZone->Extend(pPlot); }
// 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 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; }