Bazaar developer reference

PathCost(CvAStarNode *, CvAStarNode *, int, const void *, CvAStar *)

Defined in
CvAStar.cpp, line 971
Hook name
CivHooks::PathCost

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}

Calls

Called by