SCUMM: Maniac V0: If boxes are neighbors, walk directly to the new box. Remove extra call to 'animateCostume'. Remove now unnecessary V0 walk calc functions

This commit is contained in:
Robert Crossfield 2014-11-29 07:12:57 +11:00
parent 2dbd99d572
commit 29d46e8a10
6 changed files with 92 additions and 175 deletions

View file

@ -33,6 +33,7 @@
#include "scumm/resource.h" #include "scumm/resource.h"
#include "scumm/saveload.h" #include "scumm/saveload.h"
#include "scumm/scumm_v7.h" #include "scumm/scumm_v7.h"
#include "scumm/scumm_v0.h"
#include "scumm/he/sound_he.h" #include "scumm/he/sound_he.h"
#include "scumm/he/sprite_he.h" #include "scumm/he/sprite_he.h"
#include "scumm/usage_bits.h" #include "scumm/usage_bits.h"
@ -453,7 +454,7 @@ void Actor::startWalkActor(int destX, int destY, int dir) {
if(_vm->_game.version != 0 ) { if(_vm->_game.version != 0 ) {
_moving = (_moving & MF_IN_LEG) | MF_NEW_LEG; _moving = (_moving & MF_IN_LEG) | MF_NEW_LEG;
} else { } else {
((Actor_v0*)this)->unk_FDE1 = 1; ((Actor_v0*)this)->_newWalkBoxEntered = 1;
} }
_walkdata.point3.x = 32000; _walkdata.point3.x = 32000;
@ -574,91 +575,7 @@ void Actor::walkActor() {
calcMovementFactor(_walkdata.dest); calcMovementFactor(_walkdata.dest);
} }
bool Actor_v2::checkWalkboxesHaveDirectPath(Common::Point &foundPath) { bool Actor_v0::calcWalkDistances() {
// only MM v0 supports walking in direct line between walkboxes.
// MM v1 already does not support it anymore.
return false;
}
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
double det = v2.x * v1.y - v1.x * v2.y;
if (det == 0)
return false;
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;
// 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;
result.x = line1Start.x + (int)(n1 * v1.x);
result.y = line1Start.y + (int)(n1 * v1.y);
return true;
}
/*
* 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);
}
}
return false;
}
bool Actor_v0::sub_2F6F() {
_walkDirX = 0; _walkDirX = 0;
_walkDirY = 0; _walkDirY = 0;
_walkYCountGreaterThanXCount = 0; _walkYCountGreaterThanXCount = 0;
@ -710,7 +627,7 @@ byte Actor_v0::updateWalkbox() {
if (_walkdata.curbox == i ) { if (_walkdata.curbox == i ) {
setBox(i); setBox(i);
unk_FDE1 = 1; _newWalkBoxEntered = 1;
return i; return i;
} }
} }
@ -723,14 +640,14 @@ void Actor_v0::setTmpFromActor() {
_tmp_Pos = _pos; _tmp_Pos = _pos;
_pos = _tmp_Dest; _pos = _tmp_Dest;
_tmp_WalkBox = _walkbox; _tmp_WalkBox = _walkbox;
_tmp_CB5F = unk_FDE1; _tmp_NewWalkBoxEntered = _newWalkBoxEntered;
} }
void Actor_v0::setActorFromTmp() { void Actor_v0::setActorFromTmp() {
_pos = _tmp_Pos; _pos = _tmp_Pos;
_tmp_Dest = _tmp_Pos; _tmp_Dest = _tmp_Pos;
_walkbox = _tmp_WalkBox; _walkbox = _tmp_WalkBox;
unk_FDE1 = _tmp_CB5F; _newWalkBoxEntered = _tmp_NewWalkBoxEntered;
} }
byte Actor_v0::actorWalkX() { byte Actor_v0::actorWalkX() {
@ -748,21 +665,20 @@ byte Actor_v0::actorWalkX() {
// 2EAC // 2EAC
_walkXCount = A; _walkXCount = A;
setTmpFromActor(); setTmpFromActor();
if( updateWalkbox() == 0xFF ) { if (updateWalkbox() == 0xFF) {
// 2EB9 // 2EB9
setActorFromTmp(); setActorFromTmp();
return 3; return 3;
} }
// 2EBF // 2EBF
if( _tmp_Dest.x == _CurrentWalkTo.x ) if (_tmp_Dest.x == _CurrentWalkTo.x)
return 1; return 1;
return 0; return 0;
} }
byte Actor_v0::actorWalkY() { byte Actor_v0::actorWalkY() {
byte A = _walkYCount; byte A = _walkYCount;
A += _walkYCountInc; A += _walkYCountInc;
if (A >= _walkCountModulo) { if (A >= _walkCountModulo) {
@ -782,15 +698,13 @@ byte Actor_v0::actorWalkY() {
setActorFromTmp(); setActorFromTmp();
return 4; return 4;
} }
// 2EFE // 2EFE
if (_walkYCountInc != 0) { if (_walkYCountInc != 0) {
if (_walkYCountInc == 0xFF ) { if (_walkYCountInc == 0xFF) {
setActorFromTmp(); setActorFromTmp();
return 4; return 4;
} }
} }
// 2F0D // 2F0D
if (_CurrentWalkTo.y == _tmp_Dest.y) if (_CurrentWalkTo.y == _tmp_Dest.y)
return 1; return 1;
@ -798,31 +712,33 @@ byte Actor_v0::actorWalkY() {
return 0; return 0;
} }
byte Actor_v0::walkboxFindTarget() { void Actor_v0::directionUpdate() {
return 0xff;
} 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::actorSetWalkTo() { void Actor_v0::actorSetWalkTo() {
if (unk_FDE1 == 0 ) if (_newWalkBoxEntered == 0)
return; return;
unk_FDE1 = 0; _newWalkBoxEntered = 0;
byte nextBox = _vm->getNextBox(_walkbox, _walkdata.destbox);
if (nextBox != 0xFF && nextBox != _walkbox ) { int nextBox = ((ScummEngine_v0*)_vm)->walkboxFindTarget( this, _walkdata.destbox, _walkdata.dest );
Common::Point tmp; if (nextBox != 0xFF) {
_walkdata.curbox = nextBox; _walkdata.curbox = nextBox;
getClosestPtOnBox(_vm->getBoxCoordinates(nextBox), _pos.x, _pos.y, _NewWalkTo.x, _NewWalkTo.y);
//getClosestPtOnBox(_vm->getBoxCoordinates(_walkbox), tmp.x, tmp.y, _NewWalkTo.x, _NewWalkTo.y);
} else {
if( _walkdata.dest.x == -1 )
_NewWalkTo = _CurrentWalkTo;
else
_NewWalkTo = _walkdata.dest;
} }
} }
@ -839,7 +755,7 @@ loc_2A33:;
_moving &= 0xF0; _moving &= 0xF0;
_tmp_Dest = _pos; _tmp_Dest = _pos;
byte tmp = sub_2F6F(); byte tmp = calcWalkDistances();
_moving &= 0xF0; _moving &= 0xF0;
_moving |= tmp; _moving |= tmp;
@ -859,7 +775,7 @@ loc_2A33:;
directionUpdate(); directionUpdate();
if (_moving & 0x80 ) if (_moving & 0x80)
return; return;
animateActor(newDirToOldDir(_facing)); animateActor(newDirToOldDir(_facing));
@ -876,17 +792,17 @@ loc_2A33:;
// 2A9A // 2A9A
if (_moving == 2 ) if (_moving == 2)
return; return;
if ((_moving & 0x0F) == 1 ) if ((_moving & 0x0F) == 1)
return stopActorMoving(); return stopActorMoving();
// 2AAD // 2AAD
if (_moving & 0x80) { if (_moving & 0x80) {
directionUpdate(); directionUpdate();
if ((_moving & 0x80) ) if (_moving & 0x80)
return; return;
// 2AC2 // 2AC2
@ -899,7 +815,7 @@ loc_2C36:;
// 2C36 // 2C36
setTmpFromActor(); setTmpFromActor();
if (!_walkDirX ) { if (!_walkDirX) {
_pos.x--; _pos.x--;
} else { } else {
_pos.x++; _pos.x++;
@ -944,7 +860,7 @@ loc_2C36:;
} }
// 2ADA // 2ADA
if ((_moving & 0x0F) == 4 ) { if ((_moving & 0x0F) == 4) {
// 2CA3 // 2CA3
loc_2CA3:; loc_2CA3:;
setTmpFromActor(); setTmpFromActor();
@ -954,22 +870,22 @@ loc_2CA3:;
} else { } else {
_pos.y++; _pos.y++;
} }
if (updateWalkbox() == 0xFF ) { if (updateWalkbox() == 0xFF) {
// 2CC7 // 2CC7
setActorFromTmp(); setActorFromTmp();
if( _CurrentWalkTo.x == _tmp_Dest.x ) { if( _CurrentWalkTo.x == _tmp_Dest.x) {
stopActorMoving(); stopActorMoving();
return; return;
} }
// 2CD5 // 2CD5
if (!_walkDirX ) { if (!_walkDirX) {
_tmp_Dest.x--; _tmp_Dest.x--;
} else { } else {
_tmp_Dest.x++; _tmp_Dest.x++;
} }
setTmpFromActor(); setTmpFromActor();
if (updateWalkbox() == 0xFF ) { if (updateWalkbox() == 0xFF) {
setActorFromTmp(); setActorFromTmp();
stopActorMoving(); stopActorMoving();
} }
@ -981,19 +897,19 @@ loc_2CA3:;
} }
} }
if ((_moving & 0x0F) == 0 ) { if ((_moving & 0x0F) == 0) {
// 2AE8 // 2AE8
byte A = actorWalkX(); byte A = actorWalkX();
if( A == 1 ) { if (A == 1) {
A = actorWalkY(); A = actorWalkY();
if( A == 1 ) { if (A == 1) {
// 2AF6 // 2AF6
_moving &= 0xF0; _moving &= 0xF0;
_moving |= A; _moving |= A;
} else { } else {
// 2B04 // 2B04
if( A == 4 ) if (A == 4)
stopActorMoving(); stopActorMoving();
} }
@ -1018,7 +934,7 @@ loc_2CA3:;
} else { } else {
// 2B39 // 2B39
A = actorWalkY(); A = actorWalkY();
if (A != 4 ) if (A != 4)
return; return;
// 2B46 // 2B46
@ -1039,22 +955,6 @@ loc_2CA3:;
} }
} }
void Actor_v0::directionUpdate() {
int nextFacing = updateActorDirection(true);
if (_facing != nextFacing) {
// 2A89
setDirection(nextFacing);
if (_facing != _targetFacing ) {
_moving |= 0x80;
} else {
_moving &= ~0x80;
}
} else
_moving &= ~0x80;
}
void Actor_v2::walkActor() { void Actor_v2::walkActor() {
Common::Point foundPath, tmp; Common::Point foundPath, tmp;
int new_dir, next_box; int new_dir, next_box;
@ -1101,10 +1001,8 @@ void Actor_v2::walkActor() {
_walkdata.curbox = next_box; _walkdata.curbox = next_box;
if (!checkWalkboxesHaveDirectPath(foundPath)) { getClosestPtOnBox(_vm->getBoxCoordinates(_walkdata.curbox), _pos.x, _pos.y, tmp.x, tmp.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);
getClosestPtOnBox(_vm->getBoxCoordinates(_walkbox), tmp.x, tmp.y, foundPath.x, foundPath.y);
}
} }
calcMovementFactor(foundPath); calcMovementFactor(foundPath);
} }
@ -1496,7 +1394,7 @@ void Actor::putActor(int dstX, int dstY, int newRoom) {
if (_vm->_game.version == 0) { if (_vm->_game.version == 0) {
_walkdata.dest = _pos; _walkdata.dest = _pos;
((Actor_v0*)this)->unk_FDE1 = 1; ((Actor_v0*)this)->_newWalkBoxEntered = 1;
((Actor_v0*)this)->_CurrentWalkTo = _pos; ((Actor_v0*)this)->_CurrentWalkTo = _pos;
setDirection(oldDirToNewDir(2)); setDirection(oldDirToNewDir(2));
@ -2075,14 +1973,15 @@ void ScummEngine::processActors() {
// comment further up in this method for some details. // comment further up in this method for some details.
if (a->_costume) { if (a->_costume) {
if (_game.version == 0) // Unfortunately in V0, the 'animateCostume' call happens right after the call to 'walkActor', before drawing the actor... doing it the
// other way with V0, causes graphic glitches
if (_game.version == 0) {
a->animateCostume(); a->animateCostume();
a->drawActorCostume();
a->drawActorCostume(); } else {
a->drawActorCostume();
if (_game.version != 0)
a->animateCostume(); a->animateCostume();
}
} }
} }
} }

View file

@ -333,7 +333,6 @@ public:
protected: protected:
virtual bool isPlayer(); virtual bool isPlayer();
virtual void prepareDrawActorCostume(BaseCostumeRenderer *bcr); virtual void prepareDrawActorCostume(BaseCostumeRenderer *bcr);
virtual bool checkWalkboxesHaveDirectPath(Common::Point &foundPath);
}; };
enum ActorV0MiscFlags { enum ActorV0MiscFlags {
@ -349,12 +348,15 @@ enum ActorV0MiscFlags {
class Actor_v0 : public Actor_v2 { class Actor_v0 : public Actor_v2 {
public: public:
Common::Point _CurrentWalkTo, _NewWalkTo;
byte _costCommandNew; byte _costCommandNew;
byte _costCommand; byte _costCommand;
byte _miscflags; byte _miscflags;
byte _speaking; byte _speaking;
Common::Point _CurrentWalkTo, _NewWalkTo; byte _walkCountModulo;
byte _newWalkBoxEntered;
byte _walkDirX; byte _walkDirX;
byte _walkDirY; byte _walkDirY;
@ -364,16 +366,13 @@ public:
byte _walkXCountInc; byte _walkXCountInc;
byte _walkYCount; byte _walkYCount;
byte _walkYCountInc; byte _walkYCountInc;
byte _walkCountModulo;
byte _walkMaxXYCountInc; byte _walkMaxXYCountInc;
byte unk_FDE1;
Common::Point _tmp_Pos; Common::Point _tmp_Pos;
Common::Point _tmp_Dest; Common::Point _tmp_Dest;
byte _tmp_WalkBox; byte _tmp_WalkBox;
byte _tmp_CB5F; byte _tmp_NewWalkBoxEntered;
int8 _animFrameRepeat; int8 _animFrameRepeat;
int8 _limbFrameRepeatNew[8]; int8 _limbFrameRepeatNew[8];
@ -395,23 +394,18 @@ public:
void setDirection(int direction); void setDirection(int direction);
void startAnimActor(int f); void startAnimActor(int f);
bool sub_2F6F(); bool calcWalkDistances();
void walkActor(); void walkActor();
void actorSetWalkTo(); void actorSetWalkTo();
byte actorWalkX(); byte actorWalkX();
byte actorWalkY(); byte actorWalkY();
byte updateWalkbox(); byte updateWalkbox();
byte walkboxFindTarget();
void setTmpFromActor(); void setTmpFromActor();
void setActorFromTmp(); void setActorFromTmp();
// Used by the save/load system: // Used by the save/load system:
virtual void saveLoadWithSerializer(Serializer *ser); virtual void saveLoadWithSerializer(Serializer *ser);
protected:
bool intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End,
const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result);
virtual bool checkWalkboxesHaveDirectPath(Common::Point &foundPath);
}; };

View file

@ -1158,6 +1158,30 @@ bool ScummEngine::areBoxesNeighbors(int box1nr, int box2nr) {
return false; return false;
} }
byte ScummEngine_v0::walkboxFindTarget(Actor *a, int destbox, Common::Point walkdest) {
Actor_v0 *Actor = (Actor_v0*)a;
byte nextBox = getNextBox(a->_walkbox, destbox);
if (nextBox != 0xFF && nextBox == destbox && areBoxesNeighbors(a->_walkbox, nextBox)) {
Actor->_NewWalkTo = walkdest;
return nextBox;
}
if (nextBox != 0xFF && nextBox != a->_walkbox) {
getClosestPtOnBox(getBoxCoordinates(nextBox), a->getPos().x, a->getPos().y, Actor->_NewWalkTo.x, Actor->_NewWalkTo.y);
} else {
if (walkdest.x == -1)
Actor->_NewWalkTo = Actor->_CurrentWalkTo;
else
Actor->_NewWalkTo = walkdest;
}
return nextBox;
}
bool ScummEngine_v0::areBoxesNeighbors(int box1nr, int box2nr) { bool ScummEngine_v0::areBoxesNeighbors(int box1nr, int box2nr) {
int i; int i;
const int numOfBoxes = getNumBoxes(); const int numOfBoxes = getNumBoxes();

View file

@ -589,9 +589,9 @@ void ScummEngine_v0::o_loadRoomWithEgo() {
return; return;
} }
// The original interpreter seems to set the actors new room X/Y to the last rooms X/Y // The original interpreter sets the actors new room X/Y to the last rooms X/Y
// This fixes a problem with MM: script 158 in room 12, the 'Oompf!' script // This fixes a problem with MM: script 158 in room 12, the 'Oomph!' script
// This scripts runs before the actor position is set to the correct location // This scripts runs before the actor position is set to the correct room entry location
a->putActor(a->getPos().x, a->getPos().y, room); a->putActor(a->getPos().x, a->getPos().y, room);
_egoPositioned = false; _egoPositioned = false;
@ -707,7 +707,6 @@ void ScummEngine_v0::o_animateActor() {
} }
a->animateActor(anim); a->animateActor(anim);
a->animateCostume();
} }
void ScummEngine_v0::o_getActorMoving() { void ScummEngine_v0::o_getActorMoving() {

View file

@ -67,6 +67,8 @@ public:
virtual void resetScumm(); virtual void resetScumm();
byte walkboxFindTarget(Actor *a, int destbox, Common::Point walkdest);
protected: protected:
virtual void resetRoomObject(ObjectData *od, const byte *room, const byte *searchptr = NULL); virtual void resetRoomObject(ObjectData *od, const byte *room, const byte *searchptr = NULL);
@ -116,7 +118,7 @@ protected:
void resetSentence(); void resetSentence();
virtual bool areBoxesNeighbors(int box1nr, int box2nr); bool areBoxesNeighbors(int box1nr, int box2nr);
bool ifEqualActiveObject2Common(bool checkType); bool ifEqualActiveObject2Common(bool checkType);

View file

@ -708,7 +708,6 @@ void ScummEngine_v0::verbExec() {
Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "verbExec"); Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "verbExec");
int x = _virtualMouse.x / V12_X_MULTIPLIER; int x = _virtualMouse.x / V12_X_MULTIPLIER;
int y = _virtualMouse.y / V12_Y_MULTIPLIER; int y = _virtualMouse.y / V12_Y_MULTIPLIER;
//actorSetPosInBox();
// 0xB31 // 0xB31
VAR(6) = x; VAR(6) = x;