2007-05-30 21:56:52 +00:00
|
|
|
/* 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.
|
2007-01-14 21:29: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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "parallaction/parallaction.h"
|
|
|
|
|
|
|
|
namespace Parallaction {
|
|
|
|
|
|
|
|
|
2008-07-26 04:01:11 +00:00
|
|
|
|
2008-12-14 10:08:31 +00:00
|
|
|
#define IS_PATH_CLEAR(x,y) _vm->_gfx->_backgroundInfo->path.getValue((x), (y))
|
2008-07-26 05:37:52 +00:00
|
|
|
|
2007-08-06 22:03:17 +00:00
|
|
|
inline byte PathBuffer::getValue(uint16 x, uint16 y) {
|
|
|
|
byte m = data[(x >> 3) + y * internalWidth];
|
2008-07-26 04:01:11 +00:00
|
|
|
uint bit = 0;
|
|
|
|
switch (_vm->getGameType()) {
|
|
|
|
case GType_Nippon:
|
|
|
|
bit = (_vm->getPlatform() == Common::kPlatformPC) ? (x & 7) : (7 - (x & 7));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GType_BRA:
|
|
|
|
// Amiga and PC versions pack the path bits the same way in BRA
|
|
|
|
bit = 7 - (x & 7);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error("path mask not yet implemented for this game type");
|
|
|
|
}
|
|
|
|
return ((1 << bit) & m) >> bit;
|
2007-05-13 14:38:05 +00:00
|
|
|
}
|
|
|
|
|
2007-03-20 19:45:17 +00:00
|
|
|
// adjusts position towards nearest walkable point
|
2007-01-14 21:29:12 +00:00
|
|
|
//
|
2008-07-26 04:01:11 +00:00
|
|
|
void PathBuilder_NS::correctPathPoint(Common::Point &to) {
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
if (IS_PATH_CLEAR(to.x, to.y)) return;
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-12-14 10:08:31 +00:00
|
|
|
int maxX = _vm->_gfx->_backgroundInfo->path.w;
|
|
|
|
int maxY = _vm->_gfx->_backgroundInfo->path.h;
|
|
|
|
|
2007-03-20 19:45:17 +00:00
|
|
|
int16 right = to.x;
|
|
|
|
int16 left = to.x;
|
|
|
|
do {
|
|
|
|
right++;
|
2008-12-14 10:08:31 +00:00
|
|
|
} while (!IS_PATH_CLEAR(right, to.y) && (right < maxX));
|
2007-03-20 19:45:17 +00:00
|
|
|
do {
|
|
|
|
left--;
|
2008-07-26 05:37:52 +00:00
|
|
|
} while (!IS_PATH_CLEAR(left, to.y) && (left > 0));
|
2008-12-14 10:08:31 +00:00
|
|
|
right = (right == maxX) ? 1000 : right - to.x;
|
2007-03-20 19:45:17 +00:00
|
|
|
left = (left == 0) ? 1000 : to.x - left;
|
2007-01-14 21:29:12 +00:00
|
|
|
|
|
|
|
|
2007-03-20 19:45:17 +00:00
|
|
|
int16 top = to.y;
|
|
|
|
int16 bottom = to.y;
|
|
|
|
do {
|
|
|
|
top--;
|
2008-07-26 05:37:52 +00:00
|
|
|
} while (!IS_PATH_CLEAR(to.x, top) && (top > 0));
|
2007-03-20 19:45:17 +00:00
|
|
|
do {
|
|
|
|
bottom++;
|
2008-12-14 10:08:31 +00:00
|
|
|
} while (!IS_PATH_CLEAR(to.x, bottom) && (bottom < maxY));
|
2007-03-20 19:45:17 +00:00
|
|
|
top = (top == 0) ? 1000 : to.y - top;
|
2008-12-14 10:08:31 +00:00
|
|
|
bottom = (bottom == maxY) ? 1000 : bottom - to.y;
|
2007-03-20 19:45:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
int16 closeX = (right >= left) ? left : right;
|
|
|
|
int16 closeY = (top >= bottom) ? bottom : top;
|
|
|
|
int16 close = (closeX >= closeY) ? closeY : closeX;
|
|
|
|
if (close == right) {
|
|
|
|
to.x += right;
|
|
|
|
} else
|
|
|
|
if (close == left) {
|
|
|
|
to.x -= left;
|
|
|
|
} else
|
|
|
|
if (close == top) {
|
|
|
|
to.y -= top;
|
|
|
|
} else
|
|
|
|
if (close == bottom) {
|
|
|
|
to.y += bottom;
|
|
|
|
}
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2007-03-20 19:45:17 +00:00
|
|
|
return;
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2007-03-20 19:45:17 +00:00
|
|
|
}
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-07-26 04:01:11 +00:00
|
|
|
uint32 PathBuilder_NS::buildSubPath(const Common::Point& pos, const Common::Point& stop) {
|
2007-03-21 21:49:11 +00:00
|
|
|
|
|
|
|
uint32 v28 = 0;
|
|
|
|
uint32 v2C = 0;
|
|
|
|
uint32 v34 = pos.sqrDist(stop); // square distance from current position and target
|
|
|
|
uint32 v30 = v34;
|
|
|
|
|
2007-04-09 10:03:15 +00:00
|
|
|
_subPath.clear();
|
|
|
|
|
2007-03-21 21:49:11 +00:00
|
|
|
Common::Point v20(pos);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
2008-07-25 16:08:10 +00:00
|
|
|
PointList::iterator nearest = _vm->_location._walkPoints.end();
|
|
|
|
PointList::iterator locNode = _vm->_location._walkPoints.begin();
|
2007-03-21 21:49:11 +00:00
|
|
|
|
|
|
|
// scans location path nodes searching for the nearest Node
|
|
|
|
// which can't be farther than the target position
|
|
|
|
// otherwise no _closest_node is selected
|
2008-07-25 16:08:10 +00:00
|
|
|
while (locNode != _vm->_location._walkPoints.end()) {
|
2007-03-21 21:49:11 +00:00
|
|
|
|
2008-07-25 16:01:25 +00:00
|
|
|
Common::Point v8 = *locNode;
|
2007-03-21 21:49:11 +00:00
|
|
|
v2C = v8.sqrDist(stop);
|
|
|
|
v28 = v8.sqrDist(v20);
|
|
|
|
|
|
|
|
if (v2C < v34 && v28 < v30) {
|
|
|
|
v30 = v28;
|
2007-04-09 10:03:15 +00:00
|
|
|
nearest = locNode;
|
2007-03-21 21:49:11 +00:00
|
|
|
}
|
|
|
|
|
2007-04-09 10:03:15 +00:00
|
|
|
locNode++;
|
2007-03-21 21:49:11 +00:00
|
|
|
}
|
|
|
|
|
2008-07-25 16:08:10 +00:00
|
|
|
if (nearest == _vm->_location._walkPoints.end()) break;
|
2007-03-21 21:49:11 +00:00
|
|
|
|
2008-07-25 16:01:25 +00:00
|
|
|
v20 = *nearest;
|
2007-03-21 21:49:11 +00:00
|
|
|
v34 = v30 = v20.sqrDist(stop);
|
|
|
|
|
2008-07-25 16:01:25 +00:00
|
|
|
_subPath.push_back(*nearest);
|
2007-03-21 21:49:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return v34;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-03-20 19:45:17 +00:00
|
|
|
//
|
|
|
|
// x, y: mouse click (foot) coordinates
|
|
|
|
//
|
2008-07-26 05:56:39 +00:00
|
|
|
void PathBuilder_NS::buildPath(uint16 x, uint16 y) {
|
2007-04-09 10:03:15 +00:00
|
|
|
debugC(1, kDebugWalk, "PathBuilder::buildPath to (%i, %i)", x, y);
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.clear();
|
|
|
|
|
2007-03-20 19:45:17 +00:00
|
|
|
Common::Point to(x, y);
|
|
|
|
correctPathPoint(to);
|
2007-03-18 17:03:07 +00:00
|
|
|
debugC(1, kDebugWalk, "found closest path point at (%i, %i)", to.x, to.y);
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-07-25 16:01:25 +00:00
|
|
|
Common::Point v48(to);
|
|
|
|
Common::Point v44(to);
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
uint16 v38 = walkFunc1(to, v44);
|
2007-01-14 21:29:12 +00:00
|
|
|
if (v38 == 1) {
|
|
|
|
// destination directly reachable
|
2007-03-18 17:03:07 +00:00
|
|
|
debugC(1, kDebugWalk, "direct move to (%i, %i)", to.x, to.y);
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.push_back(v48);
|
|
|
|
return;
|
2007-04-09 10:03:15 +00:00
|
|
|
}
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2007-04-09 10:03:15 +00:00
|
|
|
// path is obstructed: look for alternative
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.push_back(v48);
|
2007-08-28 14:30:17 +00:00
|
|
|
Common::Point pos;
|
2008-07-26 04:01:11 +00:00
|
|
|
_ch->getFoot(pos);
|
2007-08-28 14:30:17 +00:00
|
|
|
|
2008-07-26 05:56:39 +00:00
|
|
|
uint32 v34 = buildSubPath(pos, v48);
|
2007-03-22 19:49:15 +00:00
|
|
|
if (v38 != 0 && v34 > v38) {
|
|
|
|
// no alternative path (gap?)
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.clear();
|
|
|
|
_ch->_walkPath.push_back(v44);
|
|
|
|
return;
|
2007-03-22 19:49:15 +00:00
|
|
|
}
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.insert(_ch->_walkPath.begin(), _subPath.begin(), _subPath.end());
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-07-26 05:56:39 +00:00
|
|
|
buildSubPath(pos, *_ch->_walkPath.begin());
|
|
|
|
_ch->_walkPath.insert(_ch->_walkPath.begin(), _subPath.begin(), _subPath.end());
|
2007-04-11 20:01:06 +00:00
|
|
|
|
2008-07-26 05:56:39 +00:00
|
|
|
return;
|
2007-01-14 21:29:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// x,y : top left coordinates
|
|
|
|
//
|
|
|
|
// 0 : Point not reachable
|
2007-03-20 19:45:17 +00:00
|
|
|
// 1 : Point reachable in a straight line
|
|
|
|
// other values: square distance to target (point not reachable in a straight line)
|
2007-01-14 21:29:12 +00:00
|
|
|
//
|
2008-07-26 05:37:52 +00:00
|
|
|
uint16 PathBuilder_NS::walkFunc1(const Common::Point &to, Common::Point& node) {
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
Common::Point arg(to);
|
2007-03-18 17:03:07 +00:00
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
Common::Point v4;
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2007-08-28 14:30:17 +00:00
|
|
|
Common::Point foot;
|
2008-07-26 04:01:11 +00:00
|
|
|
_ch->getFoot(foot);
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2007-03-18 17:03:07 +00:00
|
|
|
Common::Point v8(foot);
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2007-03-18 17:03:07 +00:00
|
|
|
while (foot != arg) {
|
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
if (foot.x < to.x && IS_PATH_CLEAR(foot.x + 1, foot.y)) foot.x++;
|
|
|
|
if (foot.x > to.x && IS_PATH_CLEAR(foot.x - 1, foot.y)) foot.x--;
|
|
|
|
if (foot.y < to.y && IS_PATH_CLEAR(foot.x, foot.y + 1)) foot.y++;
|
|
|
|
if (foot.y > to.y && IS_PATH_CLEAR(foot.x, foot.y - 1)) foot.y--;
|
2007-01-14 21:29:12 +00:00
|
|
|
|
|
|
|
|
2007-03-18 17:03:07 +00:00
|
|
|
if (foot == v8 && foot != arg) {
|
2007-01-14 21:29:12 +00:00
|
|
|
// foot couldn't move and still away from target
|
|
|
|
|
2007-03-18 17:03:07 +00:00
|
|
|
v4 = foot;
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2007-03-18 17:03:07 +00:00
|
|
|
while (foot != arg) {
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
if (foot.x < to.x && !IS_PATH_CLEAR(foot.x + 1, foot.y)) foot.x++;
|
|
|
|
if (foot.x > to.x && !IS_PATH_CLEAR(foot.x - 1, foot.y)) foot.x--;
|
|
|
|
if (foot.y < to.y && !IS_PATH_CLEAR(foot.x, foot.y + 1)) foot.y++;
|
|
|
|
if (foot.y > to.y && !IS_PATH_CLEAR(foot.x, foot.y - 1)) foot.y--;
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2007-03-18 17:03:07 +00:00
|
|
|
if (foot == v8 && foot != arg)
|
2007-01-14 21:29:12 +00:00
|
|
|
return 0;
|
|
|
|
|
2007-03-18 17:03:07 +00:00
|
|
|
v8 = foot;
|
2007-01-14 21:29:12 +00:00
|
|
|
}
|
|
|
|
|
2008-07-25 16:01:25 +00:00
|
|
|
node = v4;
|
2008-07-26 05:37:52 +00:00
|
|
|
return v4.sqrDist(to);
|
2007-01-14 21:29:12 +00:00
|
|
|
}
|
|
|
|
|
2007-03-18 17:03:07 +00:00
|
|
|
v8 = foot;
|
2007-01-14 21:29:12 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// there exists an unobstructed path
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
void PathWalker_NS::clipMove(Common::Point& pos, const Common::Point& to) {
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-12-14 10:08:31 +00:00
|
|
|
if ((pos.x < to.x) && (pos.x < _vm->_gfx->_backgroundInfo->path.w) && IS_PATH_CLEAR(pos.x + 2, pos.y)) {
|
2008-07-13 13:04:36 +00:00
|
|
|
pos.x = (pos.x + 2 < to.x) ? pos.x + 2 : to.x;
|
2007-03-20 19:45:17 +00:00
|
|
|
}
|
2007-01-18 21:25:02 +00:00
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
if ((pos.x > to.x) && (pos.x > 0) && IS_PATH_CLEAR(pos.x - 2, pos.y)) {
|
2008-07-13 13:04:36 +00:00
|
|
|
pos.x = (pos.x - 2 > to.x) ? pos.x - 2 : to.x;
|
2007-03-20 19:45:17 +00:00
|
|
|
}
|
2007-01-14 21:29:12 +00:00
|
|
|
|
2008-12-14 10:08:31 +00:00
|
|
|
if ((pos.y < to.y) && (pos.y < _vm->_gfx->_backgroundInfo->path.h) && IS_PATH_CLEAR(pos.x, pos.y + 2)) {
|
2008-07-13 13:04:36 +00:00
|
|
|
pos.y = (pos.y + 2 <= to.y) ? pos.y + 2 : to.y;
|
2007-03-20 19:45:17 +00:00
|
|
|
}
|
2007-01-18 21:25:02 +00:00
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
if ((pos.y > to.y) && (pos.y > 0) && IS_PATH_CLEAR(pos.x, pos.y - 2)) {
|
2008-07-13 13:04:36 +00:00
|
|
|
pos.y = (pos.y - 2 >= to.y) ? pos.y - 2 : to.y;
|
2007-03-20 19:45:17 +00:00
|
|
|
}
|
2007-01-18 21:25:02 +00:00
|
|
|
|
2007-03-20 19:45:17 +00:00
|
|
|
return;
|
|
|
|
}
|
2007-01-18 21:25:02 +00:00
|
|
|
|
2007-08-28 14:30:17 +00:00
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
void PathWalker_NS::checkDoor(const Common::Point &foot) {
|
2007-08-28 14:30:17 +00:00
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
ZonePtr z = _vm->hitZone(kZoneDoor, foot.x, foot.y);
|
2008-04-06 05:40:02 +00:00
|
|
|
if (z) {
|
2007-01-14 21:29:12 +00:00
|
|
|
if ((z->_flags & kFlagsClosed) == 0) {
|
2008-07-27 08:35:00 +00:00
|
|
|
_vm->_location._startPosition = z->u.door->_startPos;
|
|
|
|
_vm->_location._startFrame = z->u.door->_startFrame;
|
|
|
|
_vm->scheduleLocationSwitch(z->u.door->_location);
|
|
|
|
_vm->_zoneTrap = nullZonePtr;
|
2007-01-14 21:29:12 +00:00
|
|
|
} else {
|
2008-07-27 08:35:00 +00:00
|
|
|
_vm->_cmdExec->run(z->_commands, z);
|
2007-01-14 21:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
z = _vm->hitZone(kZoneTrap, foot.x, foot.y);
|
2008-04-06 05:40:02 +00:00
|
|
|
if (z) {
|
2008-07-27 08:35:00 +00:00
|
|
|
_vm->setLocationFlags(kFlagsEnter);
|
|
|
|
_vm->_cmdExec->run(z->_commands, z);
|
|
|
|
_vm->clearLocationFlags(kFlagsEnter);
|
|
|
|
_vm->_zoneTrap = z;
|
2007-01-14 21:29:12 +00:00
|
|
|
} else
|
2008-07-27 08:35:00 +00:00
|
|
|
if (_vm->_zoneTrap) {
|
|
|
|
_vm->setLocationFlags(kFlagsExit);
|
|
|
|
_vm->_cmdExec->run(_vm->_zoneTrap->_commands, _vm->_zoneTrap);
|
|
|
|
_vm->clearLocationFlags(kFlagsExit);
|
|
|
|
_vm->_zoneTrap = nullZonePtr;
|
2007-01-14 21:29:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-03-12 22:52:27 +00:00
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
void PathWalker_NS::finalizeWalk() {
|
2008-07-13 13:04:36 +00:00
|
|
|
_engineFlags &= ~kEngineWalking;
|
|
|
|
|
|
|
|
Common::Point foot;
|
2008-07-27 08:35:00 +00:00
|
|
|
_ch->getFoot(foot);
|
2008-07-13 13:04:36 +00:00
|
|
|
checkDoor(foot);
|
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
_ch->_walkPath.clear();
|
2007-05-13 14:38:05 +00:00
|
|
|
}
|
2007-03-12 22:52:27 +00:00
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
void PathWalker_NS::walk() {
|
2007-11-19 20:23:01 +00:00
|
|
|
if ((_engineFlags & kEngineWalking) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-13 06:27:31 +00:00
|
|
|
Common::Point curPos;
|
2008-07-27 08:35:00 +00:00
|
|
|
_ch->getFoot(curPos);
|
2007-03-12 22:52:27 +00:00
|
|
|
|
2008-07-13 13:04:36 +00:00
|
|
|
// update target, if previous was reached
|
2008-07-27 08:35:00 +00:00
|
|
|
PointList::iterator it = _ch->_walkPath.begin();
|
|
|
|
if (it != _ch->_walkPath.end()) {
|
2008-07-26 05:37:52 +00:00
|
|
|
if (*it == curPos) {
|
2008-07-25 16:01:25 +00:00
|
|
|
debugC(1, kDebugWalk, "walk reached node (%i, %i)", (*it).x, (*it).y);
|
2008-07-27 08:35:00 +00:00
|
|
|
it = _ch->_walkPath.erase(it);
|
2007-05-13 14:38:05 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-13 13:04:36 +00:00
|
|
|
|
|
|
|
// advance character towards the target
|
|
|
|
Common::Point targetPos;
|
2008-07-27 08:35:00 +00:00
|
|
|
if (it == _ch->_walkPath.end()) {
|
2008-01-28 13:10:49 +00:00
|
|
|
debugC(1, kDebugWalk, "walk reached last node");
|
2008-07-27 08:35:00 +00:00
|
|
|
finalizeWalk();
|
2008-07-13 13:04:36 +00:00
|
|
|
targetPos = curPos;
|
|
|
|
} else {
|
2008-07-25 16:01:25 +00:00
|
|
|
// targetPos is saved to help setting character direction
|
2008-07-26 05:37:52 +00:00
|
|
|
targetPos = *it;
|
2007-08-28 14:30:17 +00:00
|
|
|
|
2008-07-13 13:04:36 +00:00
|
|
|
Common::Point newPos(curPos);
|
|
|
|
clipMove(newPos, targetPos);
|
2008-07-27 08:35:00 +00:00
|
|
|
_ch->setFoot(newPos);
|
2007-05-13 14:38:05 +00:00
|
|
|
|
2008-07-13 13:04:36 +00:00
|
|
|
if (newPos == curPos) {
|
|
|
|
debugC(1, kDebugWalk, "walk was blocked by an unforeseen obstacle");
|
2008-07-27 08:35:00 +00:00
|
|
|
finalizeWalk();
|
2008-07-14 00:21:05 +00:00
|
|
|
targetPos = newPos; // when walking is interrupted, targetPos must be hacked so that a still frame can be selected
|
2008-07-13 13:04:36 +00:00
|
|
|
}
|
2007-05-13 14:38:05 +00:00
|
|
|
}
|
|
|
|
|
2008-07-13 13:04:36 +00:00
|
|
|
// targetPos is used to select the direction (and the walkFrame) of a character,
|
|
|
|
// since it doesn't cause the sudden changes in orientation that newPos would.
|
|
|
|
// Since newPos is 'adjusted' according to walkable areas, an imaginary line drawn
|
|
|
|
// from curPos to newPos is prone to abrutply change in direction, thus making the
|
|
|
|
// code select 'too different' frames when walking diagonally against obstacles,
|
|
|
|
// and yielding an annoying shaking effect in the character.
|
2008-07-27 08:35:00 +00:00
|
|
|
_ch->updateDirection(curPos, targetPos);
|
2007-03-12 22:52:27 +00:00
|
|
|
}
|
|
|
|
|
2007-05-13 14:38:05 +00:00
|
|
|
|
2007-04-07 10:02:59 +00:00
|
|
|
|
2008-07-26 04:01:11 +00:00
|
|
|
PathBuilder_NS::PathBuilder_NS(Character *ch) : PathBuilder(ch), _list(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PathBuilder_BR::directPathExists(const Common::Point &from, const Common::Point &to) {
|
|
|
|
|
|
|
|
Common::Point copy(from);
|
|
|
|
Common::Point p(copy);
|
|
|
|
|
|
|
|
while (p != to) {
|
|
|
|
|
2008-07-26 05:37:52 +00:00
|
|
|
if (p.x < to.x && IS_PATH_CLEAR(p.x + 1, p.y)) p.x++;
|
|
|
|
if (p.x > to.x && IS_PATH_CLEAR(p.x - 1, p.y)) p.x--;
|
|
|
|
if (p.y < to.y && IS_PATH_CLEAR(p.x, p.y + 1)) p.y++;
|
|
|
|
if (p.y > to.y && IS_PATH_CLEAR(p.x, p.y - 1)) p.y--;
|
2008-07-26 04:01:11 +00:00
|
|
|
|
|
|
|
if (p == copy && p != to) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
copy = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2007-04-09 10:03:15 +00:00
|
|
|
}
|
2007-04-07 10:02:59 +00:00
|
|
|
|
2008-07-26 05:56:39 +00:00
|
|
|
void PathBuilder_BR::buildPath(uint16 x, uint16 y) {
|
2008-07-26 04:01:11 +00:00
|
|
|
Common::Point foot;
|
|
|
|
_ch->getFoot(foot);
|
|
|
|
|
|
|
|
debugC(1, kDebugWalk, "buildPath: from (%i, %i) to (%i, %i)", foot.x, foot.y, x, y);
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.clear();
|
2008-07-26 04:01:11 +00:00
|
|
|
|
|
|
|
// look for easy path first
|
|
|
|
Common::Point dest(x, y);
|
|
|
|
if (directPathExists(foot, dest)) {
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.push_back(dest);
|
2008-07-26 04:01:11 +00:00
|
|
|
debugC(3, kDebugWalk, "buildPath: direct path found");
|
2008-07-26 05:56:39 +00:00
|
|
|
return;
|
2008-07-26 04:01:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// look for short circuit cases
|
|
|
|
ZonePtr z0 = _vm->hitZone(kZonePath, x, y);
|
2008-07-28 23:21:03 +00:00
|
|
|
if (!z0) {
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.push_back(dest);
|
2008-07-26 04:01:11 +00:00
|
|
|
debugC(3, kDebugWalk, "buildPath: corner case 0");
|
2008-07-26 05:56:39 +00:00
|
|
|
return;
|
2008-07-26 04:01:11 +00:00
|
|
|
}
|
|
|
|
ZonePtr z1 = _vm->hitZone(kZonePath, foot.x, foot.y);
|
2008-07-28 23:21:03 +00:00
|
|
|
if (!z1 || z1 == z0) {
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.push_back(dest);
|
2008-07-26 04:01:11 +00:00
|
|
|
debugC(3, kDebugWalk, "buildPath: corner case 1");
|
2008-07-26 05:56:39 +00:00
|
|
|
return;
|
2008-07-26 04:01:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// build complex path
|
|
|
|
int id = atoi(z0->_name);
|
|
|
|
|
|
|
|
if (z1->u.path->_lists[id].empty()) {
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.clear();
|
2008-07-26 04:01:11 +00:00
|
|
|
debugC(3, kDebugWalk, "buildPath: no path");
|
2008-07-26 05:56:39 +00:00
|
|
|
return;
|
2008-07-26 04:01:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PointList::iterator b = z1->u.path->_lists[id].begin();
|
|
|
|
PointList::iterator e = z1->u.path->_lists[id].end();
|
|
|
|
for ( ; b != e; b++) {
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.push_front(*b);
|
2008-07-26 04:01:11 +00:00
|
|
|
}
|
2008-07-26 05:56:39 +00:00
|
|
|
_ch->_walkPath.push_back(dest);
|
2008-07-26 04:01:11 +00:00
|
|
|
debugC(3, kDebugWalk, "buildPath: complex path");
|
|
|
|
|
2008-07-26 05:56:39 +00:00
|
|
|
return;
|
2008-07-26 04:01:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PathBuilder_BR::PathBuilder_BR(Character *ch) : PathBuilder(ch) {
|
|
|
|
}
|
2007-04-07 10:02:59 +00:00
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
void PathWalker_BR::finalizeWalk() {
|
|
|
|
_engineFlags &= ~kEngineWalking;
|
|
|
|
_first = true;
|
|
|
|
_fieldC = 1;
|
2008-07-27 10:37:54 +00:00
|
|
|
|
|
|
|
Common::Point foot;
|
|
|
|
_ch->getFoot(foot);
|
|
|
|
|
|
|
|
ZonePtr z = _vm->hitZone(kZoneDoor, foot.x, foot.y);
|
2008-07-28 23:21:03 +00:00
|
|
|
if (z && ((z->_flags & kFlagsClosed) == 0)) {
|
2008-07-27 10:37:54 +00:00
|
|
|
_vm->_location._startPosition = z->u.door->_startPos; // foot pos
|
|
|
|
_vm->_location._startFrame = z->u.door->_startFrame;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// TODO: implement working follower. Must find out a location in which the code is
|
|
|
|
// used and which is stable enough.
|
|
|
|
_followerFootInit.x = -1;
|
|
|
|
if (_follower && z->u.door->startPos2.x != -1) {
|
|
|
|
_followerFootInit.x = z->u.door->startPos2.x; // foot pos
|
|
|
|
_followerFootInit.y = z->u.door->startPos2.y; // foot pos
|
|
|
|
}
|
|
|
|
_followerFootInit.z = -1;
|
|
|
|
if (_follower && z->u.door->startPos2.z != -1) {
|
|
|
|
_followerFootInit.z = z->u.door->startPos2.z; // foot pos
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
_vm->scheduleLocationSwitch(z->u.door->_location);
|
|
|
|
_vm->_cmdExec->run(z->_commands, z);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// TODO: Input::walkTo must be extended to support destination frame in addition to coordinates
|
|
|
|
// TODO: the frame argument must be passed to PathWalker through PathBuilder, so probably
|
|
|
|
// a merge between the two Path managers is the right solution
|
|
|
|
if (_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput()
|
|
|
|
_engineFlags &= ~FINAL_WALK_FRAME;
|
|
|
|
_char.ani->_frame = _moveToF; // from readInput()...
|
|
|
|
} else {
|
|
|
|
_char.ani->_frame = _dirFrame; // from walk()
|
|
|
|
}
|
|
|
|
_char.setFoot(foot);
|
|
|
|
#endif
|
|
|
|
|
2008-08-15 04:30:45 +00:00
|
|
|
_ch->_ani->setF(_dirFrame); // temporary solution
|
2008-07-27 10:37:54 +00:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
// TODO: support scrolling ;)
|
|
|
|
if (foot.x > _gfx->hscroll + 600) _gfx->scrollRight(78);
|
|
|
|
if (foot.x < _gfx->hscroll + 40) _gfx->scrollLeft(78);
|
|
|
|
if (foot.y > 350) _gfx->scrollDown(100);
|
|
|
|
if (foot.y < 80) _gfx->scrollUp(100);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return;
|
2008-07-27 08:35:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PathWalker_BR::walk() {
|
|
|
|
if ((_engineFlags & kEngineWalking) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-27 10:37:54 +00:00
|
|
|
#if 0
|
|
|
|
// TODO: support delays in walking. This requires extending Input::walkIo().
|
2008-07-27 08:35:00 +00:00
|
|
|
if (ch._walkDelay > 0) {
|
|
|
|
ch._walkDelay--;
|
|
|
|
if (ch._walkDelay == 0 && _ch._ani->_scriptName) {
|
|
|
|
// stop script and reset
|
|
|
|
_ch._ani->_flags &= ~kFlagsActing;
|
|
|
|
Script *script = findScript(_ch._ani->_scriptName);
|
|
|
|
script->_nextCommand = script->firstCommand;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2008-07-27 10:37:54 +00:00
|
|
|
#endif
|
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
GfxObj *obj = _ch->_ani->gfxobj;
|
|
|
|
|
|
|
|
Common::Rect rect;
|
2008-08-15 04:30:45 +00:00
|
|
|
obj->getRect(_ch->_ani->getF(), rect);
|
2008-07-27 08:35:00 +00:00
|
|
|
|
|
|
|
uint scale;
|
|
|
|
if (rect.bottom > _vm->_location._zeta0) {
|
|
|
|
scale = 100;
|
|
|
|
} else
|
|
|
|
if (rect.bottom < _vm->_location._zeta1) {
|
|
|
|
scale = _vm->_location._zeta2;
|
|
|
|
} else {
|
|
|
|
scale = _vm->_location._zeta2 + ((rect.bottom - _vm->_location._zeta1) * (100 - _vm->_location._zeta2)) / (_vm->_location._zeta0 - _vm->_location._zeta1);
|
|
|
|
}
|
|
|
|
int xStep = (scale * 16) / 100 + 1;
|
|
|
|
int yStep = (scale * 10) / 100 + 1;
|
|
|
|
|
|
|
|
debugC(9, kDebugWalk, "calculated step: (%i, %i)\n", xStep, yStep);
|
2008-07-27 10:37:54 +00:00
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
if (_fieldC == 0) {
|
|
|
|
_ch->_walkPath.erase(_ch->_walkPath.begin());
|
|
|
|
|
|
|
|
if (_ch->_walkPath.empty()) {
|
|
|
|
finalizeWalk();
|
|
|
|
debugC(3, kDebugWalk, "PathWalker_BR::walk, case 0\n");
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
debugC(3, kDebugWalk, "PathWalker_BR::walk, moving to next node\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ch->getFoot(_startFoot);
|
|
|
|
|
|
|
|
_fieldC = 0;
|
|
|
|
_step++;
|
|
|
|
_step %= 8;
|
|
|
|
|
2008-12-06 04:51:04 +00:00
|
|
|
|
|
|
|
int maxX = _vm->_gfx->_backgroundInfo->width;
|
|
|
|
int minX = 0;
|
|
|
|
int maxY = _vm->_gfx->_backgroundInfo->height;
|
|
|
|
int minY = 0;
|
|
|
|
|
2008-07-27 08:35:00 +00:00
|
|
|
int walkFrame = _step;
|
2008-07-27 10:37:54 +00:00
|
|
|
_dirFrame = 0;
|
2008-07-27 08:35:00 +00:00
|
|
|
Common::Point newpos(_startFoot), delta;
|
|
|
|
|
|
|
|
Common::Point p(*_ch->_walkPath.begin());
|
|
|
|
|
2008-12-06 04:51:04 +00:00
|
|
|
if (_startFoot.y < p.y && _startFoot.y < maxY && IS_PATH_CLEAR(_startFoot.x, yStep + _startFoot.y)) {
|
2008-07-27 08:35:00 +00:00
|
|
|
if (yStep + _startFoot.y <= p.y) {
|
|
|
|
_fieldC = 1;
|
|
|
|
delta.y = yStep;
|
|
|
|
newpos.y = yStep + _startFoot.y;
|
|
|
|
} else {
|
|
|
|
delta.y = p.y - _startFoot.y;
|
|
|
|
newpos.y = p.y;
|
|
|
|
}
|
2008-07-27 10:37:54 +00:00
|
|
|
_dirFrame = 9;
|
2008-07-27 08:35:00 +00:00
|
|
|
} else
|
2008-12-06 04:51:04 +00:00
|
|
|
if (_startFoot.y > p.y && _startFoot.y > minY && IS_PATH_CLEAR(_startFoot.x, _startFoot.y - yStep)) {
|
2008-07-27 08:35:00 +00:00
|
|
|
if (_startFoot.y - yStep >= p.y) {
|
|
|
|
_fieldC = 1;
|
|
|
|
delta.y = yStep;
|
|
|
|
newpos.y = _startFoot.y - yStep;
|
|
|
|
} else {
|
|
|
|
delta.y = _startFoot.y - p.y;
|
|
|
|
newpos.y = p.y;
|
|
|
|
}
|
2008-07-27 10:37:54 +00:00
|
|
|
_dirFrame = 0;
|
2008-07-27 08:35:00 +00:00
|
|
|
}
|
|
|
|
|
2008-12-06 04:51:04 +00:00
|
|
|
if (_startFoot.x < p.x && _startFoot.x < maxX && IS_PATH_CLEAR(_startFoot.x + xStep, _startFoot.y)) {
|
2008-07-27 08:35:00 +00:00
|
|
|
if (_startFoot.x + xStep <= p.x) {
|
|
|
|
_fieldC = 1;
|
|
|
|
delta.x = xStep;
|
|
|
|
newpos.x = xStep + _startFoot.x;
|
|
|
|
} else {
|
|
|
|
delta.x = p.x - _startFoot.x;
|
|
|
|
newpos.x = p.x;
|
|
|
|
}
|
|
|
|
if (delta.y < delta.x) {
|
2008-07-27 10:37:54 +00:00
|
|
|
_dirFrame = 18; // right
|
2008-07-27 08:35:00 +00:00
|
|
|
}
|
|
|
|
} else
|
2008-12-06 04:51:04 +00:00
|
|
|
if (_startFoot.x > p.x && _startFoot.x > minX && IS_PATH_CLEAR(_startFoot.x - xStep, _startFoot.y)) {
|
2008-07-27 08:35:00 +00:00
|
|
|
if (_startFoot.x - xStep >= p.x) {
|
|
|
|
_fieldC = 1;
|
|
|
|
delta.x = xStep;
|
|
|
|
newpos.x = _startFoot.x - xStep;
|
|
|
|
} else {
|
|
|
|
delta.x = _startFoot.x - p.x;
|
|
|
|
newpos.x = p.x;
|
|
|
|
}
|
|
|
|
if (delta.y < delta.x) {
|
2008-07-27 10:37:54 +00:00
|
|
|
_dirFrame = 27; // left
|
2008-07-27 08:35:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debugC(9, kDebugWalk, "foot (%i, %i) dest (%i, %i) deltas = %i/%i \n", _startFoot.x, _startFoot.y, p.x, p.y, delta.x, delta.y);
|
|
|
|
|
|
|
|
if (_fieldC) {
|
|
|
|
debugC(9, kDebugWalk, "PathWalker_BR::walk, foot moved from (%i, %i) to (%i, %i)\n", _startFoot.x, _startFoot.y, newpos.x, newpos.y);
|
2008-08-15 04:30:45 +00:00
|
|
|
_ch->_ani->setF(walkFrame + _dirFrame + 1);
|
2008-07-27 08:35:00 +00:00
|
|
|
_startFoot.x = newpos.x;
|
|
|
|
_startFoot.y = newpos.y;
|
|
|
|
_ch->setFoot(_startFoot);
|
2008-08-15 04:30:45 +00:00
|
|
|
_ch->_ani->setZ(newpos.y);
|
2008-07-27 08:35:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_fieldC || !_ch->_walkPath.empty()) {
|
|
|
|
// checkTrap();
|
|
|
|
debugC(3, kDebugWalk, "PathWalker_BR::walk, case 1\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
debugC(3, kDebugWalk, "PathWalker_BR::walk, case 2\n");
|
|
|
|
finalizeWalk();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PathWalker_BR::PathWalker_BR(Character *ch) : PathWalker(ch), _fieldC(1), _first(true) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-14 21:29:12 +00:00
|
|
|
} // namespace Parallaction
|