diff options
author | Eugene Sandulenko | 2012-02-11 00:21:52 -0800 |
---|---|---|
committer | Eugene Sandulenko | 2012-02-11 00:21:52 -0800 |
commit | 866e23f426a03c36a9d82208a354f721004a7d4e (patch) | |
tree | 210b77a01b1f6e0f2a33848afab0011fe1360847 /engines/scumm | |
parent | edc52497726139498bb68e3b3334a792b3602ede (diff) | |
parent | 65fc7225bb31e72120dcdf16e91ae377c657548d (diff) | |
download | scummvm-rg350-866e23f426a03c36a9d82208a354f721004a7d4e.tar.gz scummvm-rg350-866e23f426a03c36a9d82208a354f721004a7d4e.tar.bz2 scummvm-rg350-866e23f426a03c36a9d82208a354f721004a7d4e.zip |
Merge pull request #178 from tobigun/mm-c64-final
SCUMM: support for MM C64
Diffstat (limited to 'engines/scumm')
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 |