Bazaar developer reference

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

Defined in
CvAStar.cpp, line 3395
Hook name
CivHooks::TacticalAnalysisMapPathValid

Hooks

static bool BeforeTacticalAnalysisMapPathValid(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder, int& result);static void AfterTacticalAnalysisMapPathValid(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder, int& result);static int ReplaceTacticalAnalysisMapPathValid(int (*original)(CvAStarNode*, CvAStarNode*, int, const void*, CvAStar*), CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder);CivRegisterBefore<CivHooks::TacticalAnalysisMapPathValid>(host, &BeforeTacticalAnalysisMapPathValid);CivRegisterAfter<CivHooks::TacticalAnalysisMapPathValid>(host, &AfterTacticalAnalysisMapPathValid);CivRegisterReplace<CivHooks::TacticalAnalysisMapPathValid>(host, &ReplaceTacticalAnalysisMapPathValid);

Code

3395int TacticalAnalysisMapPathValid(CvAStarNode* parent, CvAStarNode* node, int data, const void* pointer, CvAStar* finder)3396{3397	CvMap& theMap = GC.getMap();33983399	CvPlot* pToPlot = theMap.plotUnchecked(node->m_iX, node->m_iY);3400	PREFETCH_FASTAR_CVPLOT(reinterpret_cast<char*>(pToPlot));34013402	CvUnit* pUnit = ((CvUnit *)pointer);3403	const UnitPathCacheData* pCacheData = reinterpret_cast<const UnitPathCacheData*>(finder->GetScratchBuffer());34043405	TeamTypes eUnitTeam = pCacheData->getTeam();3406	PlayerTypes unit_owner = pCacheData->getOwner();3407	CvAssertMsg(eUnitTeam != NO_TEAM, "The unit's team should be a vaild value");3408	if (eUnitTeam == NO_TEAM)3409	{3410		eUnitTeam = GC.getGame().getActiveTeam();3411	}34123413	CvTeam& kUnitTeam = GET_TEAM(eUnitTeam);34143415	CvTacticalAnalysisMap* pTAMap = GC.getGame().GetTacticalAnalysisMap();3416	FAssert(pTAMap != NULL);3417	CvTacticalAnalysisCell* pToPlotCell = pTAMap->GetCell(pToPlot->GetPlotIndex());3418	FAssert(pToPlotCell != NULL);34193420	// Cache the data for the node3421	CvPathNodeCacheData& kToNodeCacheData = node->m_kCostCacheData;3422	kToNodeCacheData.bPlotVisibleToTeam = pToPlotCell->IsVisible();3423	kToNodeCacheData.iNumFriendlyUnitsOfType = pToPlot->getNumFriendlyUnitsOfType(pUnit);3424#if defined(MOD_GLOBAL_STACKING_RULES)3425	kToNodeCacheData.iUnitPlotLimit = pToPlot->getUnitLimit();3426#endif3427	kToNodeCacheData.bIsMountain = pToPlot->isMountain();3428	kToNodeCacheData.bIsWater = pToPlotCell->IsWater();3429#if defined(MOD_PATHFINDER_TERRAFIRMA)3430	kToNodeCacheData.bIsTerraFirma = pToPlotCell->IsTerraFirma();3431#endif3432	kToNodeCacheData.bCanEnterTerrain = pUnit->canEnterTerrain(*pToPlot, CvUnit::MOVEFLAG_PRETEND_CORRECT_EMBARK_STATE);3433	kToNodeCacheData.bIsRevealedToTeam = pToPlotCell->IsRevealed();3434	kToNodeCacheData.bContainsOtherFriendlyTeamCity = false;3435	if(pToPlotCell->IsCity())3436	{3437		CvCity* pCity = pToPlot->getPlotCity();3438		if(pCity)3439		{3440			if(unit_owner != pCity->getOwner() && !kUnitTeam.isAtWar(pCity->getTeam()))3441				kToNodeCacheData.bContainsOtherFriendlyTeamCity = true;3442		}3443	}3444	kToNodeCacheData.bContainsEnemyCity = pToPlotCell->IsEnemyCity();3445	kToNodeCacheData.bContainsVisibleEnemy = pToPlotCell->GetEnemyMilitaryUnit() != NULL;3446	kToNodeCacheData.bContainsVisibleEnemyDefender = pToPlot->getBestDefender(NO_PLAYER, unit_owner, pUnit).pointer() != NULL;34473448	// If this is the first node in the path, it is always valid (starting location)3449	if (parent == NULL)3450	{3451		return TRUE;3452	}34533454	CvPlot* pFromPlot = theMap.plotUnchecked(parent->m_iX, parent->m_iY);3455	PREFETCH_FASTAR_CVPLOT(reinterpret_cast<char*>(pFromPlot));34563457	CvTacticalAnalysisCell* pFromPlotCell = pTAMap->GetCell(pFromPlot->GetPlotIndex());3458	FAssert(pFromPlotCell != NULL);34593460	bool bAIControl = pUnit->IsAutomated();34613462	// pulling invariants out of the loop3463	int iUnitX = pUnit->getX();3464	int iUnitY = pUnit->getY();3465	DomainTypes unit_domain_type = pCacheData->getDomainType();3466	bool bUnitIsCombat           = pUnit->IsCombatUnit();3467	bool bIsHuman				 = pCacheData->isHuman();3468	int iFinderInfo              = finder->GetInfo();3469	CvPlot* pUnitPlot            = pUnit->plot();3470	int iFinderIgnoreStacking    = iFinderInfo & MOVE_IGNORE_STACKING;3471#if !defined(MOD_GLOBAL_STACKING_RULES)3472	int iUnitPlotLimit           = GC.getPLOT_UNIT_LIMIT();3473#endif3474	bool bFromPlotOwned          = !pFromPlotCell->IsUnclaimedTerritory();3475	TeamTypes eFromPlotTeam      = pFromPlot->getTeam();34763477	// We have determined that this node is not the origin above (parent == NULL)3478	CvAStarNode* pNode = node;3479	bool bPreviousNodeHostile = false;3480	int iDestX = finder->GetDestX();3481	int iDestY = finder->GetDestY();3482	int iNodeX = node->m_iX;3483	int iNodeY = node->m_iY;3484	int iOldNumTurns = -1;3485	int iNumTurns;3486	TeamTypes eTeam = eUnitTeam; // this may get modified later is eTEam == NO_TEAM34873488	// First run special case for checking "node" since it doesn't have a parent set yet3489	bool bFirstRun = true;34903491	// 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)3492	int iStartMoves = parent->m_iData1;3493	iNumTurns = parent->m_iData2;34943495	if(iStartMoves == 0)3496	{3497		iNumTurns++;3498	}34993500	iOldNumTurns = -1;35013502	CvPlot* pPlot = NULL;35033504	// Get a reference to the parent node cache data3505	CvPathNodeCacheData& kFromNodeCacheData = parent->m_kCostCacheData;35063507	// Loop through the current path until we find the path origin.3508	// 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.3509	// 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.  3510	// 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.3511	while(pNode != NULL)3512	{3513		PREFETCH_FASTAR_NODE(pNode->m_pParent);35143515		CvPathNodeCacheData& kNodeCacheData = pNode->m_kCostCacheData;3516		// This is a safeguard against the algorithm believing a plot to be impassable before actually knowing it (mid-search)3517		if(iOldNumTurns != -1 || (iDestX == iNodeX && iDestY == iNodeY))3518		{3519			// 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)3520			if(iNumTurns != iOldNumTurns || bPreviousNodeHostile)3521			{3522				// Don't count origin, or else a unit will block its own movement!3523				if(iNodeX != iUnitX || iNodeY != iUnitY)3524				{3525					// PREFETCH_FASTAR_CVPLOT(reinterpret_cast<char*>(pPlot));3526					if(kNodeCacheData.bPlotVisibleToTeam)3527					{3528						// Check to see if any units are present at this full-turn move plot... if the player can see what's there3529#if defined(MOD_GLOBAL_STACKING_RULES)3530						if(kNodeCacheData.iNumFriendlyUnitsOfType >= kNodeCacheData.iUnitPlotLimit && !(iFinderIgnoreStacking))3531#else3532						if(kNodeCacheData.iNumFriendlyUnitsOfType >= iUnitPlotLimit && !(iFinderIgnoreStacking))3533#endif3534						{3535							return FALSE;3536						}35373538						if (kNodeCacheData.bIsMountain && !(iFinderIgnoreStacking) && (!bIsHuman || bAIControl))3539						{3540							return FALSE;3541						}35423543						if(kNodeCacheData.bIsMountain && !kNodeCacheData.bCanEnterTerrain)	// only doing canEnterTerrain on mountain plots because it is expensive, though it probably should always be called and some other checks in this loop could be removed.3544						{3545							return FALSE;3546						}35473548						if ((finder->GetInfo() & CvUnit::MOVEFLAG_STAY_ON_LAND) && kNodeCacheData.bIsWater)3549						{3550							return FALSE;3551						}3552					}35533554#if defined(MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS)3555					if(kNodeCacheData.bIsRevealedToTeam && (!MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS || bUnitIsCombat))3556#else3557					if(kNodeCacheData.bIsRevealedToTeam)3558#endif3559					{3560						if (kNodeCacheData.bContainsOtherFriendlyTeamCity && !(iFinderIgnoreStacking))3561							return FALSE;3562					}3563				}3564			}3565		}35663567		bPreviousNodeHostile = false;3568		if(kNodeCacheData.bContainsEnemyCity)3569		{3570			bPreviousNodeHostile = true;3571		}3572		// Prevents units from passing through one another on its way to attack another unit3573		else if(kNodeCacheData.bContainsVisibleEnemy)3574		{3575			// except when attacking an unguarded civilian unit3576			if(kNodeCacheData.bContainsVisibleEnemyDefender)3577			{3578				bPreviousNodeHostile = true;3579			}3580		}35813582		// JON - Special case for the original node passed into this function because it's not yet linked to any parent3583		if(pNode == node && bFirstRun)3584		{3585			pNode = parent;3586			bFirstRun = false;3587		}3588		else3589		{3590			pNode = pNode->m_pParent;3591		}35923593		if(pNode != NULL)3594		{35953596			iNodeX = pNode->m_iX;3597			iNodeY = pNode->m_iY;3598			iOldNumTurns = iNumTurns;3599			iNumTurns = pNode->m_iData2;3600		}3601	}36023603	// slewis - moved this up so units can't move directly into the water. Not 100% sure this is the right solution.3604	if(unit_domain_type == DOMAIN_LAND)3605	{3606#if defined(MOD_PATHFINDER_TERRAFIRMA)3607		bool bFromTerraFirma = kFromNodeCacheData.bIsTerraFirma;3608		bool bToWater = !kToNodeCacheData.bIsTerraFirma;3609		if(bFromTerraFirma && bToWater && kToNodeCacheData.bIsRevealedToTeam && !pUnit->canEmbarkOnto(*pFromPlot, *pToPlot, true))3610#else3611		if(!kFromNodeCacheData.bIsWater && kToNodeCacheData.bIsWater && kToNodeCacheData.bIsRevealedToTeam && !pUnit->canEmbarkOnto(*pFromPlot, *pToPlot, true))3612#endif3613		{3614#if defined(MOD_PATHFINDER_DEEP_WATER_EMBARKATION)3615			if(!pUnit->canMoveAllTerrain() && !pToPlot->IsAllowsWalkWater())3616#else3617			if(!pUnit->IsHoveringUnit() && !pUnit->canMoveAllTerrain() && !pToPlot->IsAllowsWalkWater())3618#endif3619			{3620				return FALSE;3621			}3622		}3623	}36243625#if defined(MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS)3626	if (!MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS) {3627#endif3628	if(!bUnitIsCombat && unit_domain_type != DOMAIN_AIR)3629	{3630		const PlayerTypes eUnitPlayer = unit_owner;3631		const int iUnitCount = pToPlot->getNumUnits();3632		for(int iUnit = 0; iUnit < iUnitCount; ++iUnit)3633		{3634			const CvUnit* pToPlotUnit = pToPlot->getUnitByIndex(iUnit);3635			if(pToPlotUnit != NULL && pToPlotUnit->getOwner() != eUnitPlayer)3636			{3637				return FALSE; // Plot occupied by another player3638			}3639		}3640	}3641#if defined(MOD_GLOBAL_BREAK_CIVILIAN_RESTRICTIONS)3642	}3643#endif36443645	// slewis - Added to catch when the unit is adjacent to an enemy unit while it is stacked with a friendly unit.3646	//          The logic above (with bPreviousNodeHostile) catches this problem with a path that's longer than one step3647	//          but does not catch when the path is only one step.3648	if(bUnitIsCombat && unit_domain_type != DOMAIN_AIR && pUnitPlot->isAdjacent(pToPlot) && kToNodeCacheData.bContainsVisibleEnemy && !(iFinderIgnoreStacking))3649	{3650		if(kToNodeCacheData.bContainsVisibleEnemyDefender)3651		{3652#if defined(MOD_GLOBAL_STACKING_RULES)3653			if(pUnitPlot->getNumFriendlyUnitsOfType(pUnit) > pUnitPlot->getUnitLimit())3654#else3655			if(pUnitPlot->getNumFriendlyUnitsOfType(pUnit) > iUnitPlotLimit)3656#endif3657			{3658				return FALSE;3659			}3660		}3661	}36623663	if(pUnitPlot == pFromPlot)3664	{3665		return TRUE;3666	}36673668	if(iFinderInfo & MOVE_TERRITORY_NO_UNEXPLORED)3669	{3670		if(!kFromNodeCacheData.bIsRevealedToTeam)3671		{3672			return FALSE;3673		}36743675		if(bFromPlotOwned)3676		{3677			if(eFromPlotTeam != eUnitTeam)3678			{3679				return FALSE;3680			}3681		}3682	}36833684	if(iFinderInfo & MOVE_TERRITORY_NO_ENEMY)3685	{3686		if(bFromPlotOwned)3687		{3688			if(atWar(eFromPlotTeam, eUnitTeam))3689			{3690				return FALSE;3691			}3692		}3693	}36943695	if(bAIControl)3696	{3697		if((parent->m_iData2 > 1) || (parent->m_iData1 == 0))3698		{3699			if(!(iFinderInfo & MOVE_UNITS_IGNORE_DANGER))3700			{3701				if(!bUnitIsCombat || pUnit->getArmyID() == FFreeList::INVALID_INDEX)3702				{3703					if(GET_PLAYER(unit_owner).GetPlotDanger(*pFromPlot) > 0)3704					{3705						return FALSE;3706					}3707				}3708			}3709		}3710	}37113712	// slewis - added AI check and embark check to prevent units from moving into unexplored areas3713	if(bAIControl || !bIsHuman || kFromNodeCacheData.bIsRevealedToTeam || pCacheData->isEmbarked())3714	{3715		if(iFinderInfo & MOVE_UNITS_THROUGH_ENEMY)3716		{3717			if(!(pUnit->canMoveOrAttackInto(*pFromPlot, CvUnit::MOVEFLAG_PRETEND_CORRECT_EMBARK_STATE)))3718			{3719				return FALSE;3720			}3721		}3722		else3723		{3724			if(!(pUnit->canMoveThrough(*pFromPlot, CvUnit::MOVEFLAG_PRETEND_CORRECT_EMBARK_STATE)))3725			{3726				return FALSE;3727			}3728		}3729	}37303731	return TRUE;3732}

Calls

Called by