// Is this plot in dangerous territory? bool CvTacticalAnalysisMap::IsInEnemyDominatedZone(CvPlot* pPlot) { int iPlotIndex = GC.getMap().plotNum(pPlot->getX(), pPlot->getY()); CvTacticalAnalysisCell* pCell = GetCell(iPlotIndex); CvTacticalDominanceZone* pZone = GetZoneByID(pCell->GetDominanceZone()); if(pZone && pZone->GetZoneCity()) //city check is to skip the potentially very large ocean zone return (pZone->GetOverallDominanceFlag() == TACTICAL_DOMINANCE_ENEMY); return false; }
/// Add in any temporary dominance zones from tactical AI void CvTacticalAnalysisMap::AddTemporaryZones() { CvTemporaryZone* pZone; CvTacticalAI* pTacticalAI = GET_PLAYER(m_ePlayer).GetTacticalAI(); if(pTacticalAI) { pTacticalAI->DropObsoleteZones(); pZone = pTacticalAI->GetFirstTemporaryZone(); while(pZone) { // Can't be a city zone (which is just used to boost priority but not establish a new zone) if(pZone->GetTargetType() != AI_TACTICAL_TARGET_CITY) { CvPlot* pPlot = GC.getMap().plot(pZone->GetX(), pZone->GetY()); if(pPlot) { CvTacticalDominanceZone newZone; newZone.SetDominanceZoneID(m_DominanceZones.size()); newZone.SetTerritoryType(TACTICAL_TERRITORY_TEMP_ZONE); newZone.SetOwner(NO_PLAYER); newZone.SetAreaID(pPlot->getArea()); newZone.SetWater(pPlot->isWater()); newZone.Extend(pPlot); newZone.SetNavalInvasion(pZone->IsNavalInvasion()); m_DominanceZones.push_back(newZone); } } pZone = pTacticalAI->GetNextTemporaryZone(); } } }
/// Retrieve a dominance zone by closest city CvTacticalDominanceZone* CvTacticalAnalysisMap::GetZoneByCity(CvCity* pCity, bool bWater) { CvTacticalDominanceZone* pZone; for(int iI = 0; iI < GetNumZones(); iI++) { pZone = GetZoneByIndex(iI); if(pZone->GetZoneCity() == pCity && pZone->IsWater() == bWater) { return pZone; } } return NULL; }
/// Retrieve a dominance zone by ID CvTacticalDominanceZone* CvTacticalAnalysisMap::GetZoneByID(int iID) { CvTacticalDominanceZone* pZone; for(int iI = 0; iI < GetNumZones(); iI++) { pZone = GetZoneByIndex(iI); if(pZone->GetDominanceZoneID()==iID) { return pZone; } } return NULL; }
// Is this plot in dangerous territory? bool CvTacticalAnalysisMap::IsInEnemyDominatedZone(CvPlot* pPlot) { CvTacticalAnalysisCell* pCell; int iPlotIndex; CvTacticalDominanceZone* pZone; iPlotIndex = GC.getMap().plotNum(pPlot->getX(), pPlot->getY()); pCell = GetCell(iPlotIndex); for(int iI = 0; iI < GetNumZones(); iI++) { pZone = GetZone(iI); if(pZone->GetDominanceZoneID() == pCell->GetDominanceZone()) { return (pZone->GetDominanceFlag() == TACTICAL_DOMINANCE_ENEMY); } } return false; }
/// Can this cell go in an existing dominance zone? CvTacticalDominanceZone* CvTacticalAnalysisMap::FindExistingZone(CvPlot* pPlot) { CvTacticalDominanceZone* pZone; for(unsigned int iI = 0; iI < m_DominanceZones.size(); iI++) { pZone = &m_DominanceZones[iI]; // If this is a temporary zone, matches if unowned and close enough if((pZone->GetTerritoryType() == TACTICAL_TERRITORY_TEMP_ZONE) && (m_TempZone.GetTerritoryType() == TACTICAL_TERRITORY_NO_OWNER || m_TempZone.GetTerritoryType() == TACTICAL_TERRITORY_NEUTRAL) && (plotDistance(pPlot->getX(), pPlot->getY(), pZone->GetTempZoneCenter()->getX(), pZone->GetTempZoneCenter()->getY()) <= m_iTacticalRange)) { return pZone; } // If not friendly or enemy, just 1 zone per area if((pZone->GetTerritoryType() == TACTICAL_TERRITORY_NO_OWNER || pZone->GetTerritoryType() == TACTICAL_TERRITORY_NEUTRAL) && (m_TempZone.GetTerritoryType() == TACTICAL_TERRITORY_NO_OWNER || m_TempZone.GetTerritoryType() == TACTICAL_TERRITORY_NEUTRAL)) { if(pZone->GetAreaID() == m_TempZone.GetAreaID()) { return pZone; } } // Otherwise everything needs to match if(pZone->GetTerritoryType() == m_TempZone.GetTerritoryType() && pZone->GetOwner() == m_TempZone.GetOwner() && pZone->GetAreaID() == m_TempZone.GetAreaID() && pZone->GetClosestCity() == m_TempZone.GetClosestCity()) { return pZone; } } return NULL; }
void CvTacticalAnalysisMap::Dump() { if (m_ePlayer==NO_PLAYER) return; bool bLogging = GC.getLogging() && GC.getAILogging() && (GET_PLAYER(m_ePlayer).isMajorCiv() || GET_PLAYER(m_ePlayer).isBarbarian()) && GET_PLAYER(m_ePlayer).IsAtWar(); if (bLogging) { CvString fname = CvString::format( "TacticalCells_%s_%03d.txt", GET_PLAYER(m_ePlayer).getCivilizationAdjective(), GC.getGame().getGameTurn() ); FILogFile* pLog=LOGFILEMGR.GetLog( fname.c_str(), FILogFile::kDontTimeStamp ); if (pLog) { pLog->Msg( "#x,y,visible,terrain,owner,enemy,targettype,underattack,zoneid,dominance,zonetype,fstrength,estrength,city\n" ); for (int i=0; i<GC.getMap().numPlots(); i++) { CvTacticalAnalysisCell* pCell = GetCell(i); if (!pCell->IsRevealed()) continue; CvTacticalDominanceZone* pZone = GetZoneByID( pCell->GetDominanceZone() ); int iZoneFriendlyStrength = pZone ? pZone->GetFriendlyRangedStrength() + pZone->GetOverallFriendlyStrength() : -1; int iZoneEnemyStrength = pZone ? pZone->GetEnemyRangedStrength() + pZone->GetOverallEnemyStrength() : -1; CvCity* pCity = pZone ? pZone->GetZoneCity() : NULL; CvString dump = CvString::format( "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%s\n", GC.getMap().plotByIndex(i)->getX(), GC.getMap().plotByIndex(i)->getY(), pCell->IsVisible(), (int)GC.getMap().plotByIndex(i)->getTerrainType(), (int)GC.getMap().plotByIndex(i)->getOwner(), (int)pCell->IsEnemyTerritory(), (int)pCell->GetTargetType(), (int)pCell->IsSubjectToAttack(), pZone ? pZone->GetDominanceZoneID() : -1, pZone ? pZone->GetOverallDominanceFlag() : -1, pZone ? pZone->GetTerritoryType() : -1, iZoneFriendlyStrength, iZoneEnemyStrength, pCity ? pCity->getName().c_str() : "no city" ); pLog->Msg( dump.c_str() ); } pLog->Close(); } } }
/// Establish order of zone processing for the turn void CvTacticalAnalysisMap::PrioritizeZones() { // Loop through the dominance zones for(unsigned int iI = 0; iI < m_DominanceZones.size(); iI++) { // Find the zone and compute dominance here CvTacticalDominanceZone* pZone = &m_DominanceZones[iI]; eTacticalDominanceFlags eDominance = ComputeDominance(pZone); // Establish a base value for the region int iBaseValue = 1; int iMultiplier = 1; // Temporary zone? if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_TEMP_ZONE) { iMultiplier = 1000; } else { CvCity* pClosestCity = pZone->GetZoneCity(); if(pClosestCity && pClosestCity->isAdjacentToArea(pZone->GetAreaID())) { iBaseValue += (1 + (int)sqrt((float)pClosestCity->getPopulation())); if(GET_PLAYER(m_ePlayer).GetTacticalAI()->IsTemporaryZoneCity(pClosestCity)) { iBaseValue *= 2; } else if (pClosestCity->isVisible( GET_PLAYER(m_ePlayer).getTeam(), false)) { iBaseValue *= 4; // How damaged is this visible city? int iMaxDamageMultiplier = 10; int iDamage = pClosestCity->getDamage(); if (iDamage > (pClosestCity->GetMaxHitPoints() / iMaxDamageMultiplier)) { iBaseValue *= (int)((iDamage + 1) * 10 / pClosestCity->GetMaxHitPoints()); } } #if defined(MOD_BALANCE_CORE) if (GET_PLAYER(m_ePlayer).IsCityAlreadyTargeted(pClosestCity) || GET_PLAYER(m_ePlayer).GetMilitaryAI()->IsCurrentAttackTarget(pClosestCity) ) { iBaseValue *= 2; } if (pClosestCity->GetPlayer()->isMinorCiv()) { //At war with ally of this minor? Greatly reduce priority. PlayerTypes eAlly = pClosestCity->GetPlayer()->GetMinorCivAI()->GetAlly(); if (eAlly != NO_PLAYER && GET_TEAM(GET_PLAYER(m_ePlayer).getTeam()).isAtWar(GET_PLAYER(eAlly).getTeam())) { iBaseValue = 1; } } #endif } if(!pZone->IsWater()) { iBaseValue *= 3; } // Now compute a multiplier based on current conditions here if(eDominance == TACTICAL_DOMINANCE_ENEMY) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { iMultiplier = 1; } else if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { iMultiplier = 8; } } else if(eDominance == TACTICAL_DOMINANCE_EVEN) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { iMultiplier = 4; } else if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { iMultiplier = 4; } } else if(eDominance == TACTICAL_DOMINANCE_FRIENDLY) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { iMultiplier = 8; } else if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { iMultiplier = 1; } } if(!GET_PLAYER(m_ePlayer).isMinorCiv()) { if(GET_PLAYER(m_ePlayer).GetDiplomacyAI()->GetStateAllWars() == STATE_ALL_WARS_WINNING) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { #if defined(MOD_BALANCE_CORE_MILITARY) iMultiplier *= 4; #else iMultiplier *= 2; #endif } } else if(GET_PLAYER(m_ePlayer).GetDiplomacyAI()->GetStateAllWars() == STATE_ALL_WARS_LOSING) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { iMultiplier *= 4; } } } } // Save off the value for this zone pZone->SetDominanceZoneValue( iBaseValue * iMultiplier); } std::stable_sort(m_DominanceZones.begin(), m_DominanceZones.end()); }
/// Calculate military presences in each owned dominance zone void CvTacticalAnalysisMap::CalculateMilitaryStrengths() { TeamTypes eTeam = GET_PLAYER(m_ePlayer).getTeam(); // Loop through the dominance zones for(unsigned int iI = 0; iI < m_DominanceZones.size(); iI++) { CvTacticalDominanceZone* pZone = &m_DominanceZones[iI]; CvCity *pClosestCity = pZone->GetZoneCity(); if(pClosestCity) { // Start with strength of the city itself int iCityHitPoints = pClosestCity->GetMaxHitPoints() - pClosestCity->getDamage(); int iStrength = pClosestCity->getStrengthValue() * iCityHitPoints / GC.getMAX_CITY_HIT_POINTS(); if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { pZone->AddFriendlyMeleeStrength(iStrength); pZone->AddFriendlyRangedStrength(pClosestCity->getStrengthValue(true)); } else if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { pZone->AddEnemyMeleeStrength(iStrength); pZone->AddEnemyRangedStrength(pClosestCity->getStrengthValue(true)); } else { pZone->AddNeutralStrength(iStrength); } } // check all units in the world for(int iPlayerLoop = 0; iPlayerLoop < MAX_PLAYERS; iPlayerLoop++) { CvPlayer& kPlayer = GET_PLAYER((PlayerTypes) iPlayerLoop); bool bEnemy = GET_TEAM(eTeam).isAtWar(kPlayer.getTeam()); bool bFriendly = (eTeam==kPlayer.getTeam()); int iLoop; for(CvUnit* pLoopUnit = kPlayer.firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = kPlayer.nextUnit(&iLoop)) { if(!pLoopUnit->IsCombatUnit()) continue; bool bUnitMayBeRelevant = (pLoopUnit->getDomainType() == DOMAIN_AIR || pLoopUnit->isRanged() || //ranged power is cross-domain! (pLoopUnit->getDomainType() == DOMAIN_LAND && !pZone->IsWater()) || ((pLoopUnit->getDomainType() == DOMAIN_SEA || (pLoopUnit->isEmbarked() && pClosestCity) && pZone->IsWater()))); //embarked melee still count in water zone if there's a city to attack/defend if (!bUnitMayBeRelevant) continue; CvPlot* pPlot = pLoopUnit->plot(); if(!pPlot) continue; //a little cheating for AI - invisible units still count with reduced strength bool bVisible = pPlot->isVisible(eTeam) || pPlot->isAdjacentVisible(eTeam, false); bool bZoneTypeMismatch = (pLoopUnit->getDomainType() == DOMAIN_LAND && pZone->IsWater()) || (pLoopUnit->getDomainType() == DOMAIN_SEA && !pZone->IsWater()); //embarked units and crossdomain count only partially bool bReducedStrength = pLoopUnit->isEmbarked() || bZoneTypeMismatch; //if there is a city, units in adjacent zones can also count int iDistance = 0; if (pClosestCity) { iDistance = plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), pClosestCity->getX(), pClosestCity->getY()); if (iDistance > m_iTacticalRange) continue; else if (iDistance > (m_iTacticalRange / 2)) { if (bZoneTypeMismatch) continue; else bReducedStrength = true; } else { //if on another continent, they can't easily take part in the fight if (!pClosestCity->isMatchingArea(pLoopUnit->plot())) bReducedStrength = true; } } else { //if there is no city, the unit must be in the zone itself if ( GetCell(pLoopUnit->plot()->GetPlotIndex())->GetDominanceZone() != pZone->GetDominanceZoneID() ) continue; } int iMultiplier = m_iTacticalRange + MIN(3 - iDistance, 0); // 3 because action may still be spread out over the zone if(iMultiplier > 0) { int iUnitStrength = pLoopUnit->GetBaseCombatStrengthConsideringDamage(); //unit might disembark ... so don't count it for water zone, but for adjacent land if(iUnitStrength == 0 && pLoopUnit->isEmbarked() && !pZone->IsWater()) iUnitStrength = pLoopUnit->GetBaseCombatStrength(); int iRangedStrength = pLoopUnit->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, true, true) / 100; if(!bVisible || bReducedStrength) { iUnitStrength /= 2; iRangedStrength /= 2; } if (bEnemy) { #if defined(MOD_BALANCE_CORE_MILITARY_LOGGING) //CvString msg; //msg.Format("Zone %d, Enemy %s %d with %d hp at %d,%d - distance %d, strength %d, ranged strength %d (total %d)", // pZone->GetDominanceZoneID(), pLoopUnit->getName().c_str(), pLoopUnit->GetID(), pLoopUnit->GetCurrHitPoints(), // pLoopUnit->getX(), pLoopUnit->getY(), iDistance, iUnitStrength, iRangedStrength, pZone->GetOverallEnemyStrength()); //GET_PLAYER(m_ePlayer).GetTacticalAI()->LogTacticalMessage(msg, true /*bSkipLogDominanceZone*/); #endif if (pLoopUnit->getDomainType() == DOMAIN_SEA) { pZone->AddEnemyNavalStrength(iUnitStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddEnemyNavalRangedStrength(iRangedStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddEnemyNavalUnitCount(1); } else { pZone->AddEnemyMeleeStrength(iUnitStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddEnemyRangedStrength(iRangedStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddEnemyUnitCount(1); } //again only for enemies if(pZone->GetRangeClosestEnemyUnit()<0 || iDistance<pZone->GetRangeClosestEnemyUnit()) pZone->SetRangeClosestEnemyUnit(iDistance); } else if (bFriendly) { #if defined(MOD_BALANCE_CORE_MILITARY_LOGGING) //CvString msg; //msg.Format("Zone %d, Friendly %s %d with %d hp at %d,%d - distance %d, strength %d, ranged strength %d (total %d)", // pZone->GetDominanceZoneID(), pLoopUnit->getName().c_str(), pLoopUnit->GetID(), pLoopUnit->GetCurrHitPoints(), // pLoopUnit->getX(), pLoopUnit->getY(), iDistance, iUnitStrength, iRangedStrength, pZone->GetOverallFriendlyStrength()); //GET_PLAYER(m_ePlayer).GetTacticalAI()->LogTacticalMessage(msg, true /*bSkipLogDominanceZone*/); #endif if (pLoopUnit->getDomainType() == DOMAIN_SEA) { pZone->AddFriendlyNavalStrength(iUnitStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddFriendlyNavalRangedStrength(iRangedStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddFriendlyNavalUnitCount(1); } else { pZone->AddFriendlyMeleeStrength(iUnitStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddFriendlyRangedStrength(iRangedStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddFriendlyUnitCount(1); } } else { //neutral has only very few stats pZone->AddNeutralStrength(iUnitStrength*iMultiplier*m_iUnitStrengthMultiplier); pZone->AddNeutralUnitCount(1); } } } } } }
/// 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); }
/// Log dominance zone data void CvTacticalAnalysisMap::LogZones() { if(GC.getLogging() && GC.getAILogging()) { CvString szLogMsg; CvTacticalDominanceZone* pZone; for(unsigned int iI = 0; iI < m_DominanceZones.size(); iI++) { pZone = &m_DominanceZones[iI]; //don't blow up the logs for empty zones if ( pZone->GetOverallFriendlyStrength()==0 && pZone->GetOverallEnemyStrength()==0) continue; szLogMsg.Format("Zone ID: %d, Size: %d, City: %s, Area ID: %d, Value: %d, FRIENDLY Str: %d (%d), Ranged: %d (naval %d), ENEMY Str: %d (%d), Ranged: %d (naval %d), Closest Enemy: %d", pZone->GetDominanceZoneID(), pZone->GetNumPlots(), pZone->GetZoneCity() ? pZone->GetZoneCity()->getName().c_str() : "none", pZone->GetAreaID(), pZone->GetDominanceZoneValue(), pZone->GetOverallFriendlyStrength(), pZone->GetTotalFriendlyUnitCount(), pZone->GetFriendlyRangedStrength(), pZone->GetFriendlyNavalRangedStrength(), pZone->GetOverallEnemyStrength(), pZone->GetTotalEnemyUnitCount(), pZone->GetEnemyRangedStrength(), pZone->GetEnemyNavalRangedStrength(), pZone->GetRangeClosestEnemyUnit()); if(pZone->GetOverallDominanceFlag() == TACTICAL_DOMINANCE_FRIENDLY) { szLogMsg += ", Friendly"; } else if(pZone->GetOverallDominanceFlag() == TACTICAL_DOMINANCE_ENEMY) { szLogMsg += ", Enemy"; } else if(pZone->GetOverallDominanceFlag() == TACTICAL_DOMINANCE_EVEN) { szLogMsg += ", Even"; } else if(pZone->GetOverallDominanceFlag() == TACTICAL_DOMINANCE_NO_UNITS_VISIBLE) { szLogMsg += ", No Units Visible"; } if(pZone->IsWater()) { szLogMsg += ", Water"; } else { szLogMsg += ", Land"; } if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_TEMP_ZONE) { szLogMsg += ", Temporary Zone"; } else if(pZone->GetZoneCity()) { if (GET_PLAYER(m_ePlayer).GetTacticalAI()->IsTemporaryZoneCity(pZone->GetZoneCity())) { szLogMsg += " (Temp)"; } } if (pZone->IsNavalInvasion()) { szLogMsg += ", NAVAL INVASION"; } GET_PLAYER(m_ePlayer).GetTacticalAI()->LogTacticalMessage(szLogMsg, true /*bSkipLogDominanceZone*/); } } }
/// Calculate military presences in each owned dominance zone void CvTacticalAnalysisMap::CalculateMilitaryStrengths() { // Loop through the dominance zones CvTacticalDominanceZone* pZone; CvCity* pClosestCity = NULL; int iDistance; int iMultiplier; int iLoop; CvUnit* pLoopUnit; TeamTypes eTeam; eTeam = m_pPlayer->getTeam(); for(unsigned int iI = 0; iI < m_DominanceZones.size(); iI++) { pZone = &m_DominanceZones[iI]; if(pZone->GetTerritoryType() != TACTICAL_TERRITORY_NO_OWNER) { pClosestCity = pZone->GetClosestCity(); if(pClosestCity) { // Start with strength of the city itself int iCityHitPoints = pClosestCity->GetMaxHitPoints() - pClosestCity->getDamage(); int iStrength = m_iTacticalRange * pClosestCity->getStrengthValue() * iCityHitPoints / GC.getMAX_CITY_HIT_POINTS(); if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { pZone->AddFriendlyStrength(iStrength); #if defined(MOD_AI_SMART_V3) pZone->AddFriendlyRangedStrength(pClosestCity->getStrengthValue(MOD_AI_SMART_V3)); #else pZone->AddFriendlyRangedStrength(pClosestCity->getStrengthValue()); #endif } #if defined(MOD_AI_SMART_V3) else if(!MOD_AI_SMART_V3 || pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) #else else #endif { pZone->AddEnemyStrength(iStrength); #if defined(MOD_AI_SMART_V3) pZone->AddEnemyRangedStrength(pClosestCity->getStrengthValue(MOD_AI_SMART_V3)); #else pZone->AddEnemyRangedStrength(pClosestCity->getStrengthValue()); #endif } // Loop through all of OUR units first for(pLoopUnit = m_pPlayer->firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = m_pPlayer->nextUnit(&iLoop)) { if(pLoopUnit->IsCombatUnit()) { if(pLoopUnit->getDomainType() == DOMAIN_AIR || #if defined(MOD_AI_SMART_V3) //ranged power is cross-domain! (MOD_AI_SMART_V3 && pLoopUnit->isRanged()) || #endif (pLoopUnit->getDomainType() == DOMAIN_LAND && !pZone->IsWater()) || (pLoopUnit->getDomainType() == DOMAIN_SEA && pZone->IsWater())) { iDistance = plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), pClosestCity->getX(), pClosestCity->getY()); if (iDistance <= m_iTacticalRange) { #if defined(MOD_AI_SMART_V3) int iRange = MOD_AI_SMART_V3 ? MIN(4 - iDistance, 0) : 4 - iDistance; iMultiplier = m_iTacticalRange + iRange; #else iMultiplier = (m_iTacticalRange + 4 - iDistance); // "4" so unit strength isn't totally dominated by proximity to city #endif if(iMultiplier > 0) { int iUnitStrength = pLoopUnit->GetBaseCombatStrengthConsideringDamage(); if(iUnitStrength == 0 && pLoopUnit->isEmbarked() && !pZone->IsWater()) { iUnitStrength = pLoopUnit->GetBaseCombatStrength(true); } pZone->AddFriendlyStrength(iUnitStrength * iMultiplier * m_iUnitStrengthMultiplier); pZone->AddFriendlyRangedStrength(pLoopUnit->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, true, true)); if(pLoopUnit->GetRange() > GetBestFriendlyRange()) { SetBestFriendlyRange(pLoopUnit->GetRange()); } if(pLoopUnit->IsRangeAttackIgnoreLOS()) { SetIgnoreLOS(true); } pZone->AddFriendlyUnitCount(1); if(pLoopUnit->isRanged()) { pZone->AddFriendlyRangedUnitCount(1); } } } } } } // Repeat for all visible enemy units (or adjacent to visible) for(int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++) { CvPlayer& kPlayer = GET_PLAYER((PlayerTypes) iPlayerLoop); if(GET_TEAM(eTeam).isAtWar(kPlayer.getTeam())) { for(pLoopUnit = kPlayer.firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = kPlayer.nextUnit(&iLoop)) { if(pLoopUnit->IsCombatUnit()) { if(pLoopUnit->getDomainType() == DOMAIN_AIR || #if defined(MOD_AI_SMART_V3) //ranged power is cross-domain! (MOD_AI_SMART_V3 && pLoopUnit->isRanged()) || #endif (pLoopUnit->getDomainType() == DOMAIN_LAND && !pZone->IsWater()) || (pLoopUnit->getDomainType() == DOMAIN_SEA && pZone->IsWater())) { CvPlot* pPlot; pPlot = pLoopUnit->plot(); if(pPlot) { bool bVisible = true; iDistance = plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), pClosestCity->getX(), pClosestCity->getY()); if (iDistance <= m_iTacticalRange) { #if defined(MOD_AI_SMART_V3) int iRange = MOD_AI_SMART_V3 ? MIN(4 - iDistance, 0) : 4 - iDistance; iMultiplier = m_iTacticalRange + iRange; // 4 because action may still be spread out over the zone #else iMultiplier = (m_iTacticalRange + 4 - iDistance); // "4" so unit strength isn't totally dominated by proximity to city #endif if(!pPlot->isVisible(eTeam) && !pPlot->isAdjacentVisible(eTeam, false)) { bVisible = false; } if(iMultiplier > 0) { int iUnitStrength = pLoopUnit->GetBaseCombatStrengthConsideringDamage(); if(iUnitStrength == 0 && pLoopUnit->isEmbarked() && !pZone->IsWater()) { iUnitStrength = pLoopUnit->GetBaseCombatStrength(true); } if(!bVisible) { iUnitStrength /= 2; } pZone->AddEnemyStrength(iUnitStrength * iMultiplier * m_iUnitStrengthMultiplier); int iRangedStrength = pLoopUnit->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, true, true); if(!bVisible) { iRangedStrength /= 2; } pZone->AddEnemyRangedStrength(iRangedStrength); if(bVisible) { pZone->AddEnemyUnitCount(1); if(iDistance < pZone->GetRangeClosestEnemyUnit()) { pZone->SetRangeClosestEnemyUnit(iDistance); } if(pLoopUnit->isRanged()) { pZone->AddEnemyRangedUnitCount(1); } if(pLoopUnit->getDomainType() == DOMAIN_SEA) { pZone->AddEnemyNavalUnitCount(1); } } } } } } } } } } } } }
/// Add data for this cell into dominance zone information void CvTacticalAnalysisMap::AddToDominanceZones(int iIndex, CvTacticalAnalysisCell* pCell) { CvPlot* pPlot = GC.getMap().plotByIndex(iIndex); // Compute zone data for this cell m_TempZone.SetAreaID(pPlot->getArea()); m_TempZone.SetOwner(pPlot->getOwner()); m_TempZone.SetWater(pPlot->isWater()); if(!pPlot->isOwned()) { m_TempZone.SetTerritoryType(TACTICAL_TERRITORY_NO_OWNER); } else if(pPlot->getTeam() == m_pPlayer->getTeam()) { m_TempZone.SetTerritoryType(TACTICAL_TERRITORY_FRIENDLY); } else if(GET_TEAM(m_pPlayer->getTeam()).isAtWar(pPlot->getTeam())) { m_TempZone.SetTerritoryType(TACTICAL_TERRITORY_ENEMY); } else { m_TempZone.SetTerritoryType(TACTICAL_TERRITORY_NEUTRAL); } m_TempZone.SetClosestCity(NULL); if(m_TempZone.GetTerritoryType() == TACTICAL_TERRITORY_ENEMY || m_TempZone.GetTerritoryType() == TACTICAL_TERRITORY_NEUTRAL || m_TempZone.GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { int iLoop; int iBestDistance = MAX_INT; CvCity* pBestCity = NULL; for(CvCity* pLoopCity = GET_PLAYER(m_TempZone.GetOwner()).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(m_TempZone.GetOwner()).nextCity(&iLoop)) { int iDistance = plotDistance(pLoopCity->getX(), pLoopCity->getY(), pPlot->getX(), pPlot->getY()); if(iDistance < iBestDistance) { iBestDistance = iDistance; pBestCity = pLoopCity; } } if(pBestCity != NULL) { m_TempZone.SetClosestCity(pBestCity); } } // Now see if we already have a matching zone CvTacticalDominanceZone* pZone = FindExistingZone(pPlot); if(!pZone) { // Data populated, now add to vector m_TempZone.SetDominanceZoneID(m_DominanceZones.size()); m_DominanceZones.push_back(m_TempZone); pZone = &m_DominanceZones[m_DominanceZones.size() - 1]; } // If this isn't owned territory, update zone with military strength info if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_NO_OWNER || pZone->GetTerritoryType() == TACTICAL_TERRITORY_TEMP_ZONE) { CvUnit* pFriendlyUnit = pCell->GetFriendlyMilitaryUnit(); if(pFriendlyUnit) { if(pFriendlyUnit->getDomainType() == DOMAIN_AIR || (pFriendlyUnit->getDomainType() == DOMAIN_LAND && !pZone->IsWater()) || (pFriendlyUnit->getDomainType() == DOMAIN_SEA && pZone->IsWater())) { int iStrength = pFriendlyUnit->GetBaseCombatStrengthConsideringDamage(); if(iStrength == 0 && pFriendlyUnit->isEmbarked() && !pZone->IsWater()) { iStrength = pFriendlyUnit->GetBaseCombatStrength(true); } pZone->AddFriendlyStrength(iStrength * m_iUnitStrengthMultiplier); pZone->AddFriendlyRangedStrength(pFriendlyUnit->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, true, true)); if(pFriendlyUnit->GetRange() > GetBestFriendlyRange()) { SetBestFriendlyRange(pFriendlyUnit->GetRange()); } if(pFriendlyUnit->IsRangeAttackIgnoreLOS()) { SetIgnoreLOS(true); } pZone->AddFriendlyUnitCount(1); if(pFriendlyUnit->isRanged()) { pZone->AddFriendlyRangedUnitCount(1); } } } CvUnit* pEnemyUnit = pCell->GetEnemyMilitaryUnit(); if(pEnemyUnit) { if(pEnemyUnit->getDomainType() == DOMAIN_AIR || (pEnemyUnit->getDomainType() == DOMAIN_LAND && !pZone->IsWater()) || (pEnemyUnit->getDomainType() == DOMAIN_SEA && pZone->IsWater())) { int iStrength = pEnemyUnit->GetBaseCombatStrengthConsideringDamage(); if(iStrength == 0 && pEnemyUnit->isEmbarked() && !pZone->IsWater()) { iStrength = pEnemyUnit->GetBaseCombatStrength(true); } #if defined(MOD_AI_SMART_V3) int iEnemyRangedStrength = pEnemyUnit->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, true, true); if (MOD_AI_SMART_V3) { if (!pCell->IsVisible()) { iStrength /= 2; iEnemyRangedStrength /= 2; } iEnemyRangedStrength = iEnemyRangedStrength * m_iUnitStrengthMultiplier; } #endif pZone->AddEnemyStrength(iStrength * m_iUnitStrengthMultiplier); #if defined(MOD_AI_SMART_V3) pZone->AddEnemyRangedStrength(iEnemyRangedStrength); #else pZone->AddEnemyRangedStrength(pEnemyUnit->GetMaxRangedCombatStrength(NULL, /*pCity*/ NULL, true, true)); #endif pZone->AddEnemyUnitCount(1); if(pEnemyUnit->isRanged()) { pZone->AddEnemyRangedUnitCount(1); } if (pEnemyUnit->getDomainType() == DOMAIN_SEA) { pZone->AddEnemyNavalUnitCount(1); } } } } // Set zone for this cell pCell->SetDominanceZone(pZone->GetDominanceZoneID()); }
/// Establish order of zone processing for the turn void CvTacticalAnalysisMap::PrioritizeZones() { // Loop through the dominance zones CvTacticalDominanceZone* pZone; int iBaseValue; int iMultiplier; CvCity* pClosestCity = NULL; for(unsigned int iI = 0; iI < m_DominanceZones.size(); iI++) { // Find the zone and compute dominance here pZone = &m_DominanceZones[iI]; eTacticalDominanceFlags eDominance = ComputeDominance(pZone); // Establish a base value for the region iBaseValue = 1; // Temporary zone? if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_TEMP_ZONE) { iMultiplier = 1000; } else { pClosestCity = pZone->GetClosestCity(); if(pClosestCity) { iBaseValue += (1 + (int)sqrt((float)pZone->GetClosestCity()->getPopulation())); if(pClosestCity->isCapital() && !pClosestCity->GetPlayer()->isMinorCiv()) { iBaseValue *= 2; } if(m_pPlayer->GetTacticalAI()->IsTemporaryZoneCity(pClosestCity)) { iBaseValue *= 20; } else if (pZone->GetClosestCity()->isVisible(m_pPlayer->getTeam(), false)) { iBaseValue *= 4; // How damaged is this visible city? int iMaxDamageMultiplier = 10; int iDamage = pClosestCity->getDamage(); if (iDamage > (pClosestCity->GetMaxHitPoints() / iMaxDamageMultiplier)) { iBaseValue *= (int)((iDamage + 1) * 10 / pClosestCity->GetMaxHitPoints()); } } } if(!pZone->IsWater()) { iBaseValue *= 3; } // Now compute a multiplier based on current conditions here iMultiplier = 1; if(eDominance == TACTICAL_DOMINANCE_ENEMY) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { iMultiplier = 2; } else if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { iMultiplier = 6; } } else if(eDominance == TACTICAL_DOMINANCE_EVEN) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { iMultiplier = 4; } else if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { iMultiplier = 4; } } else if(eDominance == TACTICAL_DOMINANCE_FRIENDLY) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { iMultiplier = 8; } else if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { iMultiplier = 1; } } if(!m_pPlayer->isMinorCiv()) { if(m_pPlayer->GetDiplomacyAI()->GetStateAllWars() == STATE_ALL_WARS_WINNING) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_ENEMY) { iMultiplier *= 2; } } else if(m_pPlayer->GetDiplomacyAI()->GetStateAllWars() == STATE_ALL_WARS_LOSING) { if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_FRIENDLY) { iMultiplier *= 4; } } } } // Save off the value for this zone if((iBaseValue * iMultiplier) <= 0) { FAssertMsg((iBaseValue * iMultiplier) > 0, "Invalid Dominance Zone Value"); } pZone->SetDominanceZoneValue(iBaseValue * iMultiplier); } std::stable_sort(m_DominanceZones.begin(), m_DominanceZones.end()); }
/// Log dominance zone data void CvTacticalAnalysisMap::LogZones() { if(GC.getLogging() && GC.getAILogging()) { CvString szLogMsg; CvTacticalDominanceZone* pZone; for(unsigned int iI = 0; iI < m_DominanceZones.size(); iI++) { pZone = &m_DominanceZones[iI]; szLogMsg.Format("Zone ID: %d, Area ID: %d, Value: %d, FRIENDLY Str: %d (%d), Ranged: %d (%d), ENEMY Str: %d (%d), Ranged: %d (%d), Closest Enemy: %d", pZone->GetDominanceZoneID(), pZone->GetAreaID(), pZone->GetDominanceZoneValue(), pZone->GetFriendlyStrength(), pZone->GetFriendlyUnitCount(), pZone->GetFriendlyRangedStrength(), pZone->GetFriendlyRangedUnitCount(), pZone->GetEnemyStrength(), pZone->GetEnemyUnitCount(), pZone->GetEnemyRangedStrength(), pZone->GetEnemyRangedUnitCount(), pZone->GetRangeClosestEnemyUnit()); if(pZone->GetDominanceFlag() == TACTICAL_DOMINANCE_FRIENDLY) { szLogMsg += ", Friendly"; } else if(pZone->GetDominanceFlag() == TACTICAL_DOMINANCE_ENEMY) { szLogMsg += ", Enemy"; } else if(pZone->GetDominanceFlag() == TACTICAL_DOMINANCE_EVEN) { szLogMsg += ", Even"; } else if(pZone->GetDominanceFlag() == TACTICAL_DOMINANCE_NO_UNITS_VISIBLE) { szLogMsg += ", No Units Visible"; } if(pZone->IsWater()) { szLogMsg += ", Water"; } else { szLogMsg += ", Land"; } if(pZone->GetTerritoryType() == TACTICAL_TERRITORY_TEMP_ZONE) { szLogMsg += ", Temporary Zone"; } else if(pZone->GetClosestCity()) { szLogMsg += ", " + pZone->GetClosestCity()->getName(); if (m_pPlayer->GetTacticalAI()->IsTemporaryZoneCity(pZone->GetClosestCity())) { szLogMsg += " (Temp)"; } } m_pPlayer->GetTacticalAI()->LogTacticalMessage(szLogMsg, true /*bSkipLogDominanceZone*/); } } }