aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm
diff options
context:
space:
mode:
Diffstat (limited to 'engines/scumm')
-rw-r--r--engines/scumm/actor.cpp430
-rw-r--r--engines/scumm/actor.h60
-rw-r--r--engines/scumm/boxes.cpp26
-rw-r--r--engines/scumm/boxes.h2
-rw-r--r--engines/scumm/charset-fontdata.cpp2
-rw-r--r--engines/scumm/costume.cpp241
-rw-r--r--engines/scumm/costume.h18
-rw-r--r--engines/scumm/debugger.cpp8
-rw-r--r--engines/scumm/gfx.cpp89
-rw-r--r--engines/scumm/gfx.h12
-rw-r--r--engines/scumm/object.cpp177
-rw-r--r--engines/scumm/object.h20
-rw-r--r--engines/scumm/player_apple2.h1
-rw-r--r--engines/scumm/player_pce.cpp22
-rw-r--r--engines/scumm/player_pce.h1
-rw-r--r--engines/scumm/player_sid.cpp4
-rw-r--r--engines/scumm/player_sid.h3
-rw-r--r--engines/scumm/room.cpp2
-rw-r--r--engines/scumm/saveload.cpp25
-rw-r--r--engines/scumm/saveload.h2
-rw-r--r--engines/scumm/script.cpp185
-rw-r--r--engines/scumm/script_v0.cpp540
-rw-r--r--engines/scumm/script_v2.cpp142
-rw-r--r--engines/scumm/script_v5.cpp70
-rw-r--r--engines/scumm/scumm.cpp43
-rw-r--r--engines/scumm/scumm.h28
-rw-r--r--engines/scumm/scumm_v0.h93
-rw-r--r--engines/scumm/scumm_v2.h11
-rw-r--r--engines/scumm/scumm_v5.h2
-rw-r--r--engines/scumm/vars.cpp4
-rw-r--r--engines/scumm/verbs.cpp806
-rw-r--r--engines/scumm/verbs.h28
32 files changed, 1620 insertions, 1477 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index eb23c30ebe..dee601b273 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -42,6 +42,14 @@ namespace Scumm {
byte Actor::kInvalidBox = 0;
+static const byte v0ActorTalkArray[0x19] = {
+ 0x00, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x00, 0x46, 0x06,
+ 0x06, 0x06, 0x06, 0xFF, 0xFF,
+ 0x06, 0xC0, 0x06, 0x06, 0x00,
+ 0xC0, 0xC0, 0x00, 0x06, 0x06
+};
+
Actor::Actor(ScummEngine *scumm, int id) :
_vm(scumm), _number(id) {
assert(_vm != 0);
@@ -167,6 +175,21 @@ void Actor_v2::initActor(int mode) {
_talkStopFrame = 4;
}
+void Actor_v0::initActor(int mode) {
+ Actor_v2::initActor(mode);
+
+ _costCommandNew = 0xFF;
+ _costCommand = 0xFF;
+ _miscflags = 0;
+ _speaking = 0;
+
+ _animFrameRepeat = 0;
+ for (int i = 0; i < 8; ++i) {
+ _limbFrameRepeatNew[i] = 0;
+ _limbFrameRepeat[i] = 0;
+ _limb_flipped[i] = false;
+ }
+}
void Actor::setBox(int box) {
_walkbox = box;
@@ -226,12 +249,9 @@ void Actor::stopActorMoving() {
if (_walkScript)
_vm->stopScript(_walkScript);
- // V0 Games will walk on the spot if the actor is stopped mid-walk
- // So we must set the stand still frame
- if (_vm->_game.version == 0)
- startWalkAnim(3, -1);
-
_moving = 0;
+ if (_vm->_game.version == 0)
+ setDirection(_facing);
}
void Actor::setActorWalkSpeed(uint newSpeedX, uint newSpeedY) {
@@ -286,7 +306,7 @@ int Actor::calcMovementFactor(const Common::Point& next) {
deltaYFactor = 0;
}
- if ((uint) ABS((int)(deltaXFactor >> 16)) > _speedx) {
+ if ((uint) ABS(deltaXFactor) > (_speedx << 16)) {
deltaXFactor = _speedx << 16;
if (diffX < 0)
deltaXFactor = -deltaXFactor;
@@ -319,6 +339,9 @@ int Actor::actorWalkStep() {
int distX, distY;
int nextFacing;
+ if (_vm->_game.version == 0)
+ ((Actor_v0 *)this)->_animFrameRepeat = -1;
+
_needRedraw = true;
nextFacing = updateActorDirection(true);
@@ -327,6 +350,10 @@ 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)) {
@@ -361,6 +388,10 @@ int Actor::actorWalkStep() {
_moving &= ~MF_IN_LEG;
return 0;
}
+
+ if (_vm->_game.version == 0)
+ ((Actor_v0 *)this)->animateActor(newDirToOldDir(_facing));
+
return 1;
}
@@ -536,23 +567,101 @@ 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;
+}
+
+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;
+}
+
void Actor_v2::walkActor() {
Common::Point foundPath, tmp;
int new_dir, next_box;
if (_moving & MF_TURN) {
new_dir = updateActorDirection(false);
- // FIXME: is this correct?
if (_facing != new_dir) {
-
- // Actor never stops walking when an object has been selected without this
- if (_vm->_game.version ==0)
- _moving = 0;
-
setDirection(new_dir);
-
- } else
+ } else {
_moving = 0;
+ }
return;
}
@@ -588,14 +697,7 @@ void Actor_v2::walkActor() {
_walkdata.curbox = next_box;
- // WORKAROUND: The route of the meteor landing in the introduction isn't correct.
- // MM V0 in contrast to MM V2 uses two walkboxes instead of just one. Hence a route
- // from walkbox 1 to 0 is calculated first. This causes the meteor to fly on a
- // horizontal line to walkbox 0 then vertically to the ground.
- // To fix this problem, the box-to-box routing has been disabled in room 33.
- if (_vm->_game.version == 0 && _vm->_currentRoom == 33) {
- foundPath = _walkdata.dest;
- } else {
+ 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);
}
@@ -781,7 +883,7 @@ int Actor::remapDirection(int dir, bool is_walking) {
return 180;
}
- // MM C64 stores flags as a part of the mask
+ // MM v0 stores flags as a part of the mask
if (_vm->_game.version == 0) {
mask = _vm->getMaskFromBox(_walkbox);
// face the wall if climbing/descending a ladder
@@ -857,16 +959,6 @@ void Actor::setDirection(int direction) {
if (_costume == 0)
return;
- // V0 MM
- if (_vm->_game.version == 0) {
- if (_moving)
- _vm->_costumeLoader->costumeDecodeData(this, _walkFrame, 0);
- else
- _vm->_costumeLoader->costumeDecodeData(this, _standFrame, 0);
- _needRedraw = true;
- return;
- }
-
// Update the costume for the new direction (and mark the actor for redraw)
aMask = 0x8000;
for (i = 0; i < 16; i++, aMask >>= 1) {
@@ -879,6 +971,34 @@ void Actor::setDirection(int direction) {
_needRedraw = true;
}
+void Actor_v0::setDirection(int direction) {
+ int dir = newDirToOldDir( direction );
+ int res = 0;
+
+ switch (dir) {
+ case 0:
+ res = 4; // Left
+ break;
+
+ case 1:
+ res = 5; // Right
+ break;
+
+ case 2:
+ res = 6; // Face Away
+ break;
+
+ default:
+ res = 7; // Face Camera
+ break;
+ }
+
+ _animFrameRepeat = -1;
+ animateActor(res);
+ if (_moving)
+ animateCostume();
+}
+
void Actor::faceToObject(int obj) {
int x2, y2, dir;
@@ -963,6 +1083,10 @@ void Actor::putActor(int dstX, int dstY, int newRoom) {
if (isInCurrentRoom())
showActor();
}
+
+ // V0 always sets the actor to face the camera upon entering a room
+ if (_vm->_game.version == 0)
+ setDirection(oldDirToNewDir(2));
}
static bool inBoxQuickReject(const BoxCoords &box, int x, int y, int threshold) {
@@ -1062,16 +1186,11 @@ static int checkXYInBoxBounds(int boxnum, int x, int y, int &destX, int &destY)
// yDist must be divided by 4, as we are using 8x2 pixels
// blocks for actor coordinates).
int xDist = ABS(x - destX);
- int yDist;
+ int yDist = ABS(y - destY) / 4;
int dist;
- // MM C64: This fixes the trunk bug (#3070065), as well
- // as the fruit bowl, however im not sure if its
- // the proper solution or not.
- if( g_scumm->_game.version == 0 )
- yDist = ABS(y - destY);
- else
- yDist = ABS(y - destY) / 4;
+ if (g_scumm->_game.version == 0)
+ xDist *= 2;
if (xDist < yDist)
dist = (xDist >> 1) + yDist;
@@ -1090,7 +1209,9 @@ AdjustBoxResult Actor_v2::adjustXYToBeInBox(const int dstX, const int dstY) {
int numBoxes = _vm->getNumBoxes() - 1;
int bestDist = 0xFF;
- for (int box = numBoxes; box >= 0; box--) {
+ for (int i = 0; i <= numBoxes; i++) {
+ // MM v0 prioritizes lower boxes, other engines higher boxes
+ int box = (_vm->_game.version == 0 ? i : numBoxes - i);
int foundX, foundY;
int flags = _vm->getBoxFlags(box);
if ((flags & kBoxInvisible) && !((flags & kBoxPlayerOnly) && !isPlayer()))
@@ -1286,8 +1407,24 @@ void Actor::showActor() {
_vm->ensureResourceLoaded(rtCostume, _costume);
if (_vm->_game.version == 0) {
+ Actor_v0 *a = ((Actor_v0 *)this);
+
+ a->_costCommand = a->_costCommandNew = 0xFF;
+
+ for (int i = 0; i < 8; ++i) {
+ a->_limbFrameRepeat[i] = 0;
+ a->_limbFrameRepeatNew[i] = 0;
+ }
+
_cost.reset();
+
+ a->_animFrameRepeat = 1;
+ a->_speaking = 0;
+
startAnimActor(_standFrame);
+ _visible = true;
+ return;
+
} else if (_vm->_game.version <= 2) {
_cost.reset();
startAnimActor(_standFrame);
@@ -1323,23 +1460,23 @@ static const byte v0ActorSounds[24] = {
0x06, // Bernard
0x06, // Wendy
0x00, // Jeff
- 0x46, // ???
+ 0x46, // Radiation Suit
0x06, // Dr Fred
0x06, // Nurse Edna
0x06, // Weird Ed
0x06, // Dead Cousin Ted
0xFF, // Purple Tentacle
0xFF, // Green Tentacle
- 0x06, // Meteor
- 0xC0, // Plant
- 0x06, // ???
- 0x06, // ???
- 0x00, // ???
- 0xC0, // ???
- 0xC0, // ???
- 0x00, // ???
- 0x06, // Sandy
- 0x06, // ???
+ 0x06, // Meteor police
+ 0xC0, // Meteor
+ 0x06, // Mark Eteer
+ 0x06, // Talkshow Host
+ 0x00, // Plant
+ 0xC0, // Meteor Radiation
+ 0xC0, // Edsel (small, outro)
+ 0x00, // Meteor (small, intro)
+ 0x06, // Sandy (Lab)
+ 0x06, // Sandy (Cut-Scene)
};
/* Used in Scumm v5 only. Play sounds associated with actors */
@@ -1355,7 +1492,10 @@ void ScummEngine::playActorSounds() {
} else {
sound = _actors[i]->_sound[0];
}
- _sound->addSoundToQueue(sound);
+ // fast mode will flood the queue with walk sounds
+ if (!_fastMode) {
+ _sound->addSoundToQueue(sound);
+ }
for (j = 1; j < _numActors; j++) {
_actors[j]->_cost.soundCounter = 0;
}
@@ -1462,6 +1602,18 @@ void ScummEngine::processActors() {
}
}
}
+ } else if (_game.version == 0) {
+ for (int j = 0; j < numactors; ++j) {
+ for (int i = 0; i < numactors; ++i) {
+ // Note: the plant is handled different in v0, the y value is not used.
+ // In v1/2 this is done by the actor's elevation instead.
+ int sc_actor1 = (_sortedActors[j]->_number == 19 ? 0 : _sortedActors[j]->getPos().y);
+ int sc_actor2 = (_sortedActors[i]->_number == 19 ? 0 : _sortedActors[i]->getPos().y);
+ if (sc_actor1 < sc_actor2) {
+ SWAP(_sortedActors[i], _sortedActors[j]);
+ }
+ }
+ }
} else {
for (int j = 0; j < numactors; ++j) {
for (int i = 0; i < numactors; ++i) {
@@ -1479,11 +1631,26 @@ void ScummEngine::processActors() {
for (Actor** ac = _sortedActors; ac != end; ++ac) {
Actor* a = *ac;
- // V0 MM: 0x057B
if (_game.version == 0) {
- ActorC64 *A = (ActorC64*) a;
- if ((A->_speaking & 1))
- A->_speaking ^= 0xFE;
+ // 0x057B
+ Actor_v0 *a0 = (Actor_v0*) a;
+ if (a0->_speaking & 1)
+ a0->_speaking ^= 0xFE;
+
+ // 0x22B5
+ if (a0->_miscflags & kActorMiscFlagHide)
+ continue;
+
+ // Sound
+ if (a0->_moving && _currentRoom != 1 && _currentRoom != 44) {
+ if (a0->_cost.soundPos == 0)
+ a0->_cost.soundCounter++;
+
+ // Is this the correct location?
+ // 0x073C
+ if (v0ActorTalkArray[a0->_number] & 0x3F)
+ a0->_cost.soundPos = (a0->_cost.soundPos + 1) % 3;
+ }
}
// Draw and animate the actors, except those w/o a costume.
// Note: We could 'optimize' this a little bit by only putting
@@ -1779,6 +1946,25 @@ void Actor::startAnimActor(int f) {
}
}
+void Actor_v0::startAnimActor(int f) {
+ if (f == _talkStartFrame) {
+ if (v0ActorTalkArray[_number] & 0x40)
+ return;
+
+ _speaking = 1;
+ return;
+ }
+
+ if (f == _talkStopFrame) {
+
+ _speaking = 0;
+ return;
+ }
+
+ if (f == _standFrame)
+ setDirection(_facing);
+}
+
void Actor::animateActor(int anim) {
int cmd, dir;
@@ -1841,6 +2027,47 @@ void Actor::animateCostume() {
}
}
+void Actor_v0::limbFrameCheck(int limb) {
+ if (_cost.frame[limb] == 0xFFFF)
+ return;
+
+ if (_cost.start[limb] == _cost.frame[limb])
+ return;
+
+ // 0x25A4
+ _cost.start[limb] = _cost.frame[limb];
+
+ _limbFrameRepeat[limb] = _limbFrameRepeatNew[limb];
+
+ // 0x25C3
+ _cost.active[limb] = ((V0CostumeLoader*)_vm->_costumeLoader)->getFrame(this, limb);
+ _cost.curpos[limb] = 0;
+
+ _needRedraw = true;
+}
+
+void Actor_v0::animateCostume() {
+ speakCheck();
+
+ if (_vm->_costumeLoader->increaseAnims(this))
+ _needRedraw = true;
+}
+
+void Actor_v0::speakCheck() {
+ if (v0ActorTalkArray[_number] & 0x80)
+ return;
+
+ int cmd = newDirToOldDir(_facing);
+
+ if (_speaking & 0x80)
+ cmd += 0x0C;
+ else
+ cmd += 0x10;
+
+ _animFrameRepeat = -1;
+ animateActor(cmd);
+}
+
#ifdef ENABLE_SCUMM_7_8
void Actor::animateLimb(int limb, int f) {
// This methods is very similiar to animateCostume().
@@ -1994,7 +2221,7 @@ void ScummEngine::setTalkingActor(int i) {
VAR(VAR_TALK_ACTOR) = i;
}
-static const int c64MMActorTalkColor[25] = {
+static const int v0MMActorTalkColor[25] = {
1, 7, 2, 14, 8, 15, 3, 7, 7, 15, 1, 13, 1, 4, 5, 5, 4, 3, 1, 5, 1, 1, 1, 1, 7
};
static const int v1MMActorTalkColor[25] = {
@@ -2006,7 +2233,7 @@ void ScummEngine::resetV1ActorTalkColor() {
for (i = 1; i < _numActors; i++) {
if (_game.version == 0) {
- _actors[i]->_talkColor = c64MMActorTalkColor[i];
+ _actors[i]->_talkColor = v0MMActorTalkColor[i];
} else {
_actors[i]->_talkColor = v1MMActorTalkColor[i];
}
@@ -2295,22 +2522,23 @@ static const char *const v0ActorNames_English[25] = {
"Bernard",
"Wendy",
"Jeff",
- "",
+ "", // Radiation Suit
"Dr Fred",
"Nurse Edna",
"Weird Ed",
"Dead Cousin Ted",
"Purple Tentacle",
"Green Tentacle",
+ "", // Meteor Police
"Meteor",
- "",
- "",
- "",
+ "", // Mark Eteer
+ "", // Talkshow Host
"Plant",
- "",
- "",
- "",
- "Sandy"
+ "", // Meteor Radiation
+ "", // Edsel (small, outro)
+ "", // Meteor (small, intro)
+ "Sandy", // (Lab)
+ "", // Sandy (Cut-Scene)
};
static const char *const v0ActorNames_German[25] = {
@@ -2328,15 +2556,16 @@ static const char *const v0ActorNames_German[25] = {
"Ted",
"Lila Tentakel",
"Gr<nes Tentakel",
- "Meteor",
"",
+ "Meteor",
"",
"",
"Pflanze",
"",
"",
"",
- "Sandy"
+ "Sandy",
+ "",
};
const byte *Actor::getActorName() {
@@ -2467,6 +2696,8 @@ bool Actor::isPlayer() {
}
bool Actor_v2::isPlayer() {
+ // isPlayer() is not supported by v0
+ assert(_vm->_game.version != 0);
return _vm->VAR(42) <= _number && _number <= _vm->VAR(43);
}
@@ -2621,16 +2852,63 @@ void ScummEngine_v71he::queueAuxEntry(int actorNum, int subIndex) {
}
#endif
+void Actor_v0::animateActor(int anim) {
+ int dir = -1;
+
+ switch (anim) {
+ case 0x00:
+ case 0x04:
+ dir = 0;
+ break;
+
+ case 0x01:
+ case 0x05:
+ dir = 1;
+ break;
+
+ case 0x02:
+ case 0x06:
+ dir = 2;
+ break;
+
+ case 0x03:
+ case 0x07:
+ dir = 3;
+ break;
+
+ default:
+ break;
+ }
+
+ if (isInCurrentRoom()) {
+
+ _costCommandNew = anim;
+ _vm->_costumeLoader->costumeDecodeData(this, 0, 0);
+
+ if (dir == -1)
+ return;
+
+ _facing = normalizeAngle(oldDirToNewDir(dir));
+
+ } else {
+
+ if (anim > 4 && anim <= 7)
+ _facing = normalizeAngle(oldDirToNewDir(dir));
+ }
+}
-void ActorC64::saveLoadWithSerializer(Serializer *ser) {
+void Actor_v0::saveLoadWithSerializer(Serializer *ser) {
Actor::saveLoadWithSerializer(ser);
static const SaveLoadEntry actorEntries[] = {
- MKLINE(ActorC64, _costCommand, sleByte, VER(84)),
- MKLINE(ActorC64, _costFrame, sleByte, VER(84)),
- MKLINE(ActorC64, _miscflags, sleByte, VER(84)),
- MKLINE(ActorC64, _speaking, sleByte, VER(84)),
- MKLINE(ActorC64, _speakingPrev, sleByte, VER(84)),
+ MKLINE(Actor_v0, _costCommand, sleByte, VER(84)),
+ MK_OBSOLETE(Actor_v0, _costFrame, sleByte, VER(84), VER(88)),
+ MKLINE(Actor_v0, _miscflags, sleByte, VER(84)),
+ MKLINE(Actor_v0, _speaking, sleByte, VER(84)),
+ MK_OBSOLETE(Actor_v0, _speakingPrev, sleByte, VER(84), VER(88)),
+ MKLINE(Actor_v0, _animFrameRepeat, sleByte, VER(89)),
+ MKARRAY(Actor_v0, _limbFrameRepeatNew[0], sleInt8, 8, VER(89)),
+ MKARRAY(Actor_v0, _limbFrameRepeat[0], sleInt8, 8, VER(89)),
MKEND()
};
diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h
index 1584d0a78b..0ed239d005 100644
--- a/engines/scumm/actor.h
+++ b/engines/scumm/actor.h
@@ -202,13 +202,13 @@ public:
void adjustActorPos();
virtual AdjustBoxResult adjustXYToBeInBox(int dstX, int dstY);
- void setDirection(int direction);
+ virtual void setDirection(int direction);
void faceToObject(int obj);
void turnToDirection(int newdir);
virtual void walkActor();
void drawActorCostume(bool hitTestMode = false);
virtual void prepareDrawActorCostume(BaseCostumeRenderer *bcr);
- void animateCostume();
+ virtual void animateCostume();
virtual void setActorCostume(int c);
void animateLimb(int limb, int f);
@@ -222,7 +222,7 @@ protected:
void startWalkAnim(int cmd, int angle);
public:
void runActorTalkScript(int f);
- void startAnimActor(int frame);
+ virtual void startAnimActor(int frame);
void remapActorPalette(int r_fact, int g_fact, int b_fact, int threshold);
void remapActorPaletteColor(int slot, int color);
@@ -333,33 +333,53 @@ public:
protected:
virtual bool isPlayer();
virtual void prepareDrawActorCostume(BaseCostumeRenderer *bcr);
+ virtual bool checkWalkboxesHaveDirectPath(Common::Point &foundPath);
};
-class ActorC64 : public Actor_v2 {
+enum ActorV0MiscFlags {
+ kActorMiscFlagStrong = 0x01, // Kid is strong (Hunk-O-Matic used)
+ kActorMiscFlagGTFriend = 0x02, // Kid is green tentacle's friend (recording contract)
+ kActorMiscFlagWatchedTV = 0x04, // Kid knows publisher's address (watched TV)
+ kActorMiscFlagEdsEnemy = 0x08, // Kid is not Weird Ed's friend
+ kActorMiscFlag_10 = 0x10, // ???
+ kActorMiscFlag_20 = 0x20, // ???
+ kActorMiscFlagFreeze = 0x40, // Stop moving
+ kActorMiscFlagHide = 0x80 // Kid is invisible (dead or in radiation suit)
+};
+
+class Actor_v0 : public Actor_v2 {
public:
- byte _costCommand, _costFrame;
- byte _miscflags; // 0x1: strong, 0x8: Ed's enemy, 0x40: stop moving, 0x80: hide(dead/radiation suit)
- byte _speaking, _speakingPrev;
+ byte _costCommandNew;
+ byte _costCommand;
+ byte _miscflags;
+ byte _speaking;
+
+ int8 _animFrameRepeat;
+ int8 _limbFrameRepeatNew[8];
+ int8 _limbFrameRepeat[8];
+
+ bool _limb_flipped[8];
public:
- ActorC64(ScummEngine *scumm, int id) : Actor_v2(scumm, id) {
- _costCommand = 0;
- _costFrame = 0;
- _speaking = 0;
- _speakingPrev = 0;
- }
- virtual void initActor(int mode) {
- Actor_v2::initActor(mode);
- if (mode == -1) {
- _miscflags = 0;
- }
- }
+ Actor_v0(ScummEngine *scumm, int id) : Actor_v2(scumm, id) {}
+
+ virtual void initActor(int mode);
+ virtual void animateActor(int anim);
+ virtual void animateCostume();
+
+ void limbFrameCheck(int limb);
+
+ void speakCheck();
+ virtual void setDirection(int direction);
+ void startAnimActor(int f);
// Used by the save/load system:
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);
};
diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp
index 64d4d7422c..f6d2a18f38 100644
--- a/engines/scumm/boxes.cpp
+++ b/engines/scumm/boxes.cpp
@@ -42,7 +42,7 @@ struct Box { /* Internal walkbox file format */
byte y1;
byte y2;
byte mask;
- } c64;
+ } v0;
struct {
byte uy;
@@ -181,7 +181,7 @@ byte ScummEngine::getMaskFromBox(int box) {
if (_game.version == 8)
return (byte) FROM_LE_32(ptr->v8.mask);
else if (_game.version == 0)
- return ptr->c64.mask;
+ return ptr->v0.mask;
else if (_game.version <= 2)
return ptr->v2.mask;
else
@@ -479,7 +479,7 @@ Box *ScummEngine::getBoxBaseAddr(int box) {
assertRange(0, box, ptr[0] - 1, "box");
if (_game.version == 0)
- return (Box *)(ptr + box * SIZEOF_BOX_C64 + 1);
+ return (Box *)(ptr + box * SIZEOF_BOX_V0 + 1);
else if (_game.version <= 2)
return (Box *)(ptr + box * SIZEOF_BOX_V2 + 1);
else if (_game.version == 3)
@@ -602,19 +602,19 @@ BoxCoords ScummEngine::getBoxCoordinates(int boxnum) {
SWAP(box->ll, box->lr);
}
} else if (_game.version == 0) {
- box->ul.x = bp->c64.x1;
- box->ul.y = bp->c64.y1;
- box->ur.x = bp->c64.x2;
- box->ur.y = bp->c64.y1;
+ box->ul.x = bp->v0.x1;
+ box->ul.y = bp->v0.y1;
+ box->ur.x = bp->v0.x2;
+ box->ur.y = bp->v0.y1;
- box->ll.x = bp->c64.x1;
- box->ll.y = bp->c64.y2;
- box->lr.x = bp->c64.x2;
- box->lr.y = bp->c64.y2;
+ box->ll.x = bp->v0.x1;
+ box->ll.y = bp->v0.y2;
+ box->lr.x = bp->v0.x2;
+ box->lr.y = bp->v0.y2;
- if ((bp->c64.mask & 0x88) == 0x88) {
+ if ((bp->v0.mask & 0x88) == 0x88) {
// walkbox for (right/left) corner
- if (bp->c64.mask & 0x04)
+ if (bp->v0.mask & 0x04)
box->ur = box->ul;
else
box->ul = box->ur;
diff --git a/engines/scumm/boxes.h b/engines/scumm/boxes.h
index e554aea1b5..345d6a9d36 100644
--- a/engines/scumm/boxes.h
+++ b/engines/scumm/boxes.h
@@ -27,7 +27,7 @@
namespace Scumm {
-#define SIZEOF_BOX_C64 5
+#define SIZEOF_BOX_V0 5
#define SIZEOF_BOX_V2 8
#define SIZEOF_BOX_V3 18
#define SIZEOF_BOX 20
diff --git a/engines/scumm/charset-fontdata.cpp b/engines/scumm/charset-fontdata.cpp
index 29465584f8..16193f5503 100644
--- a/engines/scumm/charset-fontdata.cpp
+++ b/engines/scumm/charset-fontdata.cpp
@@ -420,7 +420,7 @@ static const byte germanCharsetDataV2[] = {
126, 10,
};
-// German C64 MM.
+// German v0 MM.
static const byte germanCharsetDataV0[] = {
36, 11,
42, 12,
diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp
index eb3cc3262c..2bbf3b3801 100644
--- a/engines/scumm/costume.cpp
+++ b/engines/scumm/costume.cpp
@@ -72,14 +72,6 @@ static const int v1MMNESLookup[25] = {
0x17, 0x00, 0x01, 0x05, 0x16
};
-static const byte v0ActorTalkArray[0x19] = {
- 0x00, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x00, 0x46, 0x06,
- 0x06, 0x06, 0x06, 0xFF, 0xFF,
- 0x06, 0xC0, 0x06, 0x06, 0x00,
- 0xC0, 0xC0, 0x00, 0x06, 0x06
-};
-
byte ClassicCostumeRenderer::mainRoutine(int xmoveCur, int ymoveCur) {
int i, skip = 0;
byte drawFlag = 1;
@@ -1171,7 +1163,7 @@ byte NESCostumeLoader::increaseAnim(Actor *a, int slot) {
return (a->_cost.curpos[slot] != oldframe);
}
-static const byte actorColorsMMC64[25] = {
+static const byte actorV0Colors[25] = {
0, 7, 2, 6, 9, 1, 3, 7, 7, 1, 1, 9, 1, 4, 5, 5, 4, 1, 0, 5, 4, 2, 2, 7, 7
};
@@ -1186,24 +1178,25 @@ static const byte actorColorsMMC64[25] = {
dst[p + 1] = palette[pcolor]; \
}
-byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {
- if (limb >= 8)
- return 0;
+byte V0CostumeRenderer::drawLimb(const Actor *a, int limb) {
+ const Actor_v0* a0 = (const Actor_v0 *)a;
- if (a->_cost.start[limb] == 0xFFFF)
+ if (limb >= 8)
return 0;
if (limb == 0) {
_draw_top = 200;
_draw_bottom = 0;
}
-
- bool flipped = (a->_cost.start[limb] & 0x80) != 0;
- byte frameStart = _loaded._frameOffsets[a->_cost.frame[limb]];
- byte frame = _loaded._frameOffsets[frameStart + a->_cost.curpos[limb]];
- if (frame == 0xFF)
+
+ // Invalid current position?
+ if (a->_cost.curpos[limb] == 0xFFFF)
return 0;
+ _loaded.loadCostume(a->_costume);
+ byte frame = _loaded._frameOffsets[a->_cost.curpos[limb] + a->_cost.active[limb]];
+
+ // Get the frame ptr
byte ptrLow = _loaded._baseptr[frame];
byte ptrHigh = ptrLow + _loaded._dataOffsets[4];
int frameOffset = (_loaded._baseptr[ptrHigh] << 8) + _loaded._baseptr[ptrLow + 2]; // 0x23EF / 0x2400
@@ -1214,7 +1207,7 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {
byte palette[4] = { 0, 0, 0, 0 };
if (_vm->getCurrentLights() & LIGHTMODE_actor_use_colors) {
palette[1] = 10;
- palette[2] = actorColorsMMC64[_actorID];
+ palette[2] = actorV0Colors[_actorID];
} else {
palette[2] = 11;
palette[3] = 11;
@@ -1231,7 +1224,7 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {
if (!width || !height)
return 0;
- int xpos = _actorX + (flipped ? -1 : +1) * (offsetX * 8 - a->_width / 2);
+ int xpos = _actorX + (a0->_limb_flipped[limb] ? -1 : +1) * (offsetX * 8 - a->_width / 2);
// +1 as we appear to be 1 pixel away from the original interpreter
int ypos = _actorY - offsetY + 1;
@@ -1241,13 +1234,13 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {
byte color = data[y * width + x];
byte pcolor;
- int destX = xpos + (flipped ? -(x + 1) : x) * 8;
+ int destX = xpos + (a0->_limb_flipped[limb] ? -(x + 1) : x) * 8;
int destY = ypos + y;
if (destY >= 0 && destY < _out.h && destX >= 0 && destX < _out.w) {
byte *dst = (byte *)_out.pixels + destY * _out.pitch + destX;
byte *mask = _vm->getMaskBuffer(0, destY, _zbuf);
- if (flipped) {
+ if (a0->_limb_flipped[limb]) {
LINE(0, 0); LINE(2, 2); LINE(4, 4); LINE(6, 6);
} else {
LINE(6, 0); LINE(4, 2); LINE(2, 4); LINE(0, 6);
@@ -1258,7 +1251,7 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {
_draw_top = MIN(_draw_top, ypos);
_draw_bottom = MAX(_draw_bottom, ypos + height);
- if (flipped)
+ if (a0->_limb_flipped[limb])
_vm->markRectAsDirty(kMainVirtScreen, xpos - (width * 8), xpos, ypos, ypos + height, _actorID);
else
_vm->markRectAsDirty(kMainVirtScreen, xpos, xpos + (width * 8), ypos, ypos + height, _actorID);
@@ -1268,11 +1261,11 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {
#undef LINE
#undef MASK_AT
-void C64CostumeRenderer::setCostume(int costume, int shadow) {
+void V0CostumeRenderer::setCostume(int costume, int shadow) {
_loaded.loadCostume(costume);
}
-void C64CostumeLoader::loadCostume(int id) {
+void V0CostumeLoader::loadCostume(int id) {
const byte *ptr = _vm->getResourceAddress(rtCostume, id);
_id = id;
@@ -1282,166 +1275,128 @@ void C64CostumeLoader::loadCostume(int id) {
_numColors = 0;
_numAnim = 0;
_mirror = 0;
- _palette = &actorColorsMMC64[id];
+ _palette = &actorV0Colors[id];
_frameOffsets = _baseptr + READ_LE_UINT16(ptr + 5);
_dataOffsets = ptr;
_animCmds = _baseptr + READ_LE_UINT16(ptr + 7);
-
- _maxHeight = 0;
}
-void C64CostumeLoader::frameUpdate(ActorC64 *a, int cmd ) {
- byte limbFrames = 0;
+void V0CostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) {
+ Actor_v0 *a0 = (Actor_v0 *)a;
+
+ if (!a->_costume)
+ return;
+
+ loadCostume(a->_costume);
+
+ // Invalid costume command?
+ if (a0->_costCommandNew == 0xFF || (a0->_costCommand == a0->_costCommandNew))
+ return;
+
+ a0->_costCommand = a0->_costCommandNew;
+
+ int cmd = a0->_costCommand;
+ byte limbFrameNumber = 0;
// Each costume-command has 8 limbs (0x2622)
cmd <<= 3;
- for (int limb = 0, pos = 0; limb < 8; ++limb, pos = 0) {
- // get a limb frames ptr from the costume command
- limbFrames = ((_animCmds + cmd)[limb]);
-
- // Dont change limb if entry is invalid
- if (limbFrames == 0xFF)
- continue;
+ for (int limb = 0; limb < 8; ++limb) {
- // Has limb frames ptr changed since last update?
- if (a->_cost.start[limb] == limbFrames)
- continue;
+ // get the frame number for the beginning of the costume command
+ limbFrameNumber = ((_animCmds + cmd)[limb]);
- // Set new limb command addresses
- a->_cost.start[limb] = limbFrames;
- a->_cost.frame[limb] = _frameOffsets[limb] + (limbFrames & 0x7f); // limb animation-frames ptr
+ // Is this limb flipped?
+ if (limbFrameNumber & 0x80) {
- // Get first entry of a limbs' frames
- byte frameStart = _frameOffsets[ a->_cost.frame[limb]];
+ // Invalid frame?
+ if (limbFrameNumber == 0xFF)
+ continue;
- // Loop each frame in this limb until we reach the end marker
- while (pos != 0xFF) { // This is just so we dont overflow
- byte frame = _frameOffsets[frameStart + pos];
+ // Store the limb frame number (clear the flipped status)
+ a->_cost.frame[limb] = (limbFrameNumber & 0x7f);
- // Each animation-frame until we find end
- if (frame == 0xFF)
- break;
+ if (a0->_limb_flipped[limb] != true)
+ a->_cost.start[limb] = 0xFFFF;
- byte ptrLow = _baseptr[frame];
- byte ptrHigh = ptrLow + _dataOffsets[4];
- int frameOffset = (_baseptr[ptrHigh] << 8) + _baseptr[ptrLow + 2]; // 0x23EF / 0x2400
+ a0->_limb_flipped[limb] = true;
- const byte *data = _baseptr + frameOffset;
+ } else {
+ //Store the limb frame number
+ a->_cost.frame[limb] = limbFrameNumber;
- if (data[3] > _maxHeight)
- _maxHeight = data[3] + 1;
+ if (a0->_limb_flipped[limb] != false)
+ a->_cost.start[limb] = 0xFFFF;
- ++pos;
+ a0->_limb_flipped[limb] = false;
}
- // Set ending position of limb frames
- a->_cost.end[limb] = pos - 1;
- a->_cost.curpos[limb] = 0;
- }
-}
-
-// based on 0x2BCA, doesn't match disassembly because 'oldDir' variable
-// is not the same value as stored in the original interpreter
-int C64CostumeLoader::dirToDirStop(int oldDir) {
- switch (oldDir) {
- case 0:
- return 4; // Left
- case 1:
- return 5; // Right
- case 2:
- return 6; // Face Camera
- case 3:
- return 7; // Face Away
+ // Set the repeat value
+ a0->_limbFrameRepeatNew[limb] = a0->_animFrameRepeat;
}
- // shouldnt' be reached
- return 4;
}
-void C64CostumeLoader::actorSpeak(ActorC64 *a, int &cmd) {
- if (v0ActorTalkArray[a->_number] & 0x80)
- return;
+byte V0CostumeLoader::getFrame(Actor *a, int limb) {
+ loadCostume(a->_costume);
- if ((a->_speaking & 0x80))
- cmd += 0x0C;
- else
- cmd += 0x10;
+ // Get the frame number for the current limb / Command
+ return _frameOffsets[_frameOffsets[limb] + a->_cost.start[limb]];
}
-void C64CostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) {
- ActorC64 *A = (ActorC64 *)a;
- int dir = newDirToOldDir(a->getFacing());
- int command = dir;
-
- loadCostume(a->_costume);
-
- // Enable/Disable speaking flag
- if (frame == a->_talkStartFrame) {
- if (v0ActorTalkArray[a->_number] & 0x40)
- return;
+byte V0CostumeLoader::increaseAnims(Actor *a) {
+ Actor_v0 *a0 = (Actor_v0 *)a;
+ int i;
+ byte r = 0;
- A->_speaking = 1;
- return;
- }
- if (frame == a->_talkStopFrame) {
- A->_speaking = 0;
- return;
+ for (i = 0; i != 8; i++) {
+ a0->limbFrameCheck(i);
+ r += increaseAnim(a, i);
}
+ return r;
+}
- // Different command for stand frame
- if (frame == a->_standFrame)
- command = dirToDirStop(dir);
-
- // Update the limb frames
- frameUpdate(A, command);
-
- // Keep current command/frame mode
- A->_costCommand = dir;
- A->_costFrame = frame;
+byte V0CostumeLoader::increaseAnim(Actor *a, int limb) {
+ Actor_v0 *a0 = (Actor_v0 *)a;
+ const uint16 limbPrevious = a->_cost.curpos[limb]++;
- // Update 'speaking' frames?
- if (A->_speaking) {
- command = dir; // Incase standing frame was set as cmd
- actorSpeak(A, command);
+ loadCostume(a->_costume);
- // Update the limb speak frames
- frameUpdate(A, command);
- }
-}
+ // 0x2543
+ byte frame = _frameOffsets[a->_cost.curpos[limb] + a->_cost.active[limb]];
-byte C64CostumeLoader::increaseAnims(Actor *a) {
- ActorC64 *A = (ActorC64 *)a;
+ // Is this frame invalid?
+ if (frame == 0xFF) {
- // check if the actor speak flag has changed since last frame increase
- if (A->_speaking != A->_speakingPrev) {
- int cmd = A->_costCommand;
- A->_speakingPrev = A->_speaking;
+ // Repeat timer has reached 0?
+ if (a0->_limbFrameRepeat[limb] == 0) {
- actorSpeak(A, cmd);
+ // Use the previous frame
+ --a0->_cost.curpos[limb];
- // Update the limb frames
- frameUpdate(A, cmd);
- }
+ // Reset the comstume command
+ a0->_costCommandNew = 0xFF;
+ a0->_costCommand = 0xFF;
+
+ // Set the frame/start to invalid
+ a0->_cost.frame[limb] = 0xFFFF;
+ a0->_cost.start[limb] = 0xFFFF;
- if (A->_moving && _vm->_currentRoom != 1 && _vm->_currentRoom != 44) {
- if (a->_cost.soundPos == 0)
- a->_cost.soundCounter++;
+ } else {
- // Is this the correct location?
- // 0x073C
- if (v0ActorTalkArray[a->_number] & 0x3F)
- a->_cost.soundPos = (a->_cost.soundPos + 1) % 3;
- }
+ // Repeat timer enabled?
+ if (a0->_limbFrameRepeat[limb] != -1)
+ --a0->_limbFrameRepeat[limb];
- // increase each frame pos
- for (int limb = 0; limb < 8; ++limb) {
- if (a->_cost.curpos[limb] < a->_cost.end[limb])
- a->_cost.curpos[limb]++;
- else
+ // No, restart at frame 0
a->_cost.curpos[limb] = 0;
+ }
}
+ // Limb frame has changed?
+ if (limbPrevious == a->_cost.curpos[limb])
+ return 0;
+
return 1;
}
diff --git a/engines/scumm/costume.h b/engines/scumm/costume.h
index 3acf2a1f6c..4a21692ddb 100644
--- a/engines/scumm/costume.h
+++ b/engines/scumm/costume.h
@@ -67,20 +67,16 @@ protected:
byte increaseAnim(Actor *a, int slot);
};
-class C64CostumeLoader : public ClassicCostumeLoader {
+class V0CostumeLoader : public ClassicCostumeLoader {
public:
- C64CostumeLoader(ScummEngine *vm) : ClassicCostumeLoader(vm) {}
+ V0CostumeLoader(ScummEngine *vm) : ClassicCostumeLoader(vm) {}
void loadCostume(int id);
void costumeDecodeData(Actor *a, int frame, uint usemask);
byte increaseAnims(Actor *a);
-
- int _maxHeight;
+ byte getFrame(Actor *a, int limb);
protected:
- void actorSpeak(ActorC64 *a, int &cmd);
- int dirToDirStop(int oldDir);
- void frameUpdate(ActorC64 *A, int cmd);
-
+ byte increaseAnim(Actor *a, int limb);
};
class ClassicCostumeRenderer : public BaseCostumeRenderer {
@@ -135,12 +131,12 @@ public:
};
#endif
-class C64CostumeRenderer : public BaseCostumeRenderer {
+class V0CostumeRenderer : public BaseCostumeRenderer {
protected:
- C64CostumeLoader _loaded;
+ V0CostumeLoader _loaded;
public:
- C64CostumeRenderer(ScummEngine *vm) : BaseCostumeRenderer(vm), _loaded(vm) {}
+ V0CostumeRenderer(ScummEngine *vm) : BaseCostumeRenderer(vm), _loaded(vm) {}
void setPalette(uint16 *palette) {}
void setFacing(const Actor *a) {}
diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp
index 54f7fea97b..edcf2e6fea 100644
--- a/engines/scumm/debugger.cpp
+++ b/engines/scumm/debugger.cpp
@@ -382,7 +382,8 @@ bool ScummDebugger::Cmd_Actor(int argc, const char **argv) {
DebugPrintf("Actor[%d].costume = %d\n", actnum, a->_costume);
}
} else if (!strcmp(argv[2], "name")) {
- DebugPrintf("Name of actor %d: %s\n", actnum, _vm->getObjOrActorName(actnum));
+ DebugPrintf("Name of actor %d: %s\n", actnum,
+ _vm->getObjOrActorName(_vm->actorToObj(actnum)));
} else if (!strcmp(argv[2], "condmask")) {
if (argc > 3) {
a->_heCondMask = value;
@@ -427,9 +428,10 @@ bool ScummDebugger::Cmd_PrintObjects(int argc, const char **argv) {
o = &(_vm->_objs[i]);
if (o->obj_nr == 0)
continue;
+ int classData = (_vm->_game.version != 0 ? _vm->_classData[o->obj_nr] : 0);
DebugPrintf("|%4d|%4d|%4d|%5d|%6d|%5d|%2d|$%08x|\n",
o->obj_nr, o->x_pos, o->y_pos, o->width, o->height, o->state,
- o->fl_object_index, _vm->_classData[o->obj_nr]);
+ o->fl_object_index, classData);
}
DebugPrintf("\n");
@@ -446,7 +448,7 @@ bool ScummDebugger::Cmd_Object(int argc, const char **argv) {
}
obj = atoi(argv[1]);
- if (obj >= _vm->_numGlobalObjects) {
+ if (_vm->_game.version != 0 && obj >= _vm->_numGlobalObjects) {
DebugPrintf("Object %d is out of range (range: 1 - %d)\n", obj, _vm->_numGlobalObjects);
return true;
}
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index a22aa1802f..bc6cfc761e 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -26,6 +26,7 @@
#include "scumm/he/intern_he.h"
#endif
#include "scumm/resource.h"
+#include "scumm/scumm_v0.h"
#include "scumm/scumm_v5.h"
#include "scumm/scumm_v6.h"
#include "scumm/usage_bits.h"
@@ -239,7 +240,7 @@ GdiPCEngine::~GdiPCEngine() {
#endif
GdiV1::GdiV1(ScummEngine *vm) : Gdi(vm) {
- memset(&_C64, 0, sizeof(_C64));
+ memset(&_V1, 0, sizeof(_V1));
}
GdiV2::GdiV2(ScummEngine *vm) : Gdi(vm) {
@@ -296,17 +297,17 @@ void GdiPCEngine::loadTiles(byte *roomptr) {
void GdiV1::roomChanged(byte *roomptr) {
for (int i = 0; i < 4; i++){
- _C64.colors[i] = roomptr[6 + i];
+ _V1.colors[i] = roomptr[6 + i];
}
- decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 10), _C64.charMap, 2048);
- decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 12), _C64.picMap, roomptr[4] * roomptr[5]);
- decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 14), _C64.colorMap, roomptr[4] * roomptr[5]);
- decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 16), _C64.maskMap, roomptr[4] * roomptr[5]);
+ decodeV1Gfx(roomptr + READ_LE_UINT16(roomptr + 10), _V1.charMap, 2048);
+ decodeV1Gfx(roomptr + READ_LE_UINT16(roomptr + 12), _V1.picMap, roomptr[4] * roomptr[5]);
+ decodeV1Gfx(roomptr + READ_LE_UINT16(roomptr + 14), _V1.colorMap, roomptr[4] * roomptr[5]);
+ decodeV1Gfx(roomptr + READ_LE_UINT16(roomptr + 16), _V1.maskMap, roomptr[4] * roomptr[5]);
// Read the mask data. The 16bit length value seems to always be 8 too big.
// See bug #1837375 for details on this.
const byte *maskPtr = roomptr + READ_LE_UINT16(roomptr + 18);
- decodeC64Gfx(maskPtr + 2, _C64.maskChar, READ_LE_UINT16(maskPtr) - 8);
+ decodeV1Gfx(maskPtr + 2, _V1.maskChar, READ_LE_UINT16(maskPtr) - 8);
_objectMode = true;
}
@@ -1487,15 +1488,17 @@ void ScummEngine_v5::drawFlashlight() {
_flashlight.isDrawn = true;
}
-// V0 Maniac doesn't have a ScummVar for VAR_CURRENT_LIGHTS, and just uses
-// an internal variable. Emulate this to prevent overwriting script vars...
-// And V6 games do not use the "lights" at all. There, the whole screen is
-// always visible, and actors are always colored, so we fake the correct
-// light value for it.
+int ScummEngine_v0::getCurrentLights() const {
+ // V0 Maniac doesn't have a ScummVar for VAR_CURRENT_LIGHTS, and just uses
+ // an internal variable. Emulate this to prevent overwriting script vars...
+ // And V6 games do not use the "lights" at all. There, the whole screen is
+ // always visible, and actors are always colored, so we fake the correct
+ // light value for it.
+ return _currentLights;
+}
+
int ScummEngine::getCurrentLights() const {
- if (_game.id == GID_MANIAC && _game.version == 0)
- return _currentLights;
- else if (_game.version >= 6)
+ if (_game.version >= 6)
return LIGHTMODE_room_lights_on | LIGHTMODE_actor_use_colors;
else
return VAR(VAR_CURRENT_LIGHTS);
@@ -1538,7 +1541,7 @@ void GdiV1::prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
const int x, const int y, const int width, const int height,
int stripnr, int numstrip) {
if (_objectMode) {
- decodeC64Gfx(ptr, _C64.objectMap, (width / 8) * (height / 8) * 3);
+ decodeV1Gfx(ptr, _V1.objectMap, (width / 8) * (height / 8) * 3);
}
}
@@ -1925,9 +1928,9 @@ bool GdiPCEngine::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const in
bool GdiV1::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const int width, const int height,
int stripnr, const byte *smap_ptr) {
if (_objectMode)
- drawStripC64Object(dstPtr, vs->pitch, stripnr, width, height);
+ drawStripV1Object(dstPtr, vs->pitch, stripnr, width, height);
else
- drawStripC64Background(dstPtr, vs->pitch, stripnr, height);
+ drawStripV1Background(dstPtr, vs->pitch, stripnr, height);
return false;
}
@@ -2068,7 +2071,7 @@ void GdiV1::decodeMask(int x, int y, const int width, const int height,
int stripnr, int numzbuf, const byte *zplane_list[9],
bool transpStrip, byte flag) {
byte *mask_ptr = getMaskBuffer(x, y, 1);
- drawStripC64Mask(mask_ptr, stripnr, width, height);
+ drawStripV1Mask(mask_ptr, stripnr, width, height);
}
void GdiV2::decodeMask(int x, int y, const int width, const int height,
@@ -3086,67 +3089,67 @@ void GdiPCEngine::drawStripPCEngineMask(byte *dst, int stripnr, int top, int hei
}
#endif
-void GdiV1::drawStripC64Background(byte *dst, int dstPitch, int stripnr, int height) {
+void GdiV1::drawStripV1Background(byte *dst, int dstPitch, int stripnr, int height) {
int charIdx;
height /= 8;
for (int y = 0; y < height; y++) {
- _C64.colors[3] = (_C64.colorMap[y + stripnr * height] & 7);
+ _V1.colors[3] = (_V1.colorMap[y + stripnr * height] & 7);
// Check for room color change in V1 zak
if (_roomPalette[0] == 255) {
- _C64.colors[2] = _roomPalette[2];
- _C64.colors[1] = _roomPalette[1];
+ _V1.colors[2] = _roomPalette[2];
+ _V1.colors[1] = _roomPalette[1];
}
- charIdx = _C64.picMap[y + stripnr * height] * 8;
+ charIdx = _V1.picMap[y + stripnr * height] * 8;
for (int i = 0; i < 8; i++) {
- byte c = _C64.charMap[charIdx + i];
- dst[0] = dst[1] = _C64.colors[(c >> 6) & 3];
- dst[2] = dst[3] = _C64.colors[(c >> 4) & 3];
- dst[4] = dst[5] = _C64.colors[(c >> 2) & 3];
- dst[6] = dst[7] = _C64.colors[(c >> 0) & 3];
+ byte c = _V1.charMap[charIdx + i];
+ dst[0] = dst[1] = _V1.colors[(c >> 6) & 3];
+ dst[2] = dst[3] = _V1.colors[(c >> 4) & 3];
+ dst[4] = dst[5] = _V1.colors[(c >> 2) & 3];
+ dst[6] = dst[7] = _V1.colors[(c >> 0) & 3];
dst += dstPitch;
}
}
}
-void GdiV1::drawStripC64Object(byte *dst, int dstPitch, int stripnr, int width, int height) {
+void GdiV1::drawStripV1Object(byte *dst, int dstPitch, int stripnr, int width, int height) {
int charIdx;
height /= 8;
width /= 8;
for (int y = 0; y < height; y++) {
- _C64.colors[3] = (_C64.objectMap[(y + height) * width + stripnr] & 7);
- charIdx = _C64.objectMap[y * width + stripnr] * 8;
+ _V1.colors[3] = (_V1.objectMap[(y + height) * width + stripnr] & 7);
+ charIdx = _V1.objectMap[y * width + stripnr] * 8;
for (int i = 0; i < 8; i++) {
- byte c = _C64.charMap[charIdx + i];
- dst[0] = dst[1] = _C64.colors[(c >> 6) & 3];
- dst[2] = dst[3] = _C64.colors[(c >> 4) & 3];
- dst[4] = dst[5] = _C64.colors[(c >> 2) & 3];
- dst[6] = dst[7] = _C64.colors[(c >> 0) & 3];
+ byte c = _V1.charMap[charIdx + i];
+ dst[0] = dst[1] = _V1.colors[(c >> 6) & 3];
+ dst[2] = dst[3] = _V1.colors[(c >> 4) & 3];
+ dst[4] = dst[5] = _V1.colors[(c >> 2) & 3];
+ dst[6] = dst[7] = _V1.colors[(c >> 0) & 3];
dst += dstPitch;
}
}
}
-void GdiV1::drawStripC64Mask(byte *dst, int stripnr, int width, int height) const {
+void GdiV1::drawStripV1Mask(byte *dst, int stripnr, int width, int height) const {
int maskIdx;
height /= 8;
width /= 8;
for (int y = 0; y < height; y++) {
if (_objectMode)
- maskIdx = _C64.objectMap[(y + 2 * height) * width + stripnr] * 8;
+ maskIdx = _V1.objectMap[(y + 2 * height) * width + stripnr] * 8;
else
- maskIdx = _C64.maskMap[y + stripnr * height] * 8;
+ maskIdx = _V1.maskMap[y + stripnr * height] * 8;
for (int i = 0; i < 8; i++) {
- byte c = _C64.maskChar[maskIdx + i];
+ byte c = _V1.maskChar[maskIdx + i];
- // V1/C64 masks are inverted compared to what ScummVM expects
+ // V1/V0 masks are inverted compared to what ScummVM expects
*dst = c ^ 0xFF;
dst += _numStrips;
}
}
}
-void GdiV1::decodeC64Gfx(const byte *src, byte *dst, int size) const {
+void GdiV1::decodeV1Gfx(const byte *src, byte *dst, int size) const {
int x, z;
byte color, run, common[4];
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index 4b44ddc376..0d81698c50 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -375,19 +375,19 @@ public:
class GdiV1 : public Gdi {
protected:
- /** Render settings which are specific to the C64 graphic decoders. */
+ /** Render settings which are specific to the v0/v1 graphic decoders. */
struct {
byte colors[4];
byte charMap[2048], objectMap[2048], picMap[4096], colorMap[4096];
byte maskMap[4096], maskChar[4096];
- } _C64;
+ } _V1;
protected:
- void decodeC64Gfx(const byte *src, byte *dst, int size) const;
+ void decodeV1Gfx(const byte *src, byte *dst, int size) const;
- void drawStripC64Object(byte *dst, int dstPitch, int stripnr, int width, int height);
- void drawStripC64Background(byte *dst, int dstPitch, int stripnr, int height);
- void drawStripC64Mask(byte *dst, int stripnr, int width, int height) const;
+ void drawStripV1Object(byte *dst, int dstPitch, int stripnr, int width, int height);
+ void drawStripV1Background(byte *dst, int dstPitch, int stripnr, int height);
+ void drawStripV1Mask(byte *dst, int stripnr, int width, int height) const;
virtual bool drawStrip(byte *dstPtr, VirtScreen *vs,
int x, int y, const int width, const int height,
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
index da238dc517..399cd91324 100644
--- a/engines/scumm/object.cpp
+++ b/engines/scumm/object.cpp
@@ -178,10 +178,7 @@ void ScummEngine::clearOwnerOf(int obj) {
// Alternatively, scan the inventory to see if the object is in there...
for (i = 0; i < _numInventory; i++) {
if (_inventory[i] == obj) {
- if (_game.version == 0)
- assert(WIO_INVENTORY == whereIsObjectInventory(obj));
- else
- assert(WIO_INVENTORY == whereIsObject(obj));
+ assert(WIO_INVENTORY == whereIsObject(obj));
// Found the object! Nuke it from the inventory.
_res->nukeResource(rtInventory, i);
@@ -206,6 +203,9 @@ void ScummEngine::clearOwnerOf(int obj) {
}
bool ScummEngine::getClass(int obj, int cls) const {
+ if (_game.version == 0)
+ return false;
+
assertRange(0, obj, _numGlobalObjects - 1, "object");
cls &= 0x7F;
assertRange(1, cls, 32, "class");
@@ -233,6 +233,9 @@ bool ScummEngine::getClass(int obj, int cls) const {
}
void ScummEngine::putClass(int obj, int cls, bool set) {
+ if (_game.version == 0)
+ return;
+
assertRange(0, obj, _numGlobalObjects - 1, "object");
cls &= 0x7F;
assertRange(1, cls, 32, "class");
@@ -315,47 +318,38 @@ int ScummEngine::getObjectIndex(int object) const {
return -1;
for (i = (_numLocalObjects-1); i > 0; i--) {
- if (_game.version == 0 )
- if( _objs[i].flags != _v0ObjectFlag )
- continue;
-
if (_objs[i].obj_nr == object)
return i;
}
return -1;
}
-int ScummEngine::whereIsObjectInventory(int object) {
- int res = 0;
- _v0ObjectInInventory = true;
- res = whereIsObject(object);
- _v0ObjectInInventory = false;
-
- return res;
-}
-
int ScummEngine::whereIsObject(int object) const {
int i;
- if (object >= _numGlobalObjects)
+ // Note: in MM v0 bg objects are greater _numGlobalObjects
+ if (_game.version != 0 && object >= _numGlobalObjects)
return WIO_NOT_FOUND;
if (object < 1)
return WIO_NOT_FOUND;
- if ((_objectOwnerTable[object] != OF_OWNER_ROOM && _game.version != 0) || _v0ObjectInInventory) {
+ if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) &&
+ _objectOwnerTable[object] != OF_OWNER_ROOM)
+ {
for (i = 0; i < _numInventory; i++)
if (_inventory[i] == object)
return WIO_INVENTORY;
return WIO_NOT_FOUND;
}
- for (i = (_numLocalObjects-1); i > 0; i--)
- if ((_objs[i].obj_nr == object && !_v0ObjectIndex) || (_v0ObjectIndex && i == object)) {
+ for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_objs[i].obj_nr == object) {
if (_objs[i].fl_object_index)
return WIO_FLOBJECT;
return WIO_ROOM;
}
+ }
return WIO_NOT_FOUND;
}
@@ -363,8 +357,8 @@ int ScummEngine::whereIsObject(int object) const {
int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) {
Actor *act;
- if (object < _numActors) {
- act = derefActorSafe(object, "getObjectOrActorXY");
+ if (objIsActor(object)) {
+ act = derefActorSafe(objToActor(object), "getObjectOrActorXY");
if (act && act->isInCurrentRoom()) {
x = act->getRealPos().x;
y = act->getRealPos().y;
@@ -377,7 +371,7 @@ int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) {
case WIO_NOT_FOUND:
return -1;
case WIO_INVENTORY:
- if (_objectOwnerTable[object] < _numActors) {
+ if (objIsActor(_objectOwnerTable[object])) {
act = derefActor(_objectOwnerTable[object], "getObjectOrActorXY(2)");
if (act && act->isInCurrentRoom()) {
x = act->getRealPos().x;
@@ -396,7 +390,7 @@ int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) {
* Returns X, Y and direction in angles
*/
void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) {
- int idx = (_v0ObjectIndex) ? object : getObjectIndex(object);
+ int idx = getObjectIndex(object);
assert(idx >= 0);
ObjectData &od = _objs[idx];
int state;
@@ -439,8 +433,15 @@ void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) {
y = od.y_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].y);
}
} else if (_game.version <= 2) {
- x = od.walk_x >> V12_X_SHIFT;
- y = od.walk_y >> V12_Y_SHIFT;
+ if (od.actordir) {
+ x = od.walk_x;
+ y = od.walk_y;
+ } else {
+ x = od.x_pos + od.width / 2;
+ y = od.y_pos + od.height / 2;
+ }
+ x = x >> V12_X_SHIFT;
+ y = y >> V12_Y_SHIFT;
} else {
x = od.walk_x;
y = od.walk_y;
@@ -462,11 +463,11 @@ int ScummEngine::getObjActToObjActDist(int a, int b) {
Actor *acta = NULL;
Actor *actb = NULL;
- if (a < _numActors)
- acta = derefActorSafe(a, "getObjActToObjActDist");
+ if (objIsActor(a))
+ acta = derefActorSafe(objToActor(a), "getObjActToObjActDist");
- if (b < _numActors)
- actb = derefActorSafe(b, "getObjActToObjActDist(2)");
+ if (objIsActor(b))
+ actb = derefActorSafe(objToActor(b), "getObjActToObjActDist(2)");
if (acta && actb && acta->getRoom() == actb->getRoom() && acta->getRoom() && !acta->isInCurrentRoom())
return 0;
@@ -492,14 +493,6 @@ int ScummEngine::getObjActToObjActDist(int a, int b) {
return getDist(x, y, x2, y2);
}
-int ScummEngine_v0::findObjectIndex(int x, int y) {
- int objIdx;
- _v0ObjectIndex = true;
- objIdx = findObject(x, y);
- _v0ObjectIndex = false;
- return objIdx;
-}
-
int ScummEngine::findObject(int x, int y) {
int i, b;
byte a;
@@ -509,11 +502,9 @@ int ScummEngine::findObject(int x, int y) {
if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable))
continue;
- if (_game.version == 0) {
- if (_objs[i].flags == 0 && _objs[i].state & kObjectStateUntouchable)
- continue;
- } else {
- if (_game.version <= 2 && _objs[i].state & kObjectStateUntouchable)
+ if ((_game.version == 0 && OBJECT_V0_TYPE(_objs[i].obj_nr) == kObjectV0TypeFG) ||
+ (_game.version > 0 && _game.version <= 2)) {
+ if (_objs[i].state & kObjectStateUntouchable)
continue;
}
@@ -529,15 +520,8 @@ int ScummEngine::findObject(int x, int y) {
}
#endif
if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
- _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y) {
- // MMC64: Set the object search flag
- if (_game.version == 0)
- _v0ObjectFlag = _objs[i].flags;
- if (_game.version == 0 && _v0ObjectIndex)
- return i;
- else
- return _objs[i].obj_nr;
- }
+ _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y)
+ return _objs[i].obj_nr;
break;
}
} while ((_objs[b].state & mask) == a);
@@ -824,7 +808,7 @@ void ScummEngine_v3old::resetRoomObjects() {
else
ptr = room + 29;
- // Default pointer of objects without image, in C64 verison of Maniac Mansion
+ // Default pointer of objects without image, in v0 version of Maniac Mansion
int defaultPtr = READ_LE_UINT16(ptr + 2 * _numObjectsInRoom);
for (i = 0; i < _numObjectsInRoom; i++) {
@@ -843,9 +827,6 @@ void ScummEngine_v3old::resetRoomObjects() {
if (_dumpScripts) {
char buf[32];
sprintf(buf, "roomobj-%d-", _roomResource);
- if (_game.version == 0)
- sprintf(buf + 11, "%d-", od->flags);
-
dumpResource(buf, od->obj_nr, room + od->OBCDoffset);
}
}
@@ -911,8 +892,7 @@ void ScummEngine_v0::resetRoomObject(ObjectData *od, const byte *room, const byt
const byte *ptr = room + od->OBCDoffset;
ptr -= 2;
- od->obj_nr = *(ptr + 6);
- od->flags = *(ptr + 7);
+ od->obj_nr = OBJECT_V0(*(ptr + 6), *(ptr + 7));
od->x_pos = *(ptr + 8) * 8;
od->y_pos = ((*(ptr + 9)) & 0x7F) * 8;
@@ -1072,8 +1052,8 @@ void ScummEngine::updateObjectStates() {
int i;
ObjectData *od = &_objs[1];
for (i = 1; i < _numLocalObjects; i++, od++) {
- // V0 MM, Room objects with Flag == 1 are objects with 'no-state' (room specific objects, non-pickup)
- if (_game.version == 0 && od->flags == 1)
+ // V0 MM, objects with type == 1 are room objects (room specific objects, non-pickup)
+ if (_game.version == 0 && OBJECT_V0_TYPE(od->obj_nr) == kObjectV0TypeBG)
continue;
if (od->obj_nr > 0)
@@ -1163,8 +1143,8 @@ const byte *ScummEngine::getObjOrActorName(int obj) {
byte *objptr;
int i;
- if (obj < _numActors && _game.version >= 1)
- return derefActor(obj, "getObjOrActorName")->getActorName();
+ if (objIsActor(obj))
+ return derefActor(objToActor(obj), "getObjOrActorName")->getActorName();
for (i = 0; i < _numNewNames; i++) {
if (_newNames[i] == obj) {
@@ -1173,7 +1153,7 @@ const byte *ScummEngine::getObjOrActorName(int obj) {
}
}
- objptr = getOBCDFromObject(obj);
+ objptr = getOBCDFromObject(obj, true);
if (objptr == NULL)
return NULL;
@@ -1200,7 +1180,7 @@ const byte *ScummEngine::getObjOrActorName(int obj) {
void ScummEngine::setObjectName(int obj) {
int i;
- if (obj < _numActors && _game.version != 0)
+ if (objIsActor(obj))
error("Can't set actor %d name with new-name-of", obj);
for (i = 0; i < _numNewNames; i++) {
@@ -1226,13 +1206,10 @@ void ScummEngine::setObjectName(int obj) {
uint32 ScummEngine::getOBCDOffs(int object) const {
int i;
- if ((_objectOwnerTable[object] != OF_OWNER_ROOM && (_game.version != 0)) || _v0ObjectInInventory)
+ if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) &&
+ _objectOwnerTable[object] != OF_OWNER_ROOM)
return 0;
- // V0 MM Return by Index
- if (_v0ObjectIndex)
- return _objs[object].OBCDoffset;
-
for (i = (_numLocalObjects-1); i > 0; i--) {
if (_objs[i].obj_nr == object) {
if (_objs[i].fl_object_index != 0)
@@ -1243,21 +1220,22 @@ uint32 ScummEngine::getOBCDOffs(int object) const {
return 0;
}
-byte *ScummEngine::getOBCDFromObject(int obj) {
- bool useInventory = _v0ObjectInInventory;
+byte *ScummEngine::getOBCDFromObject(int obj, bool v0CheckInventory) {
int i;
byte *ptr;
- _v0ObjectInInventory = false;
-
- if ((_objectOwnerTable[obj] != OF_OWNER_ROOM && (_game.version != 0)) || useInventory) {
+ if ((_game.version != 0 || OBJECT_V0_TYPE(obj) == 0) &&
+ _objectOwnerTable[obj] != OF_OWNER_ROOM)
+ {
+ if (_game.version == 0 && !v0CheckInventory)
+ return 0;
for (i = 0; i < _numInventory; i++) {
if (_inventory[i] == obj)
return getResourceAddress(rtInventory, i);
}
} else {
for (i = (_numLocalObjects-1); i > 0; --i) {
- if ((_objs[i].obj_nr == obj && !_v0ObjectIndex) || (_v0ObjectIndex && i == obj)) {
+ if (_objs[i].obj_nr == obj) {
if (_objs[i].fl_object_index) {
assert(_objs[i].OBCDoffset == 8);
ptr = getResourceAddress(rtFlObject, _objs[i].fl_object_index);
@@ -1510,11 +1488,37 @@ void ScummEngine::findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint id,
}
}
+bool ScummEngine_v0::objIsActor(int obj) {
+ // object IDs < _numActors are used in v0 for objects too (e.g. hamster)
+ return OBJECT_V0_TYPE(obj) == kObjectV0TypeActor;
+}
+
+int ScummEngine_v0::objToActor(int obj) {
+ return OBJECT_V0_ID(obj);
+}
+
+int ScummEngine_v0::actorToObj(int actor) {
+ return OBJECT_V0(actor, kObjectV0TypeActor);
+}
+
+bool ScummEngine::objIsActor(int obj) {
+ return obj < _numActors;
+}
+
+int ScummEngine::objToActor(int obj) {
+ return obj;
+}
+
+int ScummEngine::actorToObj(int actor) {
+ return actor;
+}
+
int ScummEngine::getObjX(int obj) {
- if (obj < _numActors) {
- if (obj < 1)
- return 0; /* fix for indy4's map */
- return derefActor(obj, "getObjX")->getRealPos().x;
+ if (obj < 1)
+ return 0; /* fix for indy4's map */
+
+ if (objIsActor(obj)) {
+ return derefActor(objToActor(obj), "getObjX")->getRealPos().x;
} else {
if (whereIsObject(obj) == WIO_NOT_FOUND)
return -1;
@@ -1525,10 +1529,11 @@ int ScummEngine::getObjX(int obj) {
}
int ScummEngine::getObjY(int obj) {
- if (obj < _numActors) {
- if (obj < 1)
- return 0; /* fix for indy4's map */
- return derefActor(obj, "getObjY")->getRealPos().y;
+ if (obj < 1)
+ return 0; /* fix for indy4's map */
+
+ if (objIsActor(obj)) {
+ return derefActor(objToActor(obj), "getObjY")->getRealPos().y;
} else {
if (whereIsObject(obj) == WIO_NOT_FOUND)
return -1;
@@ -1544,8 +1549,8 @@ int ScummEngine::getObjOldDir(int obj) {
int ScummEngine::getObjNewDir(int obj) {
int dir;
- if (obj < _numActors) {
- dir = derefActor(obj, "getObjNewDir")->getFacing();
+ if (objIsActor(obj)) {
+ dir = derefActor(objToActor(obj), "getObjNewDir")->getFacing();
} else {
int x, y;
getObjectXYPos(obj, x, y, dir);
diff --git a/engines/scumm/object.h b/engines/scumm/object.h
index cdf8b09e6f..8212075e43 100644
--- a/engines/scumm/object.h
+++ b/engines/scumm/object.h
@@ -24,6 +24,26 @@
namespace Scumm {
+static inline int OBJECT_V0(int id, byte type) {
+ assert(id < 256);
+ return (type << 8 | id);
+}
+#define OBJECT_V0_ID(obj) (obj & 0xFF)
+#define OBJECT_V0_TYPE(obj) ((obj >> 8) & 0xFF)
+
+enum ObjectV0Type {
+ kObjectV0TypeFG = 0, // foreground object
+ // - with owner/state, might (but has not to) be pickupable
+ // -> with entry in _objectOwner/StateTable
+ // -> all objects in _inventory have this type
+ // - image can be exchanged (background overlay)
+ kObjectV0TypeBG = 1, // background object
+ // - without owner/state, not pickupable (room only)
+ // -> without entry in _objectOwner/StateTable
+ // - image cannot be exchanged (part of background image)
+ kObjectV0TypeActor = 2 // object is an actor
+};
+
enum ObjectClass {
kObjectClassNeverClip = 20,
kObjectClassAlwaysClip = 21,
diff --git a/engines/scumm/player_apple2.h b/engines/scumm/player_apple2.h
index 4cbd24b81d..c3e1b7fc60 100644
--- a/engines/scumm/player_apple2.h
+++ b/engines/scumm/player_apple2.h
@@ -247,7 +247,6 @@ public:
_sampleRate = rate;
_sampleConverter.setSampleRate(rate);
}
- void startMusic(int songResIndex);
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
diff --git a/engines/scumm/player_pce.cpp b/engines/scumm/player_pce.cpp
index 786971c683..8d886ee008 100644
--- a/engines/scumm/player_pce.cpp
+++ b/engines/scumm/player_pce.cpp
@@ -269,13 +269,13 @@ void PSG_HuC6280::init() {
reset();
// Make waveform frequency table
- for(i = 0; i < 4096; i++) {
+ for (i = 0; i < 4096; i++) {
step = ((_clock / _rate) * 4096) / (i+1);
_waveFreqTable[(1 + i) & 0xFFF] = (uint32)step;
}
// Make noise frequency table
- for(i = 0; i < 32; i++) {
+ for (i = 0; i < 32; i++) {
step = ((_clock / _rate) * 32) / (i+1);
_noiseFreqTable[i] = (uint32)step;
}
@@ -283,7 +283,7 @@ void PSG_HuC6280::init() {
// Make volume table
// PSG_HuC6280 has 48dB volume range spread over 32 steps
step = 48.0 / 32.0;
- for(i = 0; i < 31; i++) {
+ for (i = 0; i < 31; i++) {
_volumeTable[i] = (uint16)level;
level /= pow(10.0, step / 20.0);
}
@@ -323,7 +323,7 @@ void PSG_HuC6280::write(int offset, byte data) {
case 0x04: // Channel control (key-on, DDA mode, volume)
// 1-to-0 transition of DDA bit resets waveform index
- if((chan->control & 0x40) && ((data & 0x40) == 0)) {
+ if ((chan->control & 0x40) && ((data & 0x40) == 0)) {
chan->index = 0;
}
chan->control = data;
@@ -383,9 +383,9 @@ void PSG_HuC6280::update(int16* samples, int sampleCnt) {
// Clear buffer
memset(samples, 0, 2 * sampleCnt * sizeof(int16));
- for(ch = 0; ch < 6; ch++) {
+ for (ch = 0; ch < 6; ch++) {
// Only look at enabled channels
- if(_channel[ch].control & 0x80) {
+ if (_channel[ch].control & 0x80) {
int lal = (_channel[ch].balance >> 4) & 0x0F;
int ral = (_channel[ch].balance >> 0) & 0x0F;
int al = _channel[ch].control & 0x1F;
@@ -395,25 +395,25 @@ void PSG_HuC6280::update(int16* samples, int sampleCnt) {
// Calculate volume just as the patent says
vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal);
- if(vll > 0x1F) vll = 0x1F;
+ if (vll > 0x1F) vll = 0x1F;
vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal);
- if(vlr > 0x1F) vlr = 0x1F;
+ if (vlr > 0x1F) vlr = 0x1F;
vll = _volumeTable[vll];
vlr = _volumeTable[vlr];
// Check channel mode
- if(_channel[ch].control & 0x40) {
+ if (_channel[ch].control & 0x40) {
/* DDA mode */
- for(i = 0; i < sampleCnt; i++) {
+ for (i = 0; i < sampleCnt; i++) {
samples[2*i] += (int16)(vll * (_channel[ch].dda - 16));
samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16));
}
} else {
/* Waveform mode */
uint32 step = _waveFreqTable[_channel[ch].frequency];
- for(i = 0; i < sampleCnt; i += 1) {
+ for (i = 0; i < sampleCnt; i += 1) {
int offset;
int16 data;
offset = (_channel[ch].counter >> 12) & 0x1F;
diff --git a/engines/scumm/player_pce.h b/engines/scumm/player_pce.h
index eb6afd892a..427fb1ace6 100644
--- a/engines/scumm/player_pce.h
+++ b/engines/scumm/player_pce.h
@@ -76,7 +76,6 @@ public:
virtual ~Player_PCE();
virtual void setMusicVolume(int vol) { _maxvol = vol; }
- void startMusic(int songResIndex);
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
diff --git a/engines/scumm/player_sid.cpp b/engines/scumm/player_sid.cpp
index f0f60a3924..ecfaef22c1 100644
--- a/engines/scumm/player_sid.cpp
+++ b/engines/scumm/player_sid.cpp
@@ -683,7 +683,7 @@ void Player_SID::stopSound_intern(int soundResID) { // $5093
releaseResource(soundResID);
}
-void Player_SID::stopAllSounds_intern() { // $4CAA
+void Player_SID::stopMusic_intern() { // $4CAA
statusBits1B = 0;
isMusicPlaying = false;
@@ -1352,7 +1352,7 @@ void Player_SID::stopSound(int nr) {
void Player_SID::stopAllSounds() {
Common::StackLock lock(_mutex);
- stopAllSounds_intern();
+ resetPlayerState();
}
int Player_SID::getSoundStatus(int nr) const {
diff --git a/engines/scumm/player_sid.h b/engines/scumm/player_sid.h
index baeb7bbef0..12e3573575 100644
--- a/engines/scumm/player_sid.h
+++ b/engines/scumm/player_sid.h
@@ -57,7 +57,6 @@ public:
virtual ~Player_SID();
virtual void setMusicVolume(int vol) { _maxvol = vol; }
- void startMusic(int songResIndex);
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
@@ -95,7 +94,7 @@ private:
void initMusic(int songResIndex); // $7de6
int initSound(int soundResID); // $4D0A
void stopSound_intern(int soundResID); // $5093
- void stopAllSounds_intern(); // $4CAA
+ void stopMusic_intern(); // $4CAA
void resetSID(); // $48D8
void update(); // $481B
diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp
index 63cbef8944..9ee8fb93a9 100644
--- a/engines/scumm/room.cpp
+++ b/engines/scumm/room.cpp
@@ -747,7 +747,7 @@ void ScummEngine_v3old::resetRoomSubBlocks() {
}
ptr = roomptr + *(roomptr + 0x15);
- size = numOfBoxes * SIZEOF_BOX_C64 + 1;
+ size = numOfBoxes * SIZEOF_BOX_V0 + 1;
_res->createResource(rtMatrix, 2, size + 1);
getResourceAddress(rtMatrix, 2)[0] = numOfBoxes;
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 38dbd8270a..51a291b7f2 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -1205,12 +1205,19 @@ void ScummEngine::saveOrLoad(Serializer *s) {
// Save/load local objects
//
s->saveLoadArrayOf(_objs, _numLocalObjects, sizeof(_objs[0]), objectEntries);
- if (s->isLoading() && s->getVersion() < VER(13)) {
- // Since roughly v13 of the save games, the objs storage has changed a bit
- for (i = _numObjectsInRoom; i < _numLocalObjects; i++) {
- _objs[i].obj_nr = 0;
+ if (s->isLoading()) {
+ if (s->getVersion() < VER(13)) {
+ // Since roughly v13 of the save games, the objs storage has changed a bit
+ for (i = _numObjectsInRoom; i < _numLocalObjects; i++)
+ _objs[i].obj_nr = 0;
+ } else if (_game.version == 0 && s->getVersion() < VER(89)) {
+ for (i = 0; i < _numLocalObjects; i++) {
+ // Merge object id and type (previously stored in flags)
+ if (_objs[i].obj_nr != 0 && OBJECT_V0_TYPE(_objs[i].obj_nr) == 0 && _objs[i].flags != 0)
+ _objs[i].obj_nr = OBJECT_V0(_objs[i].obj_nr, _objs[i].flags);
+ _objs[i].flags = 0;
+ }
}
-
}
@@ -1497,6 +1504,14 @@ void ScummEngine_v0::saveOrLoad(Serializer *s) {
const SaveLoadEntry v0Entrys[] = {
MKLINE(ScummEngine_v0, _currentMode, sleByte, VER(78)),
MKLINE(ScummEngine_v0, _currentLights, sleByte, VER(78)),
+ MKLINE(ScummEngine_v0, _activeVerb, sleByte, VER(89)),
+ MKLINE(ScummEngine_v0, _activeObject, sleUint16, VER(89)),
+ MKLINE(ScummEngine_v0, _activeObject2, sleUint16, VER(89)),
+ MKLINE(ScummEngine_v0, _cmdVerb, sleByte, VER(89)),
+ MKLINE(ScummEngine_v0, _cmdObject, sleUint16, VER(89)),
+ MKLINE(ScummEngine_v0, _cmdObject2, sleUint16, VER(89)),
+ MKLINE(ScummEngine_v0, _walkToObject, sleUint16, VER(89)),
+ MKLINE(ScummEngine_v0, _walkToObjectState, sleByte, VER(89)),
MKEND()
};
s->saveLoadEntries(this, v0Entrys);
diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h
index 064bdf1406..898f80f867 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 88
+#define CURRENT_VER 89
/**
* An auxillary macro, used to specify savegame versions. We use this instead
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 37ea3a9a9f..39420ee974 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -28,6 +28,7 @@
#include "scumm/object.h"
#include "scumm/resource.h"
#include "scumm/util.h"
+#include "scumm/scumm_v0.h"
#include "scumm/scumm_v2.h"
#include "scumm/sound.h"
#include "scumm/verbs.h"
@@ -131,8 +132,6 @@ void ScummEngine::runObjectScript(int object, int entry, bool freezeResistant, b
initializeLocals(slot, vars);
- // V0 Ensure we don't try and access objects via index inside the script
- _v0ObjectIndex = false;
runScriptNested(slot);
}
@@ -196,9 +195,10 @@ int ScummEngine::getVerbEntrypoint(int obj, int entry) {
return verboffs + 8 + READ_LE_UINT32(ptr + 1);
} else if (_game.version <= 2) {
do {
+ const int kFallbackEntry = (_game.version == 0 ? 0x0F : 0xFF);
if (!*verbptr)
return 0;
- if (*verbptr == entry || *verbptr == 0xFF)
+ if (*verbptr == entry || *verbptr == kFallbackEntry)
break;
verbptr += 2;
} while (1);
@@ -1000,7 +1000,7 @@ void ScummEngine::killScriptsAndResources() {
for (i = 0; i < _numNewNames; i++) {
const int obj = _newNames[i];
if (obj) {
- const int owner = getOwner(obj);
+ const int owner = getOwner((_game.version != 0 ? obj : OBJECT_V0_ID(obj)));
// We can delete custom name resources if either the object is
// no longer in use (i.e. not owned by anyone anymore); or if
// it is an object which is owned by a room.
@@ -1130,6 +1130,183 @@ void ScummEngine::checkAndRunSentenceScript() {
runScript(sentenceScript, 0, 0, localParamList);
}
+void ScummEngine_v0::walkToActorOrObject(int object) {
+ int x, y, dir;
+ Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "walkToObject");
+
+ _walkToObject = object;
+ _walkToObjectState = kWalkToObjectStateWalk;
+
+ if (OBJECT_V0_TYPE(object) == kObjectV0TypeActor) {
+ walkActorToActor(VAR(VAR_EGO), OBJECT_V0_ID(object), 4);
+ x = a->getRealPos().x;
+ y = a->getRealPos().y;
+ } else {
+ walkActorToObject(VAR(VAR_EGO), object);
+ getObjectXYPos(object, x, y, dir);
+ }
+
+ VAR(6) = x;
+ VAR(7) = y;
+
+ // actor must not move if frozen
+ if (a->_miscflags & kActorMiscFlagFreeze)
+ a->stopActorMoving();
+}
+
+bool ScummEngine_v0::checkPendingWalkAction() {
+ // before a sentence script is executed, it might be necessary to walk to
+ // and pickup objects before. Check if such an action is pending and handle
+ // it if available.
+ if (_walkToObjectState == kWalkToObjectStateDone)
+ return false;
+
+ int actor = VAR(VAR_EGO);
+ Actor_v0 *a = (Actor_v0 *)derefActor(actor, "checkPendingWalkAction");
+
+ // wait until walking or turning action is finished
+ if (a->_moving)
+ return true;
+
+ // after walking and turning finally execute the script
+ if (_walkToObjectState == kWalkToObjectStateTurn) {
+ runSentenceScript();
+ // change actor facing
+ } else {
+ int x, y, distX, distY;
+ if (objIsActor(_walkToObject)) {
+ Actor *b = derefActor(objToActor(_walkToObject), "checkPendingWalkAction(2)");
+ x = b->getRealPos().x;
+ y = b->getRealPos().y;
+ if (x < a->getRealPos().x)
+ x += 4;
+ else
+ x -= 4;
+ } else {
+ getObjectXYPos(_walkToObject, x, y);
+ }
+ AdjustBoxResult abr = a->adjustXYToBeInBox(x, y);
+ distX = ABS(a->getRealPos().x - abr.x);
+ distY = ABS(a->getRealPos().y - abr.y);
+
+ if (distX <= 4 && distY <= 8) {
+ if (objIsActor(_walkToObject)) { // walk to actor finished
+ // make actors turn to each other
+ a->faceToObject(_walkToObject);
+ int otherActor = objToActor(_walkToObject);
+ // ignore the plant
+ if (otherActor != 19) {
+ Actor *b = derefActor(otherActor, "checkPendingWalkAction(3)");
+ b->faceToObject(actorToObj(actor));
+ }
+ } else { // walk to object finished
+ int tmpX, tmpY, dir;
+ getObjectXYPos(_walkToObject, tmpX, tmpY, dir);
+ a->turnToDirection(dir);
+ }
+ _walkToObjectState = kWalkToObjectStateTurn;
+ return true;
+ }
+ }
+
+ _walkToObjectState = kWalkToObjectStateDone;
+ return false;
+}
+
+void ScummEngine_v0::checkAndRunSentenceScript() {
+ if (checkPendingWalkAction())
+ return;
+
+ if (!_sentenceNum || _sentence[_sentenceNum - 1].freezeCount)
+ return;
+
+ SentenceTab &st = _sentence[_sentenceNum - 1];
+
+ if (st.preposition && st.objectB == st.objectA) {
+ _sentenceNum--;
+ return;
+ }
+
+ _currentScript = 0xFF;
+
+ assert(st.objectA);
+
+ // If two objects are involved, at least one must be in the actors inventory
+ if (st.objectB &&
+ (OBJECT_V0_TYPE(st.objectA) != kObjectV0TypeFG || _objectOwnerTable[st.objectA] != VAR(VAR_EGO)) &&
+ (OBJECT_V0_TYPE(st.objectB) != kObjectV0TypeFG || _objectOwnerTable[st.objectB] != VAR(VAR_EGO)))
+ {
+ if (getVerbEntrypoint(st.objectA, kVerbPickUp))
+ doSentence(kVerbPickUp, st.objectA, 0);
+ else if (getVerbEntrypoint(st.objectB, kVerbPickUp))
+ doSentence(kVerbPickUp, st.objectB, 0);
+ else
+ _sentenceNum--;
+ return;
+ }
+
+ _cmdVerb = st.verb;
+ _cmdObject = st.objectA;
+ _cmdObject2 = st.objectB;
+ _sentenceNum--;
+
+ // abort sentence execution if the number of nested scripts is too high.
+ // This might happen for instance if the sentence command depends on an
+ // object that the actor has to pick-up in a nested doSentence() call.
+ // If the actor is not able to pick-up the object (e.g. because it is not
+ // reachable or pickupable) a nested pick-up command is triggered again
+ // and again, so the actual sentence command will never be executed.
+ // In this case the sentence command has to be aborted.
+ _sentenceNestedCount++;
+ if (_sentenceNestedCount > 6) {
+ _sentenceNestedCount = 0;
+ _sentenceNum = 0;
+ return;
+ }
+
+ if (whereIsObject(st.objectA) != WIO_INVENTORY) {
+ if (_currentMode != kModeKeypad) {
+ walkToActorOrObject(st.objectA);
+ return;
+ }
+ } else if (st.objectB && whereIsObject(st.objectB) != WIO_INVENTORY) {
+ walkToActorOrObject(st.objectB);
+ return;
+ }
+
+ runSentenceScript();
+ if (_currentMode == kModeKeypad) {
+ _walkToObjectState = kWalkToObjectStateDone;
+ }
+}
+
+void ScummEngine_v0::runSentenceScript() {
+ _redrawSentenceLine = true;
+
+ if (getVerbEntrypoint(_cmdObject, _cmdVerb) != 0) {
+ // do not read in the dark
+ if (!(_cmdVerb == kVerbRead && _currentLights == 0)) {
+ VAR(VAR_ACTIVE_OBJECT2) = OBJECT_V0_ID(_cmdObject2);
+ runObjectScript(_cmdObject, _cmdVerb, false, false, NULL);
+ return;
+ }
+ } else {
+ if (_cmdVerb == kVerbGive) {
+ // no "give to"-script: give to other kid or ignore
+ int actor = OBJECT_V0_ID(_cmdObject2);
+ if (actor < 8)
+ setOwnerOf(_cmdObject, actor);
+ return;
+ }
+ }
+
+ if (_cmdVerb != kVerbWalkTo) {
+ // perform verb's fallback action
+ VAR(VAR_ACTIVE_VERB) = _cmdVerb;
+ runScript(3, 0, 0, 0);
+ }
+}
+
void ScummEngine_v2::runInputScript(int clickArea, int val, int mode) {
int args[24];
int verbScript;
diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp
index e2d3f40e8e..45901186cd 100644
--- a/engines/scumm/script_v0.cpp
+++ b/engines/scumm/script_v0.cpp
@@ -27,6 +27,7 @@
#include "scumm/resource.h"
#include "scumm/scumm_v0.h"
#include "scumm/verbs.h"
+#include "scumm/util.h"
namespace Scumm {
@@ -50,7 +51,7 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0x0b, o_setActorBitVar);
/* 0C */
OPCODE(0x0c, o_loadSound);
- OPCODE(0x0d, o_printEgo_c64);
+ OPCODE(0x0d, o_printEgo);
OPCODE(0x0e, o_putActorAtObject);
OPCODE(0x0f, o2_clearState02);
/* 10 */
@@ -59,7 +60,7 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0x12, o2_panCameraTo);
OPCODE(0x13, o_lockCostume);
/* 14 */
- OPCODE(0x14, o_print_c64);
+ OPCODE(0x14, o_print);
OPCODE(0x15, o5_walkActorToActor);
OPCODE(0x16, o5_getRandomNr);
OPCODE(0x17, o2_clearState08);
@@ -79,9 +80,9 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0x22, o4_saveLoadGame);
OPCODE(0x23, o_stopCurrentScript);
/* 24 */
- OPCODE(0x24, o_unknown2);
+ OPCODE(0x24, o_ifNotEqualActiveObject2);
OPCODE(0x25, o5_loadRoom);
- OPCODE(0x26, o_getClosestObjActor);
+ OPCODE(0x26, o_getClosestActor);
OPCODE(0x27, o2_getActorY);
/* 28 */
OPCODE(0x28, o5_equalZero);
@@ -91,7 +92,7 @@ void ScummEngine_v0::setupOpcodes() {
/* 2C */
OPCODE(0x2c, o_stopCurrentScript);
OPCODE(0x2d, o2_putActorInRoom);
- OPCODE(0x2e, o_print_c64);
+ OPCODE(0x2e, o_print);
OPCODE(0x2f, o2_ifState08);
/* 30 */
OPCODE(0x30, o_loadCostume);
@@ -101,7 +102,7 @@ void ScummEngine_v0::setupOpcodes() {
/* 34 */
OPCODE(0x34, o5_getDist);
OPCODE(0x35, o_stopCurrentScript);
- OPCODE(0x36, o2_walkActorToObject);
+ OPCODE(0x36, o_walkActorToObject);
OPCODE(0x37, o2_clearState04);
/* 38 */
OPCODE(0x38, o2_isLessEqual);
@@ -144,7 +145,7 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0x56, o_getActorMoving);
OPCODE(0x57, o2_clearState08);
/* 58 */
- OPCODE(0x58, o_beginOverride);
+ OPCODE(0x58, o2_beginOverride);
OPCODE(0x59, o_stopCurrentScript);
OPCODE(0x5a, o2_add);
OPCODE(0x5b, o_getActorBitVar);
@@ -154,14 +155,14 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0x5e, o2_walkActorTo);
OPCODE(0x5f, o2_ifState04);
/* 60 */
- OPCODE(0x60, o_cursorCommand);
+ OPCODE(0x60, o_setMode);
OPCODE(0x61, o2_putActor);
OPCODE(0x62, o2_stopScript);
OPCODE(0x63, o_stopCurrentScript);
/* 64 */
- OPCODE(0x64, o_ifActiveObject);
+ OPCODE(0x64, o_ifEqualActiveObject2);
OPCODE(0x65, o_stopCurrentScript);
- OPCODE(0x66, o_getClosestObjActor);
+ OPCODE(0x66, o_getClosestActor);
OPCODE(0x67, o5_getActorFacing);
/* 68 */
OPCODE(0x68, o5_isScriptRunning);
@@ -177,11 +178,11 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0x70, o_lights);
OPCODE(0x71, o_getBitVar);
OPCODE(0x72, o_nop);
- OPCODE(0x73, o5_getObjectOwner);
+ OPCODE(0x73, o_getObjectOwner);
/* 74 */
OPCODE(0x74, o5_getDist);
- OPCODE(0x75, o_printEgo_c64);
- OPCODE(0x76, o2_walkActorToObject);
+ OPCODE(0x75, o_printEgo);
+ OPCODE(0x76, o_walkActorToObject);
OPCODE(0x77, o2_clearState04);
/* 78 */
OPCODE(0x78, o2_isGreater);
@@ -239,7 +240,7 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0xa2, o4_saveLoadGame);
OPCODE(0xa3, o_stopCurrentScript);
/* A4 */
- OPCODE(0xa4, o_unknown2);
+ OPCODE(0xa4, o_ifNotEqualActiveObject2);
OPCODE(0xa5, o5_loadRoom);
OPCODE(0xa6, o_stopCurrentScript);
OPCODE(0xa7, o2_getActorY);
@@ -251,7 +252,7 @@ void ScummEngine_v0::setupOpcodes() {
/* AC */
OPCODE(0xac, o_stopCurrentScript);
OPCODE(0xad, o2_putActorInRoom);
- OPCODE(0xae, o_print_c64);
+ OPCODE(0xae, o_print);
OPCODE(0xaf, o2_ifNotState08);
/* B0 */
OPCODE(0xb0, o_loadCostume);
@@ -261,7 +262,7 @@ void ScummEngine_v0::setupOpcodes() {
/* B4 */
OPCODE(0xb4, o5_getDist);
OPCODE(0xb5, o_stopCurrentScript);
- OPCODE(0xb6, o2_walkActorToObject);
+ OPCODE(0xb6, o_walkActorToObject);
OPCODE(0xb7, o2_setState04);
/* B8 */
OPCODE(0xb8, o2_isLessEqual);
@@ -314,12 +315,12 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0xde, o2_walkActorTo);
OPCODE(0xdf, o2_ifNotState04);
/* E0 */
- OPCODE(0xe0, o_cursorCommand);
+ OPCODE(0xe0, o_setMode);
OPCODE(0xe1, o2_putActor);
OPCODE(0xe2, o2_stopScript);
OPCODE(0xe3, o_stopCurrentScript);
/* E4 */
- OPCODE(0xe4, o_ifActiveObject);
+ OPCODE(0xe4, o_ifEqualActiveObject2);
OPCODE(0xe5, o_loadRoomWithEgo);
OPCODE(0xe6, o_stopCurrentScript);
OPCODE(0xe7, o5_getActorFacing);
@@ -337,11 +338,11 @@ void ScummEngine_v0::setupOpcodes() {
OPCODE(0xf0, o_lights);
OPCODE(0xf1, o_getBitVar);
OPCODE(0xf2, o_nop);
- OPCODE(0xf3, o5_getObjectOwner);
+ OPCODE(0xf3, o_getObjectOwner);
/* F4 */
OPCODE(0xf4, o5_getDist);
OPCODE(0xf5, o_stopCurrentScript);
- OPCODE(0xf6, o2_walkActorToObject);
+ OPCODE(0xf6, o_walkActorToObject);
OPCODE(0xf7, o2_setState04);
/* F8 */
OPCODE(0xf8, o2_isGreater);
@@ -365,7 +366,7 @@ uint ScummEngine_v0::fetchScriptWord() {
int ScummEngine_v0::getActiveObject() {
if (_opcode & PARAM_2)
- return _activeObject;
+ return OBJECT_V0_ID(_cmdObject);
return fetchScriptByte();
}
@@ -406,172 +407,121 @@ void ScummEngine_v0::decodeParseString() {
actorTalk(buffer);
}
-void ScummEngine_v0::drawSentenceWord(int object, bool usePrep, bool objInInventory) {
- const byte *temp;
- int sentencePrep = 0;
-
- // If object not in inventory, we except an index
- if (!objInInventory)
- _v0ObjectIndex = true;
- else
- _v0ObjectInInventory = true;
-
- temp = getObjOrActorName(object);
+void ScummEngine_v0::clearSentenceLine() {
+ Common::Rect sentenceline;
+ sentenceline.top = _virtscr[kVerbVirtScreen].topline;
+ sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 8;
+ sentenceline.left = 0;
+ sentenceline.right = _virtscr[kVerbVirtScreen].w - 1;
+ restoreBackground(sentenceline);
+}
- _v0ObjectInInventory = false;
- _v0ObjectIndex = false;
+void ScummEngine_v0::flushSentenceLine() {
+ byte string[80];
+ const char *ptr = _sentenceBuf.c_str();
+ int i = 0, len = 0;
- // Append the 'object-name'
- if (temp) {
- _sentenceBuf += " ";
- _sentenceBuf += (const char *)temp;
- }
+ // Maximum length of printable characters
+ int maxChars = 40;
+ while (*ptr) {
+ if (*ptr != '@')
+ len++;
+ if (len > maxChars) {
+ break;
+ }
- // Append the modifier? (With / On / To / In)
- if (!usePrep)
- return;
+ string[i++] = *ptr++;
- if (_verbs[_activeVerb].prep == 0xFF) {
- _v0ObjectInInventory = objInInventory;
- sentencePrep = verbPrep(object);
- } else {
- sentencePrep = _verbs[_activeVerb].prep;
}
+ string[i] = 0;
- if (sentencePrep > 0 && sentencePrep <= 4) {
- // The prepositions, like the fonts, were hard code in the engine. Thus
- // we have to do that, too, and provde localized versions for all the
- // languages MM/Zak are available in.
- static const char *const prepositions[][5] = {
- { " ", " in", " with", " on", " to" }, // English
- { " ", " mit", " mit", " mit", " zu" }, // German
- { " ", " dans", " avec", " sur", " <" }, // French
- { " ", " in", " con", " su", " a" }, // Italian
- { " ", " en", " con", " en", " a" }, // Spanish
- };
- int lang;
- switch (_language) {
- case Common::DE_DEU:
- lang = 1;
- break;
- case Common::FR_FRA:
- lang = 2;
- break;
- case Common::IT_ITA:
- lang = 3;
- break;
- case Common::ES_ESP:
- lang = 4;
- break;
- default:
- lang = 0; // Default to english
- }
+ _string[2].charset = 1;
+ _string[2].ypos = _virtscr[kVerbVirtScreen].topline;
+ _string[2].xpos = 0;
+ _string[2].right = _virtscr[kVerbVirtScreen].w - 1;
+ _string[2].color = 16;
+ drawString(2, (byte *)string);
+}
- _sentenceBuf += prepositions[lang][sentencePrep];
+void ScummEngine_v0::drawSentenceObject(int object) {
+ const byte *temp;
+ temp = getObjOrActorName(object);
+ if (temp) {
+ _sentenceBuf += " ";
+ _sentenceBuf += (const char *)temp;
}
}
-void ScummEngine_v0::drawSentence() {
- Common::Rect sentenceline;
- bool inventoryFirst = false;
- if (!(_userState & 32))
- return;
+void ScummEngine_v0::drawSentenceLine() {
+ _redrawSentenceLine = false;
- // Current Verb, Walk/Use
- if (getResourceAddress(rtVerb, _activeVerb)) {
- _sentenceBuf = (char *)getResourceAddress(rtVerb, _activeVerb);
- } else {
+ if (!(_userState & USERSTATE_IFACE_SENTENCE))
return;
- }
-
- // If using inventory first, draw it first
- if (_activeInvExecute && _activeInventory) {
- drawSentenceWord(_activeInventory, true, true);
- } else {
- // Not using inventory, use selected object
- if (_activeObject)
- drawSentenceWord(_activeObjectIndex, true, false);
- else
- inventoryFirst = true;
- }
-
- // Draw the inventory?
- if (_activeInventory > 0 && _activeObject2 == 0) {
- // Only if inventory isnt first (it will already be drawn by now)
- if (!_activeInvExecute) {
- drawSentenceWord(_activeInventory, inventoryFirst, true);
- } else {
- // Draw the active object, which could be inventory based, or room based
- if (_activeObject && !_activeObjectIndex) {
- drawSentenceWord(_activeObject, inventoryFirst, true);
- } else // Room based
- drawSentenceWord(_activeObjectIndex, inventoryFirst, false);
- }
-
- // Draw the 2nd active object
- } else if (_activeObject2) {
-
- // 2nd Object is in inventory
- if (_activeObject2Inv) {
- _v0ObjectInInventory = true;
- drawSentenceWord(_activeObject2, inventoryFirst, true);
- } else {
- drawSentenceWord(_activeObject2Index, inventoryFirst, false);
+ clearSentenceLine();
+
+ if (_activeVerb == kVerbNewKid) {
+ _sentenceBuf = "";
+ for (int i = 0; i < 3; ++i) {
+ const char *actorName;
+ int actorId = VAR(97 + i);
+ if (actorId == 0) {
+ // after usage of the radiation suit, kid vars are set to 0
+ actorName = " ";
+ } else {
+ Actor *a = derefActor(actorId, "drawSentenceLine");
+ actorName = (char *)a->getActorName();
+ }
+ _sentenceBuf += Common::String::format("%-13s", actorName);
}
+ flushSentenceLine();
+ return;
}
- // Draw the active actor
- if (_activeActor) {
- Actor *a = derefActor(_activeActor, "");
+ // Current Verb
+ if (_activeVerb == kVerbNone)
+ _activeVerb = kVerbWalkTo;
- _sentenceBuf += " ";
- _sentenceBuf += (const char *)a->getActorName();
- }
+ char *verbName = (char *)getResourceAddress(rtVerb, _activeVerb);
+ assert(verbName);
+ _sentenceBuf = verbName;
- _string[2].charset = 1;
- _string[2].ypos = _virtscr[kVerbVirtScreen].topline;
- _string[2].xpos = 0;
- _string[2].right = _virtscr[kVerbVirtScreen].w - 1;
- _string[2].color = 16;
+ if (_activeObject) {
+ // Draw the 1st active object
+ drawSentenceObject(_activeObject);
- byte string[80];
- const char *ptr = _sentenceBuf.c_str();
- int i = 0, len = 0;
+ // Append verb preposition
+ int sentencePrep = activeVerbPrep();
+ if (sentencePrep) {
+ drawPreposition(sentencePrep);
- // Maximum length of printable characters
- int maxChars = 40;
- while (*ptr) {
- if (*ptr != '@')
- len++;
- if (len > maxChars) {
- break;
+ // Draw the 2nd active object
+ if (_activeObject2)
+ drawSentenceObject(_activeObject2);
}
-
- string[i++] = *ptr++;
-
}
- string[i] = 0;
- sentenceline.top = _virtscr[kVerbVirtScreen].topline;
- sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 8;
- sentenceline.left = 0;
- sentenceline.right = _virtscr[kVerbVirtScreen].w - 1;
- restoreBackground(sentenceline);
-
- drawString(2, (byte *)string);
+ flushSentenceLine();
}
void ScummEngine_v0::o_stopCurrentScript() {
- int script;
+ stopScriptCommon(0);
+}
- script = vm.slot[_currentScript].number;
+void ScummEngine_v0::o_walkActorToObject() {
+ int actor = getVarOrDirectByte(PARAM_1);
+ int objId = fetchScriptByte();
+ int obj;
- if (_currentScript != 0 && vm.slot[_currentScript].number == script)
- stopObjectCode();
+ if (_opcode & 0x40)
+ obj = OBJECT_V0(objId, kObjectV0TypeBG);
else
- stopScript(script);
+ obj = OBJECT_V0(objId, kObjectV0TypeFG);
+
+ if (whereIsObject(obj) != WIO_NOT_FOUND) {
+ walkActorToObject(actor, obj);
+ }
}
void ScummEngine_v0::o_loadSound() {
@@ -625,18 +575,16 @@ void ScummEngine_v0::o_loadRoom() {
}
void ScummEngine_v0::o_loadRoomWithEgo() {
- Actor *a;
+ Actor_v0 *a;
int obj, room, x, y, dir;
obj = fetchScriptByte();
room = fetchScriptByte();
- a = derefActor(VAR(VAR_EGO), "o_loadRoomWithEgo");
+ a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "o_loadRoomWithEgo");
//0x634F
- if (((ActorC64 *)a)->_miscflags & 0x40) {
- // TODO: Check if this is the correct function
- // to be calling here
+ if (a->_miscflags & kActorMiscFlagFreeze) {
stopObjectCode();
return;
}
@@ -654,15 +602,14 @@ void ScummEngine_v0::o_loadRoomWithEgo() {
x = r.x;
y = r.y;
a->putActor(x, y, _currentRoom);
- a->setDirection(dir + 180);
-
+
camera._dest.x = camera._cur.x = a->getPos().x;
setCameraAt(a->getPos().x, a->getPos().y);
setCameraFollows(a);
_fullRedraw = true;
- resetSentence(false);
+ resetSentence();
if (x >= 0 && y >= 0) {
a->startWalkActor(x, y, -1);
@@ -679,27 +626,39 @@ void ScummEngine_v0::o_unlockRoom() {
_res->unlock(rtRoom, resid);
}
-void ScummEngine_v0::o_cursorCommand() {
- // TODO
- int state = 0;
+void ScummEngine_v0::setMode(byte mode) {
+ int state;
+
+ _currentMode = mode;
- _currentMode = fetchScriptByte();
switch (_currentMode) {
- case 0:
- state = 15;
+ case kModeCutscene:
+ _redrawSentenceLine = false;
+ // Note: do not change freeze state here
+ state = USERSTATE_SET_IFACE |
+ USERSTATE_SET_CURSOR;
break;
- case 1:
- state = 31;
+ case kModeKeypad:
+ _redrawSentenceLine = false;
+ state = USERSTATE_SET_IFACE |
+ USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON |
+ USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON;
break;
- case 2:
- break;
- case 3:
- state = 247;
+ case kModeNormal:
+ case kModeNoNewKid:
+ state = USERSTATE_SET_IFACE | USERSTATE_IFACE_ALL |
+ USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON |
+ USERSTATE_SET_FREEZE;
break;
+ default:
+ error("Invalid mode: %d", mode);
}
setUserState(state);
- debug(0, "o_cursorCommand(%d)", _currentMode);
+}
+
+void ScummEngine_v0::o_setMode() {
+ setMode(fetchScriptByte());
}
void ScummEngine_v0::o_lights() {
@@ -724,24 +683,31 @@ void ScummEngine_v0::o_lights() {
void ScummEngine_v0::o_animateActor() {
int act = getVarOrDirectByte(PARAM_1);
int anim = getVarOrDirectByte(PARAM_2);
- int unk = fetchScriptByte();
-
- debug(0,"o_animateActor: unk %d", unk);
-
- ActorC64 *a = (ActorC64*) derefActor(act, "o_animateActor");
-
- // 0x6993
- if (anim == 0xFE) {
- a->_speaking = 0x80; // Enabled, but not switching
- return;
- }
- // 0x69A3
- if (anim == 0xFD) {
- a->_speaking = 0x00;
- return;
+ int8 repeat = (int8) fetchScriptByte();
+
+ Actor_v0 *a = (Actor_v0*) derefActor(act, "o_animateActor");
+
+ a->_animFrameRepeat = repeat;
+
+ switch (anim) {
+
+ case 0xFE:
+ // 0x6993
+ a->_speaking = 0x80; // Enabled, but not switching
+ return;
+
+ case 0xFD:
+ // 0x69A3
+ a->_speaking = 0x00;
+ return;
+
+ case 0xFF:
+ a->stopActorMoving();
+ return;
}
a->animateActor(anim);
+ a->animateCostume();
}
void ScummEngine_v0::o_getActorMoving() {
@@ -760,7 +726,12 @@ void ScummEngine_v0::o_putActorAtObject() {
a = derefActor(getVarOrDirectByte(PARAM_1), "o_putActorAtObject");
- obj = fetchScriptByte();
+ int objId = fetchScriptByte();
+ if (_opcode & 0x40)
+ obj = OBJECT_V0(objId, kObjectV0TypeBG);
+ else
+ obj = OBJECT_V0(objId, kObjectV0TypeFG);
+
if (whereIsObject(obj) != WIO_NOT_FOUND) {
getObjectXYPos(obj, x, y);
AdjustBoxResult r = a->adjustXYToBeInBox(x, y);
@@ -776,20 +747,13 @@ void ScummEngine_v0::o_putActorAtObject() {
void ScummEngine_v0::o_pickupObject() {
int obj = fetchScriptByte();
- if (obj == 0) {
- obj = _activeObject;
- }
+ if (!obj)
+ obj = _cmdObject;
- if (obj < 1) {
- error("pickupObject received invalid index %d (script %d)", obj, vm.slot[_currentScript].number);
- }
-
- if (getObjectIndex(obj) == -1)
+ /* Don't take an object twice */
+ if (whereIsObject(obj) == WIO_INVENTORY)
return;
- if (whereIsObjectInventory(_activeObject2) == WIO_INVENTORY) /* Don't take an */
- return; /* object twice */
-
addObjectToInventory(obj, _roomResource);
markObjectRectAsDirty(obj);
putOwner(obj, VAR(VAR_EGO));
@@ -800,14 +764,22 @@ void ScummEngine_v0::o_pickupObject() {
}
void ScummEngine_v0::o_setObjectName() {
- int obj = fetchScriptByte();
+ int obj;
+ int objId = fetchScriptByte();
+ if (!objId) {
+ obj = _cmdObject;
+ } else {
+ if (_opcode & 0x80)
+ obj = OBJECT_V0(objId, kObjectV0TypeBG);
+ else
+ obj = OBJECT_V0(objId, kObjectV0TypeFG);
+ }
setObjectName(obj);
}
void ScummEngine_v0::o_nop() {
}
-// TODO: Maybe translate actor flags in future.
void ScummEngine_v0::o_setActorBitVar() {
byte act = getVarOrDirectByte(PARAM_1);
byte mask = getVarOrDirectByte(PARAM_2);
@@ -817,7 +789,7 @@ void ScummEngine_v0::o_setActorBitVar() {
if (act >= _numActors)
return;
- ActorC64 *a = (ActorC64 *)derefActor(act, "o_setActorBitVar");
+ Actor_v0 *a = (Actor_v0 *)derefActor(act, "o_setActorBitVar");
if (mod)
a->_miscflags |= mask;
@@ -825,20 +797,24 @@ void ScummEngine_v0::o_setActorBitVar() {
a->_miscflags &= ~mask;
// This flag causes the actor to stop moving (used by script #158, Green Tentacle 'Oomph!')
- if (a->_miscflags & 0x40)
+ if (a->_miscflags & kActorMiscFlagFreeze)
a->stopActorMoving();
- if (a->_miscflags & 0x80)
- a->setActorCostume(0);
debug(0, "o_setActorBitVar(%d, %d, %d)", act, mask, mod);
}
+void ScummEngine_v0::o_getObjectOwner() {
+ getResultPos();
+ int obj = getVarOrDirectWord(PARAM_1);
+ setResult(getOwner(obj ? obj : _cmdObject));
+}
+
void ScummEngine_v0::o_getActorBitVar() {
getResultPos();
byte act = getVarOrDirectByte(PARAM_1);
byte mask = getVarOrDirectByte(PARAM_2);
- ActorC64 *a = (ActorC64 *)derefActor(act, "o_getActorBitVar");
+ Actor_v0 *a = (Actor_v0 *)derefActor(act, "o_getActorBitVar");
setResult((a->_miscflags & mask) ? 1 : 0);
debug(0, "o_getActorBitVar(%d, %d, %d)", act, mask, (a->_miscflags & mask));
@@ -867,74 +843,97 @@ void ScummEngine_v0::o_getBitVar() {
debug(0, "o_getBitVar (%d, %d %d)", flag, mask, _bitVars[flag] & (1 << mask));
}
-void ScummEngine_v0::o_print_c64() {
+void ScummEngine_v0::o_print() {
_actorToPrintStrFor = fetchScriptByte();
decodeParseString();
}
-void ScummEngine_v0::o_printEgo_c64() {
+void ScummEngine_v0::o_printEgo() {
_actorToPrintStrFor = (byte)VAR(VAR_EGO);
decodeParseString();
}
void ScummEngine_v0::o_doSentence() {
- byte entry = fetchScriptByte();
- byte obj = fetchScriptByte();
- fetchScriptByte();
+ byte verb = fetchScriptByte();
+ int obj, obj2;
+ byte b;
+
+ b = fetchScriptByte();
+ if (b == 0xFF) {
+ obj = _cmdObject2;
+ } else if (b == 0xFE) {
+ obj = _cmdObject;
+ } else {
+ obj = OBJECT_V0(b, (_opcode & 0x80) ? kObjectV0TypeBG : kObjectV0TypeFG);
+ }
- runObjectScript(obj, entry, false, false, NULL);
-}
+ b = fetchScriptByte();
+ if (b == 0xFF) {
+ obj2 = _cmdObject2;
+ } else if (b == 0xFE) {
+ obj2 = _cmdObject;
+ } else {
+ obj2 = OBJECT_V0(b, (_opcode & 0x40) ? kObjectV0TypeBG : kObjectV0TypeFG);
+ }
-void ScummEngine_v0::o_unknown2() {
- byte var1 = fetchScriptByte();
- error("STUB: o_unknown2(%d)", var1);
+ doSentence(verb, obj, obj2);
}
-void ScummEngine_v0::o_ifActiveObject() {
+bool ScummEngine_v0::ifEqualActiveObject2Common(bool checkType) {
byte obj = fetchScriptByte();
+ if (!checkType || (OBJECT_V0_TYPE(_cmdObject2) == kObjectV0TypeFG))
+ return (obj == OBJECT_V0_ID(_cmdObject2));
+ return false;
+}
- jumpRelative(obj == _activeInventory);
+void ScummEngine_v0::o_ifEqualActiveObject2() {
+ bool equal = ifEqualActiveObject2Common((_opcode & 0x80) == 0);
+ jumpRelative(equal);
}
-void ScummEngine_v0::o_getClosestObjActor() {
- int obj;
- int act;
+void ScummEngine_v0::o_ifNotEqualActiveObject2() {
+ bool equal = ifEqualActiveObject2Common((_opcode & 0x80) == 0);
+ jumpRelative(!equal);
+}
+
+void ScummEngine_v0::o_getClosestActor() {
+ int act, check_act;
int dist;
// This code can't detect any actors farther away than 255 units
// (pixels in newer games, characters in older ones.) But this is
// perfectly OK, as it is exactly how the original behaved.
- int closest_obj = 0xFF, closest_dist = 0xFF;
+ int closest_act = 0xFF, closest_dist = 0xFF;
getResultPos();
act = getVarOrDirectByte(PARAM_1);
- obj = (_opcode & PARAM_2) ? 25 : 7;
+ check_act = (_opcode & PARAM_2) ? 25 : 7;
do {
- dist = getObjActToObjActDist(act, obj);
+ dist = getObjActToObjActDist(actorToObj(act), actorToObj(check_act));
if (dist < closest_dist) {
closest_dist = dist;
- closest_obj = obj;
+ closest_act = check_act;
}
- } while (--obj);
+ } while (--check_act);
- setResult(closest_obj);
+ setResult(closest_act);
}
void ScummEngine_v0::o_cutscene() {
- vm.cutSceneData[0] = _userState | (_userPut ? 16 : 0);
+ vm.cutSceneData[0] = _currentMode;
vm.cutSceneData[2] = _currentRoom;
- vm.cutSceneData[3] = camera._mode;
- // Hide inventory, freeze scripts, hide cursor
- setUserState(15);
+ freezeScripts(0);
+ setMode(kModeCutscene);
_sentenceNum = 0;
- resetSentence(false);
+ resetSentence();
vm.cutScenePtr[0] = 0;
+ vm.cutSceneScript[0] = 0;
}
void ScummEngine_v0::o_endCutscene() {
@@ -944,68 +943,43 @@ void ScummEngine_v0::o_endCutscene() {
vm.cutSceneScript[0] = 0;
vm.cutScenePtr[0] = 0;
- // Reset user state to values before cutscene
- setUserState(vm.cutSceneData[0] | 7);
+ setMode(vm.cutSceneData[0]);
- camera._mode = (byte) vm.cutSceneData[3];
- if (camera._mode == kFollowActorCameraMode) {
- actorFollowCamera(VAR(VAR_EGO));
- } else if (vm.cutSceneData[2] != _currentRoom) {
+ if (_currentMode == kModeKeypad) {
startScene(vm.cutSceneData[2], 0, 0);
+ // in contrast to the normal keypad behavior we unfreeze scripts here
+ unfreezeScripts();
+ } else {
+ unfreezeScripts();
+ actorFollowCamera(VAR(VAR_EGO));
+ // set mode again to have the freeze mode right
+ setMode(vm.cutSceneData[0]);
+ _redrawSentenceLine = true;
}
}
-void ScummEngine_v0::o_beginOverride() {
- const int idx = vm.cutSceneStackPointer;
- assert(0 <= idx && idx < 5);
-
- vm.cutScenePtr[idx] = _scriptPointer - _scriptOrgPointer;
- vm.cutSceneScript[idx] = _currentScript;
-
- // Skip the jump instruction following the override instruction
- // (the jump is responsible for "skipping" cutscenes, and the reason
- // why we record the current script position in vm.cutScenePtr).
- fetchScriptByte();
- ScummEngine::fetchScriptWord();
-
- // This is based on disassembly
- VAR(VAR_OVERRIDE) = 0;
-}
-
void ScummEngine_v0::o_setOwnerOf() {
int obj, owner;
obj = getVarOrDirectWord(PARAM_1);
owner = getVarOrDirectByte(PARAM_2);
- if (obj == 0)
- obj = _activeInventory;
+ if (!obj)
+ obj = _cmdObject;
setOwnerOf(obj, owner);
}
-void ScummEngine_v0::resetSentence(bool walking) {
- _activeVerb = 13;
-
- // If the actor is walking, or the screen is a keypad (no sentence verbs/objects are drawn)
- // Then reset all active objects (stops the radio crash, bug #3077966)
- if (!walking || !(_userState & 32)) {
- _v0ObjectFlag = 0;
- _activeInventory = 0;
- _activeObject = 0;
- _activeObject2 = 0;
- _activeObjectIndex = 0;
- _activeObject2Index = 0;
- }
+void ScummEngine_v0::resetSentence() {
+ _activeVerb = kVerbWalkTo;
+ _activeObject = 0;
+ _activeObject2 = 0;
- _verbExecuting = false;
- _verbPickup = false;
+ _walkToObjectState = kWalkToObjectStateDone;
+ _redrawSentenceLine = true;
- _activeActor = 0;
- _activeInvExecute = false;
- _activeObject2Inv = false;
- _activeObjectObtained = false;
- _activeObject2Obtained = false;
+ _sentenceNum = 0;
+ _sentenceNestedCount = 0;
}
} // End of namespace Scumm
diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp
index edb046d571..9c8742cffd 100644
--- a/engines/scumm/script_v2.cpp
+++ b/engines/scumm/script_v2.cpp
@@ -401,7 +401,7 @@ void ScummEngine_v2::decodeParseString() {
_string[textSlot].overhead = false;
if (_game.id == GID_MANIAC && _actorToPrintStrFor == 0xFF) {
- if (_game.platform == Common::kPlatformC64) {
+ if (_game.version == 0) {
_string[textSlot].color = 14;
} else if (_game.features & GF_DEMO) {
_string[textSlot].color = (_game.version == 2) ? 15 : 1;
@@ -412,7 +412,7 @@ void ScummEngine_v2::decodeParseString() {
}
int ScummEngine_v2::readVar(uint var) {
- if (var >= 14 && var <= 16)
+ if (_game.version >= 1 && var >= 14 && var <= 16)
var = _scummVars[var];
assertRange(0, var, _numVariables - 1, "variable (reading)");
@@ -953,12 +953,48 @@ void ScummEngine_v2::o2_doSentence() {
}
}
+void ScummEngine_v2::drawPreposition(int index) {
+ // The prepositions, like the fonts, were hard code in the engine. Thus
+ // we have to do that, too, and provde localized versions for all the
+ // languages MM/Zak are available in.
+ const char *prepositions[][5] = {
+ { " ", " in", " with", " on", " to" }, // English
+ { " ", " mit", " mit", " mit", " zu" }, // German
+ { " ", " dans", " avec", " sur", " <" }, // French
+ { " ", " in", " con", " su", " a" }, // Italian
+ { " ", " en", " con", " en", " a" }, // Spanish
+ };
+ int lang;
+ switch (_language) {
+ case Common::DE_DEU:
+ lang = 1;
+ break;
+ case Common::FR_FRA:
+ lang = 2;
+ break;
+ case Common::IT_ITA:
+ lang = 3;
+ break;
+ case Common::ES_ESP:
+ lang = 4;
+ break;
+ default:
+ lang = 0; // Default to english
+ }
+
+ if (_game.platform == Common::kPlatformNES) {
+ _sentenceBuf += (const char *)(getResourceAddress(rtCostume, 78) + VAR(VAR_SENTENCE_PREPOSITION) * 8 + 2);
+ } else
+ _sentenceBuf += prepositions[lang][index];
+}
+
void ScummEngine_v2::o2_drawSentence() {
Common::Rect sentenceline;
const byte *temp;
int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0);
- if (!((_userState & 32) || (_game.platform == Common::kPlatformNES && _userState & 0xe0)))
+ if (!((_userState & USERSTATE_IFACE_SENTENCE) ||
+ (_game.platform == Common::kPlatformNES && (_userState & USERSTATE_IFACE_ALL))))
return;
if (getResourceAddress(rtVerb, slot))
@@ -986,38 +1022,7 @@ void ScummEngine_v2::o2_drawSentence() {
}
if (0 < VAR(VAR_SENTENCE_PREPOSITION) && VAR(VAR_SENTENCE_PREPOSITION) <= 4) {
- // The prepositions, like the fonts, were hard code in the engine. Thus
- // we have to do that, too, and provde localized versions for all the
- // languages MM/Zak are available in.
- const char *prepositions[][5] = {
- { " ", " in", " with", " on", " to" }, // English
- { " ", " mit", " mit", " mit", " zu" }, // German
- { " ", " dans", " avec", " sur", " <" }, // French
- { " ", " in", " con", " su", " a" }, // Italian
- { " ", " en", " con", " en", " a" }, // Spanish
- };
- int lang;
- switch (_language) {
- case Common::DE_DEU:
- lang = 1;
- break;
- case Common::FR_FRA:
- lang = 2;
- break;
- case Common::IT_ITA:
- lang = 3;
- break;
- case Common::ES_ESP:
- lang = 4;
- break;
- default:
- lang = 0; // Default to english
- }
-
- if (_game.platform == Common::kPlatformNES) {
- _sentenceBuf += (const char *)(getResourceAddress(rtCostume, 78) + VAR(VAR_SENTENCE_PREPOSITION) * 8 + 2);
- } else
- _sentenceBuf += prepositions[lang][VAR(VAR_SENTENCE_PREPOSITION)];
+ drawPreposition(VAR(VAR_SENTENCE_PREPOSITION));
}
if (VAR(VAR_SENTENCE_OBJECT2) > 0) {
@@ -1186,11 +1191,7 @@ void ScummEngine_v2::o2_startScript() {
runScript(script, 0, 0, 0);
}
-void ScummEngine_v2::o2_stopScript() {
- int script;
-
- script = getVarOrDirectByte(PARAM_1);
-
+void ScummEngine_v2::stopScriptCommon(int script) {
if (_game.id == GID_MANIAC && _roomResource == 26 && vm.slot[_currentScript].number == 10001) {
// FIXME: Nasty hack for bug #915575
// Don't let the exit script for room 26 stop the script (116), when
@@ -1211,26 +1212,31 @@ void ScummEngine_v2::o2_stopScript() {
stopScript(script);
}
+void ScummEngine_v2::o2_stopScript() {
+ stopScriptCommon(getVarOrDirectByte(PARAM_1));
+}
+
void ScummEngine_v2::o2_panCameraTo() {
panCameraTo(getVarOrDirectByte(PARAM_1) * V12_X_MULTIPLIER, 0);
}
-void ScummEngine_v2::o2_walkActorToObject() {
- int obj;
- Actor *a;
+void ScummEngine_v2::walkActorToObject(int actor, int obj) {
+ int x, y, dir;
+ getObjectXYPos(obj, x, y, dir);
- _v0ObjectFlag = 0;
+ Actor *a = derefActor(actor, "walkActorToObject");
+ AdjustBoxResult r = a->adjustXYToBeInBox(x, y);
+ x = r.x;
+ y = r.y;
- a = derefActor(getVarOrDirectByte(PARAM_1), "o2_walkActorToObject");
- obj = getVarOrDirectWord(PARAM_2);
- if (whereIsObject(obj) != WIO_NOT_FOUND) {
- int x, y, dir;
- getObjectXYPos(obj, x, y, dir);
- AdjustBoxResult r = a->adjustXYToBeInBox(x, y);
- x = r.x;
- y = r.y;
+ a->startWalkActor(x, y, dir);
+}
- a->startWalkActor(x, y, dir);
+void ScummEngine_v2::o2_walkActorToObject() {
+ int actor = getVarOrDirectByte(PARAM_1);
+ int obj = getVarOrDirectWord(PARAM_2);
+ if (whereIsObject(obj) != WIO_NOT_FOUND) {
+ walkActorToObject(actor, obj);
}
}
@@ -1298,7 +1304,7 @@ void ScummEngine_v2::o2_findObject() {
int x = getVarOrDirectByte(PARAM_1) * V12_X_MULTIPLIER;
int y = getVarOrDirectByte(PARAM_2) * V12_Y_MULTIPLIER;
obj = findObject(x, y);
- if (obj == 0 && (_game.platform == Common::kPlatformNES) && (_userState & 0x40)) {
+ if (obj == 0 && (_game.platform == Common::kPlatformNES) && (_userState & USERSTATE_IFACE_INVENTORY)) {
if (_mouseOverBoxV2 >= 0 && _mouseOverBoxV2 < 4)
obj = findInventory(VAR(VAR_EGO), _mouseOverBoxV2 + _inventoryOffset + 1);
}
@@ -1310,7 +1316,7 @@ void ScummEngine_v2::o2_getActorX() {
getResultPos();
a = getVarOrDirectByte(PARAM_1);
- setResult(getObjX(a));
+ setResult(getObjX(actorToObj(a)));
}
void ScummEngine_v2::o2_getActorY() {
@@ -1318,7 +1324,7 @@ void ScummEngine_v2::o2_getActorY() {
getResultPos();
a = getVarOrDirectByte(PARAM_1);
- setResult(getObjY(a));
+ setResult(getObjY(actorToObj(a)));
}
void ScummEngine_v2::o2_isGreater() {
@@ -1480,7 +1486,9 @@ void ScummEngine_v2::o2_cutscene() {
VAR(VAR_CURSORSTATE) = 200;
// Hide inventory, freeze scripts, hide cursor
- setUserState(15);
+ setUserState(USERSTATE_SET_IFACE |
+ USERSTATE_SET_CURSOR |
+ USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON);
_sentenceNum = 0;
stopScript(SENTENCE_SCRIPT);
@@ -1499,7 +1507,7 @@ void ScummEngine_v2::o2_endCutscene() {
VAR(VAR_CURSORSTATE) = vm.cutSceneData[1];
// Reset user state to values before cutscene
- setUserState(vm.cutSceneData[0] | 7);
+ setUserState(vm.cutSceneData[0] | USERSTATE_SET_IFACE | USERSTATE_SET_CURSOR | USERSTATE_SET_FREEZE);
if ((_game.id == GID_MANIAC) && !(_game.platform == Common::kPlatformNES)) {
camera._mode = (byte) vm.cutSceneData[3];
@@ -1519,7 +1527,7 @@ void ScummEngine_v2::o2_beginOverride() {
// Skip the jump instruction following the override instruction
fetchScriptByte();
- fetchScriptWord();
+ ScummEngine::fetchScriptWord();
}
void ScummEngine_v2::o2_chainScript() {
@@ -1565,24 +1573,24 @@ void ScummEngine_v2::o2_cursorCommand() { // TODO: Define the magic numbers
}
void ScummEngine_v2::setUserState(byte state) {
- if (state & 4) { // Userface
+ if (state & USERSTATE_SET_IFACE) { // Userface
if (_game.platform == Common::kPlatformNES)
- _userState = (_userState & ~0xE0) | (state & 0xE0);
+ _userState = (_userState & ~USERSTATE_IFACE_ALL) | (state & USERSTATE_IFACE_ALL);
else
- _userState = state & (32 | 64 | 128);
+ _userState = state & USERSTATE_IFACE_ALL;
}
- if (state & 1) { // Freeze
- if (state & 8)
+ if (state & USERSTATE_SET_FREEZE) { // Freeze
+ if (state & USERSTATE_FREEZE_ON)
freezeScripts(0);
else
unfreezeScripts();
}
- if (state & 2) { // Cursor Show/Hide
+ if (state & USERSTATE_SET_CURSOR) { // Cursor Show/Hide
if (_game.platform == Common::kPlatformNES)
- _userState = (_userState & ~0x10) | (state & 0x10);
- if (state & 16) {
+ _userState = (_userState & ~USERSTATE_CURSOR_ON) | (state & USERSTATE_CURSOR_ON);
+ if (state & USERSTATE_CURSOR_ON) {
_userPut = 1;
_cursor.state = 1;
} else {
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index 6426b75e1e..a5591b701f 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -1095,10 +1095,16 @@ void ScummEngine_v5::o5_getClosestObjActor() {
void ScummEngine_v5::o5_getDist() {
int o1, o2;
int r;
+
getResultPos();
+
o1 = getVarOrDirectWord(PARAM_1);
o2 = getVarOrDirectWord(PARAM_2);
- r = getObjActToObjActDist(o1, o2);
+
+ if (_game.version == 0) // in v0 both parameters are always actor IDs, never objects
+ r = getObjActToObjActDist(actorToObj(o1), actorToObj(o2));
+ else
+ r = getObjActToObjActDist(o1, o2);
// FIXME: MI2 race workaround, see bug #597022. We never quite figured out
// what the real cause of this, or if it maybe occurs in the original, too...
@@ -2464,8 +2470,40 @@ void ScummEngine_v5::o5_walkActorTo() {
a->startWalkActor(x, y, -1);
}
+void ScummEngine_v5::walkActorToActor(int actor, int toActor, int dist) {
+ Actor *a = derefActor(actor, "walkActorToActor");
+ Actor *to = derefActor(toActor, "walkActorToActor(2)");
+
+ if (_game.version <= 2) {
+ dist *= V12_X_MULTIPLIER;
+ } else if (dist == 0xFF) {
+ dist = a->_scalex * a->_width / 0xFF;
+ dist += (to->_scalex * to->_width / 0xFF) / 2;
+ }
+ int x = to->getPos().x;
+ int y = to->getPos().y;
+ if (x < a->getPos().x)
+ x += dist;
+ else
+ x -= dist;
+
+ if (_game.version <= 2) {
+ x /= V12_X_MULTIPLIER;
+ y /= V12_Y_MULTIPLIER;
+ }
+ if (_game.version <= 3) {
+ AdjustBoxResult abr = a->adjustXYToBeInBox(x, y);
+ x = abr.x;
+ y = abr.y;
+ }
+ a->startWalkActor(x, y, -1);
+
+ // WORKAROUND: See bug #2971126 for details on why this is here.
+ if (_game.version == 0)
+ o5_breakHere();
+}
+
void ScummEngine_v5::o5_walkActorToActor() {
- int x, y;
Actor *a, *a2;
int nr = getVarOrDirectByte(PARAM_1);
int nr2 = getVarOrDirectByte(PARAM_2);
@@ -2499,33 +2537,7 @@ void ScummEngine_v5::o5_walkActorToActor() {
if (!a2->isInCurrentRoom())
return;
- if (_game.version <= 2) {
- dist *= V12_X_MULTIPLIER;
- } else if (dist == 0xFF) {
- dist = a->_scalex * a->_width / 0xFF;
- dist += (a2->_scalex * a2->_width / 0xFF) / 2;
- }
- x = a2->getPos().x;
- y = a2->getPos().y;
- if (x < a->getPos().x)
- x += dist;
- else
- x -= dist;
-
- if (_game.version <= 2) {
- x /= V12_X_MULTIPLIER;
- y /= V12_Y_MULTIPLIER;
- }
- if (_game.version <= 3) {
- AdjustBoxResult abr = a->adjustXYToBeInBox(x, y);
- x = abr.x;
- y = abr.y;
- }
- a->startWalkActor(x, y, -1);
-
- // WORKAROUND: See bug #2971126 for details on why this is here.
- if (_game.version == 0)
- o5_breakHere();
+ walkActorToActor(nr, nr2, dist);
}
void ScummEngine_v5::o5_walkActorToObject() {
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index d3cc218cd3..fc46f88df4 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -151,9 +151,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_fileHandle = 0;
// Init all vars
- _v0ObjectIndex = false;
- _v0ObjectInInventory = false;
- _v0ObjectFlag = 0;
_imuse = NULL;
_imuseDigital = NULL;
_musicEngine = NULL;
@@ -266,7 +263,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_bytesPerPixel = 1;
_doEffect = false;
_snapScroll = false;
- _currentLights = 0;
_shakeEnabled = false;
_shakeFrame = 0;
_screenStartStrip = 0;
@@ -701,10 +697,6 @@ ScummEngine_v2::ScummEngine_v2(OSystem *syst, const DetectorResult &dr)
_inventoryOffset = 0;
- _activeInventory = 0;
- _activeObject = 0;
- _activeVerb = 0;
-
VAR_SENTENCE_VERB = 0xFF;
VAR_SENTENCE_OBJECT1 = 0xFF;
VAR_SENTENCE_OBJECT2 = 0xFF;
@@ -719,19 +711,18 @@ ScummEngine_v2::ScummEngine_v2(OSystem *syst, const DetectorResult &dr)
ScummEngine_v0::ScummEngine_v0(OSystem *syst, const DetectorResult &dr)
: ScummEngine_v2(syst, dr) {
- _verbExecuting = false;
- _verbPickup = false;
_currentMode = 0;
+ _currentLights = 0;
+ _activeVerb = kVerbNone;
+ _activeObject = 0;
_activeObject2 = 0;
- _activeObjectIndex = 0;
- _activeObject2Index = 0;
- _activeInvExecute = false;
- _activeObject2Inv = false;
- _activeObjectObtained = false;
- _activeObject2Obtained = false;
-
- VAR_ACTIVE_ACTOR = 0xFF;
+
+ _cmdVerb = kVerbNone;
+ _cmdObject = 0;
+ _cmdObject2 = 0;
+
+ VAR_ACTIVE_OBJECT2 = 0xFF;
VAR_IS_SOUND_RUNNING = 0xFF;
VAR_ACTIVE_VERB = 0xFF;
}
@@ -1034,7 +1025,7 @@ Common::Error ScummEngine::init() {
// The kGenUnchanged method is only used for 'container files', i.e. files
// that contain the real game files bundled together in an archive format.
- // This is the case of the NES, C64 and Mac versions of certain games.
+ // This is the case of the NES, v0 and Mac versions of certain games.
// Note: All of these can also occur in 'extracted' form, in which case they
// are treated like any other SCUMM game.
if (_filenamePattern.genMethod == kGenUnchanged) {
@@ -1388,8 +1379,8 @@ void ScummEngine::setupCostumeRenderer() {
_costumeRenderer = new AkosRenderer(this);
_costumeLoader = new AkosCostumeLoader(this);
} else if (_game.version == 0) {
- _costumeRenderer = new C64CostumeRenderer(this);
- _costumeLoader = new C64CostumeLoader(this);
+ _costumeRenderer = new V0CostumeRenderer(this);
+ _costumeLoader = new V0CostumeLoader(this);
} else if (_game.platform == Common::kPlatformNES) {
_costumeRenderer = new NESCostumeRenderer(this);
_costumeLoader = new NESCostumeLoader(this);
@@ -1468,7 +1459,7 @@ void ScummEngine::resetScumm() {
_sortedActors = new Actor * [_numActors];
for (i = 0; i < _numActors; ++i) {
if (_game.version == 0)
- _actors[i] = new ActorC64(this, i);
+ _actors[i] = new Actor_v0(this, i);
else if (_game.version <= 2)
_actors[i] = new Actor_v2(this, i);
else if (_game.version == 3)
@@ -1973,6 +1964,14 @@ 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
+ // 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;
+
// Wait...
waitForTimer(delta * 1000 / 60 - diff);
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index d9237b2b30..2f1e536f0a 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -303,6 +303,19 @@ struct SaveStateMetaInfos {
uint32 playtime;
};
+enum UserStates {
+ USERSTATE_SET_FREEZE = 0x01, // freeze scripts if USERSTATE_FREEZE_ON is set, unfreeze otherwise
+ USERSTATE_SET_CURSOR = 0x02, // shows cursor if USERSTATE_CURSOR_ON is set, hides it otherwise
+ USERSTATE_SET_IFACE = 0x04, // change user-interface (sentence-line, inventory, verb-area)
+ USERSTATE_FREEZE_ON = 0x08, // only interpreted if USERSTATE_SET_FREEZE is set
+ USERSTATE_CURSOR_ON = 0x10, // only interpreted if USERSTATE_SET_CURSOR is set
+ USERSTATE_IFACE_SENTENCE = 0x20, // only interpreted if USERSTATE_SET_IFACE is set
+ USERSTATE_IFACE_INVENTORY = 0x40, // only interpreted if USERSTATE_SET_IFACE is set
+ USERSTATE_IFACE_VERBS = 0x80 // only interpreted if USERSTATE_SET_IFACE is set
+};
+
+#define USERSTATE_IFACE_ALL (USERSTATE_IFACE_SENTENCE | USERSTATE_IFACE_INVENTORY | USERSTATE_IFACE_VERBS)
+
/**
* A list of resource types.
* WARNING: Do not change the order of these, as the savegame format relies
@@ -502,10 +515,6 @@ protected:
int32 *_scummVars;
byte *_bitVars;
- bool _v0ObjectIndex; // V0 Use object index, instead of object number
- bool _v0ObjectInInventory; // V0 Use object number from inventory
- byte _v0ObjectFlag;
-
/* Global resource tables */
int _numVariables, _numBitVariables, _numLocalObjects;
int _numGlobalObjects, _numArray, _numVerbs, _numFlObject;
@@ -646,7 +655,7 @@ protected:
void updateScriptPtr();
virtual void runInventoryScript(int i);
void inventoryScriptIndy3Mac();
- void checkAndRunSentenceScript();
+ virtual void checkAndRunSentenceScript();
void runExitScript();
void runEntryScript();
void runAllScripts();
@@ -791,6 +800,9 @@ protected:
void setOwnerOf(int obj, int owner);
void clearOwnerOf(int obj);
int getObjectRoom(int obj) const;
+ virtual bool objIsActor(int obj);
+ virtual int objToActor(int obj);
+ virtual int actorToObj(int actor);
int getObjX(int obj);
int getObjY(int obj);
void getObjectXYPos(int object, int &x, int &y) { int dir; getObjectXYPos(object, x, y, dir); }
@@ -799,7 +811,6 @@ protected:
int getObjNewDir(int obj);
int getObjectIndex(int object) const;
int getObjectImageCount(int object);
- int whereIsObjectInventory(int object);
int whereIsObject(int object) const;
int findObject(int x, int y);
void findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint object, uint room);
@@ -820,7 +831,7 @@ protected:
virtual void clearDrawQueues();
uint32 getOBCDOffs(int object) const;
- byte *getOBCDFromObject(int obj);
+ byte *getOBCDFromObject(int obj, bool v0CheckInventory = true);
const byte *getOBIMFromObjectData(const ObjectData &od);
const byte *getObjectImage(const byte *ptr, int state);
virtual int getObjectIdFromOBIM(const byte *obim);
@@ -931,8 +942,7 @@ protected:
public:
bool isLightOn() const;
- byte _currentLights;
- int getCurrentLights() const;
+ virtual int getCurrentLights() const;
protected:
void initScreens(int b, int h);
diff --git a/engines/scumm/scumm_v0.h b/engines/scumm/scumm_v0.h
index af481df0e0..144dd701d4 100644
--- a/engines/scumm/scumm_v0.h
+++ b/engines/scumm/scumm_v0.h
@@ -32,20 +32,35 @@ namespace Scumm {
*/
class ScummEngine_v0 : public ScummEngine_v2 {
protected:
+ enum CurrentMode {
+ kModeCutscene = 0, // cutscene active
+ kModeKeypad = 1, // kid selection / dial pad / save-load dialog
+ kModeNoNewKid = 2, // verb "new kid" disabled (e.g. when entering lab)
+ kModeNormal = 3 // normal playing mode
+ };
+
+ enum WalkToObjectState {
+ kWalkToObjectStateDone = 0,
+ kWalkToObjectStateWalk = 1,
+ kWalkToObjectStateTurn = 2
+ };
+
+protected:
byte _currentMode;
- bool _verbExecuting; // is a verb executing
- bool _verbPickup; // are we picking up an object during a verb execute
+ byte _currentLights;
- int _activeActor; // Actor Number
- int _activeObject2; // 2nd Object Number
+ int _activeVerb; // selected verb
+ int _activeObject; // 1st selected object (see OBJECT_V0())
+ int _activeObject2; // 2nd selected object or actor (see OBJECT_V0())
- bool _activeInvExecute; // is activeInventory first to be executed
- bool _activeObject2Inv; // is activeobject2 in the inventory
- bool _activeObjectObtained; // collected _activeobject?
- bool _activeObject2Obtained; // collected _activeObject2?
+ int _cmdVerb; // script verb
+ int _cmdObject; // 1st script object (see OBJECT_V0())
+ int _cmdObject2; // 2nd script object or actor (see OBJECT_V0())
+ int _sentenceNestedCount;
- int _activeObjectIndex;
- int _activeObject2Index;
+ int _walkToObject;
+ int _walkToObjectState;
+ bool _redrawSentenceLine;
public:
ScummEngine_v0(OSystem *syst, const DetectorResult &dr);
@@ -64,26 +79,33 @@ protected:
virtual void processInput();
- virtual void runObject(int obj, int entry);
virtual void saveOrLoad(Serializer *s);
- // V0 MM Verb commands
- int verbPrep(int object);
- bool verbMove(int object, int objectIndex, bool invObject);
- bool verbMoveToActor(int actor);
- bool verbObtain(int object, int objIndex);
- bool verbExecutes(int object, bool inventory = false);
- bool verbExec();
-
- int findObjectIndex(int x, int y);
+ virtual bool objIsActor(int obj);
+ virtual int objToActor(int obj);
+ virtual int actorToObj(int actor);
+ // V0 MM Verb commands
+ int getVerbPrepId();
+ int activeVerbPrep();
+ void walkToActorOrObject(int object);
+ void verbExec();
+
+ virtual void runSentenceScript();
+ virtual void checkAndRunSentenceScript();
+ bool checkPendingWalkAction();
+ bool checkSentenceComplete();
virtual void checkExecVerbs();
virtual void handleMouseOver(bool updateInventory);
+ int verbPrepIdType(int verbid);
void resetVerbs();
- void setNewKidVerbs();
- void drawSentenceWord(int object, bool usePrep, bool objInInventory);
- void drawSentence();
+ void clearSentenceLine();
+ void flushSentenceLine();
+ void drawSentenceObject(int object);
+ void drawSentenceLine();
+
+ void setMode(byte mode);
void switchActor(int slot);
@@ -92,12 +114,17 @@ protected:
virtual int getActiveObject();
- virtual void resetSentence(bool walking);
+ virtual void resetSentence();
virtual bool areBoxesNeighbors(int box1nr, int box2nr);
- /* Version C64 script opcodes */
+ bool ifEqualActiveObject2Common(bool checkType);
+
+ virtual int getCurrentLights() const;
+
+ /* Version 0 script opcodes */
void o_stopCurrentScript();
+ void o_walkActorToObject();
void o_loadSound();
void o_getActorMoving();
void o_animateActor();
@@ -112,30 +139,30 @@ protected:
void o_lockScript();
void o_loadScript();
void o_lockRoom();
- void o_cursorCommand();
+ void o_setMode();
void o_lights();
void o_unlockCostume();
void o_unlockScript();
void o_decrement();
void o_nop();
+ void o_getObjectOwner();
void o_getActorBitVar();
void o_setActorBitVar();
void o_getBitVar();
void o_setBitVar();
void o_doSentence();
- void o_unknown2();
- void o_ifActiveObject();
- void o_getClosestObjActor();
- void o_printEgo_c64();
- void o_print_c64();
+ void o_ifEqualActiveObject2();
+ void o_ifNotEqualActiveObject2();
+ void o_getClosestActor();
+ void o_printEgo();
+ void o_print();
void o_unlockRoom();
void o_unlockSound();
void o_cutscene();
void o_endCutscene();
- void o_beginOverride();
void o_setOwnerOf();
- byte VAR_ACTIVE_ACTOR;
+ byte VAR_ACTIVE_OBJECT2;
byte VAR_IS_SOUND_RUNNING;
byte VAR_ACTIVE_VERB;
};
diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h
index 47c5fa2626..b407fd3f8a 100644
--- a/engines/scumm/scumm_v2.h
+++ b/engines/scumm/scumm_v2.h
@@ -44,17 +44,13 @@ protected:
Common::String _sentenceBuf;
uint16 _inventoryOffset;
- int _activeInventory;
- int _activeObject;
- int _activeVerb;
-
public:
ScummEngine_v2(OSystem *syst, const DetectorResult &dr);
virtual void resetScumm();
void checkV2MouseOver(Common::Point pos);
- void checkV2Inventory(int x, int y);
+ int checkV2Inventory(int x, int y);
void redrawV2Inventory();
protected:
@@ -89,6 +85,7 @@ protected:
void ifNotStateCommon(byte type);
void setStateCommon(byte type);
void clearStateCommon(byte type);
+ void stopScriptCommon(int script);
virtual void resetSentence(bool walking);
void setUserState(byte state);
@@ -100,6 +97,10 @@ protected:
virtual void setBuiltinCursor(int index);
+ void drawPreposition(int index);
+
+ void walkActorToObject(int actor, int obj);
+
/* Version 2 script opcodes */
void o2_actorFromPos();
void o2_actorOps();
diff --git a/engines/scumm/scumm_v5.h b/engines/scumm/scumm_v5.h
index b8a61c1677..0eef04b8de 100644
--- a/engines/scumm/scumm_v5.h
+++ b/engines/scumm/scumm_v5.h
@@ -87,6 +87,8 @@ protected:
void drawFlashlight();
+ void walkActorToActor(int actor, int toActor, int dist);
+
/**
* Fetch the next script word, then if cond is *false*, perform a relative jump.
* So this corresponds to a "jne" jump instruction.
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 26a6a2f3b1..6d132c601f 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -115,7 +115,7 @@ void ScummEngine_v0::setupScummVars() {
VAR_CAMERA_POS_X = 2;
VAR_HAVE_MSG = 3;
VAR_ROOM = 4;
- VAR_ACTIVE_ACTOR = 5;
+ VAR_ACTIVE_OBJECT2 = 5;
VAR_OVERRIDE = 6;
VAR_IS_SOUND_RUNNING = 8;
VAR_ACTIVE_VERB = 9;
@@ -546,7 +546,7 @@ void ScummEngine_v8::setupScummVars() {
#endif
void ScummEngine_v0::resetScummVars() {
- resetSentence(false);
+ resetSentence();
VAR(VAR_EGO) = 3;
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 67ed17c024..567ca31485 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -41,47 +41,58 @@ struct VerbSettings {
int id;
int x_pos;
int y_pos;
- int prep;
const char *name;
};
static const VerbSettings v0VerbTable_English[] = {
- { 1, 8, 0, 0, "Open"},
- { 2, 8, 1, 0, "Close"},
- { 3, 0, 2, 4, "Give"},
- { 4, 32, 0, 0, "Turn on"},
- { 5, 32, 1, 0, "Turn off"},
- { 6, 32, 2, 2, "Fix"},
- { 7, 24, 0, 0, "New Kid"},
- { 8, 24, 1, 2, "Unlock"},
- { 9, 0, 0, 0, "Push"},
- {10, 0, 1, 0, "Pull"},
- {11, 24, 2, 255, "Use"},
- {12, 8, 2, 0, "Read"},
- {13, 15, 0, 0, "Walk to"},
- {14, 15, 1, 0, "Pick up"},
- {15, 15, 2, 0, "What is"}
+ {kVerbOpen, 8, 0, "Open"},
+ {kVerbClose, 8, 1, "Close"},
+ {kVerbGive, 0, 2, "Give"},
+ {kVerbTurnOn, 32, 0, "Turn on"},
+ {kVerbTurnOff, 32, 1, "Turn off"},
+ {kVerbFix, 32, 2, "Fix"},
+ {kVerbNewKid, 24, 0, "New Kid"},
+ {kVerbUnlock, 24, 1, "Unlock"},
+ {kVerbPush, 0, 0, "Push"},
+ {kVerbPull, 0, 1, "Pull"},
+ {kVerbUse, 24, 2, "Use"},
+ {kVerbRead, 8, 2, "Read"},
+ {kVerbWalkTo, 15, 0, "Walk to"},
+ {kVerbPickUp, 15, 1, "Pick up"},
+ {kVerbWhatIs, 15, 2, "What is"}
};
-// FIXME: Replace * with the correct character
static const VerbSettings v0VerbTable_German[] = {
- { 1, 7, 0, 0, "$ffne"},
- { 2, 13, 1, 0, "Schlie*e"},
- { 3, 0, 2, 4, "Gebe"},
- { 4, 37, 1, 0, "Ein"},
- { 5, 37, 0, 0, "Aus"},
- { 6, 23, 1, 2, "Repariere"},
- { 7, 34, 2, 0, "Person"},
- { 8, 23, 0, 2, "Schlie*e auf"},
- { 9, 0, 0, 0, "Dr<cke"},
- {10, 0, 1, 0, "Ziehe"},
- {11, 23, 2, 255, "Benutz"},
- {12, 7, 2, 0, "Lese"},
- {13, 13, 0, 0, "Gehe zu"},
- {14, 7, 1, 0, "Nimm"},
- {15, 13, 2, 0, "Was ist"}
+ {kVerbOpen, 7, 0, "$ffne"},
+ {kVerbClose, 13, 1, "Schlie*e"},
+ {kVerbGive, 0, 2, "Gebe"},
+ {kVerbTurnOn, 37, 1, "Ein"},
+ {kVerbTurnOff, 37, 0, "Aus"},
+ {kVerbFix, 23, 1, "Repariere"},
+ {kVerbNewKid, 34, 2, "Person"},
+ {kVerbUnlock, 23, 0, "Schlie*e auf"},
+ {kVerbPush, 0, 0, "Dr<cke"},
+ {kVerbPull, 0, 1, "Ziehe"},
+ {kVerbUse, 23, 2, "Benutz"},
+ {kVerbRead, 7, 2, "Lese"},
+ {kVerbWalkTo, 13, 0, "Gehe zu"},
+ {kVerbPickUp, 7, 1, "Nimm"},
+ {kVerbWhatIs, 13, 2, "Was ist"}
};
+int ScummEngine_v0::verbPrepIdType(int verbid) {
+ switch (verbid) {
+ case kVerbUse: // depends on object1
+ return kVerbPrepObject;
+ case kVerbGive:
+ return kVerbPrepTo;
+ case kVerbUnlock: case kVerbFix:
+ return kVerbPrepWith;
+ default:
+ return kVerbPrepNone;
+ }
+}
+
void ScummEngine_v0::resetVerbs() {
VirtScreen *virt = &_virtscr[kVerbVirtScreen];
VerbSlot *vs;
@@ -112,62 +123,22 @@ void ScummEngine_v0::resetVerbs() {
vs->key = 0;
vs->center = 0;
vs->imgindex = 0;
- vs->prep = vtable[i - 1].prep;
+ vs->prep = verbPrepIdType(vtable[i - 1].id);
vs->curRect.left = vtable[i - 1].x_pos * 8;
vs->curRect.top = vtable[i - 1].y_pos * 8 + virt->topline + 8;
loadPtrToResource(rtVerb, i, (const byte*)vtable[i - 1].name);
}
}
-void ScummEngine_v0::setNewKidVerbs() {
- VirtScreen *virt = &_virtscr[kVerbVirtScreen];
- VerbSlot *vs;
- int i;
-
- for (i = 1; i < 16; i++)
- killVerb(i);
-
- for (i = 1; i < 4; i++) {
- vs = &_verbs[i];
- vs->verbid = i;
- vs->color = 5;
- vs->hicolor = 7;
- vs->dimcolor = 11;
- vs->type = kTextVerbType;
- vs->charset_nr = _string[0]._default.charset;
- vs->curmode = 1;
- vs->saveid = 0;
- vs->key = 0;
- vs->center = 0;
- vs->imgindex = 0;
- vs->prep = 0;
- vs->curRect.left = (i * 8) * 8;
- vs->curRect.top = virt->topline + 8;
-
- Actor *a = derefActor(VAR(96 + i), "setNewKidVerbs");
- loadPtrToResource(rtVerb, i, (const byte*)a->getActorName());
- }
- setUserState(191);
-}
-
void ScummEngine_v0::switchActor(int slot) {
- resetSentence(false);
-
- if (_currentRoom == 45)
- return;
-
- // radiation suit? don't let the player switch
- if (VAR(VAR_EGO) == 8)
- return;
+ resetSentence();
- // verbs disabled? or just new kid button?
- if (_currentMode == 0 || _currentMode == 1 || _currentMode == 2)
+ // actor switching only allowed during normal gamplay (not cutscene, ...)
+ if (_currentMode != kModeNormal)
return;
VAR(VAR_EGO) = VAR(97 + slot);
- resetVerbs();
actorFollowCamera(VAR(VAR_EGO));
- setUserState(247);
}
void ScummEngine_v2::initV2MouseOver() {
@@ -301,7 +272,7 @@ void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {
int i, x, y, new_box = -1;
// Don't do anything unless the inventory is active
- if (!(_userState & 64)) {
+ if (!(_userState & USERSTATE_IFACE_INVENTORY)) {
_mouseOverBoxV2 = -1;
return;
}
@@ -354,14 +325,14 @@ void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {
}
}
-void ScummEngine_v2::checkV2Inventory(int x, int y) {
+int ScummEngine_v2::checkV2Inventory(int x, int y) {
int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
int object = 0;
y -= _virtscr[kVerbVirtScreen].topline;
if ((y < inventoryArea) || !(_mouseAndKeyboardStat & MBS_LEFT_CLICK))
- return;
+ return 0;
if (_mouseOverBoxesV2[kInventoryUpArrow].rect.contains(x, y)) {
if (_inventoryOffset >= 2) {
@@ -382,18 +353,9 @@ void ScummEngine_v2::checkV2Inventory(int x, int y) {
}
if (object >= 4)
- return;
-
- object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
-
- if (object > 0) {
- if (_game.version == 0) {
- _activeInventory = object;
+ return 0;
- } else {
- runInputScript(kInventoryClickArea, object, 0);
- }
- }
+ return findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
}
void ScummEngine_v2::redrawV2Inventory() {
@@ -406,7 +368,7 @@ void ScummEngine_v2::redrawV2Inventory() {
_mouseOverBoxV2 = -1;
- if (!(_userState & 64)) // Don't draw inventory unless active
+ if (!(_userState & USERSTATE_IFACE_INVENTORY)) // Don't draw inventory unless active
return;
// Clear on all invocations
@@ -431,9 +393,7 @@ void ScummEngine_v2::redrawV2Inventory() {
_string[1].right = _mouseOverBoxesV2[i].rect.right - 1;
_string[1].color = _mouseOverBoxesV2[i].color;
- _v0ObjectInInventory = true;
const byte *tmp = getObjOrActorName(obj);
- _v0ObjectInInventory = false;
assert(tmp);
// Prevent inventory entries from overflowing by truncating the text
@@ -472,7 +432,7 @@ void ScummEngine_v2::redrawV2Inventory() {
}
void ScummEngine::redrawVerbs() {
- if (_game.version <= 2 && !(_userState & 128)) // Don't draw verbs unless active
+ if (_game.version <= 2 && !(_userState & USERSTATE_IFACE_VERBS)) // Don't draw verbs unless active
return;
int i, verb = 0;
@@ -516,7 +476,6 @@ void ScummEngine_v2::handleMouseOver(bool updateInventory) {
}
void ScummEngine_v0::handleMouseOver(bool updateInventory) {
- drawSentence();
ScummEngine_v2::handleMouseOver(updateInventory);
}
@@ -674,15 +633,8 @@ void ScummEngine_v2::checkExecVerbs() {
if (object != -1) {
object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
-
- if (object > 0) {
- if (_game.version == 0) {
- _activeInventory = object;
-
- } else {
- runInputScript(kInventoryClickArea, object, 0);
- }
- }
+ if (object > 0)
+ runInputScript(kInventoryClickArea, object, 0);
return;
}
@@ -703,7 +655,9 @@ void ScummEngine_v2::checkExecVerbs() {
runInputScript(kSentenceClickArea, 0, 0);
} else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + inventoryArea) {
// Click into V2 inventory
- checkV2Inventory(_mouse.x, _mouse.y);
+ int object = checkV2Inventory(_mouse.x, _mouse.y);
+ if (object > 0)
+ runInputScript(kInventoryClickArea, object, 0);
} else {
over = findVerbAtPos(_mouse.x, _mouse.y);
if (over != 0) {
@@ -717,550 +671,210 @@ void ScummEngine_v2::checkExecVerbs() {
}
}
-void ScummEngine_v0::runObject(int obj, int entry) {
- bool prev = _v0ObjectInInventory;
-
- if (getVerbEntrypoint(obj, entry) == 0) {
- // If nothing was found, attempt to find the 'WHAT-IS' verb script
- // (which is not really the what-is script, as this verb never actually executes
- // it merely seems to be some type of fallback)
- if (getVerbEntrypoint(obj, 0x0F) != 0) {
- entry = 0x0F;
- }
- }
-
- _v0ObjectInInventory = prev;
-
- if (getVerbEntrypoint(obj, entry) != 0) {
- _v0ObjectInInventory = prev;
- runObjectScript(obj, entry, false, false, NULL);
- } else if (entry != 13 && entry != 15) {
- if (_activeVerb != 3) {
- VAR(VAR_ACTIVE_VERB) = entry;
- runScript(3, 0, 0, 0);
-
- // For some reasons, certain objects don't have a "give" script
- } else if (VAR(VAR_ACTIVE_ACTOR) > 0 && VAR(VAR_ACTIVE_ACTOR) < 8) {
- if (_activeInventory)
- setOwnerOf(_activeInventory, VAR(VAR_ACTIVE_ACTOR));
- }
- }
-}
-
-bool ScummEngine_v0::verbMoveToActor(int actor) {
- Actor *a = derefActor(VAR(VAR_EGO), "verbMoveToActor");
- Actor *a2 = derefActor(actor, "verbMoveToActor");
- int dist = getDist(a->getRealPos().x, a->getRealPos().y, a2->getRealPos().x, a2->getRealPos().y);
-
- if (!a->_moving && dist > 4) {
- a->startWalkActor(a2->getRealPos().x, a2->getRealPos().y, -1);
+int ScummEngine_v0::getVerbPrepId() {
+ if (_verbs[_activeVerb].prep != 0xFF) {
+ return _verbs[_activeVerb].prep;
} else {
- if (dist <= 4) {
- a->stopActorMoving();
- return false;
- }
+ byte *ptr = getOBCDFromObject(_activeObject, true);
+ assert(ptr);
+ return (*(ptr + 11) >> 5);
}
-
- return true;
}
-bool ScummEngine_v0::verbMove(int object, int objectIndex, bool invObject) {
- int x, y, dir;
- Actor *a = derefActor(VAR(VAR_EGO), "verbMove");
-
- if (_currentMode != 3 && _currentMode != 2)
- return false;
-
- _v0ObjectIndex = true;
- getObjectXYPos(objectIndex, x, y, dir);
- _v0ObjectIndex = false;
-
- // Detect distance from target object
- int dist = getDist(a->getRealPos().x, a->getRealPos().y, x, y);
-
- if (a->_moving)
- return true;
-
- if (dist > 5) {
- a->startWalkActor(x, y, dir);
- VAR(6) = x;
- VAR(7) = y;
- return true;
- } else {
- // Finished walk, are we picking up the item?
- if (_verbPickup) {
- int oldActive = _activeObject, oldIndex = _activeObjectIndex;
- _activeObject = object;
- _activeObjectIndex = objectIndex;
-
- _v0ObjectIndex = true;
- // Execute pickup
- runObject(objectIndex, 14);
- _v0ObjectIndex = false;
-
- _activeObject = oldActive;
- _activeObjectIndex = oldIndex;
-
- // Finished picking up
- _verbPickup = false;
- }
- }
-
- return false;
+int ScummEngine_v0::activeVerbPrep() {
+ if (!_activeVerb || !_activeObject)
+ return 0;
+ return getVerbPrepId();
}
-bool ScummEngine_v0::verbObtain(int obj, int objIndex) {
- bool didPickup = false;
+void ScummEngine_v0::verbExec() {
+ _sentenceNum = 0;
+ _sentenceNestedCount = 0;
- int prep, where = whereIsObjectInventory(obj);
-
- if (objIndex == 0)
- return false;
-
- // Object in inventory ?
- if (where != WIO_INVENTORY) {
- _v0ObjectIndex = true;
- prep = verbPrep(objIndex);
-
- if (prep == 1 || prep == 4) {
- if (_activeVerb != 13 && _activeVerb != 14) {
- _verbPickup = true;
- didPickup = true;
- }
- } else {
- _verbPickup = false;
- }
-
- // Ignore verbs?
- Actor *a = derefActor(VAR(VAR_EGO), "verbObtain");
- if (((ActorC64 *)a)->_miscflags & 0x40) {
- resetSentence(false);
- return false;
+ if (_activeVerb == kVerbWhatIs)
+ return;
+
+ if (!(_activeVerb == kVerbWalkTo && _activeObject == 0)) {
+ doSentence(_activeVerb, _activeObject, _activeObject2);
+ if (_activeVerb != kVerbWalkTo) {
+ _activeVerb = kVerbWalkTo;
+ _activeObject = 0;
+ _activeObject2 = 0;
}
-
- //attempt move to object
- if (verbMove(obj, objIndex, false))
- return true;
-
- if (didPickup && (prep == 1 || prep == 4))
- if (_activeVerb != 13 && _activeVerb != 14) {
- _v0ObjectInInventory = true;
-
- if (whereIsObject(obj) == WIO_INVENTORY)
- _activeInventory = obj;
- else
- resetSentence(false);
-
- _v0ObjectInInventory = false;
- }
+ _walkToObjectState = kWalkToObjectStateDone;
+ return;
}
- return false;
-}
-
-int ScummEngine_v0::verbPrep(int object) {
- if (!_v0ObjectInInventory)
- _v0ObjectIndex = true;
- else
- _v0ObjectIndex = false;
+ Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "verbExec");
+ int x = _virtualMouse.x / V12_X_MULTIPLIER;
+ int y = _virtualMouse.y / V12_Y_MULTIPLIER;
+ //actorSetPosInBox();
- byte *ptr = getOBCDFromObject(object);
- _v0ObjectIndex = false;
- assert(ptr);
- return (*(ptr + 11) >> 5);
-}
+ // 0xB31
+ VAR(6) = x;
+ VAR(7) = y;
-bool ScummEngine_v0::verbExecutes(int object, bool inventory) {
- _v0ObjectInInventory = inventory;
- int prep = verbPrep(object);
-
- if (prep == 2 || prep == 0) {
- return true;
- }
+ if (a->_miscflags & kActorMiscFlagFreeze)
+ return;
- return false;
+ a->stopActorMoving();
+ a->startWalkActor(VAR(6), VAR(7), -1);
}
-bool ScummEngine_v0::verbExec() {
- int prep = 0;
- int entry = (_currentMode != 0 && _currentMode != 1) ? _activeVerb : 15;
-
- if ((!_activeInvExecute && _activeObject && getObjectIndex(_activeObject) == -1)) {
- resetSentence(false);
- return false;
- }
-
- // Lets try walk to the object
- if (_activeObject && _activeObjectIndex && !_activeObjectObtained && _currentMode != 0) {
- prep = verbPrep(_activeObjectIndex);
-
- if (verbObtain(_activeObject, _activeObjectIndex))
+bool ScummEngine_v0::checkSentenceComplete() {
+ if (_activeVerb && _activeVerb != kVerbWalkTo && _activeVerb != kVerbWhatIs) {
+ if (_activeObject && (!activeVerbPrep() || _activeObject2))
return true;
-
- _activeObjectObtained = true;
}
-
- // Attempt to obtain/reach object2
- if (_activeObject2 && _activeObject2Index && !_activeObject2Obtained && _currentMode != 0) {
- prep = verbPrep(_activeObject2Index);
-
- _v0ObjectInInventory = false;
- if (verbObtain(_activeObject2, _activeObject2Index))
- return true;
-
- if (prep != 1 && prep != 4) {
- _activeInventory = _activeObject;
- _activeObject = _activeObject2;
- _activeObjectIndex = _activeObject2Index;
- _activeObject2 = 0;
- _activeObject2Index = 0;
- }
-
- _activeObject2Obtained = true;
- }
-
- // Give-To
- if (_activeVerb == 3 && _activeInventory && _activeActor) {
- // FIXME: Actors need to turn and face each other
- if (verbMoveToActor(_activeActor)) {
- // Ignore verbs?
- Actor *a = derefActor(VAR(VAR_EGO), "verbExec");
- if (((ActorC64 *)a)->_miscflags & 0x40) {
- resetSentence(false);
- return false;
- }
-
- return true;
- }
- _v0ObjectInInventory = true;
- VAR(VAR_ACTIVE_ACTOR) = _activeActor;
- runObject(_activeInventory , 3);
- _v0ObjectInInventory = false;
-
- resetSentence(false);
- return false;
- }
-
- // Where we performing an action on an actor?
- if (_activeActor) {
- _v0ObjectIndex = true;
- runObject(_activeActor, entry);
- _v0ObjectIndex = false;
- _verbExecuting = false;
-
- resetSentence(false);
- return false;
- }
-
- // If we've finished walking (now near target), execute the action
- if (_activeObject && _activeObjectIndex && verbPrep(_activeObjectIndex) == 2) {
- _v0ObjectIndex = true;
- runObject(_activeObjectIndex, entry);
- _v0ObjectIndex = false;
- _verbExecuting = false;
-
- if ((_currentMode == 3 || _currentMode == 2) && _activeVerb == 13)
- return false;
-
- resetSentence(false);
- return false;
- }
-
- // We acted on an inventory item
- if (_activeInventory && verbExecutes(_activeInventory, true) && _activeVerb != 3) {
- _v0ObjectInInventory = true;
- _activeObject = _activeInventory;
- runObject(_activeInventory, _activeVerb);
-
- _verbExecuting = false;
-
- if (_currentMode == 3 && _activeVerb == 13) {
- resetSentence(true);
- return false;
- }
-
- resetSentence(false);
- return false;
- }
-
- // Item not in inventory is executed
- if (_activeObject) {
- _v0ObjectIndex = true;
- runObject(_activeObjectIndex, entry);
- _v0ObjectIndex = false;
- } else if (_activeInventory) {
- // Not sure this is the correct way to do this,
- // however its working for most situations - segra
- if (verbExecutes(_activeInventory, true) == false) {
- if (_activeObject2 && _activeObject2Inv && verbExecutes(_activeObject2, true)) {
- _v0ObjectInInventory = true;
-
- _activeObject = _activeInventory;
- _activeInventory = _activeObject2;
-
- runObject(_activeObject, _activeVerb);
- } else {
- _v0ObjectInInventory = true;
-
- if (_activeObject2) {
- _activeObject = _activeObject2;
-
- runObject(_activeObject, _activeVerb);
- } else
- runObject(_activeInventory, _activeVerb);
- }
- } else {
- _v0ObjectInInventory = true;
- runObject(_activeInventory, _activeVerb);
- }
- }
-
- _verbExecuting = false;
-
- if (_activeVerb == 13) {
- resetSentence(true);
- return false;
- }
-
- resetSentence(false);
-
return false;
}
void ScummEngine_v0::checkExecVerbs() {
- ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");
+ Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");
VirtScreen *zone = findVirtScreen(_mouse.y);
- // Is a verb currently executing
- if (_verbExecuting) {
- // Check if mouse click
- if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
- int over = findVerbAtPos(_mouse.x, _mouse.y);
- int act = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
- int obj = findObject(_virtualMouse.x, _virtualMouse.y);
-
- if (over && over != _activeVerb) {
+ bool execute = false;
+
+ if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
+ int over = findVerbAtPos(_mouse.x, _mouse.y);
+ // click region: verbs
+ if (over) {
+ if (_activeVerb != over) { // new verb
+ // keep first object if no preposition is used yet
+ if (activeVerbPrep())
+ _activeObject = 0;
+ _activeObject2 = 0;
_activeVerb = over;
- _verbExecuting = false;
- return;
- }
-
- if (!obj && !act && !over) {
- resetSentence(false);
+ _redrawSentenceLine = true;
} else {
- a->stopActorMoving();
+ // execute sentence if complete
+ if (checkSentenceComplete())
+ execute = true;
}
- } else {
-
- if (_verbExecuting && !verbExec())
- return;
}
}
- // What-Is selected, any object we hover over is selected, on mouse press we set to WalkTo
- if (_activeVerb == 15) {
- int obj = findObject(_virtualMouse.x, _virtualMouse.y);
- int objIdx = findObjectIndex(_virtualMouse.x, _virtualMouse.y);
- _activeObject = obj;
- _activeObjectIndex = objIdx;
-
- if ((_mouseAndKeyboardStat & MBS_MOUSE_MASK))
- _activeVerb = 13; // Walk-To
-
- return;
+ if (a->_miscflags & kActorMiscFlagHide) {
+ if (_activeVerb != kVerbNewKid) {
+ _activeVerb = kVerbNone;
+ }
}
- if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
- return;
-
- if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
- /* Check keypresses */
- // TODO
- } else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
- if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
- // TODO
- } else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) {
- int prevInventory = _activeInventory;
- int invOff = _inventoryOffset;
-
- // Click into V2 inventory
- checkV2Inventory(_mouse.x, _mouse.y);
-
- // Did the Inventory position changed (arrows pressed, do nothing)
- if (invOff != _inventoryOffset)
- return;
-
- // No inventory selected?
- if (!_activeInventory)
- return;
+ if (_currentMode != kModeCutscene) {
+ if (_currentMode == kModeKeypad) {
+ _activeVerb = kVerbPush;
+ }
- // Did we just change the selected inventory item?
- if (prevInventory && prevInventory != _activeInventory && _activeInventory != _activeObject2) {
- _v0ObjectInInventory = true;
- int prep = verbPrep(_activeInventory);
- _v0ObjectInInventory = true;
- int prep2 = verbPrep(prevInventory);
-
- // Should the new inventory object remain as the secondary selected object
- // Or should the new inventory object become primary?
- if (prep != prep2 || prep != 1) {
- if (prep == 1 || prep == 3) {
- int tmp = _activeInventory;
- _activeInventory = prevInventory;
- prevInventory = tmp;
+ if (_mouseAndKeyboardStat > 0 && _mouseAndKeyboardStat < MBS_MAX_KEY) {
+ // keys already checked by input handler
+ } else if ((_mouseAndKeyboardStat & MBS_MOUSE_MASK) || _activeVerb == kVerbWhatIs) {
+ // click region: sentence line
+ if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
+ if (_activeVerb == kVerbNewKid) {
+ if (_currentMode == kModeNormal) {
+ int kid;
+ int lineX = _mouse.x >> V12_X_SHIFT;
+ if (lineX < 11)
+ kid = 0;
+ else if (lineX < 25)
+ kid = 1;
+ else
+ kid = 2;
+ _activeVerb = kVerbWalkTo;
+ _redrawSentenceLine = true;
+ drawSentenceLine();
+ switchActor(kid);
}
- }
-
- // Setup object2
- _activeObject = 0;
- _activeInvExecute = true;
- _activeObject2Inv = true;
- _activeObject2 = _activeInventory;
- _activeInventory = prevInventory;
- return;
- }
-
- // is the new selected inventory the same as the last selected?, reset to previous if it is
- if (_activeInventory == _activeObject2)
- _activeInventory = prevInventory;
-
- // Inventory Selected changed
- if (prevInventory != _activeInventory)
- if (!_activeObject2 || prevInventory != _activeObject2)
- return;
-
- if (_activeVerb == 11 && !(((_activeObject || _activeInventory)) || !_activeObject2))
- return;
- } else {
- int over = findVerbAtPos(_mouse.x, _mouse.y);
- int act = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
- int obj = findObject(_virtualMouse.x, _virtualMouse.y);
- int objIdx = findObjectIndex(_virtualMouse.x, _virtualMouse.y);
-
- // If we already have an object selected, and we just clicked an actor
- // Clear any object we may of also clicked on
- if ((_activeObject || _activeInventory) && act) {
- obj = 0;
- objIdx = 0;
- }
-
- if (a->_miscflags & 0x80) {
- if (_activeVerb != 7 && over != 7) {
- _activeVerb = 0;
- over = 0;
- }
- }
-
- // Handle New Kid verb options
- if (_activeVerb == 7 || over == 7) {
- // Disable New-Kid (in the secret lab)
- if (_currentMode == 2 || _currentMode == 0)
- return;
-
- if (_activeVerb == 7 && over) {
- _activeVerb = 13;
- switchActor(_verbs[over].verbid - 1);
+ _activeVerb = kVerbWalkTo;
+ _redrawSentenceLine = true;
return;
+ } else {
+ // execute sentence if complete
+ if (checkSentenceComplete())
+ execute = true;
}
-
- setNewKidVerbs();
- _activeVerb = 7;
-
- return;
- }
-
- // Clicked on nothing, walk here?
- if (!over && !act && _activeVerb == 13 && !obj && _currentMode != 0) {
- // Clear all selected
- resetSentence(false);
-
- // 0xB31
- VAR(6) = _virtualMouse.x / V12_X_MULTIPLIER;
- VAR(7) = _virtualMouse.y / V12_Y_MULTIPLIER;
-
- if (zone->number == kMainVirtScreen) {
- // Ignore verbs?
- if (a->_miscflags & 0x40) {
- resetSentence(false);
+ // click region: inventory or main screen
+ } else if ((zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) ||
+ (zone->number == kMainVirtScreen))
+ {
+ int obj = 0;
+
+ // click region: inventory
+ if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) {
+ // click into inventory
+ int invOff = _inventoryOffset;
+ obj = checkV2Inventory(_mouse.x, _mouse.y);
+ if (invOff != _inventoryOffset) {
+ // inventory position changed (arrows pressed, do nothing)
return;
}
- a->stopActorMoving();
- a->startWalkActor(VAR(6), VAR(7), -1);
- _verbExecuting = true;
- }
- return;
- }
-
- // No new verb, use previous
- if (over == 0)
- over = _activeVerb;
-
- // No verb selected, use walk-to
- if (!_activeVerb)
- _activeVerb = over = 13; // Walk-To
-
- // New verb selected
- if (_activeVerb != over) {
- _activeVerb = over;
- if (_activeVerb == 13) {
- resetSentence(false);
- }
- return;
- }
-
- // Only allowing targetting actors if its the GIVE/USE verb
- if (_activeVerb == 3 || _activeVerb == 11) {
- // Different actor selected?
- if (act) {
- if (_activeActor != act) {
- _activeActor = act;
- return;
+ // the second object of a give-to command has to be an actor
+ if (_activeVerb == kVerbGive && _activeObject)
+ obj = 0;
+ // click region: main screen
+ } else if (zone->number == kMainVirtScreen) {
+ // click into main screen
+ if (_activeVerb == kVerbGive && _activeObject) {
+ int actor = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
+ if (actor != 0)
+ obj = OBJECT_V0(actor, kObjectV0TypeActor);
+ } else {
+ obj = findObject(_virtualMouse.x, _virtualMouse.y);
}
}
- }
-
- if (obj && obj != _activeObject) {
- if (!_activeObject)
- if (_activeInventory)
- _activeInvExecute = true;
- // USE
- if (_activeVerb == 11 || _activeVerb == 8) {
- if (obj != _activeObject || obj != _activeObject2) {
- if (!_activeObject || _activeInventory) {
+ if (!obj) {
+ if (_activeVerb == kVerbWalkTo) {
+ _activeObject = 0;
+ _activeObject2 = 0;
+ }
+ } else {
+ if (activeVerbPrep() == kVerbPrepNone) {
+ if (obj == _activeObject)
+ execute = true;
+ else
_activeObject = obj;
- _activeObjectIndex = objIdx;
- return;
- } else {
- if (_activeObject2 != obj) {
- _activeObject2 = obj;
- _activeObject2Index = objIdx;
- return;
- }
+ // immediately execute action in keypad/selection mode
+ if (_currentMode == kModeKeypad)
+ execute = true;
+ } else {
+ if (obj == _activeObject2)
+ execute = true;
+ if (obj != _activeObject) {
+ _activeObject2 = obj;
+ if (_currentMode == kModeKeypad)
+ execute = true;
}
}
- } else {
- a->stopActorMoving();
-
- _activeObject = obj;
- _activeObjectIndex = objIdx;
-
- if (_activeVerb != 13)
- return;
+ }
- //return;
+ _redrawSentenceLine = true;
+ if (_activeVerb == kVerbWalkTo && zone->number == kMainVirtScreen) {
+ _walkToObjectState = kWalkToObjectStateDone;
+ execute = true;
}
}
}
+ }
- _verbExecuting = true;
+ if (_redrawSentenceLine)
+ drawSentenceLine();
- } // mouse k/b action
+ if (!execute || !_activeVerb)
+ return;
+
+ if (_activeVerb == kVerbWalkTo)
+ verbExec();
+ else if (_activeObject) {
+ // execute if we have a 1st object and either have or do not need a 2nd
+ if (activeVerbPrep() == kVerbPrepNone || _activeObject2)
+ verbExec();
+ }
}
void ScummEngine::verbMouseOver(int verb) {
// Don't do anything unless verbs are active
- if (_game.version <= 2 && !(_userState & 128))
+ if (_game.version <= 2 && !(_userState & USERSTATE_IFACE_VERBS))
return;
if (_game.id == GID_FT)
diff --git a/engines/scumm/verbs.h b/engines/scumm/verbs.h
index fb4dc969e2..0aa008b4de 100644
--- a/engines/scumm/verbs.h
+++ b/engines/scumm/verbs.h
@@ -57,6 +57,34 @@ struct VerbSlot {
uint16 imgindex;
};
+enum VerbsV0 {
+ kVerbNone = 0,
+ kVerbOpen = 1,
+ kVerbClose = 2,
+ kVerbGive = 3,
+ kVerbTurnOn = 4,
+ kVerbTurnOff = 5,
+ kVerbFix = 6,
+ kVerbNewKid = 7,
+ kVerbUnlock = 8,
+ kVerbPush = 9,
+ kVerbPull = 10,
+ kVerbUse = 11,
+ kVerbRead = 12,
+ kVerbWalkTo = 13,
+ kVerbPickUp = 14,
+ kVerbWhatIs = 15
+};
+
+enum VerbPrepsV0 {
+ kVerbPrepNone = 0,
+ kVerbPrepIn = 1,
+ kVerbPrepWith = 2,
+ kVerbPrepOn = 3,
+ kVerbPrepTo = 4,
+ kVerbPrepObject = 0xFF // prep depends on object (USE)
+};
+
} // End of namespace Scumm
#endif