/// Updates the lookup table void CvDistanceMapTurns::Update() { //performance optimization, reduce pathfinding range int iMaxTurns = GC.getGame().getElapsedGameTurns() == 0 ? 7 : 12; int iVeryFar = iMaxTurns * 6; const CvMap& map = GC.getMap(); int nPlots = map.numPlots(); m_vDistance = std::vector<int>(nPlots, iVeryFar); m_vClosestFeature = std::vector<int>(nPlots,0); m_bArrayAllocated = true; for (int i = 0; i < MAX_PLAYERS; i++) { //if we have a set player, ignore all others 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)) { //slow update only for plots close to the city ReachablePlots turnsFromCity; SPathFinderUserData data(m_ePlayer, PT_GENERIC_REACHABLE_PLOTS, -1, iMaxTurns); turnsFromCity = GC.GetStepFinder().GetPlotsInReach(pLoopCity->plot(), data); for (ReachablePlots::iterator it = turnsFromCity.begin(); it != turnsFromCity.end(); ++it) { int iTurns = it->iTurns; bool bUpdate = (iTurns < m_vDistance[it->iPlotIndex]); //in case of equal distance, take care not to prefer the player with the lower ID if (iTurns == m_vDistance[it->iPlotIndex]) { PlayerTypes currentOwner = (PlayerTypes)UNPACK_OWNER(m_vClosestFeature[it->iPlotIndex]); CvCity* pCurrentCity = GET_PLAYER(currentOwner).getCity(UNPACK_ID(m_vClosestFeature[it->iPlotIndex])); //it can happen that there is no current city if the plot has never been updated because it's very remote bUpdate = (pCurrentCity==NULL) || (pCurrentCity->getGameTurnFounded() > pLoopCity->getGameTurnFounded()); } if (bUpdate) { m_vDistance[it->iPlotIndex] = iTurns; m_vClosestFeature[it->iPlotIndex] = PACK(pLoopCity->getOwner(), pLoopCity->GetID()); } } } } m_bDirty = false; }
/// 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; }
/// Add data for this cell into dominance zone information void CvTacticalAnalysisMap::AddToDominanceZones(int iIndex, CvTacticalAnalysisCell* pCell) { TeamTypes ourTeam = GET_PLAYER(m_ePlayer).getTeam(); CvPlot* pPlot = GC.getMap().plotByIndex(iIndex); // Compute zone data for this cell CvTacticalDominanceZone newZone; newZone.SetAreaID(pPlot->getArea()); newZone.SetWater(pPlot->isWater()); int iCityDistance = GC.getGame().GetClosestCityDistanceInTurns(pPlot); CvCity* pCity = GC.getGame().GetClosestCityByEstimatedTurns(pPlot); PlayerTypes eOwnerPlayer = NO_PLAYER; TeamTypes eOwnerTeam = NO_TEAM; //for plots far away from a city, check the owner if (iCityDistance>2) { eOwnerTeam = pPlot->getTeam(); eOwnerPlayer = pPlot->getOwner(); //there is almost always a closest city, but we're not always interested if (pCity && eOwnerPlayer!=pCity->getOwner()) pCity = NULL; } else if (pCity) //look at the city { eOwnerTeam = pCity->getTeam(); eOwnerPlayer = pCity->getOwner(); } newZone.SetOwner(eOwnerPlayer); newZone.SetZoneCity(pCity); newZone.Extend(pPlot); if(eOwnerTeam==NO_TEAM) { newZone.SetTerritoryType(TACTICAL_TERRITORY_NO_OWNER); } else if(eOwnerTeam == ourTeam) { newZone.SetTerritoryType(TACTICAL_TERRITORY_FRIENDLY); } else if(GET_TEAM(ourTeam).isAtWar(eOwnerTeam)) { newZone.SetTerritoryType(TACTICAL_TERRITORY_ENEMY); } else { newZone.SetTerritoryType(TACTICAL_TERRITORY_NEUTRAL); } // Now see if we already have a matching zone CvTacticalDominanceZone* pZone = MergeWithExistingZone(&newZone); if(!pZone) { // Data populated, now add to vector newZone.SetDominanceZoneID(m_DominanceZones.size()); m_DominanceZones.push_back(newZone); pZone = &m_DominanceZones[m_DominanceZones.size() - 1]; } // Set zone for this cell pCell->SetDominanceZone(pZone->GetDominanceZoneID()); pZone->Extend(pPlot); }
/// 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); }