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}