int CvPlayerAI::AI_plotTargetMissionAIs(CvPlot* pPlot, MissionAITypes eMissionAI, int iRange) { int iCount = 0; int iLoop; for(CvUnit* pLoopUnit = firstUnit(&iLoop); pLoopUnit; pLoopUnit = nextUnit(&iLoop)) { CvPlot* pMissionPlot = pLoopUnit->GetMissionAIPlot(); if(!pMissionPlot) { continue; } MissionAITypes eGroupMissionAI = pLoopUnit->GetMissionAIType(); if(eGroupMissionAI != eMissionAI) { continue; } int iDistance = plotDistance(pPlot->getX(), pPlot->getY(), pMissionPlot->getX(), pMissionPlot->getY()); if(iDistance == iRange) { iCount++; } } return iCount; }
// 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; int iPlotDistance; for(iDX = -(iRange); iDX <= iRange; iDX++) { for(iDY = -(iRange); iDY <= iRange; iDY++) { pLoopPlot = plotXY(pTarget->getX(), pTarget->getY(), iDX, iDY); if(pLoopPlot != NULL) { iPlotDistance = plotDistance(pLoopPlot->getX(), pLoopPlot->getY(), pTarget->getX(), pTarget->getY()); if(iPlotDistance > 0 && iPlotDistance <= iRange) { 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); } } } } } } } }
///TKe CvSelectionGroup* CvMap::findSelectionGroup(int iX, int iY, PlayerTypes eOwner, bool bReadyToSelect) { int iBestValue = MAX_INT; CvSelectionGroup* pBestSelectionGroup = NULL; for (int iI = 0; iI < MAX_PLAYERS; iI++) { if (GET_PLAYER((PlayerTypes)iI).isAlive()) { if ((eOwner == NO_PLAYER) || (iI == eOwner)) { int iLoop; for(CvSelectionGroup* pLoopSelectionGroup = GET_PLAYER((PlayerTypes)iI).firstSelectionGroup(&iLoop); pLoopSelectionGroup != NULL; pLoopSelectionGroup = GET_PLAYER((PlayerTypes)iI).nextSelectionGroup(&iLoop)) { if (!bReadyToSelect || pLoopSelectionGroup->readyToSelect()) { int iValue = plotDistance(iX, iY, pLoopSelectionGroup->getX(), pLoopSelectionGroup->getY()); if (iValue < iBestValue) { iBestValue = iValue; pBestSelectionGroup = pLoopSelectionGroup; } } } } } } return pBestSelectionGroup; }
// --------------------------------------------------------------------------- bool CvBarbarians::IsPlotValidForBarbCamp(CvPlot* pPlot) { int iRange = 4; int iDY; int iPlotX = pPlot->getX(); int iPlotY = pPlot->getY(); CvMap& kMap = GC.getMap(); for (int iDX = -(iRange); iDX <= iRange; iDX++) { for (iDY = -(iRange); iDY <= iRange; iDY++) { int iLoopPlotX = iPlotX + iDX; int iLoopPlotY = iPlotY + iDY; // Cut off corners if (plotDistance(iPlotX, iPlotY, iLoopPlotX, iLoopPlotY) > iRange) continue; // If the counter is below -1 that means a camp was cleared recently CvPlot* pLoopPlot = kMap.plot(iLoopPlotX, iLoopPlotY); if (pLoopPlot) { if (m_aiPlotBarbCampSpawnCounter[pLoopPlot->GetPlotIndex()] < -1) return false; } } } return true; }
bool CvPlayerAI::AI_captureUnit(UnitTypes, CvPlot* pPlot) { CvCity* pNearestCity; CvAssert(!isHuman()); // Barbs always capture if (isBarbarian()) return true; // we own it if (pPlot->getTeam() == getTeam()) return true; // no man's land - may as well if (pPlot->getTeam() == NO_TEAM) return true; // friendly, sure (okay, this is pretty much just means open borders) if (pPlot->IsFriendlyTerritory(GetID())) return true; // not friendly, but "near" us pNearestCity = GC.getMap().findCity(pPlot->getX(), pPlot->getY(), NO_PLAYER, getTeam()); if (pNearestCity != NULL) { if (plotDistance(pPlot->getX(), pPlot->getY(), pNearestCity->getX(), pNearestCity->getY()) <= 7) return true; } // very near someone we aren't friends with (and far from our nearest city) pNearestCity = GC.getMap().findCity(pPlot->getX(), pPlot->getY()); if (pNearestCity != NULL) { if (plotDistance(pPlot->getX(), pPlot->getY(), pNearestCity->getX(), pNearestCity->getY()) <= 4) return false; } // I'd rather we grab it and run than destroy it return true; }
/// Updates the danger plots values to reflect threats across the map void CvDistanceMap::Update() { const CvMap& map = GC.getMap(); int nPlots = map.numPlots(); m_vDistance = std::vector<int>(nPlots,INT_MAX); m_vClosestFeature = std::vector<int>(nPlots,0); m_bArrayAllocated = true; // since we know there are very few cities compared to the number of plots, // we don't need to do the full distance transform for (int i = 0; i < MAX_PLAYERS; i++) { if (m_ePlayer!=NO_PLAYER && m_ePlayer!=i) continue; // for each city CvPlayer& thisPlayer = GET_PLAYER((PlayerTypes)i); int iCityIndex = 0; for(CvCity* pLoopCity = thisPlayer.firstCity(&iCityIndex); pLoopCity != NULL; pLoopCity = thisPlayer.nextCity(&iCityIndex)) { CvPlot* pCityPlot = pLoopCity->plot(); for (int iPlotIndex=0; iPlotIndex<nPlots; iPlotIndex++) { CvPlot* pPlot = map.plotByIndexUnchecked(iPlotIndex); if (pPlot) { int iDistance = plotDistance( pCityPlot->getX(),pCityPlot->getY(),pPlot->getX(),pPlot->getY() ); bool bUpdate = (iDistance < m_vDistance[iPlotIndex]); //in case of equal distance, take care not to prefer the player with the lower ID if (iDistance == m_vDistance[iPlotIndex]) { PlayerTypes currentOwner = (PlayerTypes) UNPACK_OWNER(m_vClosestFeature[iPlotIndex]); CvCity* pCurrentCity = GET_PLAYER(currentOwner).getCity( UNPACK_ID(m_vClosestFeature[iPlotIndex]) ); bUpdate = (pCurrentCity->getGameTurnFounded() > pLoopCity->getGameTurnFounded()); } if (bUpdate) { m_vDistance[iPlotIndex] = iDistance; m_vClosestFeature[iPlotIndex] = PACK(pLoopCity->getOwner(), pLoopCity->GetID()); } } } } } m_bDirty = false; }
/// Return distance from this plot of unit in army farthest away int CvArmyAI::GetFurthestUnitDistance(CvPlot* pPlot) { int iLargestDistance = 0; CvUnit* pUnit = GetFirstUnit(); while(pUnit) { int iNewDistance = plotDistance(pUnit->getX(), pUnit->getY(), pPlot->getX(), pPlot->getY()); if(iNewDistance > iLargestDistance) { iLargestDistance = iNewDistance; } pUnit = GetNextUnit(pUnit); } return iLargestDistance; }
CvPlot* CvArmyAI::CheckTargetReached(PlayerTypes eEnemy, bool bNavalOp, int iMaxDistance) { //check if we're at the target CvPlot *pTargetPlot = GetGoalPlot(); CvPlot *pCenterOfMass = GetCenterOfMass(NO_DOMAIN); if(pCenterOfMass && pTargetPlot && plotDistance(*pCenterOfMass,*pTargetPlot) <= iMaxDistance) return pTargetPlot; //check early termination if we ran into the enemy if(GetArmyAIState() == ARMYAISTATE_MOVING_TO_DESTINATION) { CvPlot* pEnemyPlot = DetectNearbyEnemy(eEnemy, bNavalOp); if(pEnemyPlot != NULL) { CvCity* pCity = pEnemyPlot->getWorkingCity(); if(pCity != NULL) { if (bNavalOp && pCity->isCoastal() && pCity->waterArea()==pTargetPlot->area()) pEnemyPlot = pCity->plot(); if (!bNavalOp && pCity->area()==pTargetPlot->area()) pEnemyPlot = pCity->plot(); if (pEnemyPlot!=GetGoalPlot()) { if(GC.getLogging() && GC.getAILogging()) { CvString strMsg; strMsg.Format("Switching target from %d,%d to closest city at %d,%d", GetGoalX(), GetGoalY(), pEnemyPlot->getX(), pEnemyPlot->getY() ); GET_PLAYER(m_eOwner).getAIOperation(m_iOperationID)->LogOperationSpecialMessage(strMsg); } } return pEnemyPlot; } } } return NULL; }
// --------------------------------------------------------------------------- bool CvBarbarians::IsPlotValidForBarbCamp(CvPlot* pPlot) { int iRange = 4; int iDY; int iPlotX = pPlot->getX(); int iPlotY = pPlot->getY(); CvMap& kMap = GC.getMap(); for (int iDX = -(iRange); iDX <= iRange; iDX++) { for (iDY = -(iRange); iDY <= iRange; iDY++) { int iLoopPlotX = iPlotX + iDX; int iLoopPlotY = iPlotY + iDY; // Cut off corners if (plotDistance(iPlotX, iPlotY, iLoopPlotX, iLoopPlotY) > iRange) continue; // If the counter is below -1 that means a camp was cleared recently CvPlot* pLoopPlot = kMap.plot(iLoopPlotX, iLoopPlotY); if (pLoopPlot) { if (m_aiPlotBarbCampSpawnCounter[pLoopPlot->GetPlotIndex()] < -1) return false; } } } #if defined(MOD_EVENTS_BARBARIANS) if (MOD_EVENTS_BARBARIANS) { if (GAMEEVENTINVOKE_TESTALL(GAMEEVENT_BarbariansCanFoundCamp, iPlotX, iPlotY) == GAMEEVENTRETURN_FALSE) { return false; } } #endif return true; }
/// 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; }
bool CvPlayerAI::AI_captureUnit(UnitTypes, CvPlot* pPlot) { CvCity* pNearestCity; CvAssert(!isHuman()); // Barbs always capture if (isBarbarian()) return true; if (pPlot->getTeam() == getTeam()) return true; pNearestCity = GC.getMap().findCity(pPlot->getX(), pPlot->getY(), NO_PLAYER, getTeam()); if (pNearestCity != NULL) { if (plotDistance(pPlot->getX(), pPlot->getY(), pNearestCity->getX(), pNearestCity->getY()) <= 4) return true; } return false; }
/// 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); } } } } } }
bool CvMapGenerator::canPlaceBonusAt(BonusTypes eBonus, int iX, int iY, bool bIgnoreLatitude) { PROFILE_FUNC(); CvArea* pArea; CvPlot* pPlot; CvPlot* pLoopPlot; int iRange; int iDX, iDY; int iI; pPlot = GC.getMapINLINE().plotINLINE(iX, iY); pArea = pPlot->area(); if (!(pPlot->canHaveBonus(eBonus, bIgnoreLatitude))) { return false; } long result = 0; if (gDLL->getPythonIFace()->pythonCanPlaceBonusAt(pPlot, &result) && !gDLL->getPythonIFace()->pythonUsingDefaultImpl()) // Python override { if (result >= 0) { return result; } else { FAssertMsg(false, "canPlaceBonusAt() must return >= 0"); } } for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++) { pLoopPlot = plotDirection(iX, iY, ((DirectionTypes)iI)); if (pLoopPlot != NULL) { if ((pLoopPlot->getBonusType() != NO_BONUS) && (pLoopPlot->getBonusType() != eBonus)) { return false; } } } CvBonusInfo& pInfo = GC.getBonusInfo(eBonus); if (pPlot->isWater()) { if (((GC.getMapINLINE().getNumBonusesOnLand(eBonus) * 100) / (GC.getMapINLINE().getNumBonuses(eBonus) + 1)) < pInfo.getMinLandPercent()) { return false; } } // Make sure there are none of the same bonus nearby: iRange = pInfo.getUniqueRange(); for (iDX = -(iRange); iDX <= iRange; iDX++) { for (iDY = -(iRange); iDY <= iRange; iDY++) { pLoopPlot = plotXY(iX, iY, iDX, iDY); if (pLoopPlot != NULL) { if (pLoopPlot->area() == pArea) { if (plotDistance(iX, iY, pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()) <= iRange) { if (pLoopPlot->getBonusType() == eBonus) { return false; } } } } } } return true; }
// Get the maximum damage unit could receive at this plot in the next turn int CvDangerPlotContents::GetDanger(PlayerTypes ePlayer) { if (!m_pPlot) return 0; // Damage from terrain - since we don't know the unit, just assume 20 int iPlotDamage = m_bFlatPlotDamage ? 20 : 0; // Damage from units CvPlot* pAttackerPlot = NULL; for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if ( !pUnit || pUnit->isDelayedDeath() || pUnit->IsDead()) { continue; } pAttackerPlot = NULL; if (pUnit->IsCanAttackRanged()) { if (pUnit->getDomainType() == DOMAIN_AIR) { iPlotDamage += pUnit->GetAirCombatDamage(NULL, NULL, false, 0, m_pPlot); } else { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, NULL, false, 0, m_pPlot); } } else { if (plotDistance(m_iX, m_iY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pUnit->plot(); } //we don't know the defender strength, so assume it's equal to attacker strength! iPlotDamage += pUnit->getCombatDamage( pUnit->GetMaxAttackStrength(pAttackerPlot, m_pPlot, NULL), pUnit->GetBaseCombatStrength()*100, pUnit->getDamage(), false, false, false); if (pUnit->isRangedSupportFire()) { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, NULL, false, 0, m_pPlot, pAttackerPlot); } } } // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (pCity && pCity->getTeam() != GET_PLAYER(ePlayer).getTeam()) iPlotDamage += pCity->rangeCombatDamage(NULL, NULL, false, m_pPlot); } // Damage from features iPlotDamage += GetDamageFromFeatures(ePlayer); return iPlotDamage; }
// Get the maximum damage city could receive this turn if it were in this plot int CvDangerPlotContents::GetDanger(CvCity* pCity, const CvUnit* pPretendGarrison) { if (!m_pPlot || !pCity) return 0; int iPlotDamage = 0; CvPlot* pCityPlot = pCity->plot(); const int iCityX = pCityPlot->getX(); const int iCityY = pCityPlot->getY(); CvCityGarrisonOverride guard(pCity,pPretendGarrison); CvPlot* pAttackerPlot = NULL; CvUnit* pInterceptor = NULL; // Damage from ranged units and melees that cannot capture for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if (!pUnit || pUnit->isDelayedDeath() || pUnit->IsDead()) { continue; } pAttackerPlot = NULL; if (pUnit->IsCanAttackRanged()) { if (pUnit->getDomainType() == DOMAIN_AIR) { pInterceptor = pUnit->GetBestInterceptor(*m_pPlot); int iInterceptDamage = 0; if (pInterceptor) { // Always assume interception is successful iInterceptDamage = pInterceptor->GetInterceptionDamage(pUnit, false); } iPlotDamage += pUnit->GetAirCombatDamage(NULL, pCity, false, iInterceptDamage, m_pPlot); } else { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, m_pPlot); } } else if (pUnit->isNoCapture()) { if (plotDistance(iCityX, iCityY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pUnit->plot(); } iPlotDamage += pUnit->getCombatDamage(pUnit->GetMaxAttackStrength(pAttackerPlot, pCityPlot, NULL), pCity->getStrengthValue(), pUnit->getDamage(), false, false, true); if (pUnit->isRangedSupportFire()) { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, pCityPlot); } } } // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (!pCity || pCity->getTeam() == pCity->getTeam()) { continue; } iPlotDamage += pCity->rangeCombatDamage(NULL, pCity, false, pCityPlot); } // Damage from melee units for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if (!pUnit || pUnit->isDelayedDeath() || pUnit->IsDead()) { continue; } pAttackerPlot = NULL; if (!pUnit->IsCanAttackRanged() && !pUnit->isNoCapture()) { if (plotDistance(iCityX, iCityY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pUnit->plot(); } iPlotDamage += pUnit->getCombatDamage(pUnit->GetMaxAttackStrength(pAttackerPlot, pCityPlot, NULL), pCity->getStrengthValue(), pUnit->getDamage(), false, false, true); if (pUnit->isRangedSupportFire()) { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, pCityPlot); } } } //if we have a garrison, split the damage CvUnit* pGarrison = pCity->GetGarrisonedUnit(); if (pGarrison) { int iUnitShare = (iPlotDamage*2*pGarrison->GetMaxHitPoints())/(pCity->GetMaxHitPoints()+2*pGarrison->GetMaxHitPoints()); iPlotDamage -= iUnitShare; } return iPlotDamage; }
/// Retrieve the relative value of this plot (including plots that would be in city radius) int CvCitySiteEvaluator::PlotFoundValue(CvPlot* pPlot, CvPlayer* pPlayer, YieldTypes eYield, bool) { CvAssert(pPlot); if(!pPlot) return 0; // Make sure this player can even build a city here if(!CanFound(pPlot, pPlayer, false)) { return 0; } int rtnValue = 0; int iFoodValue = 0; int iHappinessValue = 0; int iProductionValue = 0; int iGoldValue = 0; int iScienceValue = 0; int iFaithValue = 0; int iResourceValue = 0; int iStrategicValue = 0; int iCelticForestCount = 0; int iIroquoisForestCount = 0; int iBrazilJungleCount = 0; int iNaturalWonderCount = 0; int iDesertCount = 0; int iWetlandsCount = 0; #if defined(MOD_BALANCE_CORE_SETTLER) int iWaterPlot = 0; int iBadPlot = 0; int iLoopPlots = 0; #endif int iTotalFoodValue = 0; int iTotalHappinessValue = 0; int iTotalProductionValue = 0; int iTotalGoldValue = 0; int iTotalScienceValue = 0; int iTotalFaithValue = 0; int iTotalResourceValue = 0; int iTotalStrategicValue = 0; int iClosestCityOfMine = 999; int iClosestEnemyCity = 999; int iCapitalArea = NULL; bool bIsInca = false; int iAdjacentMountains = 0; if ( pPlayer->getCapitalCity() ) iCapitalArea = pPlayer->getCapitalCity()->getArea(); // Custom code for Inca ideal terrace farm locations ImprovementTypes eIncaImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_TERRACE_FARM", true); if(eIncaImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(eIncaImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { bIsInca = true; } } } for (int iDX = -7; iDX <= 7; iDX++) { for (int iDY = -7; iDY <= 7; iDY++) { CvPlot* pLoopPlot = plotXY(pPlot->getX(), pPlot->getY(), iDX, iDY); if (pLoopPlot != NULL) { int iDistance = plotDistance(pPlot->getX(), pPlot->getY(), pLoopPlot->getX(), pLoopPlot->getY()); if (iDistance <= 7) { if ((pLoopPlot->getOwner() == NO_PLAYER) || (pLoopPlot->getOwner() == pPlayer->GetID())) { // See if there are other cities nearby if (iClosestCityOfMine > iDistance) { if (pLoopPlot->isCity()) { iClosestCityOfMine = iDistance; } } // Skip the city plot itself for now if (iDistance <= 5) { int iRingModifier = m_iRingModifier[iDistance]; iFoodValue = 0; iProductionValue = 0; iGoldValue = 0; iScienceValue = 0; iHappinessValue = 0; iResourceValue = 0; iStrategicValue = 0; #if defined(MOD_GLOBAL_CITY_WORKING) if (iDistance > 0 && iDistance <= pPlayer->getWorkPlotDistance()) #else if (iDistance > 0 && iDistance <= NUM_CITY_RINGS) #endif { if (eYield == NO_YIELD || eYield == YIELD_FOOD) { iFoodValue = iRingModifier * ComputeFoodValue(pLoopPlot, pPlayer) * /*15*/ GC.getSETTLER_FOOD_MULTIPLIER(); } if (eYield == NO_YIELD || eYield == YIELD_PRODUCTION) { iProductionValue = iRingModifier * ComputeProductionValue(pLoopPlot, pPlayer) * /*3*/ GC.getSETTLER_PRODUCTION_MULTIPLIER(); } if (eYield == NO_YIELD || eYield == YIELD_GOLD) { iGoldValue = iRingModifier * ComputeGoldValue(pLoopPlot, pPlayer) * /*2*/ GC.getSETTLER_GOLD_MULTIPLIER(); } if (eYield == NO_YIELD || eYield == YIELD_SCIENCE) { iScienceValue = iRingModifier * ComputeScienceValue(pLoopPlot, pPlayer) * /*1*/ GC.getSETTLER_SCIENCE_MULTIPLIER(); } if (eYield == NO_YIELD || eYield == YIELD_FAITH) { iFaithValue = iRingModifier * ComputeFaithValue(pLoopPlot, pPlayer) * /*1*/ GC.getSETTLER_FAITH_MULTIPLIER(); } } // whether or not we are working these we get the benefit as long as culture can grow to take them if (iDistance <= 5 && pLoopPlot->getOwner() == NO_PLAYER) // there is no benefit if we already own these tiles { iHappinessValue = iRingModifier * ComputeHappinessValue(pLoopPlot, pPlayer) * /*6*/ GC.getSETTLER_HAPPINESS_MULTIPLIER(); iResourceValue = iRingModifier * ComputeTradeableResourceValue(pLoopPlot, pPlayer) * /*1*/ GC.getSETTLER_RESOURCE_MULTIPLIER(); if (iDistance) iStrategicValue = ComputeStrategicValue(pLoopPlot, pPlayer, iDistance) * /*1*/ GC.getSETTLER_STRATEGIC_MULTIPLIER(); // the ring is included in the computation } iTotalFoodValue += iFoodValue; iTotalHappinessValue += iHappinessValue; iTotalProductionValue += iProductionValue; iTotalGoldValue += iGoldValue; iTotalScienceValue += iScienceValue; iTotalFaithValue += iFaithValue; iTotalResourceValue += iResourceValue; iTotalStrategicValue += iStrategicValue; int iPlotValue = iFoodValue + iHappinessValue + iProductionValue + iGoldValue + iScienceValue + iFaithValue + iResourceValue; if (iPlotValue == 0) { // this tile is so bad it gets negatives iPlotValue -= iRingModifier * GC.getSETTLER_FOOD_MULTIPLIER() * 2; } iPlotValue += iStrategicValue; // if this tile is a NW boost the value just so that we force the AI to claim them (if we can work it) #if defined(MOD_GLOBAL_CITY_WORKING) if (pLoopPlot->IsNaturalWonder() && iDistance > 0 && iDistance <= pPlayer->getWorkPlotDistance()) #else if (pLoopPlot->IsNaturalWonder() && iDistance > 0 && iDistance <= NUM_CITY_RINGS) #endif { //iPlotValue += iPlotValue * 2 + 10; iPlotValue += iPlotValue * 2 + 500; } // lower value a lot if we already own this tile if (iPlotValue > 0 && pLoopPlot->getOwner() == pPlayer->GetID()) { #if defined(MOD_BALANCE_CORE_SETTLER) if (MOD_BALANCE_CORE_SETTLER) { iPlotValue *= 2; iPlotValue /= 3; } #else iPlotValue /= 4; #endif } // add this plot into the total rtnValue += iPlotValue; FeatureTypes ePlotFeature = pLoopPlot->getFeatureType(); ImprovementTypes ePlotImprovement = pLoopPlot->getImprovementType(); ResourceTypes ePlotResource = pLoopPlot->getResourceType(); if (ePlotFeature == FEATURE_FOREST) { if (iDistance <= 5) { ++iIroquoisForestCount; if (iDistance == 1) { if (ePlotImprovement == NO_IMPROVEMENT) { ++iCelticForestCount; } } } } else if (ePlotFeature == FEATURE_JUNGLE) { #if defined(MOD_GLOBAL_CITY_WORKING) if (iDistance <= pPlayer->getWorkPlotDistance()) #else if (iDistance <= NUM_CITY_RINGS) #endif { ++iBrazilJungleCount; } } else if (ePlotFeature == FEATURE_MARSH || ePlotFeature == FEATURE_FLOOD_PLAINS) { #if defined(MOD_GLOBAL_CITY_WORKING) if (iDistance <= pPlayer->getWorkPlotDistance()) #else if (iDistance <= NUM_CITY_RINGS) #endif { ++iWetlandsCount; } } if (pLoopPlot->IsNaturalWonder()) { if (iDistance <= 1) { ++iNaturalWonderCount; } } if (pLoopPlot->getTerrainType() == TERRAIN_DESERT) { #if defined(MOD_GLOBAL_CITY_WORKING) if (iDistance <= pPlayer->getWorkPlotDistance()) #else if (iDistance <= NUM_CITY_RINGS) #endif { if (ePlotResource == NO_RESOURCE) { ++iDesertCount; } } } if (bIsInca) { if (pLoopPlot->isHills()) { #if defined(MOD_GLOBAL_CITY_WORKING) if (iDistance <= pPlayer->getWorkPlotDistance()) #else if (iDistance <= NUM_CITY_RINGS) #endif { iAdjacentMountains = pLoopPlot->GetNumAdjacentMountains(); if (iAdjacentMountains > 0 && iAdjacentMountains < 6) { //give the bonus if it's hills, with additional if bordered by mountains rtnValue += m_iIncaMultiplier + (iAdjacentMountains * m_iIncaMultiplier); } } } } #if defined(MOD_BALANCE_CORE_SETTLER) if (MOD_BALANCE_CORE_SETTLER) { if(pLoopPlot->isWater() && pLoopPlot->HasResource(NO_RESOURCE)) { iWaterPlot++; } if(pLoopPlot == NULL || pLoopPlot->isImpassable() || pLoopPlot->getTerrainType() == TERRAIN_SNOW || pLoopPlot->getFeatureType() == FEATURE_ICE) { iBadPlot++; } } #endif } } else // this tile is owned by someone else { // See if there are other cities nearby (only count major civs) if (iClosestEnemyCity > iDistance) { if (pLoopPlot->isCity() && (pLoopPlot->getOwner() < MAX_MAJOR_CIVS)) { iClosestEnemyCity = iDistance; } } } } } #if defined(MOD_BALANCE_CORE_SETTLER) if (MOD_BALANCE_CORE_SETTLER) { iLoopPlots++; } #endif } } if (pPlayer->GetPlayerTraits()->IsFaithFromUnimprovedForest()) { if (iCelticForestCount >= 3) { rtnValue += 2 * 1000 * m_iFlavorMultiplier[YIELD_FAITH]; } else if (iCelticForestCount >= 1) { rtnValue += 1 * 1000 * m_iFlavorMultiplier[YIELD_FAITH]; } } else if (pPlayer->GetPlayerTraits()->IsMoveFriendlyWoodsAsRoad()) { rtnValue += iIroquoisForestCount * 10; } else if (pPlayer->GetPlayerTraits()->GetNaturalWonderYieldModifier() > 0) //ie: Spain { rtnValue += iNaturalWonderCount * m_iSpainMultiplier; } // Custom code for Brazil ImprovementTypes eBrazilImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_BRAZILWOOD_CAMP", true); if(eBrazilImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(eBrazilImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { rtnValue += iBrazilJungleCount * m_iBrazilMultiplier; } } } // Custom code for Morocco ImprovementTypes eMoroccoImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_KASBAH", true); if(eMoroccoImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(eMoroccoImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { rtnValue += iDesertCount * m_iMorrocoMultiplier; } } } //Custom code for Netherlands ImprovementTypes ePolderImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_POLDER", true); if(ePolderImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(ePolderImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { rtnValue += iWetlandsCount * m_iNetherlandsMultiplier; } } } if (rtnValue < 0) rtnValue = 0; // Finally, look at the city plot itself and use it as an overall multiplier if (pPlot->getResourceType(pPlayer->getTeam()) != NO_RESOURCE) { rtnValue += (int)rtnValue * /*-50*/ GC.getBUILD_ON_RESOURCE_PERCENT() / 100; } if (pPlot->isRiver()) { rtnValue += (int)rtnValue * /*15*/ GC.getBUILD_ON_RIVER_PERCENT() / 100; } if (pPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN())) { // okay, coast used to have lots of gold so players settled there "naturally", it doesn't any more, so I am going to give it a nudge in that direction // slewis - removed Brian(?)'s rtnValue adjustment and raised the BUILD_ON_COAST_PERCENT to 40 from 25 //rtnValue += rtnValue > 0 ? 10 : 0; rtnValue += (int)rtnValue * /*40*/ GC.getSETTLER_BUILD_ON_COAST_PERCENT() / 100; int iNavalFlavor = pPlayer->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)m_iNavalIndex); if (iNavalFlavor > 7) { rtnValue += (int)rtnValue * /*40*/ GC.getSETTLER_BUILD_ON_COAST_PERCENT() / 100; } if (pPlayer->getCivilizationInfo().isCoastalCiv()) // we really like the coast (England, Norway, Polynesia, Carthage, etc.) { rtnValue += rtnValue > 0 ? 25 : 0; rtnValue *= 2; } } #if defined(MOD_BALANCE_CORE_SETTLER) if (MOD_BALANCE_CORE_SETTLER) { //Is this a water chokepoint? (More than four would make this a peninsula, which is not what we are looking for.) if(pPlot->IsChokePoint(true, false, 0)) { rtnValue += (int)rtnValue * /*100*/ GC.getBALANCE_CHOKEPOINT_STRATEGIC_VALUE() / 100; } //Looking for mountains for chokepoints. //If the adjacent plot is not touching another mountain, and there is more than one mountain around pPlot, we've found a potential chokepoint. else if(pPlot->IsChokePoint(false, true, 1)) { //Awesome, we found a mountain chokepoint within 1. Emphasize! rtnValue += (int)rtnValue * /*100*/ GC.getBALANCE_CHOKEPOINT_STRATEGIC_VALUE() / 75; } else if(pPlot->IsChokePoint(false, true, 2)) { //Awesome, we found a mountain chokepoint within 2. Emphasize! rtnValue += (int)rtnValue * /*100*/ GC.getBALANCE_CHOKEPOINT_STRATEGIC_VALUE() / 100; } else if(pPlot->IsChokePoint(false, true, 3)) { //Awesome, we found a mountain chokepoint within 3. Emphasize! rtnValue += (int)rtnValue * /*100*/ GC.getBALANCE_CHOKEPOINT_STRATEGIC_VALUE() / 125; } //Too many bad plots? if(iBadPlot > (iLoopPlots / 4)) { rtnValue /= 5; } //Too much empty water? if(iWaterPlot > (iLoopPlots / 2)) { rtnValue *= 2; rtnValue /= 3; } } //Let's see what we can do. (Strategic site locator pulled from below - less loop cycles used this way, as all we care about is the city plot itself, not every plot in the city's loop. if(pPlayer != NULL) { for(int iMajorLoop = 0; iMajorLoop < MAX_MAJOR_CIVS; iMajorLoop++) { PlayerTypes eOtherPlayer = (PlayerTypes) iMajorLoop; PlayerProximityTypes eProximity = GET_PLAYER(eOtherPlayer).GetProximityToPlayer(pPlayer->GetID()); if(eOtherPlayer != pPlayer->GetID() && eOtherPlayer != NO_PLAYER && GET_PLAYER(eOtherPlayer).isAlive() && !GET_PLAYER(eOtherPlayer).isMinorCiv() && !GET_PLAYER(eOtherPlayer).isBarbarian()) { if(eProximity >= PLAYER_PROXIMITY_CLOSE) { CvCity* pLoopTheirCity; int iClosestDistance = 6; int iDistance = 0; int iLoop; for(pLoopTheirCity = GET_PLAYER(eOtherPlayer).firstCity(&iLoop); pLoopTheirCity != NULL; pLoopTheirCity = GET_PLAYER(eOtherPlayer).nextCity(&iLoop)) { if(pLoopTheirCity != NULL) { if(pPlot->getArea() == pLoopTheirCity->getArea()) { iDistance = plotDistance(pPlot->getX(), pPlot->getY(), pLoopTheirCity->getX(), pLoopTheirCity->getY()); if(iDistance <= iClosestDistance) { //There's at least 6 hexes between these plots, which means we can theoretically settle here. int iNumPlots = 0; int iNumBadPlots = 0; for(int iDX = -iClosestDistance; iDX <= iClosestDistance; iDX++) { for(int iDY = -iClosestDistance; iDY <= iClosestDistance; iDY++) { CvPlot* pLoopPlot = plotXYWithRangeCheck(pPlot->getX(), pPlot->getY(), iDX, iDY, iClosestDistance); if(pLoopPlot) { //Let's look for good, empty land. if(pLoopPlot->isImpassable() || pLoopPlot->isWater() || pLoopPlot->getOwner() != NO_PLAYER) { iNumBadPlots++; } iNumPlots++; } } } if(iNumBadPlots > 0) { iNumBadPlots = (iNumBadPlots * 130) / 100; } //Good space must be greater than bad plots by at least 30% if(iNumPlots > iNumBadPlots) { //If there is significant empty land, and it is within the theoretical hex separation of the nearest two cities of two different players, there's a good chance it is border land. GET IT! rtnValue += (int)rtnValue * /*50*/ GC.getBALANCE_EMPIRE_BORDERLAND_STRATEGIC_VALUE() / 100; break; } } } } } } } } } #endif // Nearby Cities? // Human if (pPlayer != NULL && pPlayer->isHuman()) { if (iClosestCityOfMine == 3) { rtnValue /= 2; } } // AI else { int iGrowthFlavor = pPlayer->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)m_iGrowthIndex); int iExpansionFlavor = pPlayer->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)m_iExpansionIndex); int iSweetSpot = 5; iSweetSpot += (iGrowthFlavor > 7) ? 1 : 0; iSweetSpot += (iExpansionFlavor > 7) ? -1 : 0; iSweetSpot += (iGrowthFlavor < 4) ? -1 : 0; iSweetSpot += (iExpansionFlavor < 4) ? 1 : 0; iSweetSpot = max(4,iSweetSpot); iSweetSpot = min(6,iSweetSpot); if (iClosestCityOfMine == iSweetSpot) { // 1.5 was not enough 2.0 was too much, so lets split the difference rtnValue *= 175; rtnValue /= 100; } else if (iClosestCityOfMine < iSweetSpot) { rtnValue /= 2; } else if (iClosestCityOfMine > 7) { rtnValue *= 2; rtnValue /= 3; } // use boldness to decide if we want to push close to enemies int iBoldness = pPlayer->GetDiplomacyAI()->GetBoldness(); if (iBoldness < 4) { if (iClosestEnemyCity <= 4) { rtnValue /= 4; } else if (iClosestEnemyCity == 5) { rtnValue /= 2; } } else if (iBoldness > 7) { if (iClosestEnemyCity <= 5 && iClosestCityOfMine < 8) { rtnValue *= 3; rtnValue /= 2; } } else { if (iClosestEnemyCity < 5) { rtnValue *= 2; rtnValue /= 3; } } // if we are offshore, pull cities in tighter if (iCapitalArea != pPlot->getArea()) { if (iClosestCityOfMine < 7) { rtnValue *= 3; rtnValue /= 2; } } #if defined(MOD_BALANCE_CORE_SETTLER) if (MOD_BALANCE_CORE_SETTLER) { //Let's judge just how well we can defend this city if we push out. If our neighbors are stronger than us, let's turtle a bit. for(int iMajorLoop = 0; iMajorLoop < MAX_MAJOR_CIVS; iMajorLoop++) { PlayerTypes eOtherPlayer = (PlayerTypes) iMajorLoop; if(eOtherPlayer != NO_PLAYER && GET_PLAYER(eOtherPlayer).isAlive() && !GET_PLAYER(eOtherPlayer).isMinorCiv() && !GET_PLAYER(eOtherPlayer).isBarbarian()) { PlayerProximityTypes eProximity = GET_PLAYER(eOtherPlayer).GetProximityToPlayer(pPlayer->GetID()); if(eProximity == PLAYER_PROXIMITY_NEIGHBORS && (pPlayer->GetMilitaryMight() < GET_PLAYER(eOtherPlayer).GetMilitaryMight())) { //Is the plot we're looking at in the same area as our strong neighbor? if (iClosestEnemyCity <= 6) { rtnValue /= 2; } } } } } #endif } rtnValue = (rtnValue > 0) ? rtnValue : 0; return rtnValue; }
/// Retrieve the relative value of this plot (including plots that would be in city radius) int CvCitySiteEvaluator::PlotFoundValue(CvPlot* pPlot, const CvPlayer* pPlayer, YieldTypes eYield, bool /*bCoastOnly*/, CvString* pDebug) { CvAssert(pPlot); if(!pPlot) return -1; // Make sure this player can even build a city here if(!CanFound(pPlot, pPlayer, false)) { if(pDebug) *pDebug = "cannot found"; return -1; } //for debugging std::vector<std::string> vQualifiersPositive; std::vector<std::string> vQualifiersNegative; //total int iTotalPlotValue = 0; int iValueModifier = 0; //general modifiers int iCivModifier = 0; //civ-specific modifiers int iStratModifier = 0; //strategic modifiers int iCelticForestCount = 0; int iIroquoisForestCount = 0; int iBrazilJungleCount = 0; int iNaturalWonderCount = 0; int iDesertCount = 0; int iWetlandsCount = 0; int iLakeCount = 0; int iLuxuryCount = 0; //currently just for debugging int iTotalFoodValue = 0; int iTotalHappinessValue = 0; int iTotalProductionValue = 0; int iTotalGoldValue = 0; int iTotalScienceValue = 0; int iTotalFaithValue = 0; int iTotalResourceValue = 0; int iTotalStrategicValue = 0; //use a slightly negative base value to discourage settling in bad lands int iDefaultPlotValue = -100; int iBorderlandRange = 6; int iCapitalArea = NULL; bool bIsAlmostCoast = false; bool bIsInca = false; int iAdjacentMountains = 0; std::vector<SPlotWithScore> workablePlots; workablePlots.reserve(49); TeamTypes eTeam = pPlayer ? pPlayer->getTeam() : NO_TEAM; PlayerTypes ePlayer = pPlayer ? pPlayer->GetID() : NO_PLAYER; if (pPlayer) { if ( pPlayer->getCapitalCity() ) { iCapitalArea = pPlayer->getCapitalCity()->getArea(); } // Custom code for Inca ideal terrace farm locations ImprovementTypes eIncaImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_TERRACE_FARM", true); if(eIncaImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(eIncaImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { bIsInca = true; } } } } int iRange = pPlayer ? pPlayer->getWorkPlotDistance() : 3; for (int iDX = -iRange; iDX <= iRange; iDX++) { for (int iDY = -iRange; iDY <= iRange; iDY++) { CvPlot* pLoopPlot = plotXY(pPlot->getX(), pPlot->getY(), iDX, iDY); if (pLoopPlot != NULL) { int iDistance = plotDistance(pPlot->getX(), pPlot->getY(), pLoopPlot->getX(), pLoopPlot->getY()); if (iDistance <= iRange) { int iRingModifier = m_iRingModifier[iDistance]; //not only our cities, also other player's cities! int iExistingCityDistance = GC.getGame().GetClosestCityDistance(pLoopPlot); //count the tile only if the city will be able to work it if ( !pLoopPlot->isValidMovePlot(pPlayer->GetID()) || pLoopPlot->getWorkingCity()!=NULL || iExistingCityDistance<=2 ) iRingModifier = 0; if (iExistingCityDistance==3) //this plot will likely be contested between the two cities, reduce its value iRingModifier /= 2; int iPlotValue = iDefaultPlotValue; if (iRingModifier>0) { int iFoodValue = 0; int iHappinessValue = 0; int iProductionValue = 0; int iGoldValue = 0; int iScienceValue = 0; int iFaithValue = 0; int iResourceValue = 0; int iStrategicValue = 0; if (eYield == NO_YIELD || eYield == YIELD_FOOD) iFoodValue = ComputeFoodValue(pLoopPlot, pPlayer) * /*15*/ GC.getSETTLER_FOOD_MULTIPLIER(); if (eYield == NO_YIELD || eYield == YIELD_PRODUCTION) iProductionValue = ComputeProductionValue(pLoopPlot, pPlayer) * /*3*/ GC.getSETTLER_PRODUCTION_MULTIPLIER(); if (eYield == NO_YIELD || eYield == YIELD_GOLD) iGoldValue = ComputeGoldValue(pLoopPlot, pPlayer) * /*2*/ GC.getSETTLER_GOLD_MULTIPLIER(); if (eYield == NO_YIELD || eYield == YIELD_SCIENCE) iScienceValue = ComputeScienceValue(pLoopPlot, pPlayer) * /*1*/ GC.getSETTLER_SCIENCE_MULTIPLIER(); if (eYield == NO_YIELD || eYield == YIELD_FAITH) iFaithValue = ComputeFaithValue(pLoopPlot, pPlayer) * /*1*/ GC.getSETTLER_FAITH_MULTIPLIER(); if (pLoopPlot->getOwner() == NO_PLAYER) // there is no benefit if we already own these tiles { iHappinessValue = ComputeHappinessValue(pLoopPlot, pPlayer) * /*6*/ GC.getSETTLER_HAPPINESS_MULTIPLIER(); iResourceValue = ComputeTradeableResourceValue(pLoopPlot, pPlayer) * /*1*/ GC.getSETTLER_RESOURCE_MULTIPLIER(); if (iDistance) iStrategicValue = ComputeStrategicValue(pLoopPlot, pPlayer, iDistance) * /*1*/ GC.getSETTLER_STRATEGIC_MULTIPLIER(); // the ring is included in the computation } iTotalFoodValue += iFoodValue; iTotalHappinessValue += iHappinessValue; iTotalProductionValue += iProductionValue; iTotalGoldValue += iGoldValue; iTotalScienceValue += iScienceValue; iTotalFaithValue += iFaithValue; iTotalResourceValue += iResourceValue; iTotalStrategicValue += iStrategicValue; iPlotValue += iRingModifier * ( iFoodValue + iHappinessValue + iProductionValue + iGoldValue + iScienceValue + iFaithValue + iResourceValue ) + iStrategicValue; } // for the central plot if (iDistance==0) vQualifiersPositive.push_back( CvString::format("raw plot value %d", iPlotValue).c_str() ); if (iDistance==1 && !pPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN()) && pLoopPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN())) bIsAlmostCoast = true; // if this tile is a NW boost the value just so that we force the AI to claim them (if we can work it) if (pLoopPlot->IsNaturalWonder() && iPlotValue>0) iPlotValue *= 15; // lower value a lot if we already own this tile if (iPlotValue > 0 && pLoopPlot->getOwner() == ePlayer && ePlayer != NO_PLAYER) iPlotValue /= 2; // add this plot into the total workablePlots.push_back( SPlotWithScore(pLoopPlot,iPlotValue) ); // some civ-specific checks FeatureTypes ePlotFeature = pLoopPlot->getFeatureType(); ImprovementTypes ePlotImprovement = pLoopPlot->getImprovementType(); ResourceTypes ePlotResource = pLoopPlot->getResourceType(); if (ePlotFeature == FEATURE_FOREST) { if (iDistance <= 5) { ++iIroquoisForestCount; if (iDistance == 1) if (ePlotImprovement == NO_IMPROVEMENT) ++iCelticForestCount; } } else if (ePlotFeature == FEATURE_JUNGLE) { if (iDistance <= iRange) ++iBrazilJungleCount; } else if (ePlotFeature == FEATURE_MARSH || ePlotFeature == FEATURE_FLOOD_PLAINS) { if (iDistance <= iRange) ++iWetlandsCount; } if (pLoopPlot->IsNaturalWonder()) { if (iDistance <= iRange) ++iNaturalWonderCount; } if (pLoopPlot->isLake()) { if (iDistance <= iRange) ++iLakeCount; } if (pLoopPlot->getResourceType(NO_TEAM) != NO_RESOURCE) { ResourceTypes eResource = pLoopPlot->getResourceType(eTeam); if(eResource != NO_RESOURCE && GC.getResourceInfo(eResource)->getResourceUsage() == RESOURCEUSAGE_LUXURY) { if (iDistance <= iRange) { ++iLuxuryCount; } } } if (pLoopPlot->getTerrainType() == TERRAIN_DESERT) { if (iDistance <= iRange) { if (ePlotResource == NO_RESOURCE) { ++iDesertCount; } } } if (bIsInca) { if (pLoopPlot->isHills() && iDistance <= iRange) { iAdjacentMountains = pLoopPlot->GetNumAdjacentMountains(); if (iAdjacentMountains > 0 && iAdjacentMountains < 6) { //give the bonus if it's hills, with additional if bordered by mountains iCivModifier += (iAdjacentMountains+1) * m_iIncaMultiplier; vQualifiersPositive.push_back("(C) incan hills"); } } } } } } } //take into account only the best 70% of the plots - in the near term the city will not work all plots anyways std::stable_sort( workablePlots.begin(), workablePlots.end() ); size_t iIrrelevantPlots = workablePlots.size()*30/100; for (size_t idx=iIrrelevantPlots; idx<workablePlots.size(); idx++) { SPlotWithScore& ref = workablePlots[idx]; iTotalPlotValue += ref.score; } if (iTotalPlotValue<0) return 0; //civ-specific bonuses if (pPlayer) { if (pPlayer->GetPlayerTraits()->IsFaithFromUnimprovedForest()) { if (iCelticForestCount >= 3) { iCivModifier += 2 * 1000 * m_iFlavorMultiplier[YIELD_FAITH]; vQualifiersPositive.push_back("(C) much forest"); } else if (iCelticForestCount >= 1) { iCivModifier += 1 * 1000 * m_iFlavorMultiplier[YIELD_FAITH]; vQualifiersPositive.push_back("(C) some forest"); } } else if (pPlayer->GetPlayerTraits()->IsMoveFriendlyWoodsAsRoad()) { iCivModifier += iIroquoisForestCount * 10; vQualifiersPositive.push_back("(C) forested"); } else if (pPlayer->GetPlayerTraits()->GetNaturalWonderYieldModifier() > 0) //ie: Spain { iCivModifier += iNaturalWonderCount * m_iSpainMultiplier; vQualifiersPositive.push_back("(C) natural wonders"); } // Custom code for Brazil ImprovementTypes eBrazilImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_BRAZILWOOD_CAMP", true); if(eBrazilImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(eBrazilImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { iCivModifier += iBrazilJungleCount * m_iBrazilMultiplier; vQualifiersPositive.push_back("(C) jungle"); } } } // Custom code for Morocco ImprovementTypes eMoroccoImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_KASBAH", true); if(eMoroccoImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(eMoroccoImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired() && !pkEntry->IsAdjacentCity()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { iCivModifier += iDesertCount * m_iMorrocoMultiplier; vQualifiersPositive.push_back("(C) desert"); } } } // Custom code for France ImprovementTypes eFranceImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_CHATEAU", true); if(eFranceImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(eFranceImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired() && pkEntry->IsAdjacentLuxury()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { iCivModifier += iLuxuryCount * m_iFranceMultiplier; vQualifiersPositive.push_back("(C) luxury"); } } } //Custom code for Netherlands ImprovementTypes ePolderImprovement = (ImprovementTypes)GC.getInfoTypeForString("IMPROVEMENT_POLDER", true); if(ePolderImprovement != NO_IMPROVEMENT) { CvImprovementEntry* pkEntry = GC.getImprovementInfo(ePolderImprovement); if(pkEntry != NULL && pkEntry->IsSpecificCivRequired()) { CivilizationTypes eCiv = pkEntry->GetRequiredCivilization(); if(eCiv == pPlayer->getCivilizationType()) { iCivModifier += iWetlandsCount * m_iNetherlandsMultiplier; if(pkEntry->IsAdjacentLake()) { iCivModifier += (iLakeCount * m_iNetherlandsMultiplier); } vQualifiersPositive.push_back("(C) wetlands"); } } } } // Finally, look at the city plot itself if (pPlot->getResourceType(eTeam) != NO_RESOURCE) { iValueModifier += (int)iTotalPlotValue * /*-50*/ GC.getBUILD_ON_RESOURCE_PERCENT() / 100; vQualifiersNegative.push_back("(V) city on resource"); } if (pPlot->IsNaturalWonder()) { iValueModifier += (int)iTotalPlotValue * /*-50*/ GC.getBUILD_ON_RESOURCE_PERCENT() / 100; vQualifiersNegative.push_back("(V) city on natural wonder"); } if ( iTotalFoodValue>5*iTotalProductionValue || iTotalProductionValue > 2*iTotalFoodValue ) { iValueModifier -= (int)iTotalPlotValue * 10 / 100; vQualifiersNegative.push_back("(V) unbalanced yields"); } if (pPlot->isRiver()) { iValueModifier += (int)iTotalPlotValue * /*15*/ GC.getBUILD_ON_RIVER_PERCENT() / 100; if(pPlayer && pPlayer->GetPlayerTraits()->IsRiverTradeRoad()) iValueModifier += (int)iTotalPlotValue * /*15*/ GC.getBUILD_ON_RIVER_PERCENT() / 100 * 2; vQualifiersPositive.push_back("(V) river"); } if (bIsAlmostCoast) { iValueModifier -= (iTotalPlotValue * 25) / 100; vQualifiersNegative.push_back("(V) almost coast"); } CvArea* pArea = pPlot->area(); int iGoodTiles = 1; if(pArea != NULL) { iGoodTiles = max(1,(pArea->getNumUnownedTiles() - pArea->GetNumBadPlots())); } //Island maps need a little more loose restriction here. if(GC.getMap().GetAIMapHint() & ciMapHint_NavalOffshore) { if (pPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN())) { if(pArea != NULL && iGoodTiles > 0) { iValueModifier += (iTotalPlotValue * /*40*/ GC.getSETTLER_BUILD_ON_COAST_PERCENT()) / 100; vQualifiersPositive.push_back("(V) coast"); if (pPlayer) { int iNavalFlavor = pPlayer->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)m_iNavalIndex); if (iNavalFlavor > 7) { iValueModifier += (iTotalPlotValue * /*40*/ GC.getSETTLER_BUILD_ON_COAST_PERCENT()) / 100; } if (pPlayer->getCivilizationInfo().isCoastalCiv()) // we really like the coast (England, Norway, Polynesia, Carthage, etc.) { iValueModifier += iTotalPlotValue; } } } } else { if(iGoodTiles <= 3) { iValueModifier -= (iTotalPlotValue * 40) / 100; vQualifiersNegative.push_back("(V) not enough good plots"); } else if(iGoodTiles <= 6) { iValueModifier -= (iTotalPlotValue * 20) / 100; vQualifiersNegative.push_back("(V) few good plots"); } } } else { if (pPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN())) { if(pArea != NULL && iGoodTiles > 3) { iValueModifier += (iTotalPlotValue * /*40*/ GC.getSETTLER_BUILD_ON_COAST_PERCENT()) / 100; vQualifiersPositive.push_back("(V) coast"); if (pPlayer) { int iNavalFlavor = pPlayer->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)m_iNavalIndex); if (iNavalFlavor > 7) { iValueModifier += (iTotalPlotValue * /*40*/ GC.getSETTLER_BUILD_ON_COAST_PERCENT()) / 100; } if (pPlayer->getCivilizationInfo().isCoastalCiv()) // we really like the coast (England, Norway, Polynesia, Carthage, etc.) { iValueModifier += iTotalPlotValue; } } } else if(pArea != NULL && iGoodTiles == 1) { iValueModifier -= (iTotalPlotValue * 40) / 100; vQualifiersNegative.push_back("(V) coast on 1-tile island is not great"); } else { iValueModifier -= (iTotalPlotValue * 25) / 100; vQualifiersNegative.push_back("(V) coast on small island"); } } else { if(iGoodTiles <= 5) { iValueModifier -= (iTotalPlotValue * 40) / 100; vQualifiersNegative.push_back("(V) not enough good plots"); } else if(iGoodTiles <= 10) { iValueModifier -= (iTotalPlotValue * 20) / 100; vQualifiersNegative.push_back("(V) few good plots"); } } } //Is this a chokepoint? if(pPlot->IsChokePoint()) { iStratModifier += (iTotalPlotValue * /*100*/ GC.getBALANCE_CHOKEPOINT_STRATEGIC_VALUE()) / 100; vQualifiersPositive.push_back("(S) chokepoint"); //each landbride is a chokepoint, but not every chokepoint is a landbridge if(pPlot->IsLandbridge(12,54)) { iStratModifier += (iTotalPlotValue * /*100*/ GC.getBALANCE_CHOKEPOINT_STRATEGIC_VALUE()) / 100; vQualifiersPositive.push_back("(S) landbridge"); } } //Check for strategic landgrab int iOwnCityDistance = pPlayer ? pPlayer->GetCityDistance(pPlot) : INT_MAX; int iOtherCityDistance = INT_MAX; //check if the closest city is our or somebody else's CvCity* pClosestCity = GC.getGame().GetClosestCity(pPlot); if( pClosestCity && (!pPlayer || pClosestCity->getOwner()!=pPlayer->GetID()) ) { iOtherCityDistance = GC.getGame().GetClosestCityDistance(pPlot); PlayerTypes eOtherPlayer = (PlayerTypes) pClosestCity->getOwner(); PlayerProximityTypes eProximity = GET_PLAYER(eOtherPlayer).GetProximityToPlayer(pPlayer->GetID()); if(eProximity >= PLAYER_PROXIMITY_CLOSE && iOtherCityDistance<=iBorderlandRange) { //Neighbor must not be too strong if ( pPlayer->GetMilitaryMight() > GET_PLAYER(eOtherPlayer).GetMilitaryMight()*1.4f ) { iStratModifier += (iTotalPlotValue * /*50*/ GC.getBALANCE_EMPIRE_BORDERLAND_STRATEGIC_VALUE()) / 100; vQualifiersPositive.push_back("(S) landgrab"); } else if ( pPlayer->GetMilitaryMight() < GET_PLAYER(eOtherPlayer).GetMilitaryMight()*0.8f ) { iStratModifier -= (iTotalPlotValue * /*50*/ GC.getBALANCE_EMPIRE_BORDERLAND_STRATEGIC_VALUE()) / 100; vQualifiersNegative.push_back("(S) too dangerous"); } } } // where is our personal sweet spot? int iMinDistance = /*3*/ GC.getMIN_CITY_RANGE(); if(pPlayer && pPlayer->isMinorCiv()) { if(GC.getMap().getWorldInfo().getMinDistanceCityStates() > 0) { iMinDistance = GC.getMap().getWorldInfo().getMinDistanceCityStates(); } } else if(GC.getMap().getWorldInfo().getMinDistanceCities() > 0) { iMinDistance = GC.getMap().getWorldInfo().getMinDistanceCities(); } if (iOwnCityDistance <= iMinDistance) { //this case should be handled by the distance check in CanFound() also iValueModifier -= iTotalPlotValue / 2; vQualifiersNegative.push_back("(V) too close to existing friendly city"); } // AI only if (pPlayer && !pPlayer->isHuman()) { int iSweetMin = GC.getSETTLER_DISTANCE_DROPOFF_MODIFIER(); int iSweetMax = GC.getSETTLER_DISTANCE_DROPOFF_MODIFIER()+1; //check our preferred balance between tall and wide int iGrowthFlavor = pPlayer->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)m_iGrowthIndex); if (iGrowthFlavor > 5) { iSweetMax++; iSweetMin++; } else if (iGrowthFlavor < 5) { iSweetMax--; iSweetMin--; } int iExpansionFlavor = pPlayer->GetGrandStrategyAI()->GetPersonalityAndGrandStrategy((FlavorTypes)m_iExpansionIndex); if (iExpansionFlavor > 5) { iSweetMax++; iSweetMin++; } else if (iExpansionFlavor < 5) { iSweetMax--; iSweetMin--; } if(iSweetMin < iMinDistance) iSweetMin = iMinDistance; //this affects both friendly and other cities if (min(iOwnCityDistance,iOtherCityDistance) >= iSweetMin && max(iOwnCityDistance,iOtherCityDistance) <= iSweetMax) { iValueModifier += (iTotalPlotValue*20)/100; //make this a small bonus, there is a separate distance check anyway vQualifiersPositive.push_back("(V) optimal distance to existing cities"); } //boldness comes into play when there are enemy cities around int iBoldness = pPlayer->GetDiplomacyAI()->GetBoldness(); if (iBoldness > 5) iSweetMin--; if (iBoldness < 5) iSweetMin++; if (iOtherCityDistance<=iSweetMin) { iStratModifier -= iTotalPlotValue / 2; vQualifiersNegative.push_back("(S) too close to existing enemy city"); } } #if defined(MOD_EVENTS_CITY_FOUNDING) if (MOD_EVENTS_CITY_FOUNDING) { if (GAMEEVENTINVOKE_TESTALL(GAMEEVENT_PlayerCanFoundCity, pPlayer->GetID(), pPlot->getX(), pPlot->getY()) == GAMEEVENTRETURN_FALSE) { return false; } } #endif //logging logging logging if (pDebug) { pDebug->Format("%d,%d,%d,%d", iTotalPlotValue, iValueModifier, iStratModifier, iCivModifier); for (size_t i=0; i<vQualifiersPositive.size();i++) { pDebug->append(",positive: "); pDebug->append(vQualifiersPositive[i].c_str()); } for (size_t i=0; i<vQualifiersNegative.size();i++) { pDebug->append(",negative: "); pDebug->append(vQualifiersNegative[i].c_str()); } } return max(0,iTotalPlotValue + iValueModifier + iStratModifier + iCivModifier); }
///TKs Med CvCity* CvMap::findCity(int iX, int iY, PlayerTypes eOwner, TeamTypes eTeam, bool bSameArea, bool bCoastalOnly, TeamTypes eTeamAtWarWith, DirectionTypes eDirection, CvCity* pSkipCity, bool bRandom) { int iBestValue = MAX_INT; CvCity* pBestCity = NULL; std::vector<CvCity*> aCitys; for (int iI = 0; iI < MAX_PLAYERS; iI++) { if (GET_PLAYER((PlayerTypes)iI).isAlive()) { if ((eOwner == NO_PLAYER) || (iI == eOwner)) { if ((eTeam == NO_TEAM) || (GET_PLAYER((PlayerTypes)iI).getTeam() == eTeam)) { int iLoop; for (CvCity* pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop)) { if (!bSameArea || (pLoopCity->area() == plotINLINE(iX, iY)->area()) || (bCoastalOnly && (pLoopCity->waterArea() == plotINLINE(iX, iY)->area()))) { if (!bCoastalOnly || pLoopCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN())) { if ((eTeamAtWarWith == NO_TEAM) || atWar(GET_PLAYER((PlayerTypes)iI).getTeam(), eTeamAtWarWith)) { if ((eDirection == NO_DIRECTION) || (estimateDirection(dxWrap(pLoopCity->getX_INLINE() - iX), dyWrap(pLoopCity->getY_INLINE() - iY)) == eDirection)) { if ((pSkipCity == NULL) || (pLoopCity != pSkipCity)) { if (!bRandom) { int iValue = plotDistance(iX, iY, pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE()); if (iValue < iBestValue) { iBestValue = iValue; pBestCity = pLoopCity; } } else { aCitys.push_back(pLoopCity); } } } } } } } } } } } if (bRandom) { int iRandom = aCitys.size(); if (iRandom >= 1) { iRandom = GC.getGameINLINE().getSorenRandNum(iRandom, "Random Find City"); return aCitys[iRandom]; } else { return NULL; } } return pBestCity; }
CvCity* CvMap::findTraderCity(int iX, int iY, PlayerTypes eOwner, TeamTypes eTeam, bool bSameArea, bool bCoastalOnly, bool bNative, YieldTypes eNativeYield, int iMinAttitude, CvUnit* pUnit, bool bRandom) { int iBestValue = MAX_INT; CvCity* pBestCity = NULL; CvCity* pHomeCity = NULL; if (pUnit != NULL) { pHomeCity = pUnit->getHomeCity(); if (pHomeCity == NULL) { return NULL; } } std::vector<CvCity*> aCitys; for (int iI = 0; iI < MAX_PLAYERS; iI++) { if (GET_PLAYER((PlayerTypes)iI).isAlive() && (!bNative || GET_PLAYER((PlayerTypes)iI).isNative())) { if ((eOwner == NO_PLAYER) || (eOwner != NO_PLAYER && (GET_PLAYER(eOwner).isNative() != GET_PLAYER((PlayerTypes)iI).isNative()))) { if ((eTeam == NO_TEAM) || GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isOpenBorders(eTeam)) { int iLoop; for (CvCity* pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop)) { int iNativeCityPathTurns; if (pUnit == NULL || pUnit->generatePath(pLoopCity->plot(), 0, true, &iNativeCityPathTurns)) { if (!bSameArea || (pLoopCity->area() == plotINLINE(iX, iY)->area()) || (bCoastalOnly && (pLoopCity->waterArea() == plotINLINE(iX, iY)->area()))) { if (!bCoastalOnly || pLoopCity->isCoastal(GC.getMIN_WATER_SIZE_FOR_OCEAN())) { //if(!bNative || pLoopCity->isNative()) //{ if (eNativeYield == NO_YIELD || (iMinAttitude <= 0 && pLoopCity->AI_getDesiredYield() == eNativeYield) || (pLoopCity->isHuman() && pLoopCity->isImport(eNativeYield))) { int iValue = plotDistance(iX, iY, pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE()); if ((iValue <= iMinAttitude) || iMinAttitude <= 0) { if (!bRandom) { if (iValue < iBestValue) { iBestValue = iValue; pBestCity = pLoopCity; } } else { aCitys.push_back(pLoopCity); } } } //} } } } } } } } } if (bRandom) { int iRandom = aCitys.size(); if (iRandom >= 1) { iRandom = GC.getGameINLINE().getSorenRandNum(iRandom, "Random Find City"); return aCitys[iRandom]; } else { return NULL; } } return pBestCity; }
// ***** // ***** This method gets called pre-game if using a WB map (.civ5map), in which case pPlayer is NULL // ***** /// Value of this site for a civ starting location int CvSiteEvaluatorForStart::PlotFoundValue(CvPlot* pPlot, CvPlayer*, YieldTypes, bool) { int rtnValue = 0; if(!pPlot) return rtnValue; if(!CanFound(pPlot, NULL, false)) { return rtnValue; } // Is there any reason this site doesn't work for a start location? // // Not on top of a goody hut if(pPlot->isGoody()) { return 0; } // We have our own special method of scoring, so don't call the base class for that (like settler version does) for(int iI = 0; iI < RING3_PLOTS; iI++) { CvPlot* pLoopPlot = iterateRingPlots(pPlot->getX(), pPlot->getY(), iI); // Too close to map edge? if(pLoopPlot == NULL) { return 0; } else { int iDistance = plotDistance(pPlot->getX(), pPlot->getY(), pLoopPlot->getX(), pLoopPlot->getY()); int iRingModifier = m_iRingModifier[iDistance]; // Skip the city plot itself for now if(iDistance != 0) { rtnValue += iRingModifier * ComputeFoodValue(pLoopPlot, NULL) * /*6*/ GC.getSTART_AREA_FOOD_MULTIPLIER(); rtnValue += iRingModifier * ComputeHappinessValue(pLoopPlot, NULL) * /*12*/ GC.getSTART_AREA_HAPPINESS_MULTIPLIER(); rtnValue += iRingModifier * ComputeProductionValue(pLoopPlot, NULL) * /*8*/ GC.getSTART_AREA_PRODUCTION_MULTIPLIER(); rtnValue += iRingModifier * ComputeGoldValue(pLoopPlot, NULL) * /*2*/ GC.getSTART_AREA_GOLD_MULTIPLIER(); rtnValue += iRingModifier * ComputeScienceValue(pLoopPlot, NULL) * /*1*/ GC.getSTART_AREA_SCIENCE_MULTIPLIER(); rtnValue += iRingModifier * ComputeFaithValue(pLoopPlot, NULL) * /*1*/ GC.getSTART_AREA_FAITH_MULTIPLIER(); rtnValue += iRingModifier * ComputeTradeableResourceValue(pLoopPlot, NULL) * /*1*/ GC.getSTART_AREA_RESOURCE_MULTIPLIER(); rtnValue += iRingModifier * ComputeStrategicValue(pLoopPlot, NULL, iDistance) * /*1*/ GC.getSTART_AREA_STRATEGIC_MULTIPLIER(); } } } if(rtnValue < 0) rtnValue = 0; // Finally, look at the city plot itself and use it as an overall multiplier if(pPlot->getResourceType() != NO_RESOURCE) { rtnValue += rtnValue * GC.getBUILD_ON_RESOURCE_PERCENT() / 100; } if(pPlot->isRiver()) { rtnValue += rtnValue * GC.getBUILD_ON_RIVER_PERCENT() / 100; } if(pPlot->isCoastalLand()) { rtnValue += rtnValue * GC.getSTART_AREA_BUILD_ON_COAST_PERCENT() / 100; } return rtnValue; }
/// 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; }
int plotDistance(const CvPlot& plotA, const CvPlot& plotB) { return plotDistance(plotA.getX(),plotA.getY(),plotB.getX(),plotB.getY()); }
int cyPlotDistance(int iX, int iY, int iX2, int iY2) { return plotDistance(iX, iY, iX2, iY2); }
/// Recalculate when each unit will arrive at the current army position, whatever that is void CvArmyAI::UpdateCheckpointTurnsAndRemoveBadUnits() { CvAIOperation* pOperation = GET_PLAYER(GetOwner()).getAIOperation(GetOperationID()); //should be updated before calling this ... CvPlot* pCurrentArmyPlot = GC.getMap().plot(GetX(), GetY()); //kick out damaged units, but if we're defending or escorting, no point in running away if (pOperation->IsOffensive()) { for (unsigned int iI = 0; iI < m_FormationEntries.size(); iI++) { if (!m_FormationEntries[iI].IsUsed()) continue; CvUnit* pUnit = GET_PLAYER(m_eOwner).getUnit(m_FormationEntries[iI].m_iUnitID); if (pUnit == NULL) continue; //failsafe - make sure the army ID is set if (pUnit->getArmyID() != GetID()) pUnit->setArmyID(GetID()); //let tactical AI handle those if (pUnit->GetCurrHitPoints() < pUnit->GetMaxHitPoints() / 3) RemoveUnit(m_FormationEntries[iI].GetUnitID()); } } //update for all units in this army. ignore the ones still being built int iGatherTolerance = pOperation->GetGatherTolerance(this, pCurrentArmyPlot); for(unsigned int iI = 0; iI < m_FormationEntries.size(); iI++) { if (!m_FormationEntries[iI].IsUsed()) continue; CvUnit* pUnit = GET_PLAYER(m_eOwner).getUnit(m_FormationEntries[iI].GetUnitID()); if(pUnit && pCurrentArmyPlot) { if(plotDistance(*pUnit->plot(),*pCurrentArmyPlot)<iGatherTolerance) { m_FormationEntries[iI].SetTurnsToCheckpoint(0); } else { //be generous with the flags here, for some ops the muster point may be far away and intermittendly occupied by foreign units ... int iFlags = CvUnit::MOVEFLAG_APPROX_TARGET_RING2 | CvUnit::MOVEFLAG_IGNORE_STACKING | CvUnit::MOVEFLAG_IGNORE_ZOC; int iTurnsToReachCheckpoint = pUnit->TurnsToReachTarget(pCurrentArmyPlot, iFlags, pOperation->GetMaximumRecruitTurns()); //if we're already moving to target, the current army plot is moving, so we cannot check progress against ... if ( iTurnsToReachCheckpoint==INT_MAX || (GetArmyAIState()==ARMYAISTATE_WAITING_FOR_UNITS_TO_CATCH_UP && !m_FormationEntries[iI].IsMakingProgressTowardsCheckpoint(iTurnsToReachCheckpoint)) ) { CvString strMsg; strMsg.Format("Removing %s %d from army %d because no progress to checkpoint (%d:%d). ETA %d, previously %d.", pUnit->getName().c_str(), m_FormationEntries[iI].GetUnitID(), GetID(), pCurrentArmyPlot->getX(), pCurrentArmyPlot->getY(), iTurnsToReachCheckpoint, m_FormationEntries[iI].m_iPrevEstimatedTurnsToCheckpoint); pOperation->LogOperationSpecialMessage(strMsg); RemoveUnit(m_FormationEntries[iI].GetUnitID()); } else m_FormationEntries[iI].SetTurnsToCheckpoint(iTurnsToReachCheckpoint); } } } }
/// 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 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) { if (plotDistance(pRtnValue->getX(), pRtnValue->getY(), pLoopPlot->getX(), pLoopPlot->getY()) == 2) { 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; }
// Get the maximum damage unit could receive at this plot in the next turn (update this with CvUnitCombat changes!) int CvDangerPlotContents::GetDanger(const CvUnit* pUnit, AirActionType iAirAction) { if (!m_pPlot || !pUnit) return 0; // Air units only take damage from interceptions if (pUnit->getDomainType() == DOMAIN_AIR) return GetAirUnitDamage(pUnit, iAirAction); //simple caching for speedup SUnitStats unitStats(pUnit); if (unitStats==m_lastUnit) return m_lastResult; //otherwise calculate from scratch int iPlotDamage = 0; CvCity* pFriendlyCity = NULL; if ( m_pPlot->isFriendlyCity(*pUnit,true) ) pFriendlyCity = m_pPlot->getPlotCity(); // Civilians can be captured - unless they would need to be embarked on this plot if (!pUnit->IsCombatUnit() && pUnit->isNativeDomain(m_pPlot)) { // If plot contains an enemy unit, mark it as max danger if (m_pPlot->getBestDefender(NO_PLAYER, pUnit->getOwner(), NULL, true)) { return MAX_INT; } for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pAttacker = GET_PLAYER(it->first).getUnit(it->second); if ( pAttacker && !pAttacker->isDelayedDeath() && !pAttacker->IsDead()) { // If in a city and the city can be captured, we are in highest danger if (pFriendlyCity) { if (GetDanger(pFriendlyCity) + pFriendlyCity->getDamage() > pFriendlyCity->GetMaxHitPoints()) { return MAX_INT; } } // Look for a possible plot defender else { IDInfo* pUnitNode = m_pPlot->headUnitNode(); CvUnit* pBestDefender = NULL; while (pUnitNode != NULL) { pBestDefender = ::getUnit(*pUnitNode); pUnitNode = m_pPlot->nextUnitNode(pUnitNode); if (pBestDefender && pBestDefender->getOwner() == pUnit->getOwner()) { //fix endless recursion with stacked embarked civilians: defender must also be able to attack if (pBestDefender->IsCanDefend() && pBestDefender->IsCanAttack()) { if (pBestDefender != pUnit) { if (pBestDefender->isWaiting() || !(pBestDefender->canMove())) { break; } } } } pBestDefender = NULL; } // If there is a defender and it might be killed, high danger if (pBestDefender && (pBestDefender->isWaiting() || !pBestDefender->canMove())) { if (GetDanger(pBestDefender) > pBestDefender->GetCurrHitPoints()) { return INT_MAX; } } else if (pBestDefender==NULL) { //Civilian could be captured on this tile return MAX_INT; } } } } // Damage from features (citadel) iPlotDamage += GetDamageFromFeatures(pUnit->getOwner()); iPlotDamage += m_bFlatPlotDamage ? m_pPlot->getTurnDamage(pUnit->ignoreTerrainDamage(), pUnit->ignoreFeatureDamage(), pUnit->extraTerrainDamage(), pUnit->extraFeatureDamage()) : 0; // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (!pCity || pCity->getTeam() == pUnit->getTeam()) continue; iPlotDamage += pCity->rangeCombatDamage(pUnit, NULL, false, m_pPlot); } //update cache m_lastUnit = unitStats; m_lastResult = iPlotDamage; return iPlotDamage; } // Capturing a city with a garrisoned unit destroys the garrisoned unit if (pFriendlyCity) { int iCityDanger = GetDanger(pFriendlyCity, (pUnit->getDomainType() == DOMAIN_LAND ? pUnit : NULL)); if (iCityDanger + pFriendlyCity->getDamage() < pFriendlyCity->GetMaxHitPoints()) { if (pUnit->CanGarrison()) { // Reconstruct the amount of damage the garrison would absorb for the city int iUnitShare = (iCityDanger*2*pUnit->GetMaxHitPoints()) / pFriendlyCity->GetMaxHitPoints(); // Damage from features return iUnitShare + GetDamageFromFeatures(pUnit->getOwner()); } else return 0; } else { return MAX_INT; } } CvPlot* pAttackerPlot = NULL; CvUnit* pInterceptor = NULL; // Damage from units // EXTREMELY IMPORTANT THAT NO RNG IS USED FOR PREDICTION! // Otherwise a tooltip or similar can change the game state for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pAttacker = GET_PLAYER(it->first).getUnit(it->second); if (!pAttacker || pAttacker->isDelayedDeath() || pAttacker->IsDead()) continue; pAttackerPlot = NULL; if (pAttacker->plot() != m_pPlot) { if (pAttacker->IsCanAttackRanged()) { if (pAttacker->getDomainType() == DOMAIN_AIR) { pInterceptor = pAttacker->GetBestInterceptor(*m_pPlot, pUnit); int iInterceptDamage = 0; if (pInterceptor) { // Always assume interception is successful iInterceptDamage = pInterceptor->GetInterceptionDamage(pUnit, false); } iPlotDamage += pAttacker->GetAirCombatDamage(pUnit, NULL, false, iInterceptDamage, m_pPlot); } else { iPlotDamage += pAttacker->GetRangeCombatDamage(pUnit, NULL, false, 0, m_pPlot); } } else { if (plotDistance(m_iX, m_iY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pAttacker->plot(); } iPlotDamage += pAttacker->getCombatDamage( pAttacker->GetMaxAttackStrength(pAttackerPlot, m_pPlot, pUnit), pUnit->GetMaxDefenseStrength(m_pPlot, pAttacker), pAttacker->getDamage(), false, false, false); if (pAttacker->isRangedSupportFire()) { iPlotDamage += pAttacker->GetRangeCombatDamage(pUnit, NULL, false, 0, m_pPlot, pAttackerPlot); } } } } // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (!pCity || pCity->getTeam() == pUnit->getTeam()) continue; iPlotDamage += pCity->rangeCombatDamage(pUnit, NULL, false, m_pPlot); } // Damage from surrounding features (citadel) and the plot itself iPlotDamage += GetDamageFromFeatures(pUnit->getOwner()); iPlotDamage += m_bFlatPlotDamage ? m_pPlot->getTurnDamage(pUnit->ignoreTerrainDamage(), pUnit->ignoreFeatureDamage(), pUnit->extraTerrainDamage(), pUnit->extraFeatureDamage()) : 0; //update cache m_lastUnit = unitStats; m_lastResult = iPlotDamage; //done return iPlotDamage; }
/// Value of this site for a civ starting location int CvSiteEvaluatorForStart::PlotFoundValue(CvPlot* pPlot, CvPlayer* pPlayer, YieldTypes, bool) { int rtnValue = 0; int iI; CvPlot* pLoopPlot(NULL); int iCelticForestCount = 0; CvAssert(pPlot); if(!pPlot) return rtnValue; if(!CanFound(pPlot, pPlayer, false)) { return rtnValue; } // Is there any reason this site doesn't work for a start location? // // Not on top of a goody hut if(pPlot->isGoody()) { return 0; } // We have our own special method of scoring, so don't call the base class for that (like settler version does) #if defined(MOD_GLOBAL_CITY_WORKING) int iLimit = (pPlayer != NULL) ? pPlayer->GetNumWorkablePlots() : AVG_CITY_PLOTS; for(iI = 0; iI < iLimit; iI++) #else for(iI = 0; iI < NUM_CITY_PLOTS; iI++) #endif { pLoopPlot = plotCity(pPlot->getX(), pPlot->getY(), iI); // Too close to map edge? if(pLoopPlot == NULL) { return 0; } else { int iDistance = plotDistance(pPlot->getX(), pPlot->getY(), pLoopPlot->getX(), pLoopPlot->getY()); #if defined(MOD_GLOBAL_CITY_WORKING) if (pPlayer != NULL) { CvAssert(iDistance <= pPlayer->getWorkPlotDistance()); if(iDistance > pPlayer->getWorkPlotDistance()) continue; } else { CvAssert(iDistance <= AVG_CITY_RADIUS); if(iDistance > AVG_CITY_RADIUS) continue; } #else CvAssert(iDistance <= NUM_CITY_RINGS); if(iDistance > NUM_CITY_RINGS) continue; #endif int iRingModifier = m_iRingModifier[iDistance]; // Skip the city plot itself for now if(iDistance != 0) { rtnValue += iRingModifier * ComputeFoodValue(pLoopPlot, pPlayer) * /*6*/ GC.getSTART_AREA_FOOD_MULTIPLIER(); rtnValue += iRingModifier * ComputeHappinessValue(pLoopPlot, pPlayer) * /*12*/ GC.getSTART_AREA_HAPPINESS_MULTIPLIER(); rtnValue += iRingModifier * ComputeProductionValue(pLoopPlot, pPlayer) * /*8*/ GC.getSTART_AREA_PRODUCTION_MULTIPLIER(); rtnValue += iRingModifier * ComputeGoldValue(pLoopPlot, pPlayer) * /*2*/ GC.getSTART_AREA_GOLD_MULTIPLIER(); rtnValue += iRingModifier * ComputeScienceValue(pLoopPlot, pPlayer) * /*1*/ GC.getSTART_AREA_SCIENCE_MULTIPLIER(); rtnValue += iRingModifier * ComputeFaithValue(pLoopPlot, pPlayer) * /*1*/ GC.getSTART_AREA_FAITH_MULTIPLIER(); rtnValue += iRingModifier * ComputeTradeableResourceValue(pLoopPlot, pPlayer) * /*1*/ GC.getSTART_AREA_RESOURCE_MULTIPLIER(); rtnValue += iRingModifier * ComputeStrategicValue(pLoopPlot, pPlayer, iDistance) * /*1*/ GC.getSTART_AREA_STRATEGIC_MULTIPLIER(); } if (pPlayer) { if (iDistance == 1 && pLoopPlot->getFeatureType() == FEATURE_FOREST) { if (pLoopPlot->getImprovementType() == NO_IMPROVEMENT && pPlayer->GetPlayerTraits()->IsFaithFromUnimprovedForest()) { iCelticForestCount += 1; } } } } } if (iCelticForestCount >= 3) { rtnValue += 2 * 1000 * m_iFlavorMultiplier[YIELD_FAITH]; } else if (iCelticForestCount >= 1) { rtnValue += 1 * 1000 * m_iFlavorMultiplier[YIELD_FAITH]; } if(rtnValue < 0) rtnValue = 0; // Finally, look at the city plot itself and use it as an overall multiplier if(pPlot->getResourceType() != NO_RESOURCE) { rtnValue += rtnValue * GC.getBUILD_ON_RESOURCE_PERCENT() / 100; } if(pPlot->isRiver()) { rtnValue += rtnValue * GC.getBUILD_ON_RIVER_PERCENT() / 100; } if(pPlot->isCoastalLand()) { rtnValue += rtnValue * GC.getSTART_AREA_BUILD_ON_COAST_PERCENT() / 100; } return rtnValue; }
int CvMap::maxPlotDistance() { return std::max(1, plotDistance(0, 0, ((isWrapXINLINE()) ? (getGridWidthINLINE() / 2) : (getGridWidthINLINE() - 1)), ((isWrapYINLINE()) ? (getGridHeightINLINE() / 2) : (getGridHeightINLINE() - 1)))); }
/// Get center of mass of units in army (account for world wrap!) CvPlot* CvArmyAI::GetCenterOfMass(float* pfVarX, float* pfVarY) { int iTotalX = 0; int iTotalY = 0; int iNumUnits = 0; UnitHandle pUnit = GetFirstUnit(); if (!pUnit) return NULL; int iTotalX2 = 0; int iTotalY2 = 0; int iWorldWidth = GC.getMap().getGridWidth(); int iWorldHeight = GC.getMap().getGridHeight(); //the first unit is our reference ... int iRefX = pUnit->getX(); int iRefY = pUnit->getY(); iNumUnits++; pUnit = GetNextUnit(); while(pUnit) { int iDX = pUnit->getX() - iRefX; int iDY = pUnit->getY() - iRefY; if (GC.getMap().isWrapX()) { if( iDX > +(iWorldWidth / 2)) iDX -= iWorldWidth; if( iDX < -(iWorldWidth / 2)) iDX += iWorldWidth; } if (GC.getMap().isWrapY()) { if( iDY > +(iWorldHeight / 2)) iDY -= iWorldHeight; if( iDY < -(iWorldHeight / 2)) iDY += iWorldHeight; } iTotalX += iDX; iTotalY += iDY; iTotalX2 += iDX*iDX; iTotalY2 += iDY*iDY; iNumUnits++; pUnit = GetNextUnit(); } if (iNumUnits==0) return NULL; //this is for debugging float fVarX = (iTotalX2 / (float)iNumUnits) - (iTotalX/(float)iNumUnits)*(iTotalX/(float)iNumUnits); float fVarY = (iTotalY2 / (float)iNumUnits) - (iTotalY/(float)iNumUnits)*(iTotalY/(float)iNumUnits); //finally, compute average (with rounding) int iAvgX = (iTotalX + (iNumUnits / 2)) / iNumUnits + iRefX; int iAvgY = (iTotalY + (iNumUnits / 2)) / iNumUnits + iRefY; if (fVarX > 64 || fVarY > 64) { CvString msg = CvString::format("Warning: Army %d with %d units Center of Mass (%d,%d) has a large variance (%.2f,%.2f)\n", GetID(), iNumUnits, iAvgX, iAvgY, fVarX, fVarY); OutputDebugString( msg.c_str() ); } //this handles wrapped coordinates CvPlot* pCOM = GC.getMap().plot(iAvgX, iAvgY); if (!pCOM) return NULL; if (pfVarX) *pfVarX = fVarX; if (pfVarY) *pfVarY = fVarY; //don't return it directly but use the plot of the closest unit pUnit = GetFirstUnit(); std::vector<SPlotWithScore> vPlots; while (pUnit) { if (pUnit->plot()->getDomain()==GetDomainType()) { int iDistToCOM = plotDistance(*pUnit->plot(),*pCOM); int iDistToTarget = plotDistance(pUnit->getX(),pUnit->getY(),GetGoalX(),GetGoalY()); vPlots.push_back( SPlotWithScore(pUnit->plot(),iDistToCOM*100+iDistToTarget) ); } pUnit = GetNextUnit(); } if (vPlots.empty()) return NULL; //this sorts ascending! std::sort(vPlots.begin(),vPlots.end()); return vPlots.front().pPlot; }
// ----------------------------------------------------------------------------------------------- /// Contains the calculations to do the danger value for the plot according to the unit void CvDangerPlots::AssignUnitDangerValue(CvUnit* pUnit, CvPlot* pPlot) { // MAJIK NUMBARS TO MOVE TO XML #if defined(MOD_AI_SMART_ASSIGN_DANGER) int iTurnsAway = 0; #endif int iCombatValueCalc = 100; int iBaseUnitCombatValue = pUnit->GetBaseCombatStrengthConsideringDamage() * iCombatValueCalc; // Combat capable? If not, the calculations will always result in 0, so just skip it. if(iBaseUnitCombatValue > 0) { // Will any danger be zero'ed out? if(!IsDangerByRelationshipZero(pUnit->getOwner(), pPlot)) { //int iDistance = plotDistance(pUnitPlot->getX(), pUnitPlot->getY(), pPlot->getX(), pPlot->getY()); //int iRange = pUnit->baseMoves(); //FAssertMsg(iRange > 0, "0 range? Uh oh"); CvIgnoreUnitsPathFinder& kPathFinder = GC.getIgnoreUnitsPathFinder(); kPathFinder.SetData(pUnit); int iPlotX = pPlot->getX(); int iPlotY = pPlot->getY(); #if defined(MOD_AI_SMART_ASSIGN_DANGER) int pDistance = plotDistance(pUnit->getX(), pUnit->getY(), iPlotX, iPlotY); // Lest substract distance, will not greatly affect danger but will give distant = safer position. iBaseUnitCombatValue -= pDistance; //AMS: Is a ranged unit? if (pUnit->canRangeStrike()) { // Plot is in range and can strike from current position if( pDistance <= pUnit->GetRange() && pUnit->canRangeStrikeAt(pPlot->getX(),pPlot->getY())) { iTurnsAway = 1; } else if(pDistance < pUnit->GetRangePlusMoveToshot()) { if (kPathFinder.GeneratePath(pUnit->getX(), pUnit->getY(), iPlotX, iPlotY, 0, true /*bReuse*/)) { iTurnsAway = 2; } } if (iTurnsAway == 0) { return; } } if (iTurnsAway == 0) { #endif // can the unit actually walk there if(!kPathFinder.GeneratePath(pUnit->getX(), pUnit->getY(), iPlotX, iPlotY, 0, true /*bReuse*/)) { return; } CvAStarNode* pNode = kPathFinder.GetLastNode(); #if defined(MOD_AI_SMART_ASSIGN_DANGER) iTurnsAway = pNode->m_iData2; iTurnsAway = max(iTurnsAway, 1); } #else int iTurnsAway = pNode->m_iData2; iTurnsAway = max(iTurnsAway, 1); #endif int iUnitCombatValue = iBaseUnitCombatValue / iTurnsAway; iUnitCombatValue = ModifyDangerByRelationship(pUnit->getOwner(), pPlot, iUnitCombatValue); AddDanger(iPlotX, iPlotY, iUnitCombatValue, iTurnsAway <= 1); } } }