// Returns ratio of strengths of stacks times 100 // (so 100 is even ratio, numbers over 100 mean this group is more powerful than the stack on a plot) int CvSelectionGroupAI::AI_compareStacks(const CvPlot* pPlot, bool bPotentialEnemy, bool bCheckCanAttack, bool bCheckCanMove) const { FAssert(pPlot != NULL); int compareRatio; DomainTypes eDomainType = getDomainType(); // if not aircraft, then choose based on the plot, not the head unit (mainly for transport carried units) if (eDomainType != DOMAIN_AIR) { if (pPlot->isWater()) eDomainType = DOMAIN_SEA; else eDomainType = DOMAIN_LAND; } compareRatio = AI_sumStrength(pPlot, eDomainType, bCheckCanAttack, bCheckCanMove); compareRatio *= 100; PlayerTypes eOwner = getOwnerINLINE(); if (eOwner == NO_PLAYER) { eOwner = getHeadOwner(); } FAssert(eOwner != NO_PLAYER); int defenderSum = pPlot->AI_sumStrength(NO_PLAYER, getOwnerINLINE(), eDomainType, true, !bPotentialEnemy, bPotentialEnemy); compareRatio /= std::max(1, defenderSum); return compareRatio; }
// Returns attack odds out of 100 (the higher, the better...) int CvSelectionGroupAI::AI_attackOdds(const CvPlot* pPlot, bool bPotentialEnemy) const { PROFILE_FUNC(); CvUnit* pAttacker; FAssert(getOwnerINLINE() != NO_PLAYER); /************************************************************************************************/ /* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */ /* */ /* Efficiency, Lead From Behind */ /************************************************************************************************/ // From Lead From Behind by UncutDragon // original //if (pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), NULL, !bPotentialEnemy, bPotentialEnemy) == NULL) // modified if (!pPlot->hasDefender(false, NO_PLAYER, getOwnerINLINE(), NULL, !bPotentialEnemy, bPotentialEnemy)) /************************************************************************************************/ /* BETTER_BTS_AI_MOD END */ /************************************************************************************************/ { return 100; } int iOdds = 0; pAttacker = AI_getBestGroupAttacker(pPlot, bPotentialEnemy, iOdds); if (pAttacker == NULL) { return 0; } return iOdds; }
void CvPlotGroup::changeNumBonuses(BonusTypes eBonus, int iChange) { CLLNode<XYCoords>* pPlotNode; CvCity* pCity; int iOldNumBonuses; FAssertMsg(eBonus >= 0, "eBonus is expected to be non-negative (invalid Index)"); FAssertMsg(eBonus < GC.getNumBonusInfos(), "eBonus is expected to be within maximum bounds (invalid Index)"); if (iChange != 0) { iOldNumBonuses = getNumBonuses(eBonus); m_paiNumBonuses[eBonus] = (m_paiNumBonuses[eBonus] + iChange); //FAssertMsg(m_paiNumBonuses[eBonus] >= 0, "m_paiNumBonuses[eBonus] is expected to be non-negative (invalid Index)"); XXX pPlotNode = headPlotsNode(); while (pPlotNode != NULL) { pCity = GC.getMapINLINE().plotSorenINLINE(pPlotNode->m_data.iX, pPlotNode->m_data.iY)->getPlotCity(); if (pCity != NULL) { if (pCity->getOwnerINLINE() == getOwnerINLINE()) { pCity->changeNumBonuses(eBonus, iChange); } } pPlotNode = nextPlotsNode(pPlotNode); } } }
void CvPlotGroup::addPlot(CvPlot* pPlot) { XYCoords xy; xy.iX = pPlot->getX_INLINE(); xy.iY = pPlot->getY_INLINE(); insertAtEndPlots(xy); pPlot->setPlotGroup(getOwnerINLINE(), this); }
// Returns attack odds out of 100 (the higher, the better...) int CvSelectionGroupAI::AI_attackOdds(const CvPlot* pPlot, bool bPotentialEnemy) const { CvUnit* pAttacker; FAssert(getOwnerINLINE() != NO_PLAYER); if (pPlot->getBestDefender(NO_PLAYER, getOwnerINLINE(), NULL, !bPotentialEnemy, bPotentialEnemy) == NULL) { return 100; } int iOdds = 0; pAttacker = AI_getBestGroupAttacker(pPlot, bPotentialEnemy, iOdds); if (pAttacker == NULL) { return 0; } return iOdds; }
CLLNode<XYCoords>* CvPlotGroup::deletePlotsNode(CLLNode<XYCoords>* pNode) { CLLNode<XYCoords>* pPlotNode; pPlotNode = m_plots.deleteNode(pNode); if (getLengthPlots() == 0) { GET_PLAYER(getOwnerINLINE()).deletePlotGroup(getID()); } return pPlotNode; }
// Returns ratio of strengths of stacks times 100 // (so 100 is even ratio, numbers over 100 mean this group is more powerful than the stack on a plot) int CvSelectionGroupAI::AI_compareStacks(const CvPlot* pPlot, bool bCheckCanAttack) const { FAssert(pPlot != NULL); int compareRatio; DomainTypes eDomainType = getDomainType(); // if not aircraft, then choose based on the plot, not the head unit (mainly for transport carried units) if (eDomainType != DOMAIN_AIR) { if (pPlot->isWater()) eDomainType = DOMAIN_SEA; else eDomainType = DOMAIN_LAND; } compareRatio = AI_sumStrength(pPlot, eDomainType, bCheckCanAttack); compareRatio *= 100; PlayerTypes eOwner = getOwnerINLINE(); if (eOwner == NO_PLAYER) { eOwner = getHeadOwner(); } FAssert(eOwner != NO_PLAYER); // K-Mod. Note. This function currently does not support bPotentialEnemy == false. //FAssert(bPotentialEnemy); int defenderSum = pPlot->isVisible(getHeadTeam(), false) ? GET_PLAYER(eOwner).AI_localDefenceStrength(pPlot, NO_TEAM, eDomainType, 0) : GET_TEAM(getHeadTeam()).AI_getStrengthMemory(pPlot); // K-Mod end compareRatio /= std::max(1, defenderSum); // K-Mod. If there are more defenders than we have attacks, but yet the ratio is still greater than 100, // then inflate the ratio futher to account for the fact that we are going to do significantly more damage to them than they to us. // The purpose of this is to give the AI extra encouragement to attack when its units are better than the defender's units. /* if (compareRatio > 100) { FAssert(getHeadUnit() && getNumUnits() > 0); int iDefenders = pPlot->getNumVisibleEnemyDefenders(getHeadUnit()); if (iDefenders > getNumUnits()) { compareRatio += (compareRatio - 100) * (iDefenders - getNumUnits()) / getNumUnits(); } } */ // (currently disabled) // K-Mod end return compareRatio; }
// Returns ratio of strengths of stacks times 100 // (so 100 is even ratio, numbers over 100 mean this group is more powerful than the stack on a plot) int CvSelectionGroupAI::AI_compareStacks(const CvPlot* pPlot, bool bPotentialEnemy, bool bCheckCanAttack, bool bCheckCanMove) const { FAssert(pPlot != NULL); int compareRatio; DomainTypes eDomainType = getDomainType(); // if not aircraft, then choose based on the plot, not the head unit (mainly for transport carried units) if (eDomainType != DOMAIN_AIR) { if (pPlot->isWater()) eDomainType = DOMAIN_SEA; else eDomainType = DOMAIN_LAND; } compareRatio = AI_sumStrength(pPlot, eDomainType, bCheckCanAttack, bCheckCanMove); compareRatio *= 100; PlayerTypes eOwner = getOwnerINLINE(); if (eOwner == NO_PLAYER) { eOwner = getHeadOwner(); } FAssert(eOwner != NO_PLAYER); /************************************************************************************************/ /* UNOFFICIAL_PATCH 03/04/10 jdog5000 */ /* */ /* Bugfix */ /************************************************************************************************/ /* original bts code int defenderSum = pPlot->AI_sumStrength(NO_PLAYER, getOwnerINLINE(), eDomainType, true, !bPotentialEnemy, bPotentialEnemy); */ // Clearly meant to use eOwner here ... int defenderSum = pPlot->AI_sumStrength(NO_PLAYER, eOwner, eDomainType, true, !bPotentialEnemy, bPotentialEnemy); /************************************************************************************************/ /* UNOFFICIAL_PATCH END */ /************************************************************************************************/ compareRatio /= std::max(1, defenderSum); return compareRatio; }
void CvPlotGroup::removePlot(CvPlot* pPlot) { CLLNode<XYCoords>* pPlotNode; pPlotNode = headPlotsNode(); while (pPlotNode != NULL) { if (GC.getMapINLINE().plotSorenINLINE(pPlotNode->m_data.iX, pPlotNode->m_data.iY) == pPlot) { pPlot->setPlotGroup(getOwnerINLINE(), NULL); pPlotNode = deletePlotsNode(pPlotNode); // can delete this PlotGroup... break; } else { pPlotNode = nextPlotsNode(pPlotNode); } } }
PlayerTypes CvPlotGroup::getOwner() const { return getOwnerINLINE(); }
void CvPlotGroup::recalculatePlots() { PROFILE_FUNC(); CLLNode<XYCoords>* pPlotNode; CvPlot* pPlot; CLinkList<XYCoords> oldPlotGroup; XYCoords xy; PlayerTypes eOwner; int iCount; eOwner = getOwnerINLINE(); pPlotNode = headPlotsNode(); if (pPlotNode != NULL) { pPlot = GC.getMapINLINE().plotSorenINLINE(pPlotNode->m_data.iX, pPlotNode->m_data.iY); iCount = 0; gDLL->getFAStarIFace()->SetData(&GC.getPlotGroupFinder(), &iCount); gDLL->getFAStarIFace()->GeneratePath(&GC.getPlotGroupFinder(), pPlot->getX_INLINE(), pPlot->getY_INLINE(), -1, -1, false, eOwner); if (iCount == getLengthPlots()) { return; } } oldPlotGroup.clear(); pPlotNode = headPlotsNode(); while (pPlotNode != NULL) { pPlot = GC.getMapINLINE().plotSorenINLINE(pPlotNode->m_data.iX, pPlotNode->m_data.iY); FAssertMsg(pPlot != NULL, "Plot is not assigned a valid value"); xy.iX = pPlot->getX_INLINE(); xy.iY = pPlot->getY_INLINE(); oldPlotGroup.insertAtEnd(xy); pPlot->setPlotGroup(eOwner, NULL); pPlotNode = deletePlotsNode(pPlotNode); // will delete this PlotGroup... } pPlotNode = oldPlotGroup.head(); while (pPlotNode != NULL) { pPlot = GC.getMapINLINE().plotSorenINLINE(pPlotNode->m_data.iX, pPlotNode->m_data.iY); FAssertMsg(pPlot != NULL, "Plot is not assigned a valid value"); pPlot->updatePlotGroup(eOwner, true); pPlotNode = oldPlotGroup.deleteNode(pPlotNode); } }
// Returns true if the group has become busy... bool CvSelectionGroupAI::AI_update() { CLLNode<IDInfo>* pEntityNode; CvUnit* pLoopUnit; bool bDead; bool bFollow; PROFILE("CvSelectionGroupAI::AI_update"); FAssert(getOwnerINLINE() != NO_PLAYER); if (!AI_isControlled()) { return false; } if (getNumUnits() == 0) { return false; } if (isForceUpdate()) { clearMissionQueue(); // XXX ??? setActivityType(ACTIVITY_AWAKE); setForceUpdate(false); // if we are in the middle of attacking with a stack, cancel it AI_cancelGroupAttack(); } FAssert(!(GET_PLAYER(getOwnerINLINE()).isAutoMoves())); int iTempHack = 0; // XXX bDead = false; bool bFailedAlreadyFighting = false; while ((m_bGroupAttack && !bFailedAlreadyFighting) || readyToMove()) { iTempHack++; if (iTempHack > 100) { FAssert(false); CvUnit* pHeadUnit = getHeadUnit(); if (NULL != pHeadUnit) { if (GC.getLogging()) { TCHAR szOut[1024]; CvWString szTempString; getUnitAIString(szTempString, pHeadUnit->AI_getUnitAIType()); sprintf(szOut, "Unit stuck in loop: %S(%S)[%d, %d] (%S)\n", pHeadUnit->getName().GetCString(), GET_PLAYER(pHeadUnit->getOwnerINLINE()).getName(), pHeadUnit->getX_INLINE(), pHeadUnit->getY_INLINE(), szTempString.GetCString()); gDLL->messageControlLog(szOut); } pHeadUnit->finishMoves(); } break; } // if we want to force the group to attack, force another attack if (m_bGroupAttack) { m_bGroupAttack = false; groupAttack(m_iGroupAttackX, m_iGroupAttackY, MOVE_DIRECT_ATTACK, bFailedAlreadyFighting); } // else pick AI action else { CvUnit* pHeadUnit = getHeadUnit(); if (pHeadUnit == NULL || pHeadUnit->isDelayedDeath()) { break; } resetPath(); if (pHeadUnit->AI_update()) { // AI_update returns true when we should abort the loop and wait until next slice break; } } if (doDelayedDeath()) { bDead = true; break; } // if no longer group attacking, and force separate is true, then bail, decide what to do after group is split up // (UnitAI of head unit may have changed) if (!m_bGroupAttack && AI_isForceSeparate()) { AI_separate(); // pointers could become invalid... return true; } } if (!bDead) { if (!isHuman()) { bFollow = false; // if we not group attacking, then check for follow action if (!m_bGroupAttack) { pEntityNode = headUnitNode(); while ((pEntityNode != NULL) && readyToMove(true)) { pLoopUnit = ::getUnit(pEntityNode->m_data); pEntityNode = nextUnitNode(pEntityNode); if (pLoopUnit->canMove()) { resetPath(); if (pLoopUnit->AI_follow()) { bFollow = true; break; } } } } if (doDelayedDeath()) { bDead = true; } if (!bDead) { if (!bFollow && readyToMove(true)) { pushMission(MISSION_SKIP); } } } } if (bDead) { return true; } return (isBusy() || isCargoBusy()); }
// Returns true if the group has become busy... bool CvSelectionGroupAI::AI_update() { CLLNode<IDInfo>* pEntityNode; CvUnit* pLoopUnit; bool bDead; bool bFollow; PROFILE("CvSelectionGroupAI::AI_update"); FAssert(getOwnerINLINE() != NO_PLAYER); if (!AI_isControlled()) { return false; } if (getNumUnits() == 0) { return false; } // K-Mod / BBAI if (getActivityType() == ACTIVITY_SLEEP && !isHuman() && !getHeadUnit()->isCargo()) { setForceUpdate(true); } // end if (isForceUpdate()) { doForceUpdate(); // K-Mod (based on old code) } //FAssert(!(GET_PLAYER(getOwnerINLINE()).isAutoMoves())); // (no longer true in K-Mod) int iTempHack = 0; // XXX bDead = false; bool bFailedAlreadyFighting = false; //while ((m_bGroupAttack && !bFailedAlreadyFighting) || readyToMove()) while ((AI_isGroupAttack() && !isBusy()) || readyToMove()) // K-Mod { iTempHack++; if (iTempHack > 100) { FAssertMsg(false, "unit stuck in a loop"); CvUnit* pHeadUnit = getHeadUnit(); if (NULL != pHeadUnit) { if (GC.getLogging()) { TCHAR szOut[1024]; CvWString szTempString; getUnitAIString(szTempString, pHeadUnit->AI_getUnitAIType()); sprintf(szOut, "Unit stuck in loop: %S(%S)[%d, %d] (%S)\n", pHeadUnit->getName().GetCString(), GET_PLAYER(pHeadUnit->getOwnerINLINE()).getName(), pHeadUnit->getX_INLINE(), pHeadUnit->getY_INLINE(), szTempString.GetCString()); gDLL->messageControlLog(szOut); } pHeadUnit->finishMoves(); } break; } // if we want to force the group to attack, force another attack if (AI_isGroupAttack()) { AI_cancelGroupAttack(); groupAttack(m_iGroupAttackX, m_iGroupAttackY, MOVE_DIRECT_ATTACK, bFailedAlreadyFighting); } // else pick AI action else { CvUnit* pHeadUnit = getHeadUnit(); //if (pHeadUnit == NULL || pHeadUnit->isDelayedDeath()) if (pHeadUnit == NULL || pHeadUnit->doDelayedDeath()) // K-Mod { break; } //resetPath(); if (pHeadUnit->AI_update()) { // AI_update returns true when we should abort the loop and wait until next slice FAssert(!pHeadUnit->isDelayedDeath()); break; } } if (doDelayedDeath()) { bDead = true; break; } // if no longer group attacking, and force separate is true, then bail, decide what to do after group is split up // (UnitAI of head unit may have changed) if (!AI_isGroupAttack() && AI_isForceSeparate()) { AI_separate(); // pointers could become invalid... //return true; return false; // K-Mod } } if (!bDead) { if (!isHuman()) { bFollow = false; // if we not group attacking, then check for follow action if (!AI_isGroupAttack()) { pEntityNode = headUnitNode(); // K-Mod note: I've rearranged a few things below, and added 'bFirst'. bool bFirst = true; while ((pEntityNode != NULL) && readyToMove(true)) { pLoopUnit = ::getUnit(pEntityNode->m_data); pEntityNode = nextUnitNode(pEntityNode); if (bFirst) resetPath(); if (pLoopUnit->canMove()) { if (pLoopUnit->AI_follow(bFirst)) { bFollow = true; bFirst = true; // let the next unit start fresh. } else bFirst = false; } } // K-Mod end } if (doDelayedDeath()) { bDead = true; } if (!bDead) { if (!bFollow && readyToMove(true)) { pushMission(MISSION_SKIP); } } /************************************************************************************************/ /* BETTER_BTS_AI_MOD 04/28/10 jdog5000 */ /* */ /* Unit AI */ /************************************************************************************************/ // AI should never put units to sleep, how does this ever happen? //FAssert( getHeadUnit()->isCargo() || getActivityType() != ACTIVITY_SLEEP ); /************************************************************************************************/ /* BETTER_BTS_AI_MOD END */ /************************************************************************************************/ } } if (bDead) { //return true; return false; // K-Mod } return (isBusy() || isCargoBusy()); }
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; }