/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * AStar.c * * A* based findpath * */ // A* printf's //#define DEBUG_GROUP0 // open list printf's //#define DEBUG_GROUP1 // summary info printf's //#define DEBUG_GROUP2 #include #include "lib/framework/frame.h" #include "objects.h" #include "map.h" #include "raycast.h" #include "fpath.h" #include "astar.h" // open list storage methods : // binary tree - 0 // ordered list - 1 // unordered list - 2 #define OPEN_LIST 2 SDWORD firstHit, secondHit; SDWORD astarInner,astarOuter, astarRemove; // The structure to store a node of the route typedef struct _fp_node { SWORD x,y; // map coords SWORD dist, est; // distance so far and estimate to end SWORD type; // open or closed node #if OPEN_LIST == 0 struct _fp_node *psLeft, *psRight; // Open list pointer #else struct _fp_node *psOpen; #endif struct _fp_node *psRoute; // Previous point in the route struct _fp_node *psNext; } FP_NODE; // types of node #define NT_OPEN 1 #define NT_CLOSED 2 // List of open nodes FP_NODE *psOpen; // Size of closed hash table //#define FPATH_TABLESIZE 20671 #define FPATH_TABLESIZE 4091 #if OPEN_LIST == 2 // Hash table for closed nodes FP_NODE **apsNodes=NULL; #else // Hash table for closed nodes FP_NODE **apsClosed; // Hash table for open nodes FP_NODE **apsOpen; #endif // object heap to store nodes OBJ_HEAP *psFPNodeHeap; /*#define NUM_DIR 4 // Convert a direction into an offset // dir 0 => x = 0, y = -1 static POINT aDirOffset[NUM_DIR] = { 0, 1, -1, 0, 0,-1, 1, 0, };*/ #define NUM_DIR 8 // Convert a direction into an offset // dir 0 => x = 0, y = -1 static POINT aDirOffset[NUM_DIR] = { { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, }; // reset the astar counters void astarResetCounters(void) { astarInner = 0; astarOuter = 0; astarRemove = 0; } void ClearAstarNodes(void) { assert(apsNodes); memset(apsNodes, 0, sizeof(FP_NODE *) * FPATH_TABLESIZE); } // Initialise the findpath routine BOOL astarInitialise(void) { // Create the node heap if (!HEAP_CREATE(&psFPNodeHeap, sizeof(FP_NODE), FPATH_NODEINIT, FPATH_NODEEXT)) { return FALSE; } #if OPEN_LIST == 2 apsNodes = MALLOC(sizeof(FP_NODE *) * FPATH_TABLESIZE); if (!apsNodes) { return FALSE; } ClearAstarNodes(); #else // Create the two hash tables apsOpen = MALLOC(sizeof(FP_NODE *) * FPATH_TABLESIZE); if (!apsOpen) { return FALSE; } memset(apsOpen, 0, sizeof(FP_NODE *) * FPATH_TABLESIZE); apsClosed = MALLOC(sizeof(FP_NODE *) * FPATH_TABLESIZE); if (!apsClosed) { return FALSE; } memset(apsClosed, 0, sizeof(FP_NODE *) * FPATH_TABLESIZE); #endif return TRUE; } // Shutdown the findpath routine void fpathShutDown(void) { HEAP_DESTROY(psFPNodeHeap); #if OPEN_LIST == 2 FREE(apsNodes); #else FREE(apsOpen); FREE(apsClosed); #endif } // calculate a hash table index #define RAND_MULTI 25173 #define RAND_INC 13849 #define RAND_MOD 0xffff /*SDWORD fpathHashFunc(SDWORD x, SDWORD y) { UDWORD index; index = ((UDWORD)x * RAND_MULTI + RAND_INC) & RAND_MOD; index = index ^ ((UDWORD)y * RAND_MULTI + RAND_INC) & RAND_MOD; index = index % FPATH_TABLESIZE; return index; }*/ /* next four used in HashPJW */ #define BITS_IN_int 32 #define THREE_QUARTERS ((UINT) ((BITS_IN_int * 3) / 4)) #define ONE_EIGHTH ((UINT) (BITS_IN_int / 8)) #define HIGH_BITS ( ~((UINT)(~0) >> ONE_EIGHTH )) /***************************************************************************/ /* * HashString * * Adaptation of Peter Weinberger's (PJW) generic hashing algorithm listed * in Binstock+Rex, "Practical Algorithms" p 69. * * Accepts string and returns hashed integer. * * Hack to use coordinates instead of a string by John. */ /***************************************************************************/ SDWORD fpathHashFunc(SDWORD x, SDWORD y) { UINT iHashValue, i; CHAR *c; CHAR aBuff[8]; memcpy(&aBuff[0], &x, 4); memcpy(&aBuff[4], &y, 4); c = aBuff; for ( iHashValue=0; c < &aBuff[8]; ++c ) { if (*c != 0) { iHashValue = ( iHashValue << ONE_EIGHTH ) + *c; if ( (i = iHashValue & HIGH_BITS) != 0 ) { iHashValue = ( iHashValue ^ ( i >> THREE_QUARTERS ) ) & ~HIGH_BITS; } } } iHashValue %= FPATH_TABLESIZE; return iHashValue; } // Add a node to the hash table void fpathHashAdd(FP_NODE *apsTable[], FP_NODE *psNode) { SDWORD index; index = fpathHashFunc(psNode->x, psNode->y); psNode->psNext = apsTable[index]; apsTable[index] = psNode; } // See if a node is in the hash table FP_NODE *fpathHashPresent(FP_NODE *apsTable[], SDWORD x, SDWORD y) { SDWORD index; FP_NODE *psFound; index = fpathHashFunc(x,y); psFound = apsTable[index]; while (psFound && !(psFound->x == x && psFound->y == y)) { psFound = psFound->psNext; } return psFound; } /* FP_NODE *psPrev, *psFound; index = fpathHashFunc(x,y); return psFound; }*/ // Remove a node from the hash table FP_NODE *fpathHashRemove(FP_NODE *apsTable[], SDWORD x, SDWORD y) { SDWORD index; FP_NODE *psPrev, *psFound; index = fpathHashFunc(x,y); if (apsTable[index] && apsTable[index]->x == x && apsTable[index]->y == y) { psFound = apsTable[index]; apsTable[index] = apsTable[index]->psNext; } else { psPrev = NULL; for(psFound = apsTable[index]; psFound; psFound = psFound->psNext) { if (psFound->x == x && psFound->y == y) { break; } psPrev = psFound; } if (psFound) { psPrev->psNext = psFound->psNext; } } return psFound; } // Remove a node from the hash table FP_NODE *fpathHashCondRemove(FP_NODE *apsTable[], SDWORD x, SDWORD y, SDWORD dist) { SDWORD index; FP_NODE *psPrev, *psFound; index = fpathHashFunc(x,y); if (apsTable[index] && apsTable[index]->x == x && apsTable[index]->y == y) { psFound = apsTable[index]; if (psFound->dist > dist) { apsTable[index] = apsTable[index]->psNext; } firstHit ++; } else { psPrev = NULL; for(psFound = apsTable[index]; psFound; psFound = psFound->psNext) { if (psFound->x == x && psFound->y == y) { break; } psPrev = psFound; } if (psFound && psFound->dist > dist) { psPrev->psNext = psFound->psNext; } secondHit ++; } return psFound; } // Reset the hash tables void fpathHashReset(void) { SDWORD i; FP_NODE *psNext; for(i=0; ipsNext; HEAP_FREE(psFPNodeHeap, apsNodes[i]); apsNodes[i] = psNext; } #else while (apsOpen[i]) { psNext = apsOpen[i]->psNext; HEAP_FREE(psFPNodeHeap, apsOpen[i]); apsOpen[i] = psNext; } while (apsClosed[i]) { psNext = apsClosed[i]->psNext; HEAP_FREE(psFPNodeHeap, apsClosed[i]); apsClosed[i] = psNext; } #endif } HEAP_RESET(psFPNodeHeap); } // Compare two nodes __inline SDWORD fpathCompare(FP_NODE *psFirst, FP_NODE *psSecond) { SDWORD first,second; first = psFirst->dist + psFirst->est; second = psSecond->dist + psSecond->est; if (first < second) { return -1; } else if (first > second) { return 1; } // equal totals, give preference to node closer to target if (psFirst->est < psSecond->est) { return -1; } else if (psFirst->est > psSecond->est) { return 1; } // exactly equal return 0; } // make a 50/50 random choice static UWORD seed = 1234; BOOL fpathRandChoice(void) { UDWORD val; val = (seed * RAND_MULTI + RAND_INC) & RAND_MOD; seed = (UWORD)val; return val & 1; } #if OPEN_LIST == 0 // Add a node to the open list void fpathOpenAdd(FP_NODE *psNode) { FP_NODE *psCurr, *psPrev; SDWORD comp; psNode->psLeft = NULL; psNode->psRight = NULL; if (psOpen == NULL) { // Nothing in the open set, add at root psOpen = psNode; } else { // Search the tree for the insertion point psCurr = psOpen; while (psCurr != NULL) { psPrev = psCurr; comp = fpathCompare(psNode, psCurr); if (comp < 0) { psCurr = psCurr->psLeft; } else { psCurr = psCurr->psRight; } } // Add the node to the tree if (comp < 0) { psPrev->psLeft = psNode; } else { psPrev->psRight = psNode; } } } // Remove a node from the open list void fpathOpenRemove(FP_NODE *psNode) { FP_NODE *psCurr, *psParent, *psSubParent, *psInsert; SDWORD comp; if (psNode == psOpen) { // deleting the root psParent = NULL; } else { // find the parent of the node to delete psCurr = psOpen; while (psCurr && psCurr != psNode) { psParent = psCurr; comp = fpathCompare(psNode, psCurr); if (comp < 0) { psCurr = psCurr->psLeft; } else { psCurr = psCurr->psRight; } } ASSERT( psCurr != NULL, "fpathOpenRemove: couldn't find node" ); } // Find the node to take the deleted nodes place in the tree if (psNode->psLeft == NULL) { // simple case only right subtree psInsert = psNode->psRight; } else if (psNode->psRight == NULL) { // simple case only left subtree psInsert = psNode->psLeft; } else { // both subtrees present - find a node to replace this one in the tree psInsert = psNode->psRight; if (psInsert->psLeft == NULL) { psInsert->psLeft = psNode->psLeft; } else { while (psInsert->psLeft != NULL) { psSubParent = psInsert; psInsert=psInsert->psLeft; } psSubParent->psLeft = psInsert->psRight; psInsert->psLeft = psNode->psLeft; psInsert->psRight = psNode->psRight; } } // replace the deleted node in the tree if (psParent == NULL) { psOpen = psInsert; } else { if (comp < 0) { psParent->psLeft = psInsert; } else { psParent->psRight = psInsert; } } psNode->psLeft = NULL; psNode->psRight = NULL; } // Get the nearest entry in the open list FP_NODE *fpathOpenGet(void) { FP_NODE *psNode, *psPrev; if (psOpen == NULL) { return NULL; } psPrev = NULL; for(psNode = psOpen; psNode->psLeft != NULL; psNode = psNode->psLeft) { psPrev = psNode; } if (psPrev == NULL) { psOpen = psNode->psRight; } else { psPrev->psLeft = psNode->psRight; } return psNode; } // Check the binary tree is valid BOOL fpathValidateTree(FP_NODE *psNode) { BOOL left, right; left = TRUE; if (psNode->psLeft) { if (fpathCompare(psNode->psLeft, psNode) >= 0) { left = FALSE; } if (left) { left = fpathValidateTree(psNode->psLeft); } } right = TRUE; if (psNode->psRight) { if (fpathCompare(psNode->psRight, psNode) < 0) { right = FALSE; } if (right) { right = fpathValidateTree(psNode->psRight); } } return left && right; } #elif OPEN_LIST == 1 // Add a node to the open list void fpathOpenAdd(FP_NODE *psNode) { FP_NODE *psCurr,*psPrev; if (psOpen == NULL || fpathCompare(psNode, psOpen) < 0) { // Add to start psNode->psOpen = psOpen; psOpen = psNode; DBP1(("OpenAdd: start\n")); } else { // Add in the middle psPrev = NULL; for(psCurr = psOpen; psCurr && fpathCompare(psNode, psCurr) >= 0; psCurr = psCurr->psOpen) { psPrev = psCurr; } psNode->psOpen = psCurr; psPrev->psOpen = psNode; DBP1(("OpenAdd: after %d,%d dist %d\n", psPrev->x,psPrev->y, psPrev->dist)); } } // Get the nearest entry in the open list FP_NODE *fpathOpenGet(void) { FP_NODE *psNode; psNode = psOpen; psOpen = psOpen->psOpen; return psNode; } #elif OPEN_LIST == 2 // Add a node to the open list void fpathOpenAdd(FP_NODE *psNode) { psNode->psOpen = psOpen; psOpen = psNode; } // Get the nearest entry in the open list FP_NODE *fpathOpenGet(void) { FP_NODE *psNode, *psCurr, *psPrev, *psParent = NULL; SDWORD comp; if (psOpen == NULL) { return NULL; } // find the node with the lowest distance psPrev = NULL; psNode = psOpen; for(psCurr = psOpen; psCurr; psCurr = psCurr->psOpen) { comp = fpathCompare(psCurr, psNode); if (comp < 0 || (comp == 0 && fpathRandChoice())) { psParent = psPrev; psNode = psCurr; } psPrev = psCurr; } // remove the node from the list if (psNode == psOpen) { // node is at head of list psOpen = psOpen->psOpen; } else { psParent->psOpen = psNode->psOpen; } return psNode; } #endif #if OPEN_LIST == 1 || OPEN_LIST == 2 // Remove a node from the open list void fpathOpenRemove(FP_NODE *psNode) { FP_NODE *psCurr,*psPrev; if (psOpen == NULL) { ASSERT( FALSE, "fpathOpenRemove: NULL list" ); return; } else if (psNode == psOpen) { // Remove from start psOpen = psOpen->psOpen; } else { // remove from the middle psPrev = NULL; for(psCurr = psOpen; psCurr && psCurr != psNode; psCurr = psCurr->psOpen) { psPrev = psCurr; } if (psCurr) { psPrev->psOpen = psCurr->psOpen; } else { ASSERT( FALSE, "fpathOpenRemove: failed to find node" ); return; } } } #endif // estimate the distance to the target point SDWORD fpathEstimate(SDWORD x, SDWORD y, SDWORD fx, SDWORD fy) { SDWORD xdiff, ydiff; xdiff = x > fx ? x - fx : fx - x; ydiff = y > fy ? y - fy : fy - y; xdiff = xdiff * 10; ydiff = ydiff * 10; return xdiff > ydiff ? xdiff + ydiff/2 : xdiff/2 + ydiff; } // Generate a new node FP_NODE *fpathNewNode(SDWORD x, SDWORD y, SDWORD dist, FP_NODE *psRoute) { FP_NODE *psNode; if (!HEAP_ALLOC(psFPNodeHeap, (void*) &psNode)) { return NULL; } psNode->x = (SWORD)x; psNode->y = (SWORD)y; psNode->dist = (SWORD)dist; psNode->psRoute = psRoute; psNode->type = NT_OPEN; return psNode; } // Compare a location with those in the hash table and return a new one // if necessary /*FP_NODE *fpathHashCompare(SDWORD x, SDWORD y, SDWORD dist) { FP_NODE *psNew, *psPrev, *psFound; SDWORD index; index = fpathHashFunc(x,y); if (apsClosed[index] && apsClosed[index]->x == x && apsClosed[index]->y == y) { psFound = apsClosed[index]; apsClosed[index] = apsClosed[index]->psNext; } else { psPrev = NULL; for(psFound = apsClosed[index]; psFound; psFound = psFound->psNext) { if (psFound->x == x && psFound->y == y) { break; } psPrev = psFound; } if (psFound) { psPrev->psNext = psFound->psNext; } } return psNew; }*/ // Variables for the callback static SDWORD finalX,finalY, vectorX,vectorY; static BOOL obstruction; // The visibility ray callback BOOL fpathVisCallback(SDWORD x, SDWORD y, SDWORD dist) { SDWORD vx,vy; dist = dist; // See if this point is past the final point (dot product) vx = x - finalX; vy = y - finalY; if (vx*vectorX + vy*vectorY <=0) { return FALSE; } if (fpathBlockingTile(x>>TILE_SHIFT,y>>TILE_SHIFT)) { // found an obstruction obstruction = TRUE; return FALSE; } return TRUE; } // Check los between two tiles BOOL fpathTileLOS(SDWORD x1,SDWORD y1, SDWORD x2,SDWORD y2) { // convert to world coords x1 = (x1 << TILE_SHIFT) + TILE_UNITS/2; y1 = (y1 << TILE_SHIFT) + TILE_UNITS/2; x2 = (x2 << TILE_SHIFT) + TILE_UNITS/2; y2 = (y2 << TILE_SHIFT) + TILE_UNITS/2; // Initialise the callback variables finalX = x2; finalY = y2; vectorX = x1 - x2; vectorY = y1 - y2; obstruction = FALSE; rayCast(x1,y1, rayPointsToAngle(x1,y1,x2,y2), RAY_MAXLEN, fpathVisCallback); return !obstruction; } // Optimise the route void fpathOptimise(FP_NODE *psRoute) { FP_NODE *psCurr, *psSearch, *psTest; BOOL los; ASSERT( psRoute != NULL, "fpathOptimise: NULL route pointer" ); psCurr = psRoute; do { // work down the route looking for a failed LOS los = TRUE; psSearch = psCurr->psRoute; while (psSearch) { psTest = psSearch->psRoute; if (psTest) { los = fpathTileLOS(psCurr->x,psCurr->y, psTest->x,psTest->y); } if (!los) { break; } psSearch = psTest; } // store the previous successful point psCurr->psRoute = psSearch; psCurr = psSearch; } while (psCurr); } #if OPEN_LIST == 0 || OPEN_LIST == 1 // A* findpath BOOL fpathAStarRoute(ASTAR_ROUTE *psRoutePoints, SDWORD sx, SDWORD sy, SDWORD fx, SDWORD fy) { FP_NODE *psCurr, *psNew, *psRoute; FP_NODE *psCFound, *psOFound; SDWORD tileSX,tileSY,tileFX,tileFY; SDWORD dir, x,y, currDist; SDWORD index; SDWORD numPoints; /* firstHit=0; secondHit=0; astarInner=0; astarOuter=0; astarRemove=0;*/ tileSX = sx >> TILE_SHIFT; tileSY = sy >> TILE_SHIFT; tileFX = fx >> TILE_SHIFT; tileFY = fy >> TILE_SHIFT; // Add the start point to the open list psCurr = fpathNewNode(tileSX,tileSY, 0, NULL); if (!psCurr) { goto exit_error; } psCurr->est = fpathEstimate(psCurr->x, psCurr->y, tileFX, tileFY); psOpen = NULL; fpathOpenAdd(psCurr); fpathHashAdd(apsOpen, psCurr); // search for a route psRoute = NULL; while (psOpen != NULL) { psCurr = fpathOpenGet(); // debug( LOG_NEVER, "\nStart : %3d,%3d (%d,%d) = %d\n", psCurr->x,psCurr->y, psCurr->dist, psCurr->est, psCurr->dist + psCurr->est ); if (psCurr->x == tileFX && psCurr->y == tileFY) { // reached the target psRoute = psCurr; break; } astarOuter += 1; for(dir=0; dirdist + 10; } else { currDist = psCurr->dist + 14; } // Try a new location x = psCurr->x + aDirOffset[dir].x; y = psCurr->y + aDirOffset[dir].y; if (fpathBlockingTile(x,y)) { // tile is blocked, skip it // debug( LOG_NEVER, "blocked : %3d, %3d\n", x, y ); continue; } // See if this is already in the open list psOFound = fpathHashCondRemove(apsOpen, x,y, currDist); if (psOFound && psOFound->dist <= currDist) { // already in the open list by a shorter route // debug( LOG_NEVER, "blocked open : %3d, %3d dist %d\n", x, y, currDist ); continue; } // See if this is in the closed list psCFound = fpathHashCondRemove(apsClosed, x,y, currDist); ASSERT( !(psOFound && psCFound), "fpathAStarRoute: found point in open and closed lists" ); if (psCFound && psCFound->dist <= currDist) { // already in the closed list by a shorter route // debug( LOG_NEVER, "blocked closed : %3d, %3d dist %d\n", x, y, currDist ); continue; } astarInner += 1; // Now insert the point into the appropriate list if (!psOFound && !psCFound) { // Not in open or closed lists - add to the open list psNew = fpathNewNode(x,y, currDist, psCurr); psNew->est = fpathEstimate(x,y, tileFX, tileFY); fpathOpenAdd(psNew); fpathHashAdd(apsOpen, psNew); // debug( LOG_NEVER, "new : %3d, %3d (%d,%d) = %d\n", x, y, currDist, psNew->est, currDist + psNew->est ); } else if (psOFound && !psCFound) { astarRemove += 1; // already in the open list but this is shorter fpathOpenRemove(psOFound); psOFound->dist = currDist; psOFound->psRoute = psCurr; fpathOpenAdd(psOFound); fpathHashAdd(apsOpen, psOFound); // debug( LOG_NEVER, "replace open : %3d, %3d dist %d\n", x, y, currDist, psOFound->est, currDist + psOFound->est ); } else if (!psOFound && psCFound) { // already in the closed list but this is shorter psCFound->dist = currDist; psCFound->psRoute = psCurr; fpathOpenAdd(psCFound); fpathHashAdd(apsOpen, psCFound); // debug( _LOG_NEVER, "replace closed : %3d, %3d dist %d\n", x, y, currDist, psCFound->est, currDist + psCFound->est ); } else { ASSERT( FALSE,"fpathAStarRoute: the open and closed lists are f***ed" ); } } // ASSERT( fpathValidateTree(psOpen), // "fpathAStarRoute: Invalid open tree" ); // add the current point to the closed nodes fpathHashRemove(apsOpen, psCurr->x, psCurr->y); fpathHashAdd(apsClosed, psCurr); // debug( LOG_NEVER, "HashAdd - closed : %3d,%3d (%d,%d) = %d\n", psCurr->x, psCurr->y, psCurr->dist, psCurr->est, psCurr->dist+psCurr->est ); } // optimise the route if one was found if (psRoute) { fpathOptimise(psRoute); numPoints = 0; for(psCurr = psRoute; psCurr; psCurr=psCurr->psRoute) { numPoints ++; } index = numPoints -1; if (numPoints >= TRAVELSIZE) { numPoints = TRAVELSIZE -1; } psCurr = psRoute; while (psCurr) { if (index < numPoints) { psMoveCntl->MovementList[index].XCoordinate = (psCurr->x << TILE_SHIFT) + TILE_UNITS/2; psMoveCntl->MovementList[index].YCoordinate = (psCurr->y << TILE_SHIFT) + TILE_UNITS/2; } index -= 1; psCurr = psCurr->psRoute; } psMoveCntl->MovementList[numPoints].XCoordinate = -1; psMoveCntl->MovementList[numPoints].YCoordinate = -1; } else { psMoveCntl->MovementList[0].XCoordinate = -1; psMoveCntl->MovementList[0].YCoordinate = -1; } fpathHashReset(); // fpathOpenReset(); return TRUE; exit_error: fpathHashReset(); // fpathOpenReset(); return FALSE; } #elif OPEN_LIST == 2 // A* findpath SDWORD fpathAStarRoute(SDWORD routeMode, ASTAR_ROUTE *psRoutePoints, SDWORD sx, SDWORD sy, SDWORD fx, SDWORD fy) { FP_NODE *psFound, *psCurr, *psNew, *psParent, *psNext; static FP_NODE *psNearest, *psRoute; SDWORD tileSX,tileSY,tileFX,tileFY; SDWORD dir, x,y, currDist; SDWORD index; SDWORD retval; /* firstHit=0; secondHit=0; astarInner=0; astarOuter=0; astarRemove=0;*/ // DBPRINTF(("Astar start\n"); tileSX = sx >> TILE_SHIFT; tileSY = sy >> TILE_SHIFT; tileFX = fx >> TILE_SHIFT; tileFY = fy >> TILE_SHIFT; if (routeMode == ASR_NEWROUTE) { fpathHashReset(); // Add the start point to the open list psCurr = fpathNewNode(tileSX,tileSY, 0, NULL); if (!psCurr) { goto exit_error; } psCurr->est = (SWORD)fpathEstimate(psCurr->x, psCurr->y, tileFX, tileFY); psOpen = NULL; fpathOpenAdd(psCurr); fpathHashAdd(apsNodes, psCurr); psRoute = NULL; psNearest = NULL; } // search for a route while (psOpen != NULL) { if (astarInner > FPATH_LOOP_LIMIT) { return ASR_PARTIAL; } psCurr = fpathOpenGet(); // debug( LOG_NEVER, "\nStart : %3d,%3d (%d,%d) = %d\n", psCurr->x, psCurr->y, psCurr->dist, psCurr->est, psCurr->dist + psCurr->est ); if (psCurr->x == tileFX && psCurr->y == tileFY) { // reached the target psRoute = psCurr; break; } // note the nearest node to the target so far if (psNearest == NULL || psCurr->est < psNearest->est) { psNearest = psCurr; } astarOuter += 1; for(dir=0; dirdist + 10; } else { currDist = psCurr->dist + 14; } // Try a new location x = psCurr->x + aDirOffset[dir].x; y = psCurr->y + aDirOffset[dir].y; // See if the node has already been visited psFound = fpathHashPresent(apsNodes, x,y); if (psFound && psFound->dist <= currDist) { // already visited node by a shorter route // debug( LOG_NEVER, "blocked (%d) : %3d, %3d dist %d\n", psFound->type, x, y, currDist ); continue; } // If the tile hasn't been visited see if it is a blocking tile if (!psFound && fpathBlockingTile(x,y)) { // tile is blocked, skip it // debug( LOG_NEVER, "blocked : %3d, %3d\n", x, y ); continue; } astarInner += 1; // Now insert the point into the appropriate list if (!psFound) { // Not in open or closed lists - add to the open list psNew = fpathNewNode(x,y, currDist, psCurr); if (psNew) { psNew->est = (SWORD)fpathEstimate(x,y, tileFX, tileFY); fpathOpenAdd(psNew); fpathHashAdd(apsNodes, psNew); // debug( LOG_NEVER, "new : %3d, %3d (%d,%d) = %d\n", x, y, currDist, psNew->est, currDist + psNew->est ); } } else if (psFound->type == NT_OPEN) { astarRemove += 1; // already in the open list but this is shorter psFound->dist = (SWORD)currDist; psFound->psRoute = psCurr; // debug( LOG_NEVER, "replace open : %3d, %3d dist %d\n", x, y, currDist, psFound->est, currDist + psFound->est ); } else if (psFound->type == NT_CLOSED) { // already in the closed list but this is shorter psFound->type = NT_OPEN; psFound->dist = (SWORD)currDist; psFound->psRoute = psCurr; fpathOpenAdd(psFound); // debug( LOG_NEVER, "replace closed : %3d, %3d dist %d\n", x, y, currDist, psFound->est, currDist + psFound->est ); } else { ASSERT( FALSE,"fpathAStarRoute: the open and closed lists are f***ed" ); } } // ASSERT( fpathValidateTree(psOpen), // "fpathAStarRoute: Invalid open tree" ); // add the current point to the closed nodes // fpathHashRemove(apsOpen, psCurr->x, psCurr->y); // fpathHashAdd(apsClosed, psCurr); psCurr->type = NT_CLOSED; // debug( LOG_NEVER, "HashAdd - closed : %3d,%3d (%d,%d) = %d\n", psCurr->x, psCurr->y, psCurr->dist, psCurr->est, psCurr->dist+psCurr->est ); } // DBPRINTF(("Astar fin astarOuter=%d astarInner=%d\n",astarOuter,astarInner); retval = ASR_OK; // return the nearest route if no actual route was found if (!psRoute && psNearest) // !((psNearest->x == tileSX) && (psNearest->y == tileSY))) { psRoute = psNearest; fx = (psNearest->x << TILE_SHIFT) + TILE_UNITS/2; fy = (psNearest->y << TILE_SHIFT) + TILE_UNITS/2; retval = ASR_NEAREST; } // optimise the route if one was found if (psRoute) { fpathOptimise(psRoute); // get the route in the correct order // If as I suspect this is to reverse the list, then it's my suspicion that // we could route from destination to source as opposed to source to // destination. We could then save the reversal. to risky to try now...Alex M psParent = NULL; for(psCurr=psRoute; psCurr; psCurr=psNext) { psNext = psCurr->psRoute; psCurr->psRoute = psParent; psParent = psCurr; } psRoute = psParent; psCurr = psRoute; index = psRoutePoints->numPoints; while (psCurr && (index < ASTAR_MAXROUTE)) { psRoutePoints->asPos[index].x = psCurr->x; psRoutePoints->asPos[index].y = psCurr->y; index += 1; psCurr = psCurr->psRoute; } psRoutePoints->numPoints = index; psRoutePoints->finalX = psRoutePoints->asPos[index-1].x; psRoutePoints->finalY = psRoutePoints->asPos[index-1].y; } else { retval = ASR_FAILED; } fpathHashReset(); // fpathOpenReset(); return retval; exit_error: fpathHashReset(); // fpathOpenReset(); return ASR_FAILED; } #endif