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; }
/// 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); } } } }