/// Recalculate when each unit will arrive at the current army position, whatever that is void CvArmyAI::UpdateCheckpointTurnsAndRemoveBadUnits() { CvAIOperation* pOperation = GET_PLAYER(GetOwner()).getAIOperation(GetOperationID()); //should be updated before calling this ... CvPlot* pCurrentArmyPlot = GC.getMap().plot(GetX(), GetY()); //kick out damaged units, but if we're defending or escorting, no point in running away if (pOperation->IsOffensive()) { for (unsigned int iI = 0; iI < m_FormationEntries.size(); iI++) { if (!m_FormationEntries[iI].IsUsed()) continue; CvUnit* pUnit = GET_PLAYER(m_eOwner).getUnit(m_FormationEntries[iI].m_iUnitID); if (pUnit == NULL) continue; //failsafe - make sure the army ID is set if (pUnit->getArmyID() != GetID()) pUnit->setArmyID(GetID()); //let tactical AI handle those if (pUnit->GetCurrHitPoints() < pUnit->GetMaxHitPoints() / 3) RemoveUnit(m_FormationEntries[iI].GetUnitID()); } } //update for all units in this army. ignore the ones still being built int iGatherTolerance = pOperation->GetGatherTolerance(this, pCurrentArmyPlot); for(unsigned int iI = 0; iI < m_FormationEntries.size(); iI++) { if (!m_FormationEntries[iI].IsUsed()) continue; CvUnit* pUnit = GET_PLAYER(m_eOwner).getUnit(m_FormationEntries[iI].GetUnitID()); if(pUnit && pCurrentArmyPlot) { if(plotDistance(*pUnit->plot(),*pCurrentArmyPlot)<iGatherTolerance) { m_FormationEntries[iI].SetTurnsToCheckpoint(0); } else { //be generous with the flags here, for some ops the muster point may be far away and intermittendly occupied by foreign units ... int iFlags = CvUnit::MOVEFLAG_APPROX_TARGET_RING2 | CvUnit::MOVEFLAG_IGNORE_STACKING | CvUnit::MOVEFLAG_IGNORE_ZOC; int iTurnsToReachCheckpoint = pUnit->TurnsToReachTarget(pCurrentArmyPlot, iFlags, pOperation->GetMaximumRecruitTurns()); //if we're already moving to target, the current army plot is moving, so we cannot check progress against ... if ( iTurnsToReachCheckpoint==INT_MAX || (GetArmyAIState()==ARMYAISTATE_WAITING_FOR_UNITS_TO_CATCH_UP && !m_FormationEntries[iI].IsMakingProgressTowardsCheckpoint(iTurnsToReachCheckpoint)) ) { CvString strMsg; strMsg.Format("Removing %s %d from army %d because no progress to checkpoint (%d:%d). ETA %d, previously %d.", pUnit->getName().c_str(), m_FormationEntries[iI].GetUnitID(), GetID(), pCurrentArmyPlot->getX(), pCurrentArmyPlot->getY(), iTurnsToReachCheckpoint, m_FormationEntries[iI].m_iPrevEstimatedTurnsToCheckpoint); pOperation->LogOperationSpecialMessage(strMsg); RemoveUnit(m_FormationEntries[iI].GetUnitID()); } else m_FormationEntries[iI].SetTurnsToCheckpoint(iTurnsToReachCheckpoint); } } } }
// Get the maximum damage city could receive this turn if it were in this plot int CvDangerPlotContents::GetDanger(CvCity* pCity, const CvUnit* pPretendGarrison) { if (!m_pPlot || !pCity) return 0; int iPlotDamage = 0; CvPlot* pCityPlot = pCity->plot(); const int iCityX = pCityPlot->getX(); const int iCityY = pCityPlot->getY(); CvCityGarrisonOverride guard(pCity,pPretendGarrison); CvPlot* pAttackerPlot = NULL; CvUnit* pInterceptor = NULL; // Damage from ranged units and melees that cannot capture for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if (!pUnit || pUnit->isDelayedDeath() || pUnit->IsDead()) { continue; } pAttackerPlot = NULL; if (pUnit->IsCanAttackRanged()) { if (pUnit->getDomainType() == DOMAIN_AIR) { pInterceptor = pUnit->GetBestInterceptor(*m_pPlot); int iInterceptDamage = 0; if (pInterceptor) { // Always assume interception is successful iInterceptDamage = pInterceptor->GetInterceptionDamage(pUnit, false); } iPlotDamage += pUnit->GetAirCombatDamage(NULL, pCity, false, iInterceptDamage, m_pPlot); } else { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, m_pPlot); } } else if (pUnit->isNoCapture()) { if (plotDistance(iCityX, iCityY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pUnit->plot(); } iPlotDamage += pUnit->getCombatDamage(pUnit->GetMaxAttackStrength(pAttackerPlot, pCityPlot, NULL), pCity->getStrengthValue(), pUnit->getDamage(), false, false, true); if (pUnit->isRangedSupportFire()) { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, pCityPlot); } } } // 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() == pCity->getTeam()) { continue; } iPlotDamage += pCity->rangeCombatDamage(NULL, pCity, false, pCityPlot); } // Damage from melee units for (DangerUnitVector::iterator it = m_apUnits.begin(); it < m_apUnits.end(); ++it) { CvUnit* pUnit = GET_PLAYER(it->first).getUnit(it->second); if (!pUnit || pUnit->isDelayedDeath() || pUnit->IsDead()) { continue; } pAttackerPlot = NULL; if (!pUnit->IsCanAttackRanged() && !pUnit->isNoCapture()) { if (plotDistance(iCityX, iCityY, pUnit->getX(), pUnit->getY()) == 1) { pAttackerPlot = pUnit->plot(); } iPlotDamage += pUnit->getCombatDamage(pUnit->GetMaxAttackStrength(pAttackerPlot, pCityPlot, NULL), pCity->getStrengthValue(), pUnit->getDamage(), false, false, true); if (pUnit->isRangedSupportFire()) { iPlotDamage += pUnit->GetRangeCombatDamage(NULL, pCity, false, 0, pCityPlot); } } } //if we have a garrison, split the damage CvUnit* pGarrison = pCity->GetGarrisonedUnit(); if (pGarrison) { int iUnitShare = (iPlotDamage*2*pGarrison->GetMaxHitPoints())/(pCity->GetMaxHitPoints()+2*pGarrison->GetMaxHitPoints()); iPlotDamage -= iUnitShare; } return iPlotDamage; }