int plotCityXY(const CvCity* pCity, const CvPlot* pPlot)
{

	int iDX;
	int iWrappedDX = dxWrap(pPlot->getX() - pCity->getX());
	int iWrappedDY = dyWrap(pPlot->getY() - pCity->getY());
	int iDY = iWrappedDY;

	// convert to hex-space coordinates - the coordinate system axes are E and NE (not orthogonal)
	int iCityHexX = xToHexspaceX(pCity->getX(), pCity->getY());
	int iPlotHexX = xToHexspaceX(pCity->getX() + iWrappedDX, pCity->getY() + iWrappedDY);

	iDX = dxWrap(iPlotHexX - iCityHexX);

#if defined(MOD_GLOBAL_CITY_WORKING)
	if(hexDistance(iDX, iDY) > pCity->getWorkPlotDistance())
#else
	if(hexDistance(iDX, iDY) > CITY_PLOTS_RADIUS)
#endif
	{
		return -1;
	}
	else
	{
#if defined(MOD_GLOBAL_CITY_WORKING)
		// Regardless of the working radius, we need to offset into the array by the maximum radius
		return GC.getXYCityPlot((iDX + MAX_CITY_RADIUS), (iDY + MAX_CITY_RADIUS));
#else
		return GC.getXYCityPlot((iDX + CITY_PLOTS_RADIUS), (iDY + CITY_PLOTS_RADIUS));
#endif
	}
}
int plotCityXY(const CvCity* pCity, const CvPlot* pPlot)
{

	int iDX;
	int iWrappedDX = dxWrap(pPlot->getX() - pCity->getX());
	int iWrappedDY = dyWrap(pPlot->getY() - pCity->getY());
	int iDY = iWrappedDY;

	// convert to hex-space coordinates - the coordinate system axes are E and NE (not orthogonal)
	int iCityHexX = xToHexspaceX(pCity->getX(), pCity->getY());
	int iPlotHexX = xToHexspaceX(pCity->getX() + iWrappedDX, pCity->getY() + iWrappedDY);

	iDX = dxWrap(iPlotHexX - iCityHexX);

	if(hexDistance(iDX, iDY) > CITY_PLOTS_RADIUS)
	{
		return -1;
	}
	else
	{
		return GC.getXYCityPlot((iDX + CITY_PLOTS_RADIUS), (iDY + CITY_PLOTS_RADIUS));
	}
}
/// Is it valid for this player to found a city here?
bool CvCitySiteEvaluator::CanFound(CvPlot* pPlot, const CvPlayer* pPlayer, bool bTestVisible) const
{
	CvAssert(pPlot);
	if(!pPlot)
		return false;

	CvPlot* pLoopPlot(NULL);
	bool bValid(false);
	int iRange(0), iDX(0), iDY(0);

	// Used to have a Python hook: CANNOT_FOUND_CITY_CALLBACK

	if(GC.getGame().isFinalInitialized())
	{
		if(GC.getGame().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && pPlayer && pPlayer->isHuman())
		{
			if(pPlayer->getNumCities() > 0)
			{
				return false;
			}
		}
	}

	if(pPlot->isImpassable() || pPlot->isMountain())
	{
		return false;
	}

	if(pPlot->getFeatureType() != NO_FEATURE)
	{
		if(GC.getFeatureInfo(pPlot->getFeatureType())->isNoCity())
		{
			return false;
		}
	}

	if(pPlayer)
	{
		if(pPlot->isOwned() && (pPlot->getOwner() != pPlayer->GetID()))
		{
			return false;
		}
	}

	CvTerrainInfo* pTerrainInfo = GC.getTerrainInfo(pPlot->getTerrainType());

	if(!bValid)
	{
		if(pTerrainInfo->isFound())
		{
			bValid = true;
		}
	}

	if(!bValid)
	{
		if(pTerrainInfo->isFoundCoast())
		{
			if(pPlot->isCoastalLand())
			{
				bValid = true;
			}
		}
	}

	if(!bValid)
	{
		if(pTerrainInfo->isFoundFreshWater())
		{
			if(pPlot->isFreshWater())
			{
				bValid = true;
			}
		}
	}

	// Used to have a Python hook: CAN_FOUND_CITIES_ON_WATER_CALLBACK

	if(pPlot->isWater())
	{
		return false;
	}

	if(!bValid)
	{
		return false;
	}

	if(!bTestVisible)
	{
		// look at same land mass
		iRange = GC.getMIN_CITY_RANGE();

		for(iDX = -(iRange); iDX <= iRange; iDX++)
		{
			for(iDY = -(iRange); iDY <= iRange; iDY++)
			{
				pLoopPlot = plotXYWithRangeCheck(pPlot->getX(), pPlot->getY(), iDX, iDY, iRange);

				if(pLoopPlot != NULL)
				{
					if(pLoopPlot->isCity())
					{
						if(pLoopPlot->getLandmass() == pPlot->getLandmass())
						{
							return false;
						}
						else if(hexDistance(iDX, iDY) < iRange)  // one less for off shore
						{
							return false;
						}
					}
				}
			}
		}
	}

	return true;
}
void getRotatedPosition(int inHexspaceX, int inHexspaceY, DirectionTypes rotatedDirection, int& outRotatedX, int& outRotatedY)
{
	outRotatedX = inHexspaceX;
	outRotatedY = inHexspaceY;

	// early out if the facing is NE as that is the base rotation that the data is supposed to be stored in
	// also early out if we are looking at the pivot
	if(DIRECTION_NORTHEAST == rotatedDirection || (inHexspaceX == 0 && inHexspaceY == 0))
	{
		return;
	};

	// find the ring that this is on
	int ring = hexDistance(inHexspaceX, inHexspaceY);

	// find the nearest spike direction
	DirectionTypes spikeDirection = hexspaceSpikeDirection(inHexspaceX, inHexspaceY);

	int spikeX = 0;
	int spikeY = 0;
	switch(spikeDirection)
	{
	case DIRECTION_NORTHEAST:
	{
		spikeY = ring;
	}
	break;
	case DIRECTION_EAST:
	{
		spikeX = ring;
	}
	break;
	case DIRECTION_SOUTHEAST:
	{
		spikeX = ring;
		spikeY = -ring;
	}
	break;
	case DIRECTION_SOUTHWEST:
	{
		spikeY = -ring;
	}
	break;
	case DIRECTION_WEST:
	{
		spikeX = -ring;
	}
	break;
	case DIRECTION_NORTHWEST:
	{
		spikeX = -ring;
		spikeY = ring;
	}
	break;
	}

	// find the offset of this point from the spike
	int offsetOnThisRing = hexDistance(spikeX-inHexspaceX,spikeY-inHexspaceY);

	// find the rotated spike
	int newSpikeX = 0;
	int newSpikeY = 0;
	DirectionTypes newSpikeDirection = (DirectionTypes)((spikeDirection + rotatedDirection) % (NUM_DIRECTION_TYPES));
	switch(newSpikeDirection)
	{
	case DIRECTION_NORTHEAST:
	{
		newSpikeY = ring;
		// add in the offset in the appropriate direction
		outRotatedX = newSpikeX+offsetOnThisRing;
		outRotatedY = newSpikeY-offsetOnThisRing;
	}
	break;
	case DIRECTION_EAST:
	{
		newSpikeX = ring;
		// add in the offset in the appropriate direction
		outRotatedX = newSpikeX;
		outRotatedY = newSpikeY-offsetOnThisRing;
	}
	break;
	case DIRECTION_SOUTHEAST:
	{
		newSpikeX = ring;
		newSpikeY = -ring;
		// add in the offset in the appropriate direction
		outRotatedX = newSpikeX-offsetOnThisRing;
		outRotatedY = newSpikeY;
	}
	break;
	case DIRECTION_SOUTHWEST:
	{
		newSpikeY = -ring;
		// add in the offset in the appropriate direction
		outRotatedX = newSpikeX-offsetOnThisRing;
		outRotatedY = newSpikeY+offsetOnThisRing;
	}
	break;
	case DIRECTION_WEST:
	{
		newSpikeX = -ring;
		// add in the offset in the appropriate direction
		outRotatedX = newSpikeX;
		outRotatedY = newSpikeY+offsetOnThisRing;
	}
	break;
	case DIRECTION_NORTHWEST:
	{
		newSpikeX = -ring;
		newSpikeY = ring;
		// add in the offset in the appropriate direction
		outRotatedX = newSpikeX+offsetOnThisRing;
		outRotatedY = newSpikeY;
	}
	break;
	}
}
bool CvCitySiteEvaluator::CanFound(const CvPlot* pPlot, const CvPlayer* pPlayer, bool bIgnoreDistanceToExistingCities, const CvUnit* pUnit) const
#else
bool CvCitySiteEvaluator::CanFound(CvPlot* pPlot, const CvPlayer* pPlayer, bool bIgnoreDistanceToExistingCities) const
#endif
{
	CvAssert(pPlot);
	if(!pPlot)
		return false;

	CvPlot* pLoopPlot(NULL);
	bool bValid(false);

	// Used to have a Python hook: CANNOT_FOUND_CITY_CALLBACK

	if(GC.getGame().isFinalInitialized())
	{
		if(GC.getGame().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && pPlayer && pPlayer->isHuman())
		{
			if(pPlayer->getNumCities() > 0)
			{
				return false;
			}
		}
	}

	if (pUnit)
	{
		if(!pUnit->canEnterTerrain(*pPlot))
		{
			return false;
		}
	}
	else if(!pPlot->isValidMovePlot(pPlayer ? pPlayer->GetID() : NO_PLAYER ))
	{
		return false;
	}

	if(pPlot->IsNaturalWonder())
		return false;

	if(pPlot->getFeatureType() != NO_FEATURE)
	{
		if(GC.getFeatureInfo(pPlot->getFeatureType())->isNoCity())
		{
			return false;
		}
	}

	if(pPlayer)
	{
		if(pPlot->isOwned() && (pPlot->getOwner() != pPlayer->GetID()))
		{
			return false;
		}
	}

	CvTerrainInfo* pTerrainInfo = GC.getTerrainInfo(pPlot->getTerrainType());

	if(!bValid)
	{
		if(pTerrainInfo->isFound())
		{
			bValid = true;
		}
	}

	if(!bValid)
	{
		if(pTerrainInfo->isFoundCoast())
		{
			if(pPlot->isCoastalLand())
			{
				bValid = true;
			}
		}
	}

	if(!bValid)
	{
		if(pTerrainInfo->isFoundFreshWater())
		{
			if(pPlot->isFreshWater_cached())
			{
				bValid = true;
			}
		}
	}

	// Used to have a Python hook: CAN_FOUND_CITIES_ON_WATER_CALLBACK

	if(pPlot->isWater())
	{
		return false;
	}

	if(!bValid)
	{
		return false;
	}

#if defined(MOD_BALANCE_CORE)
	if(pPlayer && pPlot->IsAdjacentOwnedByOtherTeam(pPlayer->getTeam()))
	{
		return false;
	}

	if(!bIgnoreDistanceToExistingCities)
	{
		int iMinDist(0), iDX(0), iDY(0);
		// look at same land mass

		iMinDist = /*3*/ GC.getMIN_CITY_RANGE();
		if(pPlayer && pPlayer->isMinorCiv())
		{
			if(GC.getMap().getWorldInfo().getMinDistanceCityStates() > 0)
			{
				iMinDist = GC.getMap().getWorldInfo().getMinDistanceCityStates();
			}
		}
		else if(GC.getMap().getWorldInfo().getMinDistanceCities() > 0)
		{
			iMinDist = GC.getMap().getWorldInfo().getMinDistanceCities();
		}

		for(iDX = -(iMinDist); iDX <= iMinDist; iDX++)
		{
			for(iDY = -(iMinDist); iDY <= iMinDist; iDY++)
			{
				pLoopPlot = plotXYWithRangeCheck(pPlot->getX(), pPlot->getY(), iDX, iDY, iMinDist);

				if(pLoopPlot != NULL)
				{
					if(pLoopPlot->isCity())
					{
						if(pLoopPlot->getLandmass() == pPlot->getLandmass())
						{
							return false; //we know that distance <= iMinDist here
						}
						else if(hexDistance(iDX, iDY) < iMinDist)  // one less for off shore
						{
							return false;
						}
					}
				}
			}
		}
	}
#endif

	return true;
}
Example #6
0
/// Get center of mass of units in army (account for world wrap!)
CvPlot* CvArmyAI::GetCenterOfMass(DomainTypes eDomainRequired)
{
	CvPlot* pRtnValue = NULL;
	int iTotalX = 0;
	int iTotalY = 0;
	int iNumUnits = 0;
	UnitHandle pUnit;
	int iReferenceUnitX = -1;
	int iWorldWidth = GC.getMap().getGridWidth();

	pUnit = GetFirstUnit();
	if(pUnit)
	{
		iReferenceUnitX = pUnit->getX();
	}

	while(pUnit)
	{
		int iUnitX = pUnit->getX();

		bool bWorldWrapAdjust = false;
		int iDiff = iUnitX - iReferenceUnitX;
		if(abs(iDiff) > (iWorldWidth / 2))
		{
			bWorldWrapAdjust = true;
		}

		if(bWorldWrapAdjust)
		{
			iTotalX += iUnitX + iWorldWidth;
		}
		else
		{
			iTotalX += iUnitX;
		}
		iTotalY += pUnit->getY();
		iNumUnits++;
		pUnit = GetNextUnit();
	}

	if(iNumUnits > 0)
	{
		int iAverageX = (iTotalX + (iNumUnits / 2)) / iNumUnits;
		if(iAverageX >= iWorldWidth)
		{
			iAverageX = iAverageX - iWorldWidth;
		}
		int iAverageY = (iTotalY + (iNumUnits / 2)) / iNumUnits;
		pRtnValue = GC.getMap().plot(iAverageX, iAverageY);
	}

	// Domain check
	if (eDomainRequired != NO_DOMAIN && pRtnValue)
	{
		if (pRtnValue->isWater() && eDomainRequired == DOMAIN_LAND || !pRtnValue->isWater() && eDomainRequired == DOMAIN_SEA)
		{
			// Find an adjacent plot that works
			for (int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
			{
				CvPlot *pLoopPlot = plotDirection(pRtnValue->getX(), pRtnValue->getY(), ((DirectionTypes)iI));
				if (pLoopPlot != NULL)
				{
					if (pLoopPlot->isWater() && eDomainRequired == DOMAIN_SEA || !pLoopPlot->isWater() && eDomainRequired == DOMAIN_LAND)
					{
						return pLoopPlot;
					}
				}
			}

			// Try two plots out if really having problems
#ifdef AUI_HEXSPACE_DX_LOOPS
			int iMaxDX, iDX;
			CvPlot* pLoopPlot;
			for (int iDY = -2; iDY <= 2; iDY++)
			{
				iMaxDX = 2 - MAX(0, iDY);
				for (iDX = -2 - MIN(0, iDY); iDX <= iMaxDX; iDX++) // MIN() and MAX() stuff is to reduce loops (hexspace!)
				{
					// No need for range check because loops are set up properly
					pLoopPlot = plotXY(pRtnValue->getX(), pRtnValue->getY(), iDX, iDY);
					if (pLoopPlot)
					{
						if (hexDistance(iDX, iDY) == 2)
#else
			for (int iDX = -2; iDX <= 2; iDX++)
			{
				for (int iDY = -2; iDY <= 2; iDY++)
				{
					CvPlot *pLoopPlot = plotXYWithRangeCheck(pRtnValue->getX(), pRtnValue->getY(), iDX, iDY, 2);
					if (pLoopPlot)
					{
#ifdef AUI_FIX_HEX_DISTANCE_INSTEAD_OF_PLOT_DISTANCE
						if (hexDistance(iDX, iDY) == 2)
#else
						if (plotDistance(pRtnValue->getX(), pRtnValue->getY(), pLoopPlot->getX(), pLoopPlot->getY()) == 2)
#endif
#endif
						{
							if (pLoopPlot->isWater() && eDomainRequired == DOMAIN_SEA || !pLoopPlot->isWater() && eDomainRequired == DOMAIN_LAND)
							{
								return pLoopPlot;
							}
						}
					}
				}
			}

			// Give up - just use location of first unit
			pUnit = GetFirstUnit();
			pRtnValue = pUnit->plot();
		}
	}

	return pRtnValue;
}

/// Return distance from this plot of unit in army farthest away
int CvArmyAI::GetFurthestUnitDistance(CvPlot* pPlot)
{
	int iLargestDistance = 0;
	UnitHandle pUnit;
	int iNewDistance;

	pUnit = GetFirstUnit();
	while(pUnit)
	{
		iNewDistance = plotDistance(pUnit->getX(), pUnit->getY(), pPlot->getX(), pPlot->getY());
		if(iNewDistance > iLargestDistance)
		{
			iLargestDistance = iNewDistance;
		}
		pUnit = GetNextUnit();
	}
	return iLargestDistance;
}

// FORMATION ACCESSORS

/// Retrieve index of the formation used by this army
int CvArmyAI::GetFormationIndex() const
{
	return m_iFormationIndex;
}

/// Set index of the formation used by this army
void CvArmyAI::SetFormationIndex(int iFormationIndex)
{
	CvArmyFormationSlot slot;

	if(m_iFormationIndex != iFormationIndex)
	{
		m_iFormationIndex = iFormationIndex;

		CvMultiUnitFormationInfo* thisFormation = GC.getMultiUnitFormationInfo(m_iFormationIndex);
		if(thisFormation)
		{
			int iNumSlots = thisFormation->getNumFormationSlotEntries();

			// Build all the formation entries
			m_FormationEntries.clear();
			for(int iI = 0; iI < iNumSlots; iI++)
			{
				slot.SetUnitID(ARMY_NO_UNIT);
				slot.SetTurnAtCheckpoint(ARMYSLOT_UNKNOWN_TURN_AT_CHECKPOINT);
				m_FormationEntries.push_back(slot);
			}
		}
	}
}

/// How many slots are there in this formation if filled
int CvArmyAI::GetNumFormationEntries() const
{
	return m_FormationEntries.size();
}

/// How many slots do we currently have filled?
int CvArmyAI::GetNumSlotsFilled() const
{
	int iRtnValue = 0;

	for(unsigned int iI = 0; iI < m_FormationEntries.size(); iI++)
	{
		if(m_FormationEntries[iI].m_iUnitID != ARMY_NO_UNIT)
		{
			iRtnValue++;
		}
	}
	return iRtnValue;
}
// Mark cells we can use to bomb a specific target
void CvTacticalAnalysisMap::SetTargetBombardCells(CvPlot* pTarget, int iRange, bool bIgnoreLOS)
{
	int iDX, iDY;
	CvPlot* pLoopPlot;
	int iPlotIndex;
#ifdef AUI_HEXSPACE_DX_LOOPS
	int iMaxDX;
	for (iDY = -iRange; iDY <= iRange; iDY++)
	{
		iMaxDX = iRange - MAX(0, iDY);
		for (iDX = -iRange - MIN(0, iDY); iDX <= iMaxDX; iDX++) // MIN() and MAX() stuff is to reduce loops (hexspace!)
#else
	int iPlotDistance;

	for(iDX = -(iRange); iDX <= iRange; iDX++)
	{
		for(iDY = -(iRange); iDY <= iRange; iDY++)
#endif
		{
#ifdef AUI_HEXSPACE_DX_LOOPS
			if (iDX == 0 && iDY == 0)
				continue;
#endif
			pLoopPlot = plotXY(pTarget->getX(), pTarget->getY(), iDX, iDY);
			if(pLoopPlot != NULL)
			{
#ifndef AUI_HEXSPACE_DX_LOOPS
#ifdef AUI_FIX_HEX_DISTANCE_INSTEAD_OF_PLOT_DISTANCE
				iPlotDistance = hexDistance(iDX, iDY);
#else
				iPlotDistance = plotDistance(pLoopPlot->getX(), pLoopPlot->getY(), pTarget->getX(), pTarget->getY());
#endif
				if(iPlotDistance > 0 && iPlotDistance <= iRange)
#endif
				{
					iPlotIndex = GC.getMap().plotNum(pLoopPlot->getX(), pLoopPlot->getY());
					if(m_pPlots[iPlotIndex].IsRevealed() && !m_pPlots[iPlotIndex].IsImpassableTerrain() && !m_pPlots[iPlotIndex].IsImpassableTerritory())
					{
						if(!m_pPlots[iPlotIndex].IsEnemyCity() && !m_pPlots[iPlotIndex].IsNeutralCity())
						{
							if(bIgnoreLOS || pLoopPlot->canSeePlot(pTarget, m_pPlayer->getTeam(), iRange, NO_DIRECTION))
							{
								m_pPlots[iPlotIndex].SetWithinRangeOfTarget(true);
							}
						}
					}
				}
			}
		}
	}
}

// Mark cells we can use to bomb a specific target
void CvTacticalAnalysisMap::SetTargetFlankBonusCells(CvPlot* pTarget)
{
	CvPlot* pLoopPlot;
	int iPlotIndex;

	// No flank attacks on units at sea (where all combat is bombards)
	if(pTarget->isWater())
	{
		return;
	}

	for(int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
	{
		pLoopPlot = plotDirection(pTarget->getX(), pTarget->getY(), ((DirectionTypes)iI));
		if(pLoopPlot != NULL)
		{
			iPlotIndex = GC.getMap().plotNum(pLoopPlot->getX(), pLoopPlot->getY());
			if(m_pPlots[iPlotIndex].IsRevealed() && !m_pPlots[iPlotIndex].IsImpassableTerrain() && !m_pPlots[iPlotIndex].IsImpassableTerritory())
			{
				if(!m_pPlots[iPlotIndex].IsFriendlyCity() && !m_pPlots[iPlotIndex].IsEnemyCity() && !m_pPlots[iPlotIndex].IsNeutralCity())
				{
					if(!m_pPlots[iPlotIndex].IsFriendlyTurnEndTile() && m_pPlots[iPlotIndex].GetEnemyMilitaryUnit() == NULL)
					{
						m_pPlots[iPlotIndex].SetHelpsProvidesFlankBonus(true);
					}
				}
			}
		}
	}
}