/// 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; }
bool CvSelectionGroupAI::AI_tradeRoutes() { PROFILE_FUNC(); const IDInfo kEurope(getOwnerINLINE(), CvTradeRoute::EUROPE_CITY_ID); CvCity* pPlotCity = plot()->getPlotCity(); CvPlayerAI& kOwner = GET_PLAYER(getOwnerINLINE()); std::set<int>::iterator it; std::map<IDInfo, int> cityValues; std::vector<CvTradeRoute*> routes; std::vector<int> routeValues; std::vector<bool> yieldsDelivered(NUM_YIELD_TYPES, false); std::vector<bool> yieldsToUnload(NUM_YIELD_TYPES, false); std::vector<int> yieldsOnBoard(NUM_YIELD_TYPES, false); if (!isHuman() || (getAutomateType() == AUTOMATE_TRANSPORT_FULL)) { std::vector<CvTradeRoute*> aiRoutes; kOwner.getTradeRoutes(aiRoutes); for (uint i = 0; i < aiRoutes.size(); ++i) { CvTradeRoute* pRoute = aiRoutes[i]; // transport feeder - start - Nightinggale CvCity* pDestinationCity = ::getCity(pRoute->getDestinationCity()); if (pDestinationCity != NULL && pDestinationCity->isAutoImportStopped(pRoute->getYield())) { // ignore trade routes where destination is using feeder service and is full continue; } // transport feeder - end - Nightinggale // traderoute fix - start - Nightinggale if (isHuman() && pRoute->getDestinationCity().eOwner != getOwnerINLINE()) { // humans can't transport to allied cities with fully automated transports continue; } // traderoute fix - end - Nightinggale CvCity* pSourceCity = ::getCity(pRoute->getSourceCity()); CvArea* pSourceWaterArea = pSourceCity->waterArea(); if ((pSourceCity != NULL) && ((getDomainType() != DOMAIN_SEA) || (pSourceWaterArea != NULL))) { int iSourceArea = (getDomainType() == DOMAIN_SEA) ? pSourceWaterArea->getID() : pSourceCity->getArea(); if (getDomainType() == DOMAIN_SEA ? plot()->isAdjacentToArea(iSourceArea) : (iSourceArea == getArea())) { if ((getDomainType() == DOMAIN_SEA) || (pRoute->getDestinationCity() != kEurope)) { routes.push_back(pRoute); routeValues.push_back(0); yieldsDelivered[pRoute->getYield()] = true; if (pPlotCity != NULL && ::getCity(pRoute->getDestinationCity()) == pPlotCity) { yieldsToUnload[pRoute->getYield()] = true; } cityValues[pRoute->getSourceCity()] = 0; cityValues[pRoute->getDestinationCity()] = 0; } } } } } else { for (it = m_aTradeRoutes.begin(); it != m_aTradeRoutes.end(); ++it) { CvTradeRoute* pRoute = kOwner.getTradeRoute(*it); CvCity* pSourceCity = ::getCity(pRoute->getSourceCity()); if (pSourceCity != NULL) { CvArea* pSourceWaterArea = pSourceCity->waterArea(); if (getDomainType() != DOMAIN_SEA || pSourceWaterArea != NULL) { int iSourceArea = (getDomainType() == DOMAIN_SEA) ? pSourceWaterArea->getID() : pSourceCity->getArea(); if (getDomainType() == DOMAIN_SEA ? plot()->isAdjacentToArea(iSourceArea) : (iSourceArea == getArea())) { if ((getDomainType() == DOMAIN_SEA) || (pRoute->getDestinationCity() != kEurope)) { routes.push_back(pRoute); routeValues.push_back(0); yieldsDelivered[pRoute->getYield()] = true; if (pPlotCity != NULL && ::getCity(pRoute->getDestinationCity()) == pPlotCity) { yieldsToUnload[pRoute->getYield()] = true; } cityValues[pRoute->getSourceCity()] = 0; cityValues[pRoute->getDestinationCity()] = 0; } } else { FAssertMsg(false, "Unexpected : Unit can't run trade route it's assigned to"); } } } } } if ((pPlotCity != NULL) && hasCargo()) { std::vector<CvUnit*> units; //Unload everything which we should unload here, or can't unload anywhere... CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode(); while (pUnitNode != NULL) { CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = plot()->nextUnitNode(pUnitNode); if (pLoopUnit != NULL) { YieldTypes eYield = pLoopUnit->getYield(); CvUnit* pTransport = pLoopUnit->getTransportUnit(); if ((eYield != NO_YIELD) && pTransport != NULL && (yieldsToUnload[eYield] || !(yieldsDelivered[eYield]))) { if (pTransport->getGroup() == this && pLoopUnit->canUnload()) { units.push_back(pLoopUnit); } } } } for (uint i = 0; i < units.size(); ++i) { units[i]->unload(); } } short aiYieldsLoaded[NUM_YIELD_TYPES]; AI_getYieldsLoaded(aiYieldsLoaded); bool bNoCargo = true; for (int i = 0; i < NUM_YIELD_TYPES; ++i) { if (aiYieldsLoaded[i] > 0) { bNoCargo = false; break; } } if (!bNoCargo) { //We need to iterate over every destination city and see if we can unload. for (uint i = 0; i < routes.size(); ++i) { CvCity* pDestinationCity = ::getCity(routes[i]->getDestinationCity()); if ((pDestinationCity == NULL) || (pDestinationCity != pPlotCity)) { int iRouteValue = kOwner.AI_transferYieldValue(routes[i]->getDestinationCity(), routes[i]->getYield(), aiYieldsLoaded[routes[i]->getYield()]); if (iRouteValue > 0) { cityValues[routes[i]->getDestinationCity()] += iRouteValue; routeValues[i] += iRouteValue; } } } } //We need to iterate over every source city, and see if there's anything which needs moving to the respective destination city. //We apply some bias to the city we are presently at, but not too much - sometimes empty runs need to be made... //Basically this looks at the entire NEXT trade run (source-city to dest-city), with some bias given towards //starting it from pPlotCity as sourceCity. //If we are carrying cargo, only count cities where we can unload. for (uint i = 0; i < routes.size(); ++i) { CvCity* pSourceCity = ::getCity(routes[i]->getSourceCity()); if ((pSourceCity != NULL) && (bNoCargo || (cityValues[routes[i]->getSourceCity()] > 0))) { CvCity* pDestinationCity = ::getCity(routes[i]->getDestinationCity()); YieldTypes eYield = routes[i]->getYield(); // transport feeder - start - Nightinggale //int iAmount = pSourceCity->getYieldStored(eYield) - pSourceCity->getMaintainLevel(eYield); int iAmount = pSourceCity->getYieldStored(eYield) - pSourceCity->getAutoMaintainThreshold(eYield); // transport feeder - end - Nightinggale if (iAmount > 0) { int iExportValue = kOwner.AI_transferYieldValue(routes[i]->getSourceCity(), routes[i]->getYield(), -iAmount); int iImportValue = kOwner.AI_transferYieldValue(routes[i]->getDestinationCity(), routes[i]->getYield(), iAmount); int iRouteValue = (iExportValue + iImportValue + 2 * std::min(iExportValue, iImportValue)) / 4; if (pSourceCity == pPlotCity) { cityValues[routes[i]->getDestinationCity()] += 2 * iRouteValue; } else { cityValues[routes[i]->getSourceCity()] += iRouteValue; } routeValues[i] = iRouteValue; } } } IDInfo kBestDestination(NO_PLAYER, -1); int iBestDestinationValue = 0; for (std::map<IDInfo, int>::iterator it = cityValues.begin(); it != cityValues.end(); ++it) { int iValue = it->second; if (iValue > 0) { CvCity* pCity = ::getCity(it->first); if (pCity != NULL) { FAssert(!atPlot(pCity->plot())); if (generatePath(plot(), pCity->plot(), MOVE_NO_ENEMY_TERRITORY, true)) { iValue /= 1 + kOwner.AI_plotTargetMissionAIs(pCity->plot(), MISSIONAI_TRANSPORT, this, 0); } else { iValue = 0; } } if (iValue > iBestDestinationValue) { iBestDestinationValue = iValue; kBestDestination = it->first; } } } if ((pPlotCity != NULL) && (kBestDestination.eOwner != NO_PLAYER)) { //We need to keep looping and recalculating //For example a city might have "101" of an item, we want to move the first 100 but not the 1. //But it could also have 200, in which case we might want 2 loads of 100... //But it could also have 200 of two resources, and we'd want to move 100 of each... ///TKs MEd int iTestCount = 0; while (!isFull()) { iTestCount++; if (iTestCount == 1000) { FAssert(iTestCount == 0); int iID = pPlotCity->getID(); int iIDplayer = GET_PLAYER(kBestDestination.eOwner).getID(); // break; } if (iTestCount == 1001) { break; } ///TKe int iBestRoute = -1; int iBestRouteValue = 0; //Now, for any trade routes which this group is assigned to, try to pick up cargo here. for (uint i = 0; i < routes.size(); ++i) { CvCity* pSourceCity = ::getCity(routes[i]->getSourceCity()); if ((pSourceCity != NULL) && (routes[i]->getDestinationCity() == kBestDestination)) { CvCity* pDestinationCity = ::getCity(routes[i]->getDestinationCity()); YieldTypes eYield = routes[i]->getYield(); if ((pPlotCity == pSourceCity)) { // transport feeder - start - Nightinggale //int iAmount = pSourceCity->getYieldStored(eYield) - pSourceCity->getMaintainLevel(eYield); int iAmount = pSourceCity->getYieldStored(eYield) - pSourceCity->getAutoMaintainThreshold(eYield); // transport feeder - end - Nightinggale if (iAmount > 0) { int iExportValue = kOwner.AI_transferYieldValue(routes[i]->getSourceCity(), routes[i]->getYield(), -iAmount); int iImportValue = kOwner.AI_transferYieldValue(routes[i]->getDestinationCity(), routes[i]->getYield(), iAmount); int iRouteValue = (iExportValue + iImportValue + 2 * std::min(iExportValue, iImportValue)) / 4; if (iRouteValue > iBestRouteValue) { iBestRouteValue = iRouteValue; iBestRoute = i; } } } } } if (iBestRouteValue > 0) { CLLNode<IDInfo>* pUnitNode = headUnitNode(); while (pUnitNode != NULL) { CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = nextUnitNode(pUnitNode); if (pLoopUnit != NULL) { if (pLoopUnit->canLoadYield(plot(), routes[iBestRoute]->getYield(), false)) { pLoopUnit->loadYield(routes[iBestRoute]->getYield(), false); break; } } } } else { break; } } //XXX fill hold. } if ((kBestDestination.eOwner == NO_PLAYER) && hasCargo()) { // Transport group is full and can't find any destination CvCity* pCity = kOwner.AI_findBestPort(); if (pCity != NULL && !atPlot(pCity->plot())) { kBestDestination = pCity->getIDInfo(); } } //As a final step, we could consider loading yields which would be useful as parts of delivery runs... if (kBestDestination != kEurope) { CvCity* pBestDestinationCity = ::getCity(kBestDestination); if (pBestDestinationCity != NULL) { FAssert(!atPlot(pBestDestinationCity->plot())); pushMission(MISSION_MOVE_TO, pBestDestinationCity->getX_INLINE(), pBestDestinationCity->getY_INLINE(), MOVE_NO_ENEMY_TERRITORY, false, false, MISSIONAI_TRANSPORT, pBestDestinationCity->plot()); if (atPlot(pBestDestinationCity->plot())) { //Unload any goods if required (we can always pick them back up if this is an i+e city). std::vector<CvUnit*> units; CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode(); CvUnit* pLoopUnit; while (pUnitNode != NULL) { pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = plot()->nextUnitNode(pUnitNode); YieldTypes eYield = pLoopUnit->getYield(); if ((eYield != NO_YIELD) && pLoopUnit->isCargo()) { if (pLoopUnit->getTransportUnit()->getGroup() == this && pLoopUnit->canUnload()) { units.push_back(pLoopUnit); } } } for (uint i = 0; i < units.size(); ++i) { units[i]->unload(); } } return true; } } else { if (isHuman()) { getHeadUnit()->AI_setUnitAIState(UNITAI_STATE_SAIL); } } return false; }