diff --git a/engines/myst3/console.cpp b/engines/myst3/console.cpp index 474cc687da5..f83151950d6 100644 --- a/engines/myst3/console.cpp +++ b/engines/myst3/console.cpp @@ -260,7 +260,7 @@ bool Console::Cmd_Go(int argc, const char **argv) { _vm->_state->setLocationNextRoom(roomID); _vm->_state->setLocationNextNode(nodeId); - _vm->goToNode(0, 1); + _vm->goToNode(0, kTransitionFade); return false; } diff --git a/engines/myst3/inventory.cpp b/engines/myst3/inventory.cpp index 8e44da3f258..957fadf2ebe 100644 --- a/engines/myst3/inventory.cpp +++ b/engines/myst3/inventory.cpp @@ -244,7 +244,7 @@ void Inventory::openBook(uint16 age, uint16 room, uint16 node) { _vm->_state->setLocationNextAge(age); _vm->_state->setLocationNextRoom(room); - _vm->goToNode(node, 1); + _vm->goToNode(node, kTransitionFade); } void Inventory::addSaavedroChapter(uint16 var) { diff --git a/engines/myst3/menu.cpp b/engines/myst3/menu.cpp index af92bb76c28..71be551ca7d 100644 --- a/engines/myst3/menu.cpp +++ b/engines/myst3/menu.cpp @@ -142,7 +142,7 @@ void Menu::goToNode(uint16 node) { _vm->_state->setMenuEscapePressed(0); _vm->_state->setLocationNextAge(9); _vm->_state->setLocationNextRoom(901); - _vm->goToNode(node, 2); + _vm->goToNode(node, kTransitionNone); } Dialog::Dialog(Myst3Engine *vm, uint id): @@ -330,7 +330,7 @@ void Menu::loadMenuLoad() { _vm->_state->setMenuSavedRoom(0); _vm->_state->setMenuSavedNode(0); - _vm->goToNode(0, 1); + _vm->goToNode(0, kTransitionFade); } void Menu::saveMenuOpen() { diff --git a/engines/myst3/module.mk b/engines/myst3/module.mk index 8fe75d286ce..c3acd6420da 100644 --- a/engines/myst3/module.mk +++ b/engines/myst3/module.mk @@ -27,7 +27,8 @@ MODULE_OBJS := \ script.o \ sound.o \ state.o \ - subtitles.o + subtitles.o \ + transition.o # This module can be built as a plugin ifeq ($(ENABLE_MYST3), DYNAMIC_PLUGIN) diff --git a/engines/myst3/myst3.cpp b/engines/myst3/myst3.cpp index 1910a6c430b..78ae2473821 100644 --- a/engines/myst3/myst3.cpp +++ b/engines/myst3/myst3.cpp @@ -47,6 +47,7 @@ #include "engines/myst3/menu.h" #include "engines/myst3/sound.h" #include "engines/myst3/ambient.h" +#include "engines/myst3/transition.h" #include "image/jpeg.h" @@ -500,7 +501,7 @@ void Myst3Engine::processInput(bool lookOnly) { } } -void Myst3Engine::drawFrame() { +void Myst3Engine::drawFrame(bool noSwap) { _sound->update(); _gfx->clear(); @@ -573,9 +574,11 @@ void Myst3Engine::drawFrame() { if (_cursor->isVisible()) _cursor->draw(); - _system->updateScreen(); - _system->delayMillis(10); - _state->updateFrameCounters(); + if (!noSwap) { + _system->updateScreen(); + _system->delayMillis(10); + _state->updateFrameCounters(); + } } bool Myst3Engine::isInventoryVisible() { @@ -588,7 +591,7 @@ bool Myst3Engine::isInventoryVisible() { return true; } -void Myst3Engine::goToNode(uint16 nodeID, uint transition) { +void Myst3Engine::goToNode(uint16 nodeID, TransitionType transitionType) { uint16 node = _state->getLocationNextNode(); if (node == 0) node = nodeID; @@ -596,7 +599,10 @@ void Myst3Engine::goToNode(uint16 nodeID, uint transition) { uint16 room = _state->getLocationNextRoom(); uint16 age = _state->getLocationNextAge(); - if (_state->getViewType() == kCube) { + Transition *transition = Transition::initialize(this, transitionType); + + ViewType sourceViewType = _state->getViewType(); + if (sourceViewType == kCube) { // The lookat direction in the next node should be // the direction of the mouse cursor float pitch, heading; @@ -613,6 +619,11 @@ void Myst3Engine::goToNode(uint16 nodeID, uint transition) { if (_state->getAmbiantPreviousFadeOutDelay() > 0) { _ambient->playCurrentNode(100, _state->getAmbiantPreviousFadeOutDelay()); } + + if (transition) { + transition->draw(); + delete transition; + } } void Myst3Engine::loadNode(uint16 nodeID, uint32 roomID, uint32 ageID) { @@ -1180,7 +1191,7 @@ Common::Error Myst3Engine::loadGameState(int slot) { _state->setMenuSavedRoom(0); _state->setMenuSavedNode(0); - goToNode(0, 1); + goToNode(0, kTransitionFade); return Common::kNoError; } diff --git a/engines/myst3/myst3.h b/engines/myst3/myst3.h index 11b40080ab6..d4e0f22d74d 100644 --- a/engines/myst3/myst3.h +++ b/engines/myst3/myst3.h @@ -67,6 +67,14 @@ enum { kDebugScript = (1 << 3) }; +enum TransitionType { + kTransitionFade = 1, + kTransitionNone, + kTransitionZip, + kTransitionLeftToRight, + kTransitionRightToLeft +}; + class Console; class GameState; class HotSpot; @@ -123,7 +131,7 @@ public: Graphics::Surface *loadTexture(uint16 id); static Graphics::Surface *decodeJpeg(const DirectorySubEntry *jpegDesc); - void goToNode(uint16 nodeID, uint transition); + void goToNode(uint16 nodeID, TransitionType transition); void loadNode(uint16 nodeID, uint32 roomID = 0, uint32 ageID = 0); void unloadNode(); void loadNodeCubeFaces(uint16 nodeID); @@ -161,7 +169,7 @@ public: void getMovieLookAt(uint16 id, bool start, float &pitch, float &heading); void processInput(bool lookOnly); - void drawFrame(); + void drawFrame(bool noSwap = false); bool inputValidatePressed(); bool inputEscapePressed(); @@ -208,6 +216,9 @@ private: bool isInventoryVisible(); + void transitionDraw(TransitionType type, Graphics::Surface *source); + void transitionDrawStep(TransitionType type, uint32 *target, uint targetPitch, uint32 *source, uint sourcePitch, uint32 *destination, uint destinationPitch, uint destinationHeight, uint completion); + friend class Console; }; diff --git a/engines/myst3/script.cpp b/engines/myst3/script.cpp index 077e9682982..9e48c2b5f48 100644 --- a/engines/myst3/script.cpp +++ b/engines/myst3/script.cpp @@ -1898,19 +1898,19 @@ void Script::chooseNextNode(Context &c, const Opcode &cmd) { void Script::goToNodeTransition(Context &c, const Opcode &cmd) { debugC(kDebugScript, "Opcode %d: Go to node %d with transition %d", cmd.op, cmd.args[0], cmd.args[1]); - _vm->goToNode(cmd.args[0], cmd.args[1]); + _vm->goToNode(cmd.args[0], static_cast(cmd.args[1])); } void Script::goToNodeTrans2(Context &c, const Opcode &cmd) { debugC(kDebugScript, "Opcode %d: Go to node %d", cmd.op, cmd.args[0]); - _vm->goToNode(cmd.args[0], 2); + _vm->goToNode(cmd.args[0], kTransitionNone); } void Script::goToNodeTrans1(Context &c, const Opcode &cmd) { debugC(kDebugScript, "Opcode %d: Go to node %d", cmd.op, cmd.args[0]); - _vm->goToNode(cmd.args[0], 1); + _vm->goToNode(cmd.args[0], kTransitionFade); } void Script::goToRoomNode(Context &c, const Opcode &cmd) { @@ -1919,13 +1919,13 @@ void Script::goToRoomNode(Context &c, const Opcode &cmd) { _vm->_state->setLocationNextRoom(cmd.args[0]); _vm->_state->setLocationNextNode(cmd.args[1]); - _vm->goToNode(0, 1); + _vm->goToNode(0, kTransitionFade); } void Script::zipToNode(Context &c, const Opcode &cmd) { debugC(kDebugScript, "Opcode %d: Zip to node %d", cmd.op, cmd.args[0]); - _vm->goToNode(cmd.args[0], 3); + _vm->goToNode(cmd.args[0], kTransitionZip); } void Script::zipToRoomNode(Context &c, const Opcode &cmd) { @@ -1934,7 +1934,7 @@ void Script::zipToRoomNode(Context &c, const Opcode &cmd) { _vm->_state->setLocationNextRoom(cmd.args[0]); _vm->_state->setLocationNextNode(cmd.args[1]); - _vm->goToNode(0, 3); + _vm->goToNode(0, kTransitionZip); } void Script::reloadNode(Context &c, const Opcode &cmd) { diff --git a/engines/myst3/state.cpp b/engines/myst3/state.cpp index ec96413ef06..6eb0fdbd5dc 100644 --- a/engines/myst3/state.cpp +++ b/engines/myst3/state.cpp @@ -204,6 +204,8 @@ GameState::GameState(Myst3Engine *vm): VAR(185, CameraMoveSpeed, false) + VAR(187, TransitionSound, false) + VAR(188, TransitionSoundVolume, false) VAR(189, LocationNextNode, false) VAR(190, LocationNextRoom, false) VAR(191, LocationNextAge, false) diff --git a/engines/myst3/state.h b/engines/myst3/state.h index f4dd415721d..829ed559405 100644 --- a/engines/myst3/state.h +++ b/engines/myst3/state.h @@ -176,6 +176,9 @@ public: DECLARE_VAR(185, CameraMoveSpeed) + DECLARE_VAR(187, TransitionSound) + DECLARE_VAR(188, TransitionSoundVolume) + DECLARE_VAR(189, LocationNextNode) DECLARE_VAR(190, LocationNextRoom) DECLARE_VAR(191, LocationNextAge) diff --git a/engines/myst3/transition.cpp b/engines/myst3/transition.cpp new file mode 100644 index 00000000000..41ef9437aed --- /dev/null +++ b/engines/myst3/transition.cpp @@ -0,0 +1,206 @@ +/* ResidualVM - A 3D game interpreter + * + * ResidualVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/config-manager.h" + +#include "engines/myst3/transition.h" +#include "engines/myst3/sound.h" +#include "engines/myst3/state.h" + +#include "graphics/colormasks.h" + +namespace Myst3 { + +Transition *Transition::initialize(Myst3Engine *vm, TransitionType type) { + return new Transition(vm, type); +} + +Transition::Transition(Myst3Engine *vm, TransitionType type) : + _vm(vm), + _type(type), + _sourceScreenshot(nullptr) { + + // Capture a screenshot of the source node + if (type != kTransitionNone && computeDuration() != 0) { + _sourceScreenshot = _vm->_gfx->getScreenshot(); + } +} + +Transition::~Transition() { + if (_sourceScreenshot) { + _sourceScreenshot->free(); + delete _sourceScreenshot; + } +} + +int Transition::computeDuration() { + int durationFrames = 30 * (100 - ConfMan.getInt("transition_speed")) / 100; + if (_type == kTransitionZip) { + durationFrames >>= 1; + } + return durationFrames; +} + +void Transition::playSound() { + if (_vm->_state->getTransitionSound()) { + _vm->_sound->playEffect(_vm->_state->getTransitionSound(), + _vm->_state->getTransitionSoundVolume()); + } + _vm->_state->setTransitionSound(0); +} + +void Transition::draw() { + // Play the transition sound + playSound(); + + // Got any transition to draw? + if (!_sourceScreenshot) { + return; + } + + // Capture a screenshot of the destination node + _vm->drawFrame(true); + Graphics::Surface *target = _vm->_gfx->getScreenshot(); + + // Create the temporary surface and texture for the transition + Graphics::Surface *frame = new Graphics::Surface(); + frame->create(target->w, target->h, target->format); + Common::Rect frameRect = Common::Rect(frame->w, frame->h); + + Texture *frameTexture = _vm->_gfx->createTexture(frame); + + // Compute the start and end frames for the animation + int durationFrames = computeDuration(); + int startFrame = _vm->_state->getFrameCount(); + uint endFrame = startFrame + durationFrames; + + // Draw each step until completion + int completion = 0; + while (_vm->_state->getFrameCount() <= endFrame || completion < 100) { + completion = CLIP(100 * (_vm->_state->getFrameCount() - startFrame) / durationFrames, 0, 100); + + uint32 *targetPtr = (uint32 *)target->getPixels(); + uint32 *sourcePtr = (uint32 *)_sourceScreenshot->getPixels(); + uint32 *framePtr = (uint32 *)frame->getPixels(); + drawStep(targetPtr, target->pitch, sourcePtr, _sourceScreenshot->pitch, framePtr, frame->pitch, frame->h, completion); + + frameTexture->update(frame); + _vm->_gfx->drawTexturedRect2D(frameRect, frameRect, frameTexture); + + g_system->updateScreen(); + g_system->delayMillis(10); + _vm->_state->updateFrameCounters(); + } + + _vm->_gfx->freeTexture(frameTexture); + + frame->free(); + delete frame; + + target->free(); + delete target; +} + +void Transition::drawStep(uint32 *target, uint targetPitch, uint32 *source, uint sourcePitch, uint32 *destination, uint destinationPitch, uint destinationHeight, uint completion) { + switch (_type) { + case kTransitionNone: + break; + + case kTransitionFade: + case kTransitionZip: { + + for (uint y = 0; y < destinationHeight; y++) { + uint32 *sourcePtr = source + (destinationHeight - y - 1) * (sourcePitch / 4); + uint32 *targetPtr = target + (destinationHeight - y - 1) * (targetPitch / 4); + uint32 *destinationPtr = destination; + + for (uint x = 0; x < 640; x++) { + byte sourceR, sourceG, sourceB; + byte targetR, targetG, targetB; + byte destR, destG, destB; + + Graphics::colorToRGB< Graphics::ColorMasks<8888> >(*sourcePtr++, sourceR, sourceG, sourceB); + Graphics::colorToRGB< Graphics::ColorMasks<8888> >(*targetPtr++, targetR, targetG, targetB); + + // TODO: optimize ? + destR = sourceR * (100 - completion) / 100 + targetR * completion / 100; + destG = sourceG * (100 - completion) / 100 + targetG * completion / 100; + destB = sourceB * (100 - completion) / 100 + targetB * completion / 100; + + *destinationPtr++ = Graphics::RGBToColor< Graphics::ColorMasks<8888> >(destR, destG, destB); + } + + destination += (destinationPitch / 4); + } + } + break; + + case kTransitionLeftToRight: { + uint transitionX = (640 * 100 - 640 * completion) / 100; + for (uint y = 0; y < destinationHeight; y++) { + uint32 *sourcePtr = source + (destinationHeight - y - 1) * (sourcePitch / 4); + uint32 *destinationPtr = destination; + + for (uint x = 0; x < transitionX; x++) { + *destinationPtr = *sourcePtr; + destinationPtr++; + sourcePtr++; + } + + uint32 *targetPtr = target + (destinationHeight - y - 1) * (targetPitch / 4) + transitionX; + for (uint x = transitionX; x < 640; x++) { + *destinationPtr = *targetPtr; + destinationPtr++; + targetPtr++; + } + + destination += (destinationPitch / 4); + } + } + break; + + case kTransitionRightToLeft: { + uint transitionX = 640 * completion / 100; + for (uint y = 0; y < destinationHeight; y++) { + uint32 *targetPtr = target + (destinationHeight - y - 1) * (targetPitch / 4); + uint32 *destinationPtr = destination; + + for (uint x = 0; x < transitionX; x++) { + *destinationPtr = *targetPtr; + destinationPtr++; + targetPtr++; + } + + uint32 *sourcePtr = source + (destinationHeight - y - 1) * (sourcePitch / 4) + transitionX; + for (uint x = transitionX; x < 640; x++) { + *destinationPtr = *sourcePtr; + destinationPtr++; + sourcePtr++; + } + + destination += (destinationPitch / 4); + } + } + break; + } +} +} /* namespace Myst3 */ diff --git a/engines/myst3/transition.h b/engines/myst3/transition.h new file mode 100644 index 00000000000..2c6743d2db8 --- /dev/null +++ b/engines/myst3/transition.h @@ -0,0 +1,51 @@ +/* ResidualVM - A 3D game interpreter + * + * ResidualVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef TRANSITION_H_ +#define TRANSITION_H_ + +#include "engines/myst3/myst3.h" + +namespace Myst3 { + +class Transition { +public: + static Transition *initialize(Myst3Engine *vm, TransitionType type); + virtual ~Transition(); + + void draw(); + +private: + Transition(Myst3Engine *vm, TransitionType type); + void drawStep(uint32 *target, uint targetPitch, uint32 *source, uint sourcePitch, uint32 *destination, uint destinationPitch, uint destinationHeight, uint completion); + int computeDuration(); + void playSound(); + + Myst3Engine *_vm; + TransitionType _type; + + Graphics::Surface *_sourceScreenshot; + +}; + +} /* namespace Myst3 */ +#endif /* TRANSITION_H_ */