diff options
Diffstat (limited to 'engines/saga')
39 files changed, 3369 insertions, 3483 deletions
diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp index c2ecff4a1a..6d996d2d40 100644 --- a/engines/saga/actor.cpp +++ b/engines/saga/actor.cpp @@ -29,18 +29,12 @@ #include "saga/animation.h" #include "saga/console.h" #include "saga/events.h" -#include "saga/gfx.h" -#include "saga/interface.h" #include "saga/isomap.h" -#include "saga/itedata.h" #include "saga/objectmap.h" #include "saga/sagaresnames.h" #include "saga/rscfile.h" #include "saga/script.h" #include "saga/sndres.h" -#include "saga/sprite.h" -#include "saga/stream.h" -#include "saga/font.h" #include "saga/sound.h" #include "saga/scene.h" @@ -48,24 +42,6 @@ namespace Saga { -enum ActorFrameIds { -//ITE - kFrameITEStand = 0, - kFrameITEWalk = 1, - kFrameITESpeak = 2, - kFrameITEGive = 3, - kFrameITEGesture = 4, - kFrameITEWait = 5, - kFrameITEPickUp = 6, - kFrameITELook = 7, -//IHNM - kFrameIHNMStand = 0, - kFrameIHNMSpeak = 1, - kFrameIHNMWait = 2, - kFrameIHNMGesture = 3, - kFrameIHNMWalk = 4 -}; - static int commonObjectCompare(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) { int p1 = obj1->_location.y - obj1->_location.z; int p2 = obj2->_location.y - obj2->_location.z; @@ -76,6 +52,16 @@ static int commonObjectCompare(const CommonObjectDataPointer& obj1, const Common return 1; } +static int commonObjectCompareIHNM(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) { + int p1 = obj1->_location.y; + int p2 = obj2->_location.y; + if (p1 == p2) + return 0; + if (p1 < p2) + return -1; + return 1; +} + static int tileCommonObjectCompare(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) { int p1 = -obj1->_location.u() - obj1->_location.v() - obj1->_location.z; int p2 = -obj2->_location.u() - obj2->_location.v() - obj2->_location.z; @@ -89,140 +75,6 @@ static int tileCommonObjectCompare(const CommonObjectDataPointer& obj1, const Co return 1; } -inline int16 int16Compare(int16 i1, int16 i2) { - return ((i1) > (i2) ? 1 : ((i1) < (i2) ? -1 : 0)); -} - -inline int16 quickDistance(const Point &point1, const Point &point2, int16 compressX) { - Point delta; - delta.x = ABS(point1.x - point2.x) / compressX; - delta.y = ABS(point1.y - point2.y); - return ((delta.x < delta.y) ? (delta.y + delta.x / 2) : (delta.x + delta.y / 2)); -} - -inline void calcDeltaS(const Point &point1, const Point &point2, Point &delta, Point &s) { - - delta.x = point2.x - point1.x; - if (delta.x == 0) { - s.x = 0; - } else { - if (delta.x > 0) { - s.x = 1; - } else { - s.x = -1; - delta.x = -delta.x; - } - } - - - delta.y = point2.y - point1.y; - if (delta.y == 0) { - s.y = 0; - } else { - if (delta.y > 0) { - s.y = 1; - } else { - s.y = -1; - delta.y = -delta.y; - } - } -} - -// Lookup table to convert 8 cardinal directions to 4 -static const int actorDirectectionsLUT[8] = { - ACTOR_DIRECTION_BACK, // kDirUp - ACTOR_DIRECTION_RIGHT, // kDirUpRight - ACTOR_DIRECTION_RIGHT, // kDirRight - ACTOR_DIRECTION_RIGHT, // kDirDownRight - ACTOR_DIRECTION_FORWARD,// kDirDown - ACTOR_DIRECTION_LEFT, // kDirDownLeft - ACTOR_DIRECTION_LEFT, // kDirLeft - ACTOR_DIRECTION_LEFT, // kDirUpLeft -}; - -static const PathDirectionData pathDirectionLUT[8][3] = { - { { 0, Point( 0, -1) }, { 7, Point(-1, -1) }, { 4, Point( 1, -1) } }, - { { 1, Point( 1, 0) }, { 4, Point( 1, -1) }, { 5, Point( 1, 1) } }, - { { 2, Point( 0, 1) }, { 5, Point( 1, 1) }, { 6, Point(-1, 1) } }, - { { 3, Point(-1, 0) }, { 6, Point(-1, 1) }, { 7, Point(-1, -1) } }, - { { 0, Point( 0, -1) }, { 1, Point( 1, 0) }, { 4, Point( 1, -1) } }, - { { 1, Point( 1, 0) }, { 2, Point( 0, 1) }, { 5, Point( 1, 1) } }, - { { 2, Point( 0, 1) }, { 3, Point(-1, 0) }, { 6, Point(-1, 1) } }, - { { 3, Point(-1, 0) }, { 0, Point( 0, -1) }, { 7, Point(-1, -1) } } -}; - -static const int pathDirectionLUT2[8][2] = { - { 0, -1 }, - { 1, 0 }, - { 0, 1 }, - { -1, 0 }, - { 1, -1 }, - { 1, 1 }, - { -1, 1 }, - { -1, -1 } -}; - -static const int angleLUT[16][2] = { - { 0, -256 }, - { 98, -237 }, - { 181, -181 }, - { 237, -98 }, - { 256, 0 }, - { 237, 98 }, - { 181, 181 }, - { 98, 237 }, - { 0, 256 }, - { -98, 237 }, - { -181, 181 }, - { -237, 98 }, - { -256, 0 }, - { -237, -98 }, - { -181, -181 }, - { -98, -237 } -}; - -static const int directionLUT[8][2] = { - { 0 * 2, -2 * 2 }, - { 2 * 2, -1 * 2 }, - { 3 * 2, 0 * 2 }, - { 2 * 2, 1 * 2 }, - { 0 * 2, 2 * 2 }, - { -2 * 2, 1 * 2 }, - { -4 * 2, 0 * 2 }, - { -2 * 2, -1 * 2 } -}; - -static const int tileDirectionLUT[8][2] = { - { 1, 1 }, - { 2, 0 }, - { 1, -1 }, - { 0, -2 }, - { -1, -1 }, - { -2, 0 }, - { -1, 1 }, - { 0, 2 } -}; - -struct DragonMove { - uint16 baseFrame; - int16 offset[4][2]; -}; - -static const DragonMove dragonMoveTable[12] = { - { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, - { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, - { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, - { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, - { 28, { { -0, 0 }, { -1, 6 }, { -5, 11 }, { -10, 15 } } }, - { 56, { { 0, 0 }, { 1, 6 }, { 5, 11 }, { 10, 15 } } }, - { 40, { { 0, 0 }, { 6, 1 }, { 11, 5 }, { 15, 10 } } }, - { 44, { { 0, 0 }, { 6, -1 }, { 11, -5 }, { 15, -10 } } }, - { 32, { { -0, -0 }, { -6, -1 }, { -11, -5 }, { -15, -10 } } }, - { 52, { { -0, 0 }, { -6, 1 }, { -11, 5 }, { -15, 10 } } }, - { 36, { { 0, -0 }, { 1, -6 }, { 5, -11 }, { 10, -15 } } }, - { 48, { { -0, -0 }, { -1, -6 }, { -5, -11 }, { -10, -15 } } } -}; - Actor::Actor(SagaEngine *vm) : _vm(vm) { int i; byte *stringsPointer; @@ -268,8 +120,6 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) { _pathRect.top = _vm->getDisplayInfo().pathStartY; _pathRect.bottom = _vm->_scene->getHeight(); - _showActors = true; - // Get actor resource file context _actorContext = _vm->_resource->getContext(GAME_RESOURCEFILE); if (_actorContext == NULL) { @@ -723,76 +573,6 @@ void Actor::stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit, } } -void Actor::realLocation(Location &location, uint16 objectId, uint16 walkFlags) { - int angle; - int distance; - ActorData *actor; - ObjectData *obj; - debug (8, "Actor::realLocation objectId=%i", objectId); - if (walkFlags & kWalkUseAngle) { - if (_vm->_scene->getFlags() & kSceneFlagISO) { - angle = (location.x + 2) & 15; - distance = location.y; - - location.u() = (angleLUT[angle][0] * distance) >> 8; - location.v() = -(angleLUT[angle][1] * distance) >> 8; - } else { - angle = location.x & 15; - distance = location.y; - - location.x = (angleLUT[angle][0] * distance) >> 6; - location.y = (angleLUT[angle][1] * distance) >> 6; - } - } - - if (objectId != ID_NOTHING) { - if (validActorId(objectId)) { - actor = getActor(objectId); - location.addXY(actor->_location); - } else if (validObjId(objectId)) { - obj = getObj(objectId); - location.addXY(obj->_location); - } - } -} - -void Actor::actorFaceTowardsPoint(uint16 actorId, const Location &toLocation) { - ActorData *actor; - Location delta; - //debug (8, "Actor::actorFaceTowardsPoint actorId=%i", actorId); - actor = getActor(actorId); - - toLocation.delta(actor->_location, delta); - - if (_vm->_scene->getFlags() & kSceneFlagISO) { - if (delta.u() > 0) { - actor->_facingDirection = (delta.v() > 0) ? kDirUp : kDirRight; - } else { - actor->_facingDirection = (delta.v() > 0) ? kDirLeft : kDirDown; - } - } else { - if (ABS(delta.y) > ABS(delta.x * 2)) { - actor->_facingDirection = (delta.y > 0) ? kDirDown : kDirUp; - } else { - actor->_facingDirection = (delta.x > 0) ? kDirRight : kDirLeft; - } - } -} - -void Actor::actorFaceTowardsObject(uint16 actorId, uint16 objectId) { - ActorData *actor; - ObjectData *obj; - - if (validActorId(objectId)) { - actor = getActor(objectId); - actorFaceTowardsPoint(actorId, actor->_location); - } else if (validObjId(objectId)) { - obj = getObj(objectId); - actorFaceTowardsPoint(actorId, obj->_location); - } -} - - ObjectData *Actor::getObj(uint16 objId) { ObjectData *obj; @@ -830,18 +610,6 @@ ActorData *Actor::getActor(uint16 actorId) { return actor; } -bool Actor::validFollowerLocation(const Location &location) { - Point point; - location.toScreenPointXY(point); - - if ((point.x < 5) || (point.x >= _vm->getDisplayWidth() - 5) || - (point.y < 0) || (point.y > _vm->_scene->getHeight())) { - return false; - } - - return (_vm->_scene->canWalk(point)); -} - void Actor::setProtagState(int state) { _protagState = state; @@ -855,149 +623,6 @@ void Actor::setProtagState(int state) { } } -void Actor::updateActorsScene(int actorsEntrance) { - int i, j; - int followerDirection; - ActorData *actor; - Location tempLocation; - Location possibleLocation; - Point delta; - const SceneEntry *sceneEntry; - - if (_vm->_scene->currentSceneNumber() == 0) { - error("Actor::updateActorsScene _vm->_scene->currentSceneNumber() == 0"); - } - - _vm->_sound->stopVoice(); - _activeSpeech.stringsCount = 0; - _activeSpeech.playing = false; - _protagonist = NULL; - - for (i = 0; i < _actorsCount; i++) { - actor = _actors[i]; - actor->_inScene = false; - actor->_spriteList.freeMem(); - if (actor->_disabled) { - continue; - } - if ((actor->_flags & (kProtagonist | kFollower)) || (i == 0)) { - if (actor->_flags & kProtagonist) { - actor->_finalTarget = actor->_location; - _centerActor = _protagonist = actor; - } else if (_vm->getGameType() == GType_ITE && - _vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) { - continue; - } - - actor->_sceneNumber = _vm->_scene->currentSceneNumber(); - } - if (actor->_sceneNumber == _vm->_scene->currentSceneNumber()) { - actor->_inScene = true; - actor->_actionCycle = (_vm->_rnd.getRandomNumber(7) & 0x7) * 4; // 1/8th chance - } - } - - // _protagonist can be null while loading a game from the command line - if (_protagonist == NULL) - return; - - if ((actorsEntrance >= 0) && (_vm->_scene->_entryList.entryListCount > 0)) { - if (_vm->_scene->_entryList.entryListCount <= actorsEntrance) { - actorsEntrance = 0; //OCEAN bug - } - - sceneEntry = _vm->_scene->_entryList.getEntry(actorsEntrance); - if (_vm->_scene->getFlags() & kSceneFlagISO) { - _protagonist->_location = sceneEntry->location; - } else { - _protagonist->_location.x = sceneEntry->location.x * ACTOR_LMULT; - _protagonist->_location.y = sceneEntry->location.y * ACTOR_LMULT; - _protagonist->_location.z = sceneEntry->location.z * ACTOR_LMULT; - } - // Workaround for bug #1328045: - // "When entering any of the houses at the start of the - // game if you click on anything inside the building you - // start walking through the door, turn around and leave." - // - // After steping of action zone - Rif trying to exit. - // This piece of code shift Rif's entry position to non action zone area. - if (_vm->getGameType() == GType_ITE) { - if ((_vm->_scene->currentSceneNumber() >= 53) && (_vm->_scene->currentSceneNumber() <= 66)) - _protagonist->_location.y += 10; - } - - _protagonist->_facingDirection = _protagonist->_actionDirection = sceneEntry->facing; - } - - _protagonist->_currentAction = kActionWait; - - if (_vm->_scene->getFlags() & kSceneFlagISO) { - //nothing? - } else { - _vm->_scene->initDoorsState(); //TODO: move to _scene - } - - followerDirection = _protagonist->_facingDirection + 3; - calcScreenPosition(_protagonist); - - for (i = 0; i < _actorsCount; i++) { - actor = _actors[i]; - if (actor->_flags & (kFollower)) { - actor->_facingDirection = actor->_actionDirection = _protagonist->_facingDirection; - actor->_currentAction = kActionWait; - actor->_walkStepsCount = actor->_walkStepIndex = 0; - actor->_location.z = _protagonist->_location.z; - - - if (_vm->_scene->getFlags() & kSceneFlagISO) { - _vm->_isoMap->placeOnTileMap(_protagonist->_location, actor->_location, 3, followerDirection & 0x07); - } else { - followerDirection &= 0x07; - - possibleLocation = _protagonist->_location; - - - delta.x = directionLUT[followerDirection][0]; - delta.y = directionLUT[followerDirection][1]; - - - for (j = 0; j < 30; j++) { - tempLocation = possibleLocation; - tempLocation.x += delta.x; - tempLocation.y += delta.y; - - if (validFollowerLocation(tempLocation)) { - possibleLocation = tempLocation; - } else { - tempLocation = possibleLocation; - tempLocation.x += delta.x; - if (validFollowerLocation(tempLocation)) { - possibleLocation = tempLocation; - } else { - tempLocation = possibleLocation; - tempLocation.y += delta.y; - if (validFollowerLocation(tempLocation)) { - possibleLocation = tempLocation; - } else { - break; - } - } - } - } - - actor->_location = possibleLocation; - } - followerDirection += 2; - } - - } - - handleActions(0, true); - if (_vm->_scene->getFlags() & kSceneFlagISO) { - _vm->_isoMap->adjustScroll(true); - } -} - int Actor::getFrameType(ActorFrameTypes frameType) { if (_vm->getGameType() == GType_ITE) { @@ -1235,413 +860,6 @@ void Actor::handleSpeech(int msec) { _activeSpeech.playing = true; } -void Actor::handleActions(int msec, bool setup) { - int i; - ActorData *actor; - ActorFrameRange *frameRange; - int state; - int speed; - int32 framesLeft; - Location delta; - Location addDelta; - int hitZoneIndex; - const HitZone *hitZone; - Point hitPoint; - Location pickLocation; - - for (i = 0; i < _actorsCount; i++) { - actor = _actors[i]; - if (!actor->_inScene) - continue; - - if ((_vm->getGameType() == GType_ITE) && (i == ACTOR_DRAGON_INDEX)) { - moveDragon(actor); - continue; - } - - switch (actor->_currentAction) { - case kActionWait: - if (!setup && (actor->_flags & kFollower)) { - followProtagonist(actor); - if (actor->_currentAction != kActionWait) - break; - } - - if (actor->_targetObject != ID_NOTHING) { - actorFaceTowardsObject(actor->_id, actor->_targetObject); - } - - if (actor->_flags & kCycle) { - frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand)); - if (frameRange->frameCount > 0) { - actor->_actionCycle++; - actor->_actionCycle = (actor->_actionCycle) % frameRange->frameCount; - } else { - actor->_actionCycle = 0; - } - actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; - break; - } - - if ((actor->_actionCycle & 3) == 0) { - actor->cycleWrap(100); - - frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameWait)); - if ((frameRange->frameCount < 1 || actor->_actionCycle > 33)) - frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand)); - - if (frameRange->frameCount) { - actor->_frameNumber = frameRange->frameIndex + (uint16)_vm->_rnd.getRandomNumber(frameRange->frameCount - 1); - } else { - actor->_frameNumber = frameRange->frameIndex; - } - } - actor->_actionCycle++; - break; - - case kActionWalkToPoint: - case kActionWalkToLink: - if (_vm->_scene->getFlags() & kSceneFlagISO) { - actor->_partialTarget.delta(actor->_location, delta); - - while ((delta.u() == 0) && (delta.v() == 0)) { - - if ((actor == _protagonist) && (_vm->mouseButtonPressed())) { - _vm->_isoMap->screenPointToTileCoords(_vm->mousePos(), pickLocation); - - if (!actorWalkTo(_protagonist->_id, pickLocation)) { - break; - } - } else if (!_vm->_isoMap->nextTileTarget(actor) && !actorEndWalk(actor->_id, true)) { - break; - } - - actor->_partialTarget.delta(actor->_location, delta); - actor->_partialTarget.z = 0; - } - - if (actor->_flags & kFastest) { - speed = 8; - } else if (actor->_flags & kFaster) { - speed = 6; - } else { - speed = 4; - } - - if (_vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) { - speed = 2; - } - - if ((actor->_actionDirection == 2) || (actor->_actionDirection == 6)) { - speed = speed / 2; - } - - if (ABS(delta.v()) > ABS(delta.u())) { - addDelta.v() = clamp(-speed, delta.v(), speed); - if (addDelta.v() == delta.v()) { - addDelta.u() = delta.u(); - } else { - addDelta.u() = delta.u() * addDelta.v(); - addDelta.u() += (addDelta.u() > 0) ? (delta.v() / 2) : (-delta.v() / 2); - addDelta.u() /= delta.v(); - } - } else { - addDelta.u() = clamp(-speed, delta.u(), speed); - if (addDelta.u() == delta.u()) { - addDelta.v() = delta.v(); - } else { - addDelta.v() = delta.v() * addDelta.u(); - addDelta.v() += (addDelta.v() > 0) ? (delta.u() / 2) : (-delta.u() / 2); - addDelta.v() /= delta.u(); - } - } - - actor->_location.add(addDelta); - } else { - actor->_partialTarget.delta(actor->_location, delta); - - while ((delta.x == 0) && (delta.y == 0)) { - - if (actor->_walkStepIndex >= actor->_walkStepsCount) { - actorEndWalk(actor->_id, true); - break; - } - - actor->_partialTarget.fromScreenPoint(actor->_walkStepsPoints[actor->_walkStepIndex++]); - if (_vm->getGameType() == GType_ITE) { - if (actor->_partialTarget.x > 224 * 2 * ACTOR_LMULT) { - actor->_partialTarget.x -= 256 * 2 * ACTOR_LMULT; - } - } else { - if (actor->_partialTarget.x > 224 * 4 * ACTOR_LMULT) { - actor->_partialTarget.x -= 256 * 4 * ACTOR_LMULT; - } - } - - actor->_partialTarget.delta(actor->_location, delta); - - if (ABS(delta.y) > ABS(delta.x)) { - actor->_actionDirection = delta.y > 0 ? kDirDown : kDirUp; - } else { - actor->_actionDirection = delta.x > 0 ? kDirRight : kDirLeft; - } - } - - if(_vm->getGameType() == GType_ITE) - speed = (ACTOR_LMULT * 2 * actor->_screenScale + 63) / 256; - else - speed = (ACTOR_SPEED * actor->_screenScale + 128) >> 8; - - if (speed < 1) - speed = 1; - - if(_vm->getGameType() == GType_IHNM) - speed = speed / 2; - - if ((actor->_actionDirection == kDirUp) || (actor->_actionDirection == kDirDown)) { - addDelta.y = clamp(-speed, delta.y, speed); - if (addDelta.y == delta.y) { - addDelta.x = delta.x; - } else { - addDelta.x = delta.x * addDelta.y; - addDelta.x += (addDelta.x > 0) ? (delta.y / 2) : (-delta.y / 2); - addDelta.x /= delta.y; - actor->_facingDirection = actor->_actionDirection; - } - } else { - addDelta.x = clamp(-2 * speed, delta.x, 2 * speed); - if (addDelta.x == delta.x) { - addDelta.y = delta.y; - } else { - addDelta.y = delta.y * addDelta.x; - addDelta.y += (addDelta.y > 0) ? (delta.x / 2) : (-delta.x / 2); - addDelta.y /= delta.x; - actor->_facingDirection = actor->_actionDirection; - } - } - - actor->_location.add(addDelta); - } - - if (actor->_actorFlags & kActorBackwards) { - actor->_facingDirection = (actor->_actionDirection + 4) & 7; - actor->_actionCycle--; - } else { - actor->_actionCycle++; - } - - frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence); - - if (actor->_actionCycle < 0) { - actor->_actionCycle = frameRange->frameCount - 1; - } else if (actor->_actionCycle >= frameRange->frameCount) { - actor->_actionCycle = 0; - } - - actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; - break; - - case kActionWalkDir: - if (_vm->_scene->getFlags() & kSceneFlagISO) { - actor->_location.u() += tileDirectionLUT[actor->_actionDirection][0]; - actor->_location.v() += tileDirectionLUT[actor->_actionDirection][1]; - - frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence); - - actor->_actionCycle++; - actor->cycleWrap(frameRange->frameCount); - actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; - } else { - if (_vm->getGameType() == GType_ITE) { - actor->_location.x += directionLUT[actor->_actionDirection][0] * 2; - actor->_location.y += directionLUT[actor->_actionDirection][1] * 2; - } else { - // FIXME: The original does not multiply by 8 here, but we do - actor->_location.x += (directionLUT[actor->_actionDirection][0] * 8 * actor->_screenScale + 128) >> 8; - actor->_location.y += (directionLUT[actor->_actionDirection][1] * 8 * actor->_screenScale + 128) >> 8; - } - - frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence); - actor->_actionCycle++; - actor->cycleWrap(frameRange->frameCount); - actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; - } - break; - - case kActionSpeak: - actor->_actionCycle++; - actor->cycleWrap(64); - - frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameGesture)); - if (actor->_actionCycle >= frameRange->frameCount) { - if (actor->_actionCycle & 1) - break; - frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameSpeak)); - - state = (uint16)_vm->_rnd.getRandomNumber(frameRange->frameCount); - - if (state == 0) { - frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand)); - } else { - state--; - } - } else { - state = actor->_actionCycle; - } - - actor->_frameNumber = frameRange->frameIndex + state; - break; - - case kActionAccept: - case kActionStoop: - break; - - case kActionCycleFrames: - case kActionPongFrames: - if (actor->_cycleTimeCount > 0) { - actor->_cycleTimeCount--; - break; - } - - actor->_cycleTimeCount = actor->_cycleDelay; - actor->_actionCycle++; - - frameRange = getActorFrameRange(actor->_id, actor->_cycleFrameSequence); - - if (actor->_currentAction == kActionPongFrames) { - if (actor->_actionCycle >= frameRange->frameCount * 2 - 2) { - if (actor->_actorFlags & kActorContinuous) { - actor->_actionCycle = 0; - } else { - actor->_currentAction = kActionFreeze; - break; - } - } - - state = actor->_actionCycle; - if (state >= frameRange->frameCount) { - state = frameRange->frameCount * 2 - 2 - state; - } - } else { - if (actor->_actionCycle >= frameRange->frameCount) { - if (actor->_actorFlags & kActorContinuous) { - actor->_actionCycle = 0; - } else { - actor->_currentAction = kActionFreeze; - break; - } - } - state = actor->_actionCycle; - } - - if (frameRange->frameCount && (actor->_actorFlags & kActorRandom)) { - state = _vm->_rnd.getRandomNumber(frameRange->frameCount - 1); - } - - if (actor->_actorFlags & kActorBackwards) { - actor->_frameNumber = frameRange->frameIndex + frameRange->frameCount - 1 - state; - } else { - actor->_frameNumber = frameRange->frameIndex + state; - } - break; - - case kActionFall: - if (actor->_actionCycle > 0) { - framesLeft = actor->_actionCycle--; - actor->_finalTarget.delta(actor->_location, delta); - delta.x /= framesLeft; - delta.y /= framesLeft; - actor->_location.addXY(delta); - actor->_fallVelocity += actor->_fallAcceleration; - actor->_fallPosition += actor->_fallVelocity; - actor->_location.z = actor->_fallPosition >> 4; - } else { - actor->_location = actor->_finalTarget; - actor->_currentAction = kActionFreeze; - _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor); - } - break; - - case kActionClimb: - actor->_cycleDelay++; - if (actor->_cycleDelay & 3) { - break; - } - - if (actor->_location.z >= actor->_finalTarget.z + ACTOR_CLIMB_SPEED) { - actor->_location.z -= ACTOR_CLIMB_SPEED; - actor->_actionCycle--; - } else if (actor->_location.z <= actor->_finalTarget.z - ACTOR_CLIMB_SPEED) { - actor->_location.z += ACTOR_CLIMB_SPEED; - actor->_actionCycle++; - } else { - actor->_location.z = actor->_finalTarget.z; - actor->_currentAction = kActionFreeze; - _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor); - } - - frameRange = getActorFrameRange(actor->_id, actor->_cycleFrameSequence); - - if (actor->_actionCycle < 0) { - actor->_actionCycle = frameRange->frameCount - 1; - } - actor->cycleWrap(frameRange->frameCount); - actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; - break; - } - - if ((actor->_currentAction >= kActionWalkToPoint) && (actor->_currentAction <= kActionWalkDir)) { - hitZone = NULL; - - if (_vm->_scene->getFlags() & kSceneFlagISO) { - actor->_location.toScreenPointUV(hitPoint); - } else { - actor->_location.toScreenPointXY(hitPoint); - } - hitZoneIndex = _vm->_scene->_actionMap->hitTest(hitPoint); - if (hitZoneIndex != -1) { - hitZone = _vm->_scene->_actionMap->getHitZone(hitZoneIndex); - } - - if (hitZone != actor->_lastZone) { - if (actor->_lastZone) - stepZoneAction(actor, actor->_lastZone, true, false); - actor->_lastZone = hitZone; - // WORKAROUND for graphics glitch in the rat caves. Don't do this step zone action in the rat caves - // (room 51) to avoid the glitch - if (hitZone && !(_vm->getGameType() == GType_ITE && _vm->_scene->currentSceneNumber() == 51)) - stepZoneAction(actor, hitZone, false, false); - } - } - } - // Update frameCount for sfWaitFrames in IHNM - _vm->_frameCount++; -} - -void Actor::direct(int msec) { - - if (_vm->_scene->_entryList.entryListCount == 0) { - return; - } - - if (_vm->_interface->_statusTextInput) { - return; - } - - // FIXME: HACK. This should be turned into cycle event. - _lastTickMsec += msec; - - if (_lastTickMsec > 1000 / _handleActionDiv) { - _lastTickMsec = 0; - //process actions - handleActions(msec, false); - } - -//process speech - handleSpeech(msec); -} - - bool Actor::calcScreenPosition(CommonObjectData *commonObjectData) { int beginSlope, endSlope, middle; bool result; @@ -1738,7 +956,10 @@ void Actor::createDrawOrderList() { if (_vm->_scene->getFlags() & kSceneFlagISO) { compareFunction = &tileCommonObjectCompare; } else { - compareFunction = &commonObjectCompare; + if (_vm->getGameType() == GType_ITE) + compareFunction = &commonObjectCompare; + else + compareFunction = &commonObjectCompareIHNM; } _drawOrderList.clear(); @@ -1814,10 +1035,6 @@ void Actor::drawActors() { return; } - if (!_showActors) { - return; - } - CommonObjectOrderList::iterator drawOrderIterator; CommonObjectDataPointer drawObject; int frameNumber; @@ -1891,382 +1108,6 @@ void Actor::drawSpeech(void) { free(outputString); } -bool Actor::followProtagonist(ActorData *actor) { - Location protagonistLocation; - Location newLocation; - Location delta; - int protagonistBGMaskType; - Point prefer1; - Point prefer2; - Point prefer3; - int16 prefU; - int16 prefV; - int16 newU; - int16 newV; - - assert(_protagonist); - - actor->_flags &= ~(kFaster | kFastest); - protagonistLocation = _protagonist->_location; - calcScreenPosition(_protagonist); - - if (_vm->_scene->getFlags() & kSceneFlagISO) { - prefU = 60; - prefV = 60; - - - actor->_location.delta(protagonistLocation, delta); - - if (actor->_id == actorIndexToId(2)) { - prefU = prefV = 48; - } - - if ((delta.u() > prefU) || (delta.u() < -prefU) || (delta.v() > prefV) || (delta.v() < -prefV)) { - - if ((delta.u() > prefU * 2) || (delta.u() < -prefU * 2) || (delta.v() > prefV * 2) || (delta.v() < -prefV * 2)) { - actor->_flags |= kFaster; - - if ((delta.u() > prefU * 3) || (delta.u() < -prefU*3) || (delta.v() > prefV * 3) || (delta.v() < -prefV * 3)) { - actor->_flags |= kFastest; - } - } - - prefU /= 2; - prefV /= 2; - - newU = clamp(-prefU, delta.u(), prefU) + protagonistLocation.u(); - newV = clamp(-prefV, delta.v(), prefV) + protagonistLocation.v(); - - newLocation.u() = newU + _vm->_rnd.getRandomNumber(prefU - 1) - prefU / 2; - newLocation.v() = newV + _vm->_rnd.getRandomNumber(prefV - 1) - prefV / 2; - newLocation.z = 0; - - return actorWalkTo(actor->_id, newLocation); - } - - } else { - prefer1.x = (100 * _protagonist->_screenScale) >> 8; - prefer1.y = (50 * _protagonist->_screenScale) >> 8; - - if (_protagonist->_currentAction == kActionWalkDir) { - prefer1.x /= 2; - } - - if (prefer1.x < 8) { - prefer1.x = 8; - } - - if (prefer1.y < 8) { - prefer1.y = 8; - } - - prefer2.x = prefer1.x * 2; - prefer2.y = prefer1.y * 2; - prefer3.x = prefer1.x + prefer1.x / 2; - prefer3.y = prefer1.y + prefer1.y / 2; - - actor->_location.delta(protagonistLocation, delta); - - protagonistBGMaskType = 0; - if (_vm->_scene->isBGMaskPresent() && _vm->_scene->validBGMaskPoint(_protagonist->_screenPosition)) { - protagonistBGMaskType = _vm->_scene->getBGMaskType(_protagonist->_screenPosition); - } - - if ((_vm->_rnd.getRandomNumber(7) & 0x7) == 0) // 1/8th chance - actor->_actorFlags &= ~kActorNoFollow; - - if (actor->_actorFlags & kActorNoFollow) { - return false; - } - - if ((delta.x > prefer2.x) || (delta.x < -prefer2.x) || - (delta.y > prefer2.y) || (delta.y < -prefer2.y) || - ((_protagonist->_currentAction == kActionWait) && - (delta.x * 2 < prefer1.x) && (delta.x * 2 > -prefer1.x) && - (delta.y < prefer1.y) && (delta.y > -prefer1.y))) { - - if (ABS(delta.x) > ABS(delta.y)) { - - delta.x = (delta.x > 0) ? prefer3.x : -prefer3.x; - - newLocation.x = delta.x + protagonistLocation.x; - newLocation.y = clamp(-prefer2.y, delta.y, prefer2.y) + protagonistLocation.y; - } else { - delta.y = (delta.y > 0) ? prefer3.y : -prefer3.y; - - newLocation.x = clamp(-prefer2.x, delta.x, prefer2.x) + protagonistLocation.x; - newLocation.y = delta.y + protagonistLocation.y; - } - newLocation.z = 0; - - if (protagonistBGMaskType != 3) { - newLocation.x += _vm->_rnd.getRandomNumber(prefer1.x - 1) - prefer1.x / 2; - newLocation.y += _vm->_rnd.getRandomNumber(prefer1.y - 1) - prefer1.y / 2; - } - - newLocation.x = clamp(-31*4, newLocation.x, (_vm->getDisplayWidth() + 31) * 4); //fixme - - return actorWalkTo(actor->_id, newLocation); - } - } - return false; -} - -bool Actor::actorEndWalk(uint16 actorId, bool recurse) { - bool walkMore = false; - ActorData *actor; - const HitZone *hitZone; - int hitZoneIndex; - Point testPoint; - - actor = getActor(actorId); - actor->_actorFlags &= ~kActorBackwards; - - if (_vm->getGameType() == GType_ITE) { - - if (actor->_location.distance(actor->_finalTarget) > 8 && (actor->_flags & kProtagonist) && recurse && !(actor->_actorFlags & kActorNoCollide)) { - actor->_actorFlags |= kActorNoCollide; - return actorWalkTo(actorId, actor->_finalTarget); - } - } - - actor->_currentAction = kActionWait; - if (actor->_actorFlags & kActorFinalFace) { - actor->_facingDirection = actor->_actionDirection = (actor->_actorFlags >> 6) & 0x07; //? - } - - actor->_actorFlags &= ~(kActorNoCollide | kActorCollided | kActorFinalFace | kActorFacingMask); - actor->_flags &= ~(kFaster | kFastest); - - if (actor == _protagonist) { - _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor); - if (_vm->_script->_pendingVerb == _vm->_script->getVerbType(kVerbWalkTo)) { - if (_vm->getGameType() == GType_ITE) - actor->_location.toScreenPointUV(testPoint); // it's wrong calculation, but it is used in ITE - else - actor->_location.toScreenPointXY(testPoint); - - hitZoneIndex = _vm->_scene->_actionMap->hitTest(testPoint); - if (hitZoneIndex != -1) { - hitZone = _vm->_scene->_actionMap->getHitZone(hitZoneIndex); - stepZoneAction(actor, hitZone, false, true); - } else { - _vm->_script->setNoPendingVerb(); - } - } else if (_vm->_script->_pendingVerb != _vm->_script->getVerbType(kVerbNone)) { - _vm->_script->doVerb(); - } - } else { - if (recurse && (actor->_flags & kFollower)) - walkMore = followProtagonist(actor); - - _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor); - } - return walkMore; -} - -bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) { - ActorData *actor; - ActorData *anotherActor; - int i; - - Rect testBox; - Rect testBox2; - Point anotherActorScreenPosition; - Point collision; - Point pointFrom, pointTo, pointBest, pointAdd; - Point delta, bestDelta; - Point tempPoint; - bool extraStartNode; - bool extraEndNode; - - actor = getActor(actorId); - - if (actor == _protagonist) { - _vm->_scene->setDoorState(2, 0xff); - _vm->_scene->setDoorState(3, 0); - } else { - _vm->_scene->setDoorState(2, 0); - _vm->_scene->setDoorState(3, 0xff); - } - - if (_vm->_scene->getFlags() & kSceneFlagISO) { - - if ((_vm->getGameType() == GType_ITE) && (actor->_index == ACTOR_DRAGON_INDEX)) { - return false; - } - - actor->_finalTarget = toLocation; - actor->_walkStepsCount = 0; - _vm->_isoMap->findTilePath(actor, actor->_location, toLocation); - - - if ((actor->_walkStepsCount == 0) && (actor->_flags & kProtagonist)) { - actor->_actorFlags |= kActorNoCollide; - _vm->_isoMap->findTilePath(actor, actor->_location, toLocation); - } - - actor->_walkStepIndex = 0; - if (_vm->_isoMap->nextTileTarget(actor)) { - actor->_currentAction = kActionWalkToPoint; - actor->_walkFrameSequence = getFrameType(kFrameWalk); - } else { - actorEndWalk(actorId, false); - return false; - } - } else { - - actor->_location.toScreenPointXY(pointFrom); - pointFrom.x &= ~1; - - extraStartNode = _vm->_scene->offscreenPath(pointFrom); - - toLocation.toScreenPointXY(pointTo); - pointTo.x &= ~1; - - extraEndNode = _vm->_scene->offscreenPath(pointTo); - - if (_vm->_scene->isBGMaskPresent()) { - - if ((((actor->_currentAction >= kActionWalkToPoint) && - (actor->_currentAction <= kActionWalkDir)) || (actor == _protagonist)) && - !_vm->_scene->canWalk(pointFrom)) { - - int max = _vm->getGameType() == GType_ITE ? 8 : 4; - - for (i = 1; i < max; i++) { - pointAdd = pointFrom; - pointAdd.y += i; - if (_vm->_scene->canWalk(pointAdd)) { - pointFrom = pointAdd; - break; - } - pointAdd = pointFrom; - pointAdd.y -= i; - if (_vm->_scene->canWalk(pointAdd)) { - pointFrom = pointAdd; - break; - } - if (_vm->getGameType() == GType_ITE) { - pointAdd = pointFrom; - pointAdd.x += i; - if (_vm->_scene->canWalk(pointAdd)) { - pointFrom = pointAdd; - break; - } - pointAdd = pointFrom; - pointAdd.x -= i; - if (_vm->_scene->canWalk(pointAdd)) { - pointFrom = pointAdd; - break; - } - } - } - } - - _barrierCount = 0; - if (!(actor->_actorFlags & kActorNoCollide)) { - collision.x = ACTOR_COLLISION_WIDTH * actor->_screenScale / (256 * 2); - collision.y = ACTOR_COLLISION_HEIGHT * actor->_screenScale / (256 * 2); - - - for (i = 0; (i < _actorsCount) && (_barrierCount < ACTOR_BARRIERS_MAX); i++) { - anotherActor = _actors[i]; - if (!anotherActor->_inScene) - continue; - if (anotherActor == actor) - continue; - - anotherActorScreenPosition = anotherActor->_screenPosition; - testBox.left = (anotherActorScreenPosition.x - collision.x) & ~1; - testBox.right = (anotherActorScreenPosition.x + collision.x) & ~1 + 1; - testBox.top = anotherActorScreenPosition.y - collision.y; - testBox.bottom = anotherActorScreenPosition.y + collision.y + 1; - testBox2 = testBox; - testBox2.right += 2; - testBox2.left -= 2; - testBox2.top -= 1; - testBox2.bottom += 1; - - if (testBox2.contains(pointFrom)) { - if (pointFrom.x > anotherActorScreenPosition.x + 4) { - testBox.right = pointFrom.x - 1; - } else if (pointFrom.x < anotherActorScreenPosition.x - 4) { - testBox.left = pointFrom.x + 2; - } else if (pointFrom.y > anotherActorScreenPosition.y) { - testBox.bottom = pointFrom.y; - } else { - testBox.top = pointFrom.y + 1 ; - } - } - - if ((testBox.width() > 0) && (testBox.height() > 0)) { - _barrierList[_barrierCount++] = testBox; - } - } - } - - - pointBest = pointTo; - actor->_walkStepsCount = 0; - findActorPath(actor, pointFrom, pointTo); - - if (actor->_walkStepsCount == 0) { - error("actor->_walkStepsCount == 0"); - } - - if (extraStartNode) { - actor->_walkStepIndex = 0; - } else { - actor->_walkStepIndex = 1; - } - - if (extraEndNode) { - toLocation.toScreenPointXY(tempPoint); - actor->_walkStepsCount--; - actor->addWalkStepPoint(tempPoint); - } - - - pointBest = actor->_walkStepsPoints[actor->_walkStepsCount - 1]; - - pointBest.x &= ~1; - delta.x = ABS(pointFrom.x - pointTo.x); - delta.y = ABS(pointFrom.y - pointTo.y); - - bestDelta.x = ABS(pointBest.x - pointTo.x); - bestDelta.y = ABS(pointBest.y - pointTo.y); - - if ((delta.x + delta.y <= bestDelta.x + bestDelta.y) && (actor->_flags & kFollower)) { - actor->_actorFlags |= kActorNoFollow; - } - - if (pointBest == pointFrom) { - actor->_walkStepsCount = 0; - } - } else { - actor->_walkStepsCount = 0; - actor->addWalkStepPoint(pointTo); - actor->_walkStepIndex = 0; - } - - actor->_partialTarget = actor->_location; - actor->_finalTarget = toLocation; - if (actor->_walkStepsCount == 0) { - actorEndWalk(actorId, false); - return false; - } else { - if (actor->_flags & kProtagonist) { - _actors[1]->_actorFlags &= ~kActorNoFollow; // TODO: mark all actors with kFollower flag, not only 1 and 2 - _actors[2]->_actorFlags &= ~kActorNoFollow; - } - actor->_currentAction = (actor->_walkStepsCount >= ACTOR_MAX_STEPS_COUNT) ? kActionWalkToLink : kActionWalkToPoint; - actor->_walkFrameSequence = getFrameType(kFrameWalk); - } - } - return true; -} - void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) { ActorData *actor; int i; @@ -2303,11 +1144,11 @@ void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount, _activeSpeech.speechBox.right = _vm->getDisplayWidth() - 10; } - // WORKAROUND for the compact disk in Ellen's chapter + // HACK for the compact disk in Ellen's chapter // Once Ellen starts saying that "Something is different", bring the compact disk in the // scene. After speaking with AM, the compact disk is visible. She always says this line // when entering room 59, after speaking with AM, if the compact disk is not picked up yet - // Check Script::sfDropObject for the other part of this workaround + // Check Script::sfDropObject for the other part of this hack if (_vm->getGameType() == GType_IHNM && _vm->_scene->currentChapterNumber() == 3 && _vm->_scene->currentSceneNumber() == 59 && _activeSpeech.sampleResourceId == 286) { for (i = 0; i < _objsCount; i++) { @@ -2383,759 +1224,6 @@ void Actor::abortSpeech() { _activeSpeech.playingTime = 0; } -void Actor::moveDragon(ActorData *actor) { - int16 dir0, dir1, dir2, dir3; - int16 moveType; - Event event; - const DragonMove *dragonMove; - - if ((actor->_actionCycle < 0) || - ((actor->_actionCycle == 0) && (actor->_dragonMoveType >= ACTOR_DRAGON_TURN_MOVES))) { - - moveType = kDragonMoveInvalid; - if (actor->_location.distance(_protagonist->_location) < 24) { - if (_dragonHunt && (_protagonist->_currentAction != kActionFall)) { - event.type = kEvTOneshot; - event.code = kScriptEvent; - event.op = kEventExecNonBlocking; - event.time = 0; - event.param = _vm->_scene->getScriptModuleNumber(); // module number - event.param2 = ACTOR_EXP_KNOCK_RIF; // script entry point number - event.param3 = -1; // Action - event.param4 = -1; // Object - event.param5 = -1; // With Object - event.param6 = -1; // Actor - - _vm->_events->queue(&event); - _dragonHunt = false; - } - } else { - _dragonHunt = true; - } - - if (actor->_walkStepIndex + 2 > actor->_walkStepsCount) { - - _vm->_isoMap->findDragonTilePath(actor, actor->_location, _protagonist->_location, actor->_actionDirection); - - if (actor->_walkStepsCount == 0) { - _vm->_isoMap->findDragonTilePath(actor, actor->_location, _protagonist->_location, 0); - } - - if (actor->_walkStepsCount < 2) { - return; - } - - actor->_partialTarget = actor->_location; - actor->_finalTarget = _protagonist->_location; - actor->_walkStepIndex = 0; - } - - dir0 = actor->_actionDirection; - dir1 = actor->_tileDirections[actor->_walkStepIndex++]; - dir2 = actor->_tileDirections[actor->_walkStepIndex]; - dir3 = actor->_tileDirections[actor->_walkStepIndex + 1]; - - if (dir0 != dir1){ - actor->_actionDirection = dir0 = dir1; - } - - actor->_location = actor->_partialTarget; - - if ((dir1 != dir2) && (dir1 == dir3)) { - switch (dir1) { - case kDirUpLeft: - actor->_partialTarget.v() += 16; - moveType = kDragonMoveUpLeft; - break; - case kDirDownLeft: - actor->_partialTarget.u() -= 16; - moveType = kDragonMoveDownLeft; - break; - case kDirDownRight: - actor->_partialTarget.v() -= 16; - moveType = kDragonMoveDownRight; - break; - case kDirUpRight: - actor->_partialTarget.u() += 16; - moveType = kDragonMoveUpRight; - break; - } - - switch (dir2) { - case kDirUpLeft: - actor->_partialTarget.v() += 16; - break; - case kDirDownLeft: - actor->_partialTarget.u() -= 16; - break; - case kDirDownRight: - actor->_partialTarget.v() -= 16; - break; - case kDirUpRight: - actor->_partialTarget.u() += 16; - break; - } - - actor->_walkStepIndex++; - } else { - switch (dir1) { - case kDirUpLeft: - actor->_partialTarget.v() += 16; - switch (dir2) { - case kDirDownLeft: - moveType = kDragonMoveUpLeft_Left; - actor->_partialTarget.u() -= 16; - break; - case kDirUpLeft: - moveType = kDragonMoveUpLeft; - break; - case kDirUpRight: - actor->_partialTarget.u() += 16; - moveType = kDragonMoveUpLeft_Right; - break; - default: - actor->_actionDirection = dir1; - actor->_walkStepsCount = 0; - break; - } - break; - case kDirDownLeft: - actor->_partialTarget.u() -= 16; - switch (dir2) { - case kDirDownRight: - moveType = kDragonMoveDownLeft_Left; - actor->_partialTarget.v() -= 16; - break; - case kDirDownLeft: - moveType = kDragonMoveDownLeft; - break; - case kDirUpLeft: - moveType = kDragonMoveDownLeft_Right; - actor->_partialTarget.v() += 16; - break; - default: - actor->_actionDirection = dir1; - actor->_walkStepsCount = 0; - break; - } - break; - case kDirDownRight: - actor->_partialTarget.v() -= 16; - switch (dir2) { - case kDirUpRight: - moveType = kDragonMoveDownRight_Left; - actor->_partialTarget.u() += 16; - break; - case kDirDownRight: - moveType = kDragonMoveDownRight; - break; - case kDirDownLeft: - moveType = kDragonMoveDownRight_Right; - actor->_partialTarget.u() -= 16; - break; - default: - actor->_actionDirection = dir1; - actor->_walkStepsCount = 0; - break; - } - break; - case kDirUpRight: - actor->_partialTarget.u() += 16; - switch (dir2) { - case kDirUpLeft: - moveType = kDragonMoveUpRight_Left; - actor->_partialTarget.v() += 16; - break; - case kDirUpRight: - moveType = kDragonMoveUpRight; - break; - case kDirDownRight: - moveType = kDragonMoveUpRight_Right; - actor->_partialTarget.v() -= 16; - break; - default: - actor->_actionDirection = dir1; - actor->_walkStepsCount = 0; - break; - } - break; - - default: - actor->_actionDirection = dir1; - actor->_walkStepsCount = 0; - break; - } - } - - actor->_dragonMoveType = moveType; - - if (moveType >= ACTOR_DRAGON_TURN_MOVES) { - actor->_dragonStepCycle = 0; - actor->_actionCycle = 4; - actor->_walkStepIndex++; - } else { - actor->_actionCycle = 4; - } - } - - actor->_actionCycle--; - - if ((actor->_walkStepsCount < 1) || (actor->_actionCycle < 0)) { - return; - } - - if (actor->_dragonMoveType < ACTOR_DRAGON_TURN_MOVES) { - - actor->_dragonStepCycle++; - if (actor->_dragonStepCycle >= 7) { - actor->_dragonStepCycle = 0; - } - - actor->_dragonBaseFrame = actor->_dragonMoveType * 7; - - if (actor->_location.u() > actor->_partialTarget.u() + 3) { - actor->_location.u() -= 4; - } else if (actor->_location.u() < actor->_partialTarget.u() - 3) { - actor->_location.u() += 4; - } else { - actor->_location.u() = actor->_partialTarget.u(); - } - - if (actor->_location.v() > actor->_partialTarget.v() + 3) { - actor->_location.v() -= 4; - } else if (actor->_location.v() < actor->_partialTarget.v() - 3) { - actor->_location.v() += 4; - } else { - actor->_location.v() = actor->_partialTarget.v(); - } - } else { - dragonMove = &dragonMoveTable[actor->_dragonMoveType]; - actor->_dragonBaseFrame = dragonMove->baseFrame; - - - actor->_location.u() = actor->_partialTarget.u() - dragonMove->offset[actor->_actionCycle][0]; - actor->_location.v() = actor->_partialTarget.v() - dragonMove->offset[actor->_actionCycle][1]; - - actor->_dragonStepCycle++; - if (actor->_dragonStepCycle >= 3) { - actor->_dragonStepCycle = 3; - } - } - - actor->_frameNumber = actor->_dragonBaseFrame + actor->_dragonStepCycle; -} - -void Actor::findActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint) { - Point iteratorPoint; - Point bestPoint; - int maskType; - int i; - Rect intersect; - -#ifdef ACTOR_DEBUG - _debugPointsCount = 0; -#endif - - actor->_walkStepsCount = 0; - if (fromPoint == toPoint) { - actor->addWalkStepPoint(toPoint); - return; - } - - for (iteratorPoint.y = 0; iteratorPoint.y < _yCellCount; iteratorPoint.y++) { - for (iteratorPoint.x = 0; iteratorPoint.x < _xCellCount; iteratorPoint.x++) { - if (_vm->_scene->validBGMaskPoint(iteratorPoint)) { - maskType = _vm->_scene->getBGMaskType(iteratorPoint); - setPathCell(iteratorPoint, _vm->_scene->getDoorState(maskType) ? kPathCellBarrier : kPathCellEmpty); - } else { - setPathCell(iteratorPoint, kPathCellBarrier); - } - } - } - - for (i = 0; i < _barrierCount; i++) { - intersect.left = MAX(_pathRect.left, _barrierList[i].left); - intersect.top = MAX(_pathRect.top, _barrierList[i].top); - intersect.right = MIN(_pathRect.right, _barrierList[i].right); - intersect.bottom = MIN(_pathRect.bottom, _barrierList[i].bottom); - - for (iteratorPoint.y = intersect.top; iteratorPoint.y < intersect.bottom; iteratorPoint.y++) { - for (iteratorPoint.x = intersect.left; iteratorPoint.x < intersect.right; iteratorPoint.x++) { - setPathCell(iteratorPoint, kPathCellBarrier); - } - } - } - -#ifdef ACTOR_DEBUG - for (iteratorPoint.y = 0; iteratorPoint.y < _yCellCount; iteratorPoint.y++) { - for (iteratorPoint.x = 0; iteratorPoint.x < _xCellCount; iteratorPoint.x++) { - if (getPathCell(iteratorPoint) == kPathCellBarrier) { - addDebugPoint(iteratorPoint, 24); - } - } - } -#endif - - if (scanPathLine(fromPoint, toPoint)) { - actor->addWalkStepPoint(fromPoint); - actor->addWalkStepPoint(toPoint); - return; - } - - i = fillPathArray(fromPoint, toPoint, bestPoint); - - if (fromPoint == bestPoint) { - actor->addWalkStepPoint(bestPoint); - return; - } - - if (i == 0) { - error("fillPathArray returns zero"); - } - - setActorPath(actor, fromPoint, bestPoint); -} - -bool Actor::scanPathLine(const Point &point1, const Point &point2) { - Point point; - Point delta; - Point s; - Point fDelta; - int16 errterm; - - calcDeltaS(point1, point2, delta, s); - point = point1; - - fDelta.x = delta.x * 2; - fDelta.y = delta.y * 2; - - if (delta.y > delta.x) { - - errterm = fDelta.x - delta.y; - - while (delta.y > 0) { - while (errterm >= 0) { - point.x += s.x; - errterm -= fDelta.y; - } - - point.y += s.y; - errterm += fDelta.x; - - if (!validPathCellPoint(point)) { - return false; - } - if (getPathCell(point) == kPathCellBarrier) { - return false; - } - delta.y--; - } - } else { - - errterm = fDelta.y - delta.x; - - while (delta.x > 0) { - while (errterm >= 0) { - point.y += s.y; - errterm -= fDelta.x; - } - - point.x += s.x; - errterm += fDelta.y; - - if (!validPathCellPoint(point)) { - return false; - } - if (getPathCell(point) == kPathCellBarrier) { - return false; - } - delta.x--; - } - } - return true; -} - -int Actor::fillPathArray(const Point &fromPoint, const Point &toPoint, Point &bestPoint) { - int bestRating; - int currentRating; - int i; - Point bestPath; - int pointCounter; - int startDirection; - PathDirectionData *pathDirection; - PathDirectionData *newPathDirection; - const PathDirectionData *samplePathDirection; - Point nextPoint; - int directionCount; - int16 compressX = (_vm->getGameType() == GType_ITE) ? 2 : 1; - - _pathDirectionListCount = 0; - pointCounter = 0; - bestRating = quickDistance(fromPoint, toPoint, compressX); - bestPath = fromPoint; - - for (startDirection = 0; startDirection < 4; startDirection++) { - newPathDirection = addPathDirectionListData(); - newPathDirection->coord = fromPoint; - newPathDirection->direction = startDirection; - } - - if (validPathCellPoint(fromPoint)) { - setPathCell(fromPoint, kDirUp); - -#ifdef ACTOR_DEBUG - addDebugPoint(fromPoint, 24+36); -#endif - } - - i = 0; - - do { - pathDirection = &_pathDirectionList[i]; - for (directionCount = 0; directionCount < 3; directionCount++) { - samplePathDirection = &pathDirectionLUT[pathDirection->direction][directionCount]; - nextPoint = pathDirection->coord; - nextPoint.x += samplePathDirection->coord.x; - nextPoint.y += samplePathDirection->coord.y; - - if (!validPathCellPoint(nextPoint)) { - continue; - } - - if (getPathCell(nextPoint) != kPathCellEmpty) { - continue; - } - - setPathCell(nextPoint, samplePathDirection->direction); - -#ifdef ACTOR_DEBUG - addDebugPoint(nextPoint, samplePathDirection->direction + 96); -#endif - newPathDirection = addPathDirectionListData(); - newPathDirection->coord = nextPoint; - newPathDirection->direction = samplePathDirection->direction; - ++pointCounter; - if (nextPoint == toPoint) { - bestPoint = toPoint; - return pointCounter; - } - currentRating = quickDistance(nextPoint, toPoint, compressX); - if (currentRating < bestRating) { - bestRating = currentRating; - bestPath = nextPoint; - } - pathDirection = &_pathDirectionList[i]; - } - ++i; - } while (i < _pathDirectionListCount); - - bestPoint = bestPath; - return pointCounter; -} - -void Actor::setActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint) { - Point nextPoint; - int8 direction; - int i; - - _pathListIndex = -1; - addPathListPoint(toPoint); - nextPoint = toPoint; - - while (!(nextPoint == fromPoint)) { - direction = getPathCell(nextPoint); - if ((direction < 0) || (direction >= 8)) { - error("Actor::setActorPath error direction 0x%X", direction); - } - nextPoint.x -= pathDirectionLUT2[direction][0]; - nextPoint.y -= pathDirectionLUT2[direction][1]; - addPathListPoint(nextPoint); - -#ifdef ACTOR_DEBUG - addDebugPoint(nextPoint, 0x8a); -#endif - } - - pathToNode(); - removeNodes(); - nodeToPath(); - removePathPoints(); - - for (i = 0; i <= _pathNodeListIndex; i++) { - actor->addWalkStepPoint(_pathNodeList[i].point); - } -} - -void Actor::pathToNode() { - Point point1, point2, delta; - int direction; - int i; - Point *point; - - point= &_pathList[_pathListIndex]; - direction = 0; - - _pathNodeListIndex = -1; - addPathNodeListPoint(*point); - - for (i = _pathListIndex; i > 0; i--) { - point1 = *point; - --point; - point2 = *point; - if (direction == 0) { - delta.x = int16Compare(point2.x, point1.x); - delta.y = int16Compare(point2.y, point1.y); - direction++; - } - if ((point1.x + delta.x != point2.x) || (point1.y + delta.y != point2.y)) { - addPathNodeListPoint(point1); - direction--; - i++; - point++; - } - } - addPathNodeListPoint(*_pathList); -} - -int pathLine(Point *pointList, const Point &point1, const Point &point2) { - Point point; - Point delta; - Point tempPoint; - Point s; - int16 errterm; - int16 res; - - calcDeltaS(point1, point2, delta, s); - - point = point1; - - tempPoint.x = delta.x * 2; - tempPoint.y = delta.y * 2; - - if (delta.y > delta.x) { - - errterm = tempPoint.x - delta.y; - res = delta.y; - - while (delta.y > 0) { - while (errterm >= 0) { - point.x += s.x; - errterm -= tempPoint.y; - } - - point.y += s.y; - errterm += tempPoint.x; - - *pointList = point; - pointList++; - delta.y--; - } - } else { - - errterm = tempPoint.y - delta.x; - res = delta.x; - - while (delta.x > 0) { - while (errterm >= 0) { - point.y += s.y; - errterm -= tempPoint.x; - } - - point.x += s.x; - errterm += tempPoint.y; - - *pointList = point; - pointList++; - delta.x--; - } - } - return res; -} - -void Actor::nodeToPath() { - int i; - Point point1, point2; - PathNode *node; - Point *point; - - for (i = 0, point = _pathList; i < _pathListAlloced; i++, point++) { - point->x = point->y = PATH_NODE_EMPTY; - } - - _pathListIndex = 1; - _pathList[0] = _pathNodeList[0].point; - _pathNodeList[0].link = 0; - for (i = 0, node = _pathNodeList; i < _pathNodeListIndex; i++) { - point1 = node->point; - node++; - point2 = node->point; - _pathListIndex += pathLine(&_pathList[_pathListIndex], point1, point2); - node->link = _pathListIndex - 1; - } - _pathListIndex--; - _pathNodeList[_pathNodeListIndex].link = _pathListIndex; - -} - -void Actor::removeNodes() { - int i, j, k; - PathNode *iNode, *jNode, *kNode, *fNode; - fNode = &_pathNodeList[_pathNodeListIndex]; - - if (scanPathLine(_pathNodeList[0].point, fNode->point)) { - _pathNodeList[1] = *fNode; - _pathNodeListIndex = 1; - } - - if (_pathNodeListIndex < 4) { - return; - } - - for (i = _pathNodeListIndex - 1, iNode = fNode-1; i > 1 ; i--, iNode--) { - if (iNode->point.x == PATH_NODE_EMPTY) { - continue; - } - - if (scanPathLine(_pathNodeList[0].point, iNode->point)) { - for (j = 1, jNode = _pathNodeList + 1; j < i; j++, jNode++) { - jNode->point.x = PATH_NODE_EMPTY; - } - } - } - - for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex - 1; i++, iNode++) { - if (iNode->point.x == PATH_NODE_EMPTY) { - continue; - } - - if (scanPathLine(fNode->point, iNode->point)) { - for (j = i + 1, jNode = iNode + 1; j < _pathNodeListIndex; j++, jNode++) { - jNode->point.x = PATH_NODE_EMPTY; - } - } - } - condenseNodeList(); - - for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex - 1; i++, iNode++) { - if (iNode->point.x == PATH_NODE_EMPTY) { - continue; - } - for (j = i + 2, jNode = iNode + 2; j < _pathNodeListIndex; j++, jNode++) { - if (jNode->point.x == PATH_NODE_EMPTY) { - continue; - } - - if (scanPathLine(iNode->point, jNode->point)) { - for (k = i + 1,kNode = iNode + 1; k < j; k++, kNode++) { - kNode->point.x = PATH_NODE_EMPTY; - } - } - } - } - condenseNodeList(); -} - -void Actor::condenseNodeList() { - int i, j, count; - PathNode *iNode, *jNode; - - count = _pathNodeListIndex; - - for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex; i++, iNode++) { - if (iNode->point.x == PATH_NODE_EMPTY) { - j = i + 1; - jNode = iNode + 1; - while (jNode->point.x == PATH_NODE_EMPTY) { - j++; - jNode++; - } - *iNode = *jNode; - count = i; - jNode->point.x = PATH_NODE_EMPTY; - if (j == _pathNodeListIndex) { - break; - } - } - } - _pathNodeListIndex = count; -} - -void Actor::removePathPoints() { - int i, j, k, l; - PathNode *node; - int start; - int end; - Point point1, point2; - - if (_pathNodeListIndex < 2) - return; - - _newPathNodeListIndex = -1; - addNewPathNodeListPoint(_pathNodeList[0]); - - for (i = 1, node = _pathNodeList + 1; i < _pathNodeListIndex; i++, node++) { - addNewPathNodeListPoint(*node); - - for (j = 5; j > 0; j--) { - start = node->link - j; - end = node->link + j; - - if (start < 0 || end > _pathListIndex) { - continue; - } - - point1 = _pathList[start]; - point2 = _pathList[end]; - if ((point1.x == PATH_NODE_EMPTY) || (point2.x == PATH_NODE_EMPTY)) { - continue; - } - - if (scanPathLine(point1, point2)) { - for (l = 1; l <= _newPathNodeListIndex; l++) { - if (start <= _newPathNodeList[l].link) { - _newPathNodeListIndex = l; - _newPathNodeList[_newPathNodeListIndex].point = point1; - _newPathNodeList[_newPathNodeListIndex].link = start; - incrementNewPathNodeListIndex(); - break; - } - } - _newPathNodeList[_newPathNodeListIndex].point = point2; - _newPathNodeList[_newPathNodeListIndex].link = end; - - for (k = start + 1; k < end; k++) { - _pathList[k].x = PATH_NODE_EMPTY; - } - break; - } - } - } - - addNewPathNodeListPoint(_pathNodeList[_pathNodeListIndex]); - - for (i = 0, j = 0; i <= _newPathNodeListIndex; i++) { - if (_newPathNodeListIndex == i || (_newPathNodeList[i].point != _newPathNodeList[i+1].point)) { - _pathNodeList[j++] = _newPathNodeList[i]; - } - } - _pathNodeListIndex = j - 1; -} - -void Actor::drawPathTest() { -#ifdef ACTOR_DEBUG - int i; - Surface *surface; - surface = _vm->_gfx->getBackBuffer(); - if (_debugPoints == NULL) { - return; - } - - for (i = 0; i < _debugPointsCount; i++) { - *((byte *)surface->pixels + (_debugPoints[i].point.y * surface->pitch) + _debugPoints[i].point.x) = _debugPoints[i].color; - } -#endif -} - void Actor::saveState(Common::OutSaveFile *out) { uint16 i; @@ -3156,7 +1244,7 @@ void Actor::loadState(Common::InSaveFile *in) { int32 i; int16 protagState = in->readSint16LE(); - if (protagState != 0) + if (protagState != 0 || _protagonist->_shareFrames) setProtagState(protagState); for (i = 0; i < _actorsCount; i++) { @@ -3178,24 +1266,4 @@ void Actor::loadState(Common::InSaveFile *in) { } } -// Console wrappers - must be safe to run - -void Actor::cmdActorWalkTo(int argc, const char **argv) { - uint16 actorId = (uint16) atoi(argv[1]); - Location location; - Point movePoint; - - movePoint.x = atoi(argv[2]); - movePoint.y = atoi(argv[3]); - - location.fromScreenPoint(movePoint); - - if (!validActorId(actorId)) { - _vm->_console->DebugPrintf("Actor::cmActorWalkTo Invalid actorId 0x%X.\n", actorId); - return; - } - - actorWalkTo(actorId, location); -} - } // End of namespace Saga diff --git a/engines/saga/actor.h b/engines/saga/actor.h index ef62661c6c..7022ef1289 100644 --- a/engines/saga/actor.h +++ b/engines/saga/actor.h @@ -94,6 +94,24 @@ enum ActorActions { kActionClimb = 12 }; +enum ActorFrameIds { +//ITE + kFrameITEStand = 0, + kFrameITEWalk = 1, + kFrameITESpeak = 2, + kFrameITEGive = 3, + kFrameITEGesture = 4, + kFrameITEWait = 5, + kFrameITEPickUp = 6, + kFrameITELook = 7, +//IHNM + kFrameIHNMStand = 0, + kFrameIHNMSpeak = 1, + kFrameIHNMWait = 2, + kFrameIHNMGesture = 3, + kFrameIHNMWalk = 4 +}; + enum SpeechFlags { kSpeakNoAnimate = 1, kSpeakAsync = 2, @@ -112,6 +130,18 @@ enum ActorFrameTypes { kFrameLook }; +// Lookup table to convert 8 cardinal directions to 4 +static const int actorDirectectionsLUT[8] = { + ACTOR_DIRECTION_BACK, // kDirUp + ACTOR_DIRECTION_RIGHT, // kDirUpRight + ACTOR_DIRECTION_RIGHT, // kDirRight + ACTOR_DIRECTION_RIGHT, // kDirDownRight + ACTOR_DIRECTION_FORWARD,// kDirDown + ACTOR_DIRECTION_LEFT, // kDirDownLeft + ACTOR_DIRECTION_LEFT, // kDirLeft + ACTOR_DIRECTION_LEFT, // kDirUpLeft +}; + enum ActorFlagsEx { kActorNoCollide = (1 << 0), kActorNoFollow = (1 << 1), @@ -612,8 +642,6 @@ public: void freeObjList(); void loadObjList(int objectCount, int objectsResourceID); - void showActors(bool flag) { _showActors = flag; } - protected: friend class Script; bool loadActorResources(ActorData *actor); @@ -709,7 +737,6 @@ private: int _xCellCount; int _yCellCount; Rect _pathRect; - bool _showActors; PathDirectionData *_pathDirectionList; int _pathDirectionListCount; diff --git a/engines/saga/actor_walk.cpp b/engines/saga/actor_walk.cpp new file mode 100644 index 0000000000..47bf804edb --- /dev/null +++ b/engines/saga/actor_walk.cpp @@ -0,0 +1,1942 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "saga/saga.h" + +#include "saga/actor.h" +#include "saga/console.h" +#include "saga/events.h" +#include "saga/isomap.h" +#include "saga/objectmap.h" +#include "saga/sagaresnames.h" +#include "saga/script.h" +#include "saga/sound.h" +#include "saga/scene.h" + +namespace Saga { + +static const PathDirectionData pathDirectionLUT[8][3] = { + { { 0, Point( 0, -1) }, { 7, Point(-1, -1) }, { 4, Point( 1, -1) } }, + { { 1, Point( 1, 0) }, { 4, Point( 1, -1) }, { 5, Point( 1, 1) } }, + { { 2, Point( 0, 1) }, { 5, Point( 1, 1) }, { 6, Point(-1, 1) } }, + { { 3, Point(-1, 0) }, { 6, Point(-1, 1) }, { 7, Point(-1, -1) } }, + { { 0, Point( 0, -1) }, { 1, Point( 1, 0) }, { 4, Point( 1, -1) } }, + { { 1, Point( 1, 0) }, { 2, Point( 0, 1) }, { 5, Point( 1, 1) } }, + { { 2, Point( 0, 1) }, { 3, Point(-1, 0) }, { 6, Point(-1, 1) } }, + { { 3, Point(-1, 0) }, { 0, Point( 0, -1) }, { 7, Point(-1, -1) } } +}; + +static const int pathDirectionLUT2[8][2] = { + { 0, -1 }, + { 1, 0 }, + { 0, 1 }, + { -1, 0 }, + { 1, -1 }, + { 1, 1 }, + { -1, 1 }, + { -1, -1 } +}; + +static const int angleLUT[16][2] = { + { 0, -256 }, + { 98, -237 }, + { 181, -181 }, + { 237, -98 }, + { 256, 0 }, + { 237, 98 }, + { 181, 181 }, + { 98, 237 }, + { 0, 256 }, + { -98, 237 }, + { -181, 181 }, + { -237, 98 }, + { -256, 0 }, + { -237, -98 }, + { -181, -181 }, + { -98, -237 } +}; + +static const int directionLUT[8][2] = { + { 0 * 2, -2 * 2 }, + { 2 * 2, -1 * 2 }, + { 3 * 2, 0 * 2 }, + { 2 * 2, 1 * 2 }, + { 0 * 2, 2 * 2 }, + { -2 * 2, 1 * 2 }, + { -4 * 2, 0 * 2 }, + { -2 * 2, -1 * 2 } +}; + +static const int tileDirectionLUT[8][2] = { + { 1, 1 }, + { 2, 0 }, + { 1, -1 }, + { 0, -2 }, + { -1, -1 }, + { -2, 0 }, + { -1, 1 }, + { 0, 2 } +}; + +struct DragonMove { + uint16 baseFrame; + int16 offset[4][2]; +}; + +static const DragonMove dragonMoveTable[12] = { + { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, + { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, + { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, + { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, + { 28, { { -0, 0 }, { -1, 6 }, { -5, 11 }, { -10, 15 } } }, + { 56, { { 0, 0 }, { 1, 6 }, { 5, 11 }, { 10, 15 } } }, + { 40, { { 0, 0 }, { 6, 1 }, { 11, 5 }, { 15, 10 } } }, + { 44, { { 0, 0 }, { 6, -1 }, { 11, -5 }, { 15, -10 } } }, + { 32, { { -0, -0 }, { -6, -1 }, { -11, -5 }, { -15, -10 } } }, + { 52, { { -0, 0 }, { -6, 1 }, { -11, 5 }, { -15, 10 } } }, + { 36, { { 0, -0 }, { 1, -6 }, { 5, -11 }, { 10, -15 } } }, + { 48, { { -0, -0 }, { -1, -6 }, { -5, -11 }, { -10, -15 } } } +}; + +inline int16 quickDistance(const Point &point1, const Point &point2, int16 compressX) { + Point delta; + delta.x = ABS(point1.x - point2.x) / compressX; + delta.y = ABS(point1.y - point2.y); + return ((delta.x < delta.y) ? (delta.y + delta.x / 2) : (delta.x + delta.y / 2)); +} + +inline void calcDeltaS(const Point &point1, const Point &point2, Point &delta, Point &s) { + + delta.x = point2.x - point1.x; + if (delta.x == 0) { + s.x = 0; + } else { + if (delta.x > 0) { + s.x = 1; + } else { + s.x = -1; + delta.x = -delta.x; + } + } + + + delta.y = point2.y - point1.y; + if (delta.y == 0) { + s.y = 0; + } else { + if (delta.y > 0) { + s.y = 1; + } else { + s.y = -1; + delta.y = -delta.y; + } + } +} + +inline int16 int16Compare(int16 i1, int16 i2) { + return ((i1) > (i2) ? 1 : ((i1) < (i2) ? -1 : 0)); +} + +bool Actor::validFollowerLocation(const Location &location) { + Point point; + location.toScreenPointXY(point); + + if ((point.x < 5) || (point.x >= _vm->getDisplayWidth() - 5) || + (point.y < 0) || (point.y > _vm->_scene->getHeight())) { + return false; + } + + return (_vm->_scene->canWalk(point)); +} + +void Actor::realLocation(Location &location, uint16 objectId, uint16 walkFlags) { + int angle; + int distance; + ActorData *actor; + ObjectData *obj; + debug (8, "Actor::realLocation objectId=%i", objectId); + if (walkFlags & kWalkUseAngle) { + if (_vm->_scene->getFlags() & kSceneFlagISO) { + angle = (location.x + 2) & 15; + distance = location.y; + + location.u() = (angleLUT[angle][0] * distance) >> 8; + location.v() = -(angleLUT[angle][1] * distance) >> 8; + } else { + angle = location.x & 15; + distance = location.y; + + location.x = (angleLUT[angle][0] * distance) >> 6; + location.y = (angleLUT[angle][1] * distance) >> 6; + } + } + + if (objectId != ID_NOTHING) { + if (validActorId(objectId)) { + actor = getActor(objectId); + location.addXY(actor->_location); + } else if (validObjId(objectId)) { + obj = getObj(objectId); + location.addXY(obj->_location); + } + } +} + +void Actor::actorFaceTowardsObject(uint16 actorId, uint16 objectId) { + ActorData *actor; + ObjectData *obj; + + if (validActorId(objectId)) { + actor = getActor(objectId); + actorFaceTowardsPoint(actorId, actor->_location); + } else if (validObjId(objectId)) { + obj = getObj(objectId); + actorFaceTowardsPoint(actorId, obj->_location); + } +} + +void Actor::actorFaceTowardsPoint(uint16 actorId, const Location &toLocation) { + ActorData *actor; + Location delta; + //debug (8, "Actor::actorFaceTowardsPoint actorId=%i", actorId); + actor = getActor(actorId); + + toLocation.delta(actor->_location, delta); + + if (_vm->_scene->getFlags() & kSceneFlagISO) { + if (delta.u() > 0) { + actor->_facingDirection = (delta.v() > 0) ? kDirUp : kDirRight; + } else { + actor->_facingDirection = (delta.v() > 0) ? kDirLeft : kDirDown; + } + } else { + if (ABS(delta.y) > ABS(delta.x * 2)) { + actor->_facingDirection = (delta.y > 0) ? kDirDown : kDirUp; + } else { + actor->_facingDirection = (delta.x > 0) ? kDirRight : kDirLeft; + } + } +} + +void Actor::updateActorsScene(int actorsEntrance) { + int i, j; + int followerDirection; + ActorData *actor; + Location tempLocation; + Location possibleLocation; + Point delta; + const SceneEntry *sceneEntry; + + if (_vm->_scene->currentSceneNumber() == 0) { + error("Actor::updateActorsScene _vm->_scene->currentSceneNumber() == 0"); + } + + _vm->_sound->stopVoice(); + _activeSpeech.stringsCount = 0; + _activeSpeech.playing = false; + _protagonist = NULL; + + for (i = 0; i < _actorsCount; i++) { + actor = _actors[i]; + actor->_inScene = false; + actor->_spriteList.freeMem(); + if (actor->_disabled) { + continue; + } + if ((actor->_flags & (kProtagonist | kFollower)) || (i == 0)) { + if (actor->_flags & kProtagonist) { + actor->_finalTarget = actor->_location; + _centerActor = _protagonist = actor; + } else if (_vm->getGameType() == GType_ITE && + _vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) { + continue; + } + + actor->_sceneNumber = _vm->_scene->currentSceneNumber(); + } + if (actor->_sceneNumber == _vm->_scene->currentSceneNumber()) { + actor->_inScene = true; + actor->_actionCycle = (_vm->_rnd.getRandomNumber(7) & 0x7) * 4; // 1/8th chance + } + } + + // _protagonist can be null while loading a game from the command line + if (_protagonist == NULL) + return; + + if ((actorsEntrance >= 0) && (_vm->_scene->_entryList.entryListCount > 0)) { + if (_vm->_scene->_entryList.entryListCount <= actorsEntrance) { + actorsEntrance = 0; //OCEAN bug + } + + sceneEntry = _vm->_scene->_entryList.getEntry(actorsEntrance); + if (_vm->_scene->getFlags() & kSceneFlagISO) { + _protagonist->_location = sceneEntry->location; + } else { + _protagonist->_location.x = sceneEntry->location.x * ACTOR_LMULT; + _protagonist->_location.y = sceneEntry->location.y * ACTOR_LMULT; + _protagonist->_location.z = sceneEntry->location.z * ACTOR_LMULT; + } + // Workaround for bug #1328045: + // "When entering any of the houses at the start of the + // game if you click on anything inside the building you + // start walking through the door, turn around and leave." + // + // After stepping on an action zone, Rif is trying to exit. + // Shift Rif's entry position to a non action zone area. + if (_vm->getGameType() == GType_ITE) { + if ((_vm->_scene->currentSceneNumber() >= 53) && (_vm->_scene->currentSceneNumber() <= 66)) + _protagonist->_location.y += 10; + } + + _protagonist->_facingDirection = _protagonist->_actionDirection = sceneEntry->facing; + } + + _protagonist->_currentAction = kActionWait; + + if (_vm->_scene->getFlags() & kSceneFlagISO) { + //nothing? + } else { + _vm->_scene->initDoorsState(); //TODO: move to _scene + } + + followerDirection = _protagonist->_facingDirection + 3; + calcScreenPosition(_protagonist); + + for (i = 0; i < _actorsCount; i++) { + actor = _actors[i]; + if (actor->_flags & (kFollower)) { + actor->_facingDirection = actor->_actionDirection = _protagonist->_facingDirection; + actor->_currentAction = kActionWait; + actor->_walkStepsCount = actor->_walkStepIndex = 0; + actor->_location.z = _protagonist->_location.z; + + + if (_vm->_scene->getFlags() & kSceneFlagISO) { + _vm->_isoMap->placeOnTileMap(_protagonist->_location, actor->_location, 3, followerDirection & 0x07); + } else { + followerDirection &= 0x07; + + possibleLocation = _protagonist->_location; + + delta.x = directionLUT[followerDirection][0]; + delta.y = directionLUT[followerDirection][1]; + + for (j = 0; j < 30; j++) { + tempLocation = possibleLocation; + tempLocation.x += delta.x; + tempLocation.y += delta.y; + + if (validFollowerLocation(tempLocation)) { + possibleLocation = tempLocation; + } else { + tempLocation = possibleLocation; + tempLocation.x += delta.x; + if (validFollowerLocation(tempLocation)) { + possibleLocation = tempLocation; + } else { + tempLocation = possibleLocation; + tempLocation.y += delta.y; + if (validFollowerLocation(tempLocation)) { + possibleLocation = tempLocation; + } else { + break; + } + } + } + } + + actor->_location = possibleLocation; + } + followerDirection += 2; + } + + } + + handleActions(0, true); + if (_vm->_scene->getFlags() & kSceneFlagISO) { + _vm->_isoMap->adjustScroll(true); + } +} + +void Actor::handleActions(int msec, bool setup) { + int i; + ActorData *actor; + ActorFrameRange *frameRange; + int state; + int speed; + int32 framesLeft; + Location delta; + Location addDelta; + int hitZoneIndex; + const HitZone *hitZone; + Point hitPoint; + Location pickLocation; + + for (i = 0; i < _actorsCount; i++) { + actor = _actors[i]; + if (!actor->_inScene) + continue; + + if ((_vm->getGameType() == GType_ITE) && (i == ACTOR_DRAGON_INDEX)) { + moveDragon(actor); + continue; + } + + switch (actor->_currentAction) { + case kActionWait: + if (!setup && (actor->_flags & kFollower)) { + followProtagonist(actor); + if (actor->_currentAction != kActionWait) + break; + } + + if (actor->_targetObject != ID_NOTHING) { + actorFaceTowardsObject(actor->_id, actor->_targetObject); + } + + if (actor->_flags & kCycle) { + frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand)); + if (frameRange->frameCount > 0) { + actor->_actionCycle++; + actor->_actionCycle = (actor->_actionCycle) % frameRange->frameCount; + } else { + actor->_actionCycle = 0; + } + actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; + break; + } + + if ((actor->_actionCycle & 3) == 0) { + actor->cycleWrap(100); + + frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameWait)); + if ((frameRange->frameCount < 1 || actor->_actionCycle > 33)) + frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand)); + + if (frameRange->frameCount) { + actor->_frameNumber = frameRange->frameIndex + (uint16)_vm->_rnd.getRandomNumber(frameRange->frameCount - 1); + } else { + actor->_frameNumber = frameRange->frameIndex; + } + } + actor->_actionCycle++; + break; + + case kActionWalkToPoint: + case kActionWalkToLink: + if (_vm->_scene->getFlags() & kSceneFlagISO) { + actor->_partialTarget.delta(actor->_location, delta); + + while ((delta.u() == 0) && (delta.v() == 0)) { + + if ((actor == _protagonist) && (_vm->mouseButtonPressed())) { + _vm->_isoMap->screenPointToTileCoords(_vm->mousePos(), pickLocation); + + if (!actorWalkTo(_protagonist->_id, pickLocation)) { + break; + } + } else if (!_vm->_isoMap->nextTileTarget(actor) && !actorEndWalk(actor->_id, true)) { + break; + } + + actor->_partialTarget.delta(actor->_location, delta); + actor->_partialTarget.z = 0; + } + + if (actor->_flags & kFastest) { + speed = 8; + } else if (actor->_flags & kFaster) { + speed = 6; + } else { + speed = 4; + } + + if (_vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) { + speed = 2; + } + + if ((actor->_actionDirection == 2) || (actor->_actionDirection == 6)) { + speed = speed / 2; + } + + if (ABS(delta.v()) > ABS(delta.u())) { + addDelta.v() = clamp(-speed, delta.v(), speed); + if (addDelta.v() == delta.v()) { + addDelta.u() = delta.u(); + } else { + addDelta.u() = delta.u() * addDelta.v(); + addDelta.u() += (addDelta.u() > 0) ? (delta.v() / 2) : (-delta.v() / 2); + addDelta.u() /= delta.v(); + } + } else { + addDelta.u() = clamp(-speed, delta.u(), speed); + if (addDelta.u() == delta.u()) { + addDelta.v() = delta.v(); + } else { + addDelta.v() = delta.v() * addDelta.u(); + addDelta.v() += (addDelta.v() > 0) ? (delta.u() / 2) : (-delta.u() / 2); + addDelta.v() /= delta.u(); + } + } + + actor->_location.add(addDelta); + } else { + actor->_partialTarget.delta(actor->_location, delta); + + while ((delta.x == 0) && (delta.y == 0)) { + + if (actor->_walkStepIndex >= actor->_walkStepsCount) { + actorEndWalk(actor->_id, true); + break; + } + + actor->_partialTarget.fromScreenPoint(actor->_walkStepsPoints[actor->_walkStepIndex++]); + if (_vm->getGameType() == GType_ITE) { + if (actor->_partialTarget.x > 224 * 2 * ACTOR_LMULT) { + actor->_partialTarget.x -= 256 * 2 * ACTOR_LMULT; + } + } else { + if (actor->_partialTarget.x > 224 * 4 * ACTOR_LMULT) { + actor->_partialTarget.x -= 256 * 4 * ACTOR_LMULT; + } + } + + actor->_partialTarget.delta(actor->_location, delta); + + if (ABS(delta.y) > ABS(delta.x)) { + actor->_actionDirection = delta.y > 0 ? kDirDown : kDirUp; + } else { + actor->_actionDirection = delta.x > 0 ? kDirRight : kDirLeft; + } + } + + if(_vm->getGameType() == GType_ITE) + speed = (ACTOR_LMULT * 2 * actor->_screenScale + 63) / 256; + else + speed = (ACTOR_SPEED * actor->_screenScale + 128) >> 8; + + if (speed < 1) + speed = 1; + + if(_vm->getGameType() == GType_IHNM) + speed = speed / 2; + + if ((actor->_actionDirection == kDirUp) || (actor->_actionDirection == kDirDown)) { + addDelta.y = clamp(-speed, delta.y, speed); + if (addDelta.y == delta.y) { + addDelta.x = delta.x; + } else { + addDelta.x = delta.x * addDelta.y; + addDelta.x += (addDelta.x > 0) ? (delta.y / 2) : (-delta.y / 2); + addDelta.x /= delta.y; + actor->_facingDirection = actor->_actionDirection; + } + } else { + addDelta.x = clamp(-2 * speed, delta.x, 2 * speed); + if (addDelta.x == delta.x) { + addDelta.y = delta.y; + } else { + addDelta.y = delta.y * addDelta.x; + addDelta.y += (addDelta.y > 0) ? (delta.x / 2) : (-delta.x / 2); + addDelta.y /= delta.x; + actor->_facingDirection = actor->_actionDirection; + } + } + + actor->_location.add(addDelta); + } + + if (actor->_actorFlags & kActorBackwards) { + actor->_facingDirection = (actor->_actionDirection + 4) & 7; + actor->_actionCycle--; + } else { + actor->_actionCycle++; + } + + frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence); + + if (actor->_actionCycle < 0) { + actor->_actionCycle = frameRange->frameCount - 1; + } else if (actor->_actionCycle >= frameRange->frameCount) { + actor->_actionCycle = 0; + } + + actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; + break; + + case kActionWalkDir: + if (_vm->_scene->getFlags() & kSceneFlagISO) { + actor->_location.u() += tileDirectionLUT[actor->_actionDirection][0]; + actor->_location.v() += tileDirectionLUT[actor->_actionDirection][1]; + + frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence); + + actor->_actionCycle++; + actor->cycleWrap(frameRange->frameCount); + actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; + } else { + if (_vm->getGameType() == GType_ITE) { + actor->_location.x += directionLUT[actor->_actionDirection][0] * 2; + actor->_location.y += directionLUT[actor->_actionDirection][1] * 2; + } else { + // FIXME: The original does not multiply by 8 here, but we do + actor->_location.x += (directionLUT[actor->_actionDirection][0] * 8 * actor->_screenScale + 128) >> 8; + actor->_location.y += (directionLUT[actor->_actionDirection][1] * 8 * actor->_screenScale + 128) >> 8; + } + + frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence); + actor->_actionCycle++; + actor->cycleWrap(frameRange->frameCount); + actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; + } + break; + + case kActionSpeak: + actor->_actionCycle++; + actor->cycleWrap(64); + + frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameGesture)); + if (actor->_actionCycle >= frameRange->frameCount) { + if (actor->_actionCycle & 1) + break; + frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameSpeak)); + + state = (uint16)_vm->_rnd.getRandomNumber(frameRange->frameCount); + + if (state == 0) { + frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand)); + } else { + state--; + } + } else { + state = actor->_actionCycle; + } + + actor->_frameNumber = frameRange->frameIndex + state; + break; + + case kActionAccept: + case kActionStoop: + break; + + case kActionCycleFrames: + case kActionPongFrames: + if (actor->_cycleTimeCount > 0) { + actor->_cycleTimeCount--; + break; + } + + actor->_cycleTimeCount = actor->_cycleDelay; + actor->_actionCycle++; + + frameRange = getActorFrameRange(actor->_id, actor->_cycleFrameSequence); + + if (actor->_currentAction == kActionPongFrames) { + if (actor->_actionCycle >= frameRange->frameCount * 2 - 2) { + if (actor->_actorFlags & kActorContinuous) { + actor->_actionCycle = 0; + } else { + actor->_currentAction = kActionFreeze; + break; + } + } + + state = actor->_actionCycle; + if (state >= frameRange->frameCount) { + state = frameRange->frameCount * 2 - 2 - state; + } + } else { + if (actor->_actionCycle >= frameRange->frameCount) { + if (actor->_actorFlags & kActorContinuous) { + actor->_actionCycle = 0; + } else { + actor->_currentAction = kActionFreeze; + break; + } + } + state = actor->_actionCycle; + } + + if (frameRange->frameCount && (actor->_actorFlags & kActorRandom)) { + state = _vm->_rnd.getRandomNumber(frameRange->frameCount - 1); + } + + if (actor->_actorFlags & kActorBackwards) { + actor->_frameNumber = frameRange->frameIndex + frameRange->frameCount - 1 - state; + } else { + actor->_frameNumber = frameRange->frameIndex + state; + } + break; + + case kActionFall: + if (actor->_actionCycle > 0) { + framesLeft = actor->_actionCycle--; + actor->_finalTarget.delta(actor->_location, delta); + delta.x /= framesLeft; + delta.y /= framesLeft; + actor->_location.addXY(delta); + actor->_fallVelocity += actor->_fallAcceleration; + actor->_fallPosition += actor->_fallVelocity; + actor->_location.z = actor->_fallPosition >> 4; + } else { + actor->_location = actor->_finalTarget; + actor->_currentAction = kActionFreeze; + _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor); + } + break; + + case kActionClimb: + actor->_cycleDelay++; + if (actor->_cycleDelay & 3) { + break; + } + + if (actor->_location.z >= actor->_finalTarget.z + ACTOR_CLIMB_SPEED) { + actor->_location.z -= ACTOR_CLIMB_SPEED; + actor->_actionCycle--; + } else if (actor->_location.z <= actor->_finalTarget.z - ACTOR_CLIMB_SPEED) { + actor->_location.z += ACTOR_CLIMB_SPEED; + actor->_actionCycle++; + } else { + actor->_location.z = actor->_finalTarget.z; + actor->_currentAction = kActionFreeze; + _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor); + } + + frameRange = getActorFrameRange(actor->_id, actor->_cycleFrameSequence); + + if (actor->_actionCycle < 0) { + actor->_actionCycle = frameRange->frameCount - 1; + } + actor->cycleWrap(frameRange->frameCount); + actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle; + break; + } + + if ((actor->_currentAction >= kActionWalkToPoint) && (actor->_currentAction <= kActionWalkDir)) { + hitZone = NULL; + + if (_vm->_scene->getFlags() & kSceneFlagISO) { + actor->_location.toScreenPointUV(hitPoint); + } else { + actor->_location.toScreenPointXY(hitPoint); + } + hitZoneIndex = _vm->_scene->_actionMap->hitTest(hitPoint); + if (hitZoneIndex != -1) { + hitZone = _vm->_scene->_actionMap->getHitZone(hitZoneIndex); + } + + if (hitZone != actor->_lastZone) { + if (actor->_lastZone) + stepZoneAction(actor, actor->_lastZone, true, false); + actor->_lastZone = hitZone; + // WORKAROUND for graphics glitch in the rat caves. Don't do this step zone action in the rat caves + // (room 51) for hitzone 24577 (the door with the copy protection) to avoid the glitch. This glitch + // happens because the copy protection is supposed to kick in at this point, but it's bypassed + // (with permission from Wyrmkeep Entertainment) + if (hitZone && + !(_vm->getGameType() == GType_ITE && _vm->_scene->currentSceneNumber() == 51 && hitZone->getHitZoneId() == 24577)) + stepZoneAction(actor, hitZone, false, false); + } + } + } + // Update frameCount for sfWaitFrames in IHNM + _vm->_frameCount++; +} + +void Actor::direct(int msec) { + + if (_vm->_scene->_entryList.entryListCount == 0) { + return; + } + + if (_vm->_interface->_statusTextInput) { + return; + } + + // FIXME: HACK. This should be turned into cycle event. + _lastTickMsec += msec; + + if (_lastTickMsec > 1000 / _handleActionDiv) { + _lastTickMsec = 0; + //process actions + handleActions(msec, false); + } + +//process speech + handleSpeech(msec); +} + +bool Actor::followProtagonist(ActorData *actor) { + Location protagonistLocation; + Location newLocation; + Location delta; + int protagonistBGMaskType; + Point prefer1; + Point prefer2; + Point prefer3; + int16 prefU; + int16 prefV; + int16 newU; + int16 newV; + + assert(_protagonist); + + actor->_flags &= ~(kFaster | kFastest); + protagonistLocation = _protagonist->_location; + calcScreenPosition(_protagonist); + + if (_vm->_scene->getFlags() & kSceneFlagISO) { + prefU = 60; + prefV = 60; + + + actor->_location.delta(protagonistLocation, delta); + + if (actor->_id == actorIndexToId(2)) { + prefU = prefV = 48; + } + + if ((delta.u() > prefU) || (delta.u() < -prefU) || (delta.v() > prefV) || (delta.v() < -prefV)) { + + if ((delta.u() > prefU * 2) || (delta.u() < -prefU * 2) || (delta.v() > prefV * 2) || (delta.v() < -prefV * 2)) { + actor->_flags |= kFaster; + + if ((delta.u() > prefU * 3) || (delta.u() < -prefU*3) || (delta.v() > prefV * 3) || (delta.v() < -prefV * 3)) { + actor->_flags |= kFastest; + } + } + + prefU /= 2; + prefV /= 2; + + newU = clamp(-prefU, delta.u(), prefU) + protagonistLocation.u(); + newV = clamp(-prefV, delta.v(), prefV) + protagonistLocation.v(); + + newLocation.u() = newU + _vm->_rnd.getRandomNumber(prefU - 1) - prefU / 2; + newLocation.v() = newV + _vm->_rnd.getRandomNumber(prefV - 1) - prefV / 2; + newLocation.z = 0; + + return actorWalkTo(actor->_id, newLocation); + } + + } else { + prefer1.x = (100 * _protagonist->_screenScale) >> 8; + prefer1.y = (50 * _protagonist->_screenScale) >> 8; + + if (_protagonist->_currentAction == kActionWalkDir) { + prefer1.x /= 2; + } + + if (prefer1.x < 8) { + prefer1.x = 8; + } + + if (prefer1.y < 8) { + prefer1.y = 8; + } + + prefer2.x = prefer1.x * 2; + prefer2.y = prefer1.y * 2; + prefer3.x = prefer1.x + prefer1.x / 2; + prefer3.y = prefer1.y + prefer1.y / 2; + + actor->_location.delta(protagonistLocation, delta); + + protagonistBGMaskType = 0; + if (_vm->_scene->isBGMaskPresent() && _vm->_scene->validBGMaskPoint(_protagonist->_screenPosition)) { + protagonistBGMaskType = _vm->_scene->getBGMaskType(_protagonist->_screenPosition); + } + + if ((_vm->_rnd.getRandomNumber(7) & 0x7) == 0) // 1/8th chance + actor->_actorFlags &= ~kActorNoFollow; + + if (actor->_actorFlags & kActorNoFollow) { + return false; + } + + if ((delta.x > prefer2.x) || (delta.x < -prefer2.x) || + (delta.y > prefer2.y) || (delta.y < -prefer2.y) || + ((_protagonist->_currentAction == kActionWait) && + (delta.x * 2 < prefer1.x) && (delta.x * 2 > -prefer1.x) && + (delta.y < prefer1.y) && (delta.y > -prefer1.y))) { + + if (ABS(delta.x) > ABS(delta.y)) { + + delta.x = (delta.x > 0) ? prefer3.x : -prefer3.x; + + newLocation.x = delta.x + protagonistLocation.x; + newLocation.y = clamp(-prefer2.y, delta.y, prefer2.y) + protagonistLocation.y; + } else { + delta.y = (delta.y > 0) ? prefer3.y : -prefer3.y; + + newLocation.x = clamp(-prefer2.x, delta.x, prefer2.x) + protagonistLocation.x; + newLocation.y = delta.y + protagonistLocation.y; + } + newLocation.z = 0; + + if (protagonistBGMaskType != 3) { + newLocation.x += _vm->_rnd.getRandomNumber(prefer1.x - 1) - prefer1.x / 2; + newLocation.y += _vm->_rnd.getRandomNumber(prefer1.y - 1) - prefer1.y / 2; + } + + newLocation.x = clamp(-31 * 4, newLocation.x, (_vm->getDisplayWidth() + 31) * 4); + + return actorWalkTo(actor->_id, newLocation); + } + } + return false; +} + +bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) { + ActorData *actor; + ActorData *anotherActor; + int i; + + Rect testBox; + Rect testBox2; + Point anotherActorScreenPosition; + Point collision; + Point pointFrom, pointTo, pointBest, pointAdd; + Point delta, bestDelta; + Point tempPoint; + bool extraStartNode; + bool extraEndNode; + + actor = getActor(actorId); + + if (actor == _protagonist) { + _vm->_scene->setDoorState(2, 0xff); + _vm->_scene->setDoorState(3, 0); + } else { + _vm->_scene->setDoorState(2, 0); + _vm->_scene->setDoorState(3, 0xff); + } + + if (_vm->_scene->getFlags() & kSceneFlagISO) { + + if ((_vm->getGameType() == GType_ITE) && (actor->_index == ACTOR_DRAGON_INDEX)) { + return false; + } + + actor->_finalTarget = toLocation; + actor->_walkStepsCount = 0; + _vm->_isoMap->findTilePath(actor, actor->_location, toLocation); + + + if ((actor->_walkStepsCount == 0) && (actor->_flags & kProtagonist)) { + actor->_actorFlags |= kActorNoCollide; + _vm->_isoMap->findTilePath(actor, actor->_location, toLocation); + } + + actor->_walkStepIndex = 0; + if (_vm->_isoMap->nextTileTarget(actor)) { + actor->_currentAction = kActionWalkToPoint; + actor->_walkFrameSequence = getFrameType(kFrameWalk); + } else { + actorEndWalk(actorId, false); + return false; + } + } else { + + actor->_location.toScreenPointXY(pointFrom); + pointFrom.x &= ~1; + + extraStartNode = _vm->_scene->offscreenPath(pointFrom); + + toLocation.toScreenPointXY(pointTo); + pointTo.x &= ~1; + + extraEndNode = _vm->_scene->offscreenPath(pointTo); + + if (_vm->_scene->isBGMaskPresent()) { + + if ((((actor->_currentAction >= kActionWalkToPoint) && + (actor->_currentAction <= kActionWalkDir)) || (actor == _protagonist)) && + !_vm->_scene->canWalk(pointFrom)) { + + int max = _vm->getGameType() == GType_ITE ? 8 : 4; + + for (i = 1; i < max; i++) { + pointAdd = pointFrom; + pointAdd.y += i; + if (_vm->_scene->canWalk(pointAdd)) { + pointFrom = pointAdd; + break; + } + pointAdd = pointFrom; + pointAdd.y -= i; + if (_vm->_scene->canWalk(pointAdd)) { + pointFrom = pointAdd; + break; + } + if (_vm->getGameType() == GType_ITE) { + pointAdd = pointFrom; + pointAdd.x += i; + if (_vm->_scene->canWalk(pointAdd)) { + pointFrom = pointAdd; + break; + } + pointAdd = pointFrom; + pointAdd.x -= i; + if (_vm->_scene->canWalk(pointAdd)) { + pointFrom = pointAdd; + break; + } + } + } + } + + _barrierCount = 0; + if (!(actor->_actorFlags & kActorNoCollide)) { + collision.x = ACTOR_COLLISION_WIDTH * actor->_screenScale / (256 * 2); + collision.y = ACTOR_COLLISION_HEIGHT * actor->_screenScale / (256 * 2); + + + for (i = 0; (i < _actorsCount) && (_barrierCount < ACTOR_BARRIERS_MAX); i++) { + anotherActor = _actors[i]; + if (!anotherActor->_inScene) + continue; + if (anotherActor == actor) + continue; + + anotherActorScreenPosition = anotherActor->_screenPosition; + testBox.left = (anotherActorScreenPosition.x - collision.x) & ~1; + testBox.right = (anotherActorScreenPosition.x + collision.x) & ~1 + 1; + testBox.top = anotherActorScreenPosition.y - collision.y; + testBox.bottom = anotherActorScreenPosition.y + collision.y + 1; + testBox2 = testBox; + testBox2.right += 2; + testBox2.left -= 2; + testBox2.top -= 1; + testBox2.bottom += 1; + + if (testBox2.contains(pointFrom)) { + if (pointFrom.x > anotherActorScreenPosition.x + 4) { + testBox.right = pointFrom.x - 1; + } else if (pointFrom.x < anotherActorScreenPosition.x - 4) { + testBox.left = pointFrom.x + 2; + } else if (pointFrom.y > anotherActorScreenPosition.y) { + testBox.bottom = pointFrom.y; + } else { + testBox.top = pointFrom.y + 1 ; + } + } + + if ((testBox.width() > 0) && (testBox.height() > 0)) { + _barrierList[_barrierCount++] = testBox; + } + } + } + + + pointBest = pointTo; + actor->_walkStepsCount = 0; + findActorPath(actor, pointFrom, pointTo); + + if (actor->_walkStepsCount == 0) { + error("actor->_walkStepsCount == 0"); + } + + if (extraStartNode) { + actor->_walkStepIndex = 0; + } else { + actor->_walkStepIndex = 1; + } + + if (extraEndNode) { + toLocation.toScreenPointXY(tempPoint); + actor->_walkStepsCount--; + actor->addWalkStepPoint(tempPoint); + } + + + pointBest = actor->_walkStepsPoints[actor->_walkStepsCount - 1]; + + pointBest.x &= ~1; + delta.x = ABS(pointFrom.x - pointTo.x); + delta.y = ABS(pointFrom.y - pointTo.y); + + bestDelta.x = ABS(pointBest.x - pointTo.x); + bestDelta.y = ABS(pointBest.y - pointTo.y); + + if ((delta.x + delta.y <= bestDelta.x + bestDelta.y) && (actor->_flags & kFollower)) { + actor->_actorFlags |= kActorNoFollow; + } + + if (pointBest == pointFrom) { + actor->_walkStepsCount = 0; + } + } else { + actor->_walkStepsCount = 0; + actor->addWalkStepPoint(pointTo); + actor->_walkStepIndex = 0; + } + + actor->_partialTarget = actor->_location; + actor->_finalTarget = toLocation; + if (actor->_walkStepsCount == 0) { + actorEndWalk(actorId, false); + return false; + } else { + if (actor->_flags & kProtagonist) { + _actors[1]->_actorFlags &= ~kActorNoFollow; // TODO: mark all actors with kFollower flag, not only 1 and 2 + _actors[2]->_actorFlags &= ~kActorNoFollow; + } + actor->_currentAction = (actor->_walkStepsCount >= ACTOR_MAX_STEPS_COUNT) ? kActionWalkToLink : kActionWalkToPoint; + actor->_walkFrameSequence = getFrameType(kFrameWalk); + } + } + return true; +} + +bool Actor::actorEndWalk(uint16 actorId, bool recurse) { + bool walkMore = false; + ActorData *actor; + const HitZone *hitZone; + int hitZoneIndex; + Point testPoint; + + actor = getActor(actorId); + actor->_actorFlags &= ~kActorBackwards; + + if (_vm->getGameType() == GType_ITE) { + + if (actor->_location.distance(actor->_finalTarget) > 8 && (actor->_flags & kProtagonist) && recurse && !(actor->_actorFlags & kActorNoCollide)) { + actor->_actorFlags |= kActorNoCollide; + return actorWalkTo(actorId, actor->_finalTarget); + } + } + + actor->_currentAction = kActionWait; + if (actor->_actorFlags & kActorFinalFace) { + actor->_facingDirection = actor->_actionDirection = (actor->_actorFlags >> 6) & 0x07; //? + } + + actor->_actorFlags &= ~(kActorNoCollide | kActorCollided | kActorFinalFace | kActorFacingMask); + actor->_flags &= ~(kFaster | kFastest); + + if (actor == _protagonist) { + _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor); + if (_vm->_script->_pendingVerb == _vm->_script->getVerbType(kVerbWalkTo)) { + if (_vm->getGameType() == GType_ITE) + actor->_location.toScreenPointUV(testPoint); // it's wrong calculation, but it is used in ITE + else + actor->_location.toScreenPointXY(testPoint); + + hitZoneIndex = _vm->_scene->_actionMap->hitTest(testPoint); + if (hitZoneIndex != -1) { + hitZone = _vm->_scene->_actionMap->getHitZone(hitZoneIndex); + stepZoneAction(actor, hitZone, false, true); + } else { + _vm->_script->setNoPendingVerb(); + } + } else if (_vm->_script->_pendingVerb != _vm->_script->getVerbType(kVerbNone)) { + _vm->_script->doVerb(); + } + } else { + if (recurse && (actor->_flags & kFollower)) + walkMore = followProtagonist(actor); + + _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor); + } + return walkMore; +} + +void Actor::moveDragon(ActorData *actor) { + int16 dir0, dir1, dir2, dir3; + int16 moveType; + Event event; + const DragonMove *dragonMove; + + if ((actor->_actionCycle < 0) || + ((actor->_actionCycle == 0) && (actor->_dragonMoveType >= ACTOR_DRAGON_TURN_MOVES))) { + + moveType = kDragonMoveInvalid; + if (actor->_location.distance(_protagonist->_location) < 24) { + if (_dragonHunt && (_protagonist->_currentAction != kActionFall)) { + event.type = kEvTOneshot; + event.code = kScriptEvent; + event.op = kEventExecNonBlocking; + event.time = 0; + event.param = _vm->_scene->getScriptModuleNumber(); // module number + event.param2 = ACTOR_EXP_KNOCK_RIF; // script entry point number + event.param3 = -1; // Action + event.param4 = -1; // Object + event.param5 = -1; // With Object + event.param6 = -1; // Actor + _vm->_events->queue(&event); + + _dragonHunt = false; + } + } else { + _dragonHunt = true; + } + + if (actor->_walkStepIndex + 2 > actor->_walkStepsCount) { + + _vm->_isoMap->findDragonTilePath(actor, actor->_location, _protagonist->_location, actor->_actionDirection); + + if (actor->_walkStepsCount == 0) { + _vm->_isoMap->findDragonTilePath(actor, actor->_location, _protagonist->_location, 0); + } + + if (actor->_walkStepsCount < 2) { + return; + } + + actor->_partialTarget = actor->_location; + actor->_finalTarget = _protagonist->_location; + actor->_walkStepIndex = 0; + } + + dir0 = actor->_actionDirection; + dir1 = actor->_tileDirections[actor->_walkStepIndex++]; + dir2 = actor->_tileDirections[actor->_walkStepIndex]; + dir3 = actor->_tileDirections[actor->_walkStepIndex + 1]; + + if (dir0 != dir1){ + actor->_actionDirection = dir0 = dir1; + } + + actor->_location = actor->_partialTarget; + + if ((dir1 != dir2) && (dir1 == dir3)) { + switch (dir1) { + case kDirUpLeft: + actor->_partialTarget.v() += 16; + moveType = kDragonMoveUpLeft; + break; + case kDirDownLeft: + actor->_partialTarget.u() -= 16; + moveType = kDragonMoveDownLeft; + break; + case kDirDownRight: + actor->_partialTarget.v() -= 16; + moveType = kDragonMoveDownRight; + break; + case kDirUpRight: + actor->_partialTarget.u() += 16; + moveType = kDragonMoveUpRight; + break; + } + + switch (dir2) { + case kDirUpLeft: + actor->_partialTarget.v() += 16; + break; + case kDirDownLeft: + actor->_partialTarget.u() -= 16; + break; + case kDirDownRight: + actor->_partialTarget.v() -= 16; + break; + case kDirUpRight: + actor->_partialTarget.u() += 16; + break; + } + + actor->_walkStepIndex++; + } else { + switch (dir1) { + case kDirUpLeft: + actor->_partialTarget.v() += 16; + switch (dir2) { + case kDirDownLeft: + moveType = kDragonMoveUpLeft_Left; + actor->_partialTarget.u() -= 16; + break; + case kDirUpLeft: + moveType = kDragonMoveUpLeft; + break; + case kDirUpRight: + actor->_partialTarget.u() += 16; + moveType = kDragonMoveUpLeft_Right; + break; + default: + actor->_actionDirection = dir1; + actor->_walkStepsCount = 0; + break; + } + break; + case kDirDownLeft: + actor->_partialTarget.u() -= 16; + switch (dir2) { + case kDirDownRight: + moveType = kDragonMoveDownLeft_Left; + actor->_partialTarget.v() -= 16; + break; + case kDirDownLeft: + moveType = kDragonMoveDownLeft; + break; + case kDirUpLeft: + moveType = kDragonMoveDownLeft_Right; + actor->_partialTarget.v() += 16; + break; + default: + actor->_actionDirection = dir1; + actor->_walkStepsCount = 0; + break; + } + break; + case kDirDownRight: + actor->_partialTarget.v() -= 16; + switch (dir2) { + case kDirUpRight: + moveType = kDragonMoveDownRight_Left; + actor->_partialTarget.u() += 16; + break; + case kDirDownRight: + moveType = kDragonMoveDownRight; + break; + case kDirDownLeft: + moveType = kDragonMoveDownRight_Right; + actor->_partialTarget.u() -= 16; + break; + default: + actor->_actionDirection = dir1; + actor->_walkStepsCount = 0; + break; + } + break; + case kDirUpRight: + actor->_partialTarget.u() += 16; + switch (dir2) { + case kDirUpLeft: + moveType = kDragonMoveUpRight_Left; + actor->_partialTarget.v() += 16; + break; + case kDirUpRight: + moveType = kDragonMoveUpRight; + break; + case kDirDownRight: + moveType = kDragonMoveUpRight_Right; + actor->_partialTarget.v() -= 16; + break; + default: + actor->_actionDirection = dir1; + actor->_walkStepsCount = 0; + break; + } + break; + + default: + actor->_actionDirection = dir1; + actor->_walkStepsCount = 0; + break; + } + } + + actor->_dragonMoveType = moveType; + + if (moveType >= ACTOR_DRAGON_TURN_MOVES) { + actor->_dragonStepCycle = 0; + actor->_actionCycle = 4; + actor->_walkStepIndex++; + } else { + actor->_actionCycle = 4; + } + } + + actor->_actionCycle--; + + if ((actor->_walkStepsCount < 1) || (actor->_actionCycle < 0)) { + return; + } + + if (actor->_dragonMoveType < ACTOR_DRAGON_TURN_MOVES) { + + actor->_dragonStepCycle++; + if (actor->_dragonStepCycle >= 7) { + actor->_dragonStepCycle = 0; + } + + actor->_dragonBaseFrame = actor->_dragonMoveType * 7; + + if (actor->_location.u() > actor->_partialTarget.u() + 3) { + actor->_location.u() -= 4; + } else if (actor->_location.u() < actor->_partialTarget.u() - 3) { + actor->_location.u() += 4; + } else { + actor->_location.u() = actor->_partialTarget.u(); + } + + if (actor->_location.v() > actor->_partialTarget.v() + 3) { + actor->_location.v() -= 4; + } else if (actor->_location.v() < actor->_partialTarget.v() - 3) { + actor->_location.v() += 4; + } else { + actor->_location.v() = actor->_partialTarget.v(); + } + } else { + dragonMove = &dragonMoveTable[actor->_dragonMoveType]; + actor->_dragonBaseFrame = dragonMove->baseFrame; + + + actor->_location.u() = actor->_partialTarget.u() - dragonMove->offset[actor->_actionCycle][0]; + actor->_location.v() = actor->_partialTarget.v() - dragonMove->offset[actor->_actionCycle][1]; + + actor->_dragonStepCycle++; + if (actor->_dragonStepCycle >= 3) { + actor->_dragonStepCycle = 3; + } + } + + actor->_frameNumber = actor->_dragonBaseFrame + actor->_dragonStepCycle; +} + +void Actor::findActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint) { + Point iteratorPoint; + Point bestPoint; + int maskType; + int i; + Rect intersect; + +#ifdef ACTOR_DEBUG + _debugPointsCount = 0; +#endif + + actor->_walkStepsCount = 0; + if (fromPoint == toPoint) { + actor->addWalkStepPoint(toPoint); + return; + } + + for (iteratorPoint.y = 0; iteratorPoint.y < _yCellCount; iteratorPoint.y++) { + for (iteratorPoint.x = 0; iteratorPoint.x < _xCellCount; iteratorPoint.x++) { + if (_vm->_scene->validBGMaskPoint(iteratorPoint)) { + maskType = _vm->_scene->getBGMaskType(iteratorPoint); + setPathCell(iteratorPoint, _vm->_scene->getDoorState(maskType) ? kPathCellBarrier : kPathCellEmpty); + } else { + setPathCell(iteratorPoint, kPathCellBarrier); + } + } + } + + for (i = 0; i < _barrierCount; i++) { + intersect.left = MAX(_pathRect.left, _barrierList[i].left); + intersect.top = MAX(_pathRect.top, _barrierList[i].top); + intersect.right = MIN(_pathRect.right, _barrierList[i].right); + intersect.bottom = MIN(_pathRect.bottom, _barrierList[i].bottom); + + for (iteratorPoint.y = intersect.top; iteratorPoint.y < intersect.bottom; iteratorPoint.y++) { + for (iteratorPoint.x = intersect.left; iteratorPoint.x < intersect.right; iteratorPoint.x++) { + setPathCell(iteratorPoint, kPathCellBarrier); + } + } + } + +#ifdef ACTOR_DEBUG + for (iteratorPoint.y = 0; iteratorPoint.y < _yCellCount; iteratorPoint.y++) { + for (iteratorPoint.x = 0; iteratorPoint.x < _xCellCount; iteratorPoint.x++) { + if (getPathCell(iteratorPoint) == kPathCellBarrier) { + addDebugPoint(iteratorPoint, 24); + } + } + } +#endif + + if (scanPathLine(fromPoint, toPoint)) { + actor->addWalkStepPoint(fromPoint); + actor->addWalkStepPoint(toPoint); + return; + } + + i = fillPathArray(fromPoint, toPoint, bestPoint); + + if (fromPoint == bestPoint) { + actor->addWalkStepPoint(bestPoint); + return; + } + + if (i == 0) { + error("fillPathArray returns zero"); + } + + setActorPath(actor, fromPoint, bestPoint); +} + +bool Actor::scanPathLine(const Point &point1, const Point &point2) { + Point point; + Point delta; + Point s; + Point fDelta; + int16 errterm; + + calcDeltaS(point1, point2, delta, s); + point = point1; + + fDelta.x = delta.x * 2; + fDelta.y = delta.y * 2; + + if (delta.y > delta.x) { + + errterm = fDelta.x - delta.y; + + while (delta.y > 0) { + while (errterm >= 0) { + point.x += s.x; + errterm -= fDelta.y; + } + + point.y += s.y; + errterm += fDelta.x; + + if (!validPathCellPoint(point)) { + return false; + } + if (getPathCell(point) == kPathCellBarrier) { + return false; + } + delta.y--; + } + } else { + + errterm = fDelta.y - delta.x; + + while (delta.x > 0) { + while (errterm >= 0) { + point.y += s.y; + errterm -= fDelta.x; + } + + point.x += s.x; + errterm += fDelta.y; + + if (!validPathCellPoint(point)) { + return false; + } + if (getPathCell(point) == kPathCellBarrier) { + return false; + } + delta.x--; + } + } + return true; +} + +int Actor::fillPathArray(const Point &fromPoint, const Point &toPoint, Point &bestPoint) { + int bestRating; + int currentRating; + int i; + Point bestPath; + int pointCounter; + int startDirection; + PathDirectionData *pathDirection; + PathDirectionData *newPathDirection; + const PathDirectionData *samplePathDirection; + Point nextPoint; + int directionCount; + int16 compressX = (_vm->getGameType() == GType_ITE) ? 2 : 1; + + _pathDirectionListCount = 0; + pointCounter = 0; + bestRating = quickDistance(fromPoint, toPoint, compressX); + bestPath = fromPoint; + + for (startDirection = 0; startDirection < 4; startDirection++) { + newPathDirection = addPathDirectionListData(); + newPathDirection->coord = fromPoint; + newPathDirection->direction = startDirection; + } + + if (validPathCellPoint(fromPoint)) { + setPathCell(fromPoint, kDirUp); + +#ifdef ACTOR_DEBUG + addDebugPoint(fromPoint, 24+36); +#endif + } + + i = 0; + + do { + pathDirection = &_pathDirectionList[i]; + for (directionCount = 0; directionCount < 3; directionCount++) { + samplePathDirection = &pathDirectionLUT[pathDirection->direction][directionCount]; + nextPoint = pathDirection->coord; + nextPoint.x += samplePathDirection->coord.x; + nextPoint.y += samplePathDirection->coord.y; + + if (!validPathCellPoint(nextPoint)) { + continue; + } + + if (getPathCell(nextPoint) != kPathCellEmpty) { + continue; + } + + setPathCell(nextPoint, samplePathDirection->direction); + +#ifdef ACTOR_DEBUG + addDebugPoint(nextPoint, samplePathDirection->direction + 96); +#endif + newPathDirection = addPathDirectionListData(); + newPathDirection->coord = nextPoint; + newPathDirection->direction = samplePathDirection->direction; + ++pointCounter; + if (nextPoint == toPoint) { + bestPoint = toPoint; + return pointCounter; + } + currentRating = quickDistance(nextPoint, toPoint, compressX); + if (currentRating < bestRating) { + bestRating = currentRating; + bestPath = nextPoint; + } + pathDirection = &_pathDirectionList[i]; + } + ++i; + } while (i < _pathDirectionListCount); + + bestPoint = bestPath; + return pointCounter; +} + +void Actor::setActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint) { + Point nextPoint; + int8 direction; + int i; + + _pathListIndex = -1; + addPathListPoint(toPoint); + nextPoint = toPoint; + + while (!(nextPoint == fromPoint)) { + direction = getPathCell(nextPoint); + if ((direction < 0) || (direction >= 8)) { + error("Actor::setActorPath error direction 0x%X", direction); + } + nextPoint.x -= pathDirectionLUT2[direction][0]; + nextPoint.y -= pathDirectionLUT2[direction][1]; + addPathListPoint(nextPoint); + +#ifdef ACTOR_DEBUG + addDebugPoint(nextPoint, 0x8a); +#endif + } + + pathToNode(); + removeNodes(); + nodeToPath(); + removePathPoints(); + + for (i = 0; i <= _pathNodeListIndex; i++) { + actor->addWalkStepPoint(_pathNodeList[i].point); + } +} + +void Actor::pathToNode() { + Point point1, point2, delta; + int direction; + int i; + Point *point; + + point= &_pathList[_pathListIndex]; + direction = 0; + + _pathNodeListIndex = -1; + addPathNodeListPoint(*point); + + for (i = _pathListIndex; i > 0; i--) { + point1 = *point; + --point; + point2 = *point; + if (direction == 0) { + delta.x = int16Compare(point2.x, point1.x); + delta.y = int16Compare(point2.y, point1.y); + direction++; + } + if ((point1.x + delta.x != point2.x) || (point1.y + delta.y != point2.y)) { + addPathNodeListPoint(point1); + direction--; + i++; + point++; + } + } + addPathNodeListPoint(*_pathList); +} + +int pathLine(Point *pointList, const Point &point1, const Point &point2) { + Point point; + Point delta; + Point tempPoint; + Point s; + int16 errterm; + int16 res; + + calcDeltaS(point1, point2, delta, s); + + point = point1; + + tempPoint.x = delta.x * 2; + tempPoint.y = delta.y * 2; + + if (delta.y > delta.x) { + + errterm = tempPoint.x - delta.y; + res = delta.y; + + while (delta.y > 0) { + while (errterm >= 0) { + point.x += s.x; + errterm -= tempPoint.y; + } + + point.y += s.y; + errterm += tempPoint.x; + + *pointList = point; + pointList++; + delta.y--; + } + } else { + + errterm = tempPoint.y - delta.x; + res = delta.x; + + while (delta.x > 0) { + while (errterm >= 0) { + point.y += s.y; + errterm -= tempPoint.x; + } + + point.x += s.x; + errterm += tempPoint.y; + + *pointList = point; + pointList++; + delta.x--; + } + } + return res; +} + +void Actor::nodeToPath() { + int i; + Point point1, point2; + PathNode *node; + Point *point; + + for (i = 0, point = _pathList; i < _pathListAlloced; i++, point++) { + point->x = point->y = PATH_NODE_EMPTY; + } + + _pathListIndex = 1; + _pathList[0] = _pathNodeList[0].point; + _pathNodeList[0].link = 0; + for (i = 0, node = _pathNodeList; i < _pathNodeListIndex; i++) { + point1 = node->point; + node++; + point2 = node->point; + _pathListIndex += pathLine(&_pathList[_pathListIndex], point1, point2); + node->link = _pathListIndex - 1; + } + _pathListIndex--; + _pathNodeList[_pathNodeListIndex].link = _pathListIndex; + +} + +void Actor::removeNodes() { + int i, j, k; + PathNode *iNode, *jNode, *kNode, *fNode; + fNode = &_pathNodeList[_pathNodeListIndex]; + + if (scanPathLine(_pathNodeList[0].point, fNode->point)) { + _pathNodeList[1] = *fNode; + _pathNodeListIndex = 1; + } + + if (_pathNodeListIndex < 4) { + return; + } + + for (i = _pathNodeListIndex - 1, iNode = fNode-1; i > 1 ; i--, iNode--) { + if (iNode->point.x == PATH_NODE_EMPTY) { + continue; + } + + if (scanPathLine(_pathNodeList[0].point, iNode->point)) { + for (j = 1, jNode = _pathNodeList + 1; j < i; j++, jNode++) { + jNode->point.x = PATH_NODE_EMPTY; + } + } + } + + for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex - 1; i++, iNode++) { + if (iNode->point.x == PATH_NODE_EMPTY) { + continue; + } + + if (scanPathLine(fNode->point, iNode->point)) { + for (j = i + 1, jNode = iNode + 1; j < _pathNodeListIndex; j++, jNode++) { + jNode->point.x = PATH_NODE_EMPTY; + } + } + } + condenseNodeList(); + + for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex - 1; i++, iNode++) { + if (iNode->point.x == PATH_NODE_EMPTY) { + continue; + } + for (j = i + 2, jNode = iNode + 2; j < _pathNodeListIndex; j++, jNode++) { + if (jNode->point.x == PATH_NODE_EMPTY) { + continue; + } + + if (scanPathLine(iNode->point, jNode->point)) { + for (k = i + 1,kNode = iNode + 1; k < j; k++, kNode++) { + kNode->point.x = PATH_NODE_EMPTY; + } + } + } + } + condenseNodeList(); +} + +void Actor::condenseNodeList() { + int i, j, count; + PathNode *iNode, *jNode; + + count = _pathNodeListIndex; + + for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex; i++, iNode++) { + if (iNode->point.x == PATH_NODE_EMPTY) { + j = i + 1; + jNode = iNode + 1; + while (jNode->point.x == PATH_NODE_EMPTY) { + j++; + jNode++; + } + *iNode = *jNode; + count = i; + jNode->point.x = PATH_NODE_EMPTY; + if (j == _pathNodeListIndex) { + break; + } + } + } + _pathNodeListIndex = count; +} + +void Actor::removePathPoints() { + int i, j, k, l; + PathNode *node; + int start; + int end; + Point point1, point2; + + if (_pathNodeListIndex < 2) + return; + + _newPathNodeListIndex = -1; + addNewPathNodeListPoint(_pathNodeList[0]); + + for (i = 1, node = _pathNodeList + 1; i < _pathNodeListIndex; i++, node++) { + addNewPathNodeListPoint(*node); + + for (j = 5; j > 0; j--) { + start = node->link - j; + end = node->link + j; + + if (start < 0 || end > _pathListIndex) { + continue; + } + + point1 = _pathList[start]; + point2 = _pathList[end]; + if ((point1.x == PATH_NODE_EMPTY) || (point2.x == PATH_NODE_EMPTY)) { + continue; + } + + if (scanPathLine(point1, point2)) { + for (l = 1; l <= _newPathNodeListIndex; l++) { + if (start <= _newPathNodeList[l].link) { + _newPathNodeListIndex = l; + _newPathNodeList[_newPathNodeListIndex].point = point1; + _newPathNodeList[_newPathNodeListIndex].link = start; + incrementNewPathNodeListIndex(); + break; + } + } + _newPathNodeList[_newPathNodeListIndex].point = point2; + _newPathNodeList[_newPathNodeListIndex].link = end; + + for (k = start + 1; k < end; k++) { + _pathList[k].x = PATH_NODE_EMPTY; + } + break; + } + } + } + + addNewPathNodeListPoint(_pathNodeList[_pathNodeListIndex]); + + for (i = 0, j = 0; i <= _newPathNodeListIndex; i++) { + if (_newPathNodeListIndex == i || (_newPathNodeList[i].point != _newPathNodeList[i+1].point)) { + _pathNodeList[j++] = _newPathNodeList[i]; + } + } + _pathNodeListIndex = j - 1; +} + +void Actor::drawPathTest() { +#ifdef ACTOR_DEBUG + int i; + Surface *surface; + surface = _vm->_gfx->getBackBuffer(); + if (_debugPoints == NULL) { + return; + } + + for (i = 0; i < _debugPointsCount; i++) { + *((byte *)surface->pixels + (_debugPoints[i].point.y * surface->pitch) + _debugPoints[i].point.x) = _debugPoints[i].color; + } +#endif +} + +// Console wrappers - must be safe to run + +void Actor::cmdActorWalkTo(int argc, const char **argv) { + uint16 actorId = (uint16) atoi(argv[1]); + Location location; + Point movePoint; + + movePoint.x = atoi(argv[2]); + movePoint.y = atoi(argv[3]); + + location.fromScreenPoint(movePoint); + + if (!validActorId(actorId)) { + _vm->_console->DebugPrintf("Actor::cmActorWalkTo Invalid actorId 0x%X.\n", actorId); + return; + } + + actorWalkTo(actorId, location); +} + +} // End of namespace Saga diff --git a/engines/saga/animation.cpp b/engines/saga/animation.cpp index 7b3bdcd665..09b2ba412d 100644 --- a/engines/saga/animation.cpp +++ b/engines/saga/animation.cpp @@ -78,92 +78,75 @@ void Anim::freeCutawayList(void) { _cutawayListLength = 0; } -void Anim::playCutaway(int cut, bool fade) { +int Anim::playCutaway(int cut, bool fade) { debug(0, "playCutaway(%d, %d)", cut, fade); + Event event; + Event *q_event = NULL; bool startImmediately = false; + byte *resourceData; + size_t resourceDataLength; + ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE); _cutAwayFade = fade; - // Chained cutaway, clean up the previous cutaway - if (_cutawayActive) { - clearCutaway(); - - // This is used because when AM is zapping the child's mother in Benny's chapter, - // there is a cutaway followed by a video. The video needs to start immediately after - // the cutaway so that it looks like the original - startImmediately = true; - } - - // WORKAROUND: The IHNM demo deals with chained cutaways in a different manner. Don't save - // the palette of cutaway 11 (the woman looking at the marble) - if (!(_vm->getGameId() == GID_IHNM_DEMO && cut == 11)) - _vm->_gfx->savePalette(); + _vm->_gfx->savePalette(); + _vm->_gfx->getCurrentPal(saved_pal); if (fade) { - _vm->_gfx->getCurrentPal(saved_pal); - // TODO - /* - Event event; static PalEntry cur_pal[PAL_ENTRIES]; - _vm->_gfx->getCurrentPal(cur_pal); + _vm->_interface->setFadeMode(kFadeOut); + // Fade to black out + _vm->_gfx->getCurrentPal(cur_pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalToBlack; event.time = 0; event.duration = kNormalFadeDuration; event.data = cur_pal; + q_event = _vm->_events->queue(&event); - _vm->_events->queue(&event); - */ + // set fade mode + event.type = kEvTImmediate; + event.code = kInterfaceEvent; + event.op = kEventSetFadeMode; + event.param = kNoFade; + event.time = 0; + event.duration = 0; + q_event = _vm->_events->chain(q_event, &event); } // Prepare cutaway - _vm->_gfx->showCursor(false); - _vm->_interface->setStatusText(""); - _vm->_interface->setSaveReminderState(0); - _vm->_interface->rememberMode(); + if (!_cutawayActive) { + _vm->_gfx->showCursor(false); + _vm->_interface->setStatusText(""); + _vm->_interface->setSaveReminderState(0); + _vm->_interface->rememberMode(); + _cutawayActive = true; + } else { + // If another cutaway is up, start the next cutaway immediately + startImmediately = true; + } + if (_cutAwayMode == kPanelVideo) _vm->_interface->setMode(kPanelVideo); else _vm->_interface->setMode(kPanelCutaway); - _cutawayActive = true; - - // Set the initial background and palette for the cutaway - ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE); - - byte *resourceData; - size_t resourceDataLength; - - _vm->_resource->loadResource(context, _cutawayList[cut].backgroundResourceId, resourceData, resourceDataLength); - - byte *buf; - size_t buflen; - int width; - int height; - - _vm->decodeBGImage(resourceData, resourceDataLength, &buf, &buflen, &width, &height); - - const PalEntry *palette = (const PalEntry *)_vm->getImagePal(resourceData, resourceDataLength); - - Surface *bgSurface = _vm->_render->getBackGroundSurface(); - const Rect rect(width, height); - - bgSurface->blit(rect, buf); - _vm->_frameCount++; - - _vm->_gfx->setPalette(palette); - // WORKAROUND for a bug found in the original IHNM demo. The palette of cutaway 12 is incorrect (the incorrect - // palette can be seen in the original demo too, for a split second). Therefore, use the saved palette for this - // cutaway - if (_vm->getGameId() == GID_IHNM_DEMO && cut == 12) - _vm->_gfx->restorePalette(); - - free(buf); - free(resourceData); + if (fade) { + // Set the initial background and palette for the cutaway + event.type = kEvTImmediate; + event.code = kCutawayEvent; + event.op = kEventShowCutawayBg; + event.time = 0; + event.duration = 0; + event.param = _cutawayList[cut].backgroundResourceId; + q_event = _vm->_events->chain(q_event, &event); + } else { + showCutawayBg(_cutawayList[cut].backgroundResourceId); + } // Play the animation @@ -183,7 +166,7 @@ void Anim::playCutaway(int cut, bool fade) { if (cutawaySlot == -1) { warning("Could not allocate cutaway animation slot"); - return; + return 0; } // Some cutaways in IHNM have animResourceId equal to 0, which means that they only have @@ -191,48 +174,47 @@ void Anim::playCutaway(int cut, bool fade) { // An example is the "nightfall" animation in Ben's chapter (fadein-fadeout), the animation // for the second from the left monitor in Ellen's chapter etc // Therefore, skip the animation bit if animResourceId is 0 and only show the background - if (_cutawayList[cut].animResourceId == 0) - return; - - _vm->_resource->loadResource(context, _cutawayList[cut].animResourceId, resourceData, resourceDataLength); - - load(MAX_ANIMATIONS + cutawaySlot, resourceData, resourceDataLength); - - free(resourceData); + if (_cutawayList[cut].animResourceId != 0) { + _vm->_resource->loadResource(context, _cutawayList[cut].animResourceId, resourceData, resourceDataLength); + load(MAX_ANIMATIONS + cutawaySlot, resourceData, resourceDataLength); + free(resourceData); - setCycles(MAX_ANIMATIONS + cutawaySlot, _cutawayList[cut].cycles); - setFrameTime(MAX_ANIMATIONS + cutawaySlot, 1000 / _cutawayList[cut].frameRate); + setCycles(MAX_ANIMATIONS + cutawaySlot, _cutawayList[cut].cycles); + setFrameTime(MAX_ANIMATIONS + cutawaySlot, 1000 / _cutawayList[cut].frameRate); + } if (_cutAwayMode != kPanelVideo || startImmediately) play(MAX_ANIMATIONS + cutawaySlot, 0); else { - Event event; event.type = kEvTOneshot; event.code = kAnimEvent; event.op = kEventPlay; event.param = MAX_ANIMATIONS + cutawaySlot; event.time = (40 / 3) * 1000 / _cutawayList[cut].frameRate; - _vm->_events->queue(&event); + if (fade) + q_event = _vm->_events->chain(q_event, &event); + else + _vm->_events->queue(&event); } + + return MAX_ANIMATIONS + cutawaySlot; } void Anim::endCutaway(void) { - // I believe this is called by scripts after running one cutaway. At - // this time, nothing needs to be done here. + // This is called by scripts after a cutaway is finished. At this time, + // nothing needs to be done here. debug(0, "endCutaway()"); } void Anim::returnFromCutaway(void) { - // I believe this is called by scripts after running a cutaway to - // ensure that we return to the scene as if nothing had happened. It's - // not called by the IHNM intro, presumably because there is no old - // scene to return to. + // This is called by scripts after a cutaway is finished, to return back + // to the scene that the cutaway was called from. It's not called by the + // IHNM intro, as there is no old scene to return to. debug(0, "returnFromCutaway()"); - if (_cutawayActive) { Event event; Event *q_event = NULL; @@ -240,16 +222,26 @@ void Anim::returnFromCutaway(void) { if (_cutAwayFade) { static PalEntry cur_pal[PAL_ENTRIES]; - _vm->_gfx->getCurrentPal(cur_pal); + _vm->_interface->setFadeMode(kFadeOut); + // Fade to black out + _vm->_gfx->getCurrentPal(cur_pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalToBlack; event.time = 0; event.duration = kNormalFadeDuration; event.data = cur_pal; - q_event = _vm->_events->queue(&event); + + // set fade mode + event.type = kEvTImmediate; + event.code = kInterfaceEvent; + event.op = kEventSetFadeMode; + event.param = kNoFade; + event.time = 0; + event.duration = 0; + q_event = _vm->_events->chain(q_event, &event); } // Clear the cutaway. Note that this sets _cutawayActive to false @@ -272,7 +264,6 @@ void Anim::returnFromCutaway(void) { event.op = kEventResumeAll; event.time = 0; event.duration = 0; - q_event = _vm->_events->chain(q_event, &event); // chain with the other events // Draw the scene @@ -281,7 +272,6 @@ void Anim::returnFromCutaway(void) { event.op = kEventDraw; event.time = 0; event.duration = 0; - q_event = _vm->_events->chain(q_event, &event); // chain with the other events // Handle fade up, if we previously faded down @@ -292,22 +282,22 @@ void Anim::returnFromCutaway(void) { event.time = 0; event.duration = kNormalFadeDuration; event.data = saved_pal; - q_event = _vm->_events->chain(q_event, &event); - } event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventThreadWake; event.param = kWaitTypeWakeUp; - q_event = _vm->_events->chain(q_event, &event); } } void Anim::clearCutaway(void) { + PalEntry *pal; + debug(1, "clearCutaway()"); + if (_cutawayActive) { _cutawayActive = false; @@ -317,18 +307,56 @@ void Anim::clearCutaway(void) { } _vm->_interface->restoreMode(); - - if (_vm->getGameId() != GID_IHNM_DEMO) { - if (_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) { - // Don't show the mouse cursor in the non-interactive part of the IHNM demo - } else { - _vm->_gfx->showCursor(true); - } - } else { + _vm->_gfx->showCursor(true); + + if (_vm->getGameId() == GID_IHNM_DEMO) { // Enable the save reminder state after each cutaway for the IHNM demo _vm->_interface->setSaveReminderState(true); } + + // Set the scene's palette + _vm->_scene->getBGPal(pal); + _vm->_gfx->setPalette(pal); + } +} + +void Anim::showCutawayBg(int bg) { + ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE); + + byte *resourceData; + size_t resourceDataLength; + byte *buf; + size_t buflen; + int width; + int height; + Event event; + static PalEntry pal[PAL_ENTRIES]; + + _vm->_resource->loadResource(context, bg, resourceData, resourceDataLength); + _vm->decodeBGImage(resourceData, resourceDataLength, &buf, &buflen, &width, &height); + + const byte *palPointer = _vm->getImagePal(resourceData, resourceDataLength); + memcpy(pal, palPointer, sizeof(pal)); + Surface *bgSurface = _vm->_render->getBackGroundSurface(); + const Rect rect(width, height); + bgSurface->blit(rect, buf); + _vm->_frameCount++; + + if (_cutAwayFade) { + // Handle fade up, if we previously faded down + event.type = kEvTImmediate; + event.code = kPalEvent; + event.op = kEventBlackToPal; + event.time = 0; + event.duration = kNormalFadeDuration; + event.data = pal; + _vm->_events->queue(&event); + } else { + _vm->_gfx->setPalette(pal); } + + free(buf); + free(resourceData); } void Anim::startVideo(int vid, bool fade) { @@ -380,6 +408,12 @@ void Anim::load(uint16 animId, const byte *animResourceData, size_t animResource anim->start += temp; // Cache frame offsets + + // WORKAROUND: Cutaway with background resource ID 37 (loaded as cutaway #4) is ending credits. + // For some reason it has wrong number of frames specified in its header. So we calculate it here: + if (animId > MAX_ANIMATIONS && _cutawayListLength > 4 && _cutawayList[4].backgroundResourceId == 37 && anim->maxFrame == 143) + anim->maxFrame = fillFrameOffsets(anim, false); + anim->frameOffsets = (size_t *)malloc((anim->maxFrame + 1) * sizeof(*anim->frameOffsets)); if (anim->frameOffsets == NULL) { memoryError("Anim::load"); @@ -388,7 +422,9 @@ void Anim::load(uint16 animId, const byte *animResourceData, size_t animResource fillFrameOffsets(anim); // Set animation data - anim->currentFrame = 0; + // HACK: We set currentFrame to -1, as the first frame of the animation is never drawn otherwise + // (check Anim::play) + anim->currentFrame = -1; anim->completed = 0; anim->cycles = anim->maxFrame; @@ -422,6 +458,13 @@ void Anim::setCycles(uint16 animId, int cycles) { anim->cycles = cycles; } +int Anim::getCycles(uint16 animId) { + if (animId >= MAX_ANIMATIONS && _cutawayAnimations[animId - MAX_ANIMATIONS] == NULL) + return 0; + + return getAnimation(animId)->cycles; +} + void Anim::play(uint16 animId, int vectorTime, bool playing) { Event event; Surface *backGroundSurface; @@ -440,6 +483,23 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) { if (animId < MAX_ANIMATIONS && _cutawayActive) return; + if (animId >= MAX_ANIMATIONS && _cutawayAnimations[animId - MAX_ANIMATIONS] == NULL) { + // In IHNM, cutaways without an animation bit are not rendered, but the framecount + // needs to be updated + _vm->_frameCount++; + + event.type = kEvTOneshot; + event.code = kAnimEvent; + event.op = kEventFrame; + event.param = animId; + event.time = 10; + _vm->_events->queue(&event); + + // Nothing to render here (apart from the background, which is already rendered), + // so return + return; + } + anim = getAnimation(animId); backGroundSurface = _vm->_render->getBackGroundSurface(); @@ -453,19 +513,28 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) { return; } + // HACK: The first frame of the animation is never shown when entering a scene + // For now, we initialize currentFrame to be -1 instead of 0 and we draw the + // first frame of the animation on the next iteration of Anim::play + // FIXME: find out why this occurs and remove this hack. Note that when this + // hack is removed, currentFrame should be initialized to 0 again in Anim::load + if (anim->currentFrame < 0) { + anim->currentFrame = 0; + event.type = kEvTOneshot; + event.code = kAnimEvent; + event.op = kEventFrame; + event.param = animId; + event.time = 0; + _vm->_events->queue(&event); + + return; + } + if (anim->completed < anim->cycles) { - frame = anim->currentFrame; + if (anim->currentFrame < 0) + anim->currentFrame = 0; - // WORKAROUND for a buggy animation in IHNM. Animation 0 in scene 67 (the mob of angry prisoners) should - // start from frame 0, not frame 1. Frame 0 is the background of the animation (the mob of prisoners), whereas - // the rest of the frames are their animated arms. Therefore, in order for the prisoners to appear correctly, - // frame 0 should be displayed as the first frame, but anim->currentframe is set to 1, which means that the - // prisoners will never be shown. In the original, the prisoners (first frame in the animation) are shown a - // bit after the animation is started (which is wrong again, but not that apparent), whereas in ScummVM the - // first frame is never shown. Therefore, make sure that for this animation, frame 0 is shown first - if (_vm->getGameType() == GType_IHNM && _vm->_scene->currentChapterNumber() == 4 && - _vm->_scene->currentSceneNumber() == 67 && animId == 0 && anim->completed == 1) - frame = 0; + frame = anim->currentFrame; // FIXME: if start > 0, then this works incorrectly decodeFrame(anim, anim->frameOffsets[frame], displayBuffer, _vm->getDisplayWidth() * _vm->getDisplayHeight()); @@ -488,6 +557,9 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) { _vm->_frameCount += 100; // make sure the waiting thread stops waiting // Animation done playing anim->state = ANIM_PAUSE; + anim->currentFrame = 0; + anim->completed = 0; + if (anim->linkId == -1) { if (anim->flags & ANIM_FLAG_ENDSCENE) { // This animation ends the scene @@ -498,9 +570,6 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) { _vm->_events->queue(&event); } return; - } else { - anim->currentFrame = 0; - anim->completed = 0; } } @@ -521,7 +590,6 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) { event.op = kEventFrame; event.param = animId; event.time = frameTime; - _vm->_events->queue(&event); } @@ -592,6 +660,22 @@ void Anim::setFrameTime(uint16 animId, int time) { anim->frameTime = time; } +int Anim::getFrameTime(uint16 animId) { + AnimationData *anim; + + anim = getAnimation(animId); + + return anim->frameTime; +} + +bool Anim::isPlaying(uint16 animId) { + AnimationData *anim; + + anim = getAnimation(animId); + + return (anim->state == ANIM_PLAYING); +} + int16 Anim::getCurrentFrame(uint16 animId) { AnimationData *anim; @@ -631,11 +715,12 @@ void Anim::decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_ MemoryReadStream readS(anim->resourceData + frameOffset, anim->resourceLength - frameOffset); - +// FIXME: This is thrown when the first video of the IHNM end sequence is shown (the "turn off screen" +// video), however the video is played correctly and the rest of the end sequence continues normally #if 1 #define VALIDATE_WRITE_POINTER \ if ((writePointer < buf) || (writePointer >= (buf + screenWidth * screenHeight))) { \ - error("VALIDATE_WRITE_POINTER: writePointer=%p buf=%p", (void *)writePointer, (void *)buf); \ + warning("VALIDATE_WRITE_POINTER: writePointer=%p buf=%p", (void *)writePointer, (void *)buf); \ } #else #define VALIDATE_WRITE_POINTER @@ -758,8 +843,8 @@ void Anim::decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_ } while (1); } -void Anim::fillFrameOffsets(AnimationData *anim) { - uint16 currentFrame; +int Anim::fillFrameOffsets(AnimationData *anim, bool reallyFill) { + uint16 currentFrame = 0; byte markByte; uint16 control; uint16 runcount; @@ -772,12 +857,18 @@ void Anim::fillFrameOffsets(AnimationData *anim) { readS._bigEndian = !_vm->isBigEndian(); // RLE has inversion BE<>LE - for (currentFrame = 0; currentFrame <= anim->maxFrame; currentFrame++) { - anim->frameOffsets[currentFrame] = readS.pos(); + while (!readS.eos()) { + if (reallyFill) { + anim->frameOffsets[currentFrame] = readS.pos(); + + if (currentFrame == anim->maxFrame) + break; + } + currentFrame++; // For some strange reason, the animation header is in little // endian format, but the actual RLE encoded frame data, - // including the frame header, is in big endian format. */ + // including the frame header, is in big endian format do { markByte = readS.readByte(); // debug(7, "_pos=%x currentFrame=%i markByte=%x", readS.pos(), currentFrame, markByte); @@ -816,8 +907,7 @@ void Anim::fillFrameOffsets(AnimationData *anim) { case SAGA_FRAME_LONG_UNCOMPRESSED_RUN: // (16) 0001 0000 // Long Uncompressed Run runcount = readS.readSint16BE(); - for (i = 0; i < runcount; i++) - readS.readByte(); + readS.seek(runcount, SEEK_CUR); continue; break; case SAGA_FRAME_NOOP: // Does nothing @@ -855,6 +945,8 @@ void Anim::fillFrameOffsets(AnimationData *anim) { } } while (markByte != SAGA_FRAME_END); } + + return currentFrame; } void Anim::animInfo() { diff --git a/engines/saga/animation.h b/engines/saga/animation.h index f8cf90425f..4306d8e00d 100644 --- a/engines/saga/animation.h +++ b/engines/saga/animation.h @@ -115,10 +115,11 @@ public: void loadCutawayList(const byte *resourcePointer, size_t resourceLength); void freeCutawayList(void); - void playCutaway(int cut, bool fade); + int playCutaway(int cut, bool fade); void endCutaway(void); void returnFromCutaway(void); void clearCutaway(void); + void showCutawayBg(int bg); void startVideo(int vid, bool fade); void endVideo(void); @@ -140,12 +141,10 @@ public: void resume(uint16 animId, int cycles); void resumeAll(); int16 getCurrentFrame(uint16 animId); - bool hasCutaway(void) { - return _cutawayActive; - } - void setCutAwayMode(int mode) { - _cutAwayMode = mode; - } + int getFrameTime(uint16 animId); + int getCycles(uint16 animId); + bool isPlaying(uint16 animId); + bool hasAnimation(uint16 animId) { if (animId >= MAX_ANIMATIONS) { if (animId < MAX_ANIMATIONS + ARRAYSIZE(_cutawayAnimations)) @@ -154,10 +153,16 @@ public: } return (_animations[animId] != NULL); } - int cutawayResourceID(int cutaway) { return _cutawayList[cutaway].animResourceId; } + + bool hasCutaway(void) { return _cutawayActive; } + void setCutAwayMode(int mode) { _cutAwayMode = mode; } + int cutawayListLength() { return _cutawayListLength; } + int cutawayBgResourceID(int cutaway) { return _cutawayList[cutaway].backgroundResourceId; } + int cutawayAnimResourceID(int cutaway) { return _cutawayList[cutaway].animResourceId; } + private: void decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_t bufLength); - void fillFrameOffsets(AnimationData *anim); + int fillFrameOffsets(AnimationData *anim, bool reallyFill = true); void validateAnimationId(uint16 animId) { if (animId >= MAX_ANIMATIONS) { @@ -168,7 +173,7 @@ private: } } if (_animations[animId] == NULL) { - error("validateAnimationId: animId=%i unassigned", animId); + error("validateAnimationId: animId=%i unassigned.", animId); } } diff --git a/engines/saga/console.cpp b/engines/saga/console.cpp index 421245ab30..0a6b733e00 100644 --- a/engines/saga/console.cpp +++ b/engines/saga/console.cpp @@ -75,6 +75,14 @@ Console::Console(SagaEngine *vm) : GUI::Debugger() { // Panel commands DCmd_Register("current_panel_mode", WRAP_METHOD(Console, cmdCurrentPanelMode)); DCmd_Register("set_panel_mode", WRAP_METHOD(Console, cmdSetPanelMode)); + + // Font commands + DCmd_Register("set_font_mapping", WRAP_METHOD(Console, cmdSetFontMapping)); + + // Global flags commands + DCmd_Register("global_flags_info", WRAP_METHOD(Console, cmdGlobalFlagsInfo)); + DCmd_Register("set_global_flag", WRAP_METHOD(Console, cmdSetGlobalFlag)); + DCmd_Register("clear_global_flag", WRAP_METHOD(Console, cmdClearGlobalFlag)); } Console::~Console() { @@ -158,4 +166,92 @@ bool Console::cmdSetPanelMode(int argc, const char **argv) { return true; } +bool Console::cmdSetFontMapping(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Sets font mapping\nUsage: %s <Font mapping flag>\n", argv[0]); + DebugPrintf("Mapping flags:\n0 - default game behavior\n1 - force font mapping\n2 - ignore font mapping\n"); + } else { + _vm->_font->setFontMapping(atoi(argv[1])); + } + return true; +} + +bool Console::cmdGlobalFlagsInfo(int argc, const char **argv) { + DebugPrintf("Global flags status for IHNM:\n"); + + // Global flags in IHNM: + // 00: Unknown + // 01: Unknown + // 02: Unknown + // 03: Unknown + // 04: Unknown + // 05: Unknown + // 06: Unknown + // 07: Unknown + // 08: Unknown + // 09: Unknown + // 10: Unknown + // 11: Unknown + // 12: Unknown + // 13: Unknown + // 14: Unknown + // 15: Unknown + // 16: Used in the final chapter. If it's 0 when a character dies, the "bad" ending for that character is shown + // 17: Unknown + // 18: Unknown + // 19: Unknown, used in the final chapter + // 20: Unknown + // 21: Unknown + // 22: Unknown + // 23: Unknown + // 24: Unknown + // 25: Unknown + // 26: Unknown + // 27: Unknown + // 28: Unknown + // 29: Unknown + // 30: Unknown + // 31: Unknown + + int i = 0, k = 0, flagStatus = 0; + + for (i = 0; i < 32; i += 8) { + for (k = i; k < i + 8; k ++) { + flagStatus = _vm->_globalFlags & (1 << k) ? 1 : 0; + _vm->_console->DebugPrintf("%02d: %u |", k, flagStatus); + } + _vm->_console->DebugPrintf("\n"); + } + + return true; +} + +bool Console::cmdSetGlobalFlag(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Usage: %s <Global flag number>\nValid flag numbers are 0 - 31\n", argv[0]); + } else { + int flagNumber = atoi(argv[1]); + if (flagNumber >= 0 && flagNumber <= 31) { + _vm->_globalFlags |= (1 << flagNumber); + } else { + DebugPrintf("Valid flag numbers are 0 - 31\n"); + } + } + return true; +} + +bool Console::cmdClearGlobalFlag(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Usage: %s <Global flag number>\nValid flag numbers are 0 - 31\n", argv[0]); + } else { + int flagNumber = atoi(argv[1]); + if (flagNumber >= 0 && flagNumber <= 31) { + _vm->_globalFlags &= ~(1 << flagNumber); + } else { + DebugPrintf("Valid flag numbers are 0 - 31\n"); + } + } + return true; +} + } // End of namespace Saga diff --git a/engines/saga/console.h b/engines/saga/console.h index 3db9833f22..7936d3a959 100644 --- a/engines/saga/console.h +++ b/engines/saga/console.h @@ -55,6 +55,12 @@ private: bool cmdCurrentPanelMode(int argc, const char **argv); bool cmdSetPanelMode(int argc, const char **argv); + bool cmdSetFontMapping(int argc, const char **argv); + + bool cmdGlobalFlagsInfo(int argc, const char **argv); + bool cmdSetGlobalFlag(int argc, const char **argv); + bool cmdClearGlobalFlag(int argc, const char **argv); + private: SagaEngine *_vm; }; diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp index c8918998f1..29ca0e8bbe 100644 --- a/engines/saga/detection.cpp +++ b/engines/saga/detection.cpp @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL:https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/branches/gsoc2007-fsnode/engines/saga/detection.cpp $ - * $Id:detection.cpp 26949 2007-05-26 20:23:24Z david_corrales $ + * $URL$ + * $Id$ * */ @@ -49,7 +49,6 @@ struct SAGAGameDescription { const GameFontDescription *fontDescriptions; const GameSoundInfo *voiceInfo; const GameSoundInfo *sfxInfo; - const GameSoundInfo *musicInfo; int patchesCount; const GamePatchDescription *patchDescriptions; }; @@ -59,7 +58,18 @@ const bool SagaEngine::isMacResources() const { return (getPlatform() == Common: const GameResourceDescription *SagaEngine::getResourceDescription() { return _gameDescription->resourceDescription; } const GameSoundInfo *SagaEngine::getVoiceInfo() const { return _gameDescription->voiceInfo; } const GameSoundInfo *SagaEngine::getSfxInfo() const { return _gameDescription->sfxInfo; } -const GameSoundInfo *SagaEngine::getMusicInfo() const { return _gameDescription->musicInfo; } +const GameSoundInfo *SagaEngine::getMusicInfo() const { + static GameSoundInfo musicInfo; + musicInfo.resourceType = kSoundPCM; + musicInfo.frequency = 11025; + musicInfo.sampleBits = 16; + // The digital music in the ITE Mac demo version is not stereo + musicInfo.stereo = _gameDescription->gameType == GID_ITE_MACDEMO2 ? false : true; + musicInfo.isBigEndian = false; + musicInfo.isSigned = true; + + return &musicInfo; +} const GameFontDescription *SagaEngine::getFontDescription(int index) { assert(index < _gameDescription->fontsCount); diff --git a/engines/saga/detection_tables.h b/engines/saga/detection_tables.h index 0d57adb87d..5bb408e947 100644 --- a/engines/saga/detection_tables.h +++ b/engines/saga/detection_tables.h @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL:https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/branches/gsoc2007-fsnode/engines/saga/detection_tables.h $ - * $Id:detection_tables.h 26949 2007-05-26 20:23:24Z david_corrales $ + * $URL$ + * $Id$ * */ @@ -34,6 +34,7 @@ static const GameResourceDescription ITE_Resources = { RID_ITE_CONVERSE_PANEL, RID_ITE_OPTION_PANEL, 0, // Warning panel (IHNM only) + 0, // Warning panel sprites (IHNM only) RID_ITE_MAIN_SPRITES, RID_ITE_MAIN_PANEL_SPRITES, 0, // Option panel sprites (IHNM only) @@ -50,6 +51,7 @@ static const GameResourceDescription ITEDemo_Resources = { RID_ITEDEMO_CONVERSE_PANEL, RID_ITEDEMO_OPTION_PANEL, 0, // Warning panel (IHNM only) + 0, // Warning panel sprites (IHNM only) RID_ITEDEMO_MAIN_SPRITES, RID_ITEDEMO_MAIN_PANEL_SPRITES, 0, // Option panel sprites (IHNM only) @@ -59,188 +61,6 @@ static const GameResourceDescription ITEDemo_Resources = { RID_ITEDEMO_ACTOR_NAMES }; -// Inherit the Earth - DOS Demo version -static const GameFontDescription ITEDEMO_GameFonts[] = { - {0}, - {1} -}; - -// Inherit the Earth - Wyrmkeep Win32 Demo version - -static const GameFontDescription ITEWINDEMO_GameFonts[] = { - {2}, - {0} -}; - -static const GameSoundInfo ITEWINDEMO1_GameSound = { - kSoundPCM, - 22050, - 8, - false, - false, - false -}; - -static const GameSoundInfo ITEWINDEMO2_GameVoice = { - kSoundVOX, - 22050, - 16, - false, - false, - true -}; - -static const GameSoundInfo ITEWINDEMO2_GameSound = { - kSoundPCM, - 22050, - 16, - false, - false, - true -}; - -// Inherit the Earth - Wyrmkeep Mac Demo version -static const GameSoundInfo ITEMACDEMO_GameVoice = { - kSoundVOX, - 22050, - 16, - false, - false, - true -}; - -static const GameSoundInfo ITEMACDEMO_GameSound = { - kSoundPCM, - 22050, - 16, - false, - true, - true -}; - -static const GameSoundInfo ITEMACDEMO_GameMusic = { - kSoundPCM, - 11025, - 16, - false, - false, - true -}; - -static const GameSoundInfo ITEMACCD_G_GameSound = { - kSoundMacPCM, - 22050, - 8, - false, - false, - false -}; - -// Inherit the Earth - Mac Wyrmkeep version -static const GameSoundInfo ITEMACCD_GameSound = { - kSoundPCM, - 22050, - 16, - false, - true, - true -}; - -static const GameSoundInfo ITEMACCD_GameMusic = { - kSoundPCM, - 11025, - 16, - true, - false, - true -}; - -// Inherit the Earth - Diskette version -static const GameFontDescription ITEDISK_GameFonts[] = { - {2}, - {0}, - {1} -}; - -static const GameSoundInfo ITEDISK_GameSound = { - kSoundVOC, - -1, - -1, - false, - false, - true -}; - -static const GameFontDescription ITECD_GameFonts[] = { - {2}, - {0}, - {1} -}; - -static const GameSoundInfo ITECD_GameSound = { - kSoundPCM, - 22050, - 16, - false, - false, - true -}; - -// Patch files. Files not found will be ignored -static const GamePatchDescription ITEPatch_Files[] = { - { "cave.mid", GAME_RESOURCEFILE, 9, NULL}, - { "intro.mid", GAME_RESOURCEFILE, 10, NULL}, - { "fvillage.mid", GAME_RESOURCEFILE, 11, NULL}, - { "elkhall.mid", GAME_RESOURCEFILE, 12, NULL}, - { "mouse.mid", GAME_RESOURCEFILE, 13, NULL}, - { "darkclaw.mid", GAME_RESOURCEFILE, 14, NULL}, - { "birdchrp.mid", GAME_RESOURCEFILE, 15, NULL}, - { "orbtempl.mid", GAME_RESOURCEFILE, 16, NULL}, - { "spooky.mid", GAME_RESOURCEFILE, 17, NULL}, - { "catfest.mid", GAME_RESOURCEFILE, 18, NULL}, - { "elkfanfare.mid", GAME_RESOURCEFILE, 19, NULL}, - { "bcexpl.mid", GAME_RESOURCEFILE, 20, NULL}, - { "boargtnt.mid", GAME_RESOURCEFILE, 21, NULL}, - { "boarking.mid", GAME_RESOURCEFILE, 22, NULL}, - { "explorea.mid", GAME_RESOURCEFILE, 23, NULL}, - { "exploreb.mid", GAME_RESOURCEFILE, 24, NULL}, - { "explorec.mid", GAME_RESOURCEFILE, 25, NULL}, - { "sunstatm.mid", GAME_RESOURCEFILE, 26, NULL}, - { "nitstrlm.mid", GAME_RESOURCEFILE, 27, NULL}, - { "humruinm.mid", GAME_RESOURCEFILE, 28, NULL}, - { "damexplm.mid", GAME_RESOURCEFILE, 29, NULL}, - { "tychom.mid", GAME_RESOURCEFILE, 30, NULL}, - { "kitten.mid", GAME_RESOURCEFILE, 31, NULL}, - { "sweet.mid", GAME_RESOURCEFILE, 32, NULL}, - { "brutalmt.mid", GAME_RESOURCEFILE, 33, NULL}, - { "shiala.mid", GAME_RESOURCEFILE, 34, NULL}, - - { "wyrm.pak", GAME_RESOURCEFILE, 1529, NULL}, - { "wyrm1.dlt", GAME_RESOURCEFILE, 1530, NULL}, - { "wyrm2.dlt", GAME_RESOURCEFILE, 1531, NULL}, - { "wyrm3.dlt", GAME_RESOURCEFILE, 1532, NULL}, - { "wyrm4.dlt", GAME_RESOURCEFILE, 1533, NULL}, - { "credit3n.dlt", GAME_RESOURCEFILE, 1796, NULL}, - { "credit3m.dlt", GAME_RESOURCEFILE, 1796, NULL}, // Macintosh - { "credit4n.dlt", GAME_RESOURCEFILE, 1797, NULL}, - { "credit4m.dlt", GAME_RESOURCEFILE, 1797, NULL}, // Macintosh - { "p2_a.voc", GAME_VOICEFILE, 4, NULL}, - { "p2_a.iaf", GAME_VOICEFILE, 4, &ITECD_GameSound} -}; - -static const GamePatchDescription ITEMacPatch_Files[] = { - { "wyrm.pak", GAME_RESOURCEFILE, 1529, NULL}, - { "wyrm1.dlt", GAME_RESOURCEFILE, 1530, NULL}, - { "wyrm2.dlt", GAME_RESOURCEFILE, 1531, NULL}, - { "wyrm3.dlt", GAME_RESOURCEFILE, 1532, NULL}, - { "wyrm4.dlt", GAME_RESOURCEFILE, 1533, NULL}, - { "credit3m.dlt", GAME_RESOURCEFILE, 1796, NULL}, - { "credit4m.dlt", GAME_RESOURCEFILE, 1797, NULL}, - { "p2_a.iaf", GAME_VOICEFILE, 4, &ITEMACCD_GameSound} -}; - -// IHNM section - static const GameResourceDescription IHNM_Resources = { RID_IHNM_SCENE_LUT, // Scene lookup table RN RID_IHNM_SCRIPT_LUT, // Script lookup table RN @@ -248,6 +68,7 @@ static const GameResourceDescription IHNM_Resources = { RID_IHNM_CONVERSE_PANEL, RID_IHNM_OPTION_PANEL, RID_IHNM_WARNING_PANEL, + RID_IHNM_WARNING_PANEL_SPRITES, RID_IHNM_MAIN_SPRITES, RID_IHNM_MAIN_PANEL_SPRITES, RID_IHNM_OPTION_PANEL_SPRITES, @@ -264,6 +85,7 @@ static const GameResourceDescription IHNMDEMO_Resources = { RID_IHNMDEMO_CONVERSE_PANEL, RID_IHNMDEMO_OPTION_PANEL, RID_IHNMDEMO_WARNING_PANEL, + RID_IHNMDEMO_WARNING_PANEL_SPRITES, RID_IHNMDEMO_MAIN_SPRITES, RID_IHNMDEMO_MAIN_PANEL_SPRITES, RID_IHNMDEMO_OPTION_PANEL_SPRITES, @@ -273,29 +95,73 @@ static const GameResourceDescription IHNMDEMO_Resources = { 0 // Actors strings (ITE only) }; -static const GameFontDescription IHNMDEMO_GameFonts[] = { - {2}, - {3}, - {4} -}; +static const GameFontDescription ITEDEMO_GameFonts[] = { {0}, {1} }; +static const GameFontDescription ITEWINDEMO_GameFonts[] = { {2}, {0} }; +static const GameFontDescription ITE_GameFonts[] = { {2}, {0}, {1} }; +static const GameFontDescription IHNMDEMO_GameFonts[] = { {2}, {3}, {4} }; +// Font 6 is kIHNMFont8, font 8 is kIHNMMainFont +static const GameFontDescription IHNMCD_GameFonts[] = { {2}, {3}, {4}, {5}, {6}, {7}, {8} }; + +// frequency, sampleBits, stereo, isBigEndian, isSigned +static const GameSoundInfo ITEPC_GameSound = { kSoundPCM, 22050, 16, false, false, true }; +static const GameSoundInfo ITEMAC_GameSound = { kSoundPCM, 22050, 16, false, true, true }; +static const GameSoundInfo ITEWINDEMO1_GameSound = { kSoundPCM, 22050, 8, false, false, false }; +static const GameSoundInfo ITEMACCD_G_GameSound = { kSoundMacPCM, 22050, 8, false, false, false }; +static const GameSoundInfo ITEDISK_GameSound = { kSoundVOC, -1, -1, false, false, true }; +static const GameSoundInfo ITEDEMO_GameVoice = { kSoundVOX, 22050, 16, false, false, true }; +static const GameSoundInfo IHNM_GameSound = { kSoundWAV, -1, -1, false, false, true }; -static const GameFontDescription IHNMCD_GameFonts[] = { - {2}, - {3}, - {4}, - {5}, - {6}, // kIHNMFont8 - {7}, - {8} // kIHNMMainFont +// Patch files. Files not found will be ignored +static const GamePatchDescription ITEPatch_Files[] = { + { "cave.mid", GAME_RESOURCEFILE, 9, NULL}, + { "intro.mid", GAME_RESOURCEFILE, 10, NULL}, + { "fvillage.mid", GAME_RESOURCEFILE, 11, NULL}, + { "elkhall.mid", GAME_RESOURCEFILE, 12, NULL}, + { "mouse.mid", GAME_RESOURCEFILE, 13, NULL}, + { "darkclaw.mid", GAME_RESOURCEFILE, 14, NULL}, + { "birdchrp.mid", GAME_RESOURCEFILE, 15, NULL}, + { "orbtempl.mid", GAME_RESOURCEFILE, 16, NULL}, + { "spooky.mid", GAME_RESOURCEFILE, 17, NULL}, + { "catfest.mid", GAME_RESOURCEFILE, 18, NULL}, + { "elkfanfare.mid", GAME_RESOURCEFILE, 19, NULL}, + { "bcexpl.mid", GAME_RESOURCEFILE, 20, NULL}, + { "boargtnt.mid", GAME_RESOURCEFILE, 21, NULL}, + { "boarking.mid", GAME_RESOURCEFILE, 22, NULL}, + { "explorea.mid", GAME_RESOURCEFILE, 23, NULL}, + { "exploreb.mid", GAME_RESOURCEFILE, 24, NULL}, + { "explorec.mid", GAME_RESOURCEFILE, 25, NULL}, + { "sunstatm.mid", GAME_RESOURCEFILE, 26, NULL}, + { "nitstrlm.mid", GAME_RESOURCEFILE, 27, NULL}, + { "humruinm.mid", GAME_RESOURCEFILE, 28, NULL}, + { "damexplm.mid", GAME_RESOURCEFILE, 29, NULL}, + { "tychom.mid", GAME_RESOURCEFILE, 30, NULL}, + { "kitten.mid", GAME_RESOURCEFILE, 31, NULL}, + { "sweet.mid", GAME_RESOURCEFILE, 32, NULL}, + { "brutalmt.mid", GAME_RESOURCEFILE, 33, NULL}, + { "shiala.mid", GAME_RESOURCEFILE, 34, NULL}, + + { "wyrm.pak", GAME_RESOURCEFILE, 1529, NULL}, + { "wyrm1.dlt", GAME_RESOURCEFILE, 1530, NULL}, + { "wyrm2.dlt", GAME_RESOURCEFILE, 1531, NULL}, + { "wyrm3.dlt", GAME_RESOURCEFILE, 1532, NULL}, + { "wyrm4.dlt", GAME_RESOURCEFILE, 1533, NULL}, + { "credit3n.dlt", GAME_RESOURCEFILE, 1796, NULL}, // PC + { "credit3m.dlt", GAME_RESOURCEFILE, 1796, NULL}, // Macintosh + { "credit4n.dlt", GAME_RESOURCEFILE, 1797, NULL}, // PC + { "credit4m.dlt", GAME_RESOURCEFILE, 1797, NULL}, // Macintosh + { "p2_a.voc", GAME_VOICEFILE, 4, NULL}, + { "p2_a.iaf", GAME_VOICEFILE, 4, &ITEPC_GameSound} }; -static const GameSoundInfo IHNM_GameSound = { - kSoundWAV, - -1, - -1, - false, - false, - true +static const GamePatchDescription ITEMacPatch_Files[] = { + { "wyrm.pak", GAME_RESOURCEFILE, 1529, NULL}, + { "wyrm1.dlt", GAME_RESOURCEFILE, 1530, NULL}, + { "wyrm2.dlt", GAME_RESOURCEFILE, 1531, NULL}, + { "wyrm3.dlt", GAME_RESOURCEFILE, 1532, NULL}, + { "wyrm4.dlt", GAME_RESOURCEFILE, 1533, NULL}, + { "credit3m.dlt", GAME_RESOURCEFILE, 1796, NULL}, + { "credit4m.dlt", GAME_RESOURCEFILE, 1797, NULL}, + { "p2_a.iaf", GAME_VOICEFILE, 4, &ITEMAC_GameSound} }; static const SAGAGameDescription gameDescriptions[] = { @@ -333,7 +199,6 @@ static const SAGAGameDescription gameDescriptions[] = { ITEDEMO_GameFonts, &ITEDISK_GameSound, &ITEDISK_GameSound, - &ITEMACCD_GameMusic, // note: this version did not originally have digital music 0, NULL, }, @@ -363,18 +228,15 @@ static const SAGAGameDescription gameDescriptions[] = { &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), ITEWINDEMO_GameFonts, - &ITEMACDEMO_GameVoice, - &ITEMACDEMO_GameSound, - &ITEMACDEMO_GameMusic, + &ITEDEMO_GameVoice, + &ITEMAC_GameSound, ARRAYSIZE(ITEMacPatch_Files), ITEMacPatch_Files, }, - // Note: This version is NOT supported yet - // Exiting the faire leads to a crash - // Inherit the earth - MAC Demo version 1 + // Non-interactive demo { { "ite", @@ -393,14 +255,13 @@ static const SAGAGameDescription gameDescriptions[] = { }, GType_ITE, GID_ITE_MACDEMO1, - GF_BIG_ENDIAN_DATA | GF_WYRMKEEP | GF_CD_FX, + GF_BIG_ENDIAN_DATA | GF_WYRMKEEP | GF_CD_FX | GF_NON_INTERACTIVE, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), ITEWINDEMO_GameFonts, - &ITEMACDEMO_GameVoice, - &ITEMACDEMO_GameSound, - &ITEMACCD_GameMusic, + &ITEDEMO_GameVoice, + &ITEMAC_GameSound, ARRAYSIZE(ITEMacPatch_Files), ITEMacPatch_Files, }, @@ -431,18 +292,15 @@ static const SAGAGameDescription gameDescriptions[] = { &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), ITEWINDEMO_GameFonts, - &ITEWINDEMO2_GameVoice, - &ITEWINDEMO2_GameSound, - &ITEMACCD_GameMusic, + &ITEDEMO_GameVoice, + &ITEPC_GameSound, ARRAYSIZE(ITEPatch_Files), ITEPatch_Files, }, - // Note: This version is NOT supported yet - // Exiting the faire leads to a crash - // Inherit the earth - Win32 Demo version 1 + // Non-interactive demo { { "ite", @@ -460,14 +318,13 @@ static const SAGAGameDescription gameDescriptions[] = { }, GType_ITE, GID_ITE_WINDEMO1, - GF_WYRMKEEP | GF_CD_FX, + GF_WYRMKEEP | GF_CD_FX | GF_NON_INTERACTIVE, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), ITEWINDEMO_GameFonts, &ITEWINDEMO1_GameSound, &ITEWINDEMO1_GameSound, - &ITEMACCD_GameMusic, // note: this version did not originally have digital music ARRAYSIZE(ITEPatch_Files), ITEPatch_Files, }, @@ -504,7 +361,6 @@ static const SAGAGameDescription gameDescriptions[] = { ITEWINDEMO_GameFonts, &ITEMACCD_G_GameSound, &ITEMACCD_G_GameSound, - &ITEMACCD_GameMusic, // note: this version did not originally have digital music 0, NULL, }, @@ -533,9 +389,8 @@ static const SAGAGameDescription gameDescriptions[] = { &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), ITEWINDEMO_GameFonts, - &ITEMACCD_GameSound, - &ITEMACCD_GameSound, - &ITEMACCD_GameMusic, + &ITEMAC_GameSound, + &ITEMAC_GameSound, ARRAYSIZE(ITEMacPatch_Files), ITEMacPatch_Files, }, @@ -570,11 +425,10 @@ static const SAGAGameDescription gameDescriptions[] = { GF_WYRMKEEP | GF_CD_FX, ITE_DEFAULT_SCENE, &ITE_Resources, - ARRAYSIZE(ITECD_GameFonts), - ITECD_GameFonts, - &ITEMACCD_GameSound, - &ITECD_GameSound, - &ITEMACCD_GameMusic, + ARRAYSIZE(ITE_GameFonts), + ITE_GameFonts, + &ITEMAC_GameSound, + &ITEPC_GameSound, 0, NULL, }, @@ -601,11 +455,10 @@ static const SAGAGameDescription gameDescriptions[] = { GF_CD_FX, ITE_DEFAULT_SCENE, &ITE_Resources, - ARRAYSIZE(ITECD_GameFonts), - ITECD_GameFonts, - &ITECD_GameSound, - &ITECD_GameSound, - &ITEMACCD_GameMusic, + ARRAYSIZE(ITE_GameFonts), + ITE_GameFonts, + &ITEPC_GameSound, + &ITEPC_GameSound, ARRAYSIZE(ITEPatch_Files), ITEPatch_Files, }, @@ -631,11 +484,10 @@ static const SAGAGameDescription gameDescriptions[] = { GF_CD_FX, ITE_DEFAULT_SCENE, &ITE_Resources, - ARRAYSIZE(ITECD_GameFonts), - ITECD_GameFonts, - &ITECD_GameSound, - &ITECD_GameSound, - &ITEMACCD_GameMusic, // note: this version did not originally have digital music + ARRAYSIZE(ITE_GameFonts), + ITE_GameFonts, + &ITEPC_GameSound, + &ITEPC_GameSound, ARRAYSIZE(ITEPatch_Files), ITEPatch_Files, }, @@ -662,11 +514,10 @@ static const SAGAGameDescription gameDescriptions[] = { GF_CD_FX, ITE_DEFAULT_SCENE, &ITE_Resources, - ARRAYSIZE(ITECD_GameFonts), - ITECD_GameFonts, - &ITECD_GameSound, - &ITECD_GameSound, - &ITEMACCD_GameMusic, // note: this version did not originally have digital music + ARRAYSIZE(ITE_GameFonts), + ITE_GameFonts, + &ITEPC_GameSound, + &ITEPC_GameSound, 0, NULL, }, @@ -694,11 +545,10 @@ static const SAGAGameDescription gameDescriptions[] = { 0, ITE_DEFAULT_SCENE, &ITE_Resources, - ARRAYSIZE(ITEDISK_GameFonts), - ITEDISK_GameFonts, + ARRAYSIZE(ITE_GameFonts), + ITE_GameFonts, &ITEDISK_GameSound, &ITEDISK_GameSound, - &ITEMACCD_GameMusic, // note: this version did not originally have digital music 0, NULL, }, @@ -723,11 +573,10 @@ static const SAGAGameDescription gameDescriptions[] = { 0, ITE_DEFAULT_SCENE, &ITE_Resources, - ARRAYSIZE(ITEDISK_GameFonts), - ITEDISK_GameFonts, + ARRAYSIZE(ITE_GameFonts), + ITE_GameFonts, &ITEDISK_GameSound, &ITEDISK_GameSound, - &ITEMACCD_GameMusic, // note: this version did not originally have digital music ARRAYSIZE(ITEPatch_Files), ITEPatch_Files, }, @@ -766,7 +615,6 @@ static const SAGAGameDescription gameDescriptions[] = { IHNMDEMO_GameFonts, &IHNM_GameSound, &IHNM_GameSound, - NULL, 0, NULL, }, @@ -805,7 +653,6 @@ static const SAGAGameDescription gameDescriptions[] = { IHNMCD_GameFonts, &IHNM_GameSound, &IHNM_GameSound, - NULL, 0, NULL, }, @@ -845,7 +692,6 @@ static const SAGAGameDescription gameDescriptions[] = { IHNMCD_GameFonts, &IHNM_GameSound, &IHNM_GameSound, - NULL, 0, NULL, }, @@ -884,7 +730,6 @@ static const SAGAGameDescription gameDescriptions[] = { IHNMCD_GameFonts, &IHNM_GameSound, &IHNM_GameSound, - NULL, 0, NULL, }, @@ -923,12 +768,12 @@ static const SAGAGameDescription gameDescriptions[] = { IHNMCD_GameFonts, &IHNM_GameSound, &IHNM_GameSound, - NULL, 0, NULL, }, // I Have No Mouth And I Must Scream - Fr CD version + // Censored CD version (without Nimdok) { { "ihnm", @@ -961,11 +806,10 @@ static const SAGAGameDescription gameDescriptions[] = { IHNMCD_GameFonts, &IHNM_GameSound, &IHNM_GameSound, - NULL, 0, NULL, }, - { AD_TABLE_END_MARKER, 0, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL, 0, NULL } + { AD_TABLE_END_MARKER, 0, 0, 0, 0, NULL, 0, NULL, NULL, NULL, 0, NULL } }; } // End of namespace Saga diff --git a/engines/saga/displayinfo.h b/engines/saga/displayinfo.h index 74bbcc4343..d7d51a1e9f 100644 --- a/engines/saga/displayinfo.h +++ b/engines/saga/displayinfo.h @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL:https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/branches/gsoc2007-fsnode/engines/saga/displayinfo.h $ - * $Id:displayinfo.h 26949 2007-05-26 20:23:24Z david_corrales $ + * $URL$ + * $Id$ * */ @@ -280,7 +280,7 @@ static const GameDisplayInfo ITE_DisplayInfo = { }; -//TODO: ihnm +// IHNM #define IHNM_CONVERSE_MAX_TEXT_WIDTH (485 - 8) #define IHNM_CONVERSE_TEXT_HEIGHT 10 #define IHNM_CONVERSE_TEXT_LINES 11 @@ -330,20 +330,20 @@ static PanelButton IHNM_OptionPanelButtons[] = { {kPanelButtonOptionText,60,61, 0,0, kTextMusic,'-',0, 0,0,0}, // text: music {kPanelButtonOptionText,60,86, 0,0, kTextSound,'-',0, 0,0,0}, // text: noise {kPanelButtonOptionText,56,111, 0,0, kTextVoices,'-',0, 0,0,0}, // text: voices - {kPanelButtonOption, 153,29, 79,23, kTextReadingSpeed,'r',0, 0,0,0}, //read speed - {kPanelButtonOption, 153,54, 79,23, kTextMusic,'m',0, 0,0,0}, //music - {kPanelButtonOption, 153,79, 79,23, kTextSound,'n',0, 0,0,0}, //sound-noise - {kPanelButtonOption, 153,104,79,23, kTextVoices,'v',0, 0,0,0}, //voices - {kPanelButtonOption, 19,149, 200,25, kTextQuitGame,'q',0, 0,0,0}, //quit - {kPanelButtonOption, 19,177, 200,25, kTextContinuePlaying,'c',0, 0,0,0}, //continue + {kPanelButtonOption, 154,30, 79,23, kTextReadingSpeed,'r',0, 0,0,0}, //read speed + {kPanelButtonOption, 154,55, 79,23, kTextMusic,'m',0, 0,0,0}, //music + {kPanelButtonOption, 154,80, 79,23, kTextSound,'n',0, 0,0,0}, //sound-noise + {kPanelButtonOption, 154,105,79,23, kTextVoices,'v',0, 0,0,0}, //voices + {kPanelButtonOption, 20,150, 200,25, kTextQuitGame,'q',0, 0,0,0}, //quit + {kPanelButtonOption, 20,178, 200,25, kTextContinuePlaying,'c',0, 0,0,0}, //continue {kPanelButtonOptionSaveFiles, 244,18, 170,138, 0,'-',0, 0,0,0}, //savefiles - {kPanelButtonOption, 242,162, 79,23, kTextLoad,'l',0, 0,0,0}, //load - {kPanelButtonOption, 333,162, 79,23, kTextSave,'s',0, 0,0,0}, //save + {kPanelButtonOption, 243,163, 79,23, kTextLoad,'l',0, 0,0,0}, //load + {kPanelButtonOption, 334,163, 79,23, kTextSave,'s',0, 0,0,0}, //save }; static PanelButton IHNM_QuitPanelButtons[] = { - {kPanelButtonQuit, 25,79, 80,25, kTextQuit,'q',0, 0,0,0}, - {kPanelButtonQuit, 155,79, 80,25, kTextCancel,'c',0, 0,0,0}, + {kPanelButtonQuit, 26,80, 80,25, kTextQuit,'q',0, 0,0,0}, + {kPanelButtonQuit, 156,80, 80,25, kTextCancel,'c',0, 0,0,0}, {kPanelButtonQuitText, 75,30, 0,0, kTextQuitTheGameQuestion,'-',0, 0,0,0}, }; @@ -354,11 +354,10 @@ static PanelButton IHNM_LoadPanelButtons[] = { }; static PanelButton IHNM_SavePanelButtons[] = { - // TODO {kPanelButtonSave, 25,79, 80,25, kTextSave,'s',0, 0,0,0}, {kPanelButtonSave, 155,79, 80,25, kTextCancel,'c',0, 0,0,0}, - {kPanelButtonSaveEdit, 26,57, 209,17, 0,'-',0, 0,0,0}, - {kPanelButtonSaveText, 75,30, 0,0, kTextEnterSaveGameName,'-',0, 0,0,0}, + {kPanelButtonSaveEdit, 22,56, 216,17, 0,'-',0, 0,0,0}, + {kPanelButtonSaveText, 74,30, 0,0, kTextEnterSaveGameName,'-',0, 0,0,0}, }; diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp index 80e6b58595..50297b7ef0 100644 --- a/engines/saga/events.cpp +++ b/engines/saga/events.cpp @@ -154,10 +154,12 @@ int Events::handleContinuous(Event *event) { case kEventBlackToPal: _vm->_gfx->blackToPal((PalEntry *)event->data, event_pc); break; - case kEventPalToBlack: _vm->_gfx->palToBlack((PalEntry *)event->data, event_pc); break; + case kEventPalFade: + _vm->_gfx->palFade((PalEntry *)event->data, event->param, event->param2, event->param3, event->param4, event_pc); + break; default: break; } @@ -237,10 +239,12 @@ int Events::handleImmediate(Event *event) { case kEventBlackToPal: _vm->_gfx->blackToPal((PalEntry *)event->data, event_pc); break; - case kEventPalToBlack: _vm->_gfx->palToBlack((PalEntry *)event->data, event_pc); break; + case kEventPalFade: + _vm->_gfx->palFade((PalEntry *)event->data, event->param, event->param2, event->param3, event->param4, event_pc); + break; default: break; } @@ -251,6 +255,7 @@ int Events::handleImmediate(Event *event) { case kSceneEvent: case kAnimEvent: case kCutawayEvent: + case kActorEvent: handleOneShot(event); event_done = true; break; @@ -338,11 +343,22 @@ int Events::handleOneShot(Event *event) { if (event->param == kEvPSetPalette) { PalEntry *palPointer; + + if (_vm->getGameType() == GType_IHNM) { + if (_vm->_spiritualBarometer > 255) + _vm->_gfx->setPaletteColor(kIHNMColorPortrait, 0xff, 0xff, 0xff); + else + _vm->_gfx->setPaletteColor(kIHNMColorPortrait, + _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.red / 256, + _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.green / 256, + _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.blue / 256); + } + _vm->_scene->getBGPal(palPointer); _vm->_gfx->setPalette(palPointer); } } - _vm->_actor->showActors(true); + _vm->_render->clearFlag(RF_DISABLE_ACTORS); } break; case kPsychicProfileBgEvent: @@ -374,7 +390,7 @@ int Events::handleOneShot(Event *event) { free(buf); free(resourceData); - // Draw the scene. It won't be drawn by Render::drawScene(), as the RF_PLACARD is set + // Draw the scene. It won't be drawn by Render::drawScene(), as a placard is up _vm->_scene->draw(); } break; @@ -455,6 +471,9 @@ int Events::handleOneShot(Event *event) { case kEventSetFadeMode: _vm->_interface->setFadeMode(event->param); break; + case kEventRestoreMode: + _vm->_interface->restoreMode(); + break; default: break; } @@ -530,6 +549,17 @@ int Events::handleOneShot(Event *event) { case kEventClear: _vm->_anim->clearCutaway(); break; + case kEventShowCutawayBg: + _vm->_anim->showCutawayBg(event->param); + break; + default: + break; + } + case kActorEvent: + switch (event->op) { + case kEventMove: + // TODO (check Actor::direct) + break; default: break; } diff --git a/engines/saga/events.h b/engines/saga/events.h index 2486525751..de4d296eab 100644 --- a/engines/saga/events.h +++ b/engines/saga/events.h @@ -69,21 +69,20 @@ enum EventOps { // BG events kEventDisplay = 1, // ANIM events - // kEventPlay = 1, // reused - // kEventStop = 2, // reused + kEventPlay = 1, // used in music and sound events too + kEventStop = 2, // used in music and sound events too kEventFrame = 3, - kEventSetFlag = 4, - kEventClearFlag = 5, + kEventSetFlag = 4, // used in graphics events too + kEventClearFlag = 5, // used in graphics events too kEventResumeAll = 6, - // MUISC & SOUND events - kEventPlay = 1, - kEventStop = 2, + // MUSIC and SOUND events + // Reused: kEventPlay, kEventStop // SCENE events kEventDraw = 1, kEventEnd = 2, // TEXT events - kEventHide = 2, kEventRemove = 3, + // Reused: kEventHide // PALANIM events kEventCycleStart = 1, kEventCycleStep = 2, @@ -93,6 +92,7 @@ enum EventOps { kEventSetStatus = 3, kEventClearStatus = 4, kEventSetFadeMode = 5, + kEventRestoreMode = 6, // ACTOR events kEventMove = 1, // SCRIPT events @@ -101,23 +101,24 @@ enum EventOps { kEventThreadWake = 3, // CURSOR events kEventShow = 1, - // kEventHide = 2, // reused + kEventHide = 2, // used in text events too kEventSetNormalCursor = 3, kEventSetBusyCursor = 4, // GRAPHICS events kEventFillRect = 1, - // kEventSetFlag = 4, // reused - // kEventClearFlag = 5, // reused - + // Reused: kEventSetFlag, kEventClearFlag // CONTINUOUS events + // // PALETTE events kEventPalToBlack = 1, kEventBlackToPal = 2, + kEventPalFade = 3, // TRANSITION events kEventDissolve = 1, kEventDissolveBGMask = 2, // CUTAWAY events - kEventClear = 1 + kEventClear = 1, + kEventShowCutawayBg = 2 }; enum EventParams { diff --git a/engines/saga/font.cpp b/engines/saga/font.cpp index 71cd6b0eef..06e51a78e4 100644 --- a/engines/saga/font.cpp +++ b/engines/saga/font.cpp @@ -49,6 +49,7 @@ Font::Font(SagaEngine *vm) : _vm(vm), _initialized(false) { } _initialized = true; + _fontMapping = 0; } Font::~Font(void) { @@ -325,14 +326,29 @@ void Font::outFont(const FontStyle &drawFont, Surface *ds, const char *text, siz c_code = *textPointer & 0xFFU; // Translate character - if (!(flags & kFontDontmap)) + if (_fontMapping == 0) { // Check font mapping debug flag + // Default game behavior + + // It seems that this font mapping causes problems with non-english + // versions of IHNM, so it has been changed to apply for ITE only. + // It doesn't make any difference for the English version of IHNM. + // Fixes bug #1796045: "IHNM: Spanish font wrong". + if (!(flags & kFontDontmap) && _vm->getGameType() == GType_ITE) + c_code = _charMap[c_code]; + } else if (_fontMapping == 1) { + // Force font mapping c_code = _charMap[c_code]; + } else { + // In all other cases, ignore font mapping + } assert(c_code < FONT_CHARCOUNT); // Check if character is defined if ((drawFont.fontCharEntry[c_code].index == 0) && (c_code != FONT_FIRSTCHAR)) { #if FONT_SHOWUNDEFINED - if (c_code == FONT_CH_SPACE || c_code == 9) { + // A tab character appears in the IHNM demo instructions screen, so filter + // it out here + if (c_code == FONT_CH_SPACE || c_code == FONT_CH_TAB) { textPoint.x += drawFont.fontCharEntry[c_code].tracking; continue; } @@ -632,8 +648,7 @@ Font::FontId Font::knownFont2FontIdx(KnownFont font) { // The demo version of IHNM has 3 font types (like ITE), not 6 (like the full version of IHNM) if (_vm->getGameType() == GType_ITE || _vm->getGameId() == GID_IHNM_DEMO) { - switch (font) - { + switch (font) { case (kKnownFontSmall): fontId = kSmallFont; break; @@ -655,8 +670,7 @@ Font::FontId Font::knownFont2FontIdx(KnownFont font) { break; } } else if (_vm->getGameType() == GType_IHNM && _vm->getGameId() != GID_IHNM_DEMO) { - switch (font) - { + switch (font) { case (kKnownFontSmall): fontId = kSmallFont; break; diff --git a/engines/saga/font.h b/engines/saga/font.h index 5823513e0f..16fdfc22b9 100644 --- a/engines/saga/font.h +++ b/engines/saga/font.h @@ -39,6 +39,7 @@ namespace Saga { // have a valid offset of '0' #define FONT_FIRSTCHAR 33 +#define FONT_CH_TAB 9 #define FONT_CH_SPACE 32 #define FONT_CH_QMARK 63 @@ -140,8 +141,11 @@ class Font { } void textDrawRect(KnownFont font, Surface *ds, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) { textDrawRect(knownFont2FontIdx(font), ds, text, rect, color, effectColor, flags); + } + void setFontMapping(int mapping) { + _fontMapping = mapping; } - + private: enum FontId { kSmallFont, @@ -196,6 +200,7 @@ class Font { SagaEngine *_vm; bool _initialized; + int _fontMapping; int _loadedFonts; FontData **_fonts; diff --git a/engines/saga/gfx.cpp b/engines/saga/gfx.cpp index 56ffe04c96..8509da62ed 100644 --- a/engines/saga/gfx.cpp +++ b/engines/saga/gfx.cpp @@ -356,8 +356,6 @@ void Gfx::blackToPal(PalEntry *srcPal, double percent) { // Exponential fade fpercent = percent * percent; - fpercent = 1.0 - fpercent; - // Use the correct percentage change per frame for each palette entry for (i = 0, ppal = _currentPal; i < PAL_ENTRIES; i++, ppal += 4) { if (i < from || i >= from + numcolors) @@ -365,7 +363,7 @@ void Gfx::blackToPal(PalEntry *srcPal, double percent) { else palE = &srcPal[i]; - new_entry = (int)(palE->red - palE->red * fpercent); + new_entry = (int)(palE->red * fpercent); if (new_entry < 0) { ppal[0] = 0; @@ -373,7 +371,7 @@ void Gfx::blackToPal(PalEntry *srcPal, double percent) { ppal[0] = (byte)new_entry; } - new_entry = (int)(palE->green - palE->green * fpercent); + new_entry = (int)(palE->green * fpercent); if (new_entry < 0) { ppal[1] = 0; @@ -381,7 +379,7 @@ void Gfx::blackToPal(PalEntry *srcPal, double percent) { ppal[1] = (byte) new_entry; } - new_entry = (int)(palE->blue - palE->blue * fpercent); + new_entry = (int)(palE->blue * fpercent); if (new_entry < 0) { ppal[2] = 0; @@ -402,7 +400,79 @@ void Gfx::blackToPal(PalEntry *srcPal, double percent) { _system->setPalette(_currentPal, 0, PAL_ENTRIES); } +// Used in IHNM only +void Gfx::palFade(PalEntry *srcPal, int16 from, int16 to, int16 start, int16 numColors, double percent) { + int i; + int new_entry; + byte *ppal; + PalEntry *palE; + double fpercent; + + from = from > 256 ? 256 : from; + from = from < 0 ? 0 : from; + to = to > 256 ? 256 : to; + to = to < 0 ? 0 : to; + + if (from == 0 || to == 0) { + // This case works like palToBlack or blackToPal, so no changes are needed + } else { + double x = from > to ? from / to : to / from; + percent /= x; + if (from < to) + percent += 1 / x; + } + + // Exponential fade + percent = percent > 1.0 ? 1.0 : percent; + fpercent = percent * percent; + + if (from > to) + fpercent = 1.0 - fpercent; + + // Use the correct percentage change per frame for each palette entry + for (i = 0, ppal = _currentPal; i < PAL_ENTRIES; i++, ppal += 4) { + if (i < start || i >= start + numColors) + palE = &_globalPalette[i]; + else + palE = &srcPal[i]; + + new_entry = (int)(palE->red * fpercent); + + if (new_entry < 0) { + ppal[0] = 0; + } else { + ppal[0] = (byte) new_entry; + } + + new_entry = (int)(palE->green * fpercent); + + if (new_entry < 0) { + ppal[1] = 0; + } else { + ppal[1] = (byte) new_entry; + } + + new_entry = (int)(palE->blue * fpercent); + + if (new_entry < 0) { + ppal[2] = 0; + } else { + ppal[2] = (byte) new_entry; + } + ppal[3] = 0; + } + + // Color 0 should always be black in IHNM + memset(&_currentPal[0 * 4], 0, 4); + + _system->setPalette(_currentPal, 0, PAL_ENTRIES); +} + void Gfx::showCursor(bool state) { + // Don't show the mouse cursor in the non-interactive part of the IHNM demo + if (_vm->_scene->isNonInteractiveIHNMDemoPart()) + state = false; + CursorMan.showMouse(state); } diff --git a/engines/saga/gfx.h b/engines/saga/gfx.h index 0fa7aab742..ea1370c79d 100644 --- a/engines/saga/gfx.h +++ b/engines/saga/gfx.h @@ -150,6 +150,7 @@ public: void restorePalette() { setPalette(_savedPalette, true); } void palToBlack(PalEntry *src_pal, double percent); void blackToPal(PalEntry *src_pal, double percent); + void palFade(PalEntry *srcPal, int16 from, int16 to, int16 start, int16 numColors, double percent); void showCursor(bool state); void setCursor(CursorType cursorType = kCursorNormal); diff --git a/engines/saga/ihnm_introproc.cpp b/engines/saga/ihnm_introproc.cpp index 631da37e82..fcf69d1bd1 100644 --- a/engines/saga/ihnm_introproc.cpp +++ b/engines/saga/ihnm_introproc.cpp @@ -31,154 +31,52 @@ #include "saga/animation.h" #include "saga/events.h" #include "saga/interface.h" +#include "saga/render.h" #include "saga/rscfile.h" #include "saga/sndres.h" #include "saga/music.h" #include "saga/scene.h" -namespace Saga { +#include "common/events.h" -SceneResourceData IHNM_IntroMovie1RL[] = { - {30, 2, 0, 0, false}, - {31, 14, 0, 0, false} -}; - -SceneDescription IHNM_IntroMovie1Desc = { - 0, 0, 0, 0, 0, 0, 0, 0, - IHNM_IntroMovie1RL, - ARRAYSIZE(IHNM_IntroMovie1RL) -}; - -SceneResourceData IHNM_IntroMovie2RL[] = { - {32, 2, 0, 0, false}, - {33, 14, 0, 0, false} -}; - -SceneDescription IHNM_IntroMovie2Desc = { - 0, 0, 0, 0, 0, 0, 0, 0, - IHNM_IntroMovie2RL, - ARRAYSIZE(IHNM_IntroMovie2RL) -}; - -SceneResourceData IHNM_IntroMovie3RL[] = { - {34, 2, 0, 0, false}, - {35, 14, 0, 0, false} -}; - -SceneDescription IHNM_IntroMovie3Desc = { - 0, 0, 0, 0, 0, 0, 0, 0, - IHNM_IntroMovie3RL, - ARRAYSIZE(IHNM_IntroMovie3RL) -}; - -SceneResourceData IHNM_IntroMovie4RL[] = { - {1227, 2, 0, 0, false}, - {1226, 14, 0, 0, false} -}; - -SceneDescription IHNM_IntroMovie4Desc = { - 0, 0, 0, 0, 0, 0, 0, 0, - IHNM_IntroMovie4RL, - ARRAYSIZE(IHNM_IntroMovie4RL) -}; - -// Demo -SceneResourceData IHNMDEMO_IntroMovie1RL[] = { - {19, 2, 0, 0, false} // this scene doesn't have an animation -}; - -SceneDescription IHNMDEMO_IntroMovie1Desc = { - 0, 0, 0, 0, 0, 0, 0, 0, - IHNMDEMO_IntroMovie1RL, - ARRAYSIZE(IHNMDEMO_IntroMovie1RL) -}; - -SceneResourceData IHNMDEMO_IntroMovie2RL[] = { - {22, 2, 0, 0, false}, - {23, 14, 0, 0, false} -}; - -SceneDescription IHNMDEMO_IntroMovie2Desc = { - 0, 0, 0, 0, 0, 0, 0, 0, - IHNMDEMO_IntroMovie2RL, - ARRAYSIZE(IHNMDEMO_IntroMovie2RL) -}; - -LoadSceneParams IHNM_IntroList[] = { - {0, kLoadByDescription, &IHNM_IntroMovie1Desc, Scene::SC_IHNMIntroMovieProc1, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}, - {0, kLoadByDescription, &IHNM_IntroMovie2Desc, Scene::SC_IHNMIntroMovieProc2, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}, - {0, kLoadByDescription, &IHNM_IntroMovie3Desc, Scene::SC_IHNMIntroMovieProc3, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}, -}; - -LoadSceneParams IHNMDEMO_IntroList[] = { - {0, kLoadByDescription, &IHNMDEMO_IntroMovie1Desc, Scene::SC_IHNMIntroMovieProc1, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}, - {0, kLoadByDescription, &IHNMDEMO_IntroMovie2Desc, Scene::SC_IHNMIntroMovieProc3, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}, -}; +namespace Saga { // IHNM cutaway intro resource IDs #define RID_IHNM_INTRO_CUTAWAYS 39 #define RID_IHNMDEMO_INTRO_CUTAWAYS 25 int Scene::IHNMStartProc() { - size_t n_introscenes; - size_t i; - LoadSceneParams firstScene; - /* - // Test code - uses loadCutawayList to load the intro cutaways, like the original + IHNMLoadCutaways(); - ResourceContext *resourceContext; - //ResourceContext *soundContext; - byte *resourcePointer; - size_t resourceLength; - - resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE); - if (resourceContext == NULL) { - error("Scene::IHNMStartProc() resource context not found"); - } - - if (_vm->getGameId() != GID_IHNM_DEMO) - _vm->_resource->loadResource(resourceContext, RID_IHNM_INTRO_CUTAWAYS, resourcePointer, resourceLength); - else - _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_INTRO_CUTAWAYS, resourcePointer, resourceLength); - - if (resourceLength == 0) { - error("Scene::IHNMStartProc() Can't load cutaway list"); - } - - // Load the cutaways for the title screens - _vm->_anim->loadCutawayList(resourcePointer, resourceLength); - - // Note that the resource ID needed is the returned ID minus one - printf("%i\n", _vm->_anim->cutawayResourceID(0)); - printf("%i\n", _vm->_anim->cutawayResourceID(1)); - printf("%i\n", _vm->_anim->cutawayResourceID(2)); - */ - - // The original used the "play video" mechanism for the first part of - // the intro. We just use that panel mode. - - _vm->_anim->setCutAwayMode(kPanelVideo); - _vm->_interface->setMode(kPanelVideo); - - if (_vm->getGameId() != GID_IHNM_DEMO) - n_introscenes = ARRAYSIZE(IHNM_IntroList); - else - n_introscenes = ARRAYSIZE(IHNMDEMO_IntroList); - - // Queue the company and title videos if (_vm->getGameId() != GID_IHNM_DEMO) { - for (i = 0; i < n_introscenes; i++) { - _vm->_scene->queueScene(&IHNM_IntroList[i]); + int logoLength = -168; + + if (_vm->getLanguage() == Common::DE_DEU || _vm->getLanguage() == Common::ES_ESP) + logoLength = -128; + + // Play Cyberdreams logo for 168 frames + if (!playTitle(0, logoLength, true)) { + // Play Dreamers Guild logo for 10 seconds + if (!playLoopingTitle(1, 10)) { + // Play the title music + _vm->_music->play(1, MUSIC_NORMAL); + // Play title screen + playTitle(2, 17); + } } } else { - for (i = 0; i < n_introscenes; i++) { - _vm->_scene->queueScene(&IHNMDEMO_IntroList[i]); - } + _vm->_music->play(1, MUSIC_NORMAL); + playTitle(0, 10); + playTitle(2, 12); } + _vm->_music->setVolume(0, 1000); + _vm->_anim->freeCutawayList(); + + // Queue first scene firstScene.loadFlag = kLoadBySceneNumber; firstScene.sceneDescriptor = -1; firstScene.sceneDescription = NULL; @@ -193,241 +91,189 @@ int Scene::IHNMStartProc() { return SUCCESS; } -int Scene::SC_IHNMIntroMovieProc1(int param, void *refCon) { - return ((Scene *)refCon)->IHNMIntroMovieProc1(param); -} +int Scene::IHNMCreditsProc() { + IHNMLoadCutaways(); -int Scene::IHNMIntroMovieProc1(int param) { - Event event; - Event *q_event; - - switch (param) { - case SCENE_BEGIN: - // Background for intro scene is the first frame of the - // intro animation; display it and set the palette - event.type = kEvTOneshot; - event.code = kBgEvent; - event.op = kEventDisplay; - event.param = kEvPSetPalette; - event.time = 0; - - q_event = _vm->_events->queue(&event); - - if (_vm->getGameId() != GID_IHNM_DEMO) { - _vm->_anim->setFrameTime(0, IHNM_INTRO_FRAMETIME); - _vm->_anim->setFlag(0, ANIM_FLAG_ENDSCENE); - - event.type = kEvTOneshot; - event.code = kAnimEvent; - event.op = kEventPlay; - event.param = 0; - event.time = 0; - - q_event = _vm->_events->chain(q_event, &event); - } else { - // Start playing the intro music for the demo version - event.type = kEvTOneshot; - event.code = kMusicEvent; - event.param = 1; - event.param2 = MUSIC_NORMAL; - event.op = kEventPlay; - event.time = 0; - - q_event = _vm->_events->chain(q_event, &event); - - // The IHNM demo doesn't have an animation at the - // Cyberdreans logo screen - - // Queue end of scene after a while - event.type = kEvTOneshot; - event.code = kSceneEvent; - event.op = kEventEnd; - event.time = 8000; - - q_event = _vm->_events->chain(q_event, &event); - } + _vm->_music->play(0, MUSIC_NORMAL); - break; - default: - break; + if (_vm->getGameId() != GID_IHNM_DEMO) { + // Display the credits for 400 frames + playTitle(4, -400, true); + } else { + // Display sales info for 60 seconds + playTitle(3, 60, true); } - return 0; -} + _vm->_music->setVolume(0, 1000); + _vm->_anim->freeCutawayList(); -int Scene::SC_IHNMIntroMovieProc2(int param, void *refCon) { - return ((Scene *)refCon)->IHNMIntroMovieProc2(param); + return SUCCESS; } -int Scene::IHNMIntroMovieProc2(int param) { - Event event; - Event *q_event; - PalEntry *pal; - - static PalEntry current_pal[PAL_ENTRIES]; - - switch (param) { - case SCENE_BEGIN: - // Fade to black out of the intro CyberDreams logo anim - _vm->_gfx->getCurrentPal(current_pal); - - event.type = kEvTContinuous; - event.code = kPalEvent; - event.op = kEventPalToBlack; - event.time = 0; - event.duration = IHNM_PALFADE_TIME; - event.data = current_pal; - - q_event = _vm->_events->queue(&event); - - // Background for intro scene is the first frame of the - // intro animation; display it but don't set palette - event.type = kEvTOneshot; - event.code = kBgEvent; - event.op = kEventDisplay; - event.param = kEvPNoSetPalette; - event.time = 0; - - q_event = _vm->_events->chain(q_event, &event); - - _vm->_anim->setCycles(0, -1); - - // Unlike the original, we keep the logo spinning during the - // palette fades. We don't have to, but I think it looks better - // that way. - - event.type = kEvTOneshot; - event.code = kAnimEvent; - event.op = kEventPlay; - event.param = 0; - event.time = 0; - - q_event = _vm->_events->chain(q_event, &event); - - // Fade in from black to the scene background palette - _vm->_scene->getBGPal(pal); - - event.type = kEvTContinuous; - event.code = kPalEvent; - event.op = kEventBlackToPal; - event.time = 0; - event.duration = IHNM_PALFADE_TIME; - event.data = pal; - - q_event = _vm->_events->chain(q_event, &event); - - // Fade to black after looping animation for a while - event.type = kEvTContinuous; - event.code = kPalEvent; - event.op = kEventPalToBlack; - event.time = IHNM_DGLOGO_TIME; - event.duration = IHNM_PALFADE_TIME; - event.data = pal; - - q_event = _vm->_events->chain(q_event, &event); - - // Queue end of scene - event.type = kEvTOneshot; - event.code = kSceneEvent; - event.op = kEventEnd; - event.time = 0; - - q_event = _vm->_events->chain(q_event, &event); - break; - default: - break; +void Scene::IHNMLoadCutaways() { + ResourceContext *resourceContext; + //ResourceContext *soundContext; + byte *resourcePointer; + size_t resourceLength; + + resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE); + if (resourceContext == NULL) { + error("Scene::IHNMStartProc() resource context not found"); } - return 0; -} + if (_vm->getGameId() != GID_IHNM_DEMO) + _vm->_resource->loadResource(resourceContext, RID_IHNM_INTRO_CUTAWAYS, resourcePointer, resourceLength); + else + _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_INTRO_CUTAWAYS, resourcePointer, resourceLength); -int Scene::SC_IHNMIntroMovieProc3(int param, void *refCon) { - return ((Scene *)refCon)->IHNMIntroMovieProc3(param); -} + if (resourceLength == 0) { + error("Scene::IHNMStartProc() Can't load cutaway list"); + } -int Scene::IHNMIntroMovieProc3(int param) { - Event event; - Event *q_event; - PalEntry *pal; - static PalEntry current_pal[PAL_ENTRIES]; + // Load the cutaways for the title screens + _vm->_anim->loadCutawayList(resourcePointer, resourceLength); +} - switch (param) { - case SCENE_BEGIN: - // Fade to black out of the intro DG logo anim - _vm->_gfx->getCurrentPal(current_pal); +bool Scene::checkKey() { + Common::Event event; + bool res = false; + + while (_vm->_eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_QUIT: + _vm->shutDown(); + // fallthrough + case Common::EVENT_KEYDOWN: + res = true; + break; + default: + break; + } + } - event.type = kEvTContinuous; - event.code = kPalEvent; - event.op = kEventPalToBlack; - event.time = 0; - event.duration = IHNM_PALFADE_TIME; - event.data = current_pal; + return res; +} - q_event = _vm->_events->queue(&event); +bool Scene::playTitle(int title, int time, int mode) { + bool interrupted = false; + Surface *backBufferSurface; + int startTime = _vm->_system->getMillis(); + int frameTime = 0; + int curTime; + int assignedId; + int phase = 0; + bool done = false; + bool playParameter = true; + static PalEntry cur_pal[PAL_ENTRIES]; + static PalEntry pal_cut[PAL_ENTRIES]; + + backBufferSurface = _vm->_render->getBackGroundSurface(); + + // Load the cutaway + + _vm->_anim->setCutAwayMode(mode); + _vm->_frameCount = 0; + + _vm->_gfx->getCurrentPal(cur_pal); + + assignedId = _vm->_anim->playCutaway(title, false); + + _vm->_gfx->getCurrentPal(pal_cut); + + while (!done) { + curTime = _vm->_system->getMillis(); + + switch (phase) { + case 0: // fadeout + case 1: // fadeout 100% + case 7: // fadeout + case 8: // fadeout 100% + _vm->_gfx->palToBlack(cur_pal, (double)(curTime - startTime) / kNormalFadeDuration); + // fall through + + case 3: // fadein + case 4: // fadein 100% + if (phase == 3 || phase == 4) + _vm->_gfx->blackToPal(pal_cut, (double)(curTime - startTime) / kNormalFadeDuration); + + if (curTime - startTime > kNormalFadeDuration) { + phase++; + if (phase == 2 || phase == 5 || phase == 9) + startTime = curTime; + break; + } + break; + + case 2: // display background + _vm->_system->copyRectToScreen((byte *)backBufferSurface->pixels, backBufferSurface->w, 0, 0, + backBufferSurface->w, backBufferSurface->h); + phase++; + startTime = curTime; + break; + + case 5: // playback + if (time < 0) { + if (_vm->_frameCount >= -time) { + phase++; + break; + } + } else { + if (curTime - startTime >= time * 1000) { + phase++; + break; + } + } + + if (checkKey()) { + interrupted = true; + done = true; + break; + } + + if (_vm->_anim->getCycles(assignedId)) { // IHNM demo has 0 frames logo + if (curTime - frameTime > _vm->_anim->getFrameTime(assignedId)) { + _vm->_anim->play(assignedId, 0, playParameter); + + if (playParameter == true) // Do not loop animations + playParameter = false; + + frameTime = curTime; + + _vm->_system->copyRectToScreen((byte *)backBufferSurface->pixels, backBufferSurface->w, 0, 0, + backBufferSurface->w, backBufferSurface->h); + } + + } + break; + + case 6: // playback end + startTime = curTime; + _vm->_gfx->getCurrentPal(cur_pal); + phase++; + break; + + case 9: // end + done = true; + break; + } - // Music, maestro + _vm->_system->updateScreen(); + _vm->_system->delayMillis(10); + } - // In the GM file, this music also appears as tracks 7, 13, 19, - // 25 and 31, but only track 1 sounds right with the FM music. + // Clean up - if (_vm->getGameId() != GID_IHNM_DEMO) { - event.type = kEvTOneshot; - event.code = kMusicEvent; - event.param = 1; - event.param2 = MUSIC_NORMAL; - event.op = kEventPlay; - event.time = 0; + _vm->_anim->endVideo(); - q_event = _vm->_events->chain(q_event, &event); - } + memset((byte *)backBufferSurface->pixels, 0, backBufferSurface->w * backBufferSurface->h); + _vm->_system->copyRectToScreen((byte *)backBufferSurface->pixels, backBufferSurface->w, 0, 0, + backBufferSurface->w, backBufferSurface->h); - // Background for intro scene is the first frame of the intro - // animation; display it but don't set palette - event.type = kEvTOneshot; - event.code = kBgEvent; - event.op = kEventDisplay; - event.param = kEvPNoSetPalette; - event.time = 0; - - q_event = _vm->_events->chain(q_event, &event); - - // Fade in from black to the scene background palette - _vm->_scene->getBGPal(pal); - - event.type = kEvTContinuous; - event.code = kPalEvent; - event.op = kEventBlackToPal; - event.time = 0; - event.duration = IHNM_PALFADE_TIME; - event.data = pal; - - q_event = _vm->_events->chain(q_event, &event); - - event.type = kEvTOneshot; - event.code = kAnimEvent; - event.op = kEventPlay; - event.param = 0; - event.time = 0; - - q_event = _vm->_events->chain(q_event, &event); - - // Queue end of scene after a while - // The delay has been increased so the speech won't start until the music has ended - event.type = kEvTOneshot; - event.code = kSceneEvent; - event.op = kEventEnd; - if (_vm->getGameId() != GID_IHNM_DEMO) - event.time = _vm->_music->hasAdlib() ? IHNM_TITLE_TIME_FM : IHNM_TITLE_TIME_GM; - else - event.time = 12000; - - q_event = _vm->_events->chain(q_event, &event); - break; - default: - break; - } + return interrupted; +} - return 0; +bool Scene::playLoopingTitle(int title, int seconds) { + return playTitle(title, seconds, kPanelCutaway); } } // End of namespace Saga diff --git a/engines/saga/input.cpp b/engines/saga/input.cpp index 358a225efa..5082ec7aca 100644 --- a/engines/saga/input.cpp +++ b/engines/saga/input.cpp @@ -49,7 +49,7 @@ int SagaEngine::processInput() { _console->attach(); } if (_interface->_textInput || _interface->_statusTextInput) { - _interface->processAscii(event.kbd.ascii); + _interface->processAscii(event.kbd); return SUCCESS; } @@ -115,7 +115,7 @@ int SagaEngine::processInput() { _render->toggleFlag(RF_RENDERPAUSE); break; default: - _interface->processAscii(event.kbd.ascii); + _interface->processAscii(event.kbd); break; } break; diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp index 8cfd993391..e6854b1c05 100644 --- a/engines/saga/interface.cpp +++ b/engines/saga/interface.cpp @@ -229,6 +229,12 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) { _vm->_sprite->loadList(_vm->getResourceDescription()->mainPanelSpritesResourceId, _mainPanel.sprites); // Option panel sprites _vm->_sprite->loadList(_vm->getResourceDescription()->optionPanelSpritesResourceId, _optionPanel.sprites); + // Save panel sprites + _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _savePanel.sprites); + // Load panel sprites + _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _loadPanel.sprites); + // Quit panel sprites + _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _quitPanel.sprites); if (_vm->getGameType() == GType_ITE) { _vm->_sprite->loadList(_vm->getResourceDescription()->defaultPortraitsResourceId, _defPortraits); @@ -342,18 +348,10 @@ int Interface::activate() { } else if (_panelMode == kPanelNull && _vm->getGameId() == GID_IHNM_DEMO) { _saveReminderState = 1; } + _vm->_gfx->showCursor(true); draw(); } - if (_vm->getGameId() != GID_IHNM_DEMO) { - _vm->_gfx->showCursor(true); - } else { - if (_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) { - // Don't show the mouse cursor in the non-interactive part of the IHNM demo - } else { - _vm->_gfx->showCursor(true); - } - } return SUCCESS; } @@ -369,7 +367,7 @@ int Interface::deactivate() { } void Interface::rememberMode() { - debug(1, "rememberMode(%d)", _savedMode); + debug(1, "rememberMode(%d)", _panelMode); _savedMode = _panelMode; } @@ -400,9 +398,6 @@ void Interface::setMode(int mode) { if (_vm->getGameId() == GID_IHNM_DEMO) { _inMainMode = true; _saveReminderState = 1; - if ((_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) || - _vm->_scene->currentSceneNumber() == 0 || _vm->_scene->currentSceneNumber() == -1) - _vm->_gfx->showCursor(false); } } else if (mode == kPanelOption) { // Show the cursor in the IHNM demo @@ -420,8 +415,8 @@ void Interface::setMode(int mode) { switch (_panelMode) { case kPanelMain: - if (_vm->getGameType() == GType_IHNM) - warning("FIXME: Implement IHNM differences from ExecuteInventoryPanel"); + // FIXME: Implement IHNM differences from ExecuteInventoryPanel for IHNM (though I believe they're already + // implemented) _mainPanel.currentButton = NULL; break; @@ -481,22 +476,22 @@ void Interface::setMode(int mode) { draw(); } -bool Interface::processAscii(uint16 ascii) { +bool Interface::processAscii(Common::KeyState keystate) { // TODO: Checking for Esc and Enter below is a bit hackish, and // and probably only works with the English version. Maybe we should // add a flag to the button so it can indicate if it's the default or // cancel button? - + uint16 ascii = keystate.ascii; int i; PanelButton *panelButton; if (_statusTextInput) { - processStatusTextInput(ascii); + processStatusTextInput(keystate); return true; } switch (_panelMode) { case kPanelNull: - if (ascii == 27) { // Esc + if (keystate.keycode == Common::KEYCODE_ESCAPE) { if (_vm->_scene->isInIntro()) { _vm->_scene->skipScene(); } else { @@ -506,26 +501,22 @@ bool Interface::processAscii(uint16 ascii) { return true; } - if (_vm->getGameId() == GID_IHNM_DEMO) { - if (_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) - _vm->_scene->showIHNMDemoSpecialScreen(); - } + if (_vm->_scene->isNonInteractiveIHNMDemoPart()) + _vm->_scene->showIHNMDemoSpecialScreen(); break; case kPanelCutaway: - if (ascii == 27) { // Esc + if (keystate.keycode == Common::KEYCODE_ESCAPE) { if (!_disableAbortSpeeches) _vm->_actor->abortAllSpeeches(); _vm->_scene->cutawaySkip(); return true; } - if (_vm->getGameId() == GID_IHNM_DEMO) { - if (_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) - _vm->_scene->showIHNMDemoSpecialScreen(); - } + if (_vm->_scene->isNonInteractiveIHNMDemoPart()) + _vm->_scene->showIHNMDemoSpecialScreen(); break; case kPanelVideo: - if (ascii == 27) { // Esc + if (keystate.keycode == Common::KEYCODE_ESCAPE) { if (_vm->_scene->isInIntro()) { _vm->_scene->skipScene(); } else { @@ -536,14 +527,12 @@ bool Interface::processAscii(uint16 ascii) { return true; } - if (_vm->getGameId() == GID_IHNM_DEMO) { - if (_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) - _vm->_scene->showIHNMDemoSpecialScreen(); - } + if (_vm->_scene->isNonInteractiveIHNMDemoPart()) + _vm->_scene->showIHNMDemoSpecialScreen(); break; case kPanelOption: // TODO: check input dialog keys - if (ascii == 27 || ascii == 13) { // Esc or Enter + if (keystate.keycode == Common::KEYCODE_ESCAPE || keystate.keycode == Common::KEYCODE_RETURN) { // Esc or Enter ascii = 'c'; //continue } @@ -558,13 +547,13 @@ bool Interface::processAscii(uint16 ascii) { } break; case kPanelSave: - if (_textInput && processTextInput(ascii)) { + if (_textInput && processTextInput(keystate)) { return true; } - if (ascii == 27) { // Esc + if (keystate.keycode == Common::KEYCODE_ESCAPE) { ascii = 'c'; // cancel - } else if (ascii == 13) { // Enter + } else if (keystate.keycode == Common::KEYCODE_RETURN) { // Enter ascii = 's'; // save } @@ -579,9 +568,9 @@ bool Interface::processAscii(uint16 ascii) { } break; case kPanelQuit: - if (ascii == 27) { // Esc + if (keystate.keycode == Common::KEYCODE_ESCAPE) { ascii = 'c'; // cancel - } else if (ascii == 13) { // Enter + } else if (keystate.keycode == Common::KEYCODE_RETURN) { // Enter ascii = 'q'; // quit } @@ -619,8 +608,7 @@ bool Interface::processAscii(uint16 ascii) { return true; } } - if (ascii == 15) // ctrl-o - { + if (keystate.keycode == Common::KEYCODE_o && keystate.flags == Common::KBD_CTRL) { // ctrl-o if (_saveReminderState > 0) { setMode(kPanelOption); return true; @@ -663,7 +651,7 @@ bool Interface::processAscii(uint16 ascii) { mapPanelClean(); break; case kPanelSceneSubstitute: - if (ascii == 13) { + if (keystate.keycode == Common::KEYCODE_RETURN) { _vm->_render->clearFlag(RF_DEMO_SUBST); _vm->_gfx->setPalette(_mapSavedPal); setMode(kPanelMain); @@ -678,11 +666,11 @@ bool Interface::processAscii(uint16 ascii) { break; case kPanelProtect: if (_vm->getGameType() == GType_ITE) { - if (_textInput && processTextInput(ascii)) { + if (_textInput && processTextInput(keystate)) { return true; } - if (ascii == 27 || ascii == 13) { // Esc or Enter + if (keystate.keycode == Common::KEYCODE_ESCAPE || keystate.keycode == Common::KEYCODE_RETURN) { _vm->_script->wakeUpThreads(kWaitTypeRequest); _vm->_interface->setMode(kPanelMain); @@ -725,7 +713,7 @@ void Interface::setStatusText(const char *text, int statusColor) { assert(text != NULL); assert(strlen(text) < STATUS_TEXT_LEN); - if (_vm->_render->getFlags() & (RF_PLACARD | RF_MAP)) + if (_vm->_render->getFlags() & RF_MAP || _vm->_interface->getMode() == kPanelPlacard) return; strncpy(_statusText, text, STATUS_TEXT_LEN); @@ -801,16 +789,6 @@ void Interface::draw() { converseDisplayTextLines(backBuffer); } - if (_vm->getGameType() == GType_IHNM) { - if (_vm->_spiritualBarometer > 255) - _vm->_gfx->setPaletteColor(kIHNMColorPortrait, 0xff, 0xff, 0xff); - else - _vm->_gfx->setPaletteColor(kIHNMColorPortrait, - _vm->_spiritualBarometer * _portraitBgColor.red / 256, - _vm->_spiritualBarometer * _portraitBgColor.green / 256, - _vm->_spiritualBarometer * _portraitBgColor.blue / 256); - } - if (_panelMode == kPanelMain || _panelMode == kPanelConverse || _lockedMode == kPanelMain || _lockedMode == kPanelConverse || (_panelMode == kPanelNull && _vm->getGameId() == GID_IHNM_DEMO)) { @@ -841,14 +819,19 @@ void Interface::calcOptionSaveSlider() { int totalFiles = _vm->getSaveFilesCount(); int visibleFiles = _vm->getDisplayInfo().optionSaveFileVisible; int height = _optionSaveFileSlider->height; - int sliderHeight; + int sliderHeight = 13; // IHNM's save file list slider has a fixed height int pos; if (totalFiles < visibleFiles) { totalFiles = visibleFiles; } - sliderHeight = visibleFiles * height / totalFiles; + if (_vm->getGameType() == GType_ITE) { + // ITE's save file list slider has a dynamically computed height, depending on + // the number of save games + sliderHeight = visibleFiles * height / totalFiles; + } + if (sliderHeight < 7) { sliderHeight = 7; } @@ -931,6 +914,7 @@ void Interface::drawOption() { PanelButton *panelButton; Point textPoint; Point point; + Point sliderPoint; int spritenum = 0; backBuffer = _vm->_gfx->getBackBuffer(); @@ -959,7 +943,15 @@ void Interface::drawOption() { backBuffer->drawRect(_optionSaveRectTop, kITEColorDarkGrey); } - drawButtonBox(backBuffer, _optionSaveRectSlider, kSlider, _optionSaveFileSlider->state > 0); + if (_vm->getGameType() == GType_ITE) { + drawButtonBox(backBuffer, _optionSaveRectSlider, kSlider, _optionSaveFileSlider->state > 0); + } else { + panelButton = &_optionPanel.buttons[0]; + sliderPoint.x = _optionPanel.x + panelButton->xOffset; + sliderPoint.y = _optionSaveRectSlider.top; + _vm->_sprite->draw(backBuffer, _vm->getDisplayClip(), _optionPanel.sprites, 0 + _optionSaveFileSlider->state, sliderPoint, 256); + + } if (_optionSaveRectBottom.height() > 0) { backBuffer->drawRect(_optionSaveRectBottom, kITEColorDarkGrey); @@ -970,7 +962,10 @@ void Interface::drawOption() { rect2 = rect; fontHeight = _vm->_font->getHeight(kKnownFontSmall); for (j = 0; j < _vm->getDisplayInfo().optionSaveFileVisible; j++) { - bgColor = kITEColorDarkGrey0C; + if (_vm->getGameType() == GType_ITE) + bgColor = kITEColorDarkGrey0C; + else + bgColor = kIHNMColorBlack; fgColor = kITEColorBrightWhite; idx = j + _optionSaveFileTop; @@ -1052,7 +1047,10 @@ void Interface::setQuit(PanelButton *panelButton) { setMode(kPanelOption); break; case kTextQuit: - _vm->shutDown(); + if (_vm->getGameId() == GID_IHNM_DEMO) + _vm->_scene->creditsScene(); // display sales info for IHNM demo + else + _vm->shutDown(); break; } } @@ -1118,20 +1116,20 @@ void Interface::setLoad(PanelButton *panelButton) { } } -void Interface::processStatusTextInput(uint16 ascii) { +void Interface::processStatusTextInput(Common::KeyState keystate) { - switch (ascii) { - case 27: // esc + switch (keystate.keycode) { + case Common::KEYCODE_ESCAPE: _statusTextInputState = kStatusTextInputAborted; _statusTextInput = false; _vm->_script->wakeUpThreads(kWaitTypeStatusTextInput); break; - case 13: // return + case Common::KEYCODE_RETURN: _statusTextInputState = kStatusTextInputEntered; _statusTextInput = false; _vm->_script->wakeUpThreads(kWaitTypeStatusTextInput); break; - case 8: // backspace + case Common::KEYCODE_BACKSPACE: if (_statusTextInputPos == 0) { break; } @@ -1141,36 +1139,36 @@ void Interface::processStatusTextInput(uint16 ascii) { if (_statusTextInputPos >= STATUS_TEXT_INPUT_MAX) { break; } - if (((ascii >= 'a') && (ascii <='z')) || - ((ascii >= '0') && (ascii <='9')) || - ((ascii >= 'A') && (ascii <='Z')) || - (ascii == ' ')) { - _statusTextInputString[_statusTextInputPos++] = ascii; + if (isalnum(keystate.ascii) || (keystate.ascii == ' ')) { + _statusTextInputString[_statusTextInputPos++] = keystate.ascii; _statusTextInputString[_statusTextInputPos] = 0; } } setStatusText(_statusTextInputString); } -bool Interface::processTextInput(uint16 ascii) { +bool Interface::processTextInput(Common::KeyState keystate) { char ch[2]; char tempString[SAVE_TITLE_SIZE]; uint tempWidth; memset(tempString, 0, SAVE_TITLE_SIZE); ch[1] = 0; + // IHNM has a smaller save title size than ITE. We only limit the save title size during text input + // in IHNM, to preserve backwards compatibility with older save games + uint save_title_size = _vm->getGameType() == GType_ITE ? SAVE_TITLE_SIZE : IHNM_SAVE_TITLE_SIZE; - switch (ascii) { - case 13: + switch (keystate.keycode) { + case Common::KEYCODE_RETURN: return false; - case 27: // esc + case Common::KEYCODE_ESCAPE: _textInput = false; break; - case 8: // backspace + case Common::KEYCODE_BACKSPACE: if (_textInputPos <= 1) { break; } _textInputPos--; - case 127: // del + case Common::KEYCODE_DELETE: if (_textInputPos <= _textInputStringLength) { if (_textInputPos != 1) { strncpy(tempString, _textInputString, _textInputPos - 1); @@ -1182,27 +1180,25 @@ bool Interface::processTextInput(uint16 ascii) { _textInputStringLength = strlen(_textInputString); } break; - case 276: // left + case Common::KEYCODE_LEFT: if (_textInputPos > 1) { _textInputPos--; } break; - case 275: // right + case Common::KEYCODE_RIGHT: if (_textInputPos <= _textInputStringLength) { _textInputPos++; } break; default: - if (((ascii >= 'a') && (ascii <='z')) || - ((ascii >= '0') && (ascii <='9')) || - ((ascii >= 'A') && (ascii <='Z')) || - (ascii == ' ')) { - if (_textInputStringLength < SAVE_TITLE_SIZE - 1) { - ch[0] = ascii; + if (isalnum(keystate.ascii) || (keystate.ascii == ' ') || + (keystate.ascii == '-') || (keystate.ascii == '_')) { + if (_textInputStringLength < save_title_size - 1) { + ch[0] = keystate.ascii; tempWidth = _vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal); tempWidth += _vm->_font->getStringWidth(kKnownFontSmall, _textInputString, 0, kFontNormal); if (tempWidth > _textInputMaxWidth) { - break; + break; } if (_textInputPos != 1) { strncpy(tempString, _textInputString, _textInputPos - 1); @@ -1243,10 +1239,10 @@ void Interface::drawTextInput(Surface *ds, InterfacePanel *panel, PanelButton *p while ((ch[0] = _textInputString[i++]) != 0) { rect.setWidth(_vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal)); if ((i == _textInputPos) && _textInput) { - fgColor = kITEColorBlack; - ds->fillRect(rect, kITEColorWhite); + fgColor = _vm->KnownColor2ColorId(kKnownColorBlack); + ds->fillRect(rect, _vm->KnownColor2ColorId(kKnownColorWhite)); } else { - fgColor = kITEColorWhite; + fgColor = _vm->KnownColor2ColorId(kKnownColorWhite); } textPoint.x = rect.left; textPoint.y = rect.top + 1; @@ -1257,7 +1253,7 @@ void Interface::drawTextInput(Surface *ds, InterfacePanel *panel, PanelButton *p if (_textInput && (_textInputPos >= i)) { ch[0] = ' '; rect.setWidth(_vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal)); - ds->fillRect(rect, kITEColorWhite); + ds->fillRect(rect, _vm->KnownColor2ColorId(kKnownColorWhite)); } } @@ -1468,11 +1464,22 @@ void Interface::handleOptionClick(const Point& mousePoint) { void Interface::handleChapterSelectionUpdate(const Point& mousePoint) { uint16 objectId; + int hitZoneIndex; + const HitZone * hitZone; // FIXME: Original handled more object types here. objectId = _vm->_actor->hitTest(mousePoint, true); + if (objectId == ID_NOTHING) { + hitZoneIndex = _vm->_scene->_objectMap->hitTest(mousePoint); + + if ((hitZoneIndex != -1)) { + hitZone = _vm->_scene->_objectMap->getHitZone(hitZoneIndex); + objectId = hitZone->getHitZoneId(); + } + } + if (objectId != _vm->_script->_pointerObject) { _vm->_script->_pointerObject = objectId; } @@ -1492,12 +1499,12 @@ void Interface::handleChapterSelectionClick(const Point& mousePoint) { switch (objectTypeId(obj)) { case kGameObjectHitZone: - hitZone = _vm->_scene->_actionMap->getHitZone(objectIdToIndex(obj)); + hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(obj)); if (hitZone == NULL) return; - if (hitZone->getFlags() & kHitZoneExit) + if (hitZone->getFlags() & kHitZoneEnabled) script = hitZone->getScriptNumber(); break; @@ -1523,7 +1530,6 @@ void Interface::handleChapterSelectionClick(const Point& mousePoint) { event.param4 = obj; // Object event.param5 = 0; // With Object event.param6 = obj; // Actor - _vm->_events->queue(&event); } } @@ -1540,11 +1546,8 @@ void Interface::setOption(PanelButton *panelButton) { } else { if (_vm->_scene->currentChapterNumber() == 8) { setMode(kPanelChapterSelection); - } else if (_vm->getGameId() == GID_IHNM_DEMO) { - if (_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) - setMode(kPanelNull); - else - setMode(kPanelMain); + } else if (_vm->_scene->isNonInteractiveIHNMDemoPart()) { + setMode(kPanelNull); } else { setMode(kPanelMain); } @@ -1564,11 +1567,9 @@ void Interface::setOption(PanelButton *panelButton) { } break; case kTextSave: - // Disallow saving in the non-interactive part of the IHNM demo - if (_vm->getGameId() == GID_IHNM_DEMO) { - if (_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) - return; - } + // Disallow saving in the non-interactive part of the IHNM demo (original demo didn't support saving at all) + if (_vm->_scene->isNonInteractiveIHNMDemoPart()) + return; if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0)) { _textInputString[0] = 0; @@ -1787,10 +1788,8 @@ void Interface::update(const Point& mousePoint, int updateFlag) { break; case kPanelNull: - if (_vm->getGameId() == GID_IHNM_DEMO && (updateFlag & UPDATE_MOUSECLICK)) { - if (_vm->_scene->currentSceneNumber() >= 144 && _vm->_scene->currentSceneNumber() <= 149) - _vm->_scene->showIHNMDemoSpecialScreen(); - } + if (_vm->_scene->isNonInteractiveIHNMDemoPart() && (updateFlag & UPDATE_MOUSECLICK)) + _vm->_scene->showIHNMDemoSpecialScreen(); break; } @@ -1808,6 +1807,10 @@ void Interface::drawStatusBar() { if (_vm->getGameType() == GType_IHNM && _vm->_scene->currentChapterNumber() == 8) return; + // Don't draw the status bar while fading out + if (_fadeMode == kFadeOut) + return; + backBuffer = _vm->_gfx->getBackBuffer(); // Erase background of status bar @@ -1921,7 +1924,6 @@ void Interface::handleMainUpdate(const Point& mousePoint) { if ((panelButton != NULL) && (panelButton->type == kPanelButtonArrow)) { if (panelButton->state == 1) { - //TODO: insert timeout catchup inventoryChangePos(panelButton->id); } changed = true; @@ -1938,11 +1940,14 @@ void Interface::handleMainUpdate(const Point& mousePoint) { //inventory stuff void Interface::inventoryChangePos(int chg) { - if ((chg < 0 && _inventoryStart + chg >= 0) || - (chg > 0 && _inventoryStart < _inventoryEnd)) { - _inventoryStart += chg; - draw(); + // Arrows will scroll the inventory up or down up to 4 items + for (int i = 1; i <= 4; i++) { + if ((chg < 0 && _inventoryStart + chg >= 0) || + (chg > 0 && _inventoryStart < _inventoryEnd)) { + _inventoryStart += chg; } + } + draw(); } void Interface::inventorySetPos(int key) { @@ -2087,18 +2092,19 @@ void Interface::drawButtonBox(Surface *ds, const Rect& rect, ButtonKind kind, bo solidColor = down ? kITEColorLightBlue94 : kITEColorLightBlue96; break; case kEdit: - cornerColor = kITEColorLightBlue96; - frameColor = kITEColorLightBlue96; - fillColor = kITEColorLightBlue96; - our = kITEColorDarkBlue8a; - odl = kITEColorLightBlue94; - iur = 0x97; - idl = 0x95; - if (down) { - solidColor = kITEColorBlue; + if (_vm->getGameType() == GType_ITE) { + cornerColor = frameColor = fillColor = kITEColorLightBlue96; + our = kITEColorDarkBlue8a; + odl = kITEColorLightBlue94; + solidColor = down ? kITEColorBlue : kITEColorDarkGrey0C; } else { - solidColor = kITEColorDarkGrey0C; + cornerColor = frameColor = fillColor = kIHNMColorBlack; + our = kIHNMColorBlack; + odl = kIHNMColorBlack; + solidColor = kIHNMColorBlack; } + iur = 0x97; + idl = 0x95; break; default: cornerColor = 0x8b; @@ -2248,21 +2254,25 @@ void Interface::drawPanelButtonText(Surface *ds, InterfacePanel *panel, PanelBut litButton = panelButton->state > 0; if (panel == &_optionPanel) { - texturePoint.x = _optionPanel.x + panelButton->xOffset; - texturePoint.y = _optionPanel.y + panelButton->yOffset; + texturePoint.x = _optionPanel.x + panelButton->xOffset - 1; + texturePoint.y = _optionPanel.y + panelButton->yOffset - 1; _vm->_sprite->draw(ds, _vm->getDisplayClip(), _optionPanel.sprites, spritenum + 2 + litButton, texturePoint, 256); } else if (panel == &_quitPanel) { - texturePoint.x = _quitPanel.x + panelButton->xOffset; - texturePoint.y = _quitPanel.y + panelButton->yOffset; - _vm->_sprite->draw(ds, _vm->getDisplayClip(), _optionPanel.sprites, 14 + litButton, texturePoint, 256); + texturePoint.x = _quitPanel.x + panelButton->xOffset - 3; + texturePoint.y = _quitPanel.y + panelButton->yOffset - 3; + _vm->_sprite->draw(ds, _vm->getDisplayClip(), _quitPanel.sprites, litButton, texturePoint, 256); } else if (panel == &_savePanel) { - texturePoint.x = _savePanel.x + panelButton->xOffset; - texturePoint.y = _savePanel.y + panelButton->yOffset; - _vm->_sprite->draw(ds, _vm->getDisplayClip(), _optionPanel.sprites, 14 + litButton, texturePoint, 256); + texturePoint.x = _savePanel.x + panelButton->xOffset - 3; + texturePoint.y = _savePanel.y + panelButton->yOffset - 3; + _vm->_sprite->draw(ds, _vm->getDisplayClip(), _savePanel.sprites, litButton, texturePoint, 256); + // Input text box sprite + texturePoint.x = _savePanel.x + _saveEdit->xOffset - 2; + texturePoint.y = _savePanel.y + _saveEdit->yOffset - 2; + _vm->_sprite->draw(ds, _vm->getDisplayClip(), _savePanel.sprites, 2, texturePoint, 256); } else if (panel == &_loadPanel) { - texturePoint.x = _loadPanel.x + panelButton->xOffset; - texturePoint.y = _loadPanel.y + panelButton->yOffset; - _vm->_sprite->draw(ds, _vm->getDisplayClip(), _optionPanel.sprites, 14 + litButton, texturePoint, 256); + texturePoint.x = _loadPanel.x + panelButton->xOffset - 3; + texturePoint.y = _loadPanel.y + panelButton->yOffset - 3; + _vm->_sprite->draw(ds, _vm->getDisplayClip(), _loadPanel.sprites, litButton, texturePoint, 256); } else { // revert to default behavior drawButtonBox(ds, rect, kButton, panelButton->state > 0); @@ -2523,11 +2533,14 @@ void Interface::converseDisplayTextLines(Surface *ds) { } void Interface::converseChangePos(int chg) { - if ((chg < 0 && _converseStartPos + chg >= 0) || - (chg > 0 && _converseStartPos < _converseEndPos)) { - _converseStartPos += chg; - draw(); + // Arrows will scroll the converse panel or down up to 4 conversation options + for (int i = 1; i <= 4; i++) { + if ((chg < 0 && _converseStartPos + chg >= 0) || + (chg > 0 && _converseStartPos < _converseEndPos)) { + _converseStartPos += chg; + } } + draw(); } void Interface::converseSetPos(int key) { @@ -2580,7 +2593,6 @@ void Interface::handleConverseUpdate(const Point& mousePoint) { if (_conversePanel.currentButton->type == kPanelButtonArrow) { if (_conversePanel.currentButton->state == 1) { - //TODO: insert timeout catchup converseChangePos(_conversePanel.currentButton->id); } draw(); diff --git a/engines/saga/interface.h b/engines/saga/interface.h index 7ae225219a..a8e2d5a647 100644 --- a/engines/saga/interface.h +++ b/engines/saga/interface.h @@ -28,6 +28,7 @@ #ifndef SAGA_INTERFACE_H #define SAGA_INTERFACE_H +#include "common/keyboard.h" #include "common/savefile.h" #include "saga/displayinfo.h" @@ -225,7 +226,7 @@ public: void drawStatusBar(); void setVerbState(int verb, int state); - bool processAscii(uint16 ascii); + bool processAscii(Common::KeyState keystate); void keyBoss(); void keyBossExit(); @@ -341,8 +342,8 @@ private: void drawVerbPanelText(Surface *ds, PanelButton *panelButton, KnownColor textKnownColor, KnownColor textShadowKnownColor); void drawVerbPanel(Surface *backBuffer, PanelButton* panelButton); void calcOptionSaveSlider(); - bool processTextInput(uint16 ascii); - void processStatusTextInput(uint16 ascii); + bool processTextInput(Common::KeyState keystate); + void processStatusTextInput(Common::KeyState keystate); public: void converseInit(void); @@ -379,6 +380,7 @@ private: public: SpriteList _defPortraits; + PalEntry _portraitBgColor; private: SagaEngine *_vm; @@ -416,7 +418,6 @@ private: int _statusOnceColor; int _leftPortrait; int _rightPortrait; - PalEntry _portraitBgColor; Point _lastMousePoint; diff --git a/engines/saga/ite_introproc.cpp b/engines/saga/ite_introproc.cpp index 1664969644..1b74d41cb7 100644 --- a/engines/saga/ite_introproc.cpp +++ b/engines/saga/ite_introproc.cpp @@ -115,7 +115,6 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo event.op = kEventDisplay; event.data = entry; event.time = (i == 0) ? 0 : VOICE_PAD; - q_event = _vm->_events->chain(q_event, &event); // Play voice @@ -124,7 +123,6 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo event.op = kEventPlay; event.param = dialogue[i].i_voice_rn; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); voice_len = _vm->_sndRes->getVoiceLength(dialogue[i].i_voice_rn); @@ -138,7 +136,6 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo event.op = kEventRemove; event.data = entry; event.time = voice_len; - q_event = _vm->_events->chain(q_event, &event); } @@ -265,7 +262,6 @@ Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const event.op = kEventDisplay; event.data = entry; event.time = delta_time; - q_event = _vm->_events->queue(&event); // Remove text @@ -274,7 +270,6 @@ Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const event.op = kEventRemove; event.data = entry; event.time = duration; - q_event = _vm->_events->chain(q_event, &event); y += (_vm->_font->getHeight(font) + line_spacing); @@ -301,7 +296,6 @@ int Scene::ITEIntroAnimProc(int param) { event.op = kEventDisplay; event.param = kEvPSetPalette; event.time = 0; - q_event = _vm->_events->queue(&event); debug(3, "Intro animation procedure started."); @@ -340,7 +334,6 @@ int Scene::ITEIntroAnimProc(int param) { event.op = kEventPlay; event.param = 0; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); // Queue intro music playback @@ -350,7 +343,6 @@ int Scene::ITEIntroAnimProc(int param) { event.param2 = MUSIC_LOOP; event.op = kEventPlay; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); } break; @@ -427,7 +419,6 @@ int Scene::ITEIntroCave1Proc(int param) { event.code = kPalAnimEvent; event.op = kEventCycleStart; event.time = 0; - q_event = _vm->_events->queue(&event); // Queue narrator dialogue list @@ -438,8 +429,8 @@ int Scene::ITEIntroCave1Proc(int param) { event.code = kSceneEvent; event.op = kEventEnd; event.time = VOICE_PAD; - q_event = _vm->_events->chain(q_event, &event); + break; case SCENE_END: break; @@ -504,7 +495,6 @@ int Scene::ITEIntroCave2Proc(int param) { event.op = kEventDissolve; event.time = 0; event.duration = DISSOLVE_DURATION; - q_event = _vm->_events->queue(&event); // Begin palette cycling animation for candles @@ -512,7 +502,6 @@ int Scene::ITEIntroCave2Proc(int param) { event.code = kPalAnimEvent; event.op = kEventCycleStart; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); // Queue narrator dialogue list @@ -523,8 +512,8 @@ int Scene::ITEIntroCave2Proc(int param) { event.code = kSceneEvent; event.op = kEventEnd; event.time = VOICE_PAD; - q_event = _vm->_events->chain(q_event, &event); + break; case SCENE_END: break; @@ -588,7 +577,6 @@ int Scene::ITEIntroCave3Proc(int param) { event.op = kEventDissolve; event.time = 0; event.duration = DISSOLVE_DURATION; - q_event = _vm->_events->queue(&event); // Begin palette cycling animation for candles @@ -596,7 +584,6 @@ int Scene::ITEIntroCave3Proc(int param) { event.code = kPalAnimEvent; event.op = kEventCycleStart; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); // Queue narrator dialogue list @@ -607,8 +594,8 @@ int Scene::ITEIntroCave3Proc(int param) { event.code = kSceneEvent; event.op = kEventEnd; event.time = VOICE_PAD; - q_event = _vm->_events->chain(q_event, &event); + break; case SCENE_END: break; @@ -681,7 +668,6 @@ int Scene::ITEIntroCave4Proc(int param) { event.op = kEventDissolve; event.time = 0; event.duration = DISSOLVE_DURATION; - q_event = _vm->_events->queue(&event); // Begin palette cycling animation for candles @@ -689,7 +675,6 @@ int Scene::ITEIntroCave4Proc(int param) { event.code = kPalAnimEvent; event.op = kEventCycleStart; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); // Queue narrator dialogue list @@ -700,8 +685,8 @@ int Scene::ITEIntroCave4Proc(int param) { event.code = kSceneEvent; event.op = kEventEnd; event.time = VOICE_PAD; - q_event = _vm->_events->chain(q_event, &event); + break; case SCENE_END: break; @@ -747,7 +732,6 @@ int Scene::ITEIntroValleyProc(int param) { event.op = kEventPlay; event.param = 0; event.time = 0; - q_event = _vm->_events->queue(&event); // Begin ITE title theme music @@ -759,7 +743,6 @@ int Scene::ITEIntroValleyProc(int param) { event.param2 = MUSIC_NORMAL; event.op = kEventPlay; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); // Pause animation before logo @@ -768,7 +751,6 @@ int Scene::ITEIntroValleyProc(int param) { event.op = kEventStop; event.param = 0; event.time = 3000; - q_event = _vm->_events->chain(q_event, &event); // Display logo @@ -777,7 +759,6 @@ int Scene::ITEIntroValleyProc(int param) { event.op = kEventDissolveBGMask; event.time = 0; event.duration = LOGO_DISSOLVE_DURATION; - q_event = _vm->_events->chain(q_event, &event); // Remove logo @@ -786,7 +767,6 @@ int Scene::ITEIntroValleyProc(int param) { event.op = kEventDissolve; event.time = 3000; event.duration = LOGO_DISSOLVE_DURATION; - q_event = _vm->_events->chain(q_event, &event); // Unpause animation before logo @@ -795,7 +775,6 @@ int Scene::ITEIntroValleyProc(int param) { event.op = kEventPlay; event.time = 0; event.param = 0; - q_event = _vm->_events->chain(q_event, &event); // Queue game credits list @@ -806,8 +785,8 @@ int Scene::ITEIntroValleyProc(int param) { event.code = kSceneEvent; event.op = kEventEnd; event.time = 1000; - q_event = _vm->_events->chain(q_event, &event); + break; case SCENE_END: break; @@ -870,7 +849,6 @@ int Scene::ITEIntroTreeHouseProc(int param) { event.op = kEventDissolve; event.time = 0; event.duration = DISSOLVE_DURATION; - q_event = _vm->_events->queue(&event); if (_vm->_anim->hasAnimation(0)) { @@ -882,7 +860,6 @@ int Scene::ITEIntroTreeHouseProc(int param) { event.op = kEventPlay; event.param = 0; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); } @@ -895,8 +872,8 @@ int Scene::ITEIntroTreeHouseProc(int param) { event.code = kSceneEvent; event.op = kEventEnd; event.time = 1000; - q_event = _vm->_events->chain(q_event, &event); + break; case SCENE_END: break; @@ -950,7 +927,6 @@ int Scene::ITEIntroFairePathProc(int param) { event.op = kEventDissolve; event.time = 0; event.duration = DISSOLVE_DURATION; - q_event = _vm->_events->queue(&event); // Begin title screen background animation @@ -961,7 +937,6 @@ int Scene::ITEIntroFairePathProc(int param) { event.op = kEventPlay; event.param = 0; event.time = 0; - q_event = _vm->_events->chain(q_event, &event); // Queue game credits list @@ -973,8 +948,8 @@ int Scene::ITEIntroFairePathProc(int param) { event.code = kSceneEvent; event.op = kEventEnd; event.time = 1000; - q_event = _vm->_events->chain(q_event, &event); + break; case SCENE_END: break; @@ -1005,7 +980,6 @@ int Scene::ITEIntroFaireTentProc(int param) { event.op = kEventDissolve; event.time = 0; event.duration = DISSOLVE_DURATION; - q_event_start = _vm->_events->queue(&event); // End scene after momentary pause @@ -1014,6 +988,7 @@ int Scene::ITEIntroFaireTentProc(int param) { event.op = kEventEnd; event.time = 5000; q_event = _vm->_events->chain(q_event_start, &event); + break; case SCENE_END: break; diff --git a/engines/saga/itedata.h b/engines/saga/itedata.h index d9bd59adc4..3c594b5fec 100644 --- a/engines/saga/itedata.h +++ b/engines/saga/itedata.h @@ -41,9 +41,6 @@ enum ActorFlags { kNoScale = 0x80 // (1<<7) Actor is not scaled }; -// TODO: This doesn't quite correspond to the original Actor struct, so I'm not -// sure if I got it right. - struct ActorTableData { byte flags; byte nameIndex; diff --git a/engines/saga/module.mk b/engines/saga/module.mk index 6c1812ad23..68cf91be32 100644 --- a/engines/saga/module.mk +++ b/engines/saga/module.mk @@ -2,6 +2,7 @@ MODULE := engines/saga MODULE_OBJS := \ actor.o \ + actor_walk.o \ animation.o \ console.o \ detection.o \ diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp index 04e79c174d..6b233e0256 100644 --- a/engines/saga/music.cpp +++ b/engines/saga/music.cpp @@ -92,7 +92,7 @@ DigitalMusicInputStream::DigitalMusicInputStream(SagaEngine *vm, ResourceContext _compressedStream = NULL; - if (Common::File::exists("music.cmp")) { + if (Common::File::exists("music.cmp") || Common::File::exists("musicd.cmp")) { // Read compressed header to determine compression type _file->seek((long)resourceData->offset, SEEK_SET); _file->read(compressedHeader, 9); @@ -320,6 +320,8 @@ void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { void MusicPlayer::onTimer(void *refCon) { MusicPlayer *music = (MusicPlayer *)refCon; + Common::StackLock lock(music->_mutex); + if (music->_isPlaying) music->_parser->onTimer(); } @@ -329,6 +331,8 @@ void MusicPlayer::playMusic() { } void MusicPlayer::stopMusic() { + Common::StackLock lock(_mutex); + _isPlaying = false; if (_parser) { _parser->unloadMusic(); @@ -352,6 +356,7 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver, int enable } Music::~Music() { + _vm->_timer->removeTimerProc(&musicVolumeGaugeCallback); _mixer->stopHandle(_musicHandle); delete _player; xmidiParser->setMidiDriver(NULL); @@ -561,7 +566,7 @@ void Music::play(uint32 resourceId, MusicFlags flags) { parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); _player->_parser = parser; - _player->setVolume(_vm->_musicVolume == 10 ? 255 : _vm->_musicVolume * 25); + setVolume(_vm->_musicVolume == 10 ? 255 : _vm->_musicVolume * 25); if (flags & MUSIC_LOOP) _player->setLoop(true); @@ -574,15 +579,17 @@ void Music::play(uint32 resourceId, MusicFlags flags) { } void Music::pause(void) { - //TODO: do it + _player->setVolume(-1); + _player->setPlaying(false); } void Music::resume(void) { - //TODO: do it} + _player->setVolume(_vm->_musicVolume == 10 ? 255 : _vm->_musicVolume * 25); + _player->setPlaying(true); } void Music::stop(void) { - //TODO: do it + _player->stopMusic(); } } // End of namespace Saga diff --git a/engines/saga/music.h b/engines/saga/music.h index b038a25a11..1fbdda53f2 100644 --- a/engines/saga/music.h +++ b/engines/saga/music.h @@ -34,6 +34,7 @@ #include "sound/mp3.h" #include "sound/vorbis.h" #include "sound/flac.h" +#include "common/mutex.h" namespace Saga { @@ -49,6 +50,7 @@ public: ~MusicPlayer(); bool isPlaying() { return _isPlaying; } + void setPlaying(bool playing) { _isPlaying = playing; } void setVolume(int volume); int getVolume() { return _masterVolume; } @@ -77,6 +79,7 @@ public: MidiChannel *getPercussionChannel() { return 0; } MidiParser *_parser; + Common::Mutex _mutex; protected: diff --git a/engines/saga/palanim.cpp b/engines/saga/palanim.cpp index 19c2fc4d40..f318b815cc 100644 --- a/engines/saga/palanim.cpp +++ b/engines/saga/palanim.cpp @@ -131,7 +131,6 @@ int PalAnim::cycleStart() { event.code = kPalAnimEvent; event.op = kEventCycleStep; event.time = PALANIM_CYCLETIME; - _vm->_events->queue(&event); return SUCCESS; @@ -178,7 +177,6 @@ int PalAnim::cycleStep(int vectortime) { event.code = kPalAnimEvent; event.op = kEventCycleStep; event.time = vectortime + PALANIM_CYCLETIME; - _vm->_events->queue(&event); return SUCCESS; diff --git a/engines/saga/render.cpp b/engines/saga/render.cpp index 930476b4e2..e006b4a627 100644 --- a/engines/saga/render.cpp +++ b/engines/saga/render.cpp @@ -85,11 +85,12 @@ void Render::drawScene() { // Get mouse coordinates mousePoint = _vm->mousePos(); - if (!(_flags & (RF_DEMO_SUBST | RF_PLACARD | RF_MAP))) { - // Display scene background - _vm->_scene->draw(); - + if (!(_flags & (RF_DEMO_SUBST | RF_MAP) || _vm->_interface->getMode() == kPanelPlacard)) { if (_vm->_interface->getFadeMode() != kFadeOut) { + // Display scene background + if (!(_flags & RF_DISABLE_ACTORS)) + _vm->_scene->draw(); + if (_vm->_puzzle->isActive()) { _vm->_puzzle->movePiece(mousePoint); _vm->_actor->drawSpeech(); diff --git a/engines/saga/render.h b/engines/saga/render.h index 4c6e3ee5b1..95abc2e4df 100644 --- a/engines/saga/render.h +++ b/engines/saga/render.h @@ -39,11 +39,10 @@ enum RENDER_FLAGS { RF_OBJECTMAP_TEST = (1 << 3), RF_RENDERPAUSE = (1 << 4), RF_GAMEPAUSE = (1 << 5), - RF_PLACARD = (1 << 6), - RF_ACTOR_PATH_TEST = (1 << 7), - RF_MAP = (1 << 8), - RF_DISABLE_ACTORS = (1 << 9), - RF_DEMO_SUBST = (1 << 10) + RF_ACTOR_PATH_TEST = (1 << 6), + RF_MAP = (1 << 7), + RF_DISABLE_ACTORS = (1 << 8), + RF_DEMO_SUBST = (1 << 9) }; class Render { diff --git a/engines/saga/rscfile.cpp b/engines/saga/rscfile.cpp index 4d3ff1e47c..ba56e838b7 100644 --- a/engines/saga/rscfile.cpp +++ b/engines/saga/rscfile.cpp @@ -330,7 +330,11 @@ bool Resource::loadContext(ResourceContext *context) { if (resourceData->patchData->_patchFile->open(patchDescription->fileName)) { resourceData->offset = 0; resourceData->size = resourceData->patchData->_patchFile->size(); - resourceData->patchData->_patchFile->close(); + // ITE uses several patch files which are loaded and then not needed + // anymore (as they're in memory), so close them here. IHNM uses only + // 1 patch file, which is reused, so don't close it + if (_vm->getGameType() == GType_ITE) + resourceData->patchData->_patchFile->close(); } else { delete resourceData->patchData; resourceData->patchData = NULL; @@ -606,7 +610,11 @@ void Resource::loadResource(ResourceContext *context, uint32 resourceId, byte*&r if (file->read(resourceBuffer, resourceSize) != resourceSize) { error("Resource::loadResource() failed to read"); } - if (resourceData->patchData != NULL) + + // ITE uses several patch files which are loaded and then not needed + // anymore (as they're in memory), so close them here. IHNM uses only + // 1 patch file, which is reused, so don't close it + if (resourceData->patchData != NULL && _vm->getGameType() == GType_ITE) file->close(); } @@ -742,6 +750,12 @@ void Resource::loadGlobalResources(int chapter, int actorsEntrance) { if (_metaResource.songTableID > 0) { _vm->_resource->loadResource(resourceContext, _metaResource.songTableID, resourcePointer, resourceLength); + if (chapter == 6) { + int32 id = READ_LE_UINT32(&resourcePointer[actorsEntrance * 4]); + free(resourcePointer); + _vm->_resource->loadResource(resourceContext, id, resourcePointer, resourceLength); + } + if (resourceLength == 0) { error("Resource::loadGlobalResources Can't load songs list for current track"); } @@ -758,6 +772,7 @@ void Resource::loadGlobalResources(int chapter, int actorsEntrance) { free(resourcePointer); } else { // The IHNM demo has a fixed music track and doesn't load a song table + _vm->_music->setVolume(_vm->_musicVolume == 10 ? -1 : _vm->_musicVolume * 25, 1); _vm->_music->play(3, MUSIC_LOOP); } diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index 76731c201a..01a75f4af1 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -259,10 +259,10 @@ int SagaEngine::go() { _scene->changeScene(getStartSceneNumber(), 0, kTransitionNoFade); _events->handleEvents(0); // Process immediate events + _interface->setMode(kPanelMain); char *fileName; fileName = calcSaveFileName(ConfMan.getInt("save_slot")); load(fileName); - _interface->setMode(kPanelMain); } else { _framesEsc = 0; _scene->startScene(); @@ -428,6 +428,9 @@ ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) { case (kKnownColorBrightWhite): colorId = kITEColorBrightWhite; break; + case (kKnownColorWhite): + colorId = kITEColorWhite; + break; case (kKnownColorBlack): colorId = kITEColorBlack; break; @@ -449,8 +452,7 @@ ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) { error("SagaEngine::KnownColor2ColorId unknown color %i", knownColor); } } else if (getGameType() == GType_IHNM) { - switch (knownColor) - { + switch (knownColor) { case(kKnownColorTransparent): colorId = kITEColorTransBlack; break; @@ -458,6 +460,9 @@ ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) { case (kKnownColorBrightWhite): colorId = kITEColorBrightWhite; break; + case (kKnownColorWhite): + colorId = kITEColorBrightWhite; + break; case (kKnownColorBlack): colorId = kIHNMColorBlack; break; diff --git a/engines/saga/saga.h b/engines/saga/saga.h index 43952fe564..8016fb9e65 100644 --- a/engines/saga/saga.h +++ b/engines/saga/saga.h @@ -69,7 +69,12 @@ struct StringList; #define MAXPATH 512 //TODO: remove +// Note that IHNM has a smaller save title size than ITE +// We allocate the ITE save title size in savegames, to +// preserve savegame backwards compatibility. We only check +// for IHNM's save title during text input #define SAVE_TITLE_SIZE 28 +#define IHNM_SAVE_TITLE_SIZE 22 #define MAX_SAVES 96 #define MAX_FILE_NAME 256 @@ -153,7 +158,8 @@ enum GameFeatures { GF_WYRMKEEP = 1 << 1, GF_CD_FX = 1 << 2, GF_SCENE_SUBSTITUTES = 1 << 3, - GF_COMPRESSED_SOUNDS = 1 << 4 + GF_COMPRESSED_SOUNDS = 1 << 4, + GF_NON_INTERACTIVE = 1 << 5 }; enum VerbTypeIds { @@ -297,6 +303,7 @@ struct GameResourceDescription { uint32 conversePanelResourceId; uint32 optionPanelResourceId; uint32 warningPanelResourceId; + uint32 warningPanelSpritesResourceId; uint32 mainSpritesResourceId; uint32 mainPanelSpritesResourceId; uint32 optionPanelSpritesResourceId; @@ -441,6 +448,7 @@ enum ColorId { enum KnownColor { kKnownColorTransparent, kKnownColorBrightWhite, + kKnownColorWhite, kKnownColorBlack, kKnownColorSubtitleTextColor, diff --git a/engines/saga/sagaresnames.h b/engines/saga/sagaresnames.h index 761ac1ab68..b416d85fd8 100644 --- a/engines/saga/sagaresnames.h +++ b/engines/saga/sagaresnames.h @@ -108,6 +108,7 @@ namespace Saga { #define RID_IHNM_OPTION_PANEL 15 #define RID_IHNM_OPTION_PANEL_SPRITES 16 #define RID_IHNM_WARNING_PANEL 17 +#define RID_IHNM_WARNING_PANEL_SPRITES 18 #define RID_IHNM_BOSS_SCREEN 19 #define RID_IHNM_PROFILE_BG 20 #define RID_IHNM_MAIN_STRINGS 21 @@ -122,7 +123,8 @@ namespace Saga { #define RID_IHNMDEMO_OPTION_PANEL 10 #define RID_IHNMDEMO_OPTION_PANEL_SPRITES 11 #define RID_IHNMDEMO_WARNING_PANEL 12 -#define RID_IHNMDEMO_BOSS_SCREEN 13 // Does not exist in the demo +#define RID_IHNMDEMO_WARNING_PANEL_SPRITES 13 +#define RID_IHNMDEMO_BOSS_SCREEN 14 // Does not exist in the demo #define RID_IHNMDEMO_PROFILE_BG 15 #define RID_IHNMDEMO_MAIN_STRINGS 16 diff --git a/engines/saga/saveload.cpp b/engines/saga/saveload.cpp index 77d7816963..6e731de44e 100644 --- a/engines/saga/saveload.cpp +++ b/engines/saga/saveload.cpp @@ -187,6 +187,9 @@ void SagaEngine::save(const char *fileName, const char *saveName) { _saveHeader.type = MKID_BE('SAGA'); _saveHeader.size = 0; _saveHeader.version = CURRENT_SAGA_VER; + // Note that IHNM has a smaller save title size than ITE + // We allocate the ITE save title size here, to preserve + // savegame backwards compatibility strncpy(_saveHeader.name, saveName, SAVE_TITLE_SIZE); out->writeUint32BE(_saveHeader.type); @@ -255,8 +258,8 @@ void SagaEngine::load(const char *fileName) { // Some older saves were not written in an endian safe fashion. // We try to detect this here by checking for extremly high version values. // If found, we retry with the data swapped. - // FIXME: Maybe display a warning/error message instead? if (_saveHeader.version > 0xFFFFFF) { + warning("This savegame is not endian safe, retrying with the data swapped"); _saveHeader.version = SWAP_BYTES_32(_saveHeader.version); } @@ -276,7 +279,6 @@ void SagaEngine::load(const char *fileName) { // Surrounding scene sceneNumber = in->readSint32LE(); - // Protagonist if (getGameType() != GType_ITE) { int currentChapter = _scene->currentChapterNumber(); _scene->setChapterNumber(in->readSint32LE()); @@ -286,10 +288,13 @@ void SagaEngine::load(const char *fileName) { _scene->setCurrentMusicTrack(in->readSint32LE()); _scene->setCurrentMusicRepeat(in->readSint32LE()); _music->stop(); - if (getGameId() != GID_IHNM_DEMO) + if (_scene->currentChapterNumber() == 8) + _interface->setMode(kPanelChapterSelection); + if (getGameId() != GID_IHNM_DEMO) { _music->play(_music->_songTable[_scene->getCurrentMusicTrack()], _scene->getCurrentMusicRepeat() ? MUSIC_LOOP : MUSIC_NORMAL); - else + } else { _music->play(3, MUSIC_LOOP); + } } // Inset scene @@ -323,11 +328,14 @@ void SagaEngine::load(const char *fileName) { if (getGameType() != GType_ITE) { if (_scene->currentProtag() != 0 && _scene->currentChapterNumber() != 6) { ActorData *actor1 = _actor->getFirstActor(); - // Original stores the current protagonist ID from sfSwapActors: - //ActorData *actor2 = _actor->getActor(_scene->currentProtag()); - // However, we already store the protagonist, so merely getting the saved - // protagonist is easier and safer, and works without glitches - ActorData *actor2 = _actor->_protagonist; + ActorData *actor2; + // The original gets actor2 from the current protagonist ID, but this is sometimes wrong + // If the current protagonist ID is not correct, use the stored protagonist + if (!_actor->validActorId(_scene->currentProtag())) { + actor2 = _actor->_protagonist; + } else { + actor2 = _actor->getActor(_scene->currentProtag()); + } SWAP(actor1->_location, actor2->_location); @@ -345,6 +353,7 @@ void SagaEngine::load(const char *fileName) { if (insetSceneNumber != sceneNumber) { _render->setFlag(RF_DISABLE_ACTORS); + _scene->draw(); _render->drawScene(); _render->clearFlag(RF_DISABLE_ACTORS); _scene->changeScene(insetSceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade); diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp index c49fe546ee..a80b482e47 100644 --- a/engines/saga/scene.cpp +++ b/engines/saga/scene.cpp @@ -205,6 +205,7 @@ Scene::Scene(SagaEngine *vm) : _vm(vm) { _sceneLoaded = false; _sceneNumber = 0; + _chapterNumber = 0; _sceneResourceId = 0; _inGame = false; _loadDescription = false; @@ -281,6 +282,9 @@ void Scene::startScene() { break; } + // Stop the intro music + _vm->_music->stop(); + // Load the head in scene queue queueIterator = _sceneQueue.begin(); if (queueIterator == _sceneQueue.end()) { @@ -292,6 +296,31 @@ void Scene::startScene() { loadScene(sceneQueue); } +void Scene::creditsScene() { + // End the last game ending scene + _vm->_scene->endScene(); + // We're not in the game anymore + _inGame = false; + + // Hide cursor during credits + _vm->_gfx->showCursor(false); + + switch (_vm->getGameType()) { + case GType_ITE: + // Not called by ITE + break; + case GType_IHNM: + IHNMCreditsProc(); + break; + default: + error("Scene::creditsScene(): Error: Can't start credits scene... gametype not supported"); + break; + } + + _vm->shutDown(); + return; +} + void Scene::nextScene() { SceneQueueList::iterator queueIterator; LoadSceneParams *sceneQueue; @@ -561,6 +590,9 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { Event *q_event; static PalEntry current_pal[PAL_ENTRIES]; + if (loadSceneParams->transitionType == kTransitionFade) + _vm->_interface->setFadeMode(kFadeOut); + // Change the cursor to an hourglass in IHNM event.type = kEvTOneshot; event.code = kCursorEvent; @@ -575,19 +607,11 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { error("loadScene wrong usage"); } - if (loadSceneParams->chapter == 6) + if (loadSceneParams->chapter == 6 || loadSceneParams->chapter == 8) _vm->_interface->setLeftPortrait(0); _vm->_anim->freeCutawayList(); - // FIXME: Freed script modules are not reloaded correctly when changing chapters. - // This is apparent when returning back to the character selection screen, - // where the scene script module is loaded incorrectly - // Don't free them for now, but free them on game exit, like ITE. - // This has no impact on the game itself (other than increased memory usage), - // as each chapter uses a different module slot - // TODO: Find out why the script modules are not loaded correctly when - // changing chapters and uncomment this again - //_vm->_script->freeModules(); + _vm->_script->freeModules(); // deleteAllScenes(); @@ -612,6 +636,7 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { _vm->_script->setVerb(_vm->_script->getVerbType(kVerbWalkTo)); if (loadSceneParams->sceneDescriptor == -2) { + _vm->_interface->setFadeMode(kNoFade); return; } } @@ -651,11 +676,11 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { debug(3, "Loading scene number %d:", _sceneNumber); - if (_vm->getGameId() == GID_IHNM_DEMO && _sceneNumber == 144) { + if (isNonInteractiveIHNMDemoPart()) { // WORKAROUND for the non-interactive part of the IHNM demo: When restarting the - // non-interactive demo, opcode sfMainMode is incorrectly called. Therefore, if the - // starting scene of the non-interactive demo is loaded (scene 144), set panel to null - // and lock the user interface + // non-interactive demo, opcode sfMainMode is incorrectly called. Therefore, if any + // of the scenes of the non-interactive demo are loaded (scenes 144-149), set panel + // to null and lock the user interface _vm->_interface->deactivate(); _vm->_interface->setMode(kPanelNull); } @@ -709,15 +734,6 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { q_event = NULL; - //fix placard bug - //i guess we should remove RF_PLACARD flag - and use _interface->getMode() - event.type = kEvTOneshot; - event.code = kGraphicsEvent; - event.op = kEventClearFlag; - event.param = RF_PLACARD; - - q_event = _vm->_events->chain(q_event, &event); - if (loadSceneParams->transitionType == kTransitionFade) { _vm->_interface->setFadeMode(kFadeOut); @@ -764,7 +780,6 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { event.param4 = _sceneNumber; // Object event.param5 = loadSceneParams->actorsEntrance; // With Object event.param6 = 0; // Actor - q_event = _vm->_events->chain(q_event, &event); } @@ -786,7 +801,6 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { event.time = 0; event.duration = kNormalFadeDuration; event.data = _bg.pal; - q_event = _vm->_events->chain(q_event, &event); // set fade mode @@ -807,11 +821,6 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { _vm->_sound->stopAll(); - // FIXME: Does IHNM use scene background music, or is all the - // music scripted? At the very least, it shouldn't try - // to start song 0 at the beginning of the game, since - // it's the end credits music. - if (_vm->getGameType() == GType_ITE) { if (_sceneDescription.musicResourceId >= 0) { event.type = kEvTOneshot; @@ -820,14 +829,12 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { event.param2 = MUSIC_DEFAULT; event.op = kEventPlay; event.time = 0; - _vm->_events->queue(&event); } else { event.type = kEvTOneshot; event.code = kMusicEvent; event.op = kEventStop; event.time = 0; - _vm->_events->queue(&event); } } @@ -838,7 +845,6 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { event.op = kEventDisplay; event.param = kEvPSetPalette; event.time = 0; - _vm->_events->queue(&event); // Begin palette cycle animation if present @@ -846,7 +852,6 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { event.code = kPalAnimEvent; event.op = kEventCycleStart; event.time = 0; - q_event = _vm->_events->queue(&event); // Start the scene main script @@ -861,7 +866,6 @@ void Scene::loadScene(LoadSceneParams *loadSceneParams) { event.param4 = _sceneNumber; // Object event.param5 = loadSceneParams->actorsEntrance; // With Object event.param6 = 0; // Actor - _vm->_events->queue(&event); } @@ -1178,7 +1182,9 @@ void Scene::endScene() { _sceneProc(SCENE_END, this); } - // + // Stop showing actors till the next scene's background is drawn from loadScene + _vm->_render->setFlag(RF_DISABLE_ACTORS); + _vm->_script->abortAllThreads(); _vm->_script->_skipSpeeches = false; @@ -1308,31 +1314,38 @@ void Scene::clearPlacard() { Event event; Event *q_event; - _vm->_interface->restoreMode(); + _vm->_interface->setFadeMode(kFadeOut); + // Fade to black out _vm->_gfx->getCurrentPal(cur_pal); - event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalToBlack; event.time = 0; event.duration = kNormalFadeDuration; event.data = cur_pal; - q_event = _vm->_events->queue(&event); - event.type = kEvTOneshot; - event.code = kGraphicsEvent; - event.op = kEventClearFlag; - event.param = RF_PLACARD; - + // set fade mode + event.type = kEvTImmediate; + event.code = kInterfaceEvent; + event.op = kEventSetFadeMode; + event.param = kNoFade; + event.time = 0; + event.duration = 0; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kTextEvent; event.op = kEventRemove; event.data = _vm->_script->getPlacardTextEntry(); + q_event = _vm->_events->chain(q_event, &event); + event.type = kEvTImmediate; + event.code = kInterfaceEvent; + event.op = kEventRestoreMode; + event.time = 0; + event.duration = 0; q_event = _vm->_events->chain(q_event, &event); _vm->_scene->getBGPal(pal); @@ -1343,20 +1356,17 @@ void Scene::clearPlacard() { event.time = 0; event.duration = kNormalFadeDuration; event.data = pal; - q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kCursorEvent; event.op = kEventShow; - q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventThreadWake; event.param = kWaitTypePlacard; - q_event = _vm->_events->chain(q_event, &event); } @@ -1378,37 +1388,37 @@ void Scene::showPsychicProfile(const char *text) { event.type = kEvTOneshot; event.code = kCursorEvent; event.op = kEventHide; - q_event = _vm->_events->queue(&event); - _vm->_gfx->getCurrentPal(cur_pal); + _vm->_interface->setFadeMode(kFadeOut); + // Fade to black out + _vm->_gfx->getCurrentPal(cur_pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalToBlack; event.time = 0; event.duration = kNormalFadeDuration; event.data = cur_pal; - q_event = _vm->_events->chain(q_event, &event); - event.type = kEvTOneshot; + // set fade mode + event.type = kEvTImmediate; event.code = kInterfaceEvent; - event.op = kEventClearStatus; - + event.op = kEventSetFadeMode; + event.param = kNoFade; + event.time = 0; + event.duration = 0; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; - event.code = kGraphicsEvent; - event.op = kEventSetFlag; - event.param = RF_PLACARD; - + event.code = kInterfaceEvent; + event.op = kEventClearStatus; q_event = _vm->_events->chain(q_event, &event); // Set the background and palette for the psychic profile event.type = kEvTOneshot; event.code = kPsychicProfileBgEvent; - q_event = _vm->_events->chain(q_event, &event); _vm->_scene->_textList.clear(); @@ -1432,7 +1442,6 @@ void Scene::showPsychicProfile(const char *text) { event.code = kTextEvent; event.op = kEventDisplay; event.data = _psychicProfileTextEntry; - q_event = _vm->_events->chain(q_event, &event); } @@ -1444,22 +1453,21 @@ void Scene::showPsychicProfile(const char *text) { event.time = 0; event.duration = kNormalFadeDuration; event.data = pal; - q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventThreadWake; event.param = kWaitTypePlacard; - q_event = _vm->_events->chain(q_event, &event); } void Scene::clearPsychicProfile() { if (_vm->_interface->getMode() == kPanelPlacard || _vm->getGameId() == GID_IHNM_DEMO) { + _vm->_interface->restoreMode(); _vm->_scene->clearPlacard(); _vm->_scene->_textList.clear(); - _vm->_actor->showActors(false); + _vm->_render->setFlag(RF_DISABLE_ACTORS); _vm->_gfx->restorePalette(); _vm->_scene->restoreScene(); _vm->_interface->activate(); diff --git a/engines/saga/scene.h b/engines/saga/scene.h index da97bddff5..aca3f910e0 100644 --- a/engines/saga/scene.h +++ b/engines/saga/scene.h @@ -221,6 +221,7 @@ class Scene { void cmdSceneChange(int argc, const char **argv); void startScene(); + void creditsScene(); void nextScene(); void skipScene(); void endScene(); @@ -345,6 +346,10 @@ class Scene { void clearPsychicProfile(); void showIHNMDemoSpecialScreen(); + bool isNonInteractiveIHNMDemoPart() { + return _vm->getGameId() == GID_IHNM_DEMO && (_sceneNumber >= 144 && _sceneNumber <= 149); + } + private: void loadScene(LoadSceneParams *loadSceneParams); void loadSceneDescriptor(uint32 resourceId); @@ -391,17 +396,26 @@ class Scene { private: int IHNMStartProc(); + int IHNMCreditsProc(); int ITEStartProc(); + void IHNMLoadCutaways(); + bool checkKey(); + + bool playTitle(int title, int time, int mode = kPanelVideo); + bool playLoopingTitle(int title, int seconds); + public: static int SC_IHNMIntroMovieProc1(int param, void *refCon); static int SC_IHNMIntroMovieProc2(int param, void *refCon); static int SC_IHNMIntroMovieProc3(int param, void *refCon); + static int SC_IHNMCreditsMovieProc(int param, void *refCon); private: int IHNMIntroMovieProc1(int param); int IHNMIntroMovieProc2(int param); int IHNMIntroMovieProc3(int param); + int IHNMCreditsMovieProc(int param); public: static int SC_ITEIntroAnimProc(int param, void *refCon); diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp index 82fcb352c0..611975f471 100644 --- a/engines/saga/script.cpp +++ b/engines/saga/script.cpp @@ -204,6 +204,7 @@ void Script::freeModules() { for (i = 0; i < _modulesCount; i++) { if (_modules[i].loaded) { _modules[i].freeMem(); + _modules[i].loaded = false; } } _staticSize = 0; @@ -359,8 +360,6 @@ int Script::getVerbType(VerbTypes verbType) { } } else { - // TODO: This is ugly and needs rewriting, but - // it works for now switch (verbType) { case kVerbNone: return kVerbIHNMNone; @@ -507,7 +506,6 @@ void Script::doVerb() { event.param4 = _pendingObject[0]; // Object event.param5 = _pendingObject[1]; // With Object event.param6 = (objectType == kGameObjectActor) ? _pendingObject[0] : ID_PROTAG; // Actor - _vm->_events->queue(&event); } else { @@ -773,7 +771,13 @@ void Script::whichObject(const Point& mousePoint) { objectId = newObjectId; if (_vm->getGameType() == GType_ITE) objectFlags = kObjUseWith; + // Note: for IHNM, the default right button action is "Look at" for actors, + // but "Talk to" makes much more sense newRightButtonVerb = getVerbType(kVerbTalkTo); + // Slight hack because of the above change: the jukebox in Gorrister's chapter + // is an actor, so change the right button action to "Look at" + if (_vm->getGameType() == GType_IHNM && objectId == 8199) + newRightButtonVerb = getVerbType(kVerbLookAt); if ((_currentVerb == getVerbType(kVerbPickUp)) || (_currentVerb == getVerbType(kVerbOpen)) || @@ -827,6 +831,10 @@ void Script::whichObject(const Point& mousePoint) { if (newRightButtonVerb >= getVerbType(kVerbOptions)) { newRightButtonVerb = getVerbType(kVerbNone); } + } else { + if (newRightButtonVerb >= getVerbType(kVerbOptions)) { + newRightButtonVerb = getVerbType(kVerbWalkTo); + } } if ((_currentVerb == getVerbType(kVerbTalkTo)) || ((_currentVerb == getVerbType(kVerbGive)) && _firstObjectSet)) { diff --git a/engines/saga/script.h b/engines/saga/script.h index 5e5e702132..37e35cf241 100644 --- a/engines/saga/script.h +++ b/engines/saga/script.h @@ -237,7 +237,6 @@ struct ModuleData { voiceLUT.freeMem(); free(moduleBase); free(entryPoints); - memset(this, 0x0, sizeof(*this)); } }; @@ -597,7 +596,7 @@ private: void sfShowIHNMDemoHelpPage(SCRIPTFUNC_PARAMS); void sfGetPoints(SCRIPTFUNC_PARAMS); void sfSetGlobalFlag(SCRIPTFUNC_PARAMS); - void sf92(SCRIPTFUNC_PARAMS); + void sfDemoSetInteractive(SCRIPTFUNC_PARAMS); void sfClearGlobalFlag(SCRIPTFUNC_PARAMS); void sfTestGlobalFlag(SCRIPTFUNC_PARAMS); void sfSetPoints(SCRIPTFUNC_PARAMS); diff --git a/engines/saga/sfuncs.cpp b/engines/saga/sfuncs.cpp index c0e44584df..74f192c7f7 100644 --- a/engines/saga/sfuncs.cpp +++ b/engines/saga/sfuncs.cpp @@ -230,7 +230,7 @@ static const ScriptFunctionDescription IHNMscriptFunctionsList[IHNM_SCRIPT_FUNCT OPCODE(sfShowIHNMDemoHelpPage), OPCODE(sfVstopFX), OPCODE(sfVstopLoopedFX), - OPCODE(sf92), // only used in the demo version of IHNM + OPCODE(sfDemoSetInteractive), // only used in the demo version of IHNM OPCODE(sfDemoIsInteractive), OPCODE(sfVsetTrack), OPCODE(sfGetPoints), @@ -253,8 +253,7 @@ static const ScriptFunctionDescription IHNMscriptFunctionsList[IHNM_SCRIPT_FUNCT // Script function #0 (0x00) // Print a debugging message void Script::sfPutString(SCRIPTFUNC_PARAMS) { - const char *str; - str = thread->_strings->getString(thread->pop()); + const char *str = thread->_strings->getString(thread->pop()); _vm->_console->DebugPrintf("sfPutString: %s\n",str); debug(0, "sfPutString: %s", str); @@ -263,8 +262,7 @@ void Script::sfPutString(SCRIPTFUNC_PARAMS) { // Script function #1 (0x01) blocking // Param1: time in ticks void Script::sfWait(SCRIPTFUNC_PARAMS) { - int16 time; - time = thread->pop(); + int16 time = thread->pop(); if (!_skipSpeeches) { thread->waitDelay(_vm->ticksToMSec(time)); // put thread to sleep @@ -274,17 +272,17 @@ void Script::sfWait(SCRIPTFUNC_PARAMS) { // Script function #2 (0x02) void Script::sfTakeObject(SCRIPTFUNC_PARAMS) { uint16 objectId = thread->pop(); - ObjectData *obj; - obj = _vm->_actor->getObj(objectId); + ObjectData *obj = _vm->_actor->getObj(objectId); + if (obj->_sceneNumber != ITE_SCENE_INV) { obj->_sceneNumber = ITE_SCENE_INV; // WORKAROUND for two incorrect object sprites in the IHNM demo // (the mirror and the icon in Ted's part). Set them correctly here if (_vm->getGameId() == GID_IHNM_DEMO) { - if (obj->_spriteListResourceId == 4 && objectId == 16408) + if (objectId == 16408) obj->_spriteListResourceId = 24; - if (obj->_spriteListResourceId == 3 && objectId == 16409) + if (objectId == 16409) obj->_spriteListResourceId = 25; } @@ -297,23 +295,20 @@ void Script::sfTakeObject(SCRIPTFUNC_PARAMS) { void Script::sfIsCarried(SCRIPTFUNC_PARAMS) { uint16 objectId = thread->pop(); CommonObjectData *object; + if (_vm->_actor->validObjId(objectId)) { object = _vm->_actor->getObj(objectId); thread->_returnValue = (object->_sceneNumber == ITE_SCENE_INV) ? 1 : 0; } else { thread->_returnValue = 0; } - - } // Script function #4 (0x04) nonblocking // Set the command display to the specified text string // Param1: dialogue index of string void Script::sfStatusBar(SCRIPTFUNC_PARAMS) { - int16 stringIndex = thread->pop(); - - _vm->_interface->setStatusText(thread->_strings->getString(stringIndex)); + _vm->_interface->setStatusText(thread->_strings->getString(thread->pop())); } // Script function #5 (0x05) @@ -323,9 +318,29 @@ void Script::sfMainMode(SCRIPTFUNC_PARAMS) { showVerb(); _vm->_interface->activate(); _vm->_interface->setMode(kPanelMain); + // Sometimes, the active cutaway is cleared after this opcode is called, + // resulting in an incorrect mode being set. An example is Ellen's chapter + // in IHNM, when using the computer with the chaos trebler CD. Make sure + // that the saved mode is kPanelMain, so that it won't get overwritten + // by an incorrect stored mode + _vm->_interface->rememberMode(); if (_vm->getGameType() == GType_ITE) setPointerVerb(); + + // The early Windows and Mac demos of ITE were non-interactive. In those demos, + // the intro is shown and then when the first scene is shown, there's a dialog + // thanking the user for playing the demo and asking him to buy the full game, + // without allowing him to continue any further. The game data itself for these + // demos does not contain any scripts for the first scene (i.e. there's no text + // in the game data to look at Rif's silver medallion). Also, there are no more + // scenes apart from the Grand Tournament scene. This opcode is called in those + // demos, and I assume that its use there is to just show the popup window and + // exit the game. Therefore, once this opcode is called in the older ITE demos, + // exit the game. Known non-interactive demos are GID_ITE_MACDEMO1 and + // GID_ITE_WINDEMO1 + if (_vm->getFeatures() & GF_NON_INTERACTIVE) + _vm->shutDown(); } // Script function #6 (0x06) blocking @@ -333,15 +348,11 @@ void Script::sfMainMode(SCRIPTFUNC_PARAMS) { // Param2: actor x // Param3: actor y void Script::sfScriptWalkTo(SCRIPTFUNC_PARAMS) { - uint16 actorId; + uint16 actorId = thread->pop(); + ActorData *actor = _vm->_actor->getActor(actorId); Location actorLocation; - ActorData *actor; - - actorId = thread->pop(); actorLocation.x = thread->pop(); actorLocation.y = thread->pop(); - - actor = _vm->_actor->getActor(actorId); actorLocation.z = actor->_location.z; actor->_flags &= ~kFollower; @@ -357,10 +368,10 @@ void Script::sfScriptWalkTo(SCRIPTFUNC_PARAMS) { // Param3: theObject // Param4: withObject void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) { - uint16 objectId; - uint16 action; - uint16 theObject; - uint16 withObject; + uint16 objectId = thread->pop(); + uint16 action = thread->pop(); + uint16 theObject = thread->pop(); + uint16 withObject = thread->pop(); int16 scriptEntryPointNumber; int16 moduleNumber; ActorData *actor; @@ -368,11 +379,6 @@ void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) { const HitZone *hitZone; Event event; - objectId = thread->pop(); - action = thread->pop(); - theObject = thread->pop(); - withObject = thread->pop(); - switch (objectTypeId(objectId)) { case kGameObjectObject: obj = _vm->_actor->getObj(objectId); @@ -381,6 +387,8 @@ void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) { return; } moduleNumber = 0; + if (_vm->getGameType() == GType_IHNM) + moduleNumber = _vm->_scene->getScriptModuleNumber(); break; case kGameObjectActor: actor = _vm->_actor->getActor(objectId); @@ -393,6 +401,8 @@ void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) { } else { moduleNumber = _vm->_scene->getScriptModuleNumber(); } + if (_vm->getGameType() == GType_IHNM) + moduleNumber = _vm->_scene->getScriptModuleNumber(); break; case kGameObjectHitZone: case kGameObjectStepZone: @@ -421,7 +431,6 @@ void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) { event.param4 = theObject; // Object event.param5 = withObject; // With Object event.param6 = objectId; - _vm->_events->queue(&event); } @@ -429,14 +438,9 @@ void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) { // Param1: actor id // Param2: actor orientation void Script::sfSetActorFacing(SCRIPTFUNC_PARAMS) { - int16 actorId; - int actorDirection; - ActorData *actor; + ActorData *actor = _vm->_actor->getActor(thread->pop()); + int actorDirection = thread->pop(); - actorId = thread->pop(); - actorDirection = thread->pop(); - - actor = _vm->_actor->getActor(actorId); actor->_facingDirection = actor->_actionDirection = actorDirection; actor->_targetObject = ID_NOTHING; } @@ -448,7 +452,9 @@ void Script::sfStartBgdAnim(SCRIPTFUNC_PARAMS) { _vm->_anim->setCycles(animId, cycles); _vm->_anim->setFrameTime(animId, _vm->ticksToMSec(kRepeatSpeedTicks)); - _vm->_anim->play(animId, 0); + + if (!_vm->_anim->isPlaying(animId)) + _vm->_anim->play(animId, 0); debug(1, "sfStartBgdAnim(%d, %d)", animId, cycles); } @@ -468,11 +474,7 @@ void Script::sfStopBgdAnim(SCRIPTFUNC_PARAMS) { // reenabled. // Param1: boolean void Script::sfLockUser(SCRIPTFUNC_PARAMS) { - int16 lock; - - lock = thread->pop(); - - if (lock) { + if (thread->pop()) { _vm->_interface->deactivate(); } else { _vm->_interface->activate(); @@ -485,6 +487,7 @@ void Script::sfLockUser(SCRIPTFUNC_PARAMS) { void Script::sfPreDialog(SCRIPTFUNC_PARAMS) { _vm->_interface->deactivate(); _vm->_interface->converseClear(); + if (_vm->_interface->isInMainMode()) _vm->_interface->setMode(kPanelConverse); else @@ -497,10 +500,7 @@ void Script::sfPreDialog(SCRIPTFUNC_PARAMS) { void Script::sfKillActorThreads(SCRIPTFUNC_PARAMS) { ScriptThread *anotherThread; ScriptThreadList::iterator threadIterator; - int16 actorId; - - actorId = thread->pop(); - + int16 actorId = thread->pop(); for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) { anotherThread = threadIterator.operator->(); @@ -515,34 +515,21 @@ void Script::sfKillActorThreads(SCRIPTFUNC_PARAMS) { // Param1: actor id // Param2: object id void Script::sfFaceTowards(SCRIPTFUNC_PARAMS) { - int16 actorId; - int16 targetObject; - ActorData *actor; - - actorId = thread->pop(); - targetObject = thread->pop(); - - actor = _vm->_actor->getActor(actorId); - actor->_targetObject = targetObject; + ActorData *actor = _vm->_actor->getActor(thread->pop()); + actor->_targetObject = thread->pop(); } // Script function #15 (0x0F) // Param1: actor id // Param2: target object void Script::sfSetFollower(SCRIPTFUNC_PARAMS) { - int16 actorId; - int16 targetObject; - - ActorData *actor; + int16 actorId = thread->pop(); + ActorData *actor = _vm->_actor->getActor(actorId); + actor->_targetObject = thread->pop(); - actorId = thread->pop(); - targetObject = thread->pop(); + debug(1, "sfSetFollower(%d, %d) [%d]", actorId, actor->_targetObject, _vm->_actor->actorIdToIndex(actorId)); - debug(1, "sfSetFollower(%d, %d) [%d]", actorId, targetObject, _vm->_actor->actorIdToIndex(actorId)); - - actor = _vm->_actor->getActor(actorId); - actor->_targetObject = targetObject; - if (targetObject != ID_NOTHING) { + if (actor->_targetObject != ID_NOTHING) { actor->_flags |= kFollower; actor->_actorFlags &= ~kActorNoFollow; } else { @@ -552,53 +539,33 @@ void Script::sfSetFollower(SCRIPTFUNC_PARAMS) { // Script function #16 (0x10) void Script::sfScriptGotoScene(SCRIPTFUNC_PARAMS) { - int16 sceneNumber; - int16 entrance; - int16 transition = 0; // IHNM + int16 sceneNumber = thread->pop(); + int16 entrance = thread->pop(); - sceneNumber = thread->pop(); - entrance = thread->pop(); if (_vm->getGameType() == GType_IHNM) { - transition = thread->pop(); - _vm->_gfx->setCursor(kCursorBusy); } - if ((_vm->getGameType() == GType_ITE && sceneNumber < 0) || - (_vm->getGameType() == GType_IHNM && sceneNumber == 0)) { - // TODO: set creditsFlag to true for IHNM + if (_vm->getGameType() == GType_ITE && sceneNumber < 0) { _vm->shutDown(); return; } - if (_vm->getGameType() == GType_IHNM) { - // WORKAROUND for the briefly appearing actors at the beginning of each chapter - // This will stop the actors being drawn in those specific scenes until the scene background has been drawn - if ((_vm->_scene->currentChapterNumber() == 1 && _vm->_scene->currentSceneNumber() == 6) || - (_vm->_scene->currentChapterNumber() == 2 && _vm->_scene->currentSceneNumber() == 31) || - (_vm->_scene->currentChapterNumber() == 3 && _vm->_scene->currentSceneNumber() == 58) || - (_vm->_scene->currentChapterNumber() == 4 && _vm->_scene->currentSceneNumber() == 68) || - (_vm->_scene->currentChapterNumber() == 5 && _vm->_scene->currentSceneNumber() == 91) || - (_vm->_scene->currentChapterNumber() == 7 && _vm->_scene->currentSceneNumber() == 145)) - _vm->_actor->showActors(false); // Stop showing actors before the background is drawn - - // Since it doesn't look like the IHNM scripts remove the - // cutaway after the intro, this is probably the best place to do it - _vm->_anim->clearCutaway(); + if (_vm->getGameType() == GType_IHNM && sceneNumber == 0) { + _vm->_scene->creditsScene(); + return; } // It is possible to leave scene when converse panel is on, // particulalrly it may happen at Moneychanger tent. This - // prevent this from happening. + // prevents this from happening. if (_vm->_interface->getMode() == kPanelConverse) { _vm->_interface->setMode(kPanelMain); } // changeScene calls loadScene which calls setVerb. setVerb resets all pending objects and object flags if (sceneNumber == -1 && _vm->getGameType() == GType_IHNM) { - // TODO: This is used to return back to the character selection screen in IHNM. - // However, it seems more than this is needed, AM's speech is wrong and no actors - // are shown + // Return back to the character selection screen in IHNM _vm->_scene->changeScene(154, entrance, kTransitionFade, 8); } else { _vm->_scene->changeScene(sceneNumber, entrance, (sceneNumber == ITE_SCENE_ENDCREDIT1) ? kTransitionFade : kTransitionNoFade); @@ -615,23 +582,27 @@ void Script::sfScriptGotoScene(SCRIPTFUNC_PARAMS) { _currentObject[0] = _currentObject[1] = ID_NOTHING; showVerb(); // calls setStatusText("") - if (_vm->getGameType() == GType_IHNM) + if (_vm->getGameType() == GType_IHNM) { + // There are some cutaways which are not removed by game scripts, like the cutaway + // after the intro of IHNM or the cutaway at the end of Ellen's part in the IHNM demo. + // Clear any remaining cutaways here + _vm->_anim->clearCutaway(); _vm->_gfx->setCursor(kCursorNormal); + } } // Script function #17 (0x11) // Param1: object id // Param2: sprite index void Script::sfSetObjImage(SCRIPTFUNC_PARAMS) { - uint16 objectId; - uint16 spriteId; - ObjectData *obj; - - objectId = thread->pop(); - spriteId = thread->pop(); + uint16 objectId = thread->pop(); + uint16 spriteId = thread->pop(); + ObjectData *obj = _vm->_actor->getObj(objectId); - obj = _vm->_actor->getObj(objectId); - obj->_spriteListResourceId = OBJ_SPRITE_BASE + spriteId; + if (_vm->getGameType() == GType_IHNM) + obj->_spriteListResourceId = spriteId; + else + obj->_spriteListResourceId = OBJ_SPRITE_BASE + spriteId; _vm->_interface->refreshInventory(); } @@ -639,26 +610,18 @@ void Script::sfSetObjImage(SCRIPTFUNC_PARAMS) { // Param1: object id // Param2: name index void Script::sfSetObjName(SCRIPTFUNC_PARAMS) { - uint16 objectId; - uint16 nameIdx; - ObjectData *obj; - - objectId = thread->pop(); - nameIdx = thread->pop(); + uint16 objectId = thread->pop(); + uint16 nameIdx = thread->pop(); + ObjectData *obj = _vm->_actor->getObj(objectId); - obj = _vm->_actor->getObj(objectId); obj->_nameIndex = nameIdx; } // Script function #19 (0x13) // Param1: object id void Script::sfGetObjImage(SCRIPTFUNC_PARAMS) { - uint16 objectId; - ObjectData *obj; - - objectId = thread->pop(); - - obj = _vm->_actor->getObj(objectId); + uint16 objectId = thread->pop(); + ObjectData *obj = _vm->_actor->getObj(objectId); if (_vm->getGameType() == GType_IHNM) thread->_returnValue = obj->_spriteListResourceId; @@ -686,8 +649,7 @@ void Script::sfGetNumber(SCRIPTFUNC_PARAMS) { // Script function #21 (0x15) // Param1: door # void Script::sfScriptOpenDoor(SCRIPTFUNC_PARAMS) { - int16 doorNumber; - doorNumber = thread->pop(); + int16 doorNumber = thread->pop(); if (_vm->_scene->getFlags() & kSceneFlagISO) { _vm->_isoMap->setTileDoorState(doorNumber, 1); @@ -699,8 +661,7 @@ void Script::sfScriptOpenDoor(SCRIPTFUNC_PARAMS) { // Script function #22 (0x16) // Param1: door # void Script::sfScriptCloseDoor(SCRIPTFUNC_PARAMS) { - int16 doorNumber; - doorNumber = thread->pop(); + int16 doorNumber = thread->pop(); if (_vm->_scene->getFlags() & kSceneFlagISO) { _vm->_isoMap->setTileDoorState(doorNumber, 0); @@ -728,10 +689,7 @@ void Script::SF_cycleColors(SCRIPTFUNC_PARAMS) { // Script function #25 (0x19) // Param1: actor id void Script::sfDoCenterActor(SCRIPTFUNC_PARAMS) { - int16 actorId; - actorId = thread->pop(); - - _vm->_actor->_centerActor = _vm->_actor->getActor(actorId); + _vm->_actor->_centerActor = _vm->_actor->getActor(thread->pop()); } // Script function #26 (0x1A) nonblocking @@ -743,7 +701,9 @@ void Script::sfStartBgdAnimSpeed(SCRIPTFUNC_PARAMS) { _vm->_anim->setCycles(animId, cycles); _vm->_anim->setFrameTime(animId, _vm->ticksToMSec(speed)); - _vm->_anim->play(animId, 0); + + if (!_vm->_anim->isPlaying(animId)) + _vm->_anim->play(animId, 0); debug(1, "sfStartBgdAnimSpeed(%d, %d, %d)", animId, cycles, speed); } @@ -753,19 +713,14 @@ void Script::sfStartBgdAnimSpeed(SCRIPTFUNC_PARAMS) { // Param2: actor x // Param3: actor y void Script::sfScriptWalkToAsync(SCRIPTFUNC_PARAMS) { - int16 actorId; + int16 actorId = thread->pop(); + ActorData *actor = _vm->_actor->getActor(actorId); Location actorLocation; - ActorData *actor; - - actorId = thread->pop(); actorLocation.x = thread->pop(); actorLocation.y = thread->pop(); - - actor = _vm->_actor->getActor(actorId); actorLocation.z = actor->_location.z; actor->_flags &= ~kFollower; - _vm->_actor->actorWalkTo(actorId, actorLocation); } @@ -797,14 +752,8 @@ void Script::sfEnableZone(SCRIPTFUNC_PARAMS) { // Param1: actor id // Param2: current action void Script::sfSetActorState(SCRIPTFUNC_PARAMS) { - int16 actorId; - int currentAction; - ActorData *actor; - - actorId = thread->pop(); - currentAction = thread->pop(); - - actor = _vm->_actor->getActor(actorId); + ActorData *actor = _vm->_actor->getActor(thread->pop()); + int currentAction = thread->pop(); if ((currentAction >= kActionWalkToPoint) && (currentAction <= kActionWalkToPoint)) { wakeUpActorThread(kWaitTypeWalk, actor); @@ -818,14 +767,12 @@ void Script::sfSetActorState(SCRIPTFUNC_PARAMS) { // Param2: actor pos x // Param3: actor pos y void Script::sfScriptMoveTo(SCRIPTFUNC_PARAMS) { - int16 objectId; + int16 objectId = thread->pop(); Location location; - ActorData *actor; - ObjectData *obj; - - objectId = thread->pop(); location.x = thread->pop(); location.y = thread->pop(); + ActorData *actor; + ObjectData *obj; if (_vm->_actor->validActorId(objectId)) { actor = _vm->_actor->getActor(objectId); @@ -854,18 +801,11 @@ void Script::sfSceneEq(SCRIPTFUNC_PARAMS) { // Script function #32 (0x20) void Script::sfDropObject(SCRIPTFUNC_PARAMS) { - uint16 objectId; - uint16 spriteId; - int16 x; - int16 y; - ObjectData *obj; - - objectId = thread->pop(); - spriteId = thread->pop(); - x = thread->pop(); - y = thread->pop(); - - obj = _vm->_actor->getObj(objectId); + uint16 objectId = thread->pop(); + ObjectData *obj = _vm->_actor->getObj(objectId); + uint16 spriteId = thread->pop(); + obj->_location.x = thread->pop(); + obj->_location.y = thread->pop(); if (obj->_sceneNumber == ITE_SCENE_INV) { _vm->_interface->removeFromInventory(objectId); @@ -873,10 +813,10 @@ void Script::sfDropObject(SCRIPTFUNC_PARAMS) { obj->_sceneNumber = _vm->_scene->currentSceneNumber(); - // WORKAROUND for the compact disk in Ellen's chapter + // HACK for the compact disk in Ellen's chapter // Change the scene number of the compact disk so that it's not shown. It will be shown // once Ellen says that there's something different (i.e. after speaking with AM) - // See Actor::actorSpeech for the other part of this workaround + // See Actor::actorSpeech for the other part of this hack if (_vm->getGameType() == GType_IHNM && _vm->_scene->currentChapterNumber() == 3 && _vm->_scene->currentSceneNumber() == 59 && obj->_id == 16385) obj->_sceneNumber = -1; @@ -891,9 +831,6 @@ void Script::sfDropObject(SCRIPTFUNC_PARAMS) { } else { obj->_spriteListResourceId = OBJ_SPRITE_BASE + spriteId; } - - obj->_location.x = x; - obj->_location.y = y; } // Script function #33 (0x21) @@ -909,16 +846,10 @@ void Script::sfFinishBgdAnim(SCRIPTFUNC_PARAMS) { // Param1: actor id 1 // Param2: actor id 2 void Script::sfSwapActors(SCRIPTFUNC_PARAMS) { - int16 actorId1; - int16 actorId2; - ActorData *actor1; - ActorData *actor2; - - actorId1 = thread->pop(); - actorId2 = thread->pop(); - - actor1 = _vm->_actor->getActor(actorId1); - actor2 = _vm->_actor->getActor(actorId2); + int16 actorId1 = thread->pop(); + int16 actorId2 = thread->pop(); + ActorData *actor1 = _vm->_actor->getActor(actorId1); + ActorData *actor2 = _vm->_actor->getActor(actorId2); SWAP(actor1->_location, actor2->_location); @@ -944,24 +875,19 @@ void Script::sfSwapActors(SCRIPTFUNC_PARAMS) { ///.... // Param3: actor idN void Script::sfSimulSpeech(SCRIPTFUNC_PARAMS) { - int16 stringId; - int16 actorsCount; + int16 stringId = thread->pop(); + int16 actorsCount = thread->pop(); int i; uint16 actorsIds[ACTOR_SPEECH_ACTORS_MAX]; - const char *string; + const char *string = thread->_strings->getString(stringId); int16 sampleResourceId = -1; - stringId = thread->pop(); - actorsCount = thread->pop(); - if (actorsCount > ACTOR_SPEECH_ACTORS_MAX) error("sfSimulSpeech actorsCount=0x%X exceed ACTOR_SPEECH_ACTORS_MAX", actorsCount); for (i = 0; i < actorsCount; i++) actorsIds[i] = thread->pop(); - string = thread->_strings->getString(stringId); - if (thread->_voiceLUT->voices) { if (_vm->getGameType() == GType_IHNM && stringId >= 338) { sampleResourceId = -1; @@ -982,22 +908,16 @@ void Script::sfSimulSpeech(SCRIPTFUNC_PARAMS) { // Param3: actor y // Param4: actor walk flag void Script::sfScriptWalk(SCRIPTFUNC_PARAMS) { - int16 actorId; + int16 actorId = thread->pop(); + ActorData *actor = _vm->_actor->getActor(actorId); Location actorLocation; - ActorData *actor; - uint16 walkFlags; - - actorId = thread->pop(); actorLocation.x = thread->pop(); actorLocation.y = thread->pop(); - walkFlags = thread->pop(); - - actor = _vm->_actor->getActor(actorId); actorLocation.z = actor->_location.z; - - _vm->_actor->realLocation(actorLocation, ID_NOTHING, walkFlags); + uint16 walkFlags = thread->pop(); actor->_flags &= ~kFollower; + _vm->_actor->realLocation(actorLocation, ID_NOTHING, walkFlags); if (_vm->_actor->actorWalkTo(actorId, actorLocation) && !(walkFlags & kWalkAsync)) { thread->waitWalk(actor); @@ -1016,18 +936,10 @@ void Script::sfScriptWalk(SCRIPTFUNC_PARAMS) { // Param3: cycle frame number // Param4: cycle delay void Script::sfCycleFrames(SCRIPTFUNC_PARAMS) { - int16 actorId; - int16 flags; - int cycleFrameSequence; - int cycleDelay; - ActorData *actor; - - actorId = thread->pop(); - flags = thread->pop(); - cycleFrameSequence = thread->pop(); - cycleDelay = thread->pop(); - - actor = _vm->_actor->getActor(actorId); + ActorData *actor = _vm->_actor->getActor(thread->pop()); + int16 flags = thread->pop(); + int cycleFrameSequence = thread->pop(); + int cycleDelay = thread->pop(); if (flags & kCyclePong) { actor->_currentAction = kActionPongFrames; @@ -1046,8 +958,8 @@ void Script::sfCycleFrames(SCRIPTFUNC_PARAMS) { if (flags & kCycleReverse) { if (_vm->getGameType() == GType_IHNM && _vm->_scene->currentChapterNumber() == 2 && _vm->_scene->currentSceneNumber() == 41) { - // Prevent Benny from walking backwards after talking to the child via the monitor. This occurs in the - // original as well, and is fixed by not setting the kActorBackwards flag at this point + // WORKAROUND: Prevent Benny from walking backwards after talking to the child via the monitor. This + // occurs in the original as well, and is fixed by not setting the kActorBackwards flag at this point } else { actor->_actorFlags |= kActorBackwards; } @@ -1064,19 +976,11 @@ void Script::sfCycleFrames(SCRIPTFUNC_PARAMS) { // Param2: frame type // Param3: frame offset void Script::sfSetFrame(SCRIPTFUNC_PARAMS) { - int16 actorId; - int frameType; - int frameOffset; - ActorData *actor; - ActorFrameRange *frameRange; - - actorId = thread->pop(); - frameType = thread->pop(); - frameOffset = thread->pop(); - - actor = _vm->_actor->getActor(actorId); - - frameRange = _vm->_actor->getActorFrameRange(actorId, frameType); + int16 actorId = thread->pop(); + ActorData *actor = _vm->_actor->getActor(actorId); + int frameType = thread->pop(); + int frameOffset = thread->pop(); + ActorFrameRange *frameRange = _vm->_actor->getActorFrameRange(actorId, frameType); actor->_frameNumber = frameRange->frameIndex + frameOffset; @@ -1088,17 +992,13 @@ void Script::sfSetFrame(SCRIPTFUNC_PARAMS) { // Script function #39 (0x27) // Sets the right-hand portrait void Script::sfSetPortrait(SCRIPTFUNC_PARAMS) { - int16 param = thread->pop(); - - _vm->_interface->setRightPortrait(param); + _vm->_interface->setRightPortrait(thread->pop()); } // Script function #40 (0x28) // Sets the left-hand portrait void Script::sfSetProtagPortrait(SCRIPTFUNC_PARAMS) { - int16 param = thread->pop(); - - _vm->_interface->setLeftPortrait(param); + _vm->_interface->setLeftPortrait(thread->pop()); } // Script function #41 (0x29) nonblocking @@ -1130,21 +1030,15 @@ void Script::sfChainBgdAnim(SCRIPTFUNC_PARAMS) { // Param3: actor y // Param4: frame seq void Script::sfScriptSpecialWalk(SCRIPTFUNC_PARAMS) { - int16 actorId; - int16 walkFrameSequence; + int16 actorId = thread->pop(); + ActorData *actor = _vm->_actor->getActor(actorId); Location actorLocation; - ActorData *actor; - - actorId = thread->pop(); actorLocation.x = thread->pop(); actorLocation.y = thread->pop(); - walkFrameSequence = thread->pop(); - - actor = _vm->_actor->getActor(actorId); actorLocation.z = actor->_location.z; + int16 walkFrameSequence = thread->pop(); _vm->_actor->actorWalkTo(actorId, actorLocation); - actor->_walkFrameSequence = walkFrameSequence; } @@ -1156,39 +1050,20 @@ void Script::sfScriptSpecialWalk(SCRIPTFUNC_PARAMS) { // Param5: actor action // Param6: actor frame number void Script::sfPlaceActor(SCRIPTFUNC_PARAMS) { - int16 actorId; - Location actorLocation; - int actorDirection; - int frameType; - int frameOffset; - ActorData *actor; + int16 actorId = thread->pop(); + ActorData *actor = _vm->_actor->getActor(actorId); + actor->_location.x = thread->pop(); + actor->_location.y = thread->pop(); + actor->_facingDirection = actor->_actionDirection = thread->pop(); + int frameType = thread->pop(); + int frameOffset = thread->pop(); ActorFrameRange *frameRange; - actorId = thread->pop(); - actorLocation.x = thread->pop(); - actorLocation.y = thread->pop(); - actorDirection = thread->pop(); - frameType = thread->pop(); - frameOffset = thread->pop(); - - debug(1, "sfPlaceActor(id = 0x%x, x=%d, y=%d, dir=%d, frameType=%d, frameOffset=%d)", actorId, actorLocation.x, - actorLocation.y, actorDirection, frameType, frameOffset); - - actor = _vm->_actor->getActor(actorId); - actor->_location.x = actorLocation.x; - actor->_location.y = actorLocation.y; - actor->_facingDirection = actor->_actionDirection = actorDirection; - - if (!actor->_frames) - _vm->_actor->loadActorResources(actor); //? is not it already loaded ? + debug(1, "sfPlaceActor(id = 0x%x, x=%d, y=%d, dir=%d, frameType=%d, frameOffset=%d)", actorId, actor->_location.x, + actor->_location.y, actor->_facingDirection, frameType, frameOffset); if (frameType >= 0) { frameRange = _vm->_actor->getActorFrameRange(actorId, frameType); - - if (frameRange->frameCount <= frameOffset) { - warning("Wrong frameOffset 0x%X", frameOffset); - } - actor->_frameNumber = frameRange->frameIndex + frameOffset; actor->_currentAction = kActionFreeze; } else { @@ -1213,24 +1088,17 @@ void Script::sfCheckUserInterrupt(SCRIPTFUNC_PARAMS) { // Param4: actor y // Param5: actor walk flag void Script::sfScriptWalkRelative(SCRIPTFUNC_PARAMS) { - int16 actorId; - int16 objectId; - uint16 walkFlags; + int16 actorId = thread->pop(); + ActorData *actor = _vm->_actor->getActor(actorId); + int16 objectId = thread->pop(); Location actorLocation; - ActorData *actor; - - actorId = thread->pop(); - objectId = thread->pop(); actorLocation.x = thread->pop(); actorLocation.y = thread->pop(); - walkFlags = thread->pop(); - - actor = _vm->_actor->getActor(actorId); actorLocation.z = actor->_location.z; - - _vm->_actor->realLocation(actorLocation, objectId, walkFlags); + uint16 walkFlags = thread->pop(); actor->_flags &= ~kFollower; + _vm->_actor->realLocation(actorLocation, objectId, walkFlags); if (_vm->_actor->actorWalkTo(actorId, actorLocation) && !(walkFlags & kWalkAsync)) { thread->waitWalk(actor); @@ -1250,50 +1118,36 @@ void Script::sfScriptWalkRelative(SCRIPTFUNC_PARAMS) { // Param4: actor y // Param5: actor walk flag void Script::sfScriptMoveRelative(SCRIPTFUNC_PARAMS) { - int16 actorId; - int16 objectId; - uint16 walkFlags; + ActorData *actor = _vm->_actor->getActor(thread->pop()); + int16 objectId = thread->pop(); Location actorLocation; - ActorData *actor; - - actorId = thread->pop(); - objectId = thread->pop(); actorLocation.x = thread->pop(); actorLocation.y = thread->pop(); - walkFlags = thread->pop(); - - actor = _vm->_actor->getActor(actorId); actorLocation.z = actor->_location.z; + uint16 walkFlags = thread->pop(); _vm->_actor->realLocation(actorLocation, objectId, walkFlags); - actor->_location = actorLocation; actor->_actorFlags = (actor->_actorFlags & ~kActorFacingMask) | (walkFlags & kActorFacingMask); } // Script function #47 (0x2F) void Script::sfSimulSpeech2(SCRIPTFUNC_PARAMS) { - int16 stringId; - int16 actorsCount; - int16 speechFlags; + int16 stringId = thread->pop(); + const char *string = thread->_strings->getString(stringId); + int16 actorsCount = thread->pop(); + int16 speechFlags = thread->pop(); int i; uint16 actorsIds[ACTOR_SPEECH_ACTORS_MAX]; - const char *string; int16 sampleResourceId = -1; - stringId = thread->pop(); - actorsCount = thread->pop(); - speechFlags = thread->pop(); - if (actorsCount > ACTOR_SPEECH_ACTORS_MAX) error("sfSimulSpeech2 actorsCount=0x%X exceed ACTOR_SPEECH_ACTORS_MAX", actorsCount); for (i = 0; i < actorsCount; i++) actorsIds[i] = thread->pop(); - string = thread->_strings->getString(stringId); - if (thread->_voiceLUT->voices) { sampleResourceId = thread->_voiceLUT->voices[stringId]; if (sampleResourceId <= 0 || sampleResourceId > 4000) @@ -1308,7 +1162,7 @@ void Script::sfSimulSpeech2(SCRIPTFUNC_PARAMS) { // Script function #48 (0x30) // Param1: string rid void Script::sfPlacard(SCRIPTFUNC_PARAMS) { - int stringId; + int stringId = thread->pop(); Surface *backBuffer = _vm->_gfx->getBackBuffer(); static PalEntry cur_pal[PAL_ENTRIES]; PalEntry *pal; @@ -1320,36 +1174,35 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) { _vm->_interface->rememberMode(); _vm->_interface->setMode(kPanelPlacard); - stringId = thread->pop(); - event.type = kEvTOneshot; event.code = kCursorEvent; event.op = kEventHide; - q_event = _vm->_events->queue(&event); - _vm->_gfx->getCurrentPal(cur_pal); + _vm->_interface->setFadeMode(kFadeOut); + // Fade to black out + _vm->_gfx->getCurrentPal(cur_pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalToBlack; event.time = 0; event.duration = kNormalFadeDuration; event.data = cur_pal; - q_event = _vm->_events->chain(q_event, &event); - event.type = kEvTOneshot; + // set fade mode + event.type = kEvTImmediate; event.code = kInterfaceEvent; - event.op = kEventClearStatus; - + event.op = kEventSetFadeMode; + event.param = kNoFade; + event.time = 0; + event.duration = 0; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; - event.code = kGraphicsEvent; - event.op = kEventSetFlag; - event.param = RF_PLACARD; - + event.code = kInterfaceEvent; + event.op = kEventClearStatus; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; @@ -1361,7 +1214,6 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) { event.param3 = _vm->_scene->getHeight(); event.param4 = 0; event.param5 = _vm->getDisplayWidth(); - q_event = _vm->_events->chain(q_event, &event); // Put the text in the center of the viewport, assuming it will fit on @@ -1386,25 +1238,21 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) { event.code = kTextEvent; event.op = kEventDisplay; event.data = _placardTextEntry; - q_event = _vm->_events->chain(q_event, &event); _vm->_scene->getBGPal(pal); - event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventBlackToPal; event.time = 0; event.duration = kNormalFadeDuration; event.data = pal; - q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventThreadWake; event.param = kWaitTypePlacard; - q_event = _vm->_events->chain(q_event, &event); } @@ -1419,8 +1267,7 @@ void Script::sfPlacardOff(SCRIPTFUNC_PARAMS) { void Script::sfPsychicProfile(SCRIPTFUNC_PARAMS) { thread->wait(kWaitTypePlacard); - int stringId = thread->pop(); - _vm->_scene->showPsychicProfile(thread->_strings->getString(stringId)); + _vm->_scene->showPsychicProfile(thread->_strings->getString(thread->pop())); } void Script::sfPsychicProfileOff(SCRIPTFUNC_PARAMS) { @@ -1433,9 +1280,7 @@ void Script::sfPsychicProfileOff(SCRIPTFUNC_PARAMS) { // Script function #50 (0x32) void Script::sfSetProtagState(SCRIPTFUNC_PARAMS) { - int protagState = thread->pop(); - - _vm->_actor->setProtagState(protagState); + _vm->_actor->setProtagState(thread->pop()); } // Script function #51 (0x33) @@ -1456,28 +1301,20 @@ void Script::sfResumeBgdAnim(SCRIPTFUNC_PARAMS) { // Param5: actionCycle // Param6: flags void Script::sfThrowActor(SCRIPTFUNC_PARAMS) { - int16 actorId; - ActorData *actor; - int16 flags; - int32 actionCycle; - Location location; + ActorData *actor = _vm->_actor->getActor(thread->pop()); + actor->_finalTarget.x = thread->pop(); + actor->_finalTarget.y = thread->pop(); + actor->_finalTarget.z = actor->_location.z; + thread->pop(); // not used + int32 actionCycle = thread->pop(); + int16 flags = thread->pop(); - actorId = thread->pop(); - location.x = thread->pop(); - location.y = thread->pop(); - thread->pop(); - actionCycle = thread->pop(); - flags = thread->pop(); - - actor = _vm->_actor->getActor(actorId); - location.z = actor->_location.z; actor->_currentAction = kActionFall; actor->_actionCycle = actionCycle; actor->_fallAcceleration = -20; actor->_fallVelocity = - (actor->_fallAcceleration * actor->_actionCycle) / 2; - actor->_fallPosition = actor->_location.z << 4; + actor->_fallPosition = actor->_location.z << 4; - actor->_finalTarget = location; actor->_actionCycle--; if (!(flags & kWalkAsync)) { thread->waitWalk(actor); @@ -1488,11 +1325,7 @@ void Script::sfThrowActor(SCRIPTFUNC_PARAMS) { // Param1: actor id // Param2: target object void Script::sfWaitWalk(SCRIPTFUNC_PARAMS) { - int16 actorId; - ActorData *actor; - - actorId = thread->pop(); - actor = _vm->_actor->getActor(actorId); + ActorData *actor = _vm->_actor->getActor(thread->pop()); if ((actor->_currentAction == kActionWalkToPoint) || (actor->_currentAction == kActionWalkToLink) || @@ -1510,14 +1343,8 @@ void Script::sfScriptSceneID(SCRIPTFUNC_PARAMS) { // Param1: actor id // Param2: scene number void Script::sfChangeActorScene(SCRIPTFUNC_PARAMS) { - int16 actorId; - int32 sceneNumber; - ActorData *actor; - - actorId = thread->pop(); - sceneNumber = thread->pop(); - actor = _vm->_actor->getActor(actorId); - actor->_sceneNumber = sceneNumber; + ActorData *actor = _vm->_actor->getActor(thread->pop()); + actor->_sceneNumber = thread->pop(); } // Script function #56 (0x38) @@ -1526,19 +1353,11 @@ void Script::sfChangeActorScene(SCRIPTFUNC_PARAMS) { // Param3: frame seq // Param4: flags void Script::sfScriptClimb(SCRIPTFUNC_PARAMS) { - int16 actorId; - int16 z; - ActorData *actor; - uint16 flags; - int cycleFrameSequence; - - actorId = thread->pop(); - z = thread->pop(); - cycleFrameSequence = thread->pop(); - flags = thread->pop(); + ActorData *actor = _vm->_actor->getActor(thread->pop()); + actor->_finalTarget.z = thread->pop(); + int cycleFrameSequence = thread->pop(); + uint16 flags = thread->pop(); - actor = _vm->_actor->getActor(actorId); - actor->_finalTarget.z = z; actor->_flags &= ~kFollower; actor->_actionCycle = 1; actor->_cycleFrameSequence = cycleFrameSequence; @@ -1552,10 +1371,8 @@ void Script::sfScriptClimb(SCRIPTFUNC_PARAMS) { // Param1: door # // Param2: door state void Script::sfSetDoorState(SCRIPTFUNC_PARAMS) { - int16 doorNumber; - int16 doorState; - doorNumber = thread->pop(); - doorState = thread->pop(); + int16 doorNumber = thread->pop(); + int16 doorState = thread->pop(); if (_vm->_scene->getFlags() & kSceneFlagISO) { _vm->_isoMap->setTileDoorState(doorNumber, doorState); @@ -1568,14 +1385,10 @@ void Script::sfSetDoorState(SCRIPTFUNC_PARAMS) { // Param1: actor id // Param2: z void Script::sfSetActorZ(SCRIPTFUNC_PARAMS) { - int16 objectId; + int16 objectId = thread->pop(); + int16 z = thread->pop(); ActorData *actor; ObjectData *obj; - int16 z; - - objectId = thread->pop(); - z = thread->pop(); - if (_vm->_actor->validActorId(objectId)) { actor = _vm->_actor->getActor(objectId); @@ -1595,22 +1408,15 @@ void Script::sfSetActorZ(SCRIPTFUNC_PARAMS) { // Param4: x // Param5: y void Script::sfScriptText(SCRIPTFUNC_PARAMS) { - int16 stringId; - int16 flags; - Rect rect; - int color; + const char *text = thread->_strings->getString(thread->pop()); + int16 flags = thread->pop(); + int color = thread->pop(); Point point; - int width; - const char*text; - stringId = thread->pop(); - flags = thread->pop(); - color = thread->pop(); point.x = thread->pop(); point.y = thread->pop(); + Rect rect; + int width = _vm->_font->getStringWidth(kKnownFontScript, text, 0, kFontOutline); - text = thread->_strings->getString(stringId); - - width = _vm->_font->getStringWidth(kKnownFontScript, text, 0, kFontOutline); rect.top = point.y - 6; rect.setHeight(12); rect.left = point.x - width / 2; @@ -1623,11 +1429,7 @@ void Script::sfScriptText(SCRIPTFUNC_PARAMS) { // Script function #60 (0x3C) // Param1: actor id void Script::sfGetActorX(SCRIPTFUNC_PARAMS) { - int16 actorId; - ActorData *actor; - - actorId = thread->pop(); - actor = _vm->_actor->getActor(actorId); + ActorData *actor = _vm->_actor->getActor(thread->pop()); thread->_returnValue = actor->_location.x >> 2; } @@ -1635,23 +1437,17 @@ void Script::sfGetActorX(SCRIPTFUNC_PARAMS) { // Script function #61 (0x3D) // Param1: actor id void Script::sfGetActorY(SCRIPTFUNC_PARAMS) { - int16 actorId; - ActorData *actor; - - actorId = thread->pop(); - actor = _vm->_actor->getActor(actorId); + ActorData *actor = _vm->_actor->getActor(thread->pop()); thread->_returnValue = actor->_location.y >> 2; } // Script function #62 (0x3E) void Script::sfEraseDelta(SCRIPTFUNC_PARAMS) { - Surface *backGroundSurface; + Surface *backGroundSurface = _vm->_render->getBackGroundSurface(); BGInfo backGroundInfo; - backGroundSurface = _vm->_render->getBackGroundSurface(); _vm->_scene->getBGInfo(backGroundInfo); - backGroundSurface->blit(backGroundInfo.bounds, backGroundInfo.buffer); } @@ -1712,12 +1508,11 @@ void Script::sfPickClimbOutPos(SCRIPTFUNC_PARAMS) { // Script function #65 (0x41) void Script::sfTossRif(SCRIPTFUNC_PARAMS) { - int16 uc , vc; uint16 direction; ActorData *protagonist = _vm->_actor->_protagonist; + int16 uc = protagonist->_location.u() >> 4; + int16 vc = protagonist->_location.v() >> 4; - uc = protagonist->_location.u() >> 4; - vc = protagonist->_location.v() >> 4; if (_vm->_isoMap->findNearestChasm(uc, vc, direction)) { uc <<= 4; vc <<= 4; @@ -1793,10 +1588,9 @@ void Script::sfPlayLoopedSound(SCRIPTFUNC_PARAMS) { } // Script function #72 (0x48) +// Param1: animation id void Script::sfGetDeltaFrame(SCRIPTFUNC_PARAMS) { - uint16 animId = (uint16)thread->pop(); - - thread->_returnValue = _vm->_anim->getCurrentFrame(animId); + thread->_returnValue = _vm->_anim->getCurrentFrame((uint16)thread->pop()); } // Script function #73 (0x49) @@ -1813,10 +1607,8 @@ void Script::sfProtectResult(SCRIPTFUNC_PARAMS) { if (_vm->_copyProtection) { thread->_returnValue = _vm->_interface->getProtectHash(); } else { - int protectHash; - //cheating - protectHash = thread->pop(); + int protectHash = thread->pop(); thread->push(protectHash); thread->_returnValue = protectHash; } @@ -1824,10 +1616,7 @@ void Script::sfProtectResult(SCRIPTFUNC_PARAMS) { // Script function #75 (0x4b) void Script::sfRand(SCRIPTFUNC_PARAMS) { - int16 param; - - param = thread->pop(); - thread->_returnValue = _vm->_rnd.getRandomNumber(param - 1); + thread->_returnValue = _vm->_rnd.getRandomNumber(thread->pop() - 1); } // Script function #76 (0x4c) @@ -1839,7 +1628,6 @@ void Script::sfFadeMusic(SCRIPTFUNC_PARAMS) { void Script::sfPlayVoice(SCRIPTFUNC_PARAMS) { int16 param = thread->pop(); - warning("sfPlayVoice(%d)", param); if (param > 0) { _vm->_sndRes->playVoice(param + 3712); } else { @@ -1881,13 +1669,24 @@ void Script::finishDialog(int strID, int replyID, int flags, int bitOffset) { } void Script::sfSetChapterPoints(SCRIPTFUNC_PARAMS) { - int16 ethics = thread->pop(); - int16 barometer = thread->pop(); int chapter = _vm->_scene->currentChapterNumber(); + _vm->_ethicsPoints[chapter] = thread->pop(); + int16 barometer = thread->pop(); + static PalEntry cur_pal[PAL_ENTRIES]; - _vm->_ethicsPoints[chapter] = ethics; - _vm->_spiritualBarometer = ethics * 256 / barometer; + _vm->_spiritualBarometer = _vm->_ethicsPoints[chapter] * 256 / barometer; _vm->_scene->setChapterPointsChanged(true); // don't save this music when saving in IHNM + + if (_vm->_spiritualBarometer > 255) + _vm->_gfx->setPaletteColor(kIHNMColorPortrait, 0xff, 0xff, 0xff); + else + _vm->_gfx->setPaletteColor(kIHNMColorPortrait, + _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.red / 256, + _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.green / 256, + _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.blue / 256); + + _vm->_gfx->getCurrentPal(cur_pal); + _vm->_gfx->setPalette(cur_pal); } void Script::sfSetPortraitBgColor(SCRIPTFUNC_PARAMS) { @@ -1899,12 +1698,9 @@ void Script::sfSetPortraitBgColor(SCRIPTFUNC_PARAMS) { } void Script::sfScriptStartCutAway(SCRIPTFUNC_PARAMS) { - int16 cut; - int16 fade; - - cut = thread->pop(); + int16 cut = thread->pop(); thread->pop(); // Not used - fade = thread->pop(); + int16 fade = thread->pop(); _vm->_anim->setCutAwayMode(kPanelCutaway); _vm->_anim->playCutaway(cut, fade != 0); @@ -1930,61 +1726,37 @@ void Script::sfResetMouseClicks(SCRIPTFUNC_PARAMS) { // Used in IHNM only // Param1: frames void Script::sfWaitFrames(SCRIPTFUNC_PARAMS) { - int16 frames; - frames = thread->pop(); - - // HACK for the nightfall scene in Benny's chapter - // sfWaitFrames is supposed to wait for fadein and fadeout during that cutaway, but we - // don't support it yet (function sfScriptFade). This is a temporary hack to avoid - // having ScummVM wait for ever in that cutaway - // FIXME: Remove this hack once the palette fading is properly handled - if (_vm->_scene->currentChapterNumber() == 2 && _vm->_scene->currentSceneNumber() == 41 && _vm->_anim->hasCutaway()) - return; + int16 frames = thread->pop(); if (!_skipSpeeches) thread->waitFrames(_vm->_frameCount + frames); } void Script::sfScriptFade(SCRIPTFUNC_PARAMS) { - thread->pop(); // first pal entry, ignored (already handled by Gfx::palToBlack) - thread->pop(); // last pal entry, ignored (already handled by Gfx::palToBlack) + int16 firstPalEntry = thread->pop(); + int16 lastPalEntry = thread->pop(); int16 startingBrightness = thread->pop(); int16 endingBrightness = thread->pop(); - // delay between pal changes is always 10 (not used) - static PalEntry cur_pal[PAL_ENTRIES]; Event event; - short delta = (startingBrightness < endingBrightness) ? +1 : -1; + static PalEntry cur_pal[PAL_ENTRIES]; _vm->_gfx->getCurrentPal(cur_pal); - - // TODO: This is still wrong, probably a new event type needs to be added (kEventPalFade) - warning("TODO: sfScriptFade"); - return; - - if (startingBrightness > 255) - startingBrightness = 255; - if (startingBrightness < 0 ) - startingBrightness = 0; - if (endingBrightness > 255) - endingBrightness = 255; - if (endingBrightness < 0) - endingBrightness = 0; - event.type = kEvTImmediate; event.code = kPalEvent; - event.op = kEventPalToBlack; + event.op = kEventPalFade; event.time = 0; - event.duration = kNormalFadeDuration - ((endingBrightness - startingBrightness) * delta); + event.duration = kNormalFadeDuration; event.data = cur_pal; - - _vm->_events->queue(&event); + event.param = startingBrightness; + event.param2 = endingBrightness; + event.param3 = firstPalEntry; + event.param4 = lastPalEntry - firstPalEntry + 1; + _vm->_events->queue(&event); } void Script::sfScriptStartVideo(SCRIPTFUNC_PARAMS) { - int16 vid; - int16 fade; - vid = thread->pop(); - fade = thread->pop(); + int16 vid = thread->pop(); + int16 fade = thread->pop(); _vm->_anim->setCutAwayMode(kPanelVideo); _vm->_anim->startVideo(vid, fade != 0); @@ -2006,14 +1778,10 @@ void Script::sfShowIHNMDemoHelpBg(SCRIPTFUNC_PARAMS) { } void Script::sfAddIHNMDemoHelpTextLine(SCRIPTFUNC_PARAMS) { - int stringId, textHeight; + int stringId = thread->pop(); TextListEntry textEntry; Event event; - stringId = thread->pop(); - - textHeight = _vm->_font->getHeight(kKnownFontVerb, thread->_strings->getString(stringId), 226, kFontCentered); - textEntry.knownColor = kKnownColorBlack; textEntry.useRect = true; textEntry.rect.left = 245; @@ -2030,10 +1798,9 @@ void Script::sfAddIHNMDemoHelpTextLine(SCRIPTFUNC_PARAMS) { event.code = kTextEvent; event.op = kEventDisplay; event.data = _psychicProfileTextEntry; - _vm->_events->queue(&event); - _ihnmDemoCurrentY += 10; + _ihnmDemoCurrentY += _vm->_font->getHeight(kKnownFontVerb, thread->_strings->getString(stringId), 226, kFontCentered); } void Script::sfShowIHNMDemoHelpPage(SCRIPTFUNC_PARAMS) { @@ -2051,9 +1818,14 @@ void Script::sfVstopLoopedFX(SCRIPTFUNC_PARAMS) { _vm->_sound->stopSound(); } -void Script::sf92(SCRIPTFUNC_PARAMS) { - SF_stub("sf92", thread, nArgs); - // This opcode is empty in the full version of IHNM, but it's not empty in the demo +void Script::sfDemoSetInteractive(SCRIPTFUNC_PARAMS) { + if (thread->pop() == 0) { + _vm->_interface->deactivate(); + _vm->_interface->setMode(kPanelNull); + } + + // Note: the original also sets an appropriate flag here, but we don't, + // as we don't use it } void Script::sfDemoIsInteractive(SCRIPTFUNC_PARAMS) { @@ -2159,7 +1931,6 @@ void Script::sfQueueMusic(SCRIPTFUNC_PARAMS) { // change // FIXME: If this is too short for other cases apart from chapter // point change, set it back to 1000 - _vm->_events->queue(&event); if (!_vm->_scene->haveChapterPointsChanged()) { @@ -2173,9 +1944,7 @@ void Script::sfQueueMusic(SCRIPTFUNC_PARAMS) { } void Script::sfDisableAbortSpeeches(SCRIPTFUNC_PARAMS) { - int value = thread->pop(); - - _vm->_interface->disableAbortSpeeches(value != 0); + _vm->_interface->disableAbortSpeeches(thread->pop() != 0); } void Script::sfNull(SCRIPTFUNC_PARAMS) { diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp index edbdebabab..ac21656dfa 100644 --- a/engines/saga/sndres.cpp +++ b/engines/saga/sndres.cpp @@ -101,8 +101,7 @@ SndRes::~SndRes() { } } -void SndRes::setVoiceBank(int serial) -{ +void SndRes::setVoiceBank(int serial) { if (_voiceSerial == serial) return; _voiceSerial = serial; @@ -183,9 +182,9 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff } bool uncompressedSound = false; - // If a patch file exists for sound resource 4 (used in ITE intro), don't treat this sound as compressed - if (_vm->getGameType() == GType_ITE && resourceId == 4 && - (Common::File::exists("sound/p2_a.iaf") || Common::File::exists("sound/p2_a.voc"))) + // If patch data exists for sound resource 4 (used in ITE intro), don't treat this sound as compressed + // Patch data for this resource is in file p2_a.iaf or p2_a.voc + if (_vm->getGameType() == GType_ITE && resourceId == 4 && context->table[resourceId].patchData != NULL) uncompressedSound = true; // FIXME: Currently, the SFX.RES file in IHNM cannot be compressed |