[Scummvm-git-logs] scummvm master -> 69c6470c6a650fbe83b1c3c4dabd92508c08161f

elasota noreply at scummvm.org
Fri Jul 15 05:39:25 UTC 2022


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
69c6470c6a MTROPOLIS: Add scene transition effect support


Commit: 69c6470c6a650fbe83b1c3c4dabd92508c08161f
    https://github.com/scummvm/scummvm/commit/69c6470c6a650fbe83b1c3c4dabd92508c08161f
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-15T01:38:31-04:00

Commit Message:
MTROPOLIS: Add scene transition effect support

Changed paths:
    engines/mtropolis/detection.cpp
    engines/mtropolis/detection_tables.h
    engines/mtropolis/hacks.cpp
    engines/mtropolis/hacks.h
    engines/mtropolis/modifiers.cpp
    engines/mtropolis/modifiers.h
    engines/mtropolis/mtropolis.cpp
    engines/mtropolis/plugin/obsidian.h
    engines/mtropolis/plugin/standard.cpp
    engines/mtropolis/plugin/standard.h
    engines/mtropolis/render.cpp
    engines/mtropolis/render.h
    engines/mtropolis/runtime.cpp
    engines/mtropolis/runtime.h


diff --git a/engines/mtropolis/detection.cpp b/engines/mtropolis/detection.cpp
index 8280f2867fa..3e3a808b83d 100644
--- a/engines/mtropolis/detection.cpp
+++ b/engines/mtropolis/detection.cpp
@@ -70,6 +70,17 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_ENABLE_SHORT_TRANSITIONS,
+		{
+			_s("Enable short transitions"),
+			_s("Enables transitions that are set to maximum rate instead of skipping them"),
+			"mtropolis_mod_minimum_transition_duration",
+			false,
+			0,
+			0
+		}
+	},
 	{
 		GAMEOPTION_LAUNCH_DEBUG,
 		{
@@ -107,7 +118,7 @@ static const char *directoryGlobs[] = {
 class MTropolisMetaEngineDetection : public AdvancedMetaEngineDetection {
 public:
 	MTropolisMetaEngineDetection() : AdvancedMetaEngineDetection(MTropolis::gameDescriptions, sizeof(MTropolis::MTropolisGameDescription), mTropolisGames, MTropolis::optionsList) {
-		_guiOptions = GUIO3(GAMEOPTION_DYNAMIC_MIDI, GAMEOPTION_LAUNCH_DEBUG, GAMEOPTION_LAUNCH_BREAK);
+		_guiOptions = GUIO4(GAMEOPTION_DYNAMIC_MIDI, GAMEOPTION_LAUNCH_DEBUG, GAMEOPTION_LAUNCH_BREAK, GAMEOPTION_ENABLE_SHORT_TRANSITIONS);
 		_maxScanDepth = 3;
 		_directoryGlobs = directoryGlobs;
 	}
diff --git a/engines/mtropolis/detection_tables.h b/engines/mtropolis/detection_tables.h
index 40a63564187..0cd0eeed3e0 100644
--- a/engines/mtropolis/detection_tables.h
+++ b/engines/mtropolis/detection_tables.h
@@ -26,11 +26,12 @@
 
 #include "mtropolis/detection.h"
 
-#define GAMEOPTION_WIDESCREEN_MOD GUIO_GAMEOPTIONS1
-#define GAMEOPTION_DYNAMIC_MIDI   GUIO_GAMEOPTIONS2
-#define GAMEOPTION_LAUNCH_DEBUG   GUIO_GAMEOPTIONS3
-#define GAMEOPTION_LAUNCH_BREAK   GUIO_GAMEOPTIONS4
-#define GAMEOPTION_AUTO_SAVE      GUIO_GAMEOPTIONS5
+#define GAMEOPTION_WIDESCREEN_MOD			GUIO_GAMEOPTIONS1
+#define GAMEOPTION_DYNAMIC_MIDI				GUIO_GAMEOPTIONS2
+#define GAMEOPTION_LAUNCH_DEBUG				GUIO_GAMEOPTIONS3
+#define GAMEOPTION_LAUNCH_BREAK				GUIO_GAMEOPTIONS4
+#define GAMEOPTION_AUTO_SAVE				GUIO_GAMEOPTIONS5
+#define GAMEOPTION_ENABLE_SHORT_TRANSITIONS GUIO_GAMEOPTIONS6
 
 namespace MTropolis {
 
diff --git a/engines/mtropolis/hacks.cpp b/engines/mtropolis/hacks.cpp
index 31aa28bf538..5d57e6a55ff 100644
--- a/engines/mtropolis/hacks.cpp
+++ b/engines/mtropolis/hacks.cpp
@@ -34,6 +34,7 @@ namespace MTropolis {
 Hacks::Hacks() {
 	ignoreMismatchedProjectNameInObjectLookups = false;
 	midiVolumeScale = 256;
+	minTransitionDuration = 0;
 }
 
 Hacks::~Hacks() {
diff --git a/engines/mtropolis/hacks.h b/engines/mtropolis/hacks.h
index 110a55913fe..804e204ce60 100644
--- a/engines/mtropolis/hacks.h
+++ b/engines/mtropolis/hacks.h
@@ -49,6 +49,8 @@ struct Hacks {
 	bool ignoreMismatchedProjectNameInObjectLookups;
 	uint midiVolumeScale;	// 256 = 1.0
 
+	uint32 minTransitionDuration;
+
 	Common::Point reportDisplaySize;	// If X or Y is non-zero, report this as the display size
 	Common::Point mainWindowOffset;		// Coordinate offset of the main window
 
diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index d3dfa5e83ed..c4391c1f554 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -846,6 +846,39 @@ bool SceneTransitionModifier::load(ModifierLoaderContext &context, const Data::S
 	return true;
 }
 
+bool SceneTransitionModifier::respondsToEvent(const Event &evt) const {
+	return _enableWhen.respondsTo(evt) || _disableWhen.respondsTo(evt);
+}
+
+VThreadState SceneTransitionModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
+	if (_enableWhen.respondsTo(msg->getEvent())) {
+		SceneTransitionEffect effect;
+
+		// For some reason, these vary
+		uint32 timeDivisor = 100;
+		switch (effect._transitionType) {
+		case SceneTransitionTypes::kRandomDissolve:
+			timeDivisor = 50;
+			break;
+		case SceneTransitionTypes::kFade:
+			timeDivisor = 25;
+			break;
+		default:
+			break;
+		}
+
+		effect._duration = _duration / timeDivisor;
+		effect._steps = _steps;
+		effect._transitionDirection = _transitionDirection;
+		effect._transitionType = _transitionType;
+		runtime->setSceneTransitionEffect(true, &effect);
+	}
+	if (_disableWhen.respondsTo(msg->getEvent()))
+		runtime->setSceneTransitionEffect(true, nullptr);
+
+	return kVThreadReturn;
+}
+
 Common::SharedPtr<Modifier> SceneTransitionModifier::shallowClone() const {
 	return Common::SharedPtr<Modifier>(new SceneTransitionModifier(*this));
 }
diff --git a/engines/mtropolis/modifiers.h b/engines/mtropolis/modifiers.h
index 85101ba75fb..aebbd1ea091 100644
--- a/engines/mtropolis/modifiers.h
+++ b/engines/mtropolis/modifiers.h
@@ -320,6 +320,7 @@ public:
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Vector Modifier"; }
+	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
 #endif
 
 private:
@@ -348,6 +349,9 @@ class SceneTransitionModifier : public Modifier {
 public:
 	bool load(ModifierLoaderContext &context, const Data::SceneTransitionModifier &data);
 
+	bool respondsToEvent(const Event &evt) const override;
+	VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
+
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Scene Transition Modifier"; }
 #endif
diff --git a/engines/mtropolis/mtropolis.cpp b/engines/mtropolis/mtropolis.cpp
index aca9a3b09e8..70d318f6b86 100644
--- a/engines/mtropolis/mtropolis.cpp
+++ b/engines/mtropolis/mtropolis.cpp
@@ -132,6 +132,9 @@ Common::Error MTropolisEngine::run() {
 		}
 	}
 
+	if (ConfMan.getBool("mtropolis_mod_minimum_transition_duration"))
+		_runtime->getHacks().minTransitionDuration = 75;
+
 	_runtime->queueProject(projectDesc);
 
 	// Figure out pixel formats
diff --git a/engines/mtropolis/plugin/obsidian.h b/engines/mtropolis/plugin/obsidian.h
index a74982e9ddb..3c539247b08 100644
--- a/engines/mtropolis/plugin/obsidian.h
+++ b/engines/mtropolis/plugin/obsidian.h
@@ -83,6 +83,7 @@ public:
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "TextWork Modifier"; }
+	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
 #endif
 
 private:
@@ -111,6 +112,7 @@ public:
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Dictionary Modifier"; }
+	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
 #endif
 
 private:
@@ -139,6 +141,7 @@ public:
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "WordMixer Modifier"; }
+	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
 #endif
 
 private:
@@ -167,6 +170,7 @@ public:
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Xor Mod Modifier"; }
+	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
 #endif
 
 private:
@@ -190,6 +194,7 @@ public:
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Xor Check Modifier"; }
+	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
 #endif
 
 private:
diff --git a/engines/mtropolis/plugin/standard.cpp b/engines/mtropolis/plugin/standard.cpp
index 137d4057748..b8fff035c0d 100644
--- a/engines/mtropolis/plugin/standard.cpp
+++ b/engines/mtropolis/plugin/standard.cpp
@@ -1605,6 +1605,34 @@ bool STransCtModifier::load(const PlugInModifierLoaderContext &context, const Da
 	return true;
 }
 
+bool STransCtModifier::respondsToEvent(const Event &evt) const {
+	return _enableWhen.respondsTo(evt) || _disableWhen.respondsTo(evt);
+}
+
+VThreadState STransCtModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
+	if (_enableWhen.respondsTo(msg->getEvent())) {
+		SceneTransitionEffect effect;
+		effect._duration = _duration / 10;
+		effect._steps = _steps;
+
+		if (SceneTransitionTypes::loadFromData(effect._transitionType, _transitionType) && SceneTransitionDirections::loadFromData(effect._transitionDirection, _transitionDirection)) {
+			// Weird quirk: Duration doesn't seem to affect duration properly for wipe transitions.
+			// In Obsidian, this mostly effects 180-degree turns.
+			// Good place to test this is in the corners of the Bureau library.
+			if (effect._transitionType == SceneTransitionTypes::kWipe && effect._duration < 1000)
+				effect._duration = 1000;
+
+			runtime->setSceneTransitionEffect(false, &effect);
+		} else {
+			warning("Source-scene transition had invalid data");
+		}
+	}
+	if (_disableWhen.respondsTo(msg->getEvent()))
+		runtime->setSceneTransitionEffect(false, nullptr);
+
+	return kVThreadReturn;
+}
+
 bool STransCtModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
 	if (attrib == "rate") {
 		if (_duration <= (kMaxDuration / 100))
diff --git a/engines/mtropolis/plugin/standard.h b/engines/mtropolis/plugin/standard.h
index 989541b4a3d..c5481eaae3e 100644
--- a/engines/mtropolis/plugin/standard.h
+++ b/engines/mtropolis/plugin/standard.h
@@ -73,11 +73,15 @@ public:
 
 	bool load(const PlugInModifierLoaderContext &context, const Data::Standard::STransCtModifier &data);
 
+	bool respondsToEvent(const Event &evt) const override;
+	VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
+
 	bool readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) override;
 	MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "STransCt Scene Transition Modifier"; }
+	SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
 #endif
 
 private:
diff --git a/engines/mtropolis/render.cpp b/engines/mtropolis/render.cpp
index 175717b9d18..005185d8be9 100644
--- a/engines/mtropolis/render.cpp
+++ b/engines/mtropolis/render.cpp
@@ -303,6 +303,215 @@ void renderProject(Runtime *runtime, Window *mainWindow) {
 	runtime->clearSceneGraphDirty();
 }
 
+class DissolveOrderedDitherPatternGenerator {
+public:
+	DissolveOrderedDitherPatternGenerator();
+
+	uint8 getNext();
+	void nextLine();
+
+private:
+	uint8 _ditherPattern[16][16];
+
+	uint16 _x;
+	uint16 _y;
+};
+
+DissolveOrderedDitherPatternGenerator::DissolveOrderedDitherPatternGenerator() {
+	OrderedDitherGenerator<uint8, 16>::generateOrderedDither(_ditherPattern);
+}
+
+inline uint8 DissolveOrderedDitherPatternGenerator::getNext() {
+	uint8 result = _ditherPattern[_y][_x];
+
+	uint16 newX = _x + 1;
+	if (newX == 16)
+		newX = 0;
+	_x = newX;
+
+	return result;
+}
+
+inline void DissolveOrderedDitherPatternGenerator::nextLine() {
+	_x = 0;
+	uint16 newY = _y + 1;
+	if (newY == 16)
+		newY = 0;
+	_y = newY;
+}
+
+class DissolveOrderedDitherRandomGenerator {
+public:
+	DissolveOrderedDitherRandomGenerator();
+
+	uint8 getNext();
+	void nextLine();
+
+private:
+	uint32 _lcgState;
+};
+
+
+DissolveOrderedDitherRandomGenerator::DissolveOrderedDitherRandomGenerator() : _lcgState(13) {
+}
+
+inline uint8 DissolveOrderedDitherRandomGenerator::getNext() {
+	_lcgState = ((_lcgState * 1103515245u) + 12345u) & 0x7fffffffu;
+	return (_lcgState >> 16) & 0xff;
+}
+
+inline void DissolveOrderedDitherRandomGenerator::nextLine() {
+}
+
+template<class TPixel, class TGenerator>
+static void runDissolveTransitionWithType(Graphics::ManagedSurface &surface, const Graphics::ManagedSurface &oldFrame, const Graphics::ManagedSurface &newFrame, uint8 breakpoint) {
+	TGenerator generator;
+
+	assert(surface.format.bytesPerPixel == oldFrame.format.bytesPerPixel);
+	assert(surface.format.bytesPerPixel == newFrame.format.bytesPerPixel);
+
+	uint16 w = surface.w;
+	uint16 h = surface.h;
+
+	for (uint y = 0; y < h; y++) {
+		TPixel *destRow = static_cast<TPixel *>(surface.getBasePtr(0, y));
+		const TPixel *oldRow = static_cast<const TPixel *>(oldFrame.getBasePtr(0, y));
+		const TPixel *newRow = static_cast<const TPixel *>(newFrame.getBasePtr(0, y));
+
+		for (uint x = 0; x < w; x++) {
+			if (generator.getNext() <= breakpoint)
+				destRow[x] = newRow[x];
+			else
+				destRow[x] = oldRow[x];
+		}
+
+		generator.nextLine();
+	}
+}
+
+template<class TGenerator>
+static void runDissolveTransition(Graphics::ManagedSurface &surface, const Graphics::ManagedSurface &oldFrame, const Graphics::ManagedSurface &newFrame, uint8 breakpoint) {
+	switch (surface.format.bytesPerPixel) {
+	case 1:
+		runDissolveTransitionWithType<uint8, TGenerator>(surface, oldFrame, newFrame, breakpoint);
+		break;
+	case 2:
+		runDissolveTransitionWithType<uint16, TGenerator>(surface, oldFrame, newFrame, breakpoint);
+		break;
+	case 4:
+		runDissolveTransitionWithType<uint32, TGenerator>(surface, oldFrame, newFrame, breakpoint);
+		break;
+	default:
+		assert(false);
+		break;
+	}
+}
+
+static void safeCopyRectToSurface(Graphics::ManagedSurface &surface, const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect subRect) {
+	if (subRect.width() == 0 || subRect.height() == 0)
+		return;
+
+	surface.copyRectToSurface(srcSurface, destX, destY, subRect);
+}
+
+void renderSceneTransition(Runtime *runtime, Window *mainWindow, const SceneTransitionEffect &effect, uint32 startTime, uint32 endTime, uint32 currentTime, const Graphics::ManagedSurface &oldFrame, const Graphics::ManagedSurface &newFrame) {
+	Graphics::ManagedSurface &surface = *mainWindow->getSurface();
+
+	assert(endTime > startTime);
+
+	uint32 duration = endTime - startTime;
+
+	uint16 w = surface.w;
+	uint16 h = surface.h;
+
+	if (effect._transitionType == SceneTransitionTypes::kSlide || effect._transitionType == SceneTransitionTypes::kWipe)
+		safeCopyRectToSurface(surface, oldFrame, 0, 0, Common::Rect(0, 0, w, h));
+
+	switch (effect._transitionType) {
+	case SceneTransitionTypes::kPatternDissolve:
+		runDissolveTransition<DissolveOrderedDitherPatternGenerator>(surface, oldFrame, newFrame, (currentTime - startTime) * 255 / duration);
+		break;
+	case SceneTransitionTypes::kRandomDissolve:
+		runDissolveTransition<DissolveOrderedDitherRandomGenerator>(surface, oldFrame, newFrame, (currentTime - startTime) * 255 / duration);
+		break;
+	case SceneTransitionTypes::kFade:
+		// Fade transitions fade to black and then fade from black in the new scene
+		warning("Fade transitions are not implemented");
+		break;
+	case SceneTransitionTypes::kSlide:
+	case SceneTransitionTypes::kPush: {
+			uint32 directionalOffset = 0;
+			switch (effect._transitionDirection) {
+			case SceneTransitionDirections::kUp:
+				directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(h) / duration;
+
+				if (effect._transitionType == SceneTransitionTypes::kPush)
+					safeCopyRectToSurface(surface, oldFrame, 0, 0, Common::Rect(0, directionalOffset, w, h));
+
+				safeCopyRectToSurface(surface, newFrame, 0, h - directionalOffset, Common::Rect(0, 0, w, directionalOffset));
+				break;
+			case SceneTransitionDirections::kDown:
+				directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(h) / duration;
+
+				if (effect._transitionType == SceneTransitionTypes::kPush)
+					safeCopyRectToSurface(surface, oldFrame, 0, directionalOffset, Common::Rect(0, 0, w, h - directionalOffset));
+
+				safeCopyRectToSurface(surface, newFrame, 0, 0, Common::Rect(0, h - directionalOffset, w, h));
+				break;
+			case SceneTransitionDirections::kLeft:
+				directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(w) / duration;
+
+				if (effect._transitionType == SceneTransitionTypes::kPush)
+					safeCopyRectToSurface(surface, oldFrame, 0, 0, Common::Rect(directionalOffset, 0, w, h));
+
+				safeCopyRectToSurface(surface, newFrame, w - directionalOffset, 0, Common::Rect(0, 0, directionalOffset, h));
+				break;
+			case SceneTransitionDirections::kRight:
+				directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(w) / duration;
+
+				if (effect._transitionType == SceneTransitionTypes::kPush)
+					safeCopyRectToSurface(surface, oldFrame, directionalOffset, 0, Common::Rect(0, 0, w - directionalOffset, h));
+
+				safeCopyRectToSurface(surface, newFrame, 0, 0, Common::Rect(w - directionalOffset, 0, w, h));
+				break;
+			default:
+				assert(false);
+				break;
+			}
+		} break;
+	case SceneTransitionTypes::kZoom:
+		warning("Zoom transitions are not implemented");
+		break;
+	case SceneTransitionTypes::kWipe:{
+			uint32 directionalOffset = 0;
+			switch (effect._transitionDirection) {
+			case SceneTransitionDirections::kUp:
+				directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(h) / duration;
+				safeCopyRectToSurface(surface, newFrame, 0, h - directionalOffset, Common::Rect(0, h - directionalOffset, w, h));
+				break;
+			case SceneTransitionDirections::kDown:
+				directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(h) / duration;
+				safeCopyRectToSurface(surface, newFrame, 0, 0, Common::Rect(0, 0, w, directionalOffset));
+				break;
+			case SceneTransitionDirections::kLeft:
+				directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(w) / duration;
+				safeCopyRectToSurface(surface, newFrame, w - directionalOffset, 0, Common::Rect(w - directionalOffset, 0, w, h));
+				break;
+			case SceneTransitionDirections::kRight:
+				directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(w) / duration;
+				safeCopyRectToSurface(surface, newFrame, 0, 0, Common::Rect(0, 0, directionalOffset, h));
+				break;
+			default:
+				assert(false);
+				break;
+			}
+		} break;
+	default:
+		assert(false);
+		break;
+	}
+}
+
 void convert32To16(Graphics::Surface &destSurface, const Graphics::Surface &srcSurface) {
 	const Graphics::PixelFormat srcFmt = srcSurface.format;
 	const Graphics::PixelFormat destFmt = destSurface.format;
diff --git a/engines/mtropolis/render.h b/engines/mtropolis/render.h
index 17e847a4466..953ab150562 100644
--- a/engines/mtropolis/render.h
+++ b/engines/mtropolis/render.h
@@ -40,6 +40,7 @@ namespace MTropolis {
 class CursorGraphic;
 class Runtime;
 class Project;
+struct SceneTransitionEffect;
 
 enum TextAlignment {
 	kTextAlignmentLeft,
@@ -133,6 +134,8 @@ namespace Render {
 
 uint32 resolveRGB(uint8 r, uint8 g, uint8 b, const Graphics::PixelFormat &fmt);
 void renderProject(Runtime *runtime, Window *mainWindow);
+void renderSceneTransition(Runtime *runtime, Window *mainWindow, const SceneTransitionEffect &effect, uint32 startTime, uint32 endTime, uint32 currentTime, const Graphics::ManagedSurface &oldFrame, const Graphics::ManagedSurface &newFrame);
+
 void convert32To16(Graphics::Surface &destSurface, const Graphics::Surface &srcSurface);
 void convert16To32(Graphics::Surface &destSurface, const Graphics::Surface &srcSurface);
 
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index c9688035372..32c11152edd 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -259,7 +259,7 @@ size_t caseInsensitiveFind(const Common::String &strToSearch, const Common::Stri
 	return Common::String::npos;
 }
 
-bool SceneTransitionTypes::loadFromData(SceneTransitionType &transType, uint16 data) {
+bool SceneTransitionTypes::loadFromData(SceneTransitionType &transType, int32 data) {
 	switch (data) {
 	case Data::SceneTransitionTypes::kNone:
 		transType = kNone;
@@ -293,7 +293,7 @@ bool SceneTransitionTypes::loadFromData(SceneTransitionType &transType, uint16 d
 }
 
 
-bool SceneTransitionDirections::loadFromData(SceneTransitionDirection &transDir, uint16 data) {
+bool SceneTransitionDirections::loadFromData(SceneTransitionDirection &transDir, int32 data) {
 	switch (data) {
 	case Data::SceneTransitionDirections::kUp:
 		transDir = kUp;
@@ -3298,6 +3298,10 @@ HighLevelSceneTransition::HighLevelSceneTransition(const Common::SharedPtr<Struc
 	: scene(hlst_scene), type(hlst_type), addToDestinationScene(hlst_addToDestinationScene), addToReturnList(hlst_addToReturnList) {
 }
 
+SceneTransitionEffect::SceneTransitionEffect()
+	: _duration(100000), _steps(64), _transitionType(SceneTransitionTypes::kNone), _transitionDirection(SceneTransitionDirections::kUp) {
+}
+
 MessageDispatch::MessageDispatch(const Common::SharedPtr<MessageProperties> &msgProps, Structural *root, bool cascade, bool relay, bool couldBeCommand)
 	: _cascade(cascade), _relay(relay), _terminated(false), _msg(msgProps), _isCommand(false) {
 	if (couldBeCommand && EventIDs::isCommand(msgProps->getEvent().eventType)) {
@@ -3730,7 +3734,7 @@ Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, ISaveUIProvider *saveProv
 	_lastFrameCursor(nullptr), _defaultCursor(new DefaultCursorGraphic()), _platform(kProjectPlatformUnknown),
 	_cachedMousePosition(Common::Point(0, 0)), _realMousePosition(Common::Point(0, 0)), _trackedMouseOutside(false),
 	_forceCursorRefreshOnce(true), _autoResetCursor(false), _haveModifierOverrideCursor(false), _sceneGraphChanged(false), _isQuitting(false),
-	_collisionCheckTime(0), _defaultVolumeState(true) {
+	  _collisionCheckTime(0), _defaultVolumeState(true), _activeSceneTransitionEffect(nullptr), _sceneTransitionStartTime(0), _sceneTransitionEndTime(0) {
 	_random.reset(new Common::RandomSource("mtropolis"));
 
 	_vthread.reset(new VThread());
@@ -3800,7 +3804,7 @@ bool Runtime::runFrame() {
 			break;
 		}
 
-		if (_osEventQueue.size() > 0) {
+		if (_sceneTransitionState != kSceneTransitionStateTransitioning && _osEventQueue.size() > 0) {
 			Common::SharedPtr<OSEvent> evt = _osEventQueue[0];
 			_osEventQueue.remove_at(0);
 
@@ -3953,19 +3957,57 @@ bool Runtime::runFrame() {
 		}
 
 		if (_sceneTransitionState == kSceneTransitionStateWaitingForDraw) {
-			if (_sceneTransitionEffect.duration == 0) {
-				// This needs to skip past the transition phase and hit the next condition
+			if (_sourceSceneTransitionEffect._transitionType != SceneTransitionTypes::kNone)
+				_activeSceneTransitionEffect = &_sourceSceneTransitionEffect;
+			else if (_destinationSceneTransitionEffect._transitionType != SceneTransitionTypes::kNone)
+				_activeSceneTransitionEffect = &_destinationSceneTransitionEffect;
+			else
+				_activeSceneTransitionEffect = nullptr;
+
+			_sceneTransitionState = kSceneTransitionStateTransitioning;
+			_sceneTransitionStartTime = _playTime;
+
+			uint32 transitionDuration = 0;
+
+			if (_activeSceneTransitionEffect) {
+				transitionDuration = _activeSceneTransitionEffect->_duration;
+
+				if (transitionDuration < _hacks.minTransitionDuration)
+					transitionDuration = _hacks.minTransitionDuration;
+			}
+
+			if (transitionDuration == 0) {
+				// No transition at all.  This needs to skip past the transition phase and hit the next condition
 				_sceneTransitionEndTime = _playTime;
-				_sceneTransitionState = kSceneTransitionStateTransitioning;
 			} else {
-				_sceneTransitionState = kSceneTransitionStateDrawingTargetFrame;
-				_sceneTransitionEndTime = _playTime + _sceneTransitionEffect.duration / 10;
+				_sceneTransitionEndTime = _playTime + transitionDuration;
+
+				if (!_mainWindow.expired()) {
+					Common::SharedPtr<Window> mainWindow = _mainWindow.lock();
+					_sceneTransitionOldFrame.reset(new Graphics::ManagedSurface());
+					_sceneTransitionNewFrame.reset(new Graphics::ManagedSurface());
+
+					_sceneTransitionOldFrame->copyFrom(*mainWindow->getSurface());
+
+					Render::renderProject(this, mainWindow.get());
+
+					_sceneTransitionNewFrame->copyFrom(*mainWindow->getSurface());
+				}
 			}
 		}
 
 		if (_sceneTransitionState == kSceneTransitionStateTransitioning && _playTime >= _sceneTransitionEndTime) {
 			_sceneTransitionState = kSceneTransitionStateNotTransitioning;
 
+			if (_sceneTransitionNewFrame && !_mainWindow.expired())
+				_mainWindow.lock()->getSurface()->copyFrom(*_sceneTransitionNewFrame);
+
+			_sceneTransitionOldFrame.reset();
+			_sceneTransitionNewFrame.reset();
+
+			_sourceSceneTransitionEffect = SceneTransitionEffect();
+			_destinationSceneTransitionEffect = SceneTransitionEffect();
+
 			for (const SceneStackEntry &sceneStackEntry : _sceneStack)
 				recursiveAutoPlayMedia(sceneStackEntry.scene.get());
 
@@ -3976,6 +4018,11 @@ bool Runtime::runFrame() {
 			continue;
 		}
 
+		if (_sceneTransitionState == kSceneTransitionStateTransitioning) {
+			// Keep looping transition and don't do anything else until it's done
+			break;
+		}
+
 		{
 			Common::SharedPtr<ScheduledEvent> firstScheduledEvent = _scheduler.getFirstEvent();
 			if (firstScheduledEvent && firstScheduledEvent->getScheduledTime() <= _playTime) {
@@ -4031,8 +4078,13 @@ void Runtime::drawFrame() {
 
 	{
 		Common::SharedPtr<Window> mainWindow = _mainWindow.lock();
-		if (mainWindow)
-			Render::renderProject(this, mainWindow.get());
+		if (mainWindow) {
+			if (_sceneTransitionState == kSceneTransitionStateTransitioning) {
+				assert(_activeSceneTransitionEffect != nullptr);
+				Render::renderSceneTransition(this, mainWindow.get(), *_activeSceneTransitionEffect, _sceneTransitionStartTime, _sceneTransitionEndTime, _playTime, *_sceneTransitionOldFrame, *_sceneTransitionNewFrame);
+			} else
+				Render::renderProject(this, mainWindow.get());
+		}
 	}
 
 	const size_t numWindows = _windows.size();
@@ -4254,8 +4306,7 @@ void Runtime::executeCompleteTransitionToScene(const Common::SharedPtr<Structura
 
 	// Scene transitions have to be set up by the destination scene
 	_sceneTransitionState = kSceneTransitionStateWaitingForDraw;
-	_sceneTransitionEffect.transitionType = SceneTransitionTypes::kNone;
-	_sceneTransitionEffect.duration = 0;
+	_activeSceneTransitionEffect = nullptr;
 
 	executeSharedScenePostSceneChangeActions();
 }
@@ -5580,6 +5631,14 @@ void Runtime::addSceneStateTransition(const HighLevelSceneTransition &transition
 	_pendingSceneTransitions.push_back(transition);
 }
 
+void Runtime::setSceneTransitionEffect(bool isInDestinationScene, SceneTransitionEffect *effect) {
+	SceneTransitionEffect *target = isInDestinationScene ? &_destinationSceneTransitionEffect : &_sourceSceneTransitionEffect;
+	if (!effect)
+		*target = SceneTransitionEffect();
+	else
+		*target = *effect;
+}
+
 Project *Runtime::getProject() const {
 	return _project.get();
 }
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index c736ce482ab..1c97601a944 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -147,7 +147,7 @@ enum SceneTransitionType {
 	kWipe, // Directional
 };
 
-bool loadFromData(SceneTransitionType &transType, uint16 data);
+bool loadFromData(SceneTransitionType &transType, int32 data);
 
 } // End of namespace SceneTransitionTypes
 
@@ -160,7 +160,7 @@ enum SceneTransitionDirection {
 	kRight,
 };
 
-bool loadFromData(SceneTransitionDirection &transDir, uint16 data);
+bool loadFromData(SceneTransitionDirection &transDir, int32 data);
 
 } // End of namespace SceneTransitionDirections
 
@@ -1287,10 +1287,12 @@ struct HighLevelSceneTransition {
 };
 
 struct SceneTransitionEffect {
-	uint32 duration; // 6000000 is maximum
-	uint16 steps;
-	SceneTransitionTypes::SceneTransitionType transitionType;
-	SceneTransitionDirections::SceneTransitionDirection transitionDirection;
+	SceneTransitionEffect();
+
+	uint32 _duration; // 6000000 is maximum
+	uint16 _steps;
+	SceneTransitionTypes::SceneTransitionType _transitionType;
+	SceneTransitionDirections::SceneTransitionDirection _transitionDirection;
 };
 
 class MessageDispatch {
@@ -1487,6 +1489,8 @@ public:
 
 	void addSceneStateTransition(const HighLevelSceneTransition &transition);
 
+	void setSceneTransitionEffect(bool isInDestinationScene, SceneTransitionEffect *effect);
+
 	Project *getProject() const;
 
 	void postConsumeMessageTask(IMessageConsumer *msgConsumer, const Common::SharedPtr<MessageProperties> &msg);
@@ -1593,7 +1597,6 @@ private:
 	enum SceneTransitionState {
 		kSceneTransitionStateNotTransitioning,
 		kSceneTransitionStateWaitingForDraw,
-		kSceneTransitionStateDrawingTargetFrame,
 		kSceneTransitionStateTransitioning,
 	};
 
@@ -1705,7 +1708,12 @@ private:
 	Common::Array<SceneReturnListEntry> _sceneReturnList;
 
 	SceneTransitionState _sceneTransitionState;
-	SceneTransitionEffect _sceneTransitionEffect;
+	SceneTransitionEffect _sourceSceneTransitionEffect;
+	SceneTransitionEffect _destinationSceneTransitionEffect;
+	SceneTransitionEffect *_activeSceneTransitionEffect;
+	Common::SharedPtr<Graphics::ManagedSurface> _sceneTransitionOldFrame;
+	Common::SharedPtr<Graphics::ManagedSurface> _sceneTransitionNewFrame;
+	uint32 _sceneTransitionStartTime;
 	uint32 _sceneTransitionEndTime;
 
 	Common::WeakPtr<Window> _mainWindow;




More information about the Scummvm-git-logs mailing list