Hooks
static bool BeforeCvTacticalAI_ExecuteFlankAttack(CvTacticalAI* self, CvTacticalTarget& kTarget, bool& result);static void AfterCvTacticalAI_ExecuteFlankAttack(CvTacticalAI* self, CvTacticalTarget& kTarget, bool& result);static bool ReplaceCvTacticalAI_ExecuteFlankAttack(bool (*original)(CvTacticalAI*, CvTacticalTarget&), CvTacticalAI* self, CvTacticalTarget& kTarget);CivRegisterBefore<CivHooks::CvTacticalAI_ExecuteFlankAttack>(host, &BeforeCvTacticalAI_ExecuteFlankAttack);CivRegisterAfter<CivHooks::CvTacticalAI_ExecuteFlankAttack>(host, &AfterCvTacticalAI_ExecuteFlankAttack);CivRegisterReplace<CivHooks::CvTacticalAI_ExecuteFlankAttack>(host, &ReplaceCvTacticalAI_ExecuteFlankAttack);
Code
8153/// Take a multi-hex attack on an enemy unit this turn8154bool CvTacticalAI::ExecuteFlankAttack(CvTacticalTarget& kTarget)8155{8156#if defined(MOD_AI_SMART_V3)8157 if (MOD_AI_SMART_V3)8158 {8159 CvPlot* pLoopPlot;8160 int iPlotIndex;8161 vector<int> movedUnits;8162 CvTacticalAnalysisCell* pCell;8163 vector<CvTacticalTarget*> flankPlots;8164 m_TempTargets.clear();81658166 //Check all target plots.8167 for(int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)8168 {8169 pLoopPlot = plotDirection(kTarget.GetTargetX(), kTarget.GetTargetY(), ((DirectionTypes)iI));8170 if (pLoopPlot)8171 {8172 iPlotIndex = GC.getMap().plotNum(pLoopPlot->getX(), pLoopPlot->getY());8173 pCell = m_pMap->GetCell(iPlotIndex);8174 if(pCell->IsHelpsProvidesFlankBonus())8175 {8176 FindClosestUnit(pLoopPlot, 1, true/*bMustHaveHalfHP*/, false /*bMustBeRangedUnit*/, 0 /*iRangeRequired*/, false /*bNeedsIgnoreLOS*/, true /*bMustBeMeleeUnit*/);8177 if (!m_CurrentMoveUnits.empty())8178 {8179 // Store the plot and a score, less units found, higher the score8180 CvTacticalTarget target;8181 target.SetTargetX(pLoopPlot->getX());8182 target.SetTargetY(pLoopPlot->getY());8183 target.SetAuxIntData(1000 - m_CurrentMoveUnits.size());8184 m_TempTargets.push_back(target);8185 CvString strMsg0;8186 strMsg0.Format("Found %d units to flank at X: %d, Y: %d", m_CurrentMoveUnits.size(), pLoopPlot->getX(), pLoopPlot->getY());8187 LogTacticalMessage(strMsg0, false);81888189 }8190 }8191 }8192 }81938194 if (!m_TempTargets.empty())8195 {8196 // Sort by the score to maximice efficiency moving units to flank targets8197 std::stable_sort(m_TempTargets.begin(), m_TempTargets.end());8198 for(unsigned int iI = 0; iI < m_TempTargets.size(); iI++)8199 {8200 UnitHandle pUnit;8201 CvPlot* targetPlot = GC.getMap().plot(m_TempTargets[iI].GetTargetX(), m_TempTargets[iI].GetTargetY());8202 FindClosestUnit(targetPlot, 1, true/*bMustHaveHalfHP*/, false /*bMustBeRangedUnit*/, 0 /*iRangeRequired*/, false /*bNeedsIgnoreLOS*/, true /*bMustBeMeleeUnit*/);8203 // We need to check if the unit has already moved into a flanking position.8204 for(unsigned int it = 0; it < m_CurrentMoveUnits.size(); it++)8205 {8206 bool validUnit = false;8207 std::vector<int>::iterator intIt;8208 for (intIt = movedUnits.begin(); intIt != movedUnits.end(); intIt++)8209 {8210 if (*intIt != m_CurrentMoveUnits[it].GetID())8211 {8212 validUnit = true;8213 break;8214 }8215 }8216 if (validUnit)8217 {8218 pUnit = m_pPlayer->getUnit(m_CurrentMoveUnits[it].GetID());8219 movedUnits.push_back(m_CurrentMoveUnits[it].GetID());8220 break;8221 }8222 }8223 if (pUnit)8224 {8225 pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), targetPlot->getX(), targetPlot->getY());8226 if(GC.getLogging() && GC.getAILogging())8227 {8228 CvString strMsg;8229 strMsg.Format("Moving into a flanking position with %s, X: %d, Y: %d", pUnit->getName().GetCString(),8230 targetPlot->getX(), targetPlot->getY());8231 LogTacticalMessage(strMsg, false);8232 }82338234 if(!pUnit->canMove())8235 {8236 UnitProcessed(pUnit->GetID());8237 } 8238 }8239 }8240 }82418242 // Make the attacks anyways8243 CvPlot* pTargetPlot = GC.getMap().plot(kTarget.GetTargetX(), kTarget.GetTargetY());8244 UnitHandle pDefender = pTargetPlot->getVisibleEnemyDefender(m_pPlayer->GetID());8245 if(pDefender)8246 {8247 kTarget.SetAuxIntData(pDefender->GetCurrHitPoints());8248 m_CurrentMoveCities.clear();8249 if(FindUnitsWithinStrikingDistance(pTargetPlot, 1, 0, false /* bNoRangedUnits */))8250 {8251 ComputeTotalExpectedDamage(&kTarget, pTargetPlot);8252 ExecuteAttack(&kTarget, pTargetPlot, false/*bInflictWhatWeTake*/, true/*bMustSurviveAttack*/);8253 }8254 }82558256 }8257 else8258 {8259#endif8260 CvTacticalAnalysisCell* pCell;8261 CvPlot* pLoopPlot;8262 int iPlotIndex;8263 int iPossibleFlankHexes = 0;8264 CvTacticalTarget target;82658266 // Count number of possible flank attack spaces around target8267 for(int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)8268 {8269 pLoopPlot = plotDirection(kTarget.GetTargetX(), kTarget.GetTargetY(), ((DirectionTypes)iI));8270 if(pLoopPlot != NULL)8271 {8272 iPlotIndex = GC.getMap().plotNum(pLoopPlot->getX(), pLoopPlot->getY());8273 pCell = m_pMap->GetCell(iPlotIndex);8274 if(pCell->IsHelpsProvidesFlankBonus())8275 {8276 iPossibleFlankHexes++;8277 }8278 }8279 }82808281 // If more than 1, find how many we can fill with units8282 if(iPossibleFlankHexes > 1)8283 {8284 int iFillableHexes = 0;8285 int iNumAttackers = 0;8286 m_TempTargets.clear();8287 for(int iI = 0; iI < NUM_DIRECTION_TYPES; iI++)8288 {8289 pLoopPlot = plotDirection(kTarget.GetTargetX(), kTarget.GetTargetY(), ((DirectionTypes)iI));8290 if(pLoopPlot != NULL)8291 {8292 iPlotIndex = GC.getMap().plotNum(pLoopPlot->getX(), pLoopPlot->getY());8293 pCell = m_pMap->GetCell(iPlotIndex);8294 if(pCell->IsHelpsProvidesFlankBonus())8295 {8296 if(FindClosestUnit(pLoopPlot, 0, true/*bMustHaveHalfHP*/, false /*bMustBeRangedUnit*/, 0 /*iRangeRequired*/, false /*bNeedsIgnoreLOS*/, true /*bMustBeMeleeUnit*/))8297 {8298 iFillableHexes++;8299 iNumAttackers++;8300 target.SetTargetX(pLoopPlot->getX());8301 target.SetTargetY(pLoopPlot->getY());8302 m_TempTargets.push_back(target);8303 }8304 // What about next turn?8305 else if(FindClosestUnit(pLoopPlot, 1, false/*bMustHaveHalfHP*/, false /*bMustBeRangedUnit*/, 0 /*iRangeRequired*/, false /*bNeedsIgnoreLOS*/, true /*bMustBeMeleeUnit*/))8306 {8307 iFillableHexes++;8308 target.SetTargetX(pLoopPlot->getX());8309 target.SetTargetY(pLoopPlot->getY());8310 m_TempTargets.push_back(target);8311 }8312 }8313 }8314 }83158316 // As long as we either get three hexes filled with one attacker, or two we can attack from, then this multi-hex attack is worth considering8317 if((iNumAttackers >= 1 && iFillableHexes >= 3) || iNumAttackers >= 2)8318 {8319 // Compute best way to fill the hexes8320 m_PotentialBlocks.clear();8321 for(unsigned int iI = 0; iI < m_TempTargets.size(); iI++)8322 {8323 pLoopPlot = GC.getMap().plot(m_TempTargets[iI].GetTargetX(), m_TempTargets[iI].GetTargetY());8324 if(!FindClosestUnit(pLoopPlot, 1, false/*bMustHaveHalfHP*/, false /*bMustBeRangedUnit*/, 0 /*iRangeRequired*/, false /*bNeedsIgnoreLOS*/, true /*bMustBeMeleeUnit*/))8325 {8326 iFillableHexes--;8327 if(iFillableHexes < 2)8328 {8329 return false;8330 }8331 }8332 else8333 {8334 // Save off the units that could get here8335 for(unsigned int jJ = 0; jJ < m_CurrentMoveUnits.size(); jJ++)8336 {8337 CvBlockingUnit block;8338 block.SetUnitID(m_CurrentMoveUnits[jJ].GetID());8339 block.SetPlot(pLoopPlot);8340 block.SetNumChoices((int)m_CurrentMoveUnits.size());8341 block.SetDistanceToTarget(m_CurrentMoveUnits[jJ].GetMovesToTarget());8342 m_PotentialBlocks.push_back(block);8343 }8344 }8345 }83468347 if(AssignFlankingUnits(iFillableHexes))8348 {8349 // Make moves up into hexes8350 for(unsigned int iI = 0; iI < m_ChosenBlocks.size(); iI++)8351 {8352 CvUnit* pUnit = m_pPlayer->getUnit(m_ChosenBlocks[iI].GetUnitID());8353 if(pUnit)8354 {8355 if(pUnit->plot() == m_ChosenBlocks[iI].GetPlot())8356 {8357 if(GC.getLogging() && GC.getAILogging())8358 {8359 CvString strMsg;8360 strMsg.Format("Already in a flanking position with %s, X: %d, Y: %d", pUnit->getName().GetCString(),8361 m_ChosenBlocks[iI].GetPlot()->getX(), m_ChosenBlocks[iI].GetPlot()->getY());8362 LogTacticalMessage(strMsg, false);8363 }8364 }8365 else8366 {8367 pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), m_ChosenBlocks[iI].GetPlot()->getX(), m_ChosenBlocks[iI].GetPlot()->getY());8368 if(GC.getLogging() && GC.getAILogging())8369 {8370 CvString strMsg;8371 strMsg.Format("Moving into a flanking position with %s, X: %d, Y: %d", pUnit->getName().GetCString(),8372 m_ChosenBlocks[iI].GetPlot()->getX(), m_ChosenBlocks[iI].GetPlot()->getY());8373 LogTacticalMessage(strMsg, false);8374 }83758376 if(pUnit->getMoves() <= 0)8377 {8378 UnitProcessed(pUnit->GetID());8379 }8380 }8381 }8382 }83838384 // Make attacks8385 CvPlot* pTargetPlot = GC.getMap().plot(kTarget.GetTargetX(), kTarget.GetTargetY());8386 UnitHandle pDefender = pTargetPlot->getVisibleEnemyDefender(m_pPlayer->GetID());8387 if(pDefender)8388 {8389 kTarget.SetAuxIntData(pDefender->GetCurrHitPoints());8390 m_CurrentMoveCities.clear();8391 if(FindUnitsWithinStrikingDistance(pTargetPlot, 1, 0, false /* bNoRangedUnits */))8392 {8393 ComputeTotalExpectedDamage(&kTarget, pTargetPlot);8394 ExecuteAttack(&kTarget, pTargetPlot, false/*bInflictWhatWeTake*/, true/*bMustSurviveAttack*/);8395 }8396 }8397 }8398 }8399 }8400#if defined(MOD_AI_SMART_V3)8401 }8402#endif84038404 return false;8405}