aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk
diff options
context:
space:
mode:
authorBastien Bouclet2017-02-11 18:33:40 +0100
committerEugene Sandulenko2017-07-03 08:50:10 +0200
commit3900597996000ecb28f1c2cb366c8faf59495734 (patch)
treebbd75bf11a4d810d5607b685d10233779f052e4f /engines/mohawk
parent3f58a795e724cde51966cb7e8b6fd8550d576b16 (diff)
downloadscummvm-rg350-3900597996000ecb28f1c2cb366c8faf59495734.tar.gz
scummvm-rg350-3900597996000ecb28f1c2cb366c8faf59495734.tar.bz2
scummvm-rg350-3900597996000ecb28f1c2cb366c8faf59495734.zip
MOHAWK: Implement card transitions for Riven
Diffstat (limited to 'engines/mohawk')
-rw-r--r--engines/mohawk/riven.cpp21
-rw-r--r--engines/mohawk/riven.h10
-rw-r--r--engines/mohawk/riven_graphics.cpp351
-rw-r--r--engines/mohawk/riven_graphics.h17
-rw-r--r--engines/mohawk/riven_vars.cpp3
5 files changed, 339 insertions, 63 deletions
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 197065b7e6..16df6a67e6 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -156,7 +156,7 @@ Common::Error MohawkEngine_Riven::run() {
}
// Set the transition speed
- _gfx->setTransitionSpeed(_vars["transitionmode"]);
+ _gfx->setTransitionMode((RivenTransitionMode) _vars["transitionmode"]);
// Start at main cursor
_cursor->setCursor(kRivenMainCursor);
@@ -195,8 +195,8 @@ void MohawkEngine_Riven::doFrame() {
checkTimer();
_sound->updateSLST();
_gfx->runFliesEffect();
- bool needsUpdate = _gfx->runScheduledWaterEffects();
- needsUpdate |= _video->updateMovies();
+ _gfx->runScheduledWaterEffects();
+ _video->updateMovies();
Common::Event event;
@@ -212,8 +212,6 @@ void MohawkEngine_Riven::doFrame() {
else
_inventory->hide();
}
-
- needsUpdate = true;
break;
case Common::EVENT_LBUTTONDOWN:
_stack->onMouseDown(_eventMan->getMousePos());
@@ -237,7 +235,6 @@ void MohawkEngine_Riven::doFrame() {
_showHotspots = !_showHotspots;
if (_showHotspots) {
_card->drawHotspotRects();
- needsUpdate = true;
} else
refreshCard();
break;
@@ -280,9 +277,8 @@ void MohawkEngine_Riven::doFrame() {
_scriptMan->runQueuedScripts();
}
- // Update the screen if we need to
- if (needsUpdate)
- _system->updateScreen();
+ // Update the screen once per frame
+ _system->updateScreen();
// Cut down on CPU usage
_system->delayMillis(10);
@@ -440,15 +436,14 @@ void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
while (_system->getMillis() < startTime + ms && !shouldQuit()) {
_sound->updateSLST();
_gfx->runFliesEffect();
- bool needsUpdate = _gfx->runScheduledWaterEffects();
- needsUpdate |= _video->updateMovies();
+ _gfx->runScheduledWaterEffects();
+ _video->updateMovies();
Common::Event event;
while (_system->getEventManager()->pollEvent(event))
;
- if (needsUpdate)
- _system->updateScreen();
+ _system->updateScreen();
_system->delayMillis(10); // Ease off the CPU
}
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index a4c4db4188..f7ef95b31b 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -62,13 +62,6 @@ enum {
kStackLast = kStackAspit
};
-enum RivenTransitionSpeed {
- kRivenTransitionSpeedNone = 5000,
- kRivenTransitionSpeedFastest = 5001,
- kRivenTransitionSpeedNormal = 5002,
- kRivenTransitionSpeedBest = 5003
-};
-
// Engine Debug Flags
enum {
kRivenDebugScript = (1 << 0)
@@ -135,6 +128,7 @@ private:
public:
// Stack/card/script funtions
+ RivenStack *constructStackById(uint16 id);
void changeToCard(uint16 dest);
void changeToStack(uint16);
void refreshCard();
@@ -162,8 +156,6 @@ public:
void installTimer(TimerProc *proc, uint32 time);
void checkTimer();
void removeTimer();
-
- RivenStack *constructStackById(uint16 id);
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index 60b8c5d5f0..240144b5ca 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -29,14 +29,247 @@
#include "common/system.h"
#include "engines/util.h"
+#include "graphics/colormasks.h"
namespace Mohawk {
+class TransitionEffect {
+public:
+ TransitionEffect(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
+ RivenTransition type, uint duration, const Common::Rect &rect) :
+ _system(system),
+ _mainScreen(mainScreen),
+ _effectScreen(effectScreen),
+ _type(type),
+ _duration(duration),
+ _timeBased(false),
+ _rect(rect) {
+ }
+
+ virtual ~TransitionEffect() {}
+
+ bool isTimeBased() const { return _timeBased; }
+
+ virtual void drawFrame(uint32 elapsed) = 0;
+
+protected:
+ Common::Rect makeDirectionalInitalArea() const {
+ Common::Rect initialArea = _rect;
+ switch (_type) {
+ case kRivenTransitionWipeLeft:
+ case kRivenTransitionPanLeft:
+ initialArea.left = _rect.right;
+ break;
+ case kRivenTransitionWipeRight:
+ case kRivenTransitionPanRight:
+ initialArea.right = _rect.left;
+ break;
+ case kRivenTransitionWipeUp:
+ case kRivenTransitionPanUp:
+ initialArea.top = _rect.bottom;
+ break;
+ case kRivenTransitionWipeDown:
+ case kRivenTransitionPanDown:
+ initialArea.bottom = _rect.top;
+ break;
+ default:
+ error("Unhandled transition type: %d", _type);
+ }
+
+ return initialArea;
+ }
+
+ OSystem *_system;
+
+ RivenTransition _type;
+ uint _duration;
+ Common::Rect _rect;
+ bool _timeBased;
+
+ Graphics::Surface *_mainScreen;
+ Graphics::Surface *_effectScreen;
+};
+
+class TransitionEffectWipe : public TransitionEffect {
+public:
+ TransitionEffectWipe(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
+ RivenTransition type, uint duration, const Common::Rect &rect) :
+ TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
+
+ _timeBased = true;
+ _lastCopyArea = makeDirectionalInitalArea();
+ }
+
+ virtual void drawFrame(uint32 elapsed) override {
+ Common::Rect copyArea;
+ switch (_type) {
+ case kRivenTransitionWipeLeft:
+ copyArea.top = _lastCopyArea.top;
+ copyArea.bottom = _lastCopyArea.bottom;
+ copyArea.right = _lastCopyArea.left;
+ copyArea.left = _rect.width() - elapsed * _rect.width() / _duration;
+ break;
+ case kRivenTransitionWipeRight:
+ copyArea.top = _lastCopyArea.top;
+ copyArea.bottom = _lastCopyArea.bottom;
+ copyArea.left = _lastCopyArea.right;
+ copyArea.right = elapsed * _rect.width() / _duration;
+ break;
+ case kRivenTransitionWipeUp:
+ copyArea.left = _lastCopyArea.left;
+ copyArea.right = _lastCopyArea.right;
+ copyArea.bottom = _lastCopyArea.top;
+ copyArea.top = _rect.height() - elapsed * _rect.height() / _duration;
+ break;
+ case kRivenTransitionWipeDown:
+ copyArea.left = _lastCopyArea.left;
+ copyArea.right = _lastCopyArea.right;
+ copyArea.top = _lastCopyArea.bottom;
+ copyArea.bottom = elapsed * _rect.height() / _duration;
+ break;
+ default:
+ error("Unhandled transition type: %d", _type);
+ }
+
+ _lastCopyArea = copyArea;
+
+ if (copyArea.isEmpty()) {
+ // Nothing to draw
+ return;
+ }
+
+ _effectScreen->copyRectToSurface(*_mainScreen, copyArea.left, copyArea.top, copyArea);
+ _system->copyRectToScreen(_effectScreen->getBasePtr(copyArea.left, copyArea.top), _effectScreen->pitch,
+ copyArea.left, copyArea.top, copyArea.width(), copyArea.height());
+ }
+
+private:
+ Common::Rect _lastCopyArea;
+};
+
+class TransitionEffectPan : public TransitionEffect {
+public:
+ TransitionEffectPan(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
+ RivenTransition type, uint duration, const Common::Rect &rect) :
+ TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
+
+ _timeBased = true;
+ _initialArea = makeDirectionalInitalArea();
+ }
+
+ virtual void drawFrame(uint32 elapsed) override {
+ Common::Rect newArea;
+ switch (_type) {
+ case kRivenTransitionPanLeft:
+ newArea.top = _initialArea.top;
+ newArea.bottom = _initialArea.bottom;
+ newArea.right = _initialArea.right;
+ newArea.left = _rect.width() - elapsed * _rect.width() / _duration;
+ break;
+ case kRivenTransitionPanRight:
+ newArea.top = _initialArea.top;
+ newArea.bottom = _initialArea.bottom;
+ newArea.left = _initialArea.left;
+ newArea.right = elapsed * _rect.width() / _duration;
+ break;
+ case kRivenTransitionPanUp:
+ newArea.left = _initialArea.left;
+ newArea.right = _initialArea.right;
+ newArea.bottom = _initialArea.bottom;
+ newArea.top = _rect.height() - elapsed * _rect.height() / _duration;
+ break;
+ case kRivenTransitionPanDown:
+ newArea.left = _initialArea.left;
+ newArea.right = _initialArea.right;
+ newArea.top = _initialArea.top;
+ newArea.bottom = elapsed * _rect.height() / _duration;
+ break;
+ default:
+ error("Unhandled transition type: %d", _type);
+ }
+
+ if (newArea.isEmpty()) {
+ // Nothing to draw
+ return;
+ }
+
+ Common::Rect oldArea = Common::Rect(
+ newArea.right != _rect.right ? _rect.left + newArea.width() : _rect.left,
+ newArea.bottom != _rect.bottom ? _rect.top + newArea.height() : _rect.top,
+ newArea.left != _rect.left ? _rect.right - newArea.width() : _rect.right,
+ newArea.top != _rect.top ? _rect.bottom - newArea.height() : _rect.bottom
+ );
+
+ int oldX = newArea.left != _rect.left ? _rect.left + newArea.width() : _rect.left;
+ int oldY = newArea.top != _rect.top ? _rect.top + newArea.height() : _rect.top;
+ _system->copyRectToScreen(_effectScreen->getBasePtr(oldX, oldY), _effectScreen->pitch,
+ oldArea.left, oldArea.top, oldArea.width(), oldArea.height());
+
+ int newX = newArea.right != _rect.right ? _rect.left + oldArea.width() : _rect.left;
+ int newY = newArea.bottom != _rect.bottom ? _rect.top + oldArea.height() : _rect.top;
+ _system->copyRectToScreen(_mainScreen->getBasePtr(newX, newY), _mainScreen->pitch,
+ newArea.left, newArea.top, newArea.width(), newArea.height());
+
+ if (newArea == _rect) {
+ _effectScreen->copyRectToSurface(*_mainScreen, _rect.left, _rect.top, _rect);
+ }
+ }
+
+private:
+ Common::Rect _initialArea;
+};
+
+class TransitionEffectBlend : public TransitionEffect {
+public:
+ TransitionEffectBlend(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
+ RivenTransition type, uint duration, const Common::Rect &rect) :
+ TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
+
+ _timeBased = false;
+ }
+
+ virtual void drawFrame(uint32 elapsed) override {
+ assert(_effectScreen->format == _mainScreen->format);
+ assert(_effectScreen->format == _system->getScreenFormat());
+
+ if (elapsed == _duration) {
+ _effectScreen->copyRectToSurface(*_mainScreen, 0, 0, Common::Rect(_mainScreen->w, _mainScreen->h));
+ _system->copyRectToScreen(_effectScreen->getBasePtr(0, 0), _effectScreen->pitch, 0, 0, _effectScreen->w, _effectScreen->h);
+ } else {
+ Graphics::Surface *screen = _system->lockScreen();
+
+ uint alpha = elapsed * 255 / _duration;
+ for (uint y = 0; y < _mainScreen->h; y++) {
+ uint16 *src1 = (uint16 *) _mainScreen->getBasePtr(0, y);
+ uint16 *src2 = (uint16 *) _effectScreen->getBasePtr(0, y);
+ uint16 *dst = (uint16 *) screen->getBasePtr(0, y);
+ for (uint x = 0; x < _mainScreen->w; x++) {
+ uint8 r1, g1, b1, r2, g2, b2;
+ Graphics::colorToRGB< Graphics::ColorMasks<565> >(*src1++, r1, g1, b1);
+ Graphics::colorToRGB< Graphics::ColorMasks<565> >(*src2++, r2, g2, b2);
+
+ uint r = r1 * alpha + r2 * (255 - alpha);
+ uint g = g1 * alpha + g2 * (255 - alpha);
+ uint b = b1 * alpha + b2 * (255 - alpha);
+
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ *dst++ = (uint16) Graphics::RGBToColor< Graphics::ColorMasks<565> >(r, g, b);
+ }
+ }
+
+ _system->unlockScreen();
+ }
+ }
+};
+
RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm) {
_bitmapDecoder = new MohawkBitmap();
// Restrict ourselves to a single pixel format to simplify the effects implementation
- _pixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
+ _pixelFormat = Graphics::createPixelFormat<565>();
initGraphics(608, 436, true, &_pixelFormat);
// The actual game graphics only take up the first 392 rows. The inventory
@@ -55,7 +288,7 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
_creditsImage = 302;
_creditsPos = 0;
- _transitionSpeed = 0;
+ _transitionMode = kRivenTransitionModeFastest;
_fliesEffect = nullptr;
}
@@ -93,16 +326,20 @@ void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uin
void RivenGraphics::updateScreen(Common::Rect updateRect) {
if (_dirtyScreen) {
// Copy to screen if there's no transition. Otherwise transition. ;)
- if (_scheduledTransition < 0) {
+ if (_scheduledTransition == kRivenTransitionNone
+ || _transitionMode == kRivenTransitionModeDisabled) {
// mainScreen -> effectScreen -> systemScreen
_effectScreen->copyRectToSurface(*_mainScreen, updateRect.left, updateRect.top, updateRect);
_vm->_system->copyRectToScreen(_effectScreen->getBasePtr(updateRect.left, updateRect.top), _effectScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height());
+
+ // Finally, update the screen.
+ _vm->_system->updateScreen();
+
+ _scheduledTransition = kRivenTransitionNone;
} else {
runScheduledTransition();
}
- // Finally, update the screen.
- _vm->_system->updateScreen();
_dirtyScreen = false;
}
}
@@ -148,10 +385,10 @@ void RivenGraphics::clearWaterEffects() {
_waterEffects.clear();
}
-bool RivenGraphics::runScheduledWaterEffects() {
+void RivenGraphics::runScheduledWaterEffects() {
// Don't run the effect if it's disabled
if (_vm->_vars["waterenabled"] == 0)
- return false;
+ return;
Graphics::Surface *screen = NULL;
@@ -195,50 +432,92 @@ bool RivenGraphics::runScheduledWaterEffects() {
// Unlock the screen if it has been locked and return true to update the screen
if (screen) {
_vm->_system->unlockScreen();
- return true;
}
+}
- return false;
+void RivenGraphics::setTransitionMode(RivenTransitionMode mode) {
+ _transitionMode = mode;
+ switch (_transitionMode) {
+ case kRivenTransitionModeFastest:
+ _transitionFrames = 8;
+ _transitionDuration = 300;
+ break;
+ case kRivenTransitionModeNormal:
+ _transitionFrames = 16;
+ _transitionDuration = 500;
+ break;
+ case kRivenTransitionModeBest:
+ _transitionFrames = 32;
+ _transitionDuration = 700;
+ break;
+ case kRivenTransitionModeDisabled:
+ _transitionFrames = 0;
+ _transitionDuration = 0;
+ break;
+ default:
+ error("Unknown transition mode %d", _transitionMode);
+ }
}
-void RivenGraphics::scheduleTransition(RivenTransition id, Common::Rect rect) {
+void RivenGraphics::scheduleTransition(RivenTransition id, const Common::Rect &rect) {
_scheduledTransition = id;
_transitionRect = rect;
}
void RivenGraphics::runScheduledTransition() {
- if (_scheduledTransition < 0) // No transition is scheduled
+ if (_scheduledTransition == kRivenTransitionNone)
return;
- // TODO: There's a lot to be done here...
-
// Note: Transitions 0-11 are actual transitions, but none are used in-game.
// There's no point in implementing them if they're not used. These extra
// transitions were found by hacking scripts.
+ TransitionEffect *effect = nullptr;
switch (_scheduledTransition) {
- case kRivenTransitionWipeLeft:
- case kRivenTransitionWipeRight:
- case kRivenTransitionWipeUp:
- case kRivenTransitionWipeDown:
- case kRivenTransitionPanLeft:
- case kRivenTransitionPanRight:
- case kRivenTransitionPanUp:
- case kRivenTransitionPanDown:
- case kRivenTransitionBlend:
- case kRivenTransitionBlend2: // (tspit CARD 155)
- break;
- default:
- if (_scheduledTransition >= 4 && _scheduledTransition <= 11)
- error("Found unused transition %d", _scheduledTransition);
- else
- error("Found unknown transition %d", _scheduledTransition);
- }
-
- // For now, just copy the image to screen without doing any transition.
- _effectScreen->copyRectToSurface(*_mainScreen, 0, 0, Common::Rect(_mainScreen->w, _mainScreen->h));
- _vm->_system->copyRectToScreen(_effectScreen->getBasePtr(0, 0), _effectScreen->pitch, 0, 0, _effectScreen->w, _effectScreen->h);
- _vm->_system->updateScreen();
+ case kRivenTransitionWipeLeft:
+ case kRivenTransitionWipeRight:
+ case kRivenTransitionWipeUp:
+ case kRivenTransitionWipeDown: {
+ effect = new TransitionEffectWipe(_vm->_system, _mainScreen, _effectScreen,
+ _scheduledTransition, _transitionDuration, _transitionRect);
+ break;
+ }
+ case kRivenTransitionPanLeft:
+ case kRivenTransitionPanRight:
+ case kRivenTransitionPanUp:
+ case kRivenTransitionPanDown: {
+ effect = new TransitionEffectPan(_vm->_system, _mainScreen, _effectScreen,
+ _scheduledTransition, _transitionDuration, _transitionRect);
+ break;
+ }
+ case kRivenTransitionBlend:
+ case kRivenTransitionBlend2: // (tspit CARD 155)
+ effect = new TransitionEffectBlend(_vm->_system, _mainScreen, _effectScreen,
+ _scheduledTransition, _transitionFrames, _transitionRect);
+ break;
+ default:
+ error("Unhandled transition type: %d", _scheduledTransition);
+ }
+
+ if (effect->isTimeBased()) {
+ uint32 startTime = _vm->_system->getMillis();
+ uint32 timeElapsed = 0;
+ while (timeElapsed < _transitionDuration && !_vm->shouldQuit()) {
+ effect->drawFrame(timeElapsed);
+
+ _vm->doFrame();
+ timeElapsed = _vm->_system->getMillis() - startTime;
+ }
+
+ effect->drawFrame(_transitionDuration);
+ } else {
+ for (uint frame = 1; frame <= _transitionFrames && !_vm->shouldQuit(); frame++) {
+ effect->drawFrame(frame);
+
+ _vm->doFrame();
+ }
+ }
+ delete effect;
_scheduledTransition = kRivenTransitionNone; // Clear scheduled transition
}
@@ -249,7 +528,7 @@ void RivenGraphics::clearMainScreen() {
void RivenGraphics::fadeToBlack() {
// The transition speed is forced to best here
- setTransitionSpeed(kRivenTransitionSpeedBest);
+ setTransitionMode(kRivenTransitionModeBest);
scheduleTransition(kRivenTransitionBlend);
clearMainScreen();
runScheduledTransition();
diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h
index 18b1e1dcd3..565a51d08e 100644
--- a/engines/mohawk/riven_graphics.h
+++ b/engines/mohawk/riven_graphics.h
@@ -44,6 +44,13 @@ enum RivenTransition {
kRivenTransitionBlend2 = 17
};
+enum RivenTransitionMode {
+ kRivenTransitionModeDisabled = 5000,
+ kRivenTransitionModeFastest = 5001,
+ kRivenTransitionModeNormal = 5002,
+ kRivenTransitionModeBest = 5003
+};
+
class RivenGraphics : public GraphicsManager {
public:
RivenGraphics(MohawkEngine_Riven *vm);
@@ -66,7 +73,7 @@ public:
// Water Effect
void scheduleWaterEffect(uint16);
void clearWaterEffects();
- bool runScheduledWaterEffects();
+ void runScheduledWaterEffects();
// Flies Effect
void setFliesEffect(uint16 count, bool fireflies);
@@ -74,10 +81,10 @@ public:
void runFliesEffect();
// Transitions
- void scheduleTransition(RivenTransition id, Common::Rect rect = Common::Rect(0, 0, 608, 392));
+ void scheduleTransition(RivenTransition id, const Common::Rect &rect = Common::Rect(0, 0, 608, 392));
void runScheduledTransition();
void fadeToBlack();
- void setTransitionSpeed(uint32 speed) { _transitionSpeed = speed; }
+ void setTransitionMode(RivenTransitionMode mode);
// Credits
void beginCredits();
@@ -114,7 +121,9 @@ private:
// Transitions
RivenTransition _scheduledTransition;
Common::Rect _transitionRect;
- uint32 _transitionSpeed;
+ RivenTransitionMode _transitionMode;
+ uint _transitionFrames;
+ uint _transitionDuration;
// Screen Related
Graphics::Surface *_mainScreen;
diff --git a/engines/mohawk/riven_vars.cpp b/engines/mohawk/riven_vars.cpp
index 7d17562aa2..bbc39c248e 100644
--- a/engines/mohawk/riven_vars.cpp
+++ b/engines/mohawk/riven_vars.cpp
@@ -24,6 +24,7 @@
#include "mohawk/riven.h"
#include "mohawk/riven_stack.h"
+#include "mohawk/riven_graphics.h"
namespace Mohawk {
@@ -305,7 +306,7 @@ void MohawkEngine_Riven::initVars() {
_vars["bmagcar"] = 1;
_vars["gnmagcar"] = 1;
_vars["omusicplayer"] = 1;
- _vars["transitionmode"] = kRivenTransitionSpeedFastest;
+ _vars["transitionmode"] = kRivenTransitionModeFastest;
_vars["tdomeelev"] = 1;
// Randomize the telescope combination