reimplemented createBoxMatrix; this is much cleaner and easier to understand than the original code (IMHO); in a few cases it gives slightly different results (because the old code didn't always find the shortest path), but that shouldn't cause any problems
svn-id: r8403
This commit is contained in:
parent
ab7f8b3378
commit
f034b339cd
1 changed files with 114 additions and 218 deletions
332
scumm/boxes.cpp
332
scumm/boxes.cpp
|
@ -73,17 +73,8 @@ struct Box { /* Internal walkbox file format */
|
|||
#pragma END_PACK_STRUCTS
|
||||
#endif
|
||||
|
||||
struct PathNode { /* Linked list of walkpath nodes */
|
||||
uint index;
|
||||
struct PathNode *left, *right;
|
||||
};
|
||||
|
||||
struct PathVertex { /* Linked list of walkpath nodes */
|
||||
PathNode *left;
|
||||
PathNode *right;
|
||||
};
|
||||
|
||||
#define BOX_MATRIX_SIZE 2000
|
||||
#define BOX_DEBUG 0
|
||||
|
||||
|
||||
static bool compareSlope(int X1, int Y1, int X2, int Y2, int X3, int Y3);
|
||||
|
@ -750,233 +741,138 @@ bool Actor::findPathTowards(byte box1nr, byte box2nr, byte box3nr, int16 &foundP
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
class BoxHeap {
|
||||
int heapSize, heapCurrentIndex;
|
||||
byte *heapCurrent, *heapStart;
|
||||
|
||||
void *getHeapBlock(int size) {
|
||||
byte *ptr = heapCurrent;
|
||||
|
||||
heapCurrent += size;
|
||||
heapCurrentIndex += size;
|
||||
|
||||
if (heapCurrentIndex >= heapSize)
|
||||
error("Box path vertex heap overflow");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public:
|
||||
BoxHeap() {
|
||||
heapSize = 1000;
|
||||
heapStart = heapCurrent = (byte *)calloc(heapSize, 1);
|
||||
heapCurrentIndex = 0;
|
||||
}
|
||||
|
||||
~BoxHeap() {
|
||||
free(heapStart);
|
||||
}
|
||||
|
||||
PathNode *unkMatrixProc2(PathVertex *vtx, int i) {
|
||||
PathNode *node;
|
||||
|
||||
if (vtx == NULL)
|
||||
return NULL;
|
||||
|
||||
node = (PathNode *)getHeapBlock(sizeof(PathNode));
|
||||
node->index = i;
|
||||
node->left = 0;
|
||||
node->right = 0;
|
||||
|
||||
if (!vtx->right) {
|
||||
vtx->left = node;
|
||||
} else {
|
||||
vtx->right->left = node;
|
||||
node->right = vtx->right;
|
||||
#if BOX_DEBUG
|
||||
static void printMatrix(byte *boxm, int num) {
|
||||
int i;
|
||||
for (i = 0; i < num; i++) {
|
||||
printf("%d: ", i);
|
||||
while (*boxm != 0xFF) {
|
||||
printf("%d, ", *boxm);
|
||||
boxm++;
|
||||
}
|
||||
|
||||
vtx->right = node;
|
||||
|
||||
return vtx->right;
|
||||
boxm++;
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
PathVertex *newPathVertex() {
|
||||
// Reset the heap
|
||||
heapCurrent = heapStart;
|
||||
heapCurrentIndex = 0;
|
||||
|
||||
// Add a single new PathVertex to the heap
|
||||
return (PathVertex *)getHeapBlock(sizeof(PathVertex));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class BoxMatrix {
|
||||
int matrixSize;
|
||||
byte *matrixPtr;
|
||||
|
||||
public:
|
||||
BoxMatrix(byte *matrix) {
|
||||
assert(matrix);
|
||||
matrixSize = 0;
|
||||
matrixPtr = matrix;
|
||||
}
|
||||
|
||||
void add(byte b) {
|
||||
if (++matrixSize > BOX_MATRIX_SIZE)
|
||||
error("Box matrix overflow");
|
||||
*matrixPtr++ = b;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
PathVertex *unkMatrixProc1(PathVertex *vtx, PathNode *node) {
|
||||
if (node == NULL || vtx == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!node->right) {
|
||||
vtx->left = node->left;
|
||||
} else {
|
||||
node->right->left = node->left;
|
||||
}
|
||||
|
||||
if (!node->left) {
|
||||
vtx->right = node->right;
|
||||
} else {
|
||||
node->left->right = node->right;
|
||||
}
|
||||
|
||||
if (vtx->left)
|
||||
return vtx;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void printMatrix2(byte *matrix, int num) {
|
||||
int i, j;
|
||||
printf(" ");
|
||||
for (i = 0; i < num; i++)
|
||||
printf("%2d ", i);
|
||||
printf("\n");
|
||||
for (i = 0; i < num; i++) {
|
||||
printf("%2d: ", i);
|
||||
for (j = 0; j < num; j++) {
|
||||
int val = matrix[i * 64 + j];
|
||||
if (val == 250)
|
||||
printf(" ? ");
|
||||
else
|
||||
printf("%2d ", val);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Scumm::createBoxMatrix() {
|
||||
int num, i, j;
|
||||
byte flags;
|
||||
int table_1[66], table_2[66];
|
||||
int counter, val;
|
||||
int code;
|
||||
byte *distanceMatrix;
|
||||
|
||||
|
||||
// A heap (an optiimsation to avoid calling malloc/free extremly often)
|
||||
BoxHeap heap;
|
||||
|
||||
// Temporary 64*65 distance matrix
|
||||
distanceMatrix = (byte *)calloc(65, 64);
|
||||
|
||||
// The result "matrix" in the special format used by Scumm.
|
||||
BoxMatrix boxMatrix(createResource(rtMatrix, 1, BOX_MATRIX_SIZE));
|
||||
int num, i, j, k;
|
||||
byte *adjacentMatrix, *itineraryMatrix;
|
||||
|
||||
// The total number of boxes
|
||||
num = getNumBoxes();
|
||||
assert(num <= 64);
|
||||
|
||||
// Initialise the distance matrix: each box has distance 0 to itself,
|
||||
// Allocate the adjacent & itinerary matrices
|
||||
adjacentMatrix = (byte *)malloc(64 * 64);
|
||||
itineraryMatrix = (byte *)malloc(64 * 64);
|
||||
|
||||
// Initialise the adjacent matrix: each box has distance 0 to itself,
|
||||
// and distance 1 to its direct neighbors. Initially, it has distance
|
||||
// 250 (= infinity) to all other boxes.
|
||||
// 255 (= infinity) to all other boxes.
|
||||
for (i = 0; i < num; i++) {
|
||||
for (j = 0; j < num; j++) {
|
||||
if (i == j) {
|
||||
distanceMatrix[i * 64 + j] = 0;
|
||||
adjacentMatrix[i * 64 + j] = 0;
|
||||
itineraryMatrix[i * 64 + j] = j;
|
||||
} else if (areBoxesNeighbours(i, j)) {
|
||||
distanceMatrix[i * 64 + j] = 1;
|
||||
adjacentMatrix[i * 64 + j] = 1;
|
||||
itineraryMatrix[i * 64 + j] = j;
|
||||
} else {
|
||||
distanceMatrix[i * 64 + j] = 250;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all boxes
|
||||
for (j = 0; j < num; j++) {
|
||||
flags = getBoxFlags(j);
|
||||
if (flags & kBoxInvisible) {
|
||||
// Locked/invisible boxes are only reachable from themselves.
|
||||
boxMatrix.add(0xFF);
|
||||
boxMatrix.add(j);
|
||||
boxMatrix.add(j);
|
||||
boxMatrix.add(j);
|
||||
} else {
|
||||
PathNode *node, *node2 = NULL;
|
||||
PathVertex *vtx = heap.newPathVertex();
|
||||
for (i = 0; i < num; i++) {
|
||||
flags = getBoxFlags(j);
|
||||
if (!(flags & kBoxInvisible)) {
|
||||
node = heap.unkMatrixProc2(vtx, i);
|
||||
if (i == j)
|
||||
node2 = node;
|
||||
}
|
||||
}
|
||||
table_1[j] = 0;
|
||||
table_2[j] = j;
|
||||
vtx = unkMatrixProc1(vtx, node2);
|
||||
node = vtx ? vtx->left : NULL;
|
||||
|
||||
counter = 250;
|
||||
while (node) {
|
||||
val = distanceMatrix[j * 64 + node->index];
|
||||
table_1[node->index] = val;
|
||||
if (val < counter)
|
||||
counter = val;
|
||||
|
||||
if (table_1[node->index] != 250)
|
||||
table_2[node->index] = node->index;
|
||||
else
|
||||
table_2[node->index] = -1;
|
||||
|
||||
node = node->left;
|
||||
}
|
||||
|
||||
while (vtx) {
|
||||
counter = 250;
|
||||
node2 = node = vtx->left;
|
||||
|
||||
while (node) {
|
||||
if (table_1[node->index] < counter) {
|
||||
counter = table_1[node->index];
|
||||
node2 = node;
|
||||
}
|
||||
node = node->left;
|
||||
}
|
||||
vtx = unkMatrixProc1(vtx, node2);
|
||||
node = vtx ? vtx->left : NULL;
|
||||
while (node) {
|
||||
code = distanceMatrix[node2->index * 64 + node->index];
|
||||
code += table_1[node2->index];
|
||||
if (code < table_1[node->index]) {
|
||||
table_1[node->index] = code;
|
||||
table_2[node->index] = table_2[node2->index];
|
||||
}
|
||||
node = node->left;
|
||||
}
|
||||
}
|
||||
|
||||
boxMatrix.add(0xFF);
|
||||
for (i = 1; i < num; i++) {
|
||||
if (table_2[i - 1] != -1) {
|
||||
boxMatrix.add(i - 1); /* lo */
|
||||
while (table_2[i - 1] == table_2[i]) {
|
||||
++i;
|
||||
if (i == num)
|
||||
break;
|
||||
}
|
||||
boxMatrix.add(i - 1); /* hi */
|
||||
boxMatrix.add(table_2[i - 1]); /* dst */
|
||||
}
|
||||
}
|
||||
if (i == num && table_2[i - 1] != -1) {
|
||||
boxMatrix.add(i - 1); /* lo */
|
||||
boxMatrix.add(i - 1); /* hi */
|
||||
boxMatrix.add(table_2[i - 1]); /* dest */
|
||||
adjacentMatrix[i * 64 + j] = 255;
|
||||
itineraryMatrix[i * 64 + j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boxMatrix.add(0xFF);
|
||||
// Compute the shortest routes between boxes via Kleene's algorithm.
|
||||
// The original code used some kind of mangled Dijkstra's algorithm;
|
||||
// while that might in theory be slightly faster, it was
|
||||
// a) extremly obfuscated
|
||||
// b) incorrect: it didn't always find the shortest paths
|
||||
// c) not any faster in reality for our sparse & small adjacent matrices
|
||||
for (k = 1; k < num; k++) {
|
||||
for (i = 1; i < num; i++) {
|
||||
for (j = 1; j < num; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
byte distIK = adjacentMatrix[64 * i + k];
|
||||
byte distKJ = adjacentMatrix[64 * k + j];
|
||||
if (adjacentMatrix[64 * i + j] > distIK + distKJ) {
|
||||
adjacentMatrix[64 * i + j] = distIK + distKJ;
|
||||
itineraryMatrix[64 * i + j] = itineraryMatrix[64 * i + k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(distanceMatrix);
|
||||
}
|
||||
|
||||
// "Compress" the distance matrix into the box matrix format used
|
||||
// by the engine. The format is like this:
|
||||
// For each box (from 0 to num) there is first a byte with value 0xFF,
|
||||
// followed by an arbitrary number of byte triples; the end is marked
|
||||
// again by the lead 0xFF for the next "row". The meaning of the
|
||||
// byte triples is as follows: the first two bytes define a range
|
||||
// of box numbers (e.g. 7-11), while the third byte defines an
|
||||
// itineray box. Assuming we are in the 5th "row" and encounter
|
||||
// the triplet 7,11,15: this means to get from box 5 to any of
|
||||
// the boxes 7,8,9,10,11 the shortest way is to go via box 15.
|
||||
// See also getPathToDestBox.
|
||||
|
||||
byte *matrixStart = createResource(rtMatrix, 1, BOX_MATRIX_SIZE);
|
||||
const byte *matrixEnd = matrixStart + BOX_MATRIX_SIZE;
|
||||
|
||||
#define addToMatrix(b) do { *matrixStart++ = (b); assert(matrixStart < matrixEnd); } while (0)
|
||||
|
||||
addToMatrix(0xFF);
|
||||
addToMatrix(0);
|
||||
addToMatrix(0);
|
||||
addToMatrix(0);
|
||||
for (i = 1; i < num; i++) {
|
||||
addToMatrix(0xFF);
|
||||
for (j = 1; j < num; j++) {
|
||||
byte itinerary = itineraryMatrix[64 * i + j];
|
||||
if (itinerary != 0) {
|
||||
addToMatrix(j);
|
||||
while (j < num && itinerary == itineraryMatrix[64 * i + (j + 1)])
|
||||
j++;
|
||||
addToMatrix(j);
|
||||
addToMatrix(itinerary);
|
||||
}
|
||||
}
|
||||
}
|
||||
addToMatrix(0xFF);
|
||||
|
||||
|
||||
#if BOX_DEBUG
|
||||
printf("Itinerary matrix:\n");
|
||||
printMatrix2(itineraryMatrix, num);
|
||||
printf("compressed matrix:\n");
|
||||
printMatrix(getBoxMatrixBaseAddr(), num);
|
||||
#endif
|
||||
|
||||
free(adjacentMatrix);
|
||||
free(itineraryMatrix);
|
||||
}
|
||||
|
||||
/** Check if two boxes are neighbours. */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue