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