mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-21 09:21:08 +00:00
1800f9d8dc
The bug was caused by a check introduced by me to avoid division-by-zero errors when the source and dest x values are equal. This had the side effect that it didn't work well in this case outlined in the bug report, maybe also in other places. I'm not sure how to handle a DBZ correctly here so I'm setting the x delta to 1.0 if it would normally be 0.0, which seems to work after walking around in some scenes.
463 lines
15 KiB
C++
463 lines
15 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 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.
|
|
*
|
|
*/
|
|
|
|
#include "bbvs/bbvs.h"
|
|
#include "bbvs/gamemodule.h"
|
|
|
|
namespace Bbvs {
|
|
|
|
static const int8 kTurnInfo[8][8] = {
|
|
{ 0, 1, 1, 1, 1, -1, -1, -1},
|
|
{-1, 0, 1, 1, 1, 1, -1, -1},
|
|
{-1, -1, 0, 1, 1, 1, 1, -1},
|
|
{-1, -1, -1, 0, 1, 1, 1, 1},
|
|
{ 1, -1, -1, -1, 0, 1, 1, 1},
|
|
{ 1, 1, -1, -1, -1, 0, 1, 1},
|
|
{ 1, 1, 1, -1, -1, -1, 0, 1},
|
|
{ 1, 1, 1, 1, -1, -1, -1, 0}
|
|
};
|
|
|
|
static const int8 kWalkAnimTbl[32] = {
|
|
3, 0, 0, 0, 2, 1, 1, 1,
|
|
15, 12, 14, 13, 0, 0, 0, 0,
|
|
7, 9, 4, 8, 6, 10, 5, 11,
|
|
3, 0, 2, 1, 15, 12, 14, 13
|
|
};
|
|
|
|
void BbvsEngine::startWalkObject(SceneObject *sceneObject) {
|
|
if (_buttheadObject != sceneObject && _beavisObject != sceneObject)
|
|
return;
|
|
|
|
initWalkAreas(sceneObject);
|
|
_sourceWalkAreaPt.x = sceneObject->x >> 16;
|
|
_sourceWalkAreaPt.y = sceneObject->y >> 16;
|
|
|
|
_sourceWalkArea = getWalkAreaAtPos(_sourceWalkAreaPt);
|
|
if (!_sourceWalkArea)
|
|
return;
|
|
|
|
_destWalkAreaPt = sceneObject->walkDestPt;
|
|
|
|
_destWalkArea = getWalkAreaAtPos(_destWalkAreaPt);
|
|
if (!_destWalkArea)
|
|
return;
|
|
|
|
if (_sourceWalkArea != _destWalkArea) {
|
|
_currWalkDistance = kMaxDistance;
|
|
walkFindPath(_sourceWalkArea, 0);
|
|
_destWalkAreaPt = _currWalkDistance == kMaxDistance ? _sourceWalkAreaPt : _finalWalkPt;
|
|
}
|
|
|
|
walkObject(sceneObject, _destWalkAreaPt, sceneObject->sceneObjectDef->walkSpeed);
|
|
|
|
}
|
|
|
|
void BbvsEngine::updateWalkObject(SceneObject *sceneObject) {
|
|
int animIndex;
|
|
|
|
if (sceneObject->walkCount > 0 && (sceneObject->xIncr != 0 || sceneObject->yIncr != 0)) {
|
|
if (ABS(sceneObject->xIncr) <= ABS(sceneObject->yIncr))
|
|
sceneObject->turnValue = sceneObject->yIncr >= 0 ? 0 : 4;
|
|
else
|
|
sceneObject->turnValue = sceneObject->xIncr >= 0 ? 6 : 2;
|
|
animIndex = sceneObject->sceneObjectDef->animIndices[kWalkAnimTbl[sceneObject->turnValue]];
|
|
sceneObject->turnCount = 0;
|
|
sceneObject->turnTicks = 0;
|
|
} else {
|
|
animIndex = sceneObject->sceneObjectDef->animIndices[kWalkTurnTbl[sceneObject->turnValue]];
|
|
}
|
|
|
|
Animation *anim = 0;
|
|
if (animIndex > 0)
|
|
anim = _gameModule->getAnimation(animIndex);
|
|
|
|
if (sceneObject->anim != anim) {
|
|
if (anim) {
|
|
sceneObject->anim = anim;
|
|
sceneObject->animIndex = animIndex;
|
|
sceneObject->frameTicks = 1;
|
|
sceneObject->frameIndex = anim->frameCount - 1;
|
|
} else {
|
|
sceneObject->anim = 0;
|
|
sceneObject->animIndex = 0;
|
|
sceneObject->frameTicks = 0;
|
|
sceneObject->frameIndex = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void BbvsEngine::walkObject(SceneObject *sceneObject, const Common::Point &destPt, int walkSpeed) {
|
|
int deltaX = destPt.x - (sceneObject->x >> 16);
|
|
int deltaY = destPt.y - (sceneObject->y >> 16);
|
|
float distance = (float)sqrt((double)(deltaX * deltaX + deltaY * deltaY));
|
|
// NOTE The original doesn't have this check but without it the whole pathfinding breaks
|
|
if (distance > 0.0f) {
|
|
sceneObject->walkCount = (int)(distance / ((((float)ABS(deltaX) / distance) + 1.0f) * ((float)walkSpeed / 120)));
|
|
sceneObject->xIncr = (int)(((float)deltaX / sceneObject->walkCount) * 65536.0f);
|
|
sceneObject->yIncr = (int)(((float)deltaY / sceneObject->walkCount) * 65536.0f);
|
|
sceneObject->x = (sceneObject->x & 0xFFFF0000) | 0x8000;
|
|
sceneObject->y = (sceneObject->y & 0xFFFF0000) | 0x8000;
|
|
} else
|
|
sceneObject->walkCount = 0;
|
|
}
|
|
|
|
void BbvsEngine::turnObject(SceneObject *sceneObject) {
|
|
if (sceneObject->turnTicks > 0) {
|
|
--sceneObject->turnTicks;
|
|
} else {
|
|
int turnDir = kTurnInfo[sceneObject->turnValue][sceneObject->turnCount & 0x7F];
|
|
if (turnDir) {
|
|
sceneObject->turnValue = (sceneObject->turnValue + turnDir) & 7;
|
|
int turnAnimIndex = sceneObject->sceneObjectDef->animIndices[kWalkTurnTbl[sceneObject->turnValue]];
|
|
if (turnAnimIndex) {
|
|
Animation *anim = _gameModule->getAnimation(turnAnimIndex);
|
|
if (anim) {
|
|
sceneObject->anim = anim;
|
|
sceneObject->animIndex = turnAnimIndex;
|
|
sceneObject->turnTicks = 4;
|
|
sceneObject->frameTicks = 1;
|
|
sceneObject->frameIndex = anim->frameCount - 1;
|
|
}
|
|
}
|
|
} else {
|
|
sceneObject->turnCount = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int BbvsEngine::rectSubtract(const Common::Rect &rect1, const Common::Rect &rect2, Common::Rect *outRects) {
|
|
int count = 0;
|
|
Common::Rect workRect = rect1.findIntersectingRect(rect2);
|
|
if (!workRect.isEmpty()) {
|
|
count = 0;
|
|
outRects[count] = Common::Rect(rect2.width(), workRect.top - rect2.top);
|
|
if (!outRects[count].isEmpty()) {
|
|
outRects[count].translate(rect2.left, rect2.top);
|
|
++count;
|
|
}
|
|
outRects[count] = Common::Rect(workRect.left - rect2.left, workRect.height());
|
|
if (!outRects[count].isEmpty()) {
|
|
outRects[count].translate(rect2.left, workRect.top);
|
|
++count;
|
|
}
|
|
outRects[count] = Common::Rect(rect2.right - workRect.right, workRect.height());
|
|
if (!outRects[count].isEmpty()) {
|
|
outRects[count].translate(workRect.right, workRect.top);
|
|
++count;
|
|
}
|
|
outRects[count] = Common::Rect(rect2.width(), rect2.bottom - workRect.bottom);
|
|
if (!outRects[count].isEmpty()) {
|
|
outRects[count].translate(rect2.left, workRect.bottom);
|
|
++count;
|
|
}
|
|
} else {
|
|
outRects[0] = rect2;
|
|
count = 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
WalkInfo *BbvsEngine::addWalkInfo(int16 x, int16 y, int delta, int direction, int16 midPtX, int16 midPtY, int walkAreaIndex) {
|
|
WalkInfo *walkInfo = &_walkInfos[_walkInfosCount++];
|
|
walkInfo->walkAreaIndex = walkAreaIndex;
|
|
walkInfo->direction = direction;
|
|
walkInfo->x = x;
|
|
walkInfo->y = y;
|
|
walkInfo->delta = delta;
|
|
walkInfo->midPt.x = midPtX;
|
|
walkInfo->midPt.y = midPtY;
|
|
return walkInfo;
|
|
}
|
|
|
|
void BbvsEngine::initWalkAreas(SceneObject *sceneObject) {
|
|
int16 objX = sceneObject->x >> 16;
|
|
int16 objY = sceneObject->y >> 16;
|
|
Common::Rect rect;
|
|
bool doRect = false;
|
|
Common::Rect *workWalkableRects;
|
|
|
|
if (_buttheadObject == sceneObject && _beavisObject->anim) {
|
|
rect = _beavisObject->anim->frameRects2[_beavisObject->frameIndex];
|
|
rect.translate(_beavisObject->x >> 16, 1 + (_beavisObject->y >> 16));
|
|
doRect = !rect.isEmpty();
|
|
} else if (_buttheadObject->anim) {
|
|
rect = _buttheadObject->anim->frameRects2[_buttheadObject->frameIndex];
|
|
rect.translate(_buttheadObject->x >> 16, 1 + (_buttheadObject->y >> 16));
|
|
doRect = !rect.isEmpty();
|
|
}
|
|
|
|
workWalkableRects = _walkableRects;
|
|
|
|
_walkAreasCount = _walkableRectsCount;
|
|
|
|
if (doRect && !rect.contains(objX, objY)) {
|
|
_walkAreasCount = 0;
|
|
for (int i = 0; i < _walkableRectsCount; ++i)
|
|
_walkAreasCount += rectSubtract(rect, _walkableRects[i], &_tempWalkableRects1[_walkAreasCount]);
|
|
workWalkableRects = _tempWalkableRects1;
|
|
}
|
|
|
|
for (int i = 0; i < _walkAreasCount; ++i) {
|
|
_walkAreas[i].x = workWalkableRects[i].left;
|
|
_walkAreas[i].y = workWalkableRects[i].top;
|
|
_walkAreas[i].width = workWalkableRects[i].width();
|
|
_walkAreas[i].height = workWalkableRects[i].height();
|
|
_walkAreas[i].checked = false;
|
|
_walkAreas[i].linksCount = 0;
|
|
}
|
|
|
|
_walkInfosCount = 0;
|
|
|
|
// Find connections between the walkRects
|
|
|
|
for (int i = 0; i < _walkAreasCount; ++i) {
|
|
WalkArea *walkArea1 = &_walkAreas[i];
|
|
int xIter = walkArea1->x + walkArea1->width;
|
|
int yIter = walkArea1->y + walkArea1->height;
|
|
|
|
for (int j = 0; j < _walkAreasCount; ++j) {
|
|
WalkArea *walkArea2 = &_walkAreas[j];
|
|
|
|
if (i == j)
|
|
continue;
|
|
|
|
if (walkArea2->y == yIter) {
|
|
int wa1x = MAX(walkArea1->x, walkArea2->x);
|
|
int wa2x = MIN(walkArea2->x + walkArea2->width, xIter);
|
|
if (wa2x > wa1x) {
|
|
debug(5, "WalkArea %d connected to %d by Y", i, j);
|
|
WalkInfo *walkInfo1 = addWalkInfo(wa1x, yIter - 1, wa2x - wa1x, 0, wa1x + (wa2x - wa1x) / 2, yIter - 1, i);
|
|
WalkInfo *walkInfo2 = addWalkInfo(wa1x, yIter, wa2x - wa1x, 0, wa1x + (wa2x - wa1x) / 2, yIter, j);
|
|
walkArea1->linksD1[walkArea1->linksCount] = walkInfo1;
|
|
walkArea1->linksD2[walkArea1->linksCount] = walkInfo2;
|
|
walkArea1->links[walkArea1->linksCount++] = walkArea2;
|
|
walkArea2->linksD1[walkArea2->linksCount] = walkInfo2;
|
|
walkArea2->linksD2[walkArea2->linksCount] = walkInfo1;
|
|
walkArea2->links[walkArea2->linksCount++] = walkArea1;
|
|
}
|
|
}
|
|
|
|
if (walkArea2->x == xIter) {
|
|
int wa1y = MAX(walkArea1->y, walkArea2->y);
|
|
int wa2y = MIN(walkArea2->y + walkArea2->height, yIter);
|
|
if (wa2y > wa1y) {
|
|
debug(5, "WalkArea %d connected to %d by X", i, j);
|
|
WalkInfo *walkInfo1 = addWalkInfo(xIter - 1, wa1y, wa2y - wa1y, 1, xIter - 1, wa1y + (wa2y - wa1y) / 2, i);
|
|
WalkInfo *walkInfo2 = addWalkInfo(xIter, wa1y, wa2y - wa1y, 1, xIter, wa1y + (wa2y - wa1y) / 2, j);
|
|
walkArea1->linksD1[walkArea1->linksCount] = walkInfo1;
|
|
walkArea1->linksD2[walkArea1->linksCount] = walkInfo2;
|
|
walkArea1->links[walkArea1->linksCount++] = walkArea2;
|
|
walkArea2->linksD1[walkArea2->linksCount] = walkInfo2;
|
|
walkArea2->linksD2[walkArea2->linksCount] = walkInfo1;
|
|
walkArea2->links[walkArea2->linksCount++] = walkArea1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WalkArea *BbvsEngine::getWalkAreaAtPos(const Common::Point &pt) {
|
|
for (int i = 0; i < _walkAreasCount; ++i) {
|
|
WalkArea *walkArea = &_walkAreas[i];
|
|
if (walkArea->contains(pt))
|
|
return walkArea;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool BbvsEngine::canButtheadWalkToDest(const Common::Point &destPt) {
|
|
Common::Point srcPt;
|
|
|
|
_walkReachedDestArea = false;
|
|
initWalkAreas(_buttheadObject);
|
|
srcPt.x = _buttheadObject->x >> 16;
|
|
srcPt.y = _buttheadObject->y >> 16;
|
|
_sourceWalkArea = getWalkAreaAtPos(srcPt);
|
|
if (_sourceWalkArea) {
|
|
_destWalkArea = getWalkAreaAtPos(destPt);
|
|
if (_destWalkArea)
|
|
canWalkToDest(_sourceWalkArea, 0);
|
|
}
|
|
return _walkReachedDestArea;
|
|
}
|
|
|
|
void BbvsEngine::canWalkToDest(WalkArea *walkArea, int infoCount) {
|
|
|
|
if (_destWalkArea == walkArea) {
|
|
_walkReachedDestArea = true;
|
|
return;
|
|
}
|
|
|
|
if (_gameModule->getFieldC() <= 320 || infoCount <= 20) {
|
|
walkArea->checked = true;
|
|
for (int linkIndex = 0; linkIndex < walkArea->linksCount; ++linkIndex) {
|
|
if (!walkArea->links[linkIndex]->checked) {
|
|
canWalkToDest(walkArea->links[linkIndex], infoCount + 2);
|
|
if (_walkReachedDestArea)
|
|
break;
|
|
}
|
|
}
|
|
walkArea->checked = false;
|
|
}
|
|
|
|
}
|
|
|
|
bool BbvsEngine::walkTestLineWalkable(const Common::Point &sourcePt, const Common::Point &destPt, WalkInfo *walkInfo) {
|
|
const float ptDeltaX = MAX<float>(destPt.x - sourcePt.x, 1.0f);
|
|
const float ptDeltaY = destPt.y - sourcePt.y;
|
|
const float wDeltaX = walkInfo->x - sourcePt.x;
|
|
const float wDeltaY = walkInfo->y - sourcePt.y;
|
|
if (walkInfo->direction) {
|
|
const float nDeltaY = wDeltaX * ptDeltaY / ptDeltaX + (float)sourcePt.y - (float)walkInfo->y;
|
|
return (nDeltaY >= 0.0f) && (nDeltaY < (float)walkInfo->delta);
|
|
} else {
|
|
const float nDeltaX = wDeltaY / ptDeltaX * ptDeltaY + (float)sourcePt.x - (float)walkInfo->x;
|
|
return (nDeltaX >= 0.0f) && (nDeltaX < (float)walkInfo->delta);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void BbvsEngine::walkFindPath(WalkArea *sourceWalkArea, int infoCount) {
|
|
if (_destWalkArea == sourceWalkArea) {
|
|
walkFoundPath(infoCount);
|
|
} else if (_gameModule->getFieldC() <= 320 || infoCount <= 20) {
|
|
sourceWalkArea->checked = true;
|
|
for (int linkIndex = 0; linkIndex < sourceWalkArea->linksCount; ++linkIndex) {
|
|
if (!sourceWalkArea->links[linkIndex]->checked) {
|
|
_walkInfoPtrs[infoCount + 0] = sourceWalkArea->linksD1[linkIndex];
|
|
_walkInfoPtrs[infoCount + 1] = sourceWalkArea->linksD2[linkIndex];
|
|
walkFindPath(sourceWalkArea->links[linkIndex], infoCount + 2);
|
|
}
|
|
}
|
|
sourceWalkArea->checked = false;
|
|
}
|
|
}
|
|
|
|
int BbvsEngine::calcDistance(const Common::Point &pt1, const Common::Point &pt2) {
|
|
return (int)sqrt((double)(pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y));
|
|
}
|
|
|
|
void BbvsEngine::walkFoundPath(int count) {
|
|
debug(5, "BbvsEngine::walkFoundPath(%d)", count);
|
|
|
|
Common::Point midPt = _sourceWalkAreaPt;
|
|
int totalMidPtDistance = 0;
|
|
|
|
if (count > 0) {
|
|
Common::Point lastMidPt;
|
|
int halfCount = (count + 1) >> 1;
|
|
for (int i = 0; i < halfCount; ++i) {
|
|
lastMidPt = midPt;
|
|
midPt = _walkInfoPtrs[i * 2]->midPt;
|
|
totalMidPtDistance += calcDistance(midPt, lastMidPt);
|
|
}
|
|
}
|
|
|
|
int distance = calcDistance(midPt, _destWalkAreaPt) + totalMidPtDistance;
|
|
|
|
debug(5, "BbvsEngine::walkFoundPath() distance: %d; _currWalkDistance: %d", distance, _currWalkDistance);
|
|
|
|
if (distance >= _currWalkDistance)
|
|
return;
|
|
|
|
debug(5, "BbvsEngine::walkFoundPath() distance smaller");
|
|
|
|
_currWalkDistance = distance;
|
|
|
|
Common::Point destPt = _destWalkAreaPt, newDestPt;
|
|
|
|
while (1) {
|
|
|
|
int index = 0;
|
|
if (count > 0) {
|
|
do {
|
|
if (!walkTestLineWalkable(_sourceWalkAreaPt, destPt, _walkInfoPtrs[index]))
|
|
break;
|
|
++index;
|
|
} while (index < count);
|
|
}
|
|
|
|
if (index == count)
|
|
break;
|
|
|
|
WalkInfo *walkInfo = _walkInfoPtrs[--count];
|
|
destPt.x = walkInfo->x;
|
|
destPt.y = walkInfo->y;
|
|
|
|
if (walkInfo->direction) {
|
|
newDestPt.x = walkInfo->x;
|
|
newDestPt.y = walkInfo->y + walkInfo->delta - 1;
|
|
} else {
|
|
newDestPt.x = walkInfo->x + walkInfo->delta - 1;
|
|
newDestPt.y = walkInfo->y;
|
|
}
|
|
|
|
if ((newDestPt.x - _destWalkAreaPt.x) * (newDestPt.x - _destWalkAreaPt.x) +
|
|
(newDestPt.y - _destWalkAreaPt.y) * (newDestPt.y - _destWalkAreaPt.y) <
|
|
(destPt.x - _destWalkAreaPt.x) * (destPt.x - _destWalkAreaPt.x) +
|
|
(destPt.y - _destWalkAreaPt.y) * (destPt.y - _destWalkAreaPt.y))
|
|
destPt = newDestPt;
|
|
|
|
}
|
|
|
|
debug(5, "BbvsEngine::walkFoundPath() destPt: (%d, %d)", destPt.x, destPt.y);
|
|
|
|
_finalWalkPt = destPt;
|
|
|
|
debug(5, "BbvsEngine::walkFoundPath() OK");
|
|
|
|
}
|
|
|
|
void BbvsEngine::updateWalkableRects() {
|
|
// Go through all walkable rects and subtract all scene object rects
|
|
Common::Rect *rectsList1 = _tempWalkableRects1;
|
|
Common::Rect *rectsList2 = _gameModule->getWalkRects();
|
|
_walkableRectsCount = _gameModule->getWalkRectsCount();
|
|
for (int i = 0; i < _gameModule->getSceneObjectDefsCount(); ++i) {
|
|
SceneObject *sceneObject = &_sceneObjects[i];
|
|
Animation *anim = sceneObject->anim;
|
|
if (anim && _buttheadObject != sceneObject && _beavisObject != sceneObject) {
|
|
Common::Rect rect = sceneObject->anim->frameRects2[sceneObject->frameIndex];
|
|
rect.translate(sceneObject->x >> 16, sceneObject->y >> 16);
|
|
int count = _walkableRectsCount;
|
|
_walkableRectsCount = 0;
|
|
for (int j = 0; j < count; ++j)
|
|
_walkableRectsCount += rectSubtract(rect, rectsList2[j], &rectsList1[_walkableRectsCount]);
|
|
if (rectsList1 == _tempWalkableRects1) {
|
|
rectsList1 = _tempWalkableRects2;
|
|
rectsList2 = _tempWalkableRects1;
|
|
} else {
|
|
rectsList1 = _tempWalkableRects1;
|
|
rectsList2 = _tempWalkableRects2;
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < _walkableRectsCount; ++i)
|
|
_walkableRects[i] = rectsList2[i];
|
|
}
|
|
|
|
} // End of namespace Bbvs
|