diff options
Diffstat (limited to 'engines/scumm')
55 files changed, 2637 insertions, 1604 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index eb23c30ebe..b8722b6963 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,64 @@ 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(89)), + MKLINE(Actor_v0, _miscflags, sleByte, VER(84)), + MKLINE(Actor_v0, _speaking, sleByte, VER(84)), + MK_OBSOLETE(Actor_v0, _speakingPrev, sleByte, VER(84), VER(89)), + MK_OBSOLETE(Actor_v0, _limbTemp, sleByte, VER(89), VER(89)), + MKLINE(Actor_v0, _animFrameRepeat, sleByte, VER(89)), + MKARRAY(Actor_v0, _limbFrameRepeatNew[0], sleInt8, 8, VER(89)), + MKARRAY(Actor_v0, _limbFrameRepeat[0], sleInt8, 8, VER(90)), 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..6e7e9ff688 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; @@ -676,7 +668,7 @@ void ClassicCostumeRenderer::procPCEngine(Codec1 &v1) { (v1.mask_ptr && (mask[0] & maskbit)); if (pcolor && !masked) { - WRITE_UINT16(dst, ((uint16*)_palette)[pcolor]); + WRITE_UINT16(dst, ((uint16 *)_palette)[pcolor]); } xPos += xStep; @@ -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/cursor.cpp b/engines/scumm/cursor.cpp index 36f06a4889..42f11498d9 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -636,7 +636,7 @@ void ScummEngine_v5::setBuiltinCursor(int idx) { byte *dst2 = (_textSurfaceMultiplier == 2) ? dst1 + 16 * scl : dst1; if (_outputPixelFormat.bytesPerPixel == 2) { for (int b = 0; b < scl; b += 2) { - *((uint16*)dst1) = *((uint16*)dst2) = color; + *((uint16 *)dst1) = *((uint16 *)dst2) = color; dst1 += 2; dst2 += 2; } 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/detection.cpp b/engines/scumm/detection.cpp index 2ae994040b..b47982af00 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -585,7 +585,6 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul // Print some debug info int filesize = tmp->size(); - if (d.md5Entry->filesize != filesize) debug(1, "SCUMM detector found matching file '%s' with MD5 %s, size %d\n", file.c_str(), md5str.c_str(), filesize); @@ -599,7 +598,7 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul } if (isDiskImg) - closeDiskImage((ScummDiskImage*)tmp); + closeDiskImage((ScummDiskImage *)tmp); delete tmp; } @@ -972,9 +971,6 @@ GameList ScummMetaEngine::detectGames(const Common::FSList &fslist) const { ::detectGames(fslist, results, 0); - // TODO: We still don't handle the FM-TOWNS demos (like zakloom) very well. - // In particular, they are detected as ZakTowns, which is bad. - for (Common::List<DetectorResult>::iterator x = results.begin(); x != results.end(); ++x) { const PlainGameDescriptor *g = findPlainGameDescriptor(x->game.gameid, gameDescriptions); @@ -988,26 +984,6 @@ GameList ScummMetaEngine::detectGames(const Common::FSList &fslist) const { // Based on generateComplexID() in advancedDetector.cpp. dg["preferredtarget"] = generatePreferredTarget(*x); - // HACK: Detect and distinguish the FM-TOWNS demos - if (x->game.platform == Common::kPlatformFMTowns && (x->game.features & GF_DEMO)) { - if (x->md5 == "2d388339d6050d8ccaa757b64633954e") { - // Indy + Loom demo - dg.description() = "Indiana Jones and the Last Crusade & Loom"; - dg.updateDesc(x->extra); - dg["preferredtarget"] = "indyloom"; - } else if (x->md5 == "77f5c9cc0986eb729c1a6b4c8823bbae") { - // Zak + Loom demo - dg.description() = "Zak McKracken & Loom"; - dg.updateDesc(x->extra); - dg["preferredtarget"] = "zakloom"; - } else if (x->md5 == "3938ee1aa4433fca9d9308c9891172b1") { - // Indy + Zak demo - dg.description() = "Indiana Jones and the Last Crusade & Zak McKracken"; - dg.updateDesc(x->extra); - dg["preferredtarget"] = "indyzak"; - } - } - dg.setGUIOptions(x->game.guioptions + MidiDriver::musicType2GUIO(x->game.midi)); dg.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language)); diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h index ad8b3cec12..b6dfa757bb 100644 --- a/engines/scumm/detection.h +++ b/engines/scumm/detection.h @@ -23,7 +23,8 @@ #ifndef SCUMM_DETECTION_H #define SCUMM_DETECTION_H -#include "common/util.h" +#include "common/language.h" +#include "common/platform.h" namespace Scumm { diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index cd055a5b78..5b222a51b6 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -24,6 +24,7 @@ #define SCUMM_DETECTION_TABLES_H #include "engines/obsolete.h" +#include "common/gui_options.h" #include "common/rect.h" #include "common/util.h" @@ -70,6 +71,9 @@ static const PlainGameDescriptor gameDescriptions[] = { { "samnmax", "Sam & Max Hit the Road" }, { "tentacle", "Day of the Tentacle" }, { "zak", "Zak McKracken and the Alien Mindbenders" }, + { "indyloom", "Indiana Jones and the Last Crusade & Loom" }, + { "indyzak", "Indiana Jones and the Last Crusade & Zak McKracken" }, + { "zakloom", "Zak McKracken & Loom" }, #ifdef ENABLE_SCUMM_7_8 { "ft", "Full Throttle" }, @@ -210,6 +214,9 @@ static const GameSettings gameVariantsTable[] = { {"zak", "V1", "v1", GID_ZAK, 1, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"zak", "V2", "v2", GID_ZAK, 2, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"zak", "FM-TOWNS", 0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, + {"zakloom", "FM-TOWNS", 0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, + {"indyloom", "FM-TOWNS", 0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, + {"indyzak", "FM-TOWNS", 0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, {"indy3", "EGA", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, 0, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"indy3", "No AdLib", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, @@ -437,6 +444,10 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "indy3", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, + { "indyloom", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, + { "indyzak", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, + { "zakloom", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, + { "loom", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, { "loom", "%03d.LFL", kGenRoomNum, UNK_LANG, UNK, "VGA" }, // Loom CD @@ -541,7 +552,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "freddicove", "freddicove", kGenHEPC, UNK_LANG, UNK, 0 }, { "freddicove", "FreddiCCC", kGenHEPC, UNK_LANG, UNK, 0 }, { "freddicove", "FreddiCove", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, - { "freddicove", "FreddiDZZ", kGenHEPC, Common::NL_NLD, UNK, 0 }, + { "freddicove", "FreddiDZZ", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 }, { "freddicove", "FreddiDZZ", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "freddicove", "FreddiMML", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "freddicove", "FreddiMML", kGenHEMac, Common::FR_FRA, Common::kPlatformMacintosh, 0 }, @@ -586,6 +597,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "balloon", "balloon", kGenHEPC, UNK_LANG, UNK, 0 }, { "balloon", "Balloon-O-Rama", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, + { "balloon", "Ballon-O-Rama", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "baseball", "baseball", kGenHEPC, UNK_LANG, UNK, 0 }, { "baseball", "BaseBall", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -622,8 +634,9 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "chase", "chase", kGenHEPC, UNK_LANG, UNK, 0 }, { "chase", "Cheese Chase", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, - { "dog", "dog", kGenHEPC, UNK_LANG, UNK, 0 }, + { "dog", "dog", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "dog", "Dog on a Stick", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, + { "dog", "Springparadijs", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "farm", "farm", kGenHEPC, UNK_LANG, UNK, 0 }, { "farm", "farmdemo", kGenHEPC, UNK_LANG, UNK, 0 }, @@ -637,6 +650,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "freddi", "freddi", kGenHEPC, UNK_LANG, UNK, 0 }, { "freddi", "Freddi", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi", "Freddi1", kGenHEPC, UNK_LANG, UNK, 0 }, + { "freddi", "Freddemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi", "freddemo", kGenHEPC, UNK_LANG, UNK, 0 }, { "freddi", "Freddi Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi", "Freddi Fish", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -647,7 +661,8 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "freddi", "MM-DEMO", kGenHEPC, UNK_LANG, UNK, 0 }, { "freddi2", "freddi2", kGenHEPC, UNK_LANG, UNK, 0 }, - { "freddi2", "ff2-demo", kGenHEPC, UNK_LANG, UNK, 0 }, + { "freddi2", "FF2-demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, + { "freddi2", "ff2-demo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "freddi2", "FFHSDemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi2", "FFHSDemo", kGenHEPC, UNK_LANG, UNK, 0 }, { "freddi2", "Freddi Fish 2 Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -657,10 +672,12 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "freddi2", "Fritzi Fisch 2", kGenHEMac, Common::DE_DEU, Common::kPlatformMacintosh, 0 }, { "freddi2", "MALICE2", kGenHEMac, Common::FR_FRA, Common::kPlatformMacintosh, 0 }, - { "freddi3", "freddi3", kGenHEPC, UNK_LANG, UNK, 0 }, + { "freddi3", "freddi3", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "freddi3", "F3-Mdemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi3", "f3-mdemo", kGenHEPC, UNK_LANG, UNK, 0 }, - { "freddi3", "FF3-DEMO", kGenHEPC, UNK_LANG, UNK, 0 }, + { "freddi3", "FF3 Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, + { "freddi3", "FF3-demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, + { "freddi3", "FF3-DEMO", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "freddi3", "FF3DEMO", kGenHEPC, Common::HE_ISR, UNK, 0 }, { "freddi3", "Freddi 3", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "freddi3", "Freddi Fish 3", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -677,10 +694,10 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "freddi3", "MM3-DEMO", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "freddi3", "MM3-Demo", kGenHEMac, Common::FR_FRA, Common::kPlatformMacintosh, 0 }, - { "freddi4", "freddi4", kGenHEPC, UNK_LANG, UNK, 0 }, + { "freddi4", "freddi4", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "freddi4", "Freddi4", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "freddi4", "f4-demo", kGenHEPC, UNK_LANG, UNK, 0 }, - { "freddi4", "ff4demo", kGenHEPC, UNK_LANG, UNK, 0 }, + { "freddi4", "ff4demo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "freddi4", "Ff4demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi4", "Freddi 4", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi4", "Freddi 4 Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -692,7 +709,8 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "freddi4", "MaliceMRC", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "freddi4", "Mm4demo", kGenHEPC, Common::FR_FRA, UNK, 0 }, - { "FreddisFunShop", "FreddisFunShop", kGenHEPC, UNK_LANG, UNK, 0 }, + { "FreddisFunShop", "FreddisFunShop", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, + { "FreddisFunShop", "FreddisFunShop", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "FreddisFunShop", "Freddi's FunShop", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "jungle", "jungle", kGenHEPC, UNK_LANG, UNK, 0 }, @@ -705,7 +723,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "lost", "Verloren", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "maze", "maze", kGenHEPC, UNK_LANG, UNK, 0 }, - { "maze", "Doolhof", kGenHEPC, Common::NL_NLD, UNK, 0 }, + { "maze", "Doolhof", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 }, { "maze", "Doolhof", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "maze", "Maze Madness", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -716,7 +734,8 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "pajama", "Pyjama Pit", kGenHEMac, Common::DE_DEU, Common::kPlatformMacintosh, 0 }, { "pajama", "Pajama Sam", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "pajama", "PajamaNHD", kGenHEPC, UNK_LANG, UNK, 0 }, - { "pajama", "PJS-DEMO", kGenHEPC, UNK_LANG, UNK, 0 }, + { "pajama", "PJS-DEMO", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, + { "pajama", "PJS-DEMO", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "pajama", "pjsam", kGenHEPC, UNK_LANG, UNK, 0 }, { "pajama", "PjSamDemo", kGenHEPC, UNK_LANG, UNK, 0 }, { "pajama", "PYJAMA", kGenHEPC, Common::DE_DEU, UNK, 0 }, @@ -725,7 +744,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "pajama", "sampyjam", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "pajama", "SamPyjam", kGenHEMac, Common::FR_FRA, Common::kPlatformMacintosh, 0 }, - { "pajama2", "pajama2", kGenHEPC, UNK_LANG, UNK, 0 }, + { "pajama2", "pajama2", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "pajama2", "Pajama2", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "pajama2", "pyjam2", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "pajama2", "Pajama Sam 2", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -740,7 +759,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "pajama2", "PJP2DEMO", kGenHEPC, Common::DE_DEU, UNK, 0 }, { "pajama2", "PJ2Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "pajama2", "pj2demo", kGenHEPC, UNK_LANG, UNK, 0 }, - { "pajama2", "Pjs2demo", kGenHEPC, UNK_LANG, UNK, 0 }, + { "pajama2", "Pjs2demo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "pajama2", "PJ2 Demo", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "pajama2", "PS2DEMO", kGenHEPC, Common::HE_ISR, UNK, 0 }, @@ -751,7 +770,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "pajama3", "Pajama Sam 3", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "pajama3", "Pajama Sam 3-Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "pajama3", "pj3-demo", kGenHEPC, UNK_LANG, UNK, 0 }, - { "pajama3", "pj3demo", kGenHEPC, UNK_LANG, UNK, 0 }, + { "pajama3", "pj3demo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "pajama3", "PJ3Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "pajama3", "Pajama Sam Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "pajama3", "PJMini", kGenHEPC, UNK_LANG, UNK, 0 }, @@ -775,14 +794,14 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "puttcircus", "ToffToffGZZ", kGenHEPC, Common::DE_DEU, UNK, 0 }, { "puttrace", "puttrace", kGenHEPC, UNK_LANG, UNK, 0 }, - { "puttrace", "500demo", kGenHEPC, Common::NL_NLD, UNK, 0 }, + { "puttrace", "500demo", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 }, { "puttrace", "course", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "puttrace", "CourseDemo", kGenHEPC, Common::FR_FRA, UNK, 0 }, - { "puttrace", "racedemo", kGenHEPC, UNK_LANG, UNK, 0 }, + { "puttrace", "racedemo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "puttrace", "RaceDemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "puttrace", "Rennen", kGenHEPC, Common::DE_DEU, UNK, 0 }, { "puttrace", "PouceCourse", kGenHEPC, Common::FR_FRA, UNK, 0 }, - { "puttrace", "Putt500", kGenHEPC, Common::NL_NLD, UNK, 0 }, + { "puttrace", "Putt500", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 }, { "puttrace", "Putt500", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "puttrace", "Putt500 demo", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "puttrace", "Putt Race", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -796,18 +815,19 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "PuttsFunShop", "PuttsFunShop", kGenHEPC, UNK_LANG, UNK, 0 }, { "PuttsFunShop", "Putt's FunShop", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, - { "putttime", "putttime", kGenHEPC, UNK_LANG, UNK, 0 }, + { "putttime", "putttime", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "putttime", "PuttTime", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "putttime", "pouce", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "putttime", "Pouce-Pouce", kGenHEMac, Common::FR_FRA, Common::kPlatformMacintosh, 0 }, { "putttime", "PuttPuttTTT", kGenHEPC, UNK_LANG, UNK, 0 }, { "putttime", "PuttPuttTTT", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, - { "putttime", "PuttTijd", kGenHEPC, Common::NL_NLD, UNK, 0 }, + { "putttime", "PuttTijd", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 }, { "putttime", "PuttTijd", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "putttime", "Putt Time", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "putttime", "PuttTTT", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "putttime", "PuttTTT", kGenHEPC, UNK_LANG, UNK, 0 }, - { "putttime", "TIJDDEMO", kGenHEPC, UNK_LANG, UNK, 0 }, + { "putttime", "TIJDDEMO", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 }, + { "putttime", "TijdDemo", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "putttime", "timedemo", kGenHEPC, UNK_LANG, UNK, 0 }, { "putttime", "TimeDemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "putttime", "TEMPDEMO", kGenHEPC, Common::FR_FRA, UNK, 0 }, @@ -827,6 +847,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "puttzoo", "T\xD6""FFZOO", kGenHEPC, Common::DE_DEU, UNK, 0 }, // Windows encoding { "puttzoo", "T\xC3\xB6""ff-T\xC3\xB6""ff\xE2\x84\xA2 Zoo Demo", kGenHEMac, Common::DE_DEU, Common::kPlatformMacintosh, 0 }, // UTF-8 encoding { "puttzoo", "T\xF6""ff-T""\xF6""ff\x99 Zoo Demo", kGenHEMac, Common::DE_DEU, Common::kPlatformMacintosh, 0 }, // Windows encoding + { "puttzoo", "Zoodemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "puttzoo", "zoodemo", kGenHEPC, UNK_LANG, UNK, 0 }, { "puttzoo", "Zoo Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "puttzoo", "Putt-Putt Saves the Zoo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -843,15 +864,15 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "socks", "SokkenSoep", kGenHEPC, Common::NL_NLD, UNK, 0 }, { "socks", "SokkenSoep", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, - { "spyfox", "spyfox", kGenHEPC, UNK_LANG, UNK, 0 }, + { "spyfox", "spyfox", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "spyfox", "Fuchsdem", kGenHEMac, Common::DE_DEU, Common::kPlatformMacintosh, 0 }, { "spyfox", "FUCHSDEM", kGenHEPC, Common::DE_DEU, UNK, 0 }, { "spyfox", "FoxDemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, - { "spyfox", "foxdemo", kGenHEPC, UNK_LANG, UNK, 0 }, + { "spyfox", "foxdemo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "spyfox", "JAMESDEM", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "spyfox", "renard", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "spyfox", "Spydemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, - { "spyfox", "Spydemo", kGenHEPC, UNK_LANG, UNK, 0 }, + { "spyfox", "Spydemo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "spyfox", "SPYFox", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "spyfox", "SPYFoxDC", kGenHEPC, UNK_LANG, UNK, 0 }, { "spyfox", "SPYFoxDC", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -866,7 +887,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "spyfox2", "spyfox2", kGenHEPC, UNK_LANG, UNK, 0 }, { "spyfox2", "sf2-demo", kGenHEPC, UNK_LANG, UNK, 0 }, - { "spyfox2", "sf2demo", kGenHEPC, UNK_LANG, UNK, 0 }, + { "spyfox2", "sf2demo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "spyfox2", "Sf2demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "spyfox2", "Spy Fox 2", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "spyfox2", "Spy Fox 2 - Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -888,7 +909,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "thinkerk", "thinkerk", kGenHEPC, UNK_LANG, UNK, 0 }, { "thinkerk", "ThinkerK", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, - { "water", "water", kGenHEPC, UNK_LANG, UNK, 0 }, + { "water", "water", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "water", "Water", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "water", "Water Worries", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, #endif diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 20aedae089..0e531daf73 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -648,4 +648,38 @@ void DebugInputDialog::handleKeyDown(Common::KeyState state) { } } +LoomTownsDifficultyDialog::LoomTownsDifficultyDialog() + : Dialog("LoomTownsDifficultyDialog"), _difficulty(-1) { + GUI::StaticTextWidget *text1 = new GUI::StaticTextWidget(this, "LoomTownsDifficultyDialog.Description1", _("Select a Proficiency Level.")); + text1->setAlign(Graphics::kTextAlignCenter); + GUI::StaticTextWidget *text2 = new GUI::StaticTextWidget(this, "LoomTownsDifficultyDialog.Description2", _("Refer to your Loom(TM) manual for help.")); + text2->setAlign(Graphics::kTextAlignCenter); + + new GUI::ButtonWidget(this, "LoomTownsDifficultyDialog.Standard", _("Standard"), 0, kStandardCmd); + new GUI::ButtonWidget(this, "LoomTownsDifficultyDialog.Practice", _("Practice"), 0, kPracticeCmd); + new GUI::ButtonWidget(this, "LoomTownsDifficultyDialog.Expert", _("Expert"), 0, kExpertCmd); +} + +void LoomTownsDifficultyDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { + switch (cmd) { + case kStandardCmd: + _difficulty = 1; + close(); + break; + + case kPracticeCmd: + _difficulty = 0; + close(); + break; + + case kExpertCmd: + _difficulty = 2; + close(); + break; + + default: + GUI::Dialog::handleCommand(sender, cmd, data); + } +} + } // End of namespace Scumm diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h index c26aa9f414..7977f123ed 100644 --- a/engines/scumm/dialogs.h +++ b/engines/scumm/dialogs.h @@ -187,6 +187,27 @@ public: Common::String mainText; }; +/** + * Difficulty selection dialog for Loom FM-Towns. + */ +class LoomTownsDifficultyDialog : public GUI::Dialog { +public: + LoomTownsDifficultyDialog(); + + int getSelectedDifficulty() const { return _difficulty; } +protected: + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + +private: + enum { + kStandardCmd = 'STDD', + kPracticeCmd = 'PRAD', + kExpertCmd = 'EXPD' + }; + + int _difficulty; +}; + } // End of namespace Scumm #endif diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index a22aa1802f..2cf4a429db 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; } @@ -679,11 +680,10 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i srcPtr += vsPitch; textPtr += _textSurface.pitch - width * m; } - } + } else { #ifdef USE_ARM_GFX_ASM - asmDrawStripToScreen(height, width, text, src, _compositeBuf, vs->pitch, width, _textSurface.pitch); + asmDrawStripToScreen(height, width, text, src, _compositeBuf, vs->pitch, width, _textSurface.pitch); #else - else { // We blit four pixels at a time, for improved performance. const uint32 *src32 = (const uint32 *)src; uint32 *dst32 = (uint32 *)_compositeBuf; @@ -714,8 +714,8 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i src32 += vsPitch; text32 += textPitch; } - } #endif + } src = _compositeBuf; pitch = width * vs->format.bytesPerPixel; @@ -1135,7 +1135,7 @@ void ScummEngine::clearTextSurface() { _townsScreen->fillLayerRect(1, 0, 0, _textSurface.w, _textSurface.h, 0); #endif - fill((byte*)_textSurface.pixels, _textSurface.pitch, + fill((byte *)_textSurface.pixels, _textSurface.pitch, #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE _game.platform == Common::kPlatformFMTowns ? 0 : #endif @@ -1487,15 +1487,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 +1540,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 +1927,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 +2070,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, @@ -2770,7 +2772,7 @@ void GdiNES::drawStripNESMask(byte *dst, int stripnr, int top, int height) const void readOffsetTable(const byte *ptr, uint16 **table, int *count) { int pos = 0; *count = READ_LE_UINT16(ptr) / 2 + 1; - *table = (uint16*)malloc(*count * sizeof(uint16)); + *table = (uint16 *)malloc(*count * sizeof(uint16)); for (int i = 0; i < *count; i++) { (*table)[i] = READ_LE_UINT16(ptr + pos) + pos + 2; pos += 2; @@ -2974,10 +2976,10 @@ void GdiPCEngine::decodePCEngineTileData(const byte *ptr) { if (_distaff) { free(_PCE.staffTiles); - _PCE.staffTiles = (byte*)calloc(_PCE.numTiles * 8 * 8, sizeof(byte)); + _PCE.staffTiles = (byte *)calloc(_PCE.numTiles * 8 * 8, sizeof(byte)); } else { free(_PCE.roomTiles); - _PCE.roomTiles = (byte*)calloc(_PCE.numTiles * 8 * 8, sizeof(byte)); + _PCE.roomTiles = (byte *)calloc(_PCE.numTiles * 8 * 8, sizeof(byte)); } for (int i = 0; i < _PCE.numTiles; ++i) { @@ -3020,7 +3022,7 @@ void GdiPCEngine::decodePCEngineMaskData(const byte *ptr) { readOffsetTable(ptr, &maskOffsets, &_PCE.numMasks); free(_PCE.masks); - _PCE.masks = (byte*)malloc(_PCE.numMasks * 8 * sizeof(byte)); + _PCE.masks = (byte *)malloc(_PCE.numMasks * 8 * sizeof(byte)); for (int i = 0; i < _PCE.numMasks; ++i) { mask = &_PCE.masks[i * 8]; @@ -3086,67 +3088,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/gfxARM.s b/engines/scumm/gfxARM.s index 92f8951466..9238888831 100644 --- a/engines/scumm/gfxARM.s +++ b/engines/scumm/gfxARM.s @@ -59,10 +59,6 @@ _asmDrawStripToScreen: CMP r1,#4 @ If width<4 BLT end @ return - @ Width &= ~4 ? What''s that about then? Width &= ~3 I could have - @ understood... - BIC r1,r1,#4 - SUB r5,r5,r1 @ vsPitch -= width SUB r6,r6,r1 @ vmScreenWidth -= width SUB r7,r7,r1 @ textSurfacePitch -= width diff --git a/engines/scumm/gfx_towns.cpp b/engines/scumm/gfx_towns.cpp index 6a3f50a1af..f86a4e56d5 100644 --- a/engines/scumm/gfx_towns.cpp +++ b/engines/scumm/gfx_towns.cpp @@ -39,7 +39,7 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in int m = _textSurfaceMultiplier; uint8 *src1 = vs->getPixels(srcX, srcY); - uint8 *src2 = (uint8*)_textSurface.getBasePtr(srcX * m, (srcY + vs->topline - _screenTop) * m); + uint8 *src2 = (uint8 *)_textSurface.getBasePtr(srcX * m, (srcY + vs->topline - _screenTop) * m); uint8 *dst1 = _townsScreen->getLayerPixels(0, dstX, dstY); uint8 *dst2 = _townsScreen->getLayerPixels(1, dstX * m, dstY * m); @@ -52,7 +52,7 @@ void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, in for (int h = 0; h < height; ++h) { if (_outputPixelFormat.bytesPerPixel == 2) { for (int w = 0; w < width; ++w) { - *(uint16*)dst1 = _16BitPalette[*src1++]; + *(uint16 *)dst1 = _16BitPalette[*src1++]; dst1 += _outputPixelFormat.bytesPerPixel; } @@ -245,7 +245,7 @@ void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void l->numCol = numCol; l->bpp = ((numCol - 1) & 0xff00) ? 2 : 1; l->pitch = width * l->bpp; - l->palette = (uint8*)pal; + l->palette = (uint8 *)pal; if (l->palette && _pixelFormat.bytesPerPixel == 1) warning("TownsScreen::setupLayer(): Layer palette usage requires 16 bit graphics setting.\nLayer palette will be ignored."); @@ -271,7 +271,7 @@ void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void l->enabled = true; _layers[0].onBottom = true; - _layers[1].onBottom = _layers[0].enabled ? false : true; + _layers[1].onBottom = !_layers[0].enabled; l->ready = true; } @@ -304,7 +304,7 @@ void TownsScreen::fillLayerRect(int layer, int x, int y, int w, int h, int col) for (int i = 0; i < h; ++i) { if (l->bpp == 2) { for (int ii = 0; ii < w; ++ii) { - *(uint16*)pos = col; + *(uint16 *)pos = col; pos += 2; } pos += (l->pitch - w * 2); @@ -424,7 +424,7 @@ void TownsScreen::toggleLayers(int flag) { _layers[0].enabled = (flag & 1) ? true : false; _layers[0].onBottom = true; _layers[1].enabled = (flag & 2) ? true : false; - _layers[1].onBottom = _layers[0].enabled ? false : true; + _layers[1].onBottom = !_layers[0].enabled; _dirtyRects.clear(); _dirtyRects.push_back(Common::Rect(_width - 1, _height - 1)); @@ -472,10 +472,10 @@ void TownsScreen::updateOutputBuffer() { if (col || l->onBottom) { if (l->numCol == 16) col = (col >> 4) & (col & 0x0f); - *(uint16*)dst = l->bltTmpPal[col]; + *(uint16 *)dst = l->bltTmpPal[col]; } } else { - *(uint16*)dst = *(uint16*)src; + *(uint16 *)dst = *(uint16 *)src; } dst += 2; } diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp index 85e2a2f1dd..1007d2a7b0 100644 --- a/engines/scumm/he/sound_he.cpp +++ b/engines/scumm/he/sound_he.cpp @@ -652,7 +652,7 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags) * even addresses, so the use of (void *) in the * following cast shuts the compiler from warning * unnecessarily. */ - size = voxStream->readBuffer((int16*)(void *)sound, size * 2); + size = voxStream->readBuffer((int16 *)(void *)sound, size * 2); size *= 2; // 16bits. delete voxStream; diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 3995aba64e..798f703db6 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -687,7 +687,7 @@ void Wiz::copyRawWizImage(uint8 *dst, const uint8 *src, int dstPitch, int dstTyp } #ifdef USE_RGB_COLOR -template <int type> +template<int type> void Wiz::write16BitColor(uint8 *dstPtr, const uint8 *dataPtr, int dstType, const uint8 *xmapPtr) { uint16 col = READ_LE_UINT16(dataPtr); if (type == kWizXMap) { @@ -701,7 +701,7 @@ void Wiz::write16BitColor(uint8 *dstPtr, const uint8 *dataPtr, int dstType, cons } } -template <int type> +template<int type> void Wiz::decompress16BitWizImage(uint8 *dst, int dstPitch, int dstType, const uint8 *src, const Common::Rect &srcRect, int flags, const uint8 *xmapPtr) { const uint8 *dataPtr, *dataPtrNext; uint8 code; @@ -804,7 +804,7 @@ void Wiz::decompress16BitWizImage(uint8 *dst, int dstPitch, int dstType, const u } #endif -template <int type> +template<int type> void Wiz::write8BitColor(uint8 *dstPtr, const uint8 *dataPtr, int dstType, const uint8 *palPtr, const uint8 *xmapPtr, uint8 bitDepth) { if (bitDepth == 2) { if (type == kWizXMap) { @@ -833,7 +833,7 @@ void Wiz::write8BitColor(uint8 *dstPtr, const uint8 *dataPtr, int dstType, const } } -template <int type> +template<int type> void Wiz::decompressWizImage(uint8 *dst, int dstPitch, int dstType, const uint8 *src, const Common::Rect &srcRect, int flags, const uint8 *palPtr, const uint8 *xmapPtr, uint8 bitDepth) { const uint8 *dataPtr, *dataPtrNext; uint8 code, *dstPtr, *dstPtrNext; @@ -942,7 +942,7 @@ template void Wiz::decompressWizImage<kWizXMap>(uint8 *dst, int dstPitch, int ds template void Wiz::decompressWizImage<kWizRMap>(uint8 *dst, int dstPitch, int dstType, const uint8 *src, const Common::Rect &srcRect, int flags, const uint8 *palPtr, const uint8 *xmapPtr, uint8 bitDepth); template void Wiz::decompressWizImage<kWizCopy>(uint8 *dst, int dstPitch, int dstType, const uint8 *src, const Common::Rect &srcRect, int flags, const uint8 *palPtr, const uint8 *xmapPtr, uint8 bitDepth); -template <int type> +template<int type> void Wiz::decompressRawWizImage(uint8 *dst, int dstPitch, int dstType, const uint8 *src, int srcPitch, int w, int h, int transColor, const uint8 *palPtr, uint8 bitDepth) { if (type == kWizRMap) { assert(palPtr != 0); diff --git a/engines/scumm/help.h b/engines/scumm/help.h index 5ba6bdc65c..a3948566c4 100644 --- a/engines/scumm/help.h +++ b/engines/scumm/help.h @@ -24,6 +24,7 @@ #define SCUMM_HELP_H #include "common/str.h" +#include "common/platform.h" namespace Scumm { diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 781ca30459..1f219f5187 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -34,6 +34,7 @@ MODULE_OBJS := \ midiparser_ro.o \ object.o \ palette.o \ + player_apple2.o \ player_mod.o \ player_nes.o \ player_pce.o \ 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.cpp b/engines/scumm/player_apple2.cpp new file mode 100644 index 0000000000..a8e150caa9 --- /dev/null +++ b/engines/scumm/player_apple2.cpp @@ -0,0 +1,500 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "engines/engine.h" +#include "scumm/player_apple2.h" +#include "scumm/scumm.h" + +namespace Scumm { + +/************************************ + * Apple-II sound-resource parsers + ************************************/ + +/* + * SoundFunction1: frequency up/down + */ +class AppleII_SoundFunction1_FreqUpDown : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _delta = params[0]; + _count = params[1]; + _interval = params[2]; + _limit = params[3]; + _decInterval = (params[4] >= 0x40); + } + + virtual bool update() { // D085 + if (_decInterval) { + do { + _update(_interval, _count); + _interval -= _delta; + } while (_interval >= _limit); + } else { + do { + _update(_interval, _count); + _interval += _delta; + } while (_interval < _limit); + } + return true; + } + +private: + void _update(int interval /*a*/, int count /*y*/) { // D076 + assert(interval > 0); // 0 == 256? + assert(count > 0); // 0 == 256? + + for (; count >= 0; --count) { + _player->speakerToggle(); + _player->generateSamples(17 + 5 * interval); + } + } + +protected: + int _delta; + int _count; + byte _interval; // must be unsigned byte ("interval < delta" possible) + int _limit; + bool _decInterval; +}; + +/* + * SoundFunction2: symmetric wave (~) + */ +class AppleII_SoundFunction2_SymmetricWave : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _params = params; + _pos = 1; + } + + virtual bool update() { // D0D6 + // while (pos = 1; pos < 256; ++pos) + if (_pos < 256) { + byte interval = _params[_pos]; + if (interval == 0xFF) + return true; + _update(interval, _params[0] /*, LD12F=interval*/); + + ++_pos; + return false; + } + return true; + } + +private: + void _update(int interval /*a*/, int count) { // D0EF + if (interval == 0xFE) { + _player->wait(interval, 10); + } else { + assert(count > 0); // 0 == 256? + assert(interval > 0); // 0 == 256? + + int a = (interval >> 3) + count; + for (int y = a; y > 0; --y) { + _player->generateSamples(1292 - 5*interval); + _player->speakerToggle(); + + _player->generateSamples(1287 - 5*interval); + _player->speakerToggle(); + } + } + } + +protected: + const byte *_params; + int _pos; +}; + +/* + * SoundFunction3: asymmetric wave (__-) + */ +class AppleII_SoundFunction3_AsymmetricWave : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _params = params; + _pos = 1; + } + + virtual bool update() { // D132 + // while (pos = 1; pos < 256; ++pos) + if (_pos < 256) { + byte interval = _params[_pos]; + if (interval == 0xFF) + return true; + _update(interval, _params[0]); + + ++_pos; + return false; + } + return true; + } + +private: + void _update(int interval /*a*/, int count /*LD12D*/) { // D14B + if (interval == 0xFE) { + _player->wait(interval, 70); + } else { + assert(interval > 0); // 0 == 256? + assert(count > 0); // 0 == 256? + + for (int y = count; y > 0; --y) { + _player->generateSamples(1289 - 5*interval); + _player->speakerToggle(); + } + } + } + +protected: + const byte *_params; + int _pos; +}; + +/* + * SoundFunction4: polyphone (2 voices) + */ +class AppleII_SoundFunction4_Polyphone : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _params = params; + _updateRemain1 = 80; + _updateRemain2 = 10; + _count = 0; + } + + virtual bool update() { // D170 + // while (_params[0] != 0x01) + if (_params[0] != 0x01) { + if (_count == 0) // prepare next loop + nextLoop(_params[0], _params[1], _params[2]); + if (loopIteration()) // loop finished -> fetch next parameter set + _params += 3; + return false; + } + return true; + } + +private: + /* + * prepare for next parameter set loop + */ + void nextLoop(byte param0, byte param1, byte param2) { // LD182 + _count = (-param2 << 8) | 0x3; + + _bitmask1 = 0x3; + _bitmask2 = 0x3; + + _updateInterval2 = param0; + if (_updateInterval2 == 0) + _bitmask2 = 0x0; + + _updateInterval1 = param1; + if (_updateInterval1 == 0) { + _bitmask1 = 0x0; + if (_bitmask2 != 0) { + _bitmask1 = _bitmask2; + _bitmask2 = 0; + _updateInterval1 = _updateInterval2; + } + } + + _speakerShiftReg = 0; + } + + /* + * perform one loop iteration + * Returns true if loop finished + */ + bool loopIteration() { // D1A2 + --_updateRemain1; + --_updateRemain2; + + if (_updateRemain2 == 0) { + _updateRemain2 = _updateInterval2; + // use only first voice's data (bitmask1) if both voices are triggered + if (_updateRemain1 != 0) { + _speakerShiftReg ^= _bitmask2; + } + } + + if (_updateRemain1 == 0) { + _updateRemain1 = _updateInterval1; + _speakerShiftReg ^= _bitmask1; + } + + if (_speakerShiftReg & 0x1) + _player->speakerToggle(); + _speakerShiftReg >>= 1; + _player->generateSamples(42); /* actually 42.5 */ + + ++_count; + return (_count == 0); + } + +protected: + const byte *_params; + + byte _updateRemain1; + byte _updateRemain2; + + uint16 _count; + byte _bitmask1; + byte _bitmask2; + byte _updateInterval1; + byte _updateInterval2; + byte _speakerShiftReg; +}; + +/* + * SoundFunction5: periodic noise + */ +class AppleII_SoundFunction5_Noise : public AppleII_SoundFunction { +public: + virtual void init(Player_AppleII *player, const byte *params) { + _player = player; + _index = 0; + _param0 = params[0]; + assert(_param0 > 0); + } + + virtual bool update() { // D222 + const byte noiseMask[] = { + 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F + }; + + // while (i = 0; i < 10; ++i) + if (_index < 10) { + int count = _param0; + do { + _update(noise() & noiseMask[_index], 1); + --count; + } while (count > 0); + + ++_index; + return false; + } + + return true; + } + +private: + void _update(int interval /*a*/, int count) { // D270 + assert(count > 0); // 0 == 256? + if (interval == 0) + interval = 256; + + for (int i = count; i > 0; --i) { + _player->generateSamples(10 + 5*interval); + _player->speakerToggle(); + + _player->generateSamples(5 + 5*interval); + _player->speakerToggle(); + } + } + + byte /*a*/ noise() { // D261 + static int pos = 0; // initial value? + byte result = _noiseTable[pos]; + pos = (pos + 1) % 256; + return result; + } + +protected: + int _index; + int _param0; + +private: + static const byte _noiseTable[256]; +}; + +// LD000[loc] ^ LD00A[loc] +const byte AppleII_SoundFunction5_Noise::_noiseTable[256] = { + 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63, + 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c, + 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc, + 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64, + 0xe5, 0xb5, 0x5d, 0xe0, 0xb7, 0x7d, 0xe9, 0x8c, 0x55, 0x65, 0xc5, 0xb5, 0x5d, 0xd8, 0x09, 0x0d, + 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3, + 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a, + 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d, + 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c, + 0x0a, 0x5d, 0x1d, 0x61, 0x10, 0x3c, 0x0b, 0x19, 0x88, 0x21, 0xc0, 0x21, 0x07, 0x00, 0x65, 0x62, + 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06, + 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d, + 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48, + 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37, + 0x19, 0x37, 0x00, 0xf1, 0x00, 0x01, 0x1f, 0x00, 0xad, 0xc1, 0x01, 0x01, 0x2e, 0x00, 0x40, 0xc6, + 0x7a, 0x9b, 0x95, 0x43, 0xfc, 0x18, 0xd2, 0x9e, 0x2a, 0x5a, 0x4b, 0x2a, 0xb6, 0x87, 0x30, 0x6c +}; + +/************************************ + * Apple-II player + ************************************/ + +Player_AppleII::Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer) + : _mixer(mixer), _vm(scumm), _soundFunc(0) { + resetState(); + setSampleRate(_mixer->getOutputRate()); + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); +} + +Player_AppleII::~Player_AppleII() { + _mixer->stopHandle(_soundHandle); + delete _soundFunc; +} + +void Player_AppleII::resetState() { + _soundNr = 0; + _type = 0; + _loop = 0; + _params = NULL; + _speakerState = 0; + delete _soundFunc; + _soundFunc = 0; + _sampleConverter.reset(); +} + +void Player_AppleII::startSound(int nr) { + Common::StackLock lock(_mutex); + + byte *data = _vm->getResourceAddress(rtSound, nr); + assert(data); + byte *ptr1 = data + 4; + + resetState(); + _soundNr = nr; + _type = ptr1[0]; + _loop = ptr1[1]; + _params = &ptr1[2]; + + switch (_type) { + case 0: // empty (nothing to play) + resetState(); + return; + case 1: + _soundFunc = new AppleII_SoundFunction1_FreqUpDown(); + break; + case 2: + _soundFunc = new AppleII_SoundFunction2_SymmetricWave(); + break; + case 3: + _soundFunc = new AppleII_SoundFunction3_AsymmetricWave(); + break; + case 4: + _soundFunc = new AppleII_SoundFunction4_Polyphone(); + break; + case 5: + _soundFunc = new AppleII_SoundFunction5_Noise(); + break; + } + _soundFunc->init(this, _params); + + assert(_loop > 0); + + debug(4, "startSound %d: type %d, loop %d", + nr, _type, _loop); +} + +bool Player_AppleII::updateSound() { + if (!_soundFunc) + return false; + + if (_soundFunc->update()) { + --_loop; + if (_loop <= 0) { + delete _soundFunc; + _soundFunc = 0; + } else { + // reset function state on each loop + _soundFunc->init(this, _params); + } + } + + return true; +} + +void Player_AppleII::stopAllSounds() { + Common::StackLock lock(_mutex); + resetState(); +} + +void Player_AppleII::stopSound(int nr) { + Common::StackLock lock(_mutex); + if (_soundNr == nr) { + resetState(); + } +} + +int Player_AppleII::getSoundStatus(int nr) const { + Common::StackLock lock(_mutex); + return (_soundNr == nr); +} + +int Player_AppleII::getMusicTimer() { + /* Apple-II sounds are synchronous -> no music timer */ + return 0; +} + +int Player_AppleII::readBuffer(int16 *buffer, const int numSamples) { + Common::StackLock lock(_mutex); + + if (!_soundNr) + return 0; + + int samplesLeft = numSamples; + do { + int nSamplesRead = _sampleConverter.readSamples(buffer, samplesLeft); + samplesLeft -= nSamplesRead; + buffer += nSamplesRead; + } while ((samplesLeft > 0) && updateSound()); + + // reset state if sound is played completely + if (!_soundFunc && (_sampleConverter.availableSize() == 0)) + resetState(); + + return numSamples - samplesLeft; +} + +/************************************ + * Apple-II sound-resource helpers + ************************************/ + +// toggle speaker on/off +void Player_AppleII::speakerToggle() { + _speakerState ^= 0x1; +} + +void Player_AppleII::generateSamples(int cycles) { + _sampleConverter.addCycles(_speakerState, cycles); +} + +void Player_AppleII::wait(int interval, int count /*y*/) { + assert(count > 0); // 0 == 256? + assert(interval > 0); // 0 == 256? + generateSamples(11 + count*(8 + 5 * interval)); +} + +} // End of namespace Scumm diff --git a/engines/scumm/player_apple2.h b/engines/scumm/player_apple2.h new file mode 100644 index 0000000000..b4a7d409fb --- /dev/null +++ b/engines/scumm/player_apple2.h @@ -0,0 +1,297 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCUMM_PLAYER_APPLEII_H +#define SCUMM_PLAYER_APPLEII_H + +#include "common/mutex.h" +#include "common/scummsys.h" +#include "common/memstream.h" +#include "scumm/music.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" +#include "audio/softsynth/sid.h" + +namespace Scumm { + +class ScummEngine; + +/* + * Optimized for use with periodical read/write phases when the buffer + * is filled in a write phase and completely read in a read phase. + * The growing strategy is optimized for repeated small (e.g. 2 bytes) + * single writes resulting in large buffers + * (avg.: 4KB, max: 18KB @ 16bit/22.050kHz (MM sound21)). + */ +class SampleBuffer { +public: + SampleBuffer() : _data(0) { + clear(); + } + + ~SampleBuffer() { + free(_data); + } + + void clear() { + free(_data); + _data = 0; + _capacity = 0; + _writePos = 0; + _readPos = 0; + } + + void ensureFree(uint32 needed) { + // if data was read completely, reset read/write pos to front + if ((_writePos != 0) && (_writePos == _readPos)) { + _writePos = 0; + _readPos = 0; + } + + // check for enough space at end of buffer + uint32 freeEndCnt = _capacity - _writePos; + if (needed <= freeEndCnt) + return; + + uint32 avail = availableSize(); + + // check for enough space at beginning and end of buffer + if (needed <= _readPos + freeEndCnt) { + // move unread data to front of buffer + memmove(_data, _data + _readPos, avail); + _writePos = avail; + _readPos = 0; + } else { // needs a grow + byte *old_data = _data; + uint32 new_len = avail + needed; + + _capacity = new_len + 2048; + _data = (byte *)malloc(_capacity); + + if (old_data) { + // copy old unread data to front of new buffer + memcpy(_data, old_data + _readPos, avail); + free(old_data); + _writePos = avail; + _readPos = 0; + } + } + } + + uint32 availableSize() const { + if (_readPos >= _writePos) + return 0; + return _writePos - _readPos; + } + + uint32 write(const void *dataPtr, uint32 dataSize) { + ensureFree(dataSize); + memcpy(_data + _writePos, dataPtr, dataSize); + _writePos += dataSize; + return dataSize; + } + + uint32 read(byte *dataPtr, uint32 dataSize) { + uint32 avail = availableSize(); + if (avail == 0) + return 0; + if (dataSize > avail) + dataSize = avail; + memcpy(dataPtr, _data + _readPos, dataSize); + _readPos += dataSize; + return dataSize; + } + +private: + uint32 _writePos; + uint32 _readPos; + uint32 _capacity; + byte *_data; +}; + +// CPU_CLOCK according to AppleWin +static const double APPLEII_CPU_CLOCK = 1020484.5; // ~ 1.02 MHz + +/* + * Converts the 1-bit speaker state values into audio samples. + * This is done by aggregation of the speaker states at each + * CPU cycle in a sampling period into an audio sample. + */ +class SampleConverter { +private: + void addSampleToBuffer(int sample) { + int16 value = sample * _volume / _maxVolume; + _buffer.write(&value, sizeof(value)); + } + +public: + SampleConverter() : + _cyclesPerSampleFP(0), + _missingCyclesFP(0), + _sampleCyclesSumFP(0), + _volume(_maxVolume) + {} + + ~SampleConverter() {} + + void reset() { + _missingCyclesFP = 0; + _sampleCyclesSumFP = 0; + _buffer.clear(); + } + + uint32 availableSize() const { + return _buffer.availableSize(); + } + + void setMusicVolume(int vol) { + assert(vol >= 0 && vol <= _maxVolume); + _volume = vol; + } + + void setSampleRate(int rate) { + /* ~46 CPU cycles per sample @ 22.05kHz */ + _cyclesPerSampleFP = int(APPLEII_CPU_CLOCK * (1 << PREC_SHIFT) / rate); + reset(); + } + + void addCycles(byte level, const int cycles) { + /* convert to fixed precision floats */ + int cyclesFP = cycles << PREC_SHIFT; + + // step 1: if cycles are left from the last call, process them first + if (_missingCyclesFP > 0) { + int n = (_missingCyclesFP < cyclesFP) ? _missingCyclesFP : cyclesFP; + if (level) + _sampleCyclesSumFP += n; + cyclesFP -= n; + _missingCyclesFP -= n; + if (_missingCyclesFP == 0) { + addSampleToBuffer(2*32767 * _sampleCyclesSumFP / _cyclesPerSampleFP - 32767); + } else { + return; + } + } + + _sampleCyclesSumFP = 0; + + // step 2: process blocks of cycles fitting into a whole sample + while (cyclesFP >= _cyclesPerSampleFP) { + addSampleToBuffer(level ? 32767 : -32767); + cyclesFP -= _cyclesPerSampleFP; + } + + // step 3: remember cycles left for next call + if (cyclesFP > 0) { + _missingCyclesFP = _cyclesPerSampleFP - cyclesFP; + if (level) + _sampleCyclesSumFP = cyclesFP; + } + } + + uint32 readSamples(void *buffer, int numSamples) { + return _buffer.read((byte *)buffer, numSamples * 2) / 2; + } + +private: + static const int PREC_SHIFT = 7; + +private: + int _cyclesPerSampleFP; /* (fixed precision) */ + int _missingCyclesFP; /* (fixed precision) */ + int _sampleCyclesSumFP; /* (fixed precision) */ + int _volume; /* 0 - 256 */ + static const int _maxVolume = 256; + SampleBuffer _buffer; +}; + +class Player_AppleII; + +class AppleII_SoundFunction { +public: + AppleII_SoundFunction() {} + virtual ~AppleII_SoundFunction() {} + virtual void init(Player_AppleII *player, const byte *params) = 0; + /* returns true if finished */ + virtual bool update() = 0; +protected: + Player_AppleII *_player; +}; + +class Player_AppleII : public Audio::AudioStream, public MusicEngine { +public: + Player_AppleII(ScummEngine *scumm, Audio::Mixer *mixer); + virtual ~Player_AppleII(); + + virtual void setMusicVolume(int vol) { _sampleConverter.setMusicVolume(vol); } + void setSampleRate(int rate) { + _sampleRate = rate; + _sampleConverter.setSampleRate(rate); + } + virtual void startSound(int sound); + virtual void stopSound(int sound); + virtual void stopAllSounds(); + virtual int getSoundStatus(int sound) const; + virtual int getMusicTimer(); + + // AudioStream API + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { return false; } + bool endOfData() const { return false; } + int getRate() const { return _sampleRate; } + +public: + void speakerToggle(); + void generateSamples(int cycles); + void wait(int interval, int count); + +private: + // sound number + int _soundNr; + // type of sound + int _type; + // number of loops left + int _loop; + // global sound param list + const byte *_params; + // speaker toggle state (0 / 1) + byte _speakerState; + // sound function + AppleII_SoundFunction *_soundFunc; + // cycle to sample converter + SampleConverter _sampleConverter; + +private: + ScummEngine *_vm; + Audio::Mixer *_mixer; + Audio::SoundHandle _soundHandle; + int _sampleRate; + Common::Mutex _mutex; + +private: + void resetState(); + bool updateSound(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/player_nes.cpp b/engines/scumm/player_nes.cpp index 3f8bcef8b7..a6ffc9ed86 100644 --- a/engines/scumm/player_nes.cpp +++ b/engines/scumm/player_nes.cpp @@ -507,7 +507,7 @@ void APU::Reset () { Frame.Cycles = 1; } -template <class T> +template<class T> int step(T &obj, int sampcycles, uint frame_Cycles, int frame_Num) { int samppos = 0; while (sampcycles) { 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..7a609364e5 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; @@ -1293,7 +1293,7 @@ int Player_SID::readBuffer(int16 *buffer, const int numSamples) { _cpuCyclesLeft = timingProps[_videoSystem].cyclesPerFrame; } // fetch samples - int sampleCount = _sid->updateClock(_cpuCyclesLeft, (short*)buffer, samplesLeft); + int sampleCount = _sid->updateClock(_cpuCyclesLeft, (short *)buffer, samplesLeft); samplesLeft -= sampleCount; buffer += sampleCount; } @@ -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/player_v1.cpp b/engines/scumm/player_v1.cpp index 8afede8c5a..8e784e9866 100644 --- a/engines/scumm/player_v1.cpp +++ b/engines/scumm/player_v1.cpp @@ -351,8 +351,8 @@ parse_again: *_value_ptr_2 = _start_2; } debug(6, "chunk 1: %lu: %d step %d for %d, %lu: %d step %d for %d", - (long)(_value_ptr - (uint*)_channels), _start, _delta, _time_left, - (long)(_value_ptr_2 - (uint*)_channels), _start_2, _delta_2, _time_left_2); + (long)(_value_ptr - (uint *)_channels), _start, _delta, _time_left, + (long)(_value_ptr_2 - (uint *)_channels), _start_2, _delta_2, _time_left_2); break; case 2: diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp index f445a44ded..b2093e9c1a 100644 --- a/engines/scumm/resource.cpp +++ b/engines/scumm/resource.cpp @@ -232,7 +232,7 @@ void ScummEngine::askForDisk(const char *filename, int disknum) { #endif } else { sprintf(buf, "Cannot find file: '%s'", filename); - InfoDialog dialog(this, (char*)buf); + InfoDialog dialog(this, (char *)buf); runDialog(dialog); error("Cannot find file: '%s'", filename); } @@ -350,7 +350,7 @@ void ScummEngine_v7::readIndexBlock(uint32 blocktype, uint32 itemsize) { switch (blocktype) { case MKTAG('A','N','A','M'): // Used by: The Dig, FT num = _fileHandle->readUint16LE(); - ptr = (char*)malloc(num * 9); + ptr = (char *)malloc(num * 9); _fileHandle->read(ptr, num * 9); _imuseDigital->setAudioNames(num, ptr); break; 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..beac077fd1 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -875,21 +875,21 @@ void ScummEngine::saveOrLoad(Serializer *s) { // vm.localvar grew from 25 to 40 script entries and then from // 16 to 32 bit variables (but that wasn't reflect here)... and // THEN from 16 to 25 variables. - MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 17, 25, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(8), VER(8)), - MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 17, 40, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(9), VER(14)), + MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 17, 25, (byte *)vm.localvar[1] - (byte *)vm.localvar[0], VER(8), VER(8)), + MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 17, 40, (byte *)vm.localvar[1] - (byte *)vm.localvar[0], VER(9), VER(14)), // We used to save 25 * 40 = 1000 blocks; but actually, each 'row consisted of 26 entry, // i.e. 26 * 40 = 1040. Thus the last 40 blocks of localvar where not saved at all. To be // able to load this screwed format, we use a trick: We load 26 * 38 = 988 blocks. // Then, we mark the followin 12 blocks (24 bytes) as obsolete. - MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 26, 38, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(15), VER(17)), + MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 26, 38, (byte *)vm.localvar[1] - (byte *)vm.localvar[0], VER(15), VER(17)), MK_OBSOLETE_ARRAY(ScummEngine, vm.localvar[39][0], sleUint16, 12, VER(15), VER(17)), // This was the first proper multi dimensional version of the localvars, with 32 bit values - MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint32, 26, 40, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(18), VER(19)), + MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint32, 26, 40, (byte *)vm.localvar[1] - (byte *)vm.localvar[0], VER(18), VER(19)), // Then we doubled the script slots again, from 40 to 80 - MKARRAY2(ScummEngine, vm.localvar[0][0], sleUint32, 26, NUM_SCRIPT_SLOT, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(20)), + MKARRAY2(ScummEngine, vm.localvar[0][0], sleUint32, 26, NUM_SCRIPT_SLOT, (byte *)vm.localvar[1] - (byte *)vm.localvar[0], VER(20)), MKARRAY(ScummEngine, _resourceMapper[0], sleByte, 128, VER(8)), @@ -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(91)) { + 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(92)), + MKLINE(ScummEngine_v0, _activeObject, sleUint16, VER(92)), + MKLINE(ScummEngine_v0, _activeObject2, sleUint16, VER(92)), + MKLINE(ScummEngine_v0, _cmdVerb, sleByte, VER(92)), + MKLINE(ScummEngine_v0, _cmdObject, sleUint16, VER(92)), + MKLINE(ScummEngine_v0, _cmdObject2, sleUint16, VER(92)), + MKLINE(ScummEngine_v0, _walkToObject, sleUint16, VER(92)), + MKLINE(ScummEngine_v0, _walkToObjectState, sleByte, VER(92)), MKEND() }; s->saveLoadEntries(this, v0Entrys); @@ -1523,7 +1538,7 @@ void ScummEngine_v5::saveOrLoad(Serializer *s) { ScummEngine::saveOrLoad(s); const SaveLoadEntry cursorEntries[] = { - MKARRAY2(ScummEngine_v5, _cursorImages[0][0], sleUint16, 16, 4, (byte*)_cursorImages[1] - (byte*)_cursorImages[0], VER(44)), + MKARRAY2(ScummEngine_v5, _cursorImages[0][0], sleUint16, 16, 4, (byte *)_cursorImages[1] - (byte *)_cursorImages[0], VER(44)), MKARRAY(ScummEngine_v5, _cursorHotspots[0], sleByte, 8, VER(44)), MKEND() }; diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index 064bdf1406..d5f7ea526e 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 92 /** * An auxillary macro, used to specify savegame versions. We use this instead @@ -74,13 +74,13 @@ namespace Scumm { * what POD means refer to <http://en.wikipedia.org/wiki/Plain_Old_Data_Structures> or * to <http://www.informit.com/guides/content.asp?g=cplusplus&seqNum=32&rl=1>) */ -#define OFFS(type,item) (((ptrdiff_t)(&((type*)42)->type::item))-42) +#define OFFS(type,item) (((ptrdiff_t)(&((type *)42)->type::item))-42) /** * Similar to the OFFS macro, this macro computes the size (in bytes) of a * member of a given struct/class type. */ -#define SIZE(type,item) sizeof(((type*)42)->type::item) +#define SIZE(type,item) sizeof(((type *)42)->type::item) // Any item that is still in use automatically gets a maxVersion equal to CURRENT_VER #define MKLINE(type,item,saveas,minVer) {OFFS(type,item),saveas,SIZE(type,item),minVer,CURRENT_VER} diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp index cfc4b3c419..39420ee974 100644 --- a/engines/scumm/script.cpp +++ b/engines/scumm/script.cpp @@ -28,7 +28,9 @@ #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" namespace Scumm { @@ -130,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); } @@ -195,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); @@ -935,6 +936,17 @@ void ScummEngine::runExitScript() { } if (VAR_EXIT_SCRIPT2 != 0xFF && VAR(VAR_EXIT_SCRIPT2)) runScript(VAR(VAR_EXIT_SCRIPT2), 0, 0, 0); + +#ifdef ENABLE_SCUMM_7_8 + // WORKAROUND: The spider lair (room 44) will optionally play the sound + // of trickling water (sound 215), but it never stops it. The same sound + // effect is also used in room 33, so let's do the same fade out that it + // does in that room's exit script. + if (_game.id == GID_DIG && _currentRoom == 44) { + int scriptCmds[] = { 14, 215, 0x600, 0, 30, 0, 0, 0 }; + _sound->soundKludge(scriptCmds, ARRAYSIZE(scriptCmds)); + } +#endif } void ScummEngine::runEntryScript() { @@ -988,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. @@ -1118,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 7f02e899b4..ce162b4a6a 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)"); @@ -873,7 +873,7 @@ void ScummEngine_v2::o2_doSentence() { return; } if (a == 0xFB) { - resetSentence(false); + resetSentence(); return; } @@ -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) { @@ -1171,25 +1176,30 @@ void ScummEngine_v2::o2_startScript() { // imprisonment of the player), then any attempt to start script 87 // (which makes Ted go answer the door bell) is simply ignored. This // way, the door bell still chimes, but Ted ignores it. - if (_game.id == GID_MANIAC && script == 87) { - if (isScriptRunning(88) || isScriptRunning(89)) { - return; + if (_game.id == GID_MANIAC) { + if (_game.version >= 1 && script == 87) { + if (isScriptRunning(88) || isScriptRunning(89)) + return; + } + // Script numbers are different in V0 + if (_game.version == 0 && script == 82) { + if (isScriptRunning(83) || isScriptRunning(84)) + return; } } 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 // switching to the dungeon (script 89) - if ((script == 116) && isScriptRunning(89)) + if (_game.version >= 1 && script == 116 && isScriptRunning(89)) + return; + // Script numbers are different in V0 + if (_game.version == 0 && script == 111 && isScriptRunning(84)) return; } @@ -1202,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); } } @@ -1289,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); } @@ -1301,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() { @@ -1309,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() { @@ -1396,7 +1411,7 @@ void ScummEngine_v2::o2_loadRoomWithEgo() { _fullRedraw = true; - resetSentence(false); + resetSentence(); if (x >= 0 && y >= 0) { a->startWalkActor(x, y, -1); @@ -1471,11 +1486,13 @@ 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); - resetSentence(false); + resetSentence(); vm.cutScenePtr[0] = 0; } @@ -1490,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]; @@ -1510,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() { @@ -1556,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 { @@ -1623,7 +1640,7 @@ void ScummEngine_v2::o2_switchCostumeSet() { o2_dummy(); } -void ScummEngine_v2::resetSentence(bool walking) { +void ScummEngine_v2::resetSentence() { VAR(VAR_SENTENCE_VERB) = VAR(VAR_BACKUP_VERB); VAR(VAR_SENTENCE_OBJECT1) = 0; VAR(VAR_SENTENCE_OBJECT2) = 0; diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp index 8340f62dbc..1de9f08168 100644 --- a/engines/scumm/script_v4.cpp +++ b/engines/scumm/script_v4.cpp @@ -350,7 +350,7 @@ void ScummEngine_v4::loadIQPoints(byte *ptr, int size) { file = _saveFileMan->openForLoading(filename); if (file != NULL) { - byte *tmp = (byte*)malloc(size); + byte *tmp = (byte *)malloc(size); int nread = file->read(tmp, size); if (nread == size) { memcpy(ptr, tmp, size); @@ -414,7 +414,7 @@ void ScummEngine_v4::o4_saveLoadGame() { // use name entered by the user char* ptr; int firstSlot = (_game.id == GID_LOOM) ? STRINGID_SAVENAME1_LOOM : STRINGID_SAVENAME1; - ptr = (char*)getStringAddress(slot + firstSlot - 1); + ptr = (char *)getStringAddress(slot + firstSlot - 1); strncpy(name, ptr, sizeof(name)); } 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/script_v8.cpp b/engines/scumm/script_v8.cpp index c8b92be3c8..f6f376f3c9 100644 --- a/engines/scumm/script_v8.cpp +++ b/engines/scumm/script_v8.cpp @@ -1122,7 +1122,7 @@ void ScummEngine_v8::o8_kernelSetFunctions() { } case 26: { // saveGameWrite // FIXME: This doesn't work - char *address = (char*)getStringAddress(args[2]); + char *address = (char *)getStringAddress(args[2]); debug(0, "o8_kernelSetFunctions: saveGame(%d, %s)", args[1], address); break; } diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index 42ce74ec29..2344bc0497 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Mon Nov 28 01:09:07 2011 + This file was generated by the md5table tool on Sun Mar 11 08:36:15 2012 DO NOT EDIT MANUALLY! */ @@ -16,7 +16,7 @@ struct MD5Table { static const MD5Table md5table[] = { { "008e76ec3ae58d0add637ea7aa299a2c", "freddi3", "", "", -1, Common::FR_FRA, Common::kPlatformMacintosh }, { "02cae0e7ff8504f73618391873d5781a", "freddi3", "HE 98.5", "", -1, Common::DE_DEU, Common::kPlatformWindows }, - { "0305e850382b812fec6e5998ef88a966", "pajama", "", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, + { "0305e850382b812fec6e5998ef88a966", "pajama", "", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "035deab53b47bc43abc763560d0f8d4b", "atlantis", "Floppy", "Demo", -1, Common::EN_ANY, Common::kPlatformPC }, { "037385a953789190298494d92b89b3d0", "catalog", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, { "03d3b18ee3fd68114e2a687c871e38d5", "freddi4", "HE 99", "Mini Game", -1, Common::EN_USA, Common::kPlatformWindows }, @@ -50,6 +50,7 @@ static const MD5Table md5table[] = { { "0c45eb4baff0c12c3d9dfa889c8070ab", "pajama3", "", "Demo", 13884, Common::DE_DEU, Common::kPlatformUnknown }, { "0cccfa5223099a60e76cfcca57a1a141", "freddi3", "", "", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "0d1b69471605201ef2fa9cec1f5f02d2", "maniac", "V2", "V2", -1, Common::ES_ESP, Common::kPlatformPC }, + { "0ddf1174d0d097956ba10dd452ea65e6", "freddi3", "HE 99", "", -1, Common::HE_ISR, Common::kPlatformWindows }, { "0e4c5d54a0ad4b26132e78b5ea76642a", "samnmax", "Floppy", "Demo", 6485, Common::EN_ANY, Common::kPlatformPC }, { "0e96ab45a4eb72acc1b46813976589fd", "activity", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh }, { "0e9b01430e31d9fcd94071d433bbc6bf", "loom", "No AdLib", "EGA", -1, Common::FR_FRA, Common::kPlatformAtariST }, @@ -76,7 +77,7 @@ static const MD5Table md5table[] = { { "15f588e887e857e8c56fe6ade4956168", "atlantis", "Floppy", "Floppy", -1, Common::ES_ESP, Common::kPlatformAmiga }, { "16542a7342a918bfe4ba512007d36c47", "FreddisFunShop", "HE 99L", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "166553538ff320c69edafeee29525419", "samnmax", "", "CD", 199195304, Common::EN_ANY, Common::kPlatformMacintosh }, - { "16effd200aa6b8abe9c569c3e578814d", "freddi4", "HE 99", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, + { "16effd200aa6b8abe9c569c3e578814d", "freddi4", "HE 99", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "179879b6e35c1ead0d93aab26db0951b", "fbear", "HE 70", "", 13381, Common::EN_ANY, Common::kPlatformWindows }, { "17b5d5e6af4ae89d62631641d66d5a05", "indy3", "VGA", "VGA", -1, Common::IT_ITA, Common::kPlatformPC }, { "17f7296f63c78642724f057fd8e736a7", "maniac", "NES", "", 2082, Common::EN_GRB, Common::kPlatformNES }, @@ -89,6 +90,8 @@ static const MD5Table md5table[] = { { "19263586f749a560c1adf8b3393a9593", "socks", "HE 85", "", -1, Common::RU_RUS, Common::kPlatformWindows }, { "19bf6938a94698296bcb0c99c31c91a7", "spyfox2", "", "Demo", -1, Common::EN_GRB, Common::kPlatformWindows }, { "1a6e5ae2777a6a33f06ffc0226210934", "atlantis", "", "CD", -1, Common::EN_ANY, Common::kPlatformMacintosh }, + { "1af4eb581a33d808707d66d50e084dca", "pajama2", "HE 99", "", -1, Common::HE_ISR, Common::kPlatformWindows }, + { "1b720def35ecfa07032ddf1efb34c368", "dog", "", "", 19681, Common::NL_NLD, Common::kPlatformUnknown }, { "1c792d28376d45e145cb916bca0400a2", "spyfox2", "", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "1c7e7db2cfab1ad62746ab680a634204", "maniac", "NES", "", -1, Common::FR_FRA, Common::kPlatformNES }, { "1ca86e2cf9aaa2068738a1e5ba477e60", "zak", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, @@ -107,7 +110,7 @@ static const MD5Table md5table[] = { { "2108d83dcf09f8adb4bc524669c8cf51", "PuttTime", "HE 99", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, { "21a6592322f92550f144f68a8a4e685e", "dig", "", "", -1, Common::FR_FRA, Common::kPlatformMacintosh }, { "21abe302e1b1e2b66d6f5c12e241ebfd", "freddicove", "unenc", "Unencrypted", -1, Common::RU_RUS, Common::kPlatformWindows }, - { "2232b0b9411575b1f9961713ebc9de61", "balloon", "HE 80", "", -1, Common::ES_ESP, Common::kPlatformWindows }, + { "2232b0b9411575b1f9961713ebc9de61", "balloon", "HE 80", "", -1, Common::UNK_LANG, Common::kPlatformWindows }, { "225e18566e810c634bf7de63e7568e3e", "mustard", "", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "22c9eb04455440131ffc157aeb8d40a8", "fbear", "HE 70", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, { "22de86b2f7ec6e5db745ed1123310b44", "spyfox2", "", "Demo", 15832, Common::FR_FRA, Common::kPlatformWindows }, @@ -127,7 +130,7 @@ static const MD5Table md5table[] = { { "2c04aacffb8428f30ccf4f734fbe3adc", "activity", "", "", -1, Common::EN_ANY, Common::kPlatformPC }, { "2ccd8891ce4d3f1a334d21bff6a88ca2", "monkey", "CD", "", 9455, Common::EN_ANY, Common::kPlatformMacintosh }, { "2d1e891fe52df707c30185e52c50cd92", "monkey", "CD", "CD", 8955, Common::EN_ANY, Common::kPlatformPC }, - { "2d388339d6050d8ccaa757b64633954e", "zak", "FM-TOWNS", "Demo", 7520, Common::EN_ANY, Common::kPlatformFMTowns }, + { "2d388339d6050d8ccaa757b64633954e", "indyloom", "FM-TOWNS", "Demo", 7520, Common::EN_ANY, Common::kPlatformFMTowns }, { "2d4536a56e01da4b02eb021e7770afa2", "zak", "FM-TOWNS", "", 7520, Common::EN_ANY, Common::kPlatformFMTowns }, { "2d4acbdcfd8e374c9da8c2e7303a5cd0", "BluesBirthday", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "2d624d1b214f7faf0094daea65c6d1a6", "maniac", "Apple II", "", -1, Common::EN_ANY, Common::kPlatformApple2GS }, @@ -158,10 +161,9 @@ static const MD5Table md5table[] = { { "37ff1b308999c4cca7319edfcc1280a0", "puttputt", "HE 70", "Demo", 8269, Common::EN_ANY, Common::kPlatformWindows }, { "3824e60cdf639d22f6df92a03dc4b131", "fbear", "HE 62", "", 7732, Common::EN_ANY, Common::kPlatformPC }, { "387a544b8b10b26912d8413bab63a853", "monkey2", "", "Demo", -1, Common::EN_ANY, Common::kPlatformPC }, - { "3938ee1aa4433fca9d9308c9891172b1", "zak", "FM-TOWNS", "Demo", 7520, Common::EN_ANY, Common::kPlatformFMTowns }, + { "3938ee1aa4433fca9d9308c9891172b1", "indyzak", "FM-TOWNS", "Demo", 7520, Common::EN_ANY, Common::kPlatformFMTowns }, { "399b217b0c8d65d0398076da486363a9", "indy3", "VGA", "VGA", 6295, Common::DE_DEU, Common::kPlatformPC }, - { "39cb9dec16fa16f38d79acd80effb059", "loom", "EGA", "EGA", -1, Common::FR_FRA, Common::kPlatformAmiga }, - { "39cb9dec16fa16f38d79acd80effb059", "loom", "EGA", "EGA", -1, Common::IT_ITA, Common::kPlatformAmiga }, + { "39cb9dec16fa16f38d79acd80effb059", "loom", "EGA", "EGA", -1, Common::UNK_LANG, Common::kPlatformAmiga }, { "39fd6db10d0222d817025c4d3346e3b4", "farm", "", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, { "3a03dab514e4038df192d8a8de469788", "atlantis", "Floppy", "Floppy", -1, Common::EN_ANY, Common::kPlatformAmiga }, { "3a0c35f3c147b98a2bdf8d400cfc4ab5", "indy3", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, @@ -208,10 +210,11 @@ static const MD5Table md5table[] = { { "4ce2d5b355964bbcb5e5ce73236ef868", "freddicove", "HE 100", "", -1, Common::RU_RUS, Common::kPlatformWindows }, { "4cfd3fda4a4e6e64a1fc488eba973b7a", "fbpack", "", "", -1, Common::EN_ANY, Common::kPlatformPC }, { "4d34042713958b971cb139fba4658586", "atlantis", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, + { "4d3fbc888de4e6565013f61dc83da6b6", "FreddisFunShop", "HE 99", "", 36245, Common::NL_NLD, Common::kPlatformUnknown }, { "4dbff3787aedcd96b0b325f2d92d7ad9", "maze", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, { "4dc780f1bc587a193ce8a97652791438", "loom", "EGA", "EGA", -1, Common::EN_ANY, Common::kPlatformAmiga }, { "4e5867848ee61bc30d157e2c94eee9b4", "PuttTime", "HE 90", "Demo", 18394, Common::EN_USA, Common::kPlatformUnknown }, - { "4edbf9d03550f7ba01e7f34d69b678dd", "spyfox", "HE 98.5", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, + { "4edbf9d03550f7ba01e7f34d69b678dd", "spyfox", "HE 98.5", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "4f04b321a95d4315ce6d65f8e1dd0368", "maze", "HE 80", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "4f138ac6f9b2ac5a41bc68b2c3296064", "freddi4", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformWindows }, { "4f1d6f8b38343dba405472538b5037ed", "fbear", "HE 62", "", 7717, Common::EN_ANY, Common::kPlatformPC }, @@ -225,7 +228,7 @@ static const MD5Table md5table[] = { { "50b831f11b8c4b83784cf81f4dcc69ea", "spyfox", "HE 100", "", -1, Common::EN_ANY, Common::kPlatformWii }, { "50fcdc982a25063b78ad46bf389b8e8d", "tentacle", "Floppy", "Floppy", -1, Common::IT_ITA, Common::kPlatformPC }, { "51305e929e330e24a75a0351c8f9975e", "freddi2", "HE 99", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, - { "513f91a9dbe8d5490b39e56a3ac5bbdf", "pajama2", "HE 98.5", "", -1, Common::NL_NLD, Common::kPlatformMacintosh }, + { "513f91a9dbe8d5490b39e56a3ac5bbdf", "pajama2", "HE 98.5", "", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "5262a27afcaee04e5c4900220bd463e7", "PuttsFunShop", "", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "52a4bae0746a11d7b1e8554e91a6645c", "zak", "V2", "V2", -1, Common::FR_FRA, Common::kPlatformPC }, { "53e94115b55dd51d4b8ff0871aa1df1e", "spyfox", "", "Demo", 20103, Common::EN_ANY, Common::kPlatformUnknown }, @@ -290,7 +293,7 @@ static const MD5Table md5table[] = { { "6a30a07f353a75cdc602db27d73e1b42", "puttputt", "HE 70", "", -1, Common::EN_ANY, Common::kPlatformWindows }, { "6a60d395b78b205c93a956100b1bf5ae", "pajama2", "HE 98.5", "", -1, Common::DE_DEU, Common::kPlatformUnknown }, { "6af2419fe3db5c2fdb091ae4e5833770", "puttrace", "HE 98.5", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, - { "6b19d0e25cbf720d05822379b8b90ed9", "PuttTime", "HE 90", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, + { "6b19d0e25cbf720d05822379b8b90ed9", "PuttTime", "HE 90", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "6b257bb2827dd894b8109a50a1a18b5a", "freddicove", "HE 100", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "6b27dbcd8d5697d5c918eeca0f68ef6a", "puttrace", "HE CUP", "Preview", 3901484, Common::UNK_LANG, Common::kPlatformUnknown }, { "6b3ec67da214f558dc5ceaa2acd47453", "indy3", "EGA", "EGA", -1, Common::EN_ANY, Common::kPlatformPC }, @@ -321,13 +324,13 @@ static const MD5Table md5table[] = { { "7410a8ba9795020cd42f171c4320659e", "pajama3", "", "", -1, Common::FR_FRA, Common::kPlatformWindows }, { "746e88c172a5b7a1ae89ac0ee3ee681a", "freddi", "HE 90", "Updated", -1, Common::RU_RUS, Common::kPlatformWindows }, { "74da3494fbe1a7d20213b0afe0954755", "catalog", "HE CUP", "Preview", 10841544, Common::FR_FRA, Common::kPlatformUnknown }, - { "754feb59d3bf86b8a00840df74fd7b26", "freddi3", "", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, + { "754feb59d3bf86b8a00840df74fd7b26", "freddi3", "", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "75ba23fff4fd63fa446c02864f2a5a4b", "zak", "V2", "V2", -1, Common::IT_ITA, Common::kPlatformPC }, { "75bff95816b84672b877d22a911ab811", "freddi3", "HE 99", "Updated", -1, Common::RU_RUS, Common::kPlatformWindows }, { "76b66b43e593ad4d2f1dfb5cc8f19700", "spyfox", "HE 99", "", -1, Common::NL_NLD, Common::kPlatformWindows }, { "771bc18ec6f93837b839c992b211904b", "monkey", "Demo", "EGA Demo", -1, Common::DE_DEU, Common::kPlatformPC }, { "7766c9487f9d53a8cb0edabda5119c3d", "puttputt", "HE 60", "", 8022, Common::EN_ANY, Common::kPlatformPC }, - { "77f5c9cc0986eb729c1a6b4c8823bbae", "zak", "FM-TOWNS", "Demo", 7520, Common::EN_ANY, Common::kPlatformFMTowns }, + { "77f5c9cc0986eb729c1a6b4c8823bbae", "zakloom", "FM-TOWNS", "Demo", 7520, Common::EN_ANY, Common::kPlatformFMTowns }, { "780e4a0ae2ff17dc296f4a79543b44f8", "puttmoon", "", "", -1, Common::UNK_LANG, Common::kPlatformPC }, { "782393c5934ecd0b536eaf5fd541bd26", "pajama", "HE 100", "Updated", -1, Common::EN_ANY, Common::kPlatformWindows }, { "784b499c98d07260a30952685758636b", "pajama3", "", "Demo", 13911, Common::DE_DEU, Common::kPlatformWindows }, @@ -435,6 +438,7 @@ static const MD5Table md5table[] = { { "a336134914eaab4892d35625aa15ad1d", "freddi4", "HE 99", "", -1, Common::RU_RUS, Common::kPlatformWindows }, { "a525c1753c1db5011c00417da37887ef", "PuttTime", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, { "a561d2e2413cc1c71d5a1bf87bf493ea", "lost", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, + { "a56a05c6b865b9956639f8c51269e5ab", "balloon", "HE 80", "", -1, Common::NL_NLD, Common::kPlatformMacintosh }, { "a56e8d9d4281c53c3f63c9bd22a59e21", "catalog", "HE CUP", "Preview", 10978342, Common::EN_ANY, Common::kPlatformUnknown }, { "a570381b028972d891052ee1e51dc011", "maniac", "V2", "V2", 1988, Common::EN_ANY, Common::kPlatformAtariST }, { "a59a438cb182124c30c4447d8ed469e9", "freddi", "HE 100", "", 34837, Common::NB_NOR, Common::kPlatformWii }, @@ -486,7 +490,7 @@ static const MD5Table md5table[] = { { "bfdf584b01503f0762baded581f6a0a2", "SoccerMLS", "", "", -1, Common::EN_ANY, Common::kPlatformWindows }, { "c0039ad982999c92d0de81910d640fa0", "freddi", "HE 71", "", -1, Common::NL_NLD, Common::kPlatformWindows }, { "c13225cb1bbd3bc9fe578301696d8021", "monkey", "SEGA", "", -1, Common::EN_ANY, Common::kPlatformSegaCD }, - { "c20848f53c2d48bfacdc840993843765", "freddi2", "HE 80", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, + { "c20848f53c2d48bfacdc840993843765", "freddi2", "HE 80", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "c225bec1b6c0798a2b8c89ac226dc793", "pajama", "HE 100", "", -1, Common::EN_ANY, Common::kPlatformWii }, { "c24c490373aeb48fbd54caa8e7ae376d", "loom", "No AdLib", "EGA", -1, Common::DE_DEU, Common::kPlatformAtariST }, { "c25755b08a8d0d47695e05f1e2111bfc", "freddi4", "", "Demo", -1, Common::EN_USA, Common::kPlatformUnknown }, @@ -505,6 +509,7 @@ static const MD5Table md5table[] = { { "c7890e038806df2bb5c0c8c6f1986ea2", "monkey", "VGA", "VGA", -1, Common::EN_ANY, Common::kPlatformPC }, { "c7be10f775404fd9785a8b92a06d240c", "atlantis", "FM-TOWNS", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns }, { "c7c492a107ec520d7a7943037d0ca54a", "freddi", "HE 71", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, + { "c8253da0f4626d2236b5291b99e33408", "puttcircus", "HE 99", "", -1, Common::HE_ISR, Common::kPlatformWindows }, { "c83079157ec765a28de445aec9768d60", "tentacle", "", "Demo", 7477, Common::EN_ANY, Common::kPlatformUnknown }, { "c8575e0b973ff1723aba6cd92c642db2", "puttrace", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, { "c8aac5e3e701874e2fa4117896f9e1b1", "freddi", "HE 73", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, @@ -610,6 +615,7 @@ static const MD5Table md5table[] = { { "f1b0e0d587b85052de5534a3847e68fe", "water", "HE 99", "Updated", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "f237bf8a5ef9af78b2a6a4f3901da341", "pajama", "", "Demo", 18354, Common::EN_ANY, Common::kPlatformUnknown }, { "f27b1ba0eadaf2a6617b2b58192d1dbf", "samnmax", "Floppy", "Floppy", -1, Common::DE_DEU, Common::kPlatformPC }, + { "f2ec78e50bdc63b70044e9758be10914", "spyfox", "HE 98.5", "Demo", -1, Common::NL_NLD, Common::kPlatformMacintosh }, { "f3d55aea441e260e9e9c7d2a187097e0", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows }, { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, { "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index f94496b14b..fc46f88df4 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -55,6 +55,7 @@ #include "scumm/player_nes.h" #include "scumm/player_sid.h" #include "scumm/player_pce.h" +#include "scumm/player_apple2.h" #include "scumm/player_v1.h" #include "scumm/player_v2.h" #include "scumm/player_v2cms.h" @@ -150,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; @@ -265,7 +263,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _bytesPerPixel = 1; _doEffect = false; _snapScroll = false; - _currentLights = 0; _shakeEnabled = false; _shakeFrame = 0; _screenStartStrip = 0; @@ -700,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; @@ -718,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; } @@ -1033,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) { @@ -1255,6 +1247,16 @@ void ScummEngine::setupScumm() { // Load game from specified slot, if any if (ConfMan.hasKey("save_slot")) { requestLoad(ConfMan.getInt("save_slot")); + } else if (!ConfMan.hasKey("boot_param") && _game.id == GID_LOOM && _game.platform == Common::kPlatformFMTowns) { + // In case we run the Loom FM-Towns version and have no boot parameter + // nor start save game supplied we will show our own custom difficulty + // selection dialog, since the original does not have any. + LoomTownsDifficultyDialog difficultyDialog; + runDialog(difficultyDialog); + + int difficulty = difficultyDialog.getSelectedDifficulty(); + if (difficulty != -1) + _bootParam = difficulty; } _res->allocResTypeData(rtBuffer, 0, 10, kDynamicResTypeMode); @@ -1377,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); @@ -1457,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) @@ -1797,7 +1799,7 @@ void ScummEngine::setupMusic(int midi) { if (_game.version >= 7) { // Setup for digital iMuse is performed in another place } else if (_game.platform == Common::kPlatformApple2GS && _game.version == 0){ - // TODO: Add support for music format + _musicEngine = new Player_AppleII(this, _mixer); } else if (_game.platform == Common::kPlatformC64 && _game.version <= 1) { #ifndef DISABLE_SID _musicEngine = new Player_SID(this, _mixer); @@ -1962,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..cacf8c214e 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -24,6 +24,7 @@ #define SCUMM_H #include "engines/engine.h" + #include "common/endian.h" #include "common/events.h" #include "common/file.h" @@ -31,6 +32,7 @@ #include "common/keyboard.h" #include "common/random.h" #include "common/rect.h" +#include "common/rendermode.h" #include "common/str.h" #include "common/textconsole.h" #include "graphics/surface.h" @@ -303,6 +305,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 +517,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 +657,7 @@ protected: void updateScriptPtr(); virtual void runInventoryScript(int i); void inventoryScriptIndy3Mac(); - void checkAndRunSentenceScript(); + virtual void checkAndRunSentenceScript(); void runExitScript(); void runEntryScript(); void runAllScripts(); @@ -791,6 +802,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 +813,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 +833,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 +944,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..7f532c04d7 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); + 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..316a08d325 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,8 +85,9 @@ protected: void ifNotStateCommon(byte type); void setStateCommon(byte type); void clearStateCommon(byte type); + void stopScriptCommon(int script); - virtual void resetSentence(bool walking); + void resetSentence(); void setUserState(byte state); virtual void handleMouseOver(bool updateInventory); @@ -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/scumm_v7.h b/engines/scumm/scumm_v7.h index 81bb25e0b5..6fb98a0af7 100644 --- a/engines/scumm/scumm_v7.h +++ b/engines/scumm/scumm_v7.h @@ -72,7 +72,7 @@ protected: int _languageIndexSize; char _lastStringTag[12+1]; -#if defined(__SYMBIAN32__) || defined (_WIN32_WCE) // for some reason VC6 cannot find the base class TextObject +#if defined(__SYMBIAN32__) || defined(_WIN32_WCE) // for some reason VC6 cannot find the base class TextObject struct SubtitleText { int16 xpos, ypos; byte color; diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp index 2f4e86bf61..a53b808ba1 100644 --- a/engines/scumm/smush/smush_player.cpp +++ b/engines/scumm/smush/smush_player.cpp @@ -90,13 +90,13 @@ public: assert(def_end != NULL); char *id_end = def_end; - while (id_end >= def_start && !isdigit(static_cast<unsigned char>(*(id_end-1)))) { + while (id_end >= def_start && !Common::isDigit(*(id_end-1))) { id_end--; } assert(id_end > def_start); char *id_start = id_end; - while (isdigit(static_cast<unsigned char>(*(id_start - 1)))) { + while (Common::isDigit(*(id_start - 1))) { id_start--; } diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index f058ef1a2c..1dc026ad52 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -94,6 +94,7 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer) Sound::~Sound() { stopCDTimer(); g_system->getAudioCDManager()->stop(); + free(_offsetTable); } void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) { diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 61bb89328d..975307d0d0 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -112,7 +112,7 @@ void ScummEngine::showMessageDialog(const byte *msg) { if (_string[3].color == 0) _string[3].color = 4; - InfoDialog dialog(this, (char*)buf); + InfoDialog dialog(this, (char *)buf); VAR(VAR_KEYPRESS) = runDialog(dialog); } @@ -1389,10 +1389,10 @@ void ScummEngine_v7::loadLanguageBundle() { } else if (*ptr == '#') { // Number of subtags following a given basetag. We don't need that // information so we just skip it - } else if (isdigit(static_cast<unsigned char>(*ptr))) { + } else if (Common::isDigit(*ptr)) { int idx = 0; // A number (up to three digits)... - while (isdigit(static_cast<unsigned char>(*ptr))) { + while (Common::isDigit(*ptr)) { idx = idx * 10 + (*ptr - '0'); ptr++; } @@ -1430,12 +1430,12 @@ void ScummEngine_v7::loadLanguageBundle() { for (i = 0; i < _languageIndexSize; i++) { // First 8 chars in the line give the string ID / 'tag' int j; - for (j = 0; j < 8 && !isspace(static_cast<unsigned char>(*ptr)); j++, ptr++) + for (j = 0; j < 8 && !Common::isSpace(*ptr); j++, ptr++) _languageIndex[i].tag[j] = toupper(*ptr); _languageIndex[i].tag[j] = 0; // After that follows a single space which we skip - assert(isspace(static_cast<unsigned char>(*ptr))); + assert(Common::isSpace(*ptr)); ptr++; // Then comes the translated string: we record an offset to that. 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 |