scummvm/scumm/boxes.cpp

1087 lines
24 KiB
C++
Raw Normal View History

/* ScummVM - Scumm Interpreter
2001-10-09 14:30:12 +00:00
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001/2002 The ScummVM project
2001-10-09 14:30:12 +00:00
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
2001-10-09 14:30:12 +00:00
*
*/
#include "stdafx.h"
#include "scumm.h"
#include "actor.h"
#include "common/util.h"
#include <math.h>
2001-10-09 14:30:12 +00:00
#if !defined(__GNUC__)
#pragma START_PACK_STRUCTS
#endif
struct Box { /* Internal walkbox file format */
union {
struct {
int16 ulx, uly;
int16 urx, ury;
int16 lrx, lry;
int16 llx, lly;
byte mask;
byte flags;
uint16 scale;
} GCC_PACK old;
struct {
int32 ulx, uly;
int32 urx, ury;
int32 lrx, lry;
int32 llx, lly;
uint32 mask; // FIXME - is 'mask' really here?
uint32 flags; // FIXME - is 'flags' really here?
uint32 unk1;
uint32 scale;
uint32 unk2;
uint32 unk3;
} GCC_PACK v8;
} GCC_PACK;
} GCC_PACK;
#if !defined(__GNUC__)
#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
PathVertex *unkMatrixProc1(PathVertex *vtx, PathNode *node);
byte Scumm::getMaskFromBox(int box)
{
2001-10-09 14:30:12 +00:00
Box *ptr = getBoxBaseAddr(box);
if (!ptr)
return 0;
if (_features & GF_AFTER_V8)
2002-12-30 14:22:23 +00:00
return (byte) FROM_LE_32(ptr->v8.mask);
else
return ptr->old.mask;
2001-10-09 14:30:12 +00:00
}
void Scumm::setBoxFlags(int box, int val)
{
debug(2, "setBoxFlags(%d, 0x%02x)", box, val);
/* FULL_THROTTLE stuff */
if (val & 0xC000) {
assert(box >= 0 && box < 65);
_extraBoxFlags[box] = val;
} else {
Box *b = getBoxBaseAddr(box);
assert(b);
if (_features & GF_AFTER_V8)
b->v8.flags = TO_LE_32(val);
else
b->old.flags = val;
}
}
byte Scumm::getBoxFlags(int box)
{
2001-10-09 14:30:12 +00:00
Box *ptr = getBoxBaseAddr(box);
if (!ptr)
return 0;
if (_features & GF_AFTER_V8)
2002-12-30 14:22:23 +00:00
return (byte) FROM_LE_32(ptr->v8.flags);
else
return ptr->old.flags;
2001-10-09 14:30:12 +00:00
}
void Scumm::setBoxScale(int box, int scale)
{
Box *b = getBoxBaseAddr(box);
if (_features & GF_AFTER_V8)
b->v8.scale = TO_LE_32(scale);
else
b->old.scale = TO_LE_16(scale);
}
int Scumm::getBoxScale(int box)
{
if (_features & GF_NO_SCALLING)
return 255;
2001-10-09 14:30:12 +00:00
Box *ptr = getBoxBaseAddr(box);
if (!ptr)
return 255;
if (_features & GF_AFTER_V8)
return FROM_LE_32(ptr->v8.scale);
else
return FROM_LE_16(ptr->old.scale);
2001-10-09 14:30:12 +00:00
}
byte Scumm::getNumBoxes()
{
byte *ptr = getResourceAddress(rtMatrix, 2);
if (!ptr)
return 0;
if (_features & GF_AFTER_V8)
2002-12-30 14:22:23 +00:00
return (byte) READ_LE_UINT32(ptr);
else
return ptr[0];
2001-10-09 14:30:12 +00:00
}
Box *Scumm::getBoxBaseAddr(int box)
{
byte *ptr = getResourceAddress(rtMatrix, 2);
if (!ptr)
return NULL;
checkRange(ptr[0] - 1, 0, box, "Illegal box %d");
if (_features & GF_SMALL_HEADER) {
if (_features & GF_OLD256)
2002-07-07 20:25:23 +00:00
return (Box *)(ptr + box * (SIZEOF_BOX - 2) + 1);
2002-02-14 18:20:22 +00:00
else
2002-07-07 20:25:23 +00:00
return (Box *)(ptr + box * SIZEOF_BOX + 1);
} else if (_features & GF_AFTER_V8) {
return (Box *)(ptr + box * 52 + 4);
} else
2002-07-07 20:25:23 +00:00
return (Box *)(ptr + box * SIZEOF_BOX + 2);
2001-10-09 14:30:12 +00:00
}
int Scumm::getSpecialBox(int x, int y)
{
int i;
int numOfBoxes;
byte flag;
2002-07-07 20:25:23 +00:00
numOfBoxes = getNumBoxes() - 1;
2002-07-07 20:25:23 +00:00
for (i = numOfBoxes; i >= 0; i--) {
flag = getBoxFlags(i);
if (!(flag & kBoxInvisible) && (flag & kBoxPlayerOnly))
2002-07-07 20:25:23 +00:00
return (-1);
if (checkXYInBoxBounds(i, x, y))
2002-07-07 20:25:23 +00:00
return (i);
}
2002-07-07 20:25:23 +00:00
return (-1);
}
bool Scumm::checkXYInBoxBounds(int b, int x, int y)
{
BoxCoords box;
if (b == 0 && (!(_features & GF_SMALL_HEADER)))
2002-08-12 16:05:14 +00:00
return false;
2001-10-09 14:30:12 +00:00
getBoxCoordinates(b, &box);
2001-10-09 14:30:12 +00:00
if (x < box.ul.x && x < box.ur.x && x < box.lr.x && x < box.ll.x)
2002-08-12 16:05:14 +00:00
return false;
if (x > box.ul.x && x > box.ur.x && x > box.lr.x && x > box.ll.x)
2002-08-12 16:05:14 +00:00
return false;
if (y < box.ul.y && y < box.ur.y && y < box.lr.y && y < box.ll.y)
2002-08-12 16:05:14 +00:00
return false;
if (y > box.ul.y && y > box.ur.y && y > box.lr.y && y > box.ll.y)
2002-08-12 16:05:14 +00:00
return false;
if (box.ul.x == box.ur.x && box.ul.y == box.ur.y && box.lr.x == box.ll.x && box.lr.y == box.ll.y ||
box.ul.x == box.ll.x && box.ul.y == box.ll.y && box.ur.x == box.lr.x && box.ur.y == box.lr.y) {
2001-10-09 14:30:12 +00:00
ScummPoint pt;
pt = closestPtOnLine(box.ul.x, box.ul.y, box.lr.x, box.lr.y, x, y);
if (distanceFromPt(x, y, pt.x, pt.y) <= 4)
2002-08-12 16:05:14 +00:00
return true;
2001-10-09 14:30:12 +00:00
}
if (!compareSlope(box.ul.x, box.ul.y, box.ur.x, box.ur.y, x, y))
2002-08-12 16:05:14 +00:00
return false;
if (!compareSlope(box.ur.x, box.ur.y, box.lr.x, box.lr.y, x, y))
2002-08-12 16:05:14 +00:00
return false;
if (!compareSlope(box.ll.x, box.ll.y, x, y, box.lr.x, box.lr.y))
2002-08-12 16:05:14 +00:00
return false;
if (!compareSlope(box.ul.x, box.ul.y, x, y, box.ll.x, box.ll.y))
2002-08-12 16:05:14 +00:00
return false;
2001-10-09 14:30:12 +00:00
2002-08-12 16:05:14 +00:00
return true;
2001-10-09 14:30:12 +00:00
}
2002-07-07 20:25:23 +00:00
void Scumm::getBoxCoordinates(int boxnum, BoxCoords *box)
{
Box *bp = getBoxBaseAddr(boxnum);
if (_features & GF_AFTER_V8) {
box->ul.x = (int32)FROM_LE_32(bp->v8.ulx);
box->ul.y = (int32)FROM_LE_32(bp->v8.uly);
box->ur.x = (int32)FROM_LE_32(bp->v8.urx);
box->ur.y = (int32)FROM_LE_32(bp->v8.ury);
box->ll.x = (int32)FROM_LE_32(bp->v8.llx);
box->ll.y = (int32)FROM_LE_32(bp->v8.lly);
box->lr.x = (int32)FROM_LE_32(bp->v8.lrx);
box->lr.y = (int32)FROM_LE_32(bp->v8.lry);
} else {
box->ul.x = (int16)FROM_LE_16(bp->old.ulx);
box->ul.y = (int16)FROM_LE_16(bp->old.uly);
box->ur.x = (int16)FROM_LE_16(bp->old.urx);
box->ur.y = (int16)FROM_LE_16(bp->old.ury);
box->ll.x = (int16)FROM_LE_16(bp->old.llx);
box->ll.y = (int16)FROM_LE_16(bp->old.lly);
box->lr.x = (int16)FROM_LE_16(bp->old.lrx);
box->lr.y = (int16)FROM_LE_16(bp->old.lry);
}
2001-10-09 14:30:12 +00:00
}
uint Scumm::distanceFromPt(int x, int y, int ptx, int pty)
{
2001-10-09 14:30:12 +00:00
int diffx, diffy;
diffx = abs(ptx - x);
2001-10-09 14:30:12 +00:00
if (diffx >= 0x100)
return 0xFFFF;
diffy = abs(pty - y);
2001-10-09 14:30:12 +00:00
if (diffy >= 0x100)
return 0xFFFF;
diffx *= diffx;
diffy *= diffy;
return diffx + diffy;
}
2002-07-07 20:25:23 +00:00
ScummPoint Scumm::closestPtOnLine(int ulx, int uly, int llx, int lly, int x, int y)
{
int lydiff, lxdiff;
int32 dist, a, b, c;
int x2, y2;
ScummPoint pt;
2001-10-09 14:30:12 +00:00
if (llx == ulx) { // Vertical line?
2001-10-09 14:30:12 +00:00
x2 = ulx;
y2 = y;
} else if (lly == uly) { // Horizontal line?
2001-10-09 14:30:12 +00:00
x2 = x;
y2 = uly;
} else {
lydiff = lly - uly;
lxdiff = llx - ulx;
if (abs(lxdiff) > abs(lydiff)) {
dist = lxdiff * lxdiff + lydiff * lydiff;
a = ulx * lydiff / lxdiff;
b = x * lxdiff / lydiff;
c = (a + b - uly + y) * lydiff * lxdiff / dist;
x2 = c;
y2 = c * lydiff / lxdiff - a + uly;
} else {
dist = lydiff * lydiff + lxdiff * lxdiff;
a = uly * lxdiff / lydiff;
b = y * lydiff / lxdiff;
2001-10-09 14:30:12 +00:00
c = (a + b - ulx + x) * lydiff * lxdiff / dist;
y2 = c;
x2 = c * lxdiff / lydiff - a + ulx;
}
}
lxdiff = llx - ulx;
lydiff = lly - uly;
if (abs(lydiff) < abs(lxdiff)) {
if (lxdiff > 0) {
if (x2 < ulx) {
type1:;
2001-10-09 14:30:12 +00:00
x2 = ulx;
y2 = uly;
} else if (x2 > llx) {
type2:;
2001-10-09 14:30:12 +00:00
x2 = llx;
y2 = lly;
}
} else {
if (x2 > ulx)
goto type1;
if (x2 < llx)
goto type2;
2001-10-09 14:30:12 +00:00
}
} else {
if (lydiff > 0) {
if (y2 < uly)
goto type1;
if (y2 > lly)
goto type2;
2001-10-09 14:30:12 +00:00
} else {
if (y2 > uly)
goto type1;
if (y2 < lly)
goto type2;
2001-10-09 14:30:12 +00:00
}
}
pt.x = x2;
pt.y = y2;
return pt;
}
bool Scumm::inBoxQuickReject(int b, int x, int y, int threshold)
{
2001-10-09 14:30:12 +00:00
int t;
BoxCoords box;
2001-10-09 14:30:12 +00:00
getBoxCoordinates(b, &box);
2001-10-09 14:30:12 +00:00
if (threshold == 0)
2002-08-12 16:05:14 +00:00
return true;
2001-10-09 14:30:12 +00:00
t = x - threshold;
if (t > box.ul.x && t > box.ur.x && t > box.lr.x && t > box.ll.x)
2002-08-12 16:05:14 +00:00
return false;
2001-10-09 14:30:12 +00:00
t = x + threshold;
if (t < box.ul.x && t < box.ur.x && t < box.lr.x && t < box.ll.x)
2002-08-12 16:05:14 +00:00
return false;
2001-10-09 14:30:12 +00:00
t = y - threshold;
if (t > box.ul.y && t > box.ur.y && t > box.lr.y && t > box.ll.y)
2002-08-12 16:05:14 +00:00
return false;
2001-10-09 14:30:12 +00:00
t = y + threshold;
if (t < box.ul.y && t < box.ur.y && t < box.lr.y && t < box.ll.y)
2002-08-12 16:05:14 +00:00
return false;
2001-10-09 14:30:12 +00:00
2002-08-12 16:05:14 +00:00
return true;
2001-10-09 14:30:12 +00:00
}
AdjustBoxResult Scumm::getClosestPtOnBox(int b, int x, int y)
{
ScummPoint pt;
2001-10-09 14:30:12 +00:00
AdjustBoxResult best;
uint dist;
uint bestdist = (uint) 0xFFFF;
BoxCoords box;
2001-10-09 14:30:12 +00:00
getBoxCoordinates(b, &box);
2001-10-09 14:30:12 +00:00
pt = closestPtOnLine(box.ul.x, box.ul.y, box.ur.x, box.ur.y, x, y);
2001-10-09 14:30:12 +00:00
dist = distanceFromPt(x, y, pt.x, pt.y);
if (dist < bestdist) {
bestdist = dist;
best.x = pt.x;
best.y = pt.y;
}
pt = closestPtOnLine(box.ur.x, box.ur.y, box.lr.x, box.lr.y, x, y);
2001-10-09 14:30:12 +00:00
dist = distanceFromPt(x, y, pt.x, pt.y);
if (dist < bestdist) {
bestdist = dist;
best.x = pt.x;
best.y = pt.y;
}
pt = closestPtOnLine(box.lr.x, box.lr.y, box.ll.x, box.ll.y, x, y);
2001-10-09 14:30:12 +00:00
dist = distanceFromPt(x, y, pt.x, pt.y);
if (dist < bestdist) {
bestdist = dist;
best.x = pt.x;
best.y = pt.y;
}
pt = closestPtOnLine(box.ll.x, box.ll.y, box.ul.x, box.ul.y, x, y);
2001-10-09 14:30:12 +00:00
dist = distanceFromPt(x, y, pt.x, pt.y);
if (dist < bestdist) {
bestdist = dist;
best.x = pt.x;
best.y = pt.y;
}
2001-10-09 14:30:12 +00:00
best.dist = bestdist;
return best;
}
byte *Scumm::getBoxMatrixBaseAddr()
{
byte *ptr = getResourceAddress(rtMatrix, 1);
if (*ptr == 0xFF)
ptr++;
2001-10-09 14:30:12 +00:00
return ptr;
}
2002-08-12 16:05:14 +00:00
/*
* Compute if there is a way that connects box 'from' with box 'to'.
* Returns the number of a box adjactant to 'from' that is the next on the
* way to 'to' (this can be 'to' itself or a third box).
* If there is no connection -1 is return.
*/
int Scumm::getPathToDestBox(byte from, byte to)
{
2001-10-09 14:30:12 +00:00
byte *boxm;
byte i;
int dest = -1;
2001-10-09 14:30:12 +00:00
if (from == to)
2001-10-09 14:30:12 +00:00
return to;
2001-10-09 14:30:12 +00:00
boxm = getBoxMatrixBaseAddr();
i = 0;
2001-10-09 14:30:12 +00:00
while (i != from) {
while (*boxm != 0xFF)
boxm += 3;
i++;
boxm++;
}
while (boxm[0] != 0xFF) {
2002-12-29 21:14:28 +00:00
if (boxm[0] <= to && to <= boxm[1])
dest = boxm[2];
boxm += 3;
2001-10-09 14:30:12 +00:00
}
return dest;
2001-10-09 14:30:12 +00:00
}
2002-08-12 16:05:14 +00:00
/*
* Computes the next point actor a has to walk towards in a straight
* line in order to get from box1 to box3 via box2.
*/
bool Scumm::findPathTowards(Actor *a, byte box1nr, byte box2nr, byte box3nr, int16 &foundPathX, int16 &foundPathY)
{
BoxCoords box1;
BoxCoords box2;
ScummPoint tmp;
int i, j;
int flag;
int q, pos;
getBoxCoordinates(box1nr, &box1);
getBoxCoordinates(box2nr, &box2);
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
2002-07-07 20:25:23 +00:00
if (box1.ul.x == box1.ur.x && box1.ul.x == box2.ul.x && box1.ul.x == box2.ur.x) {
flag = 0;
if (box1.ul.y > box1.ur.y) {
SWAP(box1.ul.y, box1.ur.y);
flag |= 1;
2001-10-09 14:30:12 +00:00
}
if (box2.ul.y > box2.ur.y) {
SWAP(box2.ul.y, box2.ur.y);
flag |= 2;
2001-10-09 14:30:12 +00:00
}
if (box1.ul.y > box2.ur.y || box2.ul.y > box1.ur.y ||
(box1.ur.y == box2.ul.y || box2.ur.y == box1.ul.y) &&
box1.ul.y != box1.ur.y && box2.ul.y != box2.ur.y) {
if (flag & 1)
SWAP(box1.ul.y, box1.ur.y);
if (flag & 2)
SWAP(box2.ul.y, box2.ur.y);
} else {
2002-08-12 16:05:14 +00:00
pos = a->y;
if (box2nr == box3nr) {
int diffX = a->walkdata.destx - a->x;
int diffY = a->walkdata.desty - a->y;
int boxDiffX = box1.ul.x - a->x;
if (diffX != 0) {
int t;
diffY *= boxDiffX;
t = diffY / diffX;
if (t == 0 && (diffY <= 0 || diffX <= 0)
&& (diffY >= 0 || diffX >= 0))
t = -1;
pos = a->y + t;
2001-10-09 14:30:12 +00:00
}
}
q = pos;
if (q < box2.ul.y)
q = box2.ul.y;
if (q > box2.ur.y)
q = box2.ur.y;
if (q < box1.ul.y)
q = box1.ul.y;
if (q > box1.ur.y)
q = box1.ur.y;
if (q == pos && box2nr == box3nr)
2002-08-12 16:05:14 +00:00
return true;
foundPathY = q;
foundPathX = box1.ul.x;
return false;
2001-10-09 14:30:12 +00:00
}
}
2002-07-07 20:25:23 +00:00
if (box1.ul.y == box1.ur.y && box1.ul.y == box2.ul.y && box1.ul.y == box2.ur.y) {
flag = 0;
if (box1.ul.x > box1.ur.x) {
SWAP(box1.ul.x, box1.ur.x);
flag |= 1;
2001-10-09 14:30:12 +00:00
}
if (box2.ul.x > box2.ur.x) {
SWAP(box2.ul.x, box2.ur.x);
flag |= 2;
2001-10-09 14:30:12 +00:00
}
if (box1.ul.x > box2.ur.x || box2.ul.x > box1.ur.x ||
(box1.ur.x == box2.ul.x || box2.ur.x == box1.ul.x) &&
box1.ul.x != box1.ur.x && box2.ul.x != box2.ur.x) {
if (flag & 1)
SWAP(box1.ul.x, box1.ur.x);
if (flag & 2)
SWAP(box2.ul.x, box2.ur.x);
} else {
if (box2nr == box3nr) {
int diffX = a->walkdata.destx - a->x;
int diffY = a->walkdata.desty - a->y;
int boxDiffY = box1.ul.y - a->y;
pos = a->x;
if (diffY != 0) {
pos += diffX * boxDiffY / diffY;
2001-10-09 14:30:12 +00:00
}
} else {
pos = a->x;
2001-10-09 14:30:12 +00:00
}
q = pos;
if (q < box2.ul.x)
q = box2.ul.x;
if (q > box2.ur.x)
q = box2.ur.x;
if (q < box1.ul.x)
q = box1.ul.x;
if (q > box1.ur.x)
q = box1.ur.x;
if (q == pos && box2nr == box3nr)
2002-08-12 16:05:14 +00:00
return true;
foundPathX = q;
foundPathY = box1.ul.y;
return false;
2001-10-09 14:30:12 +00:00
}
}
tmp = box1.ul;
box1.ul = box1.ur;
box1.ur = box1.lr;
box1.lr = box1.ll;
box1.ll = tmp;
2001-10-09 14:30:12 +00:00
}
tmp = box2.ul;
box2.ul = box2.ur;
box2.ur = box2.lr;
box2.lr = box2.ll;
box2.ll = tmp;
}
2002-08-12 16:05:14 +00:00
return false;
2001-10-09 14:30:12 +00:00
}
2002-08-12 16:05:14 +00:00
void Scumm::createBoxMatrix()
{
int num, i, j;
2001-10-09 14:30:12 +00:00
byte flags;
int table_1[66], table_2[66];
int counter, val;
2001-10-09 14:30:12 +00:00
int code;
2002-12-29 21:14:28 +00:00
// A heap (an optiimsation to avoid calling malloc/free extremly often)
2001-10-09 14:30:12 +00:00
_maxBoxVertexHeap = 1000;
2002-12-29 21:14:28 +00:00
createResource(rtMatrix, 4, _maxBoxVertexHeap);
_boxPathVertexHeap = getResourceAddress(rtMatrix, 4);
_boxPathVertexHeapIndex = _boxMatrixItem = 0;
2002-12-29 21:14:28 +00:00
// Temporary 64*65 distance matrix
createResource(rtMatrix, 3, 65 * 64);
_boxMatrixPtr3 = getResourceAddress(rtMatrix, 3);
// The result "matrix" in the special format used by Scumm.
createResource(rtMatrix, 1, BOX_MATRIX_SIZE);
_boxMatrixPtr1 = getResourceAddress(rtMatrix, 1);
2001-10-09 14:30:12 +00:00
num = getNumBoxes();
2002-12-29 21:14:28 +00:00
// Initialise the distance 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.
for (i = 0; i < num; i++) {
for (j = 0; j < num; j++) {
if (i == j) {
_boxMatrixPtr3[i * 64 + j] = 0;
2001-10-09 14:30:12 +00:00
} else if (areBoxesNeighbours(i, j)) {
_boxMatrixPtr3[i * 64 + j] = 1;
2001-10-09 14:30:12 +00:00
} else {
_boxMatrixPtr3[i * 64 + j] = 250;
2001-10-09 14:30:12 +00:00
}
}
}
2002-12-29 21:14:28 +00:00
// Iterate over all boxes
for (j = 0; j < num; j++) {
2001-10-09 14:30:12 +00:00
flags = getBoxFlags(j);
if (flags & kBoxInvisible) {
2002-12-29 21:14:28 +00:00
// Locked/invisible boxes are only reachable from themselves.
2001-10-09 14:30:12 +00:00
addToBoxMatrix(0xFF);
addToBoxMatrix(j);
addToBoxMatrix(j);
addToBoxMatrix(j);
} else {
2002-12-29 21:14:28 +00:00
PathNode *node, *node2 = NULL;
PathVertex *vtx = addPathVertex();
for (i = 0; i < num; i++) {
2001-10-09 14:30:12 +00:00
flags = getBoxFlags(j);
if (!(flags & kBoxInvisible)) {
2001-10-09 14:30:12 +00:00
node = unkMatrixProc2(vtx, i);
if (i == j)
2001-10-09 14:30:12 +00:00
node2 = node;
}
}
table_1[j] = 0;
table_2[j] = j;
vtx = unkMatrixProc1(vtx, node2);
node = vtx ? vtx->left : NULL;
counter = 250;
while (node) {
val = _boxMatrixPtr3[j * 64 + node->index];
2001-10-09 14:30:12 +00:00
table_1[node->index] = val;
if (val < counter)
counter = val;
if (table_1[node->index] != 250)
2001-10-09 14:30:12 +00:00
table_2[node->index] = node->index;
else
table_2[node->index] = -1;
node = node->left;
}
2001-10-09 14:30:12 +00:00
while (vtx) {
counter = 250;
node2 = node = vtx->left;
while (node) {
if (table_1[node->index] < counter) {
2001-10-09 14:30:12 +00:00
counter = table_1[node->index];
node2 = node;
}
node = node->left;
}
vtx = unkMatrixProc1(vtx, node2);
node = vtx ? vtx->left : NULL;
while (node) {
code = _boxMatrixPtr3[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;
}
}
addToBoxMatrix(0xFF);
2002-12-29 21:14:28 +00:00
for (i = 1; i < num; i++) {
if (table_2[i - 1] != -1) {
addToBoxMatrix(i - 1); /* lo */
2002-12-29 21:14:28 +00:00
while (table_2[i - 1] == table_2[i]) {
++i;
if (i == num)
break;
2001-10-09 14:30:12 +00:00
}
addToBoxMatrix(i - 1); /* hi */
2002-12-29 21:14:28 +00:00
addToBoxMatrix(table_2[i - 1]); /* dst */
2001-10-09 14:30:12 +00:00
}
}
2002-12-29 21:14:28 +00:00
if (i == num && table_2[i - 1] != -1) {
addToBoxMatrix(i - 1); /* lo */
addToBoxMatrix(i - 1); /* hi */
addToBoxMatrix(table_2[i - 1]); /* dest */
}
2001-10-09 14:30:12 +00:00
}
}
addToBoxMatrix(0xFF);
nukeResource(rtMatrix, 4);
nukeResource(rtMatrix, 3);
2001-10-09 14:30:12 +00:00
}
PathVertex *unkMatrixProc1(PathVertex *vtx, PathNode *node)
{
if (node == NULL || vtx == NULL)
2001-10-09 14:30:12 +00:00
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;
}
2002-07-07 20:25:23 +00:00
PathNode *Scumm::unkMatrixProc2(PathVertex *vtx, int i)
{
2001-10-09 14:30:12 +00:00
PathNode *node;
if (vtx == NULL)
2001-10-09 14:30:12 +00:00
return NULL;
2002-12-29 21:14:28 +00:00
node = (PathNode *)addToBoxVertexHeap(sizeof(PathNode));
node->index = i;
node->left = 0;
node->right = 0;
2001-10-09 14:30:12 +00:00
2002-12-29 21:14:28 +00:00
if (!vtx->right) {
vtx->left = node;
2001-10-09 14:30:12 +00:00
} else {
vtx->right->left = node;
node->right = vtx->right;
}
2002-12-29 21:14:28 +00:00
vtx->right = node;
2001-10-09 14:30:12 +00:00
return vtx->right;
}
2002-12-29 21:14:28 +00:00
2001-10-09 14:30:12 +00:00
/* Check if two boxes are neighbours */
bool Scumm::areBoxesNeighbours(int box1nr, int box2nr)
{
int j, k, m, n;
2001-10-09 14:30:12 +00:00
int tmp_x, tmp_y;
bool result;
BoxCoords box;
BoxCoords box2;
2001-10-09 14:30:12 +00:00
if (getBoxFlags(box1nr) & kBoxInvisible || getBoxFlags(box2nr) & kBoxInvisible)
2001-10-09 14:30:12 +00:00
return false;
getBoxCoordinates(box1nr, &box2);
getBoxCoordinates(box2nr, &box);
2001-10-09 14:30:12 +00:00
result = false;
j = 4;
2001-10-09 14:30:12 +00:00
do {
k = 4;
do {
2002-07-07 20:25:23 +00:00
if (box2.ur.x == box2.ul.x && box.ul.x == box2.ul.x && box.ur.x == box2.ur.x) {
2001-10-09 14:30:12 +00:00
n = m = 0;
if (box2.ur.y < box2.ul.y) {
2001-10-09 14:30:12 +00:00
n = 1;
SWAP(box2.ur.y, box2.ul.y);
2001-10-09 14:30:12 +00:00
}
if (box.ur.y < box.ul.y) {
2001-10-09 14:30:12 +00:00
m = 1;
SWAP(box.ur.y, box.ul.y);
2001-10-09 14:30:12 +00:00
}
if (box.ur.y < box2.ul.y ||
box.ul.y > box2.ur.y ||
(box.ul.y == box2.ur.y ||
2002-07-07 20:25:23 +00:00
box.ur.y == box2.ul.y) && box2.ur.y != box2.ul.y && box.ul.y != box.ur.y) {
2001-10-09 14:30:12 +00:00
if (n) {
SWAP(box2.ur.y, box2.ul.y);
2001-10-09 14:30:12 +00:00
}
if (m) {
SWAP(box.ur.y, box.ul.y);
2001-10-09 14:30:12 +00:00
}
} else {
if (n) {
SWAP(box2.ur.y, box2.ul.y);
2001-10-09 14:30:12 +00:00
}
if (m) {
SWAP(box.ur.y, box.ul.y);
2001-10-09 14:30:12 +00:00
}
result = true;
}
2001-10-09 14:30:12 +00:00
}
2002-07-07 20:25:23 +00:00
if (box2.ur.y == box2.ul.y && box.ul.y == box2.ul.y && box.ur.y == box2.ur.y) {
2001-10-09 14:30:12 +00:00
n = m = 0;
if (box2.ur.x < box2.ul.x) {
2001-10-09 14:30:12 +00:00
n = 1;
SWAP(box2.ur.x, box2.ul.x);
2001-10-09 14:30:12 +00:00
}
if (box.ur.x < box.ul.x) {
2001-10-09 14:30:12 +00:00
m = 1;
SWAP(box.ur.x, box.ul.x);
2001-10-09 14:30:12 +00:00
}
if (box.ur.x < box2.ul.x ||
box.ul.x > box2.ur.x ||
(box.ul.x == box2.ur.x ||
2002-07-07 20:25:23 +00:00
box.ur.x == box2.ul.x) && box2.ur.x != box2.ul.x && box.ul.x != box.ur.x) {
2001-10-09 14:30:12 +00:00
if (n) {
SWAP(box2.ur.x, box2.ul.x);
2001-10-09 14:30:12 +00:00
}
if (m) {
SWAP(box.ur.x, box.ul.x);
2001-10-09 14:30:12 +00:00
}
} else {
if (n) {
SWAP(box2.ur.x, box2.ul.x);
2001-10-09 14:30:12 +00:00
}
if (m) {
SWAP(box.ur.x, box.ul.x);
2001-10-09 14:30:12 +00:00
}
result = true;
}
}
tmp_x = box2.ul.x;
tmp_y = box2.ul.y;
box2.ul.x = box2.ur.x;
box2.ul.y = box2.ur.y;
box2.ur.x = box2.lr.x;
box2.ur.y = box2.lr.y;
box2.lr.x = box2.ll.x;
box2.lr.y = box2.ll.y;
box2.ll.x = tmp_x;
box2.ll.y = tmp_y;
2001-10-09 14:30:12 +00:00
} while (--k);
tmp_x = box.ul.x;
tmp_y = box.ul.y;
box.ul.x = box.ur.x;
box.ul.y = box.ur.y;
box.ur.x = box.lr.x;
box.ur.y = box.lr.y;
box.lr.x = box.ll.x;
box.lr.y = box.ll.y;
box.ll.x = tmp_x;
box.ll.y = tmp_y;
2001-10-09 14:30:12 +00:00
} while (--j);
return result;
}
void Scumm::addToBoxMatrix(byte b)
{
2001-10-09 14:30:12 +00:00
if (++_boxMatrixItem > BOX_MATRIX_SIZE)
error("Box matrix overflow");
*_boxMatrixPtr1++ = b;
}
void *Scumm::addToBoxVertexHeap(int size)
{
2002-12-29 21:14:28 +00:00
byte *ptr = _boxPathVertexHeap;
2001-10-09 14:30:12 +00:00
2002-12-29 21:14:28 +00:00
_boxPathVertexHeap += size;
2001-10-09 14:30:12 +00:00
_boxPathVertexHeapIndex += size;
if (_boxPathVertexHeapIndex >= _maxBoxVertexHeap)
error("Box path vertex heap overflow");
return ptr;
}
PathVertex *Scumm::addPathVertex()
{
2002-12-29 21:14:28 +00:00
_boxPathVertexHeap = getResourceAddress(rtMatrix, 4);
2001-10-09 14:30:12 +00:00
_boxPathVertexHeapIndex = 0;
2002-07-07 20:25:23 +00:00
return (PathVertex *)addToBoxVertexHeap(sizeof(PathVertex));
2001-10-09 14:30:12 +00:00
}
2002-08-12 16:05:14 +00:00
void Scumm::findPathTowardsOld(Actor *actor, byte trap1, byte trap2, byte final_trap, ScummPoint gateLoc[5])
{
ScummPoint pt;
ScummPoint gateA[2];
ScummPoint gateB[2];
getGates(trap1, trap2, gateA, gateB);
gateLoc[1].x = actor->x;
gateLoc[1].y = actor->y;
gateLoc[2].x = 32000;
gateLoc[3].x = 32000;
gateLoc[4].x = 32000;
if (trap2 == final_trap) { /* next = final box? */
gateLoc[4].x = actor->walkdata.destx;
gateLoc[4].y = actor->walkdata.desty;
if (getMaskFromBox(trap1) == getMaskFromBox(trap2) || 1) {
if (compareSlope(gateLoc[1].x, gateLoc[1].y, gateLoc[4].x, gateLoc[4].y, gateA[0].x, gateA[0].y) !=
compareSlope(gateLoc[1].x, gateLoc[1].y, gateLoc[4].x, gateLoc[4].y, gateB[0].x, gateB[0].y) &&
compareSlope(gateLoc[1].x, gateLoc[1].y, gateLoc[4].x, gateLoc[4].y, gateA[1].x, gateA[1].y) !=
compareSlope(gateLoc[1].x, gateLoc[1].y, gateLoc[4].x, gateLoc[4].y, gateB[1].x, gateB[1].y)) {
2002-08-12 16:05:14 +00:00
return; /* same zplane and between both gates? */
}
}
}
pt = closestPtOnLine(gateA[1].x, gateA[1].y, gateB[1].x, gateB[1].y, gateLoc[1].x, gateLoc[1].y);
gateLoc[3].x = pt.x;
gateLoc[3].y = pt.y;
if (compareSlope(gateLoc[1].x, gateLoc[1].y, gateLoc[3].x, gateLoc[3].y, gateA[0].x, gateA[0].y) ==
compareSlope(gateLoc[1].x, gateLoc[1].y, gateLoc[3].x, gateLoc[3].y, gateB[0].x, gateB[0].y)) {
closestPtOnLine(gateA[0].x, gateA[0].y, gateB[0].x, gateB[0].y, gateLoc[1].x, gateLoc[1].y);
2002-07-07 20:25:23 +00:00
gateLoc[2].x = pt.x; /* if point 2 between gates, ignore! */
gateLoc[2].y = pt.y;
}
2002-08-12 16:05:14 +00:00
return;
}
void Scumm::getGates(int trap1, int trap2, ScummPoint gateA[2], ScummPoint gateB[2])
{
int i, j;
int Dist[8];
int MinDist[3];
int Closest[3];
int Box[3];
BoxCoords box;
ScummPoint Clo[8];
ScummPoint poly[8];
AdjustBoxResult abr;
int line1, line2;
// For all corner coordinates of the first box, compute the point cloest
// to them on the second box (and also compute the distance of these points).
getBoxCoordinates(trap1, &box);
poly[0] = box.ul;
poly[1] = box.ur;
poly[2] = box.lr;
poly[3] = box.ll;
for (i = 0; i < 4; i++) {
abr = getClosestPtOnBox(trap2, poly[i].x, poly[i].y);
Dist[i] = abr.dist;
Clo[i].x = abr.x;
Clo[i].y = abr.y;
}
// Now do the same but with the roles of the first and second box swapped.
getBoxCoordinates(trap2, &box);
poly[4] = box.ul;
poly[5] = box.ur;
poly[6] = box.lr;
poly[7] = box.ll;
for (i = 4; i < 8; i++) {
abr = getClosestPtOnBox(trap1, poly[i].x, poly[i].y);
Dist[i] = abr.dist;
Clo[i].x = abr.x;
Clo[i].y = abr.y;
}
// Find the three closest "close" points between the two boxes.
for (j = 0; j < 3; j++) {
MinDist[j] = 0xFFFF;
for (i = 0; i < 8; i++) {
if (Dist[i] < MinDist[j]) {
MinDist[j] = Dist[i];
Closest[j] = i;
}
}
Dist[Closest[j]] = 0xFFFF;
MinDist[j] = (int)sqrt((double)MinDist[j]);
Box[j] = (Closest[j] > 3); // Is the poin on the first or on the second box?
}
// Finally, compute the "gate". That's a pair of two points that are
// in the same box (actually, on the border of that box), which both have
// "minimal" distance to the other box in a certain sense.
if (Box[0] == Box[1] && abs(MinDist[0] - MinDist[1]) < 4) {
line1 = Closest[0];
line2 = Closest[1];
} else if (Box[0] == Box[1] && MinDist[0] == MinDist[1]) { /* parallel */
line1 = Closest[0];
line2 = Closest[1];
} else if (Box[0] == Box[2] && MinDist[0] == MinDist[2]) { /* parallel */
line1 = Closest[0];
line2 = Closest[2];
} else if (Box[1] == Box[2] && MinDist[1] == MinDist[2]) { /* parallel */
line1 = Closest[1];
line2 = Closest[2];
} else if (Box[0] == Box[2] && abs(MinDist[0] - MinDist[2]) < 4) {
line1 = Closest[0];
line2 = Closest[2];
} else if (abs(MinDist[0] - MinDist[2]) < 4) { /* if 1 close to 3 then use 2-3 */
line1 = Closest[1];
line2 = Closest[2];
} else if (abs(MinDist[0] - MinDist[1]) < 4) {
line1 = Closest[0];
line2 = Closest[1];
} else {
line1 = Closest[0];
line2 = Closest[0];
}
// Set the gate
if (line1 < 4) { /* from box 1 to box 2 */
gateA[0] = poly[line1];
gateA[1] = Clo[line1];
} else {
gateA[1] = poly[line1];
gateA[0] = Clo[line1];
}
if (line2 < 4) { /* from box */
gateB[0] = poly[line2];
gateB[1] = Clo[line2];
} else {
gateB[1] = poly[line2];
gateB[0] = Clo[line2];
}
}
bool Scumm::compareSlope(int X1, int Y1, int X2, int Y2, int X3, int Y3)
{
return (Y2 - Y1) * (X3 - X1) <= (Y3 - Y1) * (X2 - X1);
}