aboutsummaryrefslogtreecommitdiff
path: root/engines/mads/rails.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mads/rails.cpp')
-rw-r--r--engines/mads/rails.cpp269
1 files changed, 269 insertions, 0 deletions
diff --git a/engines/mads/rails.cpp b/engines/mads/rails.cpp
new file mode 100644
index 0000000000..0baaa12793
--- /dev/null
+++ b/engines/mads/rails.cpp
@@ -0,0 +1,269 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "mads/mads.h"
+#include "mads/rails.h"
+
+namespace MADS {
+
+WalkNode::WalkNode() {
+ _active = false;
+ Common::fill(&_distances[0], &_distances[MAX_ROUTE_NODES], 0);
+}
+
+void WalkNode::load(Common::SeekableReadStream *f) {
+ _walkPos.x = f->readSint16LE();
+ _walkPos.y = f->readSint16LE();
+ for (int i = 0; i < MAX_ROUTE_NODES; ++i)
+ _distances[i] = f->readUint16LE();
+}
+
+/*------------------------------------------------------------------------*/
+
+Rails::Rails() {
+ _depthSurface = nullptr;
+ _routeLength = 0;
+}
+
+void Rails::load(const WalkNodeList &nodes, DepthSurface *depthSurface, int depthStyle) {
+ // Store the depth surface and depth style to use
+ _depthSurface = depthSurface;
+ _depthStyle = depthStyle;
+
+ // Load the passed node list
+ _nodes.clear();
+
+ for (uint i = 0; i < nodes.size(); ++i)
+ _nodes.push_back(nodes[i]);
+
+ // Add two more empty nodes for the start and end points of any walk sequence
+ _nodes.push_back(WalkNode());
+ _nodes.push_back(WalkNode());
+}
+
+
+void Rails::setupRoute(bool bitFlag, const Common::Point &srcPos, const Common::Point &destPos) {
+ // Reset the nodes in as being inactive
+ for (uint i = 0; i < _nodes.size(); ++i)
+ _nodes[i]._active = false;
+
+ // Set the two extra walk nodes to the start and destination positions
+ setNodePosition(_nodes.size() - 2, srcPos);
+ setNodePosition(_nodes.size() - 1, destPos);
+
+ // Start constructing route node list
+ _routeLength = 0x3FFF;
+ _routeIndexes.clear();
+
+ // Recursively form a route from the destination walk node back to the player's position
+ setupRouteNode(&_tempRoute[0], _nodes.size() - 1, bitFlag ? 0xC000 : 0x8000, 0);
+
+ _next = 0;
+ if (_routeIndexes.size() > 0) {
+ Common::Point currPos = srcPos;
+ for (int routeCtr = size() - 1; (routeCtr >= 0) && (_next == 0); --routeCtr) {
+ int idx = _routeIndexes[routeCtr];
+ const Common::Point &pt = _nodes[idx]._walkPos;
+
+ _next = scanPath(currPos, pt);
+ currPos = pt;
+ }
+ }
+}
+
+void Rails::setupRouteNode(int *routeIndexP, int nodeIndex, int flags, int routeLength) {
+ WalkNode &currentNode = _nodes[nodeIndex];
+ currentNode._active = true;
+
+ *routeIndexP++ = nodeIndex;
+
+ // Get the index of the ultimate source position (the player)
+ int subIndex = _nodes.size() - 2;
+
+ int distanceVal = _nodes[nodeIndex]._distances[subIndex];
+ if (distanceVal & flags) {
+ routeLength += distanceVal & 0x3FFF;
+ if (routeLength < _routeLength) {
+ // Found a new shorter route to destination, so set up the route with the found one
+ _routeIndexes.clear();
+ for (int i = 0; routeIndexP != &_tempRoute[i]; ++i)
+ _routeIndexes.push(_tempRoute[i]);
+ _routeLength = routeLength;
+ }
+ } else {
+ for (int idx = _nodes.size() - 2; idx > 0; --idx) {
+ int nodePos = idx - 1;
+ if (!_nodes[nodePos]._active && ((currentNode._distances[nodePos] & flags) != 0))
+ setupRouteNode(routeIndexP, nodePos, 0x8000, distanceVal & 0x3fff);
+ }
+ }
+
+ currentNode._active = false;
+}
+
+
+int Rails::scanPath(const Common::Point &srcPos, const Common::Point &destPos) {
+ // For compressed depth surfaces, always return 0
+ if (_depthStyle != 2)
+ return 0;
+
+ int yDiff = destPos.y - srcPos.y;
+ int yAmount = MADS_SCREEN_WIDTH;
+
+ if (yDiff < 0) {
+ yDiff = -yDiff;
+ yAmount = -yAmount;
+ }
+
+ int xDiff = destPos.x - srcPos.y;
+ int xDirection = 1;
+ int xAmount = 0;
+ if (xDiff < 0) {
+ xDiff = -xDiff;
+ xDirection = -xDirection;
+ xAmount = MIN(yDiff, xDiff);
+ }
+
+ ++xDiff;
+ ++yDiff;
+
+ const byte *srcP = _depthSurface->getBasePtr(srcPos.x, srcPos.y);
+ int index = xAmount;
+
+ // Outer horizontal movement loop
+ for (int yIndex = 0; yIndex < yDiff; ++yIndex) {
+ index += yDiff;
+ int v = (*srcP & 0x7F) >> 4;
+ if (v)
+ return v;
+
+ // Inner loop for handling vertical movement
+ while (index >= xDiff) {
+ index -= xDiff;
+
+ v = (*srcP & 0x7F) >> 4;
+ if (v)
+ return v;
+
+ srcP += yAmount;
+ }
+
+ srcP += xDirection;
+ }
+
+ return 0;
+}
+
+void Rails::resetRoute() {
+ _routeIndexes.clear();
+ _next = 0;
+}
+
+const WalkNode &Rails::popNode() {
+ assert(!_routeIndexes.empty());
+
+ return _nodes[_routeIndexes.pop()];
+}
+
+void Rails::setNodePosition(int nodeIndex, const Common::Point &pt) {
+ int flags, hypotenuse;
+
+ _nodes[nodeIndex]._walkPos = pt;
+
+ // Recalculate inter-node lengths
+ for (uint idx = 0; idx < _nodes.size(); ++idx) {
+ int entry;
+ if (idx == (uint)nodeIndex) {
+ entry = 0x3FFF;
+ }
+ else {
+ // Process the node
+ flags = getRouteFlags(pt, _nodes[idx]._walkPos);
+
+ int xDiff = ABS(_nodes[idx]._walkPos.x - pt.x);
+ int yDiff = ABS(_nodes[idx]._walkPos.y - pt.y);
+ hypotenuse = sqrt((double)(xDiff * xDiff + yDiff * yDiff));
+
+ if (hypotenuse >= 0x3FFF)
+ // Shouldn't ever be this large
+ hypotenuse = 0x3FFF;
+
+ entry = hypotenuse | flags;
+ _nodes[idx]._distances[nodeIndex] = entry;
+ _nodes[nodeIndex]._distances[idx] = entry;
+ }
+ }
+}
+
+int Rails::getRouteFlags(const Common::Point &src, const Common::Point &dest) {
+ int result = 0x8000;
+ bool flag = false;
+
+ int xDiff = ABS(dest.x - src.x);
+ int yDiff = ABS(dest.y - src.y);
+ int xDirection = dest.x >= src.x ? 1 : -1;
+ int yDirection = dest.y >= src.y ? _depthSurface->w : -_depthSurface->w;
+ int majorDiff = 0;
+ if (dest.x < src.x)
+ majorDiff = MAX(xDiff, yDiff);
+ ++xDiff;
+ ++yDiff;
+
+ byte *srcP = _depthSurface->getBasePtr(src.x, src.y);
+
+ int totalCtr = majorDiff;
+ for (int xCtr = 0; xCtr < xDiff; ++xCtr, srcP += xDirection) {
+ totalCtr += yDiff;
+
+ if ((*srcP & 0x80) == 0)
+ flag = false;
+ else if (!flag) {
+ flag = true;
+ result -= 0x4000;
+ if (result == 0)
+ break;
+ }
+
+ while (totalCtr >= xDiff) {
+ totalCtr -= xDiff;
+
+ if ((*srcP & 0x80) == 0)
+ flag = false;
+ else if (!flag) {
+ flag = true;
+ result -= 0x4000;
+ if (result == 0)
+ break;
+ }
+
+ srcP += yDirection;
+ }
+ if (result == 0)
+ break;
+ }
+
+ return result;
+}
+
+
+} // End of namespace MADS