Hooks
static bool BeforeCvTacticalAI_ExecuteSafeBombards(CvTacticalAI* self, CvTacticalTarget& kTarget, bool& result);static void AfterCvTacticalAI_ExecuteSafeBombards(CvTacticalAI* self, CvTacticalTarget& kTarget, bool& result);static bool ReplaceCvTacticalAI_ExecuteSafeBombards(bool (*original)(CvTacticalAI*, CvTacticalTarget&), CvTacticalAI* self, CvTacticalTarget& kTarget);CivRegisterBefore<CivHooks::CvTacticalAI_ExecuteSafeBombards>(host, &BeforeCvTacticalAI_ExecuteSafeBombards);CivRegisterAfter<CivHooks::CvTacticalAI_ExecuteSafeBombards>(host, &AfterCvTacticalAI_ExecuteSafeBombards);CivRegisterReplace<CivHooks::CvTacticalAI_ExecuteSafeBombards>(host, &ReplaceCvTacticalAI_ExecuteSafeBombards);
Code
7668/// Bombard enemy units from plots they can't reach (return true if some attack made)7669bool CvTacticalAI::ExecuteSafeBombards(CvTacticalTarget& kTarget)7670{7671 CvTacticalUnit unit;7672 void* pFirstAttacker = NULL;7673 UnitHandle pDefender;7674 int iRequiredDamage = 0;7675 bool bCityCanAttack = false;7676 bool bFirstAttackCity = false;76777678 CvPlot* pTargetPlot = GC.getMap().plot(kTarget.GetTargetX(), kTarget.GetTargetY());7679 if(PlotAlreadyTargeted(pTargetPlot) != -1)7680 {7681 return false;7682 }76837684 // Get required damage on unit target7685 pDefender = pTargetPlot->getVisibleEnemyDefender(m_pPlayer->GetID());7686#if defined(MOD_AI_SMART_V3)7687 if(pDefender && (!MOD_AI_SMART_V3 || !pTargetPlot->getPlotCity()))7688#else7689 if(pDefender)7690#endif7691 {7692 iRequiredDamage = pDefender->GetCurrHitPoints();76937694 // If this is a unit target we might also be able to hit it with a city7695 bCityCanAttack = FindCitiesWithinStrikingDistance(pTargetPlot);7696 if(bCityCanAttack)7697 {7698 ComputeTotalExpectedBombardDamage(pDefender);76997700 // Start by applying damage from city bombards7701 for(unsigned int iI = 0; iI < m_CurrentMoveCities.size() && iRequiredDamage > 0; iI++)7702 {7703 CvCity* pCity = m_pPlayer->getCity(m_CurrentMoveCities[iI].GetID());7704 if(pCity != NULL)7705 {7706 if(QueueAttack(pCity, &kTarget, true /*bRanged*/, true /*bCity*/))7707 {7708 pFirstAttacker = (void*)pCity;7709 bFirstAttackCity = true;7710 }77117712 // Subtract off expected damage7713 iRequiredDamage -= m_CurrentMoveCities[iI].GetExpectedTargetDamage();7714 }7715 }7716 }7717 }77187719 // Get required damage on city target7720 else7721 {7722 CvCity* pCity = pTargetPlot->getPlotCity();7723 if(pCity != NULL)7724 {7725 iRequiredDamage = pCity->GetMaxHitPoints() - pCity->getDamage();77267727 // Can't eliminate a city with ranged fire, so don't target one if that low on health7728 if(iRequiredDamage <= 1)7729 {7730 return false;7731 }7732 }7733 }77347735 // Need to keep hitting target?7736 if(iRequiredDamage <= 0)7737 {7738 return false;7739 }77407741 // For each of our ranged units, see if they are already in a plot that can bombard that can't be attacked.7742 // If so, bombs away!7743 m_CurrentMoveUnits.clear();7744 list<int>::iterator it;7745 for(it = m_CurrentTurnUnits.begin(); it != m_CurrentTurnUnits.end(); it++)7746 {7747 UnitHandle pUnit = m_pPlayer->getUnit(*it);7748 if(pUnit && pUnit->IsCanAttackRanged() && !pUnit->isOutOfAttacks())7749 {7750 int iPlotIndex = GC.getMap().plotNum(pUnit->getX(), pUnit->getY());7751 CvTacticalAnalysisCell* pCell = GC.getGame().GetTacticalAnalysisMap()->GetCell(iPlotIndex);7752 if(pCell->IsWithinRangeOfTarget() && !pCell->IsSubjectToAttack() && IsExpectedToDamageWithRangedAttack(pUnit, pTargetPlot))7753 {7754 if(pUnit->canSetUpForRangedAttack(NULL))7755 {7756 pUnit->setSetUpForRangedAttack(true);7757 if(GC.getLogging() && GC.getAILogging())7758 {7759 CvString strMsg;7760 strMsg.Format("Set up %s for ranged attack", pUnit->getName().GetCString());7761 LogTacticalMessage(strMsg, false);7762 }7763 }77647765 if(pUnit->canMove() && pUnit->canRangeStrikeAt(kTarget.GetTargetX(), kTarget.GetTargetY()))7766 {7767 if(GC.getLogging() && GC.getAILogging())7768 {7769 CvString strMsg;7770 strMsg.Format("Making a safe bombard (no move) with %s, Target X: %d, Target Y: %d, At X: %d, At Y: %d", pUnit->getName().GetCString(),7771 kTarget.GetTargetX(), kTarget.GetTargetY(), pUnit->getX(), pUnit->getY());7772 LogTacticalMessage(strMsg, false);7773 }77747775 if(QueueAttack((void*)pUnit.pointer(), &kTarget, true /*bRanged*/, false /*bCity*/))7776 {7777 pFirstAttacker = (void*)pUnit.pointer();7778 }77797780 // Save off ID so can be cleared from list to process for turn7781 unit.SetID(pUnit->GetID());7782 m_CurrentMoveUnits.push_back(unit);7783 }7784 }7785 }7786 }77877788 // Clear out the units we just processed from the list for this turn7789 std::vector<CvTacticalUnit>::iterator currMoveIt;7790 for(currMoveIt = m_CurrentMoveUnits.begin(); currMoveIt != m_CurrentMoveUnits.end(); ++currMoveIt)7791 {7792 UnitProcessed(currMoveIt->GetID());7793 }77947795 // For each plot that we can bombard from that the enemy can't attack, try and move a ranged unit there.7796 // If so, make that move and mark that tile as blocked with our unit. If unit has movement left, queue up an attack7797 int iDX, iDY;7798 CvPlot* pLoopPlot;7799 int iPlotIndex;7800 CvTacticalAnalysisCell* pCell;78017802 for(int iTurnsToReach = 0; iTurnsToReach <= 2; iTurnsToReach++)7803 {7804 int iRange = m_pMap->GetBestFriendlyRange();78057806 for(iDX = -(iRange); iDX <= iRange; iDX++)7807 {7808 for(iDY = -(iRange); iDY <= iRange; iDY++)7809 {7810 pLoopPlot = plotXY(kTarget.GetTargetX(), kTarget.GetTargetY(), iDX, iDY);7811 if(pLoopPlot != NULL)7812 {7813 int iDistance = plotDistance(pLoopPlot->getX(), pLoopPlot->getY(), kTarget.GetTargetX(), kTarget.GetTargetY());7814 if(iDistance > 0 && iDistance <= iRange)7815 {7816 iPlotIndex = GC.getMap().plotNum(pLoopPlot->getX(), pLoopPlot->getY());7817 pCell = m_pMap->GetCell(iPlotIndex);7818 if(pCell->IsRevealed() && pCell->CanUseForOperationGathering())7819 {7820 if(pCell->IsWithinRangeOfTarget() && !pCell->IsSubjectToAttack())7821 {7822 bool bHaveLOS = pLoopPlot->canSeePlot(pTargetPlot, m_pPlayer->getTeam(), iRange, NO_DIRECTION);7823 if(FindClosestUnit(pLoopPlot, iTurnsToReach, false/*bMustHaveHalfHP*/, true/*bMustBeRangedUnit*/, iDistance, !bHaveLOS, false/*bMustBeMeleeUnit*/, true/*bIgnoreUnits*/, pTargetPlot))7824 {7825 UnitHandle pUnit = m_pPlayer->getUnit(m_CurrentMoveUnits[0].GetID());7826 if(pUnit)7827 {7828 // Check for presence of unmovable friendly units7829 UnitHandle pBlockingUnit = pLoopPlot->getBestDefender(m_pPlayer->GetID());7830 if(!pBlockingUnit || ExecuteMoveOfBlockingUnit(pBlockingUnit))7831 {7832 pUnit->PushMission(CvTypes::getMISSION_MOVE_TO(), pLoopPlot->getX(), pLoopPlot->getY());78337834 if(GC.getLogging() && GC.getAILogging())7835 {7836 CvString strMsg;7837 strMsg.Format("Moving closer for safe bombard with %s, Target X: %d, Y: %d, Bombard From X: %d, Y: %d, Now At X: %d, Y: %d", pUnit->getName().GetCString(),7838 kTarget.GetTargetX(), kTarget.GetTargetY(), pLoopPlot->getX(), pLoopPlot->getY(), pUnit->getX(), pUnit->getY());7839 LogTacticalMessage(strMsg, false);7840 }78417842 UnitProcessed(m_CurrentMoveUnits[0].GetID());78437844 if(pUnit->canSetUpForRangedAttack(NULL))7845 {7846 pUnit->setSetUpForRangedAttack(true);7847 if(GC.getLogging() && GC.getAILogging())7848 {7849 CvString strMsg;7850 strMsg.Format("Set up %s for ranged attack", pUnit->getName().GetCString());7851 LogTacticalMessage(strMsg, false);7852 }7853 }78547855 if(pUnit->canMove() && !pUnit->isOutOfAttacks() && pUnit->canRangeStrikeAt(kTarget.GetTargetX(), kTarget.GetTargetY()))7856 {7857 if(GC.getLogging() && GC.getAILogging())7858 {7859 CvString strMsg;7860 strMsg.Format("Making a safe bombard (half move) with %s, Target X: %d, Target Y: %d, At X: %d, At Y: %d", pUnit->getName().GetCString(),7861 kTarget.GetTargetX(), kTarget.GetTargetY(), pUnit->getX(), pUnit->getY());7862 LogTacticalMessage(strMsg, false);7863 }78647865 if(QueueAttack((void*)pUnit.pointer(), &kTarget, true /*bRanged*/, false /*bCityAttack*/))7866 {7867 pFirstAttacker = (void*)pUnit.pointer();7868 }7869 }7870 }7871 }7872 }7873 }7874 }7875 }7876 }7877 }7878 }7879 }78807881 // Launch the initial attack plotted7882 if(pFirstAttacker)7883 {7884 LaunchAttack(pFirstAttacker, &kTarget, true /*bFirst*/, true /*bFirstAttackRanged*/, bFirstAttackCity);7885 return true;7886 }7887 return false;7888}