mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 14:18:37 +00:00
88ed622702
svn-id: r26117
501 lines
12 KiB
C++
501 lines
12 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2006 The ScummVM project
|
|
*
|
|
* 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.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "parallaction/defs.h"
|
|
#include "parallaction/parallaction.h"
|
|
#include "parallaction/commands.h"
|
|
#include "parallaction/graphics.h"
|
|
#include "parallaction/walk.h"
|
|
#include "parallaction/zone.h"
|
|
|
|
namespace Parallaction {
|
|
|
|
uint16 walkFunc1(int16, int16, WalkNode *);
|
|
|
|
|
|
WalkNode _NULL_WALKNODE = { {NULL, NULL}, 0, 0 };
|
|
|
|
static byte *_buffer;
|
|
|
|
static uint16 _doorData1 = 1000;
|
|
static Zone *_zoneTrap = NULL;
|
|
|
|
static uint16 walkData1 = 0;
|
|
static uint16 walkData2 = 0; // next walk frame
|
|
static int16 walkData3 = -1000; // unused
|
|
|
|
|
|
int32 dotProduct(Point *p1, Point *p2) {
|
|
return p1->_x * p2->_x + p1->_y * p2->_y;
|
|
}
|
|
|
|
//
|
|
// x, y: mouse click (foot) coordinates
|
|
//
|
|
WalkNode *buildWalkPath(uint16 x, uint16 y) {
|
|
debugC(1, kDebugWalk, "buildWalkPath to (%i, %i)", x, y);
|
|
|
|
int16 to_x = x;
|
|
int16 to_y = y;
|
|
|
|
int16 left, bottom, right, top, close, closeY, closeX;
|
|
|
|
// looks for closest usable path Point
|
|
if (queryPath(to_x, to_y) == 0) {
|
|
|
|
right = left = to_x;
|
|
|
|
do {
|
|
right++;
|
|
} while ((queryPath(right, to_y) == 0) && (right < SCREEN_WIDTH));
|
|
|
|
do {
|
|
left--;
|
|
} while ((queryPath(left, to_y) == 0) && (left > 0));
|
|
|
|
right = (right == SCREEN_WIDTH) ? 1000 : right - to_x;
|
|
left = (left == 0) ? 1000 : to_x - left;
|
|
|
|
top = bottom = to_y;
|
|
|
|
do {
|
|
top--;
|
|
} while ((queryPath(to_x, top) == 0) && (top > 0));
|
|
|
|
do {
|
|
bottom++;
|
|
} while ((queryPath(to_x, bottom) == 0) && (bottom < SCREEN_HEIGHT));
|
|
|
|
top = (top == 0) ? 1000 : to_y - top;
|
|
bottom = (bottom == SCREEN_HEIGHT) ? 1000 : bottom - to_y;
|
|
|
|
closeX = (right >= left) ? left : right;
|
|
closeY = (top >= bottom) ? bottom : top;
|
|
|
|
close = (closeX >= closeY) ? closeY : closeX;
|
|
|
|
if (close == right) {
|
|
to_x += right;
|
|
walkData3 = (_yourself._cnv._count == 20) ? 7 : 9;
|
|
} else
|
|
if (close == left) {
|
|
to_x -= left;
|
|
walkData3 = 0;
|
|
} else
|
|
if (close == top) {
|
|
to_y -= top;
|
|
} else
|
|
if (close == bottom) {
|
|
to_y += bottom;
|
|
walkData3 = (_yourself._cnv._count == 20) ? 17 : 21;
|
|
}
|
|
|
|
}
|
|
debugC(1, kDebugWalk, "found closest path point at (%i, %i)", to_x, to_y);
|
|
|
|
WalkNode *v48 = (WalkNode*)malloc(sizeof(WalkNode));
|
|
WalkNode *v44 = (WalkNode*)malloc(sizeof(WalkNode));
|
|
|
|
v48->_x = to_x - _yourself._cnv._width / 2; // target top left coordinates
|
|
v48->_y = to_y - _yourself._cnv._height;
|
|
v48->_node._next = NULL;
|
|
memcpy(v44, v48, sizeof(WalkNode));
|
|
|
|
uint16 v38 = walkFunc1(to_x, to_y, v44);
|
|
if (v38 == 1) {
|
|
// destination directly reachable
|
|
debugC(1, kDebugWalk, "direct move to (%i, %i)", to_x, to_y);
|
|
free(v44);
|
|
return v48;
|
|
}
|
|
|
|
// path is obstructed: find alternative
|
|
debugC(1, kDebugWalk, "trying to build walk path to (%i, %i)", to_x, to_y);
|
|
|
|
WalkNode v58;
|
|
memset(&v58, 0, sizeof(WalkNode));
|
|
|
|
int16 _si = v48->_x; // _si, _di: target top left coordinates
|
|
int16 _di = v48->_y;
|
|
addNode(&v58._node, &v48->_node);
|
|
|
|
WalkNode *_closest_node = NULL;
|
|
|
|
Point v20;
|
|
Point v8;
|
|
|
|
int32 v30, v34, v2C, v28;
|
|
|
|
byte _closest_node_found = 1;
|
|
bool emptyList = true;
|
|
|
|
do {
|
|
|
|
v48 = &v58;
|
|
|
|
v20._x = _yourself._zone.pos._position._x;
|
|
v20._y = _yourself._zone.pos._position._y;
|
|
|
|
v8._x = _si - _yourself._zone.pos._position._x;
|
|
v8._y = _di - _yourself._zone.pos._position._y;
|
|
v34 = v30 = dotProduct(&v8, &v8); // square distance from current position and target
|
|
|
|
while (_closest_node_found != 0) {
|
|
|
|
_closest_node_found = 0;
|
|
WalkNode *location_node = (WalkNode*)_vm->_location._walkNodes._next;
|
|
|
|
// scans location path nodes searching for the nearest Node
|
|
// which can't be farther than the target position
|
|
// otherwise no _closest_node is selected
|
|
while (location_node != NULL) {
|
|
v8._x = location_node->_x - _si;
|
|
v8._y = location_node->_y - _di;
|
|
v2C = dotProduct(&v8, &v8); // square distance from Node to target position
|
|
|
|
v8._x = location_node->_x - v20._x;
|
|
v8._y = location_node->_y - v20._y;
|
|
v28 = dotProduct(&v8, &v8); // square distance from Node to current position
|
|
|
|
if (v2C < v34 && v28 < v30) {
|
|
_closest_node_found = 1;
|
|
v30 = v28;
|
|
_closest_node = location_node;
|
|
}
|
|
|
|
location_node = (WalkNode*)location_node->_node._next;
|
|
}
|
|
|
|
if (_closest_node_found == 0) break;
|
|
|
|
WalkNode *_newnode = (WalkNode*)malloc(sizeof(WalkNode));
|
|
memcpy( _newnode, _closest_node, sizeof(WalkNode));
|
|
v20._x = _newnode->_x;
|
|
v20._y = _newnode->_y;
|
|
|
|
v34 = v30 = (_si - v20._x) * (_si - v20._x) + (_di - v20._y) * (_di - v20._y);
|
|
|
|
|
|
debugC(1, kDebugWalk, "adding walk node (%i, %i) to path", _newnode->_x, _newnode->_y);
|
|
|
|
addNode(&v48->_node, &_newnode->_node);
|
|
v48 = _newnode;
|
|
}
|
|
|
|
if (!emptyList) break;
|
|
|
|
if (v38 != 0 && v34 > v38) {
|
|
// no alternative path (gap?)
|
|
freeNodeList(v58._node._next);
|
|
debugC(1, kDebugWalk, "can't find a path node: rejecting partial path");
|
|
return v44;
|
|
} else {
|
|
_si = ((WalkNode*)(v58._node._next))->_x;
|
|
_di = ((WalkNode*)(v58._node._next))->_y;
|
|
emptyList = false;
|
|
_closest_node_found = 1;
|
|
}
|
|
|
|
} while (true);
|
|
|
|
debugC(1, kDebugWalk, "walk path completed");
|
|
|
|
WalkNode* tmp = &v58;
|
|
uint16 i = 1;
|
|
while (tmp->_node._next) {
|
|
debugC(1, kDebugWalk, "node %i: %i, %i", i, tmp->_x, tmp->_y);
|
|
tmp = (WalkNode*)tmp->_node._next;
|
|
i++;
|
|
}
|
|
|
|
|
|
free(v44);
|
|
return (WalkNode*)v58._node._next;
|
|
}
|
|
|
|
|
|
//
|
|
// x,y : top left coordinates
|
|
//
|
|
// 0 : Point not reachable
|
|
// 1 : Point reachable
|
|
// other values: square distance to target (not reachable)
|
|
//
|
|
uint16 walkFunc1(int16 x, int16 y, WalkNode *Node) {
|
|
|
|
Point v4 = { 0, 0 };
|
|
|
|
Point foot = {
|
|
_yourself._zone.pos._position._x + _yourself._cnv._width/2,
|
|
_yourself._zone.pos._position._y + _yourself._cnv._height
|
|
};
|
|
|
|
Point v8 = {
|
|
foot._x,
|
|
foot._y
|
|
};
|
|
|
|
while (foot._x != x || foot._y != y) {
|
|
|
|
if (foot._x < x) {
|
|
if (queryPath(foot._x + 1, foot._y) != 0) foot._x++;
|
|
}
|
|
if (foot._x > x) {
|
|
if (queryPath(foot._x - 1, foot._y) != 0) foot._x--;
|
|
}
|
|
if (foot._y < y) {
|
|
if (queryPath(foot._x, foot._y + 1) != 0) foot._y++;
|
|
}
|
|
if (foot._y > y) {
|
|
if (queryPath(foot._x, foot._y - 1) != 0) foot._y--;
|
|
}
|
|
|
|
if ((foot._x == v8._x) && (foot._y == v8._y) && ((foot._x != x) || (foot._y != y))) {
|
|
// foot couldn't move and still away from target
|
|
|
|
v4._x = foot._x;
|
|
v4._y = foot._y;
|
|
|
|
while (foot._x != x || foot._y != y) {
|
|
|
|
if (foot._x < x) {
|
|
if (queryPath(foot._x + 1, foot._y) == 0) foot._x++;
|
|
}
|
|
if (foot._x > x) {
|
|
if (queryPath(foot._x - 1, foot._y) == 0) foot._x--;
|
|
}
|
|
if (foot._y < y) {
|
|
if (queryPath(foot._x, foot._y + 1) == 0) foot._y++;
|
|
}
|
|
if (foot._y > y) {
|
|
if (queryPath(foot._x, foot._y - 1) == 0) foot._y--;
|
|
}
|
|
|
|
if ((foot._x == v8._x) && (foot._y == v8._y) && (foot._x != x || foot._y != y))
|
|
return 0;
|
|
|
|
v8._x = foot._x;
|
|
v8._y = foot._y;
|
|
}
|
|
|
|
Node->_x = v4._x - _yourself._cnv._width / 2;
|
|
Node->_y = v4._y - _yourself._cnv._height;
|
|
|
|
return (x - v4._x) * (x - v4._x) + (y - v4._y) * (y - v4._y);
|
|
}
|
|
|
|
v8._x = foot._x;
|
|
v8._y = foot._y;
|
|
|
|
}
|
|
|
|
// there exists an unobstructed path
|
|
return 1;
|
|
}
|
|
|
|
|
|
void jobWalk(void *parm, Job *j) {
|
|
WalkNode *node = (WalkNode*)parm;
|
|
|
|
int16 _si = _yourself._zone.pos._position._x;
|
|
int16 _di = _yourself._zone.pos._position._y;
|
|
|
|
// debugC(1, kDebugWalk, "jobWalk to (%i, %i)", node->_x + _yourself._cnv._width / 2, node->_y + _yourself._cnv._height);
|
|
|
|
_yourself._zone.pos._oldposition._x = _si;
|
|
_yourself._zone.pos._oldposition._y = _di;
|
|
|
|
if ((node->_x == _si) && (node->_y == _di)) {
|
|
if (node->_node._next == NULL) {
|
|
|
|
debugC(1, kDebugWalk, "jobWalk reached last node");
|
|
|
|
j->_finished = 1;
|
|
checkDoor();
|
|
free(node);
|
|
return;
|
|
}
|
|
|
|
|
|
WalkNode *tmp = (WalkNode*)node->_node._next;
|
|
j->_parm = node->_node._next;
|
|
free(node);
|
|
|
|
debugC(1, kDebugWalk, "jobWalk moving to next node (%i, %i)", tmp->_x, tmp->_y);
|
|
|
|
node = (WalkNode*)tmp;
|
|
}
|
|
|
|
Point dist = {
|
|
node->_x - _yourself._zone.pos._position._x,
|
|
node->_y - _yourself._zone.pos._position._y
|
|
};
|
|
|
|
if (dist._x < 0)
|
|
dist._x = -dist._x;
|
|
if (dist._y < 0)
|
|
dist._y = -dist._y;
|
|
|
|
walkData1++;
|
|
|
|
// walk frame selection
|
|
int16 v16;
|
|
if (_yourself._cnv._count == 20) {
|
|
|
|
if (dist._x > dist._y) {
|
|
walkData2 = (node->_x > _si) ? 0 : 7;
|
|
walkData1 %= 12;
|
|
v16 = walkData1 / 2;
|
|
} else {
|
|
walkData2 = (node->_y > _di) ? 14 : 17;
|
|
walkData1 %= 8;
|
|
v16 = walkData1 / 4;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (dist._x > dist._y) {
|
|
walkData2 = (node->_x > _si) ? 0 : 9;
|
|
walkData1 %= 16;
|
|
v16 = walkData1 / 2;
|
|
} else {
|
|
walkData2 = (node->_y > _di) ? 18 : 21;
|
|
walkData1 %= 8;
|
|
v16 = walkData1 / 4;
|
|
}
|
|
|
|
}
|
|
|
|
// StaticCnv v14;
|
|
// v14._width = _yourself._cnv._width;
|
|
// v14._height = _yourself._cnv._height;
|
|
// v14._data0 = _yourself._cnv._array[_yourself._frame];
|
|
// v14._data1 = _yourself._cnv.field_8[_yourself._frame];
|
|
|
|
if ((_si < node->_x) && (_si < SCREEN_WIDTH) && (queryPath(_yourself._cnv._width/2 + _si + 2, _yourself._cnv._height + _di) != 0)) {
|
|
// printf("walk right\n");
|
|
_si = (_si + 2 < node->_x) ? _si + 2 : node->_x;
|
|
}
|
|
|
|
if ((_si > node->_x) && (_si > -20) && (queryPath(_yourself._cnv._width/2 + _si - 2, _yourself._cnv._height + _di) != 0)) {
|
|
// printf("walk left\n");
|
|
_si = (_si - 2 > node->_x) ? _si - 2 :node->_x;
|
|
}
|
|
|
|
if ((_di < node->_y) && (_di < (SCREEN_HEIGHT - _yourself._cnv._height)) && (queryPath(_yourself._cnv._width/2 + _si, _yourself._cnv._height + _di + 2) != 0)) {
|
|
// printf("walk down\n");
|
|
_di = (_di + 2 <= node->_y) ? _di + 2 : node->_y;
|
|
}
|
|
|
|
if ((_di > node->_y) && (_di > -20) && (queryPath(_yourself._cnv._width/2 + _si, _yourself._cnv._height + _di - 2) != 0)) {
|
|
// printf("walk up\n");
|
|
_di = (_di - 2 >= node->_y) ? _di - 2 : node->_y;
|
|
}
|
|
|
|
// printf("hitZone: %i, %i\n", _si, _di);
|
|
_yourself._zone.pos._position._x = _si;
|
|
_yourself._zone.pos._position._y = _di;
|
|
|
|
if ((_si == _yourself._zone.pos._oldposition._x) && (_di == _yourself._zone.pos._oldposition._y)) {
|
|
|
|
j->_finished = 1;
|
|
checkDoor();
|
|
freeNodeList(&node->_node);
|
|
|
|
} else {
|
|
|
|
_yourself._frame = v16 + walkData2 + 1;
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
uint16 checkDoor() {
|
|
// printf("checkDoor()...");
|
|
|
|
if (_vm->_currentLocationIndex != _doorData1) {
|
|
_doorData1 = _vm->_currentLocationIndex;
|
|
_zoneTrap = NULL;
|
|
}
|
|
|
|
_engineFlags &= ~kEngineWalking;
|
|
Zone *z = hitZone(kZoneDoor, _yourself._zone.pos._position._x + _yourself._cnv._width / 2, _yourself._zone.pos._position._y + _yourself._cnv._height);
|
|
|
|
if (z != NULL) {
|
|
|
|
if ((z->_flags & kFlagsClosed) == 0) {
|
|
_vm->_location._startPosition._x = z->u.door->_startPos._x;
|
|
_vm->_location._startPosition._y = z->u.door->_startPos._y;
|
|
_vm->_location._startFrame = z->u.door->_startFrame;
|
|
strcpy( _vm->_location._name, z->u.door->_location );
|
|
|
|
_engineFlags |= kEngineChangeLocation;
|
|
_zoneTrap = NULL;
|
|
|
|
} else {
|
|
runCommands(z->_commands, z);
|
|
}
|
|
}
|
|
|
|
z = hitZone(kZoneTrap, _yourself._zone.pos._position._x + _yourself._cnv._width / 2, _yourself._zone.pos._position._y + _yourself._cnv._height);
|
|
|
|
if (z != NULL) {
|
|
_localFlags[_vm->_currentLocationIndex] |= kFlagsEnter;
|
|
runCommands(z->_commands, z);
|
|
_localFlags[_vm->_currentLocationIndex] &= ~kFlagsEnter;
|
|
_zoneTrap = z;
|
|
} else
|
|
if (_zoneTrap != NULL) {
|
|
_localFlags[_vm->_currentLocationIndex] |= kFlagsExit;
|
|
runCommands(_zoneTrap->_commands, _zoneTrap);
|
|
_localFlags[_vm->_currentLocationIndex] &= ~kFlagsExit;
|
|
_zoneTrap = NULL;
|
|
}
|
|
|
|
// printf("done\n");
|
|
|
|
_yourself._frame = walkData2;
|
|
return _yourself._frame;
|
|
}
|
|
|
|
uint16 queryPath(uint16 x, uint16 y) {
|
|
|
|
byte _al = _buffer[y*40 + x/8];
|
|
byte _dl = 1 << (x % 8);
|
|
|
|
return _al & _dl;
|
|
|
|
}
|
|
|
|
void setPath(byte *path) {
|
|
memcpy(_buffer, path, SCREENPATH_WIDTH*SCREEN_HEIGHT);
|
|
}
|
|
|
|
void initWalk() {
|
|
_buffer = (byte*)malloc(SCREENPATH_WIDTH * SCREEN_HEIGHT);
|
|
}
|
|
|
|
} // namespace Parallaction
|
|
|