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().
This commit is contained in:
athrxx 2024-07-16 19:52:44 +02:00
parent 261e37a340
commit 8d88e7b4d5
3 changed files with 116 additions and 58 deletions

View File

@ -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 };

View File

@ -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 {

View File

@ -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];