mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 13:50:13 +00:00
291 lines
7.0 KiB
C++
291 lines
7.0 KiB
C++
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#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;
|
|
_depthStyle = 0;
|
|
_next = 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; --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 ¤tNode = _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, routeLength + (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.x;
|
|
int xDirection = 1;
|
|
int xAmount = 0;
|
|
if (xDiff < 0) {
|
|
xDiff = -xDiff;
|
|
xDirection = -xDirection;
|
|
xAmount = MIN(yDiff, xDiff);
|
|
}
|
|
|
|
++xDiff;
|
|
++yDiff;
|
|
|
|
const byte *srcP = (const byte *)_depthSurface->getBasePtr(srcPos.x, srcPos.y);
|
|
int index = xAmount;
|
|
|
|
// Outer loop
|
|
for (int xCtr = 0; xCtr < xDiff; ++xCtr, srcP += xDirection) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 = (int)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 minorDiff = 0;
|
|
if (dest.x < src.x)
|
|
minorDiff = MIN(xDiff, yDiff);
|
|
++xDiff;
|
|
++yDiff;
|
|
|
|
byte *srcP = _depthSurface->getBasePtr(src.x, src.y);
|
|
|
|
int totalCtr = minorDiff;
|
|
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;
|
|
}
|
|
|
|
void Rails::synchronize(Common::Serializer &s) {
|
|
s.syncAsSint16LE(_routeLength);
|
|
s.syncAsSint16LE(_next);
|
|
|
|
if (s.isLoading()) {
|
|
_routeIndexes.clear();
|
|
}
|
|
}
|
|
|
|
void Rails::disableNode(int nodeIndex) {
|
|
_nodes[nodeIndex]._active = false;
|
|
|
|
for (uint16 i = 0; i < _nodes.size(); i++) {
|
|
if (i != nodeIndex)
|
|
disableLine(i, nodeIndex);
|
|
}
|
|
}
|
|
|
|
void Rails::disableLine(int from, int to) {
|
|
_nodes[from]._distances[to] = 0x3FFF;
|
|
_nodes[to]._distances[from] = 0x3FFF;
|
|
}
|
|
|
|
} // End of namespace MADS
|