Hooks
static bool BeforeCvMilitaryAI_FindBestAttackTarget(CvMilitaryAI* self, AIOperationTypes eAIOperationType, PlayerTypes eEnemy, int* piWinningScore, CvMilitaryTarget& result);static void AfterCvMilitaryAI_FindBestAttackTarget(CvMilitaryAI* self, AIOperationTypes eAIOperationType, PlayerTypes eEnemy, int* piWinningScore, CvMilitaryTarget& result);static CvMilitaryTarget ReplaceCvMilitaryAI_FindBestAttackTarget(CvMilitaryTarget (*original)(CvMilitaryAI*, AIOperationTypes, PlayerTypes, int*), CvMilitaryAI* self, AIOperationTypes eAIOperationType, PlayerTypes eEnemy, int* piWinningScore);// CivHooks.h gives this hook point no types, so nothing checks these hooks' parameters.host->RegisterHook(host->pluginIndex, CivHooks::CvMilitaryAI_FindBestAttackTarget::ID, CIV_HOOK_BEFORE, (void*)&BeforeCvMilitaryAI_FindBestAttackTarget);host->RegisterHook(host->pluginIndex, CivHooks::CvMilitaryAI_FindBestAttackTarget::ID, CIV_HOOK_AFTER, (void*)&AfterCvMilitaryAI_FindBestAttackTarget);host->RegisterHook(host->pluginIndex, CivHooks::CvMilitaryAI_FindBestAttackTarget::ID, CIV_HOOK_REPLACE, (void*)&ReplaceCvMilitaryAI_FindBestAttackTarget);
Code
983/// Best target by land OR sea984CvMilitaryTarget CvMilitaryAI::FindBestAttackTarget(AIOperationTypes eAIOperationType, PlayerTypes eEnemy, int* piWinningScore)985{986 int iFriendlyLoop;987 int iEnemyLoop;988 int iUnitLoop;989 CvCity* pFriendlyCity;990 CvCity* pEnemyCity;991 CvUnit* pLoopUnit;992 static CvWeightedVector<CvMilitaryTarget, SAFE_ESTIMATE_NUM_CITIES* 10, true> weightedTargetList;993 CvMilitaryTarget chosenTarget;994 CvPlayer &kEnemy = GET_PLAYER(eEnemy);995996 // Estimate the relative strength of units near our cities and near their cities (can't use TacticalAnalysisMap because we may not be at war - and that it isn't current if we are calling this from the DiploAI)997 for (pFriendlyCity = m_pPlayer->firstCity(&iFriendlyLoop); pFriendlyCity != NULL; pFriendlyCity = m_pPlayer->nextCity(&iFriendlyLoop))998 {999 CvPlot* pPlot = pFriendlyCity->plot();1000 int iX = pPlot->getX();1001 int iY = pPlot->getY();1002 bool bGeneralInTheVicinity = false;1003 int iPower = 0;1004 for (pLoopUnit = m_pPlayer->firstUnit(&iUnitLoop); pLoopUnit != NULL; pLoopUnit = m_pPlayer->nextUnit(&iUnitLoop))1005 {1006 if (pLoopUnit->IsCombatUnit())1007 {1008 int iDistance = plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), iX, iY);1009 if (iDistance <= 5)1010 {1011 iPower += pLoopUnit->GetPower();1012 }1013 }1014 if (!bGeneralInTheVicinity && pLoopUnit->IsGreatGeneral())1015 {1016 int iDistance = plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), iX, iY);1017 if (iDistance <= 5)1018 {1019 bGeneralInTheVicinity = true;1020 }1021 }1022 }1023 if (bGeneralInTheVicinity)1024 {1025 iPower *= 11;1026 iPower /= 10;1027 }1028 pFriendlyCity->iScratch = iPower;1029 }1030 for(pEnemyCity = kEnemy.firstCity(&iEnemyLoop); pEnemyCity != NULL; pEnemyCity = kEnemy.nextCity(&iEnemyLoop))1031 {1032 CvPlot* pPlot = pEnemyCity->plot();1033 if(pPlot->isRevealed(m_pPlayer->getTeam()))1034 {1035 int iX = pPlot->getX();1036 int iY = pPlot->getY();1037 bool bGeneralInTheVicinity = false;1038 int iPower = 0;1039 for (pLoopUnit = kEnemy.firstUnit(&iUnitLoop); pLoopUnit != NULL; pLoopUnit = kEnemy.nextUnit(&iUnitLoop))1040 {1041 if (pLoopUnit->IsCombatUnit())1042 {1043 int iDistance = plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), iX, iY);1044 if (iDistance <= 5)1045 {1046 iPower += pLoopUnit->GetPower();1047 }1048 }1049 if (!bGeneralInTheVicinity && pLoopUnit->IsGreatGeneral())1050 {1051 int iDistance = plotDistance(pLoopUnit->getX(), pLoopUnit->getY(), iX, iY);1052 if (iDistance <= 5)1053 {1054 bGeneralInTheVicinity = true;1055 }1056 }1057 }1058 if (bGeneralInTheVicinity)1059 {1060 iPower *= 11;1061 iPower /= 10;1062 }1063 pEnemyCity->iScratch = iPower;1064 }1065 }10661067 // Build a list of all the possible start city/target city pairs1068 static CvWeightedVector<CvMilitaryTarget, SAFE_ESTIMATE_NUM_CITIES* 10, true> prelimWeightedTargetList;1069 prelimWeightedTargetList.clear();1070 for(pFriendlyCity = m_pPlayer->firstCity(&iFriendlyLoop); pFriendlyCity != NULL; pFriendlyCity = m_pPlayer->nextCity(&iFriendlyLoop))1071 {1072 for(pEnemyCity = GET_PLAYER(eEnemy).firstCity(&iEnemyLoop); pEnemyCity != NULL; pEnemyCity = GET_PLAYER(eEnemy).nextCity(&iEnemyLoop))1073 {1074 if(pEnemyCity->plot()->isRevealed(m_pPlayer->getTeam()))1075 {1076 CvMilitaryTarget target;1077 int iWeight;1078 target.m_pMusterCity = pFriendlyCity;1079 target.m_pTargetCity = pEnemyCity;1080 target.iMusterNearbyUnitPower = pFriendlyCity->iScratch;1081 target.iTargetNearbyUnitPower = pEnemyCity->iScratch;10821083 if (eAIOperationType == AI_OPERATION_PURE_NAVAL_CITY_ATTACK)1084 {1085 target.m_bAttackBySea = true;1086 if (target.m_pMusterCity->isCoastal() && target.m_pTargetCity->isCoastal())1087 {1088 target.m_iPathLength = plotDistance(target.m_pMusterCity->getX(), target.m_pMusterCity->getY(), target.m_pTargetCity->getX(), target.m_pTargetCity->getY());1089 }1090 }10911092 else1093 {1094 ShouldAttackBySea(eEnemy, target);10951096 if (!pFriendlyCity->isCoastal() && target.m_bAttackBySea)1097 {1098 continue;1099 }1100 }11011102 if(target.m_iPathLength > 0)1103 {1104 iWeight = (10000 - target.m_iPathLength); // Start by using the path length as the weight, shorter paths have higher weight1105 prelimWeightedTargetList.push_back(target, iWeight);1106 }1107 }1108 }1109 }11101111 // Let's score the 25 shortest paths ... anything more than that means there are too many interior cities from one (or both) sides being considered1112 prelimWeightedTargetList.SortItems();1113 weightedTargetList.clear();1114 int iTargetsConsidered = 0;1115 for (int iI = 0; iI < prelimWeightedTargetList.size() && iTargetsConsidered < 25; iI++)1116 {1117 CvMilitaryTarget target = prelimWeightedTargetList.GetElement(iI);1118 int iWeight;11191120 // If a sea target, we haven't checked the path yet. Do that now1121 if (target.m_bAttackBySea)1122 {1123 if (!target.m_pMusterCity->isCoastal())1124 {1125 continue;1126 }1127 if (!target.m_pTargetCity->isCoastal())1128 {1129 continue;1130 }1131 CvPlot *pSeaPlotNearMuster = GetCoastalPlotAdjacentToTarget(target.m_pMusterCity->plot(), NULL);1132 CvPlot *pSeaPlotNearTarget = GetCoastalPlotAdjacentToTarget(target.m_pTargetCity->plot(), NULL);1133 if(!GC.getStepFinder().DoesPathExist(m_pPlayer->GetID(), eEnemy, pSeaPlotNearMuster, pSeaPlotNearTarget))1134 {1135 continue;1136 }1137 }11381139 iWeight = ScoreTarget(target, eAIOperationType);1140 weightedTargetList.push_back(target, iWeight);1141 iTargetsConsidered++;1142 }11431144 // Didn't find anything, abort1145 if(weightedTargetList.size() == 0)1146 {1147 chosenTarget.m_pTargetCity = NULL; // Call off the attack1148 if (piWinningScore)1149 {1150 *piWinningScore = -1;1151 }1152 return chosenTarget;1153 }11541155 weightedTargetList.SortItems();1156 LogAttackTargets(eAIOperationType, eEnemy, weightedTargetList);11571158 if(weightedTargetList.GetTotalWeight() > 0)1159 {1160 RandomNumberDelegate fcn;1161 fcn = MakeDelegate(&GC.getGame(), &CvGame::getJonRandNum);1162 int iNumChoices = max (1, (weightedTargetList.size() * 25 / 100));1163 chosenTarget = weightedTargetList.ChooseFromTopChoices(iNumChoices, &fcn, "Choosing attack target from top 25%% of choices");1164 // if we need the winning score1165 if (piWinningScore)1166 {1167 *piWinningScore = ScoreTarget(chosenTarget, eAIOperationType);1168 }1169 LogChosenTarget(eAIOperationType, eEnemy, chosenTarget);1170 }1171 else1172 {1173 chosenTarget.m_pTargetCity = NULL; // Call off the attack1174 if (piWinningScore)1175 {1176 *piWinningScore = -1;1177 }1178 }11791180 return chosenTarget;1181}