CvUnit* CvSelectionGroupAI::AI_getBestGroupAttacker(const CvPlot* pPlot, bool bPotentialEnemy, int& iUnitOdds, bool bForce, bool bNoBlitz) const { CLLNode<IDInfo>* pUnitNode; CvUnit* pLoopUnit; CvUnit* pBestUnit; int iValue; int iBestValue; int iOdds; int iBestOdds; iBestValue = 0; iBestOdds = 0; pBestUnit = NULL; pUnitNode = headUnitNode(); bool bIsHuman = (pUnitNode != NULL) ? GET_PLAYER(::getUnit(pUnitNode->m_data)->getOwnerINLINE()).isHuman() : true; while (pUnitNode != NULL) { pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = nextUnitNode(pUnitNode); if (!pLoopUnit->isDead()) { bool bCanAttack = false; bCanAttack = pLoopUnit->canAttack(); if (bCanAttack && bNoBlitz && pLoopUnit->isBlitz() && pLoopUnit->isMadeAttack()) { bCanAttack = false; } if (bCanAttack) { if (bForce || pLoopUnit->canMove()) { if (bForce || pLoopUnit->canMoveInto(pPlot, /*bAttack*/ true, /*bDeclareWar*/ bPotentialEnemy)) { iOdds = pLoopUnit->AI_attackOdds(pPlot, bPotentialEnemy); iValue = iOdds; FAssertMsg(iValue > 0, "iValue is expected to be greater than 0"); // if non-human, prefer the last unit that has the best value (so as to avoid splitting the group) if (iValue > iBestValue || (!bIsHuman && iValue > 0 && iValue == iBestValue)) { iBestValue = iValue; iBestOdds = iOdds; pBestUnit = pLoopUnit; } } } } } } iUnitOdds = iBestOdds; return pBestUnit; }
int CvSelectionGroupAI::AI_sumStrength(const CvPlot* pAttackedPlot, DomainTypes eDomainType, bool bCheckCanAttack, bool bCheckCanMove) const { CLLNode<IDInfo>* pUnitNode; CvUnit* pLoopUnit; int strSum = 0; pUnitNode = headUnitNode(); while (pUnitNode != NULL) { pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = nextUnitNode(pUnitNode); if (!pLoopUnit->isDead()) { bool bCanAttack = false; if (pLoopUnit->getDomainType() == DOMAIN_AIR) bCanAttack = pLoopUnit->canAirAttack(); else bCanAttack = pLoopUnit->canAttack(); if (!bCheckCanAttack || bCanAttack) { if (!bCheckCanMove || pLoopUnit->canMove()) if (!bCheckCanMove || pAttackedPlot == NULL || pLoopUnit->canMoveInto(pAttackedPlot, /*bAttack*/ true, /*bDeclareWar*/ true)) if (eDomainType == NO_DOMAIN || pLoopUnit->getDomainType() == eDomainType) strSum += pLoopUnit->currEffectiveStr(pAttackedPlot, pLoopUnit); } } } return strSum; }
CvUnit* CvSelectionGroupAI::AI_getBestGroupSacrifice(const CvPlot* pPlot, bool bPotentialEnemy, bool bForce, bool bNoBlitz) const { int iBestValue = 0; CvUnit* pBestUnit = NULL; CLLNode<IDInfo>* pUnitNode = headUnitNode(); while (pUnitNode != NULL) { CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = nextUnitNode(pUnitNode); if (!pLoopUnit->isDead()) { bool bCanAttack = false; if (pLoopUnit->getDomainType() == DOMAIN_AIR) { bCanAttack = pLoopUnit->canAirAttack(); } else { bCanAttack = pLoopUnit->canAttack(); if (bCanAttack && bNoBlitz && pLoopUnit->isBlitz() && pLoopUnit->isMadeAttack()) { bCanAttack = false; } } if (bCanAttack) { if (bForce || pLoopUnit->canMove()) { if (bForce || pLoopUnit->canMoveInto(pPlot, true)) { int iValue = pLoopUnit->AI_sacrificeValue(pPlot); FAssertMsg(iValue > 0, "iValue is expected to be greater than 0"); // we want to pick the last unit of highest value, so pick the last unit with a good value if (iValue >= iBestValue) { iBestValue = iValue; pBestUnit = pLoopUnit; } } } } } } return pBestUnit; }
CvUnit* CvSelectionGroupAI::AI_getBestGroupAttacker(const CvPlot* pPlot, bool bPotentialEnemy, int& iUnitOdds, bool bForce, bool bNoBlitz) const { CLLNode<IDInfo>* pUnitNode; CvUnit* pLoopUnit; CvUnit* pBestUnit; int iPossibleTargets; int iValue; int iBestValue; int iOdds; int iBestOdds; iBestValue = 0; iBestOdds = 0; pBestUnit = NULL; pUnitNode = headUnitNode(); bool bIsHuman = (pUnitNode != NULL) ? GET_PLAYER(::getUnit(pUnitNode->m_data)->getOwnerINLINE()).isHuman() : true; while (pUnitNode != NULL) { pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = nextUnitNode(pUnitNode); if (!pLoopUnit->isDead()) { bool bCanAttack = false; if (pLoopUnit->getDomainType() == DOMAIN_AIR) { bCanAttack = pLoopUnit->canAirAttack(); } else { bCanAttack = pLoopUnit->canAttack(); if (bCanAttack && bNoBlitz && pLoopUnit->isBlitz() && pLoopUnit->isMadeAttack()) { bCanAttack = false; } } if (bCanAttack) { if (bForce || pLoopUnit->canMove()) { if (bForce || pLoopUnit->canMoveInto(pPlot, /*bAttack*/ true, /*bDeclareWar*/ bPotentialEnemy)) { iOdds = pLoopUnit->AI_attackOdds(pPlot, bPotentialEnemy); iValue = iOdds; FAssertMsg(iValue > 0, "iValue is expected to be greater than 0"); if (pLoopUnit->collateralDamage() > 0) { iPossibleTargets = std::min((pPlot->getNumVisibleEnemyDefenders(pLoopUnit) - 1), pLoopUnit->collateralDamageMaxUnits()); if (iPossibleTargets > 0) { iValue *= (100 + ((pLoopUnit->collateralDamage() * iPossibleTargets) / 5)); iValue /= 100; } } // if non-human, prefer the last unit that has the best value (so as to avoid splitting the group) if (iValue > iBestValue || (!bIsHuman && iValue > 0 && iValue == iBestValue)) { iBestValue = iValue; iBestOdds = iOdds; pBestUnit = pLoopUnit; } } } } } } iUnitOdds = iBestOdds; return pBestUnit; }
// 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()); }
// Get the maximum damage unit could receive at this plot in the next turn (update this with CvUnitCombat changes!) int CvDangerPlotContents::GetDanger(const CvUnit* pUnit, AirActionType iAirAction) { if (!m_pPlot || !pUnit) return 0; // Air units only take damage from interceptions if (pUnit->getDomainType() == DOMAIN_AIR) return GetAirUnitDamage(pUnit, iAirAction); //simple caching for speedup SUnitStats unitStats(pUnit); if (unitStats==m_lastUnit) return m_lastResult; //otherwise calculate from scratch int iPlotDamage = 0; CvCity* pFriendlyCity = NULL; if ( m_pPlot->isFriendlyCity(*pUnit,true) ) pFriendlyCity = m_pPlot->getPlotCity(); // Civilians can be captured - unless they would need to be embarked on this plot if (!pUnit->IsCombatUnit() && pUnit->isNativeDomain(m_pPlot)) { // If plot contains an enemy unit, mark it as max danger if (m_pPlot->getBestDefender(NO_PLAYER, pUnit->getOwner(), NULL, true)) { return MAX_INT; } for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pAttacker = GET_PLAYER(it->first).getUnit(it->second); if ( pAttacker && !pAttacker->isDelayedDeath() && !pAttacker->IsDead()) { // If in a city and the city can be captured, we are in highest danger if (pFriendlyCity) { if (GetDanger(pFriendlyCity) + pFriendlyCity->getDamage() > pFriendlyCity->GetMaxHitPoints()) { return MAX_INT; } } // Look for a possible plot defender else { IDInfo* pUnitNode = m_pPlot->headUnitNode(); CvUnit* pBestDefender = NULL; while (pUnitNode != NULL) { pBestDefender = ::getUnit(*pUnitNode); pUnitNode = m_pPlot->nextUnitNode(pUnitNode); if (pBestDefender && pBestDefender->getOwner() == pUnit->getOwner()) { //fix endless recursion with stacked embarked civilians: defender must also be able to attack if (pBestDefender->IsCanDefend() && pBestDefender->IsCanAttack()) { if (pBestDefender != pUnit) { if (pBestDefender->isWaiting() || !(pBestDefender->canMove())) { break; } } } } pBestDefender = NULL; } // If there is a defender and it might be killed, high danger if (pBestDefender && (pBestDefender->isWaiting() || !pBestDefender->canMove())) { if (GetDanger(pBestDefender) > pBestDefender->GetCurrHitPoints()) { return INT_MAX; } } else if (pBestDefender==NULL) { //Civilian could be captured on this tile return MAX_INT; } } } } // Damage from features (citadel) iPlotDamage += GetDamageFromFeatures(pUnit->getOwner()); iPlotDamage += m_bFlatPlotDamage ? m_pPlot->getTurnDamage(pUnit->ignoreTerrainDamage(), pUnit->ignoreFeatureDamage(), pUnit->extraTerrainDamage(), pUnit->extraFeatureDamage()) : 0; // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (!pCity || pCity->getTeam() == pUnit->getTeam()) continue; iPlotDamage += pCity->rangeCombatDamage(pUnit, NULL, false, m_pPlot); } //update cache m_lastUnit = unitStats; m_lastResult = iPlotDamage; return iPlotDamage; } // Capturing a city with a garrisoned unit destroys the garrisoned unit if (pFriendlyCity) { int iCityDanger = GetDanger(pFriendlyCity, (pUnit->getDomainType() == DOMAIN_LAND ? pUnit : NULL)); if (iCityDanger + pFriendlyCity->getDamage() < pFriendlyCity->GetMaxHitPoints()) { if (pUnit->CanGarrison()) { // Reconstruct the amount of damage the garrison would absorb for the city int iUnitShare = (iCityDanger*2*pUnit->GetMaxHitPoints()) / pFriendlyCity->GetMaxHitPoints(); // Damage from features return iUnitShare + GetDamageFromFeatures(pUnit->getOwner()); } else return 0; } else { return MAX_INT; } } CvPlot* pAttackerPlot = NULL; CvUnit* pInterceptor = NULL; // Damage from units // EXTREMELY IMPORTANT THAT NO RNG IS USED FOR PREDICTION! // Otherwise a tooltip or similar can change the game state for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pAttacker = GET_PLAYER(it->first).getUnit(it->second); if (!pAttacker || pAttacker->isDelayedDeath() || pAttacker->IsDead()) continue; pAttackerPlot = NULL; if (pAttacker->plot() != m_pPlot) { if (pAttacker->IsCanAttackRanged()) { if (pAttacker->getDomainType() == DOMAIN_AIR) { pInterceptor = pAttacker->GetBestInterceptor(*m_pPlot, pUnit); int iInterceptDamage = 0; if (pInterceptor) { // Always assume interception is successful iInterceptDamage = pInterceptor->GetInterceptionDamage(pUnit, false); } iPlotDamage += pAttacker->GetAirCombatDamage(pUnit, NULL, false, iInterceptDamage, m_pPlot); } else { iPlotDamage += pAttacker->GetRangeCombatDamage(pUnit, NULL, false, 0, m_pPlot); } } else { if (plotDistance(m_iX, m_iY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pAttacker->plot(); } iPlotDamage += pAttacker->getCombatDamage( pAttacker->GetMaxAttackStrength(pAttackerPlot, m_pPlot, pUnit), pUnit->GetMaxDefenseStrength(m_pPlot, pAttacker), pAttacker->getDamage(), false, false, false); if (pAttacker->isRangedSupportFire()) { iPlotDamage += pAttacker->GetRangeCombatDamage(pUnit, NULL, false, 0, m_pPlot, pAttackerPlot); } } } } // Damage from cities for (DangerCityVector::iterator it = m_apCities.begin(); it < m_apCities.end(); ++it) { CvCity* pCity = GET_PLAYER(it->first).getCity(it->second); if (!pCity || pCity->getTeam() == pUnit->getTeam()) continue; iPlotDamage += pCity->rangeCombatDamage(pUnit, NULL, false, m_pPlot); } // Damage from surrounding features (citadel) and the plot itself iPlotDamage += GetDamageFromFeatures(pUnit->getOwner()); iPlotDamage += m_bFlatPlotDamage ? m_pPlot->getTurnDamage(pUnit->ignoreTerrainDamage(), pUnit->ignoreFeatureDamage(), pUnit->extraTerrainDamage(), pUnit->extraFeatureDamage()) : 0; //update cache m_lastUnit = unitStats; m_lastResult = iPlotDamage; //done return iPlotDamage; }
// K-Mod. I've removed bCheckMove, and changed bCheckCanAttack to include checks for moves, and for hasAlreadyAttacked / blitz int CvSelectionGroupAI::AI_sumStrength(const CvPlot* pAttackedPlot, DomainTypes eDomainType, bool bCheckCanAttack) const { CLLNode<IDInfo>* pUnitNode; CvUnit* pLoopUnit; int strSum = 0; bool bDefenders = pAttackedPlot ? pAttackedPlot->isVisibleEnemyUnit(getHeadOwner()) : false; // K-Mod bool bCountCollateral = pAttackedPlot && pAttackedPlot != plot(); // K-Mod pUnitNode = headUnitNode(); int iBaseCollateral = bCountCollateral ? iBaseCollateral = estimateCollateralWeight(pAttackedPlot, pAttackedPlot->getTeam() == getTeam() ? NO_TEAM : pAttackedPlot->getTeam()) : 0; while (pUnitNode != NULL) { pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = nextUnitNode(pUnitNode); if (!pLoopUnit->isDead()) { // K-Mod. (original checks deleted.) if (bCheckCanAttack) { if (pLoopUnit->getDomainType() == DOMAIN_AIR) { if (!pLoopUnit->canAirAttack() || !pLoopUnit->canMove() || (pAttackedPlot && bDefenders && !pLoopUnit->canMoveInto(pAttackedPlot, true, true))) continue; // can't attack. } else { if (!pLoopUnit->canAttack() || !pLoopUnit->canMove() || (pAttackedPlot && bDefenders && !pLoopUnit->canMoveInto(pAttackedPlot, true, true)) || (!pLoopUnit->isBlitz() && pLoopUnit->isMadeAttack())) continue; // can't attack. } } // K-Mod end if (eDomainType == NO_DOMAIN || pLoopUnit->getDomainType() == eDomainType) { strSum += pLoopUnit->currEffectiveStr(pAttackedPlot, pLoopUnit); // K-Mod estimate the attack power of collateral units. (cf with calculation in AI_localAttackStrength) if (bCountCollateral && pLoopUnit->collateralDamage() > 0) { int iPossibleTargets = pLoopUnit->collateralDamageMaxUnits(); // If !bCheckCanAttack, then lets not assume pAttackPlot won't get more units on it. if (bCheckCanAttack && pAttackedPlot->isVisible(getTeam(), false)) iPossibleTargets = std::min(iPossibleTargets, pAttackedPlot->getNumVisibleEnemyDefenders(pLoopUnit) - 1); if (iPossibleTargets > 0) { // collateral damage is not trivial to calculate. This estimate is pretty rough. // (Note: collateralDamage() and iBaseCollateral both include factors of 100.) strSum += pLoopUnit->baseCombatStr() * iBaseCollateral * pLoopUnit->collateralDamage() * iPossibleTargets / 10000; } } // K-Mod end } } } return strSum; }
CvUnit* CvSelectionGroupAI::AI_getBestGroupAttacker(const CvPlot* pPlot, bool bPotentialEnemy, int& iUnitOdds, bool bForce, bool bNoBlitz) const { PROFILE_FUNC(); int iBestValue = 0; int iBestOdds = 0; CvUnit* pBestUnit = NULL; CLLNode<IDInfo>* pUnitNode = headUnitNode(); bool bIsHuman = (pUnitNode != NULL) ? GET_PLAYER(::getUnit(pUnitNode->m_data)->getOwnerINLINE()).isHuman() : true; while (pUnitNode != NULL) { CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = nextUnitNode(pUnitNode); if (!pLoopUnit->isDead()) { bool bCanAttack = false; if (pLoopUnit->getDomainType() == DOMAIN_AIR) { bCanAttack = pLoopUnit->canAirAttack(); } else { bCanAttack = pLoopUnit->canAttack(); if (bCanAttack && bNoBlitz && pLoopUnit->isBlitz() && pLoopUnit->isMadeAttack()) { bCanAttack = false; } } if (bCanAttack) { if (bForce || pLoopUnit->canMove()) { if (bForce || pLoopUnit->canMoveInto(pPlot, /*bAttack*/ true, /*bDeclareWar*/ bPotentialEnemy)) { /************************************************************************************************/ /* BETTER_BTS_AI_MOD 02/21/10 jdog5000 */ /* */ /* Lead From Behind */ /************************************************************************************************/ // From Lead From Behind by UncutDragon if (GC.getLFBEnable() && GC.getLFBUseCombatOdds()) { //pLoopUnit->LFBgetBetterAttacker(&pBestUnit, pPlot, bPotentialEnemy, iBestOdds, iValue); pLoopUnit->LFBgetBetterAttacker(&pBestUnit, pPlot, bPotentialEnemy, iBestOdds, iBestValue); // K-Mod. } else { int iOdds = pLoopUnit->AI_attackOdds(pPlot, bPotentialEnemy); int iValue = iOdds; FAssertMsg(iValue > 0, "iValue is expected to be greater than 0"); if (pLoopUnit->collateralDamage() > 0) { int iPossibleTargets = std::min((pPlot->getNumVisibleEnemyDefenders(pLoopUnit) - 1), pLoopUnit->collateralDamageMaxUnits()); if (iPossibleTargets > 0) { iValue *= (100 + ((pLoopUnit->collateralDamage() * iPossibleTargets) / 5)); iValue /= 100; } } // if non-human, prefer the last unit that has the best value (so as to avoid splitting the group) if (iValue > iBestValue || (!bIsHuman && iValue > 0 && iValue == iBestValue)) { iBestValue = iValue; iBestOdds = iOdds; pBestUnit = pLoopUnit; } } /************************************************************************************************/ /* BETTER_BTS_AI_MOD END */ /************************************************************************************************/ } } } } } iUnitOdds = iBestOdds; return pBestUnit; }
// 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_launchAssault(CvPlot* pTargetCityPlot) { std::multimap<int, CvUnit*, std::greater<int> > units; std::multimap<int, CvUnit*, std::greater<int> >::iterator units_it; CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode(); CvUnit* pLoopUnit; while (pUnitNode != NULL) { pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = plot()->nextUnitNode(pUnitNode); if (pLoopUnit->isCargo()) { if (pLoopUnit->getTransportUnit()->getGroup() == this) { int iValue = pLoopUnit->baseCombatStr(); if (pLoopUnit->canAttack()) { iValue *= 10; } iValue *= 100; units.insert(std::make_pair(iValue, pLoopUnit)); } } } if (units.empty()) { return false; } bool bAction = false; for (units_it = units.begin(); units_it != units.end(); ++units_it) { pLoopUnit = units_it->second; if (pLoopUnit->canMove() && pLoopUnit->canAttack()) { // if (pLoopUnit->AI_attackFromTransport(NULL, 40, 80)) // { // bAction = true; // } int iPriority = 41; pLoopUnit->AI_setMovePriority(iPriority); } } // for (units_it = units.begin(); units_it != units.end(); ++units_it) // { // pLoopUnit = units_it->second; // if (pLoopUnit->canMove()) // { // if (pLoopUnit->AI_moveFromTransport(NULL)) // { // bAction = true; // } // } // } // for (units_it = units.begin(); units_it != units.end(); ++units_it) // { // pLoopUnit = units_it->second; // if (pLoopUnit->canMove() && pLoopUnit->canAttack()) // { // if (pLoopUnit->AI_attackFromTransport(NULL, 0, 100)) // { // bAction = true; // } // } // } if (bAction) { //pushMission(MISSION_SKIP); return true; } return false; }
CvUnit* CvSelectionGroupAI::AI_getBestGroupSacrifice(const CvPlot* pPlot, bool bPotentialEnemy, bool bForce, bool bNoBlitz) const { int iBestValue = 0; CvUnit* pBestUnit = NULL; CLLNode<IDInfo>* pUnitNode = headUnitNode(); while (pUnitNode != NULL) { CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data); pUnitNode = nextUnitNode(pUnitNode); if (!pLoopUnit->isDead()) { bool bCanAttack = false; if (pLoopUnit->getDomainType() == DOMAIN_AIR) { bCanAttack = pLoopUnit->canAirAttack(); } else { bCanAttack = pLoopUnit->canAttack(); if (bCanAttack && bNoBlitz && pLoopUnit->isBlitz() && pLoopUnit->isMadeAttack()) { bCanAttack = false; } } if (bCanAttack) { if (bForce || pLoopUnit->canMove()) { if (bForce || pLoopUnit->canMoveInto(pPlot, true)) { int iValue = pLoopUnit->AI_sacrificeValue(pPlot); FAssertMsg(iValue > 0, "iValue is expected to be greater than 0"); /*************************************************************************************************/ /** BETTER AI (Summons make good groupattack sacrifice units) Sephi **/ /** **/ /** **/ /*************************************************************************************************/ if (pLoopUnit->getDuration()>0) { iValue+=10000; } /*************************************************************************************************/ /** END **/ /*************************************************************************************************/ /*************************************************************************************************/ /** BETTER AI (Block some Units from attacking at low odds) Sephi **/ /** **/ /** **/ /*************************************************************************************************/ if (!GET_PLAYER(pLoopUnit->getOwnerINLINE()).isHuman()) { if (pLoopUnit->AI_getUnitAIType()==UNITAI_WARWIZARD) { iValue=1; } if (pLoopUnit->AI_getUnitAIType()==UNITAI_HERO) { iValue=1; } if (pLoopUnit->getLevel()>4) { iValue=1; } } /*************************************************************************************************/ /** END **/ /*************************************************************************************************/ // we want to pick the last unit of highest value, so pick the last unit with a good value if (iValue >= iBestValue) { iBestValue = iValue; pBestUnit = pLoopUnit; } } } } } } return pBestUnit; }