Hooks
static bool BeforePathCost(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder, int& result);static void AfterPathCost(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder, int& result);static int ReplacePathCost(int (*original)(CvAStarNode*, CvAStarNode*, int, const void*, CvAStar*), CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder);CivRegisterBefore<CivHooks::PathCost>(host, &BeforePathCost);CivRegisterAfter<CivHooks::PathCost>(host, &AfterPathCost);CivRegisterReplace<CivHooks::PathCost>(host, &ReplacePathCost);
Code
970/// Standard path finder - compute cost of a path971int PathCost(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder)972{973 CvMap& kMap = GC.getMap();974 int iFromPlotX = parent->m_iX;975 int iFromPlotY = parent->m_iY;976 CvPlot* pFromPlot = kMap.plotUnchecked(iFromPlotX, iFromPlotY);977978 int iToPlotX = node->m_iX;979 int iToPlotY = node->m_iY;980 CvPlot* pToPlot = kMap.plotUnchecked(iToPlotX, iToPlotY);981982 CvUnit* pUnit = ((CvUnit*)pointer);983 const UnitPathCacheData* pCacheData = reinterpret_cast<const UnitPathCacheData*>(finder->GetScratchBuffer());984985 DomainTypes eUnitDomain = pCacheData->getDomainType();986987 CvAssertMsg(eUnitDomain != DOMAIN_AIR, "pUnit->getDomainType() is not expected to be equal with DOMAIN_AIR");988989 bool bToPlotIsWater = pToPlot->isWater() && !pToPlot->IsAllowsWalkWater();990#if defined(MOD_PATHFINDER_TERRAFIRMA)991 bToPlotIsWater = !pToPlot->isTerraFirma(pUnit) && !pToPlot->IsAllowsWalkWater();992#endif993 int iMax;994 if(parent->m_iData1 > 0)995 {996 iMax = parent->m_iData1;997 }998 else999 {1000 if (CvUnitMovement::ConsumesAllMoves(pUnit, pFromPlot, pToPlot) || CvUnitMovement::IsSlowedByZOC(pUnit, pFromPlot, pToPlot))1001 {1002 // The movement would consume all moves, get the moves we will forfeit based on the source plot, rather than1003 // the destination plot. This fixes issues where a land unit that has more movement points on water than on land1004 // would have a very high cost to move onto water if their first move of the turn was at the edge of the water.1005 iMax = pCacheData->baseMoves((pFromPlot->isWater() && !pFromPlot->IsAllowsWalkWater())?DOMAIN_SEA:DOMAIN_LAND) * GC.getMOVE_DENOMINATOR();1006 }1007 else1008 iMax = pCacheData->baseMoves(bToPlotIsWater?DOMAIN_SEA:DOMAIN_LAND) * GC.getMOVE_DENOMINATOR();1009 }10101011 // Get the cost of moving to the new plot, passing in our max moves or the moves we have left, in case the movementCost 1012 // method wants to burn all our remaining moves. This is needed because our remaining moves for this segment of the path1013 // may be larger or smaller than the baseMoves if some moves have already been used or if the starting domain (LAND/SEA)1014 // of the path segment is different from the destination plot.1015 int iCost = CvUnitMovement::MovementCost(pUnit, pFromPlot, pToPlot, pCacheData->baseMoves((pToPlot->isWater() || pCacheData->isEmbarked())?DOMAIN_SEA:pCacheData->getDomainType()), pCacheData->maxMoves(), iMax);10161017 TeamTypes eUnitTeam = pCacheData->getTeam();1018 bool bMaximizeExplore = finder->GetInfo() & MOVE_MAXIMIZE_EXPLORE;10191020 int iMovesLeft = iMax - iCost;1021 // Is the cost greater than our max?1022 if (iMovesLeft < 0)1023 {1024 // Yes, we will still let the move happen, but that is the end of the turn.1025 iCost = iMax;1026 iMovesLeft = 0;1027 }10281029 if(iMovesLeft == 0)1030 {1031 iCost = (PATH_MOVEMENT_WEIGHT * iCost);10321033#if defined(MOD_PATHFINDER_TERRAFIRMA)1034 bool bFromTerraFirma = pFromPlot->isTerraFirma(pUnit);1035 if(eUnitDomain == DOMAIN_LAND && bFromTerraFirma && bToPlotIsWater && !pUnit->canEmbarkOnto(*pFromPlot, *pToPlot, true))1036#else1037 if(eUnitDomain == DOMAIN_LAND && !pFromPlot->isWater() && bToPlotIsWater && !pUnit->canEmbarkOnto(*pFromPlot, *pToPlot, true))1038#endif1039 {1040 iCost += PATH_INCORRECT_EMBARKING_WEIGHT;1041 }10421043 if(bMaximizeExplore)1044 {1045 if(!pToPlot->isHills())1046 {1047 iCost += PATH_EXPLORE_NON_HILL_WEIGHT;1048 }1049 }10501051 // Damage caused by features (mods)1052 if(0 != GC.getPATH_DAMAGE_WEIGHT())1053 {1054 if(pToPlot->getFeatureType() != NO_FEATURE)1055 {1056#if defined(MOD_UNITS_MAX_HP)1057 // We don't have the specific unit, so we'll just use the generic MAX_HIT_POINTS value1058#endif1059#if defined(MOD_API_PLOT_BASED_DAMAGE)1060 if (MOD_API_PLOT_BASED_DAMAGE) {1061 iCost += (GC.getPATH_DAMAGE_WEIGHT() * std::max(0, pToPlot->getTurnDamage(pUnit->ignoreTerrainDamage(), pUnit->ignoreFeatureDamage(), pUnit->extraTerrainDamage(), pUnit->extraFeatureDamage()))) / GC.getMAX_HIT_POINTS();1062 } else {1063#endif1064 iCost += (GC.getPATH_DAMAGE_WEIGHT() * std::max(0, GC.getFeatureInfo(pToPlot->getFeatureType())->getTurnDamage())) / GC.getMAX_HIT_POINTS();1065#if defined(MOD_API_PLOT_BASED_DAMAGE)1066 }1067#endif1068 }10691070 if(pToPlot->getExtraMovePathCost() > 0)1071 {1072 iCost += (PATH_MOVEMENT_WEIGHT * pToPlot->getExtraMovePathCost());1073 }1074 }10751076 // Penalty for stacking1077#if defined(MOD_GLOBAL_STACKING_RULES)1078 if(pToPlot->getUnitLimit() > 0 && !(finder->GetInfo() & MOVE_IGNORE_STACKING))1079#else1080 if(GC.getPLOT_UNIT_LIMIT() > 0 && !(finder->GetInfo() & MOVE_IGNORE_STACKING))1081#endif1082 {1083 // Check to see if any units are present at this full-turn move plot... if the player can see what's there1084#if defined(MOD_GLOBAL_STACKING_RULES)1085 if(pToPlot->getNumFriendlyUnitsOfType(pUnit) >= pToPlot->getUnitLimit())1086#else1087 if(pToPlot->getNumFriendlyUnitsOfType(pUnit) >= GC.getPLOT_UNIT_LIMIT())1088#endif1089 {1090 iCost += PATH_STACKING_WEIGHT;1091 }1092 }10931094 // Penalty for ending a turn on a mountain1095 if(pToPlot->isMountain())1096 {1097 // We want to discourage AIs and automated units from exhausting their movement on a mountain, but if the unit is manually controlled by the human, let them do what they want.1098 if (!pCacheData->isHuman() || pCacheData->IsAutomated())1099 {1100 iCost += PATH_END_TURN_MOUNTAIN_WEIGHT;1101 }1102 }11031104 if (pUnit->isHasPromotion((PromotionTypes)GC.getPROMOTION_UNWELCOME_EVANGELIST()))1105 {1106 // Avoid being in a territory that we are not welcome in, unless the human is manually controlling the unit.1107 if (!pCacheData->isHuman() || pCacheData->IsAutomated())1108 {1109 // Also, ignore the penalty if the destination of the path is in the same team's territory, no sense in avoiding a place we want to get to. 1110 PlayerTypes ePlotOwner = pToPlot->getOwner();1111 CvPlot* pDestPlot = (finder->GetDestX() >= 0 && finder->GetDestY() >= 0)?kMap.plotCheckInvalid(finder->GetDestX(), finder->GetDestY()):NULL;1112 if (!pDestPlot || pDestPlot->getOwner() != ePlotOwner)1113 {1114 TeamTypes ePlotTeam = pToPlot->getTeam();1115 if (ePlotOwner != NO_PLAYER && !GET_PLAYER(ePlotOwner).isMinorCiv() && ePlotTeam != pCacheData->getTeam() && !GET_TEAM(ePlotTeam).IsAllowsOpenBordersToTeam(pCacheData->getTeam()))1116 {1117 iCost += PATH_END_TURN_MISSIONARY_OTHER_TERRITORY;1118 }1119 }1120 }1121 }1122 else1123 {1124 if(pToPlot->getTeam() != eUnitTeam)1125 {1126 iCost += PATH_TERRITORY_WEIGHT;1127 }1128 }11291130#if PATH_CITY_AVOID_WEIGHT != 01131 if(pToPlot->getPlotCity() && !(pToPlot->getX() == finder->GetDestX() && pToPlot->getY() == finder->GetDestY()))1132 {1133 iCost += PATH_CITY_AVOID_WEIGHT; // slewis - this should be zeroed out currently1134 }1135#endif1136 }1137 else1138 {1139 iCost = (PATH_MOVEMENT_WEIGHT * iCost);1140 }11411142 if(bMaximizeExplore)1143 {1144 int iUnseenPlots = pToPlot->getNumAdjacentNonrevealed(eUnitTeam);1145 if(!pToPlot->isRevealed(eUnitTeam))1146 {1147 iUnseenPlots += 1;1148 }11491150 iCost += (7 - iUnseenPlots) * PATH_EXPLORE_NON_REVEAL_WEIGHT;1151 }11521153 // If we are a land unit and we are moving through the water, make the cost a little higher so that1154 // we favor staying on land or getting back to land as quickly as possible because it is dangerous to1155 // be on the water. Don't add this penalty if the unit is human controlled however, we will assume they want1156 // the best path, rather than the safest.1157 if(eUnitDomain == DOMAIN_LAND && bToPlotIsWater && (!pCacheData->isHuman() || pCacheData->IsAutomated()))1158 {1159 iCost += PATH_THROUGH_WATER;1160 }11611162 if(pUnit->IsCombatUnit())1163 {1164 if(iMovesLeft == 0)1165 {1166 iCost += (PATH_DEFENSE_WEIGHT * std::max(0, (200 - ((pUnit->noDefensiveBonus()) ? 0 : pToPlot->defenseModifier(eUnitTeam, false)))));1167 }11681169 if(pCacheData->IsAutomated())1170 {1171 if(pCacheData->IsCanAttack())1172 {1173 if(finder->IsPathDest(iToPlotX, iToPlotY))1174 {1175 if(pToPlot->isVisibleEnemyDefender(pUnit))1176 {1177 iCost += (PATH_DEFENSE_WEIGHT * std::max(0, (200 - ((pUnit->noDefensiveBonus()) ? 0 : pFromPlot->defenseModifier(eUnitTeam, false)))));11781179 // I guess we may as well be the garrison1180#if PATH_CITY_WEIGHT != 01181 if(!(pFromPlot->isCity()))1182 {1183 iCost += PATH_CITY_WEIGHT;1184 }1185#endif11861187 if(pFromPlot->isRiverCrossing(directionXY(iFromPlotX, iFromPlotY, iToPlotX, iToPlotY)))1188 {1189 if(!(pUnit->isRiverCrossingNoPenalty()))1190 {1191 iCost += (PATH_RIVER_WEIGHT * -(GC.getRIVER_ATTACK_MODIFIER()));1192 iCost += (PATH_MOVEMENT_WEIGHT * iMovesLeft);1193 }1194 }1195 }1196 }1197 }1198 }1199 }12001201 FAssert(iCost != MAX_INT);12021203 iCost += PATH_STEP_WEIGHT;12041205 FAssert(iCost > 0);12061207 return iCost;1208}