From 8d88e7b4d5b6ed22288d63709d2766e4eaa713b9 Mon Sep 17 00:00:00 2001 From: athrxx Date: Tue, 16 Jul 2024 19:52:44 +0200 Subject: [PATCH] SCUMM: (V1/2) - fix bug no. 15273 (Walking animation when turning not correct) Also match v1/2 walk code more to the orginal behavior in general, to eliminate accuracy glitches. Also, get rid of memory leaks in resetScumm(). --- engines/scumm/actor.cpp | 153 ++++++++++++++++++++++++++-------------- engines/scumm/actor.h | 14 ++-- engines/scumm/scumm.cpp | 7 ++ 3 files changed, 116 insertions(+), 58 deletions(-) diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index afffc8d9bb9..00345d36bd3 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -581,8 +581,11 @@ int Actor::calcMovementFactor(const Common::Point& next) { int Actor_v3::calcMovementFactor(const Common::Point& next) { int32 deltaXFactor, deltaYFactor; - if (_pos == next) + if (_pos == next) { + if (_vm->_game.version == 2) + _moving |= MF_IN_LEG; return 0; + } int diffX = next.x - _pos.x; int diffY = next.y - _pos.y; @@ -611,14 +614,23 @@ int Actor_v3::calcMovementFactor(const Common::Point& next) { _walkdata.next = next; _walkdata.deltaXFactor = deltaXFactor; _walkdata.deltaYFactor = deltaYFactor; + _walkdata.facing = diffX >= 0 ? (diffY >= 0 ? 1 : 0) : (diffY >= 0 ? 2 : 3); // The x/y distance ratio which determines whether to face up/down instead of left/right is different for SCUMM1/2 and SCUMM3. _targetFacing = oldDirToNewDir(((ABS(diffY) * _facingXYratio) > ABS(diffX)) ? 3 - (diffY >= 0 ? 1 : 0) : (diffX >= 0 ? 1 : 0)); - if (_vm->_game.version <= 2 && _facing != updateActorDirection(true)) + if (_vm->_game.version > 2) + return actorWalkStep(); + + _moving &= ~MF_IN_LEG; + if (_facing != _targetFacing) _moving |= MF_TURN; - return actorWalkStep(); + + if (_walkFrame != _frame || _facing != _targetFacing) + startWalkAnim(1, _facing); + + return (_moving & MF_TURN) ? 0 : actorWalkStep(); } int Actor::actorWalkStep() { @@ -672,39 +684,9 @@ int Actor::actorWalkStep() { return 1; } -int Actor_v3::actorWalkStep() { +int Actor_v2::actorWalkStep() { _needRedraw = true; - int nextFacing = updateActorDirection(true); - if (!(_moving & MF_IN_LEG) || _facing != nextFacing) { - if (_walkFrame != _frame || _facing != nextFacing) - startWalkAnim(1, nextFacing); - - _moving |= MF_IN_LEG; - // The next two lines fix bug #12278 for ZAK FM-TOWNS (SCUMM3). They are alse required for SCUMM 1/2 to prevent movement while - // turning, but only if the character has to make a turn. The correct behavior for v1/2 can be tested by letting Zak (only v1/2 - // versions) walk in the starting room from the torn wallpaper to the desk drawer: Zak should first turn around clockwise by - // 180°, then walk one step to the left, then turn clockwise 90°. For ZAK FM-TOWNS (SCUMM3) this part will look quite different - // (and a bit weird), but I have confirmed the correctness with the FM-Towns emulator, too. - if (_vm->_game.version == 3 || (_vm->_game.version <= 2 && (_moving & MF_TURN))) - return 1; - } - - if (_vm->_game.version == 3) { - if (_walkdata.next.x - (int)_stepX <= _pos.x && _walkdata.next.x + (int)_stepX >= _pos.x) - _pos.x = _walkdata.next.x; - if (_walkdata.next.y - (int)_speedy <= _pos.y && _walkdata.next.y + (int)_speedy >= _pos.y) - _pos.y = _walkdata.next.y; - - if (_walkbox != _walkdata.curbox && _vm->checkXYInBoxBounds(_walkdata.curbox, _pos.x, _pos.y)) - setBox(_walkdata.curbox); - - if (_pos == _walkdata.next) { - _moving &= ~MF_IN_LEG; - return 0; - } - } - if ((_walkdata.xfrac += _walkdata.xAdd) >= _stepThreshold) { if (_pos.x != _walkdata.next.x) _pos.x += _walkdata.deltaXFactor; @@ -716,11 +698,49 @@ int Actor_v3::actorWalkStep() { _walkdata.yfrac -= _stepThreshold; } - if (_vm->_game.version <= 2 && _pos == _walkdata.next) { + if (_pos == _walkdata.next) + _moving |= MF_IN_LEG; + + return 0; +} + +int Actor_v3::actorWalkStep() { + _needRedraw = true; + + int nextFacing = updateActorDirection(true); + if (!(_moving & MF_IN_LEG) || _facing != nextFacing) { + if (_walkFrame != _frame || _facing != nextFacing) + startWalkAnim(1, nextFacing); + + _moving |= MF_IN_LEG; + // The next line fixes bug #12278 for ZAK FM-TOWNS (SCUMM3). + return 1; + } + + if (_walkdata.next.x - (int)_stepX <= _pos.x && _walkdata.next.x + (int)_stepX >= _pos.x) + _pos.x = _walkdata.next.x; + if (_walkdata.next.y - (int)_speedy <= _pos.y && _walkdata.next.y + (int)_speedy >= _pos.y) + _pos.y = _walkdata.next.y; + + if (_walkbox != _walkdata.curbox && _vm->checkXYInBoxBounds(_walkdata.curbox, _pos.x, _pos.y)) + setBox(_walkdata.curbox); + + if (_pos == _walkdata.next) { _moving &= ~MF_IN_LEG; return 0; } + if ((_walkdata.xfrac += _walkdata.xAdd) >= _stepThreshold) { + if (_pos.x != _walkdata.next.x) + _pos.x += _walkdata.deltaXFactor; + _walkdata.xfrac -= _stepThreshold; + } + if ((_walkdata.yfrac += _walkdata.yAdd) >= _stepThreshold) { + if (_pos.y != _walkdata.next.y) + _pos.y += _walkdata.deltaYFactor; + _walkdata.yfrac -= _stepThreshold; + } + return 1; } @@ -892,7 +912,7 @@ void Actor::startWalkActor(int destX, int destY, int dir) { ((Actor_v0 *)this)->walkBoxQueuePrepare(); } else if (_vm->_game.version <= 2) { - _moving = (_moving & ~(MF_LAST_LEG | MF_IN_LEG)) | MF_NEW_LEG; + _moving = (_moving & ~MF_LAST_LEG) | MF_IN_LEG | MF_NEW_LEG; } else { _moving = (_moving & MF_IN_LEG) | MF_NEW_LEG; } @@ -1230,50 +1250,46 @@ UpdateActorDirection:; } void Actor_v2::walkActor() { - Common::Point foundPath, tmp; - int new_dir, next_box; - if (_moving & MF_TURN) { - new_dir = updateActorDirection(false); - if (_facing != new_dir) { - setDirection(new_dir); - } else { + int newDir = updateActorDirection(false); + if (_targetFacing == newDir) _moving &= ~MF_TURN; - } + setDirection(newDir); return; } - if (!_moving) + if (!(_moving & MF_NEW_LEG)) return; - if (_moving & MF_IN_LEG) { + if (!(_moving & MF_IN_LEG)) { actorWalkStep(); } else { if (_moving & MF_LAST_LEG) { _moving = MF_TURN; startAnimActor(_standFrame); - if (_targetFacing != _walkdata.destdir) + if (_walkdata.destdir != -1) turnToDirection(_walkdata.destdir); } else { + Common::Point foundPath, tmp; setBox(_walkdata.curbox); if (_walkbox == _walkdata.destbox) { foundPath = _walkdata.dest; _moving |= MF_LAST_LEG; } else { - next_box = _vm->getNextBox(_walkbox, _walkdata.destbox); - if (next_box < 0) { + int nextBox = _vm->getNextBox(_walkbox, _walkdata.destbox); + if (nextBox < 0) { _moving |= MF_LAST_LEG; return; } // Can't walk through locked boxes - int flags = _vm->getBoxFlags(next_box); + int flags = _vm->getBoxFlags(nextBox); if ((flags & kBoxLocked) && !((flags & kBoxPlayerOnly) && !isPlayer())) { _moving |= MF_LAST_LEG; - //_walkdata.destdir = -1; + _walkdata.destdir = -1; } - _walkdata.curbox = next_box; + _walkdata.curbox = nextBox; 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); @@ -1495,6 +1511,39 @@ int Actor::remapDirection(int dir, bool is_walking) { return dir; } +int Actor_v2::remapDirection(int dir, bool is_walking) { + if (_vm->_game.version == 0) + return Actor::remapDirection(dir, is_walking); + + static const byte remapTable1[] = { + 0x04, 0x01, 0x02, 0x00, 0x01, 0x02, 0x03, 0x00, + 0x06, 0x01, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, + 0x07, 0x00, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, + 0x05, 0x00, 0x02, 0x00, 0x01, 0x02, 0x03, 0x00 + }; + + static const byte remapTable2[] = { + 0x00, 0x01, 0x03, 0x02, 0x03, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x03, 0x02, 0x01, 0x03, 0x01, 0x02, + 0x00, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x03, 0x02, 0x03, 0x03, 0x01, 0x01 + }; + + static const byte remapTable3[] = { + 0x00, 0x00, 0x02, 0x00, 0x01, 0x03, 0x02, 0x00, + 0x01, 0x01, 0x02, 0x00, 0x01, 0x03, 0x02, 0x00, + 0x02, 0x01, 0x02, 0x00, 0x01, 0x03, 0x02, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x01, 0x03, 0x02, 0x00 + }; + + if (_moving & ~MF_TURN) + _targetFacing = oldDirToNewDir(remapTable2[newDirToOldDir(dir) * 8 + remapTable1[_walkdata.facing * 8 + (_vm->getBoxFlags(_walkbox) & 7)]]); + else + _targetFacing = oldDirToNewDir(remapTable3[newDirToOldDir(dir) * 8 + (_vm->getBoxFlags(_walkbox) & 7)]); + + return _targetFacing | 0x400; +} + int Actor::updateActorDirection(bool is_walking) { static const uint8 actorTurnInterpolateTable[] = { 0, 2, 2, 3, 2, 1, 2, 3, 0, 1, 2, 1, 0, 1, 0, 3 }; diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h index d91f710271d..8b44ad54c2f 100644 --- a/engines/scumm/actor.h +++ b/engines/scumm/actor.h @@ -223,8 +223,8 @@ public: void setActorWalkSpeed(uint newSpeedX, uint newSpeedY); protected: virtual int calcMovementFactor(const Common::Point& next); - int actorWalkStep(); - int remapDirection(int dir, bool is_walking); + virtual int actorWalkStep(); + virtual int remapDirection(int dir, bool is_walking); virtual void setupActorScale(); void setBox(int box); @@ -362,13 +362,12 @@ public: protected: int calcMovementFactor(const Common::Point& next) override; - int actorWalkStep(); - void setupActorScale() override; void findPathTowardsOld(byte box, byte box2, byte box3, Common::Point &p2, Common::Point &p3); - + uint _stepThreshold; private: - uint _stepX, _stepThreshold; + virtual int actorWalkStep(); + uint _stepX; const int _facingXYratio; }; @@ -383,6 +382,9 @@ public: protected: bool isPlayer() override; void prepareDrawActorCostume(BaseCostumeRenderer *bcr) override; +private: + int actorWalkStep() override; + int remapDirection(int dir, bool is_walking) override; }; class Actor_v7 final : public Actor { diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index ed863de4eed..c182b9d5da3 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -1760,6 +1760,13 @@ void ScummEngine::resetScumm() { setShake(0); _cursor.animate = 1; + if (_actors) { + for (i = 0; i < _numActors; ++i) + delete _actors[i]; + delete[] _actors; + } + delete[] _sortedActors; + // Allocate and Initialize actors Actor::kInvalidBox = ((_game.features & GF_SMALL_HEADER) ? kOldInvalidBox : kNewInvalidBox); _actors = new Actor * [_numActors];