Hooks
static bool BeforeCvTacticalAI_ExecuteAttack(CvTacticalAI* self, CvTacticalTarget* pTarget, CvPlot* pTargetPlot, bool bInflictWhatWeTake, bool bMustSurviveAttack);static void AfterCvTacticalAI_ExecuteAttack(CvTacticalAI* self, CvTacticalTarget* pTarget, CvPlot* pTargetPlot, bool bInflictWhatWeTake, bool bMustSurviveAttack);static void ReplaceCvTacticalAI_ExecuteAttack(void (*original)(CvTacticalAI*, CvTacticalTarget*, CvPlot*, bool, bool), CvTacticalAI* self, CvTacticalTarget* pTarget, CvPlot* pTargetPlot, bool bInflictWhatWeTake, bool bMustSurviveAttack);CivRegisterBefore<CivHooks::CvTacticalAI_ExecuteAttack>(host, &BeforeCvTacticalAI_ExecuteAttack);CivRegisterAfter<CivHooks::CvTacticalAI_ExecuteAttack>(host, &AfterCvTacticalAI_ExecuteAttack);CivRegisterReplace<CivHooks::CvTacticalAI_ExecuteAttack>(host, &ReplaceCvTacticalAI_ExecuteAttack);
Code
6294/// Attack a defended space6295void CvTacticalAI::ExecuteAttack(CvTacticalTarget* pTarget, CvPlot* pTargetPlot, bool bInflictWhatWeTake, bool bMustSurviveAttack)6296{6297 void* pFirstAttacker = NULL;6298 bool bFirstAttackRanged = false;6299 bool bFirstAttackCity = false;63006301 if(PlotAlreadyTargeted(pTargetPlot) != -1)6302 {6303 return;6304 }63056306 // How much damage do we still need to inflict?6307 int iDamageRemaining = (pTarget->GetAuxIntData() * (100 + GC.getAI_TACTICAL_OVERKILL_PERCENT())) / 100;63086309#if defined(MOD_AI_SMART_V3)6310 if (MOD_AI_SMART_V3)6311 {6312 // Start by sending possible air sweeps6313 for(unsigned int iI = 0; iI < m_CurrentAirUnits.size(); iI++)6314 {6315 UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentAirUnits[iI].GetID());63166317 if(pUnit && !pUnit->TurnProcessed())6318 {6319 if(pUnit->canAirSweep())6320 {6321 if (pTarget)6322 {6323 pUnit->PushMission(CvTypes::getMISSION_AIR_SWEEP(), pTargetPlot->getX(), pTargetPlot->getY());6324 UnitProcessed(m_CurrentAirUnits[iI].GetID(), false /*bMarkTacticalMap*/);63256326 if(GC.getLogging() && GC.getAILogging())6327 {6328 CvString strMsg;6329 strMsg.Format("Sending %s to air sweep prior to attack to Target X: %d, Y: %d", pUnit->getName().GetCString(), pTargetPlot->getX(), pTargetPlot->getY());6330 LogTacticalMessage(strMsg);6331 }6332 }6333 }6334 }6335 }6336 }6337#endif63386339 // Start by applying damage from city bombards6340 for(unsigned int iI = 0; iI < m_CurrentMoveCities.size() && iDamageRemaining > 0; iI++)6341 {6342 CvCity* pCity = m_pPlayer->getCity(m_CurrentMoveCities[iI].GetID());6343 if(pCity != NULL)6344 {6345 if(QueueAttack(pCity, pTarget, true /*bRanged*/, true /*bCity*/))6346 {6347 pFirstAttacker = (void*)pCity;6348 bFirstAttackRanged = true;6349 bFirstAttackCity = true;6350 }63516352 // Subtract off expected damage6353 iDamageRemaining -= m_CurrentMoveCities[iI].GetExpectedTargetDamage();6354 }6355 }63566357#if defined(MOD_AI_SMART_V3)6358 if (MOD_AI_SMART_V3)6359 {6360 // Make a list of adjacent plots6361 std::vector<CvPlot *> plotList;6362 for(int iDirectionLoop = 0; iDirectionLoop < NUM_DIRECTION_TYPES; ++iDirectionLoop)6363 {6364 CvPlot* pAdjacentPlot = plotDirection(pTargetPlot->getX(), pTargetPlot->getY(), ((DirectionTypes)iDirectionLoop));6365 if (pAdjacentPlot)6366 {6367 int iPlotIndex = GC.getMap().plotNum(pAdjacentPlot->getX(), pAdjacentPlot->getY());6368 CvTacticalAnalysisCell *pCell = m_pMap->GetCell(iPlotIndex);6369 if(pAdjacentPlot != NULL && pAdjacentPlot->getNumDefenders(pTarget->GetTargetPlayer()) == 0 && !pCell->IsEnemyCity())6370 {6371 plotList.push_back(pAdjacentPlot);6372 }6373 }6374 }63756376 // First loop for melee units just to reposition.6377 for(unsigned int iI = 0; iI < m_CurrentMoveUnits.size(); iI++)6378 {6379 if(!bInflictWhatWeTake || m_CurrentMoveUnits[iI].GetExpectedTargetDamage() >= m_CurrentMoveUnits[iI].GetExpectedSelfDamage())6380 {6381 UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentMoveUnits[iI].GetID());63826383 if(pUnit)6384 {6385 if(pUnit->getMoves() > 0 && (!bMustSurviveAttack || ((m_CurrentMoveUnits[iI].GetExpectedSelfDamage() + pUnit->getDamage()) < pUnit->GetMaxHitPoints())))6386 {6387 // Are we a melee unit6388 if(!pUnit->IsCanAttackRanged())6389 {6390 bool unitHasMoved = false;63916392 // Not adjacent6393 if (plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) > 1)6394 {6395 // Find spaces adjacent to target we can move into with MP left6396 std::vector<CvPlot*>::iterator it;63976398 for (it = plotList.begin(); it != plotList.end(); it++)6399 {6400 if (TurnsToReachTarget(pUnit, *it, false /*bReusePaths*/, false /*bIgnoreUnits*/, true /*bIgnoreStacking*/) == 0)6401 {6402 // Move up there6403 pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), (*it)->getX(), (*it)->getY());6404 plotList.erase(it);6405 unitHasMoved = true;6406 break;6407 }6408 }64096410 // Didn't find an open hex6411 if (!unitHasMoved)6412 {6413 if(GC.getLogging() && GC.getAILogging())6414 {6415 CvString strMsg;6416 strMsg.Format("Not enough free hexes next for all melee units to attack, Target X: %d, Y: %d", pTargetPlot->getX(), pTargetPlot->getY());6417 LogTacticalMessage(strMsg);6418 }64196420 // Move up if we can6421 if (MoveToEmptySpaceNearTarget(pUnit, pTargetPlot, (pUnit->getDomainType() == DOMAIN_LAND)))6422 {6423 if(GC.getLogging() && GC.getAILogging())6424 {6425 CvString strMsg;6426 strMsg.Format("Moving adjacent for attack next turn, Now at X: %d, Y: %d", pUnit->getX(), pUnit->getY());6427 LogTacticalMessage(strMsg);6428 }6429 }6430 }6431 }6432 }6433 }6434 }6435 }6436 else6437 {6438 if(GC.getLogging() && GC.getAILogging())6439 {6440 CvString strMsg;6441 strMsg.Format("Not attacking with unit. Can't generate a good damage ratio.");6442 LogTacticalMessage(strMsg);6443 }6444 }6445 }6446 }6447#endif64486449 // First loop is ranged units only6450 for(unsigned int iI = 0; iI < m_CurrentMoveUnits.size() && iDamageRemaining > 0; iI++)6451 {6452 if(!bInflictWhatWeTake || m_CurrentMoveUnits[iI].GetExpectedTargetDamage() >= m_CurrentMoveUnits[iI].GetExpectedSelfDamage())6453 {6454 UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentMoveUnits[iI].GetID());6455 if(pUnit)6456 {6457 if(pUnit->getMoves() > 0)6458 {6459#if defined(MOD_AI_SMART_V3)6460 if (MOD_AI_SMART_V3)6461 {6462 // Are we a ranged unit6463 if(pUnit->IsCanAttackRanged())6464 {6465 bool bQueueTryRangedAttack = false;64666467 // Are we in range and do we have LOS to the target?6468 if((plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) <= pUnit->GetRange()) && (pUnit->canEverRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY())))6469 {6470 bQueueTryRangedAttack = true;6471 }6472 else6473 {6474 // Not in range/ not LOS: then lets try to reposition unit.6475 CvPlot* repositionPlot = GetBestRepositionPlot(pUnit, pTargetPlot);64766477 if (repositionPlot != NULL)6478 {6479 if(GC.getLogging() && GC.getAILogging())6480 {6481 CvString strMsg;6482 strMsg.Format("Repositioning ranged unit from X: %d, Y: %d to X: %d, Y: %d", pUnit->getX(), pUnit->getY(), repositionPlot->getX(), repositionPlot->getY());6483 LogTacticalMessage(strMsg);6484 }64856486 pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), repositionPlot->getX(), repositionPlot->getY());6487 bQueueTryRangedAttack = true;6488 }6489 }64906491 if (bQueueTryRangedAttack)6492 {6493 // Do we have LOS to the target?6494 if(pUnit->canEverRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY()))6495 {6496 // Do we need to set up to make a ranged attack?6497 if(pUnit->canSetUpForRangedAttack(NULL))6498 {6499 pUnit->setSetUpForRangedAttack(true);6500 if(GC.getLogging() && GC.getAILogging())6501 {6502 CvString strTemp;6503 CvString strMsg;6504 strTemp = GC.getUnitInfo(pUnit->getUnitType())->GetDescription();6505 strMsg.Format("Set up %s for ranged attack", strTemp.GetCString());6506 LogTacticalMessage(strMsg);6507 }6508 if(!pUnit->canMove())6509 {6510 pUnit->SetTacticalAIPlot(NULL);6511 UnitProcessed(pUnit->GetID());6512 }6513 }65146515 // Can we hit it with a ranged attack? If so, that gets first priority6516 if(pUnit->canMove() && pUnit->canRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY()))6517 {6518 // Queue up this attack6519 if(QueueAttack((void*)pUnit.pointer(), pTarget, true /*bRanged*/, false /*bCity*/))6520 {6521 pFirstAttacker = (void*)pUnit.pointer();6522 bFirstAttackRanged = true;6523 }6524 pUnit->SetTacticalAIPlot(NULL);6525 UnitProcessed(m_CurrentMoveUnits[iI].GetID());65266527 // Subtract off expected damage6528 iDamageRemaining -= m_CurrentMoveUnits[iI].GetExpectedTargetDamage();6529 }6530 }6531 }6532 }6533 }6534 else6535 {6536#endif6537 if(!bMustSurviveAttack || ((m_CurrentMoveUnits[iI].GetExpectedSelfDamage() + pUnit->getDamage()) < pUnit->GetMaxHitPoints()))6538 {6539 // Are we a ranged unit6540 if(pUnit->IsCanAttackRanged())6541 {6542 // Are we in range?6543 if(plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) <= pUnit->GetRange())6544 {6545 // Do we have LOS to the target?6546 if(pUnit->canEverRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY()))6547 {6548 // Do we need to set up to make a ranged attack?6549 if(pUnit->canSetUpForRangedAttack(NULL))6550 {6551 pUnit->setSetUpForRangedAttack(true);6552 if(GC.getLogging() && GC.getAILogging())6553 {6554 CvString strTemp;6555 CvString strMsg;6556 strTemp = GC.getUnitInfo(pUnit->getUnitType())->GetDescription();6557 strMsg.Format("Set up %s for ranged attack", strTemp.GetCString());6558 LogTacticalMessage(strMsg);6559 }6560 if(!pUnit->canMove())6561 {6562 pUnit->SetTacticalAIPlot(NULL);6563 UnitProcessed(pUnit->GetID());6564 }6565 }65666567 // Can we hit it with a ranged attack? If so, that gets first priority6568 if(pUnit->canMove() && pUnit->canRangeStrikeAt(pTargetPlot->getX(), pTargetPlot->getY()))6569 {6570 // Queue up this attack6571 if(QueueAttack((void*)pUnit.pointer(), pTarget, true /*bRanged*/, false /*bCity*/))6572 {6573 pFirstAttacker = (void*)pUnit.pointer();6574 bFirstAttackRanged = true;6575 }6576 pUnit->SetTacticalAIPlot(NULL);6577 UnitProcessed(m_CurrentMoveUnits[iI].GetID());65786579 // Subtract off expected damage6580 iDamageRemaining -= m_CurrentMoveUnits[iI].GetExpectedTargetDamage();6581 }6582 }6583 }6584 }6585 }6586 else6587 {6588 if(GC.getLogging() && GC.getAILogging())6589 {6590 CvString strMsg;6591 strMsg.Format("Not attacking with unit. We'll destroy ourself, %s, X: %d, Y: %d", pUnit->getName().GetCString(), pUnit->getX(), pUnit->getY());6592 LogTacticalMessage(strMsg);6593 }6594 }6595#if defined(MOD_AI_SMART_V3)6596 }6597#endif6598 }6599 }6600 }6601 else6602 {6603 if(GC.getLogging() && GC.getAILogging())6604 {6605 CvString strMsg;6606 strMsg.Format("Not attacking with unit. Can't generate a good damage ratio.");6607 LogTacticalMessage(strMsg);6608 }6609 }6610 }66116612 // If target is city, want to get in one melee attack, so set damage remaining to 16613 if(pTarget->GetTargetType() == AI_TACTICAL_TARGET_CITY && iDamageRemaining < 1)6614 {6615 iDamageRemaining = 1;6616 }66176618 // Make a list of adjacent plots6619 std::vector<CvPlot *> plotList;6620#if defined(MOD_AI_SMART_V3)6621 if (!MOD_AI_SMART_V3)6622 {6623#endif6624 for(int iDirectionLoop = 0; iDirectionLoop < NUM_DIRECTION_TYPES; ++iDirectionLoop)6625 {6626 CvPlot* pAdjacentPlot = plotDirection(pTargetPlot->getX(), pTargetPlot->getY(), ((DirectionTypes)iDirectionLoop));6627 if (pAdjacentPlot)6628 {6629 int iPlotIndex = GC.getMap().plotNum(pAdjacentPlot->getX(), pAdjacentPlot->getY());6630 CvTacticalAnalysisCell *pCell = m_pMap->GetCell(iPlotIndex);6631 if(pAdjacentPlot != NULL && pAdjacentPlot->getNumDefenders(pTarget->GetTargetPlayer()) == 0 && !pCell->IsEnemyCity())6632 {6633 plotList.push_back(pAdjacentPlot);6634 }6635 }6636 }6637#if defined(MOD_AI_SMART_V3)6638 }6639#endif66406641 // Second loop are only melee units6642 for(unsigned int iI = 0; iI < m_CurrentMoveUnits.size() && iDamageRemaining > 0; iI++)6643 {6644 if(!bInflictWhatWeTake || m_CurrentMoveUnits[iI].GetExpectedTargetDamage() >= m_CurrentMoveUnits[iI].GetExpectedSelfDamage())6645 {6646 UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentMoveUnits[iI].GetID());6647 if(pUnit)6648 {6649 if(pUnit->getMoves() > 0 && (!bMustSurviveAttack || ((m_CurrentMoveUnits[iI].GetExpectedSelfDamage() + pUnit->getDamage()) < pUnit->GetMaxHitPoints())))6650 {6651 // Are we a melee unit6652 if(!pUnit->IsCanAttackRanged())6653 {6654#if defined(MOD_AI_SMART_V3)6655 if (MOD_AI_SMART_V3)6656 {6657 // Already adjacent?6658 if (plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) <= 1)6659 {6660 // Queue up this attack6661 if(QueueAttack((void*)pUnit.pointer(), pTarget, false /*bRanged*/, false /*bCity*/))6662 {6663 pFirstAttacker = (void*)pUnit.pointer();6664 }6665 pUnit->SetTacticalAIPlot(NULL);6666 UnitProcessed(m_CurrentMoveUnits[iI].GetID(), false /*bMarkTacticalMap*/);66676668 // Subtract off expected damage6669 iDamageRemaining -= m_CurrentMoveUnits[iI].GetExpectedTargetDamage();6670 }6671 }6672 else6673 {6674#endif6675 bool bQueueAttackNow = false;6676 6677 // Already adjacent?6678 if (plotDistance(pUnit->getX(), pUnit->getY(), pTargetPlot->getX(), pTargetPlot->getY()) <= 1)6679 {6680 bQueueAttackNow = true;6681 }6682 else6683 {6684 // Find spaces adjacent to target we can move into with MP left6685 std::vector<CvPlot *>::iterator it;6686 for (it = plotList.begin(); it != plotList.end(); it++)6687 {6688 if (TurnsToReachTarget(pUnit, *it, false /*bReusePaths*/, false /*bIgnoreUnits*/, true /*bIgnoreStacking*/) == 0)6689 {6690 // Move up there6691 pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), (*it)->getX(), (*it)->getY());6692 bQueueAttackNow = true;66936694 plotList.erase(it);6695 break;6696 }6697 }66986699 // Didn't find an open hex6700 if (!bQueueAttackNow)6701 {6702 if(GC.getLogging() && GC.getAILogging())6703 {6704 CvString strMsg;6705 strMsg.Format("Not enough free hexes next for all melee units to attack, Target X: %d, Y: %d", pTargetPlot->getX(), pTargetPlot->getY());6706 LogTacticalMessage(strMsg);6707 }6708 6709 // Move up if we can6710 if (MoveToEmptySpaceNearTarget(pUnit, pTargetPlot, (pUnit->getDomainType() == DOMAIN_LAND)))6711 {6712 if(GC.getLogging() && GC.getAILogging())6713 {6714 CvString strMsg;6715 strMsg.Format("Moving adjacent for attack next turn, Now at X: %d, Y: %d", pUnit->getX(), pUnit->getY());6716 LogTacticalMessage(strMsg);6717 }6718 }6719 }6720 }67216722 if (bQueueAttackNow)6723 {6724 // Queue up this attack6725 if(QueueAttack((void*)pUnit.pointer(), pTarget, false /*bRanged*/, false /*bCity*/))6726 {6727 pFirstAttacker = (void*)pUnit.pointer();6728 }6729 pUnit->SetTacticalAIPlot(NULL);6730 UnitProcessed(m_CurrentMoveUnits[iI].GetID(), false /*bMarkTacticalMap*/);67316732 // Subtract off expected damage6733 iDamageRemaining -= m_CurrentMoveUnits[iI].GetExpectedTargetDamage();6734 }6735#if defined(MOD_AI_SMART_V3)6736 }6737#endif6738 }6739 }6740 else6741 {6742 if(GC.getLogging() && GC.getAILogging())6743 {6744 CvString strMsg;6745 strMsg.Format("Not attacking with unit. We'll destroy ourself, %s, X: %d, Y: %d", pUnit->getName().GetCString(), pUnit->getX(), pUnit->getY());6746 LogTacticalMessage(strMsg);6747 }6748 }6749 }6750 }6751 else6752 {6753 if(GC.getLogging() && GC.getAILogging())6754 {6755 CvString strMsg;6756 strMsg.Format("Not attacking with unit. Can't generate a good damage ratio.");6757 LogTacticalMessage(strMsg);6758 }6759 }6760 }67616762 // Start up first attack6763 if(pFirstAttacker)6764 {6765 LaunchAttack(pFirstAttacker, pTarget, true /*bFirst*/, bFirstAttackRanged, bFirstAttackCity);6766 }6767}