aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm
diff options
context:
space:
mode:
Diffstat (limited to 'engines/scumm')
-rw-r--r--engines/scumm/actor.cpp187
-rw-r--r--engines/scumm/actor.h14
-rw-r--r--engines/scumm/boxes.cpp59
-rw-r--r--engines/scumm/gfx.cpp5
-rw-r--r--engines/scumm/gfx.h2
-rw-r--r--engines/scumm/object.cpp2
-rw-r--r--engines/scumm/saveload.h2
-rw-r--r--engines/scumm/scumm.cpp50
-rw-r--r--engines/scumm/scumm.h11
-rw-r--r--engines/scumm/scumm_v0.h4
10 files changed, 291 insertions, 45 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index cd54c175cc..b3e7926015 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -259,6 +259,8 @@ void Actor_v0::initActor(int mode) {
_limb_flipped[i] = false;
}
+ walkBoxQueueReset();
+
if (_vm->_game.features & GF_DEMO) {
_sound[0] = v0ActorDemoTalk[_number];
} else {
@@ -266,6 +268,116 @@ void Actor_v0::initActor(int mode) {
}
}
+void Actor_v0::walkBoxQueueReset() {
+ _walkboxHistory.clear();
+ _walkboxQueueIndex = 0;
+
+ for (uint i = 0; i < ARRAYSIZE(_walkboxQueue); ++i) {
+ _walkboxQueue[i] = kInvalidBox;
+ }
+}
+
+bool Actor_v0::walkBoxQueueAdd(int box) {
+
+ if (_walkboxQueueIndex == ARRAYSIZE(_walkboxQueue))
+ return false;
+
+ _walkboxQueue[_walkboxQueueIndex++] = box;
+ _walkboxHistory.push_back(box);
+ return true;
+}
+
+void Actor_v0::walkboxQueueReverse() {
+ int j = ARRAYSIZE(_walkboxQueue) - 1;
+
+ while (_walkboxQueue[j] == kInvalidBox && j >= 1)
+ --j;
+
+ if (j <= 1)
+ return;
+
+ for (int i = 1; i < j && j >= 1 ; ++i, --j) {
+
+ byte tmp = _walkboxQueue[i];
+
+ _walkboxQueue[i] = _walkboxQueue[j];
+ _walkboxQueue[j] = tmp;
+ }
+}
+
+bool Actor_v0::walkBoxQueueFind(int box) {
+
+ for (uint i = 0; i < _walkboxHistory.size(); ++i) {
+ if (box == _walkboxHistory[i])
+ return true;
+ }
+
+ return false;
+}
+
+bool Actor_v0::walkBoxQueuePrepare() {
+ walkBoxQueueReset();
+ int BoxFound = _walkbox;
+
+ if (BoxFound == _walkdata.destbox) {
+
+ _newWalkBoxEntered = true;
+ return true;
+ }
+
+ // Build a series of walkboxes from our current position, to the target
+ do {
+ // Add the current box to the queue
+ if (!walkBoxQueueAdd(BoxFound))
+ return false;
+
+ // Loop until we find a walkbox which hasn't been tested
+ while (_walkboxQueueIndex > 0) {
+
+ // Check if the dest box is a direct neighbour
+ if ((BoxFound = _vm->getNextBox(BoxFound, _walkdata.destbox)) == kInvalidBox) {
+
+ // Its not, start hunting through this boxes immediate connections
+ byte* boxm = _vm->getBoxConnectionBase(_walkboxQueue[_walkboxQueueIndex - 1]);
+
+ // Attempt to find one, which we havn't already used
+ for (; *boxm != kInvalidBox; ++boxm) {
+ if (walkBoxQueueFind(*boxm) != true)
+ break;
+ }
+
+ BoxFound = *boxm;
+ }
+
+ // Found one?
+ if (BoxFound != kInvalidBox) {
+
+ // Did we find a connection to the final walkbox
+ if (BoxFound == _walkdata.destbox) {
+
+ _newWalkBoxEntered = true;
+
+ walkBoxQueueAdd(BoxFound);
+
+ walkboxQueueReverse();
+ return true;
+ }
+
+ // Nope, check the next box
+ break;
+ }
+
+ // Drop this box, its useless to us
+ _walkboxQueue[--_walkboxQueueIndex] = kInvalidBox;
+
+ BoxFound = _walkboxQueue[_walkboxQueueIndex - 1];
+ }
+
+ } while (_walkboxQueueIndex > 0);
+
+ return false;
+}
+
void Actor::setBox(int box) {
_walkbox = box;
setupActorScale();
@@ -634,17 +746,18 @@ void Actor::startWalkActor(int destX, int destY, int dir) {
_walkdata.dest.y = abr.y;
_walkdata.destbox = abr.box;
_walkdata.destdir = dir;
-
+ _walkdata.point3.x = 32000;
+ _walkdata.curbox = _walkbox;
+
if (_vm->_game.version == 0) {
- ((Actor_v0*)this)->_newWalkBoxEntered = true;
+ ((Actor_v0 *)this)->walkBoxQueuePrepare();
+
} else if (_vm->_game.version <= 2) {
_moving = (_moving & ~(MF_LAST_LEG | MF_IN_LEG)) | MF_NEW_LEG;
} else {
_moving = (_moving & MF_IN_LEG) | MF_NEW_LEG;
}
- _walkdata.point3.x = 32000;
- _walkdata.curbox = _walkbox;
}
void Actor::startWalkAnim(int cmd, int angle) {
@@ -861,13 +974,7 @@ L2C36:;
stopActorMoving();
return;
}
- // 2C98: Yes, an exact copy of what just occurred.. the original does this, so im doing it...
- // Just to keep me sane when going over it :)
- if (A == 0xFF) {
- setActorFromTmp();
- stopActorMoving();
- return;
- }
+
return;
}
@@ -1398,13 +1505,13 @@ void Actor::putActor(int dstX, int dstY, int newRoom) {
showActor();
}
- // V0 always sets the actor to face the camera upon entering a room
if (_vm->_game.version == 0) {
- _walkdata.dest = _pos;
- ((Actor_v0*)this)->_newWalkBoxEntered = true;
- ((Actor_v0*)this)->_CurrentWalkTo = _pos;
+ ((Actor_v0 *)this)->_newWalkBoxEntered = false;
+ ((Actor_v0 *)this)->_CurrentWalkTo = _pos;
+ ((Actor_v0 *)this)->_NewWalkTo = _pos;
+ // V0 always sets the actor to face the camera upon entering a room
setDirection(oldDirToNewDir(2));
}
}
@@ -1549,7 +1656,7 @@ AdjustBoxResult Actor_v0::adjustPosInBorderWalkbox(AdjustBoxResult box) {
if (A < box.x)
return box;
- if (A < 0xA0 || A == 0xA0)
+ if (A <= 0xA0)
A = 0;
Result.x = A;
@@ -1980,9 +2087,10 @@ void ScummEngine::processActors() {
if (_game.version == 0) {
// 0x057B
Actor_v0 *a0 = (Actor_v0*) a;
- if (a0->_speaking & 1)
+ if (a0->_speaking & 1) {
a0->_speaking ^= 0xFE;
-
+ ++_V0Delay._actorRedrawCount;
+ }
// 0x22B5
if (a0->_miscflags & kActorMiscFlagHide)
continue;
@@ -2404,8 +2512,13 @@ void Actor_v0::limbFrameCheck(int limb) {
void Actor_v0::animateCostume() {
speakCheck();
- if (_vm->_costumeLoader->increaseAnims(this))
+ byte count = _vm->_costumeLoader->increaseAnims(this);
+
+ if (count) {
+ _vm->_V0Delay._actorLimbRedrawDrawCount += count;
+
_needRedraw = true;
+ }
}
void Actor_v0::speakCheck() {
@@ -3254,7 +3367,7 @@ void Actor_v0::animateActor(int anim) {
} else {
- if (anim > 4 && anim <= 7)
+ if (anim >= 4 && anim <= 7)
_facing = normalizeAngle(oldDirToNewDir(dir));
}
}
@@ -3311,13 +3424,13 @@ void Actor_v0::setActorFromTmp() {
}
void Actor_v0::actorSetWalkTo() {
-
+
if (_newWalkBoxEntered == false)
return;
_newWalkBoxEntered = false;
- int nextBox = ((ScummEngine_v0*)_vm)->walkboxFindTarget(this, _walkdata.destbox, _walkdata.dest);
+ int nextBox = ((ScummEngine_v0 *)_vm)->walkboxFindTarget(this, _walkdata.destbox, _walkdata.dest);
if (nextBox != kInvalidBox) {
_walkdata.curbox = nextBox;
}
@@ -3350,10 +3463,38 @@ void Actor_v0::saveLoadWithSerializer(Serializer *ser) {
MKLINE(Actor_v0, _walkYCount, sleByte, VER(97)),
MKLINE(Actor_v0, _walkYCountInc, sleByte, VER(97)),
MKLINE(Actor_v0, _walkMaxXYCountInc, sleByte, VER(97)),
+
+ MKARRAY(Actor_v0, _walkboxQueue[0], sleByte, 16, VER(98)),
+ MKLINE(Actor_v0, _walkboxQueueIndex, sleByte, VER(98)),
MKEND()
};
ser->saveLoadEntries(this, actorEntries);
+
+ // When loading, we need to ensure the limbs are restarted
+ if (ser->isLoading()) {
+
+ // valid costume command?
+ if (_costCommand != 0xFF) {
+
+ // Do we have a walkbox queue?
+ if (_walkboxQueueIndex < 1) {
+ _costCommand = 0xFF;
+
+ // Standing Still
+ setDirection(_facing);
+ speakCheck();
+
+ } else {
+ // Force limb direction update
+ _facing = 0;
+ directionUpdate();
+
+ // Begin walking
+ animateActor(newDirToOldDir(_facing));
+ }
+ }
+ }
}
void Actor::saveLoadWithSerializer(Serializer *ser) {
@@ -3496,6 +3637,8 @@ void Actor::saveLoadWithSerializer(Serializer *ser) {
_walkdata.point3.x >>= V12_X_SHIFT;
_walkdata.point3.y >>= V12_Y_SHIFT;
}
+
+ setDirection(_facing);
}
}
diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h
index c1a3f23318..62ba161f4e 100644
--- a/engines/scumm/actor.h
+++ b/engines/scumm/actor.h
@@ -350,6 +350,11 @@ class Actor_v0 : public Actor_v2 {
public:
Common::Point _CurrentWalkTo, _NewWalkTo;
+ Common::Array<byte> _walkboxHistory;
+
+ byte _walkboxQueue[0x10];
+ byte _walkboxQueueIndex;
+
byte _costCommandNew;
byte _costCommand;
byte _miscflags;
@@ -380,6 +385,12 @@ public:
bool _limb_flipped[8];
+private:
+
+ bool walkBoxQueueAdd(int box);
+ bool walkBoxQueueFind(int box);
+ void walkboxQueueReverse();
+
public:
Actor_v0(ScummEngine *scumm, int id) : Actor_v2(scumm, id) {}
@@ -401,6 +412,9 @@ public:
byte actorWalkY();
byte updateWalkbox();
+ void walkBoxQueueReset();
+ bool walkBoxQueuePrepare();
+
AdjustBoxResult adjustXYToBeInBox(int dstX, int dstY);
AdjustBoxResult adjustPosInBorderWalkbox(AdjustBoxResult box);
diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp
index 087d8425ac..b5caec3c4f 100644
--- a/engines/scumm/boxes.cpp
+++ b/engines/scumm/boxes.cpp
@@ -692,6 +692,22 @@ byte *ScummEngine::getBoxMatrixBaseAddr() {
return ptr;
}
+byte *ScummEngine::getBoxConnectionBase(int box) {
+ byte *boxm = getBoxMatrixBaseAddr();
+
+ int boxIndex = 0;
+
+ for (; boxIndex != box; ++boxIndex) {
+
+ while (*boxm != 0xFF) {
+ ++boxm;
+ }
+
+ ++boxm;
+ }
+
+ return boxm;
+}
/**
* Compute if there is a way that connects box 'from' with box 'to'.
* Returns the number of a box adjacent to 'from' that is the next on the
@@ -719,21 +735,18 @@ int ScummEngine::getNextBox(byte from, byte to) {
boxm = getBoxMatrixBaseAddr();
if (_game.version == 0) {
- // calculate shortest paths
- byte *itineraryMatrix = (byte *)malloc(numOfBoxes * numOfBoxes);
- calcItineraryMatrix(itineraryMatrix, numOfBoxes);
- dest = to;
- do {
- dest = itineraryMatrix[numOfBoxes * from + dest];
- } while (dest != Actor::kInvalidBox && !areBoxesNeighbors(from, dest));
+ boxm = getBoxConnectionBase(from);
- if (dest == Actor::kInvalidBox)
- dest = -1;
+ for (; *boxm != 0xFF; ++boxm) {
+ if (*boxm == to)
+ break;
+ }
- free(itineraryMatrix);
- return dest;
- } else if (_game.version <= 2) {
+ return *boxm;
+
+ }
+ else if (_game.version <= 2) {
// The v2 box matrix is a real matrix with numOfBoxes rows and columns.
// The first numOfBoxes bytes contain indices to the start of the corresponding
// row (although that seems unnecessary to me - the value is easily computable.
@@ -967,6 +980,7 @@ void ScummEngine::calcItineraryMatrix(byte *itineraryMatrix, int num) {
// 255 (= infinity) to all other boxes.
for (i = 0; i < num; i++) {
for (j = 0; j < num; j++) {
+
if (i == j) {
adjacentMatrix[i * boxSize + j] = 0;
itineraryMatrix[i * boxSize + j] = j;
@@ -1159,21 +1173,32 @@ bool ScummEngine::areBoxesNeighbors(int box1nr, int box2nr) {
}
byte ScummEngine_v0::walkboxFindTarget(Actor *a, int destbox, Common::Point walkdest) {
- Actor_v0 *Actor = (Actor_v0*)a;
+ Actor_v0 *Actor = (Actor_v0 *)a;
+ byte nextBox = kOldInvalidBox;
- byte nextBox = getNextBox(a->_walkbox, destbox);
+ // Do we have a walkbox queue to process
+ if (Actor->_walkboxQueueIndex > 1) {
+ nextBox = Actor->_walkboxQueue[--Actor->_walkboxQueueIndex];
- if (nextBox != 0xFF && nextBox == destbox && areBoxesNeighbors(a->_walkbox, nextBox)) {
+ if (Actor->_walkboxQueueIndex <= 1) {
+ Actor->walkBoxQueueReset();
+ }
+ }
+
+ // Target box reached?
+ if (nextBox != Actor::kInvalidBox && nextBox == destbox && areBoxesNeighbors(a->_walkbox, nextBox)) {
Actor->_NewWalkTo = walkdest;
return nextBox;
}
- if (nextBox != 0xFF && nextBox != a->_walkbox) {
+ // Next box reached
+ if (nextBox != Actor::kInvalidBox && nextBox != a->_walkbox) {
- getClosestPtOnBox(getBoxCoordinates(nextBox), a->getPos().x, a->getPos().y, Actor->_NewWalkTo.x, Actor->_NewWalkTo.y);
+ getClosestPtOnBox(getBoxCoordinates(nextBox), a->getRealPos().x, a->getRealPos().y, Actor->_NewWalkTo.x, Actor->_NewWalkTo.y);
} else {
+
if (walkdest.x == -1)
Actor->_NewWalkTo = Actor->_CurrentWalkTo;
else
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 1d1b6b4f13..48818b8abf 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -3795,6 +3795,11 @@ void ScummEngine::fadeOut(int effect) {
_textSurface.fillRect(Common::Rect(0, vs->topline * _textSurfaceMultiplier, _textSurface.pitch, (vs->topline + vs->h) * _textSurfaceMultiplier), 0);
#endif
+ // V0 wipes the text area before fading out
+ if (_game.version == 0) {
+ updateDirtyScreen(kTextVirtScreen);
+ }
+
// TheDig can disable fadeIn(), and may call fadeOut() several times
// successively. Disabling the _screenEffectFlag check forces the screen
// to get cleared. This fixes glitches, at least, in the first cutscenes
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index 42844daf30..86913f9e2e 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -58,7 +58,7 @@ struct CameraData {
/** Virtual screen identifiers */
enum VirtScreenNumber {
kMainVirtScreen = 0, // The 'stage'
- kTextVirtScreen = 1, // In V1-V3 games: the area where text is printed
+ kTextVirtScreen = 1, // In V0-V3 games: the area where text is printed
kVerbVirtScreen = 2, // The verb area
kUnkVirtScreen = 3 // ?? Not sure what this one is good for...
};
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
index db836467ef..da94a34baf 100644
--- a/engines/scumm/object.cpp
+++ b/engines/scumm/object.cpp
@@ -1126,6 +1126,7 @@ void ScummEngine_v80he::clearDrawQueues() {
*/
void ScummEngine::markObjectRectAsDirty(int obj) {
int i, strip;
+ ++_V0Delay._objectRedrawCount;
for (i = 1; i < _numLocalObjects; i++) {
if (_objs[i].obj_nr == (uint16)obj) {
@@ -1133,6 +1134,7 @@ void ScummEngine::markObjectRectAsDirty(int obj) {
const int minStrip = MAX(_screenStartStrip, _objs[i].x_pos / 8);
const int maxStrip = MIN(_screenEndStrip+1, _objs[i].x_pos / 8 + _objs[i].width / 8);
for (strip = minStrip; strip < maxStrip; strip++) {
+ ++_V0Delay._objectStripRedrawCount;
setGfxUsageBit(strip, USAGE_BIT_DIRTY);
}
}
diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h
index 753287e217..fb2d45df8c 100644
--- a/engines/scumm/saveload.h
+++ b/engines/scumm/saveload.h
@@ -47,7 +47,7 @@ namespace Scumm {
* only saves/loads those which are valid for the version of the savegame
* which is being loaded/saved currently.
*/
-#define CURRENT_VER 97
+#define CURRENT_VER 98
/**
* An auxillary macro, used to specify savegame versions. We use this instead
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 3b7dea194b..3c617470fe 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -732,11 +732,40 @@ ScummEngine_v0::ScummEngine_v0(OSystem *syst, const DetectorResult &dr)
VAR_ACTIVE_OBJECT2 = 0xFF;
VAR_IS_SOUND_RUNNING = 0xFF;
VAR_ACTIVE_VERB = 0xFF;
+
+ DelayReset();
if (strcmp(dr.fp.pattern, "maniacdemo.d64") == 0 )
_game.features |= GF_DEMO;
}
+void ScummEngine_v0::DelayReset() {
+ _V0Delay._screenScroll = false;
+ _V0Delay._objectRedrawCount = 0;
+ _V0Delay._objectStripRedrawCount = 0;
+ _V0Delay._actorRedrawCount = 0;
+ _V0Delay._actorLimbRedrawDrawCount = 0;
+}
+
+int ScummEngine_v0::DelayCalculateDelta() {
+ float Time = 0;
+
+ // These values are made up, based on trial/error with visual inspection against WinVice
+ // If anyone feels inclined, the routines in the original engine could be profiled
+ // and these values changed accordindly.
+ Time += _V0Delay._objectRedrawCount * 7;
+ Time += _V0Delay._objectStripRedrawCount * 0.6;
+ Time += _V0Delay._actorRedrawCount * 2.0;
+ Time += _V0Delay._actorLimbRedrawDrawCount * 0.3;
+
+ if (_V0Delay._screenScroll)
+ Time += 3.6f;
+
+ DelayReset();
+
+ return roundf(Time);
+}
+
ScummEngine_v6::ScummEngine_v6(OSystem *syst, const DetectorResult &dr)
: ScummEngine(syst, dr) {
_blastObjectQueuePos = 0;
@@ -2079,13 +2108,24 @@ Common::Error ScummEngine::go() {
if (delta < 1) // Ensure we don't get into an endless loop
delta = 1; // by not decreasing sleepers.
- // WORKAROUND: walking speed in the original v0/v1 interpreter
+ // WORKAROUND: Unfortunately the MOS 6502 wasn't always fast enough for MM
+ // a number of situations can lead to the engine running at less than 60 ticks per second, without this drop
+ // - A single kid is able to escape via the Dungeon Door (after pushing the brick)
+ // - During the intro, calls to 'SetState08' are made for the lights on the mansion, with a 'breakHere'
+ // in between each, the reduction in ticks then occurs while affected stripes are being redrawn.
+ // The music buildup is then out of sync with the text "A Lucasfilm Games Production".
+ // Each call to 'breakHere' has been replaced with calls to 'Delay' in the V1/V2 versions of the game
+ if (_game.version == 0) {
+ delta += ((ScummEngine_v0 *)this)->DelayCalculateDelta();
+ }
+
+ // WORKAROUND: walking speed in the original v1 interpreter
// is sometimes slower (e.g. during scrolling) than in ScummVM.
// This is important for the door-closing action in the dungeon,
// otherwise (delta < 6) a single kid is able to escape.
- if ((_game.version == 0 && isScriptRunning(132)) ||
- (_game.version == 1 && isScriptRunning(137)))
- delta = 6;
+ if (_game.version == 1 && isScriptRunning(137)) {
+ delta = 6;
+ }
// Wait...
waitForTimer(delta * 1000 / 60 - diff);
@@ -2452,6 +2492,8 @@ void ScummEngine_v8::scummLoop_handleSaveLoad() {
void ScummEngine::scummLoop_handleDrawing() {
if (camera._cur != camera._last || _bgNeedsRedraw || _fullRedraw) {
+ _V0Delay._screenScroll = true;
+
redrawBGAreas();
}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index f9758aec33..2906fc71f5 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -298,7 +298,14 @@ struct StringTab : StringSlot {
}
};
+struct ScummEngine_v0_Delays {
+ bool _screenScroll;
+ uint _objectRedrawCount;
+ uint _objectStripRedrawCount;
+ uint _actorRedrawCount;
+ uint _actorLimbRedrawDrawCount;
+};
enum WhereIsObject {
WIO_NOT_FOUND = -1,
@@ -1097,6 +1104,8 @@ public:
// Indy4 Amiga specific
byte *_verbPalette;
+ ScummEngine_v0_Delays _V0Delay;
+
protected:
int _shadowPaletteSize;
byte _currentPalette[3 * 256];
@@ -1131,6 +1140,8 @@ public:
byte getNumBoxes();
byte *getBoxMatrixBaseAddr();
+ byte *getBoxConnectionBase(int box);
+
int getNextBox(byte from, byte to);
void setBoxFlags(int box, int val);
diff --git a/engines/scumm/scumm_v0.h b/engines/scumm/scumm_v0.h
index 4098d639c4..5f40940166 100644
--- a/engines/scumm/scumm_v0.h
+++ b/engines/scumm/scumm_v0.h
@@ -70,6 +70,10 @@ public:
byte walkboxFindTarget(Actor *a, int destbox, Common::Point walkdest);
+ /* Delay calculation */
+ void DelayReset();
+ int DelayCalculateDelta();
+
protected:
virtual void resetRoomObject(ObjectData *od, const byte *room, const byte *searchptr = NULL);