diff options
Diffstat (limited to 'engines/scumm/actor.cpp')
-rw-r--r-- | engines/scumm/actor.cpp | 589 |
1 files changed, 489 insertions, 100 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index 116a953b0b..90ee5bf368 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -33,6 +33,7 @@ #include "scumm/resource.h" #include "scumm/saveload.h" #include "scumm/scumm_v7.h" +#include "scumm/scumm_v0.h" #include "scumm/he/sound_he.h" #include "scumm/he/sprite_he.h" #include "scumm/usage_bits.h" @@ -50,6 +51,12 @@ static const byte v0ActorTalkArray[0x19] = { 0xC0, 0xC0, 0x00, 0x06, 0x06 }; +static const byte v0WalkboxSlantedModifier[0x16] = { + 0x00,0x01,0x02,0x03,0x03,0x04,0x05,0x06, + 0x06,0x07,0x08,0x09,0x09,0x0A,0x0B, + 0x0C,0x0C,0x0D,0x0E,0x0F,0x10,0x10 +}; + Actor::Actor(ScummEngine *scumm, int id) : _vm(scumm), _number(id) { assert(_vm != 0); @@ -182,6 +189,20 @@ void Actor_v0::initActor(int mode) { _costCommand = 0xFF; _miscflags = 0; _speaking = 0; + + _walkCountModulo = 0; + _newWalkBoxEntered = false; + _walkDirX = 0; + _walkDirY = 0; + _walkYCountGreaterThanXCount = 0; + _walkXCount = 0; + _walkXCountInc = 0; + _walkYCount = 0; + _walkYCountInc = 0; + _walkMaxXYCountInc = 0; + + _tmp_WalkBox = 0; + _tmp_NewWalkBoxEntered = 0; _animFrameRepeat = 0; for (int i = 0; i < 8; ++i) { @@ -249,9 +270,12 @@ void Actor::stopActorMoving() { if (_walkScript) _vm->stopScript(_walkScript); - _moving = 0; - if (_vm->_game.version == 0) + if (_vm->_game.version == 0) { + _moving = 2; setDirection(_facing); + } else { + _moving = 0; + } } void Actor::setActorWalkSpeed(uint newSpeedX, uint newSpeedY) { @@ -339,9 +363,6 @@ int Actor::actorWalkStep() { int distX, distY; int nextFacing; - if (_vm->_game.version == 0) - ((Actor_v0 *)this)->_animFrameRepeat = -1; - _needRedraw = true; nextFacing = updateActorDirection(true); @@ -350,10 +371,6 @@ int Actor::actorWalkStep() { startWalkAnim(1, nextFacing); } _moving |= MF_IN_LEG; - - // V0: Don't move during the turn - if (_vm->_game.version == 0) - return 0; } if (_walkbox != _walkdata.curbox && _vm->checkXYInBoxBounds(_walkdata.curbox, _pos.x, _pos.y)) { @@ -389,12 +406,113 @@ int Actor::actorWalkStep() { return 0; } - if (_vm->_game.version == 0) - ((Actor_v0 *)this)->animateActor(newDirToOldDir(_facing)); - return 1; } +bool Actor_v0::calcWalkDistances() { + _walkDirX = 0; + _walkDirY = 0; + _walkYCountGreaterThanXCount = 0; + uint16 A = 0; + + if (_CurrentWalkTo.x >= _tmp_Dest.x ) { + A = _CurrentWalkTo.x - _tmp_Dest.x; + _walkDirX = 1; + } else { + A = _tmp_Dest.x - _CurrentWalkTo.x; + } + + _walkXCountInc = A; + + if (_CurrentWalkTo.y >= _tmp_Dest.y ) { + A = _CurrentWalkTo.y - _tmp_Dest.y; + _walkDirY = 1; + } else { + A = _tmp_Dest.y - _CurrentWalkTo.y; + } + + _walkYCountInc = A; + if ( !_walkXCountInc && !_walkYCountInc ) + return true; + + if( _walkXCountInc <= _walkYCountInc ) + _walkYCountGreaterThanXCount = 1; + + // 2FCC + A = _walkXCountInc; + if( A <= _walkYCountInc ) + A = _walkYCountInc; + + _walkMaxXYCountInc = A; + _walkXCount = _walkXCountInc; + _walkYCount = _walkYCountInc; + _walkCountModulo = _walkMaxXYCountInc; + + return false; +} + +byte Actor_v0::actorWalkX() { + byte A = _walkXCount; + A += _walkXCountInc; + if (A >= _walkCountModulo) { + if (!_walkDirX ) { + _tmp_Dest.x--; + } else { + _tmp_Dest.x++; + } + + A -= _walkCountModulo; + } + // 2EAC + _walkXCount = A; + setTmpFromActor(); + if (updateWalkbox() == kInvalidBox) { + // 2EB9 + setActorFromTmp(); + + return 3; + } + // 2EBF + if (_tmp_Dest.x == _CurrentWalkTo.x) + return 1; + + return 0; +} + +byte Actor_v0::actorWalkY() { + byte A = _walkYCount; + A += _walkYCountInc; + if (A >= _walkCountModulo) { + if (!_walkDirY ) { + _tmp_Dest.y--; + } else { + _tmp_Dest.y++; + } + + A -= _walkCountModulo; + } + // 2EEB + _walkYCount = A; + setTmpFromActor(); + if (updateWalkbox() == kInvalidBox) { + // 2EF8 + setActorFromTmp(); + return 4; + } + // 2EFE + if (_walkYCountInc != 0) { + if (_walkYCountInc == 0xFF) { + setActorFromTmp(); + return 4; + } + } + // 2F0D + if (_CurrentWalkTo.y == _tmp_Dest.y) + return 1; + + return 0; +} + void Actor::startWalkActor(int destX, int destY, int dir) { AdjustBoxResult abr; @@ -447,10 +565,14 @@ void Actor::startWalkActor(int destX, int destY, int dir) { _walkdata.dest.y = abr.y; _walkdata.destbox = abr.box; _walkdata.destdir = dir; - _moving = (_moving & MF_IN_LEG) | MF_NEW_LEG; _walkdata.point3.x = 32000; - _walkdata.curbox = _walkbox; + + if(_vm->_game.version == 0 ) { + ((Actor_v0*)this)->_newWalkBoxEntered = true; + } else { + _moving = (_moving & MF_IN_LEG) | MF_NEW_LEG; + } } void Actor::startWalkAnim(int cmd, int angle) { @@ -567,88 +689,206 @@ void Actor::walkActor() { calcMovementFactor(_walkdata.dest); } -bool Actor_v2::checkWalkboxesHaveDirectPath(Common::Point &foundPath) { - // only MM v0 supports walking in direct line between walkboxes. - // MM v1 already does not support it anymore. - return false; -} +void Actor_v0::walkActor() { + actorSetWalkTo(); -bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, - const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result) -{ - const Common::Point v1 = line1End - line1Start; // line1(n1) = line1Start + n1 * v1 - const Common::Point v2 = line2End - line2Start; // line2(n2) = line2Start + n2 * v2 + _needRedraw = true; + if (_NewWalkTo != _CurrentWalkTo) { + _CurrentWalkTo = _NewWalkTo; - double det = v2.x * v1.y - v1.x * v2.y; - if (det == 0) - return false; +L2A33:; + _moving &= 0xF0; + _tmp_Dest = _pos; - double n1 = ((double)v2.x * (line2Start.y - line1Start.y) - - (double)v2.y * (line2Start.x - line1Start.x)) / det; - double n2 = ((double)v1.x * (line2Start.y - line1Start.y) - - (double)v1.y * (line2Start.x - line1Start.x)) / det; + byte tmp = calcWalkDistances(); + _moving &= 0xF0; + _moving |= tmp; - // both coefficients have to be in [0, 1], otherwise the intersection is - // not inside of at least one of the two line segments - if (n1 < 0.0 || n1 > 1.0 || n2 < 0.0 || n2 > 1.0) - return false; + if (!_walkYCountGreaterThanXCount) { + if (_walkDirX) { + _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*1, V12_Y_MULTIPLIER*0, false); + } else { + _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*-1, V12_Y_MULTIPLIER*0, false); + } + } else { + if (_walkDirY) { + _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*0, V12_Y_MULTIPLIER*1, false); + } else { + _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*0, V12_Y_MULTIPLIER*-1, false); + } + } - result.x = line1Start.x + (int)(n1 * v1.x); - result.y = line1Start.y + (int)(n1 * v1.y); - return true; -} + directionUpdate(); + + if (_moving & 0x80) + return; -/* - * MM v0 allows the actor to walk in a direct line between boxes to the target - * if actor and target share a horizontal or vertical corridor. - * If such a corridor is found the actor is not forced to go horizontally or - * vertically from one box to the next but can also walk diagonally. - * - * Note: the original v0 interpreter sets the target destination for diagonal - * walking only once and then rechecks whenever the actor reaches a new box if the - * walk destination is still suitable for the current box. - * ScummVM does not perform such a check, so it is possible to leave the walkboxes - * in some cases, for example L-shaped rooms like the swimming pool (actor walks over water) - * or the medical room (actor walks over examination table). - * To solve this we intersect the new walk destination with the actor's walkbox borders, - * so a recheck is done when the actor leaves his box. This is done by the - * intersectLineSegments() routine calls. - */ -bool Actor_v0::checkWalkboxesHaveDirectPath(Common::Point &foundPath) { - BoxCoords boxCoords = _vm->getBoxCoordinates(_walkbox); - BoxCoords curBoxCoords = _vm->getBoxCoordinates(_walkdata.curbox); - - // check if next walkbox is left or right to actor's box - if (boxCoords.ll.x > curBoxCoords.lr.x || boxCoords.lr.x < curBoxCoords.ll.x) { - // determine horizontal corridor gates - int gateUpper = MAX(boxCoords.ul.y, curBoxCoords.ul.y); - int gateLower = MIN(boxCoords.ll.y, curBoxCoords.ll.y); - - // check if actor and target are in the same horizontal corridor between the boxes - if ((_pos.y >= gateUpper && _pos.y <= gateLower) && - (_walkdata.dest.y >= gateUpper && _walkdata.dest.y <= gateLower)) { - if (boxCoords.ll.x > curBoxCoords.lr.x) // next box is left - return intersectLineSegments(_pos, _walkdata.dest, boxCoords.ll, boxCoords.ul, foundPath); - else // next box is right - return intersectLineSegments(_pos, _walkdata.dest, boxCoords.lr, boxCoords.ur, foundPath); - } - // check if next walkbox is above or below actor's box - } else if (boxCoords.ul.y > curBoxCoords.ll.y || boxCoords.ll.y < curBoxCoords.ul.y) { - // determine vertical corridor gates - int gateLeft = MAX(boxCoords.ll.x, curBoxCoords.ll.x); - int gateRight = MIN(boxCoords.lr.x, curBoxCoords.lr.x); - - // check if actor and target are in the same vertical corridor between the boxes - if ((_pos.x >= gateLeft && _pos.x <= gateRight) && - (_walkdata.dest.x >= gateLeft && _walkdata.dest.x <= gateRight)) { - if (boxCoords.ul.y > curBoxCoords.ll.y) // next box is above - return intersectLineSegments(_pos, _walkdata.dest, boxCoords.ul, boxCoords.ur, foundPath); - else // next box is below - return intersectLineSegments(_pos, _walkdata.dest, boxCoords.ll, boxCoords.lr, foundPath); + animateActor(newDirToOldDir(_facing)); + + } else { + // 2A0A + if ((_moving & 0x7F) != 1) { + + if (_NewWalkTo == _pos) + return; } } - return false; + // 2A9A + if (_moving == 2) + return; + + if ((_moving & 0x0F) == 1) + return stopActorMoving(); + + // 2AAD + if (_moving & 0x80) { + directionUpdate(); + + if (_moving & 0x80) + return; + + animateActor(newDirToOldDir(_facing)); + } + + if ((_moving & 0x0F) == 3 ) { +L2C36:; + setTmpFromActor(); + + if (!_walkDirX) { + _pos.x--; + } else { + _pos.x++; + } + + // 2C51 + if (updateWalkbox() != kInvalidBox) { + + setActorFromTmp(); + goto L2A33; + } + + setActorFromTmp(); + + if (_CurrentWalkTo.y == _tmp_Dest.y) { + stopActorMoving(); + return; + } + + if (!_walkDirY) { + _tmp_Dest.y--; + } else { + _tmp_Dest.y++; + } + + setTmpFromActor(); + + byte A = updateWalkbox(); + if (A == 0xFF) { + setActorFromTmp(); + stopActorMoving(); + return; + } + // 2C98: Yes, an exact copy of what just occured.. the original does this, so im doing it... + // Just to keep me sane when going over it :) + if (A == 0xFF) { + setActorFromTmp(); + stopActorMoving(); + return; + } + return; + } + + // 2ADA + if ((_moving & 0x0F) == 4) { +L2CA3:; + setTmpFromActor(); + + if (!_walkDirY) { + _pos.y--; + } else { + _pos.y++; + } + if (updateWalkbox() == kInvalidBox) { + // 2CC7 + setActorFromTmp(); + if( _CurrentWalkTo.x == _tmp_Dest.x) { + stopActorMoving(); + return; + } + + if (!_walkDirX) { + _tmp_Dest.x--; + } else { + _tmp_Dest.x++; + } + setTmpFromActor(); + + if (updateWalkbox() == kInvalidBox) { + setActorFromTmp(); + stopActorMoving(); + } + + return; + } else { + setActorFromTmp(); + goto L2A33; + } + } + + if ((_moving & 0x0F) == 0) { + // 2AE8 + byte A = actorWalkX(); + + if (A == 1) { + A = actorWalkY(); + if (A == 1) { + _moving &= 0xF0; + _moving |= A; + } else { + if (A == 4) + stopActorMoving(); + } + + return; + + } else { + // 2B0C + if (A == 3) { + _moving &= 0xF0; + _moving |= A; + + if (_walkDirY) { + _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*0, V12_Y_MULTIPLIER*1, false); + } else { + _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*0, V12_Y_MULTIPLIER*-1, false); + } + + directionUpdate(); + animateActor(newDirToOldDir(_facing)); + goto L2C36; + + } else { + // 2B39 + A = actorWalkY(); + if (A != 4) + return; + + _moving &= 0xF0; + _moving |= A; + + if (_walkDirX) { + _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*1, V12_Y_MULTIPLIER*0, false); + } else { + _targetFacing = getAngleFromPos(V12_X_MULTIPLIER*-1, V12_Y_MULTIPLIER*0, false); + } + + directionUpdate(); + animateActor(newDirToOldDir(_facing)); + goto L2CA3; + } + } + } } void Actor_v2::walkActor() { @@ -697,10 +937,8 @@ void Actor_v2::walkActor() { _walkdata.curbox = next_box; - if (!checkWalkboxesHaveDirectPath(foundPath)) { - getClosestPtOnBox(_vm->getBoxCoordinates(_walkdata.curbox), _pos.x, _pos.y, tmp.x, tmp.y); - getClosestPtOnBox(_vm->getBoxCoordinates(_walkbox), tmp.x, tmp.y, foundPath.x, foundPath.y); - } + getClosestPtOnBox(_vm->getBoxCoordinates(_walkdata.curbox), _pos.x, _pos.y, tmp.x, tmp.y); + getClosestPtOnBox(_vm->getBoxCoordinates(_walkbox), tmp.x, tmp.y, foundPath.x, foundPath.y); } calcMovementFactor(foundPath); } @@ -985,18 +1223,16 @@ void Actor_v0::setDirection(int direction) { break; case 2: - res = 6; // Face Away + res = 6; // Face Camera break; default: - res = 7; // Face Camera + res = 7; // Face Away break; } _animFrameRepeat = -1; animateActor(res); - if (_moving) - animateCostume(); } void Actor::faceToObject(int obj) { @@ -1017,8 +1253,14 @@ void Actor::turnToDirection(int newdir) { return; if (_vm->_game.version <= 6) { - _moving = MF_TURN; _targetFacing = newdir; + + if (_vm->_game.version == 0 ) { + setDirection( newdir ); + return; + } + _moving = MF_TURN; + } else { _moving &= ~MF_TURN; if (newdir != _facing) { @@ -1085,8 +1327,14 @@ void Actor::putActor(int dstX, int dstY, int newRoom) { } // V0 always sets the actor to face the camera upon entering a room - if (_vm->_game.version == 0) + if (_vm->_game.version == 0) { + _walkdata.dest = _pos; + + ((Actor_v0*)this)->_newWalkBoxEntered = true; + ((Actor_v0*)this)->_CurrentWalkTo = _pos; + setDirection(oldDirToNewDir(2)); + } } static bool inBoxQuickReject(const BoxCoords &box, int x, int y, int threshold) { @@ -1200,6 +1448,59 @@ static int checkXYInBoxBounds(int boxnum, int x, int y, int &destX, int &destY) return dist; } +AdjustBoxResult Actor_v0::adjustPosInBorderWalkbox(AdjustBoxResult box) { + AdjustBoxResult Result = box; + BoxCoords BoxCoord = _vm->getBoxCoordinates(box.box); + + byte boxMask = _vm->getMaskFromBox(box.box); + if (!(boxMask & 0x80)) + return Result; + + char A; + boxMask &= 0x7C; + if (boxMask == 0x0C) + A = 2; + else { + if (boxMask != 0x08) + return Result; + + A = 1; + } + + // 1BC6 + byte Modifier = box.y - BoxCoord.ul.y; + assert(Modifier < 0x16); + + if (A == 1) { + // 1BCF + A = BoxCoord.ur.x - v0WalkboxSlantedModifier[ Modifier ]; + if (A < box.x) + return box; + + if (A < 0xA0 || A == 0xA0) + A = 0; + + Result.x = A; + } else { + // 1BED + A = BoxCoord.ul.x + v0WalkboxSlantedModifier[ Modifier ]; + + if (A < box.x || A == box.x) + Result.x = A; + } + + return Result; +} + +AdjustBoxResult Actor_v0::adjustXYToBeInBox(int dstX, int dstY) { + AdjustBoxResult Result = Actor_v2::adjustXYToBeInBox(dstX, dstY); + + if( Result.box == kInvalidBox ) + return Result; + + return adjustPosInBorderWalkbox(Result); +} + AdjustBoxResult Actor_v2::adjustXYToBeInBox(const int dstX, const int dstY) { AdjustBoxResult abr; @@ -1410,6 +1711,7 @@ void Actor::showActor() { Actor_v0 *a = ((Actor_v0 *)this); a->_costCommand = a->_costCommandNew = 0xFF; + _walkdata.dest = a->_CurrentWalkTo; for (int i = 0; i < 8; ++i) { a->_limbFrameRepeat[i] = 0; @@ -1642,7 +1944,7 @@ void ScummEngine::processActors() { continue; // Sound - if (a0->_moving && _currentRoom != 1 && _currentRoom != 44) { + if (a0->_moving != 2 && _currentRoom != 1 && _currentRoom != 44) { if (a0->_cost.soundPos == 0) a0->_cost.soundCounter++; @@ -1659,8 +1961,17 @@ void ScummEngine::processActors() { // would hence cause regressions. See also the other big // comment further up in this method for some details. if (a->_costume) { - a->drawActorCostume(); - a->animateCostume(); + + // Unfortunately in V0, the 'animateCostume' call happens right after the call to 'walkActor' (which is before drawing the actor)... + // doing it the other way with V0, causes animation glitches (when beginnning to walk, as the costume hasnt been updated). + // Updating the costume directly after 'walkActor' and again, after drawing... causes frame skipping + if (_game.version == 0) { + a->animateCostume(); + a->drawActorCostume(); + } else { + a->drawActorCostume(); + a->animateCostume(); + } } } } @@ -2897,6 +3208,70 @@ void Actor_v0::animateActor(int anim) { } } +byte Actor_v0::updateWalkbox() { + if( _vm->checkXYInBoxBounds( _walkbox, _pos.x, _pos.y ) ) + return 0; + + int numBoxes = _vm->getNumBoxes() - 1; + for (int i = 0; i <= numBoxes; i++) { + if (_vm->checkXYInBoxBounds( i, _pos.x, _pos.y ) == true ) { + if (_walkdata.curbox == i) { + setBox(i); + directionUpdate(); + + _newWalkBoxEntered = true; + return i; + } + } + } + + return kInvalidBox; +} + +void Actor_v0::directionUpdate() { + + int nextFacing = updateActorDirection(true); + if (_facing != nextFacing) { + // 2A89 + setDirection(nextFacing); + + // Still need to turn? + if (_facing != _targetFacing) { + _moving |= 0x80; + return; + } + } + + _moving &= ~0x80; +} + +void Actor_v0::setTmpFromActor() { + _tmp_Pos = _pos; + _pos = _tmp_Dest; + _tmp_WalkBox = _walkbox; + _tmp_NewWalkBoxEntered = _newWalkBoxEntered; +} + +void Actor_v0::setActorFromTmp() { + _pos = _tmp_Pos; + _tmp_Dest = _tmp_Pos; + _walkbox = _tmp_WalkBox; + _newWalkBoxEntered = _tmp_NewWalkBoxEntered; +} + +void Actor_v0::actorSetWalkTo() { + + if (_newWalkBoxEntered == false) + return; + + _newWalkBoxEntered = false; + + int nextBox = ((ScummEngine_v0*)_vm)->walkboxFindTarget(this, _walkdata.destbox, _walkdata.dest); + if (nextBox != kInvalidBox) { + _walkdata.curbox = nextBox; + } +} + void Actor_v0::saveLoadWithSerializer(Serializer *ser) { Actor::saveLoadWithSerializer(ser); @@ -2910,6 +3285,20 @@ void Actor_v0::saveLoadWithSerializer(Serializer *ser) { MKLINE(Actor_v0, _animFrameRepeat, sleByte, VER(89)), MKARRAY(Actor_v0, _limbFrameRepeatNew[0], sleInt8, 8, VER(89)), MKARRAY(Actor_v0, _limbFrameRepeat[0], sleInt8, 8, VER(90)), + MKLINE(Actor_v0, _CurrentWalkTo.x, sleInt16, VER(97)), + MKLINE(Actor_v0, _CurrentWalkTo.y, sleInt16, VER(97)), + MKLINE(Actor_v0, _NewWalkTo.x, sleInt16, VER(97)), + MKLINE(Actor_v0, _NewWalkTo.y, sleInt16, VER(97)), + MKLINE(Actor_v0, _walkCountModulo, sleInt8, VER(97)), + MKLINE(Actor_v0, _newWalkBoxEntered, sleByte, VER(97)), + MKLINE(Actor_v0, _walkDirX, sleByte, VER(97)), + MKLINE(Actor_v0, _walkDirY, sleByte, VER(97)), + MKLINE(Actor_v0, _walkYCountGreaterThanXCount, sleByte, VER(97)), + MKLINE(Actor_v0, _walkXCount, sleByte, VER(97)), + MKLINE(Actor_v0, _walkXCountInc, sleByte, VER(97)), + MKLINE(Actor_v0, _walkYCount, sleByte, VER(97)), + MKLINE(Actor_v0, _walkYCountInc, sleByte, VER(97)), + MKLINE(Actor_v0, _walkMaxXYCountInc, sleByte, VER(97)), MKEND() }; |