// Find all our enemies (combat units) void CvTacticalAnalysisMap::BuildEnemyUnitList() { m_EnemyUnits.clear(); m_EnemyCities.clear(); TeamTypes ourTeam = GET_PLAYER(m_ePlayer).getTeam(); for(int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++) { const PlayerTypes ePlayer = (PlayerTypes)iPlayer; CvPlayer& kPlayer = GET_PLAYER(ePlayer); const TeamTypes eTeam = kPlayer.getTeam(); // for each opposing civ if(kPlayer.isAlive() && GET_TEAM(eTeam).isAtWar(ourTeam)) { int iLoop; CvUnit* pLoopUnit = NULL; CvCity* pLoopCity; for(pLoopCity = kPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kPlayer.nextCity(&iLoop)) if (pLoopCity->plot()->isRevealed(ourTeam)) m_EnemyCities.push_back(pLoopCity->GetIDInfo()); for(pLoopUnit = kPlayer.firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = kPlayer.nextUnit(&iLoop)) if(pLoopUnit->IsCanAttack() && pLoopUnit->plot()->isVisible(ourTeam)) m_EnemyUnits.push_back(pLoopUnit->GetIDInfo()); } } }
// Find all our enemies (combat units) void CvTacticalAnalysisMap::BuildEnemyUnitList() { CvTacticalAnalysisEnemy enemy; m_EnemyUnits.clear(); for(int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++) { const PlayerTypes ePlayer = (PlayerTypes)iPlayer; CvPlayer& kPlayer = GET_PLAYER(ePlayer); const TeamTypes eTeam = kPlayer.getTeam(); // for each opposing civ if(kPlayer.isAlive() && GET_TEAM(eTeam).isAtWar(m_pPlayer->getTeam())) { int iLoop; CvUnit* pLoopUnit = NULL; for(pLoopUnit = kPlayer.firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = kPlayer.nextUnit(&iLoop)) { // Make sure this unit can attack if(pLoopUnit->IsCanAttack()) { m_EnemyUnits.push_back(pLoopUnit); } } } } }
// 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; }