/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see . * */ #include "m4/adv_r/adv_rails.h" #include "m4/core/errors.h" #include "m4/core/imath.h" #include "m4/mem/mem.h" #include "m4/vars.h" namespace M4 { #define TOP_EDGE 0x01 #define LEFT_EDGE 0x02 #define BOTTOM_EDGE 0x04 #define RIGHT_EDGE 0x08 bool InitRails() { int32 i; // Register with the stash the frequently used structs if (!mem_register_stash_type(&_G(rails).memtypePATHN, sizeof(pathNode), 32, "+PATHNODE")) { return false; } // Create the stack. Since any path through a series of nodes can have at most MAXRAILNODES... if ((_G(rails).stackBottom = (railNode **)mem_alloc(sizeof(railNode *) * MAXRAILNODES, STR_RAILNODE)) == nullptr) { return false; } // Allocate the array of railNode pointers and initialize... if ((_G(rails).myNodes = (railNode **)mem_alloc(sizeof(railNode *) * MAXRAILNODES, STR_RAILNODE)) == nullptr) { return false; } for (i = 0; i < MAXRAILNODES; i++) { _G(rails).myNodes[i] = nullptr; } // Calculate the size of the edge table, allocate, and initialize // The edge table stores the upper triangle of a square matrix. const int32 edgeTableSize = (MAXRAILNODES * (MAXRAILNODES - 1)) >> 1; if ((_G(rails).myEdges = (int16 *)mem_alloc(sizeof(int16) * edgeTableSize, "edge table")) == nullptr) { return false; } for (i = 0; i < edgeTableSize; i++) { _G(rails).myEdges[i] = 0; } // Set the parameters and return _G(rails).noWalkRectList = nullptr; return true; } void rail_system_shutdown(void) { if (_G(rails).stackBottom) { mem_free(_G(rails).stackBottom); _G(rails).stackBottom = nullptr; } ClearRails(); if (_G(rails).myNodes) { mem_free(_G(rails).myNodes); _G(rails).myNodes = nullptr; } if (_G(rails).myEdges) { mem_free(_G(rails).myEdges); _G(rails).myEdges = nullptr; } } void ClearRails(void) { int32 i; if (_G(rails).myNodes) { for (i = 0; i < MAXRAILNODES; i++) { if (_G(rails).myNodes[i]) { mem_free((void *)_G(rails).myNodes[i]); _G(rails).myNodes[i] = nullptr; } } } if (_G(rails).myEdges) { const int32 edgeTableSize = (MAXRAILNODES * (MAXRAILNODES - 1)) >> 1; for (i = 0; i < edgeTableSize; i++) { _G(rails).myEdges[i] = 0; } } // Now turf the noWalkRectList noWalkRect *myRect = _G(rails).noWalkRectList; while (myRect) { _G(rails).noWalkRectList = _G(rails).noWalkRectList->next; mem_free((void *)myRect); myRect = _G(rails).noWalkRectList; } } noWalkRect *intr_add_no_walk_rect(int32 x1, int32 y1, int32 x2, int32 y2, int32 altX, int32 altY, Buffer *walkCodes) { noWalkRect *newRect; // Parameter verification if ((x2 < x1) || (y2 < y1)) { return nullptr; } // Create new noWalkRect structure if ((newRect = (noWalkRect *)mem_alloc(sizeof(noWalkRect), "intr noWalkRect")) == nullptr) { error_show(FL, 'IADN', "rect size: %d %d %d %d", x1, y1, x2, y2); return nullptr; } // Initialize the new rect newRect->x1 = x1; newRect->y1 = y1; newRect->x2 = x2; newRect->y2 = y2; // Add the alternate walkto node - this node must exist newRect->alternateWalkToNode = AddRailNode(altX, altY, walkCodes, false); if (newRect->alternateWalkToNode < 0) { error_show(FL, 'IADN', "could not add node. coord: %d %d", altX, altY); } // Now add the corner nodes. Not as important if these don't exist newRect->walkAroundNode1 = AddRailNode(x1 - 1, y1 - 1, walkCodes, false); newRect->walkAroundNode2 = AddRailNode(x2 + 1, y1 - 1, walkCodes, false); newRect->walkAroundNode3 = AddRailNode(x2 + 1, y2 + 1, walkCodes, false); newRect->walkAroundNode4 = AddRailNode(x1 - 1, y2 + 1, walkCodes, false); // Now link the rectangle into the list newRect->prev = nullptr; newRect->next = _G(rails).noWalkRectList; if (_G(rails).noWalkRectList) { _G(rails).noWalkRectList->prev = newRect; } _G(rails).noWalkRectList = newRect; // Now refresh all the edges in case the noWalkRect is blocking edges RestoreEdgeList(walkCodes); return newRect; } noWalkRect *intr_add_no_walk_rect(int32 x1, int32 y1, int32 x2, int32 y2, int32 altX, int32 altY) { return intr_add_no_walk_rect(x1, y1, x2, y2, altX, altY, _G(screenCodeBuff)->get_buffer()); } void intr_move_no_walk_rect(noWalkRect *myRect, int32 new_x1, int32 new_y1, int32 new_x2, int32 new_y2, int32 new_altX, int32 new_altY, Buffer *walkCodes) { if (!myRect) { return; } // Set the new values into the rect myRect->x1 = new_x1; myRect->y1 = new_y1; myRect->x2 = new_x2; myRect->y2 = new_y2; // Now move the nodes MoveRailNode(myRect->alternateWalkToNode, new_altX, new_altY, walkCodes, false); MoveRailNode(myRect->walkAroundNode1, new_x1 - 1, new_y1 - 1, walkCodes, false); MoveRailNode(myRect->walkAroundNode2, new_x2 + 1, new_y1 - 1, walkCodes, false); MoveRailNode(myRect->walkAroundNode3, new_x2 + 1, new_y2 - 1, walkCodes, false); MoveRailNode(myRect->walkAroundNode4, new_x1 - 1, new_y2 - 1, walkCodes, false); // Now refresh all the edges RestoreEdgeList(walkCodes); } void intr_remove_no_walk_rect(noWalkRect *myRect, Buffer *walkCodes) { // Parameter verification if (!myRect) { return; } // Remove myRect from the list if (myRect->prev) { myRect->prev->next = myRect->next; } else { _G(rails).noWalkRectList = myRect->next; } if (myRect->next) { myRect->next->prev = myRect->prev; } // Remove the railNodes RemoveRailNode(myRect->alternateWalkToNode, walkCodes, false); RemoveRailNode(myRect->walkAroundNode1, walkCodes, false); RemoveRailNode(myRect->walkAroundNode2, walkCodes, false); RemoveRailNode(myRect->walkAroundNode3, walkCodes, false); RemoveRailNode(myRect->walkAroundNode4, walkCodes, false); // Turf myRect mem_free(myRect); // Now refresh all the edges RestoreEdgeList(walkCodes); } void intr_remove_no_walk_rect(noWalkRect *myRect) { intr_remove_no_walk_rect(myRect, _G(screenCodeBuff)->get_buffer()); } bool intr_LineCrossesRect(int32 line_x1, int32 line_y1, int32 line_x2, int32 line_y2, int32 rect_x1, int32 rect_y1, int32 rect_x2, int32 rect_y2) { // Ensure we have a valid rectangle if ((rect_x1 > rect_x2) || (rect_y1 > rect_y2)) { return false; } // Make copies of x1, y1, x2, y2 int32 p1X = line_x1; int32 p1Y = line_y1; int32 p2X = line_x2; int32 p2Y = line_y2; // Calculate the cohen sutherland codes for the endpoints of the line // For (p1X, p1Y) uint8 endCode1 = 0; if (p1X < rect_x1) { endCode1 = LEFT_EDGE; } else if (p1X > rect_x2) { endCode1 = RIGHT_EDGE; } if (p1Y < rect_y1) { endCode1 |= TOP_EDGE; } else if (p1Y > rect_y2) { endCode1 |= BOTTOM_EDGE; } // For (p2X, p2Y) uint8 endCode2 = 0; if (p2X < rect_x1) { endCode2 = LEFT_EDGE; } else if (p2X > rect_x2) { endCode2 = RIGHT_EDGE; } if (p2Y < rect_y1) { endCode2 |= TOP_EDGE; } else if (p2Y > rect_y2) { endCode2 |= BOTTOM_EDGE; } // If either endCode is 0, that point is inside the rect, therefore the line intersects if ((!endCode1) || (!endCode2)) { return true; } bool finished = false; while (!finished) { // If both have a bit set in common, then the line segment is completely off one edge if (endCode1 & endCode2) { finished = true; } // Calculate the mid point of the line segment const int32 mX = (p1X + p2X) >> 1; const int32 mY = (p1Y + p2Y) >> 1; // Because the midpoint is an integer (round-off err), make sure it isn't the same // as one of the two endpoints. if (((mX == p1X) && (mY == p1Y)) || ((mX == p2X) && (mY == p2Y))) { return false; } // Calculate the cohen sutherland codes for the midpoint of the line segment uint8 midCode = 0; if (mX < rect_x1) { midCode = LEFT_EDGE; } else if (mX > rect_x2) { midCode = RIGHT_EDGE; } if (mY < rect_y1) { midCode |= TOP_EDGE; } else if (mY > rect_y2) { midCode |= BOTTOM_EDGE; } if (!midCode) { return true; } // Else the midCode and one of the end points must form a line segment completely off one edge else if (midCode & endCode1) { // Setting endpoint1 to the midpoint throws away that half of the line segment p1X = mX; p1Y = mY; endCode1 = midCode; } else { // Ditto for endpoint 2 p2X = mX; p2Y = mY; endCode2 = midCode; } } // Got through without intersecting, therefore... return false; } static bool intr_LinePassesThroughRect(int32 x1, int32 y1, int32 x2, int32 y2) { // If there aren't any no-walk rects, no problem, return false if (!_G(rails).noWalkRectList) { return false; } // Loop through the _G(rails).noWalkRectList noWalkRect *tempRect = _G(rails).noWalkRectList; bool intersected = false; while (tempRect && (!intersected)) { // See if the line passes through tempRect intersected = intr_LineCrossesRect(x1, y1, x2, y2, tempRect->x1, tempRect->y1, tempRect->x2, tempRect->y2); tempRect = tempRect->next; } return intersected; } bool intr_LinesCross(int32 line1_x1, int32 line1_y1, int32 line1_x2, int32 line1_y2, int32 line2_x1, int32 line2_y1, int32 line2_x2, int32 line2_y2) { // The theory is that either line1 intersects the rectangle created by line2, and/or line2 // intersects the rectangle reacted by line1. // Make sure both lines are listed left to right, top to bottom when passing in the coords as a rectangle int32 rectX1 = imath_min(line1_x1, line1_x2); int32 rectY1 = imath_min(line1_y1, line1_y2); int32 rectX2 = imath_max(line1_x1, line1_x2); int32 rectY2 = imath_max(line1_y1, line1_y2); bool intersected = intr_LineCrossesRect(line2_x1, line2_y1, line2_x2, line2_y2, rectX1, rectY1, rectX2, rectY2); if (intersected) { rectX1 = imath_min(line2_x1, line2_x2); rectY1 = imath_min(line2_y1, line2_y2); rectX2 = imath_max(line2_x1, line2_x2); rectY2 = imath_max(line2_y1, line2_y2); intersected = intr_LineCrossesRect(line1_x1, line1_y1, line1_x2, line1_y2, rectX1, rectY1, rectX2, rectY2); } return intersected; } void CreateEdge(int32 node1, int32 node2, Buffer *walkCodes) { int32 i; int32 y_unit, error_term; frac16 distance; // Check for nodes and edges if ((!_G(rails).myNodes) || (!_G(rails).myEdges)) { return; } if ((node1 < 0) || (node1 >= MAXRAILNODES) || (node2 < 0) || (node2 >= MAXRAILNODES)) { return; } if (node1 == node2) { return; } // Ensure node1 < node2 if (node2 < node1) { SWAP(node1, node2); } // If node1 is y and node2 is x, first find table entry ie. tableWidth * y + x, the subtract // n(n+1)/2 since only the upper triangle of the table is stored... const int32 index = (MAXRAILNODES - 1) * node1 + node2 - 1 - (node1 * (node1 + 1) >> 1); _G(rails).myEdges[index] = 0; bool valid = true; uint8 *walkCodePtr = nullptr; bool finished = false; if ((!_G(rails).myNodes[node1]) || (!_G(rails).myNodes[node2])) return; int32 x1 = _G(rails).myNodes[node1]->x; int32 y1 = _G(rails).myNodes[node1]->y; int32 x2 = _G(rails).myNodes[node2]->x; int32 y2 = _G(rails).myNodes[node2]->y; // Ensure the algorithm is symmetric... if (x2 < x1) { SWAP(x1, x2); SWAP(y1, y2); } if (walkCodes && walkCodes->data) { // Initialize the buffer data pointer, the maximum dimensions of the buffer, and the scan x and y const int32 width = walkCodes->w; const int32 stride = walkCodes->stride; const int32 height = walkCodes->h; int32 scanX = x1; int32 scanY = y1; // Calculate the difference along the y-axis int32 yDiff = y2 - y1; // If we are scanning from bottom to top if (yDiff < 0) { //set yDiff to be the absolute, and set the y_unit direction negative yDiff = -yDiff; y_unit = -1; } //else set the y_unit direction positive else { y_unit = 1; } // Because of the symmetry check, xDiff is always positive const int32 xDiff = x2 - x1; const int32 x_unit = 1; // If the difference is bigger along the x axis if (xDiff > yDiff) { // Initialize the error term error_term = xDiff >> 1; // Loop along the x axis and adjust scanY as necessary for (i = 0; ((i <= xDiff) && valid && !finished); i++) { // Check if we have scanned off the edge of the buffer if ((scanX >= width) || ((y_unit > 0) && (scanY >= height)) || ((y_unit < 0) && (scanY < 0))) { finished = true; } else { // Else we either haven't yet reached the buffer, or we are on it // Make sure we're on the buffer if ((scanX >= 0) && (scanY >= 0) && (scanY < height)) { // Check to see if this is a valid walking area if (!walkCodePtr) { walkCodePtr = (uint8 *) & ((walkCodes->data)[scanY * stride + scanX]); } if ((*walkCodePtr) & 0x10) { valid = false; } } // Update scanY if appropriate // Update the error term error_term += yDiff; // If the error_term has exceeded the xdiff, we need to move one unit along the y axis if (error_term >= xDiff) { // Reset the error term error_term -= xDiff; // Move along the y axis scanY += y_unit; // Update the walkCodePtr index if necessary if (walkCodePtr) { if (y_unit > 0) { walkCodePtr += stride; } else { walkCodePtr -= stride; } } } } scanX += x_unit; // Update the walkCodePtr index if necessary if (walkCodePtr) { walkCodePtr++; } } } else { // Else the difference is bigger along the y axis // Initialize the error term error_term = yDiff >> 1; // Loop along the y axis and adjust scanX as necessary for (i = 0; ((i <= yDiff) && valid && !finished); i++) { // Check if we have scanned off the edge of the buffer if (scanX >= width || ((y_unit > 0) && (scanY >= height)) || ((y_unit < 0) && (scanY < 0))) { finished = true; } else { // Else we either haven't yet reached the buffer, or we are on it // Make sure we're on the buffer if ((scanX >= 0) && (scanX < width) && (scanY >= 0) && (scanY < height)) { // Check to see if this is a valid walking area if (!walkCodePtr) { walkCodePtr = (uint8 *) & ((walkCodes->data)[scanY * stride + scanX]); } if ((*walkCodePtr) & 0x10) { valid = false; } } // Update scanX if appropriate // Update the error term error_term += xDiff; // If the error_term has exceeded the xDiff, we need to move one unit along the y axis if (error_term >= yDiff) { // Reset the error term error_term -= yDiff; // Move along the x axis scanX += x_unit; // Update the walkCodePtr index if necessary if (walkCodePtr) { walkCodePtr++; } } } scanY += y_unit; // Update the walkCodePtr index if necessary if (walkCodePtr) { if (y_unit > 0) { walkCodePtr += stride; } else { walkCodePtr -= stride; } } } } } // Now that we've checked it against the walk codes, we check if the line passes through // any of the forbidden rectangles if (valid) { if (intr_LinePassesThroughRect(x1, y1, x2, y2)) { valid = false; } } // Finally, if the edge is still valid, fill in the edge table with the distance between the nodes. if (valid) { frac16 deltaX = imath_abs(((frac16)(x2 - x1)) << 16); frac16 deltaY = imath_abs(((frac16)(y2 - y1)) << 16); if ((deltaX >= 0x800000) || (deltaY >= 0x800000)) { deltaX >>= 16; deltaY >>= 16; distance = (frac16)(SqrtF16(deltaX * deltaX + deltaY * deltaY) << 16); } else { distance = SqrtF16(SquareSF16(deltaX) + SquareSF16(deltaY)) << 8; } _G(rails).myEdges[index] = (int16)(distance >> 16); } } void RestoreNodeEdges(int32 nodeID, Buffer *walkCodes) { for (int32 i = 0; i < MAXRAILNODES; i++) { CreateEdge(i, nodeID, walkCodes); } } void RestoreEdgeList(Buffer *walkCodes) { for (int32 i = 0; i < MAXRAILNODES; i++) { for (int32 j = i + 1; j < MAXRAILNODES; j++) { CreateEdge(i, j, walkCodes); } } } int32 AddRailNode(int32 x, int32 y, Buffer *walkCodes, bool restoreEdges) { int32 i; if ((!_G(rails).myNodes) || (!_G(rails).myEdges)) { return -1; } for (i = 0; (i < MAXRAILNODES) && _G(rails).myNodes[i]; i++) { } if (i < MAXRAILNODES) { railNode *newNode = (railNode *)mem_alloc(sizeof(railNode), "railNode"); if (newNode == nullptr) { return -1; } newNode->nodeID = (Byte)i; newNode->x = (int16)x; newNode->y = (int16)y; _G(rails).myNodes[i] = newNode; if (restoreEdges) { for (int32 j = 0; j < MAXRAILNODES; j++) { if (_G(rails).myNodes[j]) CreateEdge(i, j, walkCodes); } } return i; } return -1; } void MoveRailNode(int32 nodeID, int32 x, int32 y, Buffer *walkCodes, bool restoreEdges) { if ((!_G(rails).myNodes) || (!_G(rails).myEdges) || (nodeID < 0) || (nodeID >= MAXRAILNODES) || (!_G(rails).myNodes[nodeID])) { return; } _G(rails).myNodes[nodeID]->x = (int16)x; _G(rails).myNodes[nodeID]->y = (int16)y; if (restoreEdges) { RestoreNodeEdges(nodeID, walkCodes); } } bool RemoveRailNode(int32 nodeID, Buffer *walkCodes, bool restoreEdges) { if ((nodeID < 0) || (nodeID >= MAXRAILNODES)) { return false; } if ((!_G(rails).myNodes) || (!_G(rails).myNodes[nodeID]) || (!_G(rails).myEdges)) { return false; } mem_free((void *)_G(rails).myNodes[nodeID]); _G(rails).myNodes[nodeID] = nullptr; if (restoreEdges) { RestoreNodeEdges(nodeID, walkCodes); } return true; } bool RailNodeExists(int32 nodeID, int32 *nodeX, int32 *nodeY) { if ((nodeID < 0) || (nodeID >= MAXRAILNODES) || (!_G(rails).myNodes) || (!_G(rails).myNodes[nodeID])) { return false; } if (nodeX) { *nodeX = _G(rails).myNodes[nodeID]->x; } if (nodeY) { *nodeY = _G(rails).myNodes[nodeID]->y; } return true; } int16 GetEdgeLength(int32 node1, int32 node2) { if (!_G(rails).myEdges) return 0; if (node1 == node2) return 0; if (node2 < node1) SWAP(node1, node2); // If node1 is y and node2 is x, first find table entry ie. tableWidth * y + x, the subtract // n(n+1)/2 since only the upper triangle of the table is stored... const int32 index = (MAXRAILNODES - 1) * node1 + node2 - 1 - (node1 * (node1 + 1) >> 1); return _G(rails).myEdges[index]; } void DisposePath(railNode *pathStart) { railNode *tempNode = pathStart; while (tempNode) { pathStart = pathStart->shortPath; mem_free((void *)tempNode); tempNode = pathStart; } } static railNode *DuplicatePath(railNode *pathStart) { railNode *newNode; // Initialize pointers railNode *firstNode = nullptr; railNode *prevNode = nullptr; // This routine assumes a valid path from _G(rails).myNodes[origID] following _G(rails).myNodes[]->shortPath until nullptr railNode *pathNode = pathStart; // Loop until nullptr - end of path while (pathNode) { // Create a new railNode, and duplicate values if ((newNode = (railNode *)mem_alloc(sizeof(railNode), "+RAIL")) == nullptr) { error_show(FL, 'OOM!', "Could not alloc railNode"); return nullptr; } newNode->x = pathNode->x; newNode->y = pathNode->y; newNode->shortPath = nullptr; // Link into the new list if (!firstNode) { firstNode = newNode; } else { prevNode->shortPath = newNode; } prevNode = newNode; // Get the next in the list pathNode = pathNode->shortPath; } return firstNode; } railNode *CreateCustomPath(int coord, ...) { va_list argPtr; railNode *prevNode = nullptr; // Initialize firstNode railNode *firstNode = nullptr; // Set argPtr to point to the beginning of the variable arg list va_start(argPtr, coord); // Loop until coord == -1 while (coord != -1) { // Set x const int32 x = coord; // Read the next arg off the arg list, and set y coord = va_arg(argPtr, int); const int32 y = coord; // Create a new node struct railNode *newNode = (railNode *)mem_alloc(sizeof(railNode), "railNode"); if (newNode == nullptr) { error_show(FL, 'OOM!', "could not alloc railNode"); return nullptr; } // Set the new node values... newNode->x = x; newNode->y = y; newNode->shortPath = nullptr; // Link into path list if (!firstNode) { firstNode = newNode; } else { assert(prevNode); prevNode->shortPath = newNode; } prevNode = newNode; // Read another arg, should be the next "x" for the next pair of args if (coord != -1) { coord = va_arg(argPtr, int); } } // Restore the stack va_end(argPtr); return firstNode; } bool GetShortestPath(int32 origID, int32 destID, railNode **shortPath) { pathNode *tempPathNode; railNode *tempNode; int32 i, prevID; *shortPath = nullptr; // Check that we have two valid and different nodes to walk between if ((!_G(rails).myNodes) || (!_G(rails).myEdges)) { return false; } if (origID == destID) { return true; } if ((!_G(rails).myNodes[origID]) || (!_G(rails).myNodes[destID])) { return false; } if ((_G(rails).myNodes[origID]->x == _G(rails).myNodes[destID]->x) && (_G(rails).myNodes[origID]->y == _G(rails).myNodes[destID]->y)) { return true; } // Set the end of the shortest path _G(rails).myNodes[destID]->shortPath = nullptr; // Check to see if we can walk directly from oridID to destID int16 edgeDist = GetEdgeLength(origID, destID); if (edgeDist > 0) { _G(rails).myNodes[origID]->shortPath = _G(rails).myNodes[destID]; _G(rails).myNodes[destID]->pathWeight = edgeDist; *shortPath = DuplicatePath(_G(rails).myNodes[origID]->shortPath); return true; } // Otherwise, run the algorithm to determine the shortest path // Initialize the railNodes and find the largest node ID to speed up for loops int32 maxNodeID = 0; for (i = 0; i < MAXRAILNODES; i++) { tempNode = _G(rails).myNodes[i]; if (tempNode) { maxNodeID = i; tempNode->shortPath = nullptr; tempNode->pathWeight = 32767; } } // Initialize the stack _G(rails).stackTop = _G(rails).stackBottom; // Initialize the bitmask of the nodes and the current path list uint32 currPathNodes = 0; pathNode *thePath = nullptr; // Put the first node onto the stack (the address of, actually) _G(rails).myNodes[origID]->pathWeight = 0; *_G(rails).stackTop++ = _G(rails).myNodes[origID]; // While there are still nodes on the stack keep processing while (_G(rails).stackTop > _G(rails).stackBottom) { // Take the first node off the stack railNode *currNode = *(--_G(rails).stackTop); // See if it is adjacent to the destination node - always 0 the first time through... edgeDist = GetEdgeLength(currNode->nodeID, destID); // Yes it is, therefore, we have a valid path, maybe the shortest... if (edgeDist > 0) { // Check whether the pathWeight of the second last node + the edge to get to the last // is less than the pathWeight of the previously found shortest path if (currNode->pathWeight + edgeDist < _G(rails).myNodes[destID]->pathWeight) { // If so, store the new shortest path weight, and complete the path link _G(rails).myNodes[destID]->pathWeight = (int16)(currNode->pathWeight + edgeDist); _G(rails).myNodes[currNode->nodeID]->shortPath = _G(rails).myNodes[destID]; // Now we follow "thePath" back to the origID, updating the bitMask, and setting shortPath links // initialize variables used in the loop prevID = currNode->nodeID; currPathNodes = 0; tempPathNode = thePath; // Loop through to the end of the path while (tempPathNode) { // Link up the nodes in the shortPath _G(rails).myNodes[tempPathNode->nodeID]->shortPath = _G(rails).myNodes[prevID]; // Or in the bitmask currPathNodes |= (1 << prevID); // Setup for the next link prevID = tempPathNode->nodeID; tempPathNode = tempPathNode->next; } } // Set the stackTop to point at the node preceding currNode, which precedes destID // we want to check the contents of the stack from the top, and stackTop always point to // the next available location, not directly at the top element. _G(rails).stackTop--; // Setup temp Ptr tempPathNode = thePath; // While the top of the stack point at a node directly in thePath - // we remove all the nodes in thePath from the top of the stack until we come across // one that is not in thePath. This one that is not in thePath will not yet have been // checked. while ((tempPathNode) && (tempPathNode->nodeID == (*_G(rails).stackTop)->nodeID)) { _G(rails).stackTop--; thePath = thePath->next; mem_free_to_stash((void *)tempPathNode, _G(rails).memtypePATHN); tempPathNode = thePath; } // Since thePath cannot have more elements than are on the stack, we will never have // a stack underflow, but when the _G(rails).stackTop points to a node not in thePath, or // if the entire thePath was on the stack, thus the stack was completely emptied, // we must reset the _G(rails).stackTop to point to the next available location. _G(rails).stackTop++; } else { // Else the currNode is not adjacent to the dest node // Put currNode back onto the stack _G(rails).stackTop++; // Setup a temporary pointer, so we know whether any neighbors of the currNode were stacked... railNode **checkStackTop = _G(rails).stackTop; // Check to see whether the path leading to currNode is at least shorter than the current shortest path if (currNode->pathWeight < _G(rails).myNodes[destID]->pathWeight) { // If so, loop through all the nodes for (i = 0; i <= maxNodeID; i++) { if (_G(rails).myNodes[i]) { // For each different valid node, check to see if the currNode is adjacent to it edgeDist = GetEdgeLength(currNode->nodeID, i); // If it is a neighbor, and the pathWeight reaching it through currNode is // less than the weight of any previous path that reached the same neighbor... if ((edgeDist > 0) && ((currNode->pathWeight + edgeDist) < _G(rails).myNodes[i]->pathWeight)) { // Now see if that neighbor is already part of the current shortest path if (currPathNodes & (1 << i)) { // If so, the path from node i to the destination node will already have been created // therefore the entire path will become shorter by the original weight to reach node i // minus (the weight to get to the currNode plus the weight of the edge to // get from the currNode to node i). const int16 shortcutWeight = (int16)(_G(rails).myNodes[i]->pathWeight - (currNode->pathWeight + edgeDist)); // Loop from node i to the dest node, subtract the shortcutWeight, and or the bitmask currPathNodes = 0; tempNode = _G(rails).myNodes[i]; while (tempNode) { currPathNodes |= (1 << tempNode->nodeID); tempNode->pathWeight -= shortcutWeight; tempNode = tempNode->shortPath; } // Link the currNode to node i _G(rails).myNodes[currNode->nodeID]->shortPath = _G(rails).myNodes[i]; // ThePath is a linked list that lists the nodeIDs // FROM: the node leading up to the currNode // TO: the origID (ie. backwards) // Loop through backwards, linking up the new shortPath, and ORing the bitmask prevID = currNode->nodeID; tempPathNode = thePath; while (tempPathNode) { _G(rails).myNodes[tempPathNode->nodeID]->shortPath = _G(rails).myNodes[prevID]; currPathNodes |= (1 << prevID); prevID = tempPathNode->nodeID; tempPathNode = tempPathNode->next; } } else { // Else we don't know whether we can reach the destID from node i // Set the pathWeight of node i to the smaller value, and place it on the stack _G(rails).myNodes[i]->pathWeight = (int16)(currNode->pathWeight + edgeDist); *_G(rails).stackTop++ = _G(rails).myNodes[i]; } } } } } // If we put neighbors of the currNode onto the stack... if (_G(rails).stackTop != checkStackTop) { // Create a pathNode, and place in the list. tempPathNode = (pathNode *)mem_get_from_stash(_G(rails).memtypePATHN, "+PATH"); tempPathNode->nodeID = currNode->nodeID; tempPathNode->next = thePath; thePath = tempPathNode; } else { // Otherwise currNode had no neighbors such that it is not known whether traversing // through it's neighbor is shorter. currNode is either a dead end, or is in the path // Take currNode off the stack top _G(rails).stackTop--; // Set the _G(rails).stackTop to point at the node preceding currNode // we want to check the contents of the stack from the top, and _G(rails).stackTop always point to // the next available location, not directly at the top element. _G(rails).stackTop--; //setup temp Ptr tempPathNode = thePath; // While the top of the stack point at a node directly in thePath - // we remove all the nodes in thePath from the top of the stack until we come across // one that is not in thePath. This one that is not in thePath will not yet have been // checked. while ((tempPathNode) && (tempPathNode->nodeID == (*_G(rails).stackTop)->nodeID)) { _G(rails).stackTop--; thePath = thePath->next; mem_free_to_stash((void *)tempPathNode, _G(rails).memtypePATHN); tempPathNode = thePath; } // Since thePath cannot have more elements than are on the stack, we will never have // a stack underflow, but when the _G(rails).stackTop points to a node not in thePath, or // if the entire thePath was on the stack, thus the stack was completely emptied, // we must reset the _G(rails).stackTop to point to the next available location. _G(rails).stackTop++; } } } // We've completed the enhanced breadth-first search of the rail nodes. return the result if (_G(rails).myNodes[destID]->pathWeight < 32767) { *shortPath = DuplicatePath(_G(rails).myNodes[origID]->shortPath); return true; } else { return false; } } bool intr_PathCrossesLine(int32 startX, int32 startY, railNode *pathStart, int32 line_x1, int32 line_y1, int32 line_x2, int32 line_y2) { bool intersected = false; int32 prevX = startX; int32 prevY = startY; // Loop through the path nodes. Each node is the end of line segment started at (prevX, prevY) railNode *tempNode = pathStart; while (tempNode && (!intersected)) { intersected = intr_LinesCross(line_x1, line_y1, line_x2, line_y2, prevX, prevY, tempNode->x, tempNode->y); prevX = tempNode->x; prevY = tempNode->y; tempNode = tempNode->shortPath; } return intersected; } } // End of namespace M4