Bazaar developer reference

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

Defined in
CvAStar.cpp, line 1212
Hook name
CivHooks::PathValid

Hooks

static bool BeforePathValid(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder, int& result);static void AfterPathValid(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder, int& result);static int ReplacePathValid(int (*original)(CvAStarNode*, CvAStarNode*, int, const void*, CvAStar*), CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder);CivRegisterBefore<CivHooks::PathValid>(host, &BeforePathValid);CivRegisterAfter<CivHooks::PathValid>(host, &AfterPathValid);CivRegisterReplace<CivHooks::PathValid>(host, &ReplacePathValid);

Code

1211/// Standard path finder - check validity of a coordinate1212int PathValid(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder)1213{1214	CvMap& theMap = GC.getMap();12151216	CvPlot* pToPlot = theMap.plotUnchecked(node->m_iX, node->m_iY);1217	PREFETCH_FASTAR_CVPLOT(reinterpret_cast<char*>(pToPlot));12181219	CvUnit* pUnit = ((CvUnit*)pointer);1220	const UnitPathCacheData* pCacheData = reinterpret_cast<const UnitPathCacheData*>(finder->GetScratchBuffer());1221	TeamTypes eUnitTeam = pCacheData->getTeam();1222	PlayerTypes unit_owner = pCacheData->getOwner();12231224	CvAssertMsg(eUnitTeam != NO_TEAM, "The unit's team should be a vaild value");1225	if (eUnitTeam == NO_TEAM)1226	{1227		eUnitTeam = GC.getGame().getActiveTeam();1228	}12291230	CvTeam& kUnitTeam = GET_TEAM(eUnitTeam);12311232	// Cache values for this node that we will use in the loop1233	CvPathNodeCacheData& kToNodeCacheData = node->m_kCostCacheData;1234	kToNodeCacheData.bPlotVisibleToTeam = pToPlot->isVisible(eUnitTeam);1235	kToNodeCacheData.iNumFriendlyUnitsOfType = pToPlot->getNumFriendlyUnitsOfType(pUnit);1236#if defined(MOD_GLOBAL_STACKING_RULES)1237	kToNodeCacheData.iUnitPlotLimit = pToPlot->getUnitLimit();1238#endif1239	kToNodeCacheData.bIsMountain = pToPlot->isMountain();1240	kToNodeCacheData.bIsWater = (pToPlot->isWater() && !pToPlot->IsAllowsWalkWater());1241#if defined(MOD_PATHFINDER_TERRAFIRMA)1242	kToNodeCacheData.bIsTerraFirma = pToPlot->isTerraFirma(pUnit) && !pToPlot->IsAllowsWalkWater();1243#endif1244	kToNodeCacheData.bCanEnterTerrain = pUnit->canEnterTerrain(*pToPlot, CvUnit::MOVEFLAG_PRETEND_CORRECT_EMBARK_STATE);1245	kToNodeCacheData.bIsRevealedToTeam = pToPlot->isRevealed(eUnitTeam);1246	kToNodeCacheData.bContainsOtherFriendlyTeamCity = false;1247	CvCity* pCity = pToPlot->getPlotCity();1248	if(pCity)1249	{1250		if(unit_owner != pCity->getOwner() && !kUnitTeam.isAtWar(pCity->getTeam()))1251			kToNodeCacheData.bContainsOtherFriendlyTeamCity = true;1252	}1253	kToNodeCacheData.bContainsEnemyCity = pToPlot->isEnemyCity(*pUnit);1254	kToNodeCacheData.bContainsVisibleEnemy = pToPlot->isVisibleEnemyUnit(pUnit);1255	kToNodeCacheData.bContainsVisibleEnemyDefender = pToPlot->getBestDefender(NO_PLAYER, unit_owner, pUnit).pointer() != NULL;12561257	// If this is the first node in the path, it is always valid (starting location)1258	if (parent == NULL)1259	{1260		return TRUE;1261	}12621263	CvPlot* pFromPlot = theMap.plotUnchecked(parent->m_iX, parent->m_iY);1264	PREFETCH_FASTAR_CVPLOT(reinterpret_cast<char*>(pFromPlot));12651266	// pulling invariants out of the loop1267	bool bAIControl = pCacheData->IsAutomated();1268	int iUnitX = pUnit->getX();1269	int iUnitY = pUnit->getY();1270	DomainTypes unit_domain_type = pCacheData->getDomainType();1271	bool bUnitIsCombat           = pUnit->IsCombatUnit();1272	bool bIsHuman				 = pCacheData->isHuman();1273	int iFinderInfo              = finder->GetInfo();1274	CvPlot* pUnitPlot            = pUnit->plot();1275	int iFinderIgnoreStacking    = iFinderInfo & MOVE_IGNORE_STACKING;1276#if !defined(MOD_GLOBAL_STACKING_RULES)1277	int iUnitPlotLimit           = GC.getPLOT_UNIT_LIMIT();1278#endif1279	bool bFromPlotOwned          = pFromPlot->isOwned();1280	TeamTypes eFromPlotTeam      = pFromPlot->getTeam();12811282	// We have determined that this node is not the origin above (parent == NULL)1283	CvAStarNode* pNode = node;1284	bool bPreviousNodeHostile = false;1285	bool bPreviousVisibleToTeam = kToNodeCacheData.bPlotVisibleToTeam;1286	int iDestX = finder->GetDestX();1287	int iDestY = finder->GetDestY();1288	int iNodeX = node->m_iX;1289	int iNodeY = node->m_iY;1290	int iOldNumTurns = -1;1291	int iNumTurns;12921293	// First run special case for checking "node" since it doesn't have a parent set yet1294	bool bFirstRun = true;12951296	// Have to calculate this specially because the node passed into this function doesn't yet have data stored it in (hasn't reached pathAdd yet)1297	int iStartMoves = parent->m_iData1;1298	iNumTurns = parent->m_iData2;12991300	if(iStartMoves == 0)1301	{1302		iNumTurns++;1303	}13041305	iOldNumTurns = -1;13061307	CvPlot* pPlot = NULL;13081309	// Get a reference to the parent node cache data1310	CvPathNodeCacheData& kFromNodeCacheData = parent->m_kCostCacheData;13111312	// Loop through the current path until we find the path origin.1313	// This validates the path with the inclusion of the new path node.  We must do this because of the rules of where a unit can finish a turn.1314	// Please note that this can be an expensive loop as the path gets longer and longer, do as little work as possible in validating each node.  1315	// If there is an invariant value that needs to be fetched from the plot or unit for the node, please do the calculation and put it in the node's data cache.1316	while(pNode != NULL)1317	{1318		PREFETCH_FASTAR_NODE(pNode->m_pParent);13191320		CvPathNodeCacheData& kNodeCacheData = pNode->m_kCostCacheData;1321		// This is a safeguard against the algorithm believing a plot to be impassable before actually knowing it (mid-search)1322		if(iOldNumTurns != -1 || (iDestX == iNodeX && iDestY == iNodeY))1323		{1324			// This plot is of greater distance than previously, so we know the unit is ending its turn here (pNode), or it's trying to attack through a unit (and might end up on this tile if an attack fails to kill the enemy)1325			if(iNumTurns != iOldNumTurns || bPreviousNodeHostile || !bPreviousVisibleToTeam)1326			{1327				// Don't count origin, or else a unit will block its own movement!1328				if(iNodeX != iUnitX || iNodeY != iUnitY)1329				{1330					if(kNodeCacheData.bPlotVisibleToTeam)1331					{1332						// Check to see if any units are present at this full-turn move plot... if the player can see what's there1333#if defined(MOD_GLOBAL_STACKING_RULES)1334						if(kNodeCacheData.iNumFriendlyUnitsOfType >= kNodeCacheData.iUnitPlotLimit && !(iFinderIgnoreStacking))1335#else1336						if(kNodeCacheData.iNumFriendlyUnitsOfType >= iUnitPlotLimit && !(iFinderIgnoreStacking))1337#endif1338						{1339							return FALSE;1340						}13411342						if (kNodeCacheData.bIsMountain && !(iFinderIgnoreStacking) && (!bIsHuman || bAIControl))1343						{1344							return FALSE;1345						}13461347						if(kNodeCacheData.bIsMountain && !kNodeCacheData.bCanEnterTerrain)1348						{1349							return FALSE;1350						}13511352						if ((iFinderInfo & CvUnit::MOVEFLAG_STAY_ON_LAND) && kNodeCacheData.bIsWater)1353						{1354							return FALSE;1355						}1356					}13571358#if defined(MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS)1359					if(kNodeCacheData.bIsRevealedToTeam && (!MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS || bUnitIsCombat))1360#else1361					if(kNodeCacheData.bIsRevealedToTeam)1362#endif1363					{1364						if (kNodeCacheData.bContainsOtherFriendlyTeamCity && !(iFinderIgnoreStacking))1365							return FALSE;1366					}1367				}1368			}1369		}13701371		bPreviousNodeHostile = false;1372		if(kNodeCacheData.bContainsEnemyCity)1373		{1374			bPreviousNodeHostile = true;1375		}1376		// Prevents units from passing through one another on its way to attack another unit1377		else if(kNodeCacheData.bContainsVisibleEnemy)1378		{1379			// except when attacking an unguarded civilian unit1380			if(kNodeCacheData.bContainsVisibleEnemyDefender)1381			{1382				bPreviousNodeHostile = true;1383			}1384		}13851386		bPreviousVisibleToTeam = kNodeCacheData.bPlotVisibleToTeam;1387		// JON - Special case for the original node passed into this function because it's not yet linked to any parent1388		if(pNode == node && bFirstRun)1389		{1390			pNode = parent;1391			bFirstRun = false;1392		}1393		else1394		{1395			pNode = pNode->m_pParent;1396		}13971398		if(pNode != NULL)1399		{1400			iNodeX = pNode->m_iX;1401			iNodeY = pNode->m_iY;1402			iOldNumTurns = iNumTurns;1403			iNumTurns = pNode->m_iData2;1404		}1405	}14061407	// slewis - moved this up so units can't move directly into the water. Not 100% sure this is the right solution.1408	if(unit_domain_type == DOMAIN_LAND)1409	{1410#if defined(MOD_PATHFINDER_TERRAFIRMA)1411		bool bFromTerraFirma = kFromNodeCacheData.bIsTerraFirma;1412		bool bToWater = !kToNodeCacheData.bIsTerraFirma;1413		if( bFromTerraFirma && bToWater && kToNodeCacheData.bIsRevealedToTeam && !pUnit->canEmbarkOnto(*pFromPlot, *pToPlot, true))1414#else1415		if(!kFromNodeCacheData.bIsWater && kToNodeCacheData.bIsWater && kToNodeCacheData.bIsRevealedToTeam && !pUnit->canEmbarkOnto(*pFromPlot, *pToPlot, true))1416#endif1417		{1418#if defined(MOD_PATHFINDER_DEEP_WATER_EMBARKATION)1419			if(!pUnit->canMoveAllTerrain() && !pToPlot->IsAllowsWalkWater())1420#else1421			if(!pUnit->IsHoveringUnit() && !pUnit->canMoveAllTerrain() && !pToPlot->IsAllowsWalkWater())1422#endif1423			{1424				return FALSE;1425			}1426		}1427	}14281429#if defined(MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS)1430	if (!MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS) {1431#endif1432	if(!bUnitIsCombat && unit_domain_type != DOMAIN_AIR)1433	{1434		const PlayerTypes eUnitPlayer = unit_owner;1435		const int iUnitCount = pToPlot->getNumUnits();1436		for(int iUnit = 0; iUnit < iUnitCount; ++iUnit)1437		{1438			const CvUnit* pToPlotUnit = pToPlot->getUnitByIndex(iUnit);1439			if(pToPlotUnit != NULL && pToPlotUnit->getOwner() != eUnitPlayer)1440			{1441#if defined(MOD_BUGFIX_RADARING)1442				if (MOD_BUGFIX_RADARING) {1443					// Only if the player can see what's in the plot1444					if (pToPlot->isVisible(eUnitTeam)) {1445						return FALSE;1446					}1447				}1448				else1449#endif1450				return FALSE; // Plot occupied by another player1451			}1452		}1453	}1454#if defined(MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS)1455	}1456#endif14571458	// slewis - Added to catch when the unit is adjacent to an enemy unit while it is stacked with a friendly unit.1459	//          The logic above (with bPreviousNodeHostile) catches this problem with a path that's longer than one step1460	//          but does not catch when the path is only one step.1461	if(bUnitIsCombat && unit_domain_type != DOMAIN_AIR && pUnitPlot->isAdjacent(pToPlot) && kToNodeCacheData.bContainsVisibleEnemy && !(iFinderIgnoreStacking))1462	{1463		if(kToNodeCacheData.bContainsVisibleEnemyDefender)1464		{1465#if defined(MOD_GLOBAL_STACKING_RULES)1466			if(pUnitPlot->getNumFriendlyUnitsOfType(pUnit) > pUnitPlot->getUnitLimit())1467#else1468			if(pUnitPlot->getNumFriendlyUnitsOfType(pUnit) > iUnitPlotLimit)1469#endif1470			{1471				return FALSE;1472			}1473		}1474	}14751476	if(pUnitPlot == pFromPlot)1477	{1478		return TRUE;1479	}14801481	if(iFinderInfo & MOVE_TERRITORY_NO_UNEXPLORED)1482	{1483		if(!(kFromNodeCacheData.bIsRevealedToTeam))1484		{1485			return FALSE;1486		}14871488		if(bFromPlotOwned)1489		{1490			if(eFromPlotTeam != eUnitTeam)1491			{1492				return FALSE;1493			}1494		}1495	}14961497	if(iFinderInfo & MOVE_TERRITORY_NO_ENEMY)1498	{1499		if(bFromPlotOwned)1500		{1501			if(atWar(eFromPlotTeam, eUnitTeam))1502			{1503				return FALSE;1504			}1505		}1506	}15071508	if(bAIControl)1509	{1510		if((parent->m_iData2 > 1) || (parent->m_iData1 == 0))1511		{1512			if(!(iFinderInfo & MOVE_UNITS_IGNORE_DANGER))1513			{1514				if(!bUnitIsCombat || pUnit->getArmyID() == FFreeList::INVALID_INDEX)1515				{1516					if(GET_PLAYER(unit_owner).GetPlotDanger(*pFromPlot) > 0)1517					{1518						return FALSE;1519					}1520				}1521			}1522		}1523	}15241525	// slewis - added AI check and embark check to prevent units from moving into unexplored areas1526	if(bAIControl || kFromNodeCacheData.bIsRevealedToTeam || pCacheData->isEmbarked() || !bIsHuman)1527	{1528		if(iFinderInfo & MOVE_UNITS_THROUGH_ENEMY)1529		{1530			if(!(pUnit->canMoveOrAttackInto(*pFromPlot, CvUnit::MOVEFLAG_PRETEND_CORRECT_EMBARK_STATE)))1531			{1532				return FALSE;1533			}1534		}1535		else1536		{1537			if(!(pUnit->canMoveThrough(*pFromPlot, CvUnit::MOVEFLAG_PRETEND_CORRECT_EMBARK_STATE)))1538			{1539				return FALSE;1540			}1541		}1542	}15431544	return TRUE;1545}

Calls

Called by