diff options
Diffstat (limited to 'engines/bbvs/walk.cpp')
-rw-r--r-- | engines/bbvs/walk.cpp | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/engines/bbvs/walk.cpp b/engines/bbvs/walk.cpp new file mode 100644 index 0000000000..5ef14101a0 --- /dev/null +++ b/engines/bbvs/walk.cpp @@ -0,0 +1,464 @@ +/* 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 = destPt.x - sourcePt.x; + const float ptDeltaY = destPt.y - sourcePt.y; + const float wDeltaX = walkInfo->x - sourcePt.x; + const float wDeltaY = walkInfo->y - sourcePt.y; + if (destPt.x == sourcePt.x) + return true; + 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 |