// Find all our enemies (combat units) void CvTacticalAnalysisMap::BuildEnemyUnitList() { m_EnemyUnits.clear(); m_EnemyCities.clear(); TeamTypes ourTeam = GET_PLAYER(m_ePlayer).getTeam(); for(int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++) { const PlayerTypes ePlayer = (PlayerTypes)iPlayer; CvPlayer& kPlayer = GET_PLAYER(ePlayer); const TeamTypes eTeam = kPlayer.getTeam(); // for each opposing civ if(kPlayer.isAlive() && GET_TEAM(eTeam).isAtWar(ourTeam)) { int iLoop; CvUnit* pLoopUnit = NULL; CvCity* pLoopCity; for(pLoopCity = kPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kPlayer.nextCity(&iLoop)) if (pLoopCity->plot()->isRevealed(ourTeam)) m_EnemyCities.push_back(pLoopCity->GetIDInfo()); for(pLoopUnit = kPlayer.firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = kPlayer.nextUnit(&iLoop)) if(pLoopUnit->IsCanAttack() && pLoopUnit->plot()->isVisible(ourTeam)) m_EnemyUnits.push_back(pLoopUnit->GetIDInfo()); } } }
/// 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; }
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; }
/// Updates the danger plots values to reflect threats across the map void CvDangerPlots::UpdateDanger (bool bPretendWarWithAllCivs, bool bIgnoreVisibility) { // danger plots have not been initialized yet, so no need to update if (!m_bArrayAllocated) { return; } // wipe out values int iGridSize = GC.getMap().numPlots(); CvAssertMsg(iGridSize == m_DangerPlots.size(), "iGridSize does not match number of DangerPlots"); for (int i = 0; i < iGridSize; i++) { m_DangerPlots[i] = 0; } CvPlayer& thisPlayer = GET_PLAYER(m_ePlayer); TeamTypes thisTeam = thisPlayer.getTeam(); // for each opposing civ for (int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++) { PlayerTypes ePlayer = (PlayerTypes)iPlayer; CvPlayer& loopPlayer = GET_PLAYER(ePlayer); TeamTypes eTeam = loopPlayer.getTeam(); if (!loopPlayer.isAlive()) { continue; } if (eTeam == thisTeam) { continue; } if (ShouldIgnorePlayer(ePlayer) && !bPretendWarWithAllCivs) { continue; } //for each unit int iLoop; CvUnit* pLoopUnit = NULL; for (pLoopUnit = loopPlayer.firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = loopPlayer.nextUnit(&iLoop)) { if (ShouldIgnoreUnit(pLoopUnit, bIgnoreVisibility)) { continue; } int iRange = pLoopUnit->baseMoves(); if (pLoopUnit->canRangeStrike()) { iRange += pLoopUnit->GetRange(); } CvPlot* pUnitPlot = pLoopUnit->plot(); AssignUnitDangerValue(pLoopUnit, pUnitPlot); CvPlot* pLoopPlot = NULL; for (int iDX = -(iRange); iDX <= iRange; iDX++) { for (int iDY = -(iRange); iDY <= iRange; iDY++) { pLoopPlot = plotXYWithRangeCheck(pUnitPlot->getX(), pUnitPlot->getY(), iDX, iDY, iRange); if (!pLoopPlot || pLoopPlot == pUnitPlot) { continue; } if (!pLoopUnit->canMoveOrAttackInto(*pLoopPlot) && !pLoopUnit->canRangeStrikeAt(pLoopPlot->getX(),pLoopPlot->getY())) { continue; } AssignUnitDangerValue(pLoopUnit, pLoopPlot); } } } // for each city CvCity* pLoopCity; for (pLoopCity = loopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = loopPlayer.nextCity(&iLoop)) { if (ShouldIgnoreCity(pLoopCity, bIgnoreVisibility)) { continue; } int iRange = GC.getCITY_ATTACK_RANGE(); CvPlot* pCityPlot = pLoopCity->plot(); AssignCityDangerValue(pLoopCity, pCityPlot); CvPlot* pLoopPlot = NULL; for (int iDX = -(iRange); iDX <= iRange; iDX++) { for (int iDY = -(iRange); iDY <= iRange; iDY++) { pLoopPlot = plotXYWithRangeCheck(pCityPlot->getX(), pCityPlot->getY(), iDX, iDY, iRange); if (!pLoopPlot) { continue; } AssignCityDangerValue(pLoopCity, pLoopPlot); } } } } // Citadels int iCitadelValue = GetDangerValueOfCitadel(); int iPlotLoop; CvPlot *pPlot, *pAdjacentPlot; for (iPlotLoop = 0; iPlotLoop < GC.getMap().numPlots(); iPlotLoop++) { pPlot = GC.getMap().plotByIndexUnchecked(iPlotLoop); if (pPlot->isRevealed(thisTeam)) { ImprovementTypes eImprovement = pPlot->getRevealedImprovementType(thisTeam); if (eImprovement != NO_IMPROVEMENT && GC.getImprovementInfo(eImprovement)->GetNearbyEnemyDamage() > 0) { if (!ShouldIgnoreCitadel(pPlot, bIgnoreVisibility)) { for (int iI = 0; iI < NUM_DIRECTION_TYPES; iI++) { pAdjacentPlot = plotDirection(pPlot->getX(), pPlot->getY(), ((DirectionTypes)iI)); if (pAdjacentPlot != NULL) { AddDanger(pAdjacentPlot->getX(), pAdjacentPlot->getY(), iCitadelValue); } } } } } } // testing city danger values CvCity* pLoopCity; int iLoopCity = 0; for (pLoopCity = thisPlayer.firstCity(&iLoopCity); pLoopCity != NULL; pLoopCity = thisPlayer.nextCity(&iLoopCity)) { int iThreatValue = GetCityDanger(pLoopCity); pLoopCity->SetThreatValue(iThreatValue); } m_bDirty = false; }
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; }
/// Updates the danger plots values to reflect threats across the map void CvDangerPlots::UpdateDanger(bool bPretendWarWithAllCivs, bool bIgnoreVisibility) { // danger plots have not been initialized yet, so no need to update if(!m_bArrayAllocated) return; // wipe out values int iGridSize = GC.getMap().numPlots(); CvAssertMsg(iGridSize == m_DangerPlots.size(), "iGridSize does not match number of DangerPlots"); for(int i = 0; i < iGridSize; i++) { m_DangerPlots[i].clear(); } //units we know from last turn UnitSet previousKnownUnits = m_knownUnits; m_knownUnits.clear(); CvPlayer& thisPlayer = GET_PLAYER(m_ePlayer); TeamTypes thisTeam = thisPlayer.getTeam(); // for each opposing civ for(int iPlayer = 0; iPlayer < MAX_PLAYERS; iPlayer++) { PlayerTypes ePlayer = (PlayerTypes)iPlayer; CvPlayer& loopPlayer = GET_PLAYER(ePlayer); TeamTypes eTeam = loopPlayer.getTeam(); if(!loopPlayer.isAlive()) continue; if(eTeam == thisTeam) continue; if(ShouldIgnorePlayer(ePlayer) && !bPretendWarWithAllCivs) continue; //for each unit int iLoop; CvUnit* pLoopUnit = NULL; for(pLoopUnit = loopPlayer.firstUnit(&iLoop); pLoopUnit != NULL; pLoopUnit = loopPlayer.nextUnit(&iLoop)) { UpdateDangerSingleUnit(pLoopUnit, bIgnoreVisibility, true); } // for each city CvCity* pLoopCity; for(pLoopCity = loopPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = loopPlayer.nextCity(&iLoop)) { if(ShouldIgnoreCity(pLoopCity, bIgnoreVisibility)) continue; #if defined(MOD_EVENTS_CITY_BOMBARD) bool bIndirectFireAllowed = false; int iRange = pLoopCity->getBombardRange(bIndirectFireAllowed); #else int iRange = GC.getCITY_ATTACK_RANGE(); #endif CvPlot* pCityPlot = pLoopCity->plot(); CvPlot* pLoopPlot = NULL; for(int iDX = -(iRange); iDX <= iRange; iDX++) { for(int iDY = -(iRange); iDY <= iRange; iDY++) { pLoopPlot = plotXYWithRangeCheck(pCityPlot->getX(), pCityPlot->getY(), iDX, iDY, iRange); if(!pLoopPlot || pLoopPlot == pCityPlot) continue; #if defined(MOD_EVENTS_CITY_BOMBARD) if (!bIndirectFireAllowed && !pCityPlot->canSeePlot(pLoopPlot, NO_TEAM, iRange, NO_DIRECTION)) continue; #endif AssignCityDangerValue(pLoopCity, pLoopPlot); } } } } // now compare the new known units with the previous known units for (UnitSet::iterator it = previousKnownUnits.begin(); it != previousKnownUnits.end(); ++it) { //might have made peace ... if (ShouldIgnorePlayer(it->first)) continue; if (m_knownUnits.find(*it) == m_knownUnits.end()) { CvUnit* pVanishedUnit = GET_PLAYER(it->first).getUnit(it->second); //it's still there, but moved out of sight - nevertheless count is, a human would do that as well //do not add it to the known units though, so next turn we will have forgotten about it if (pVanishedUnit) UpdateDangerSingleUnit(pVanishedUnit, true, false); } } int iPlotLoop; CvPlot* pPlot, *pAdjacentPlot; for(iPlotLoop = 0; iPlotLoop < GC.getMap().numPlots(); iPlotLoop++) { pPlot = GC.getMap().plotByIndexUnchecked(iPlotLoop); if(pPlot->isRevealed(thisTeam)) { //remember the plot based damage, but it depends on the unit's promotions also, so we won't apply it directly int iPlotDamage = 0; if (pPlot->getFeatureType() != NO_FEATURE) iPlotDamage += (GC.getFeatureInfo(pPlot->getFeatureType())->getTurnDamage()); if (pPlot->getTerrainType() != NO_FEATURE) iPlotDamage += (GC.getTerrainInfo(pPlot->getTerrainType())->getTurnDamage()); m_DangerPlots[iPlotLoop].m_bFlatPlotDamage = (iPlotDamage>0); ImprovementTypes eImprovement = pPlot->getRevealedImprovementType(thisTeam); if(eImprovement != NO_IMPROVEMENT && GC.getImprovementInfo(eImprovement)->GetNearbyEnemyDamage() > 0) { if(!ShouldIgnoreCitadel(pPlot, bIgnoreVisibility)) { for(int iI = 0; iI < NUM_DIRECTION_TYPES; iI++) { pAdjacentPlot = plotDirection(pPlot->getX(), pPlot->getY(), ((DirectionTypes)iI)); if(pAdjacentPlot != NULL) { m_DangerPlots[iPlotLoop].m_pCitadel = pPlot; } } } } } } // testing city danger values CvCity* pLoopCity; int iLoopCity = 0; for(pLoopCity = thisPlayer.firstCity(&iLoopCity); pLoopCity != NULL; pLoopCity = thisPlayer.nextCity(&iLoopCity)) { //adding danger would count each unit multiple times, is biased towards fast units //so we pretend they would all attack the city and tally up the damage //question is, what about our own defensive units in the area. should we count those as well? int iEvalRange = 4; int iThreatValue = 0; for(int iX = -iEvalRange; iX <= iEvalRange; iX++) for(int iY = -iEvalRange; iY <= iEvalRange; iY++) { CvPlot* pEvalPlot = plotXYWithRangeCheck(pLoopCity->getX(), pLoopCity->getY(), iX, iY, iEvalRange); if (pEvalPlot) { const CvUnit* pEnemy = pEvalPlot->getBestDefender(NO_PLAYER, thisPlayer.GetID(), NULL, true); if (pEnemy) { int iAttackerDamage = 0; //to be ignored iThreatValue += TacticalAIHelpers::GetSimulatedDamageFromAttackOnCity(pLoopCity,pEnemy,iAttackerDamage); } } } pLoopCity->SetThreatValue(iThreatValue); } m_bDirty = false; }
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; }