/// Recalculate when each unit will arrive on target void CvArmyAI::UpdateCheckpointTurns() { for(unsigned int iI = 0; iI < m_FormationEntries.size(); iI++) { // No reestimate for units being built if(m_FormationEntries[iI].GetUnitID() != ARMY_NO_UNIT) { CvUnit* pUnit = GET_PLAYER(m_eOwner).getUnit(m_FormationEntries[iI].GetUnitID()); CvPlot* pMusterPlot = GC.getMap().plot(GetX(), GetY()); if(pUnit && pMusterPlot) { int iTurnsToReachCheckpoint = pUnit->TurnsToReachTarget(pMusterPlot, false, true, true); if(iTurnsToReachCheckpoint < MAX_INT) SetEstimatedTurn(iI, iTurnsToReachCheckpoint); else SetEstimatedTurn(iI, ARMYSLOT_UNKNOWN_TURN_AT_CHECKPOINT); } } } }
/// Recalculate when each unit will arrive at the current army position, whatever that is void CvArmyAI::UpdateCheckpointTurns() { for(unsigned int iI = 0; iI < m_FormationEntries.size(); iI++) { // No reestimate for units being built if(m_FormationEntries[iI].GetUnitID() > ARMYSLOT_NO_UNIT) { CvUnit* pUnit = GET_PLAYER(m_eOwner).getUnit(m_FormationEntries[iI].GetUnitID()); CvPlot* pCurrentPlot = GC.getMap().plot(GetX(), GetY()); if(pUnit && pCurrentPlot) { //be lenient here, for some ops the muster point may be far away and intermittendly occupied by foreign units ... int iFlags = CvUnit::MOVEFLAG_APPROXIMATE_TARGET | CvUnit::MOVEFLAG_IGNORE_STACKING | CvUnit::MOVEFLAG_IGNORE_ZOC; int iTurnsToReachCheckpoint = pUnit->TurnsToReachTarget(pCurrentPlot, iFlags, GC.getAI_OPERATIONAL_MAX_RECRUIT_TURNS_ENEMY_TERRITORY()); if(iTurnsToReachCheckpoint < MAX_INT) SetEstimatedTurn(iI, iTurnsToReachCheckpoint); else SetEstimatedTurn(iI, ARMYSLOT_UNKNOWN_TURN_AT_CHECKPOINT); } } } }
/// Add a unit to our army (and we know which slot) void CvArmyAI::AddUnit(int iUnitID, int iSlotNum) { CvPlayer& thisPlayer = GET_PLAYER(m_eOwner); CvUnit* pThisUnit = thisPlayer.getUnit(iUnitID); if (!pThisUnit) return; // remove this unit from an army if it is already in one thisPlayer.removeFromArmy(pThisUnit->getArmyID(), GetID()); m_FormationEntries[iSlotNum] = CvArmyFormationSlot(); //reset m_FormationEntries[iSlotNum].SetUnitID(iUnitID); pThisUnit->setArmyID(GetID()); #if defined(MOD_BALANCE_CORE) //just for avoiding confusion pThisUnit->setTacticalMove(NO_TACTICAL_MOVE); #endif // Finally, compute when we think this unit will arrive at the next checkpoint CvPlot* pMusterPlot = GC.getMap().plot(GetX(), GetY()); if(pMusterPlot) { int iFlags = CvUnit::MOVEFLAG_APPROX_TARGET_RING2 | CvUnit::MOVEFLAG_IGNORE_STACKING | CvUnit::MOVEFLAG_IGNORE_ZOC; int iTurnsToReachCheckpoint = pThisUnit->TurnsToReachTarget(pMusterPlot, iFlags, GC.getAI_OPERATIONAL_MAX_RECRUIT_TURNS_ENEMY_TERRITORY()); if(iTurnsToReachCheckpoint < MAX_INT) { m_FormationEntries[iSlotNum].SetTurnsToCheckpoint(iTurnsToReachCheckpoint); } if (GC.getLogging() && GC.getAILogging()) { CvAIOperation* pOperation = GET_PLAYER(GetOwner()).getAIOperation(GetOperationID()); CvString strMsg; strMsg.Format("Added %s %d to slot %d in army %d. ETA at (%d:%d) is %d ", pThisUnit->getName().c_str(), m_FormationEntries[iSlotNum].GetUnitID(), iSlotNum, GetID(), pMusterPlot->getX(), pMusterPlot->getY(), iTurnsToReachCheckpoint); pOperation->LogOperationSpecialMessage(strMsg); } } }
/// 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); } } } }