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