[Scummvm-git-logs] scummvm master -> f14a24f07c8ecf6a43f16eeb92f7af5ab54ea322

elasota noreply at scummvm.org
Sat Jul 16 08:30:00 UTC 2022


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

Summary:
d2cd12a976 MTROPOLIS: Change minimum wipe duration to 500ms
f120ec49f9 MTROPOLIS: Add debug inspectors for movement and rectshift modifiers
f14a24f07c MTROPOLIS: Implement Obsidian rectshift and movement modifiers


Commit: d2cd12a9766c3413091b7ed61382d77701095e37
    https://github.com/scummvm/scummvm/commit/d2cd12a9766c3413091b7ed61382d77701095e37
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-16T04:29:01-04:00

Commit Message:
MTROPOLIS: Change minimum wipe duration to 500ms

Changed paths:
    engines/mtropolis/plugin/standard.cpp


diff --git a/engines/mtropolis/plugin/standard.cpp b/engines/mtropolis/plugin/standard.cpp
index b8fff035c0d..b279d185ed5 100644
--- a/engines/mtropolis/plugin/standard.cpp
+++ b/engines/mtropolis/plugin/standard.cpp
@@ -1619,8 +1619,10 @@ VThreadState STransCtModifier::consumeMessage(Runtime *runtime, const Common::Sh
 			// 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;
+			const uint32 kMinWipeDuration = 500;
+
+			if (effect._transitionType == SceneTransitionTypes::kWipe && effect._duration < kMinWipeDuration)
+				effect._duration = kMinWipeDuration;
 
 			runtime->setSceneTransitionEffect(false, &effect);
 		} else {


Commit: f120ec49f99268c07755505f9d0a2f307ad86f66
    https://github.com/scummvm/scummvm/commit/f120ec49f99268c07755505f9d0a2f307ad86f66
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-16T04:29:01-04:00

Commit Message:
MTROPOLIS: Add debug inspectors for movement and rectshift modifiers

Changed paths:
    engines/mtropolis/plugin/obsidian.cpp
    engines/mtropolis/plugin/obsidian.h


diff --git a/engines/mtropolis/plugin/obsidian.cpp b/engines/mtropolis/plugin/obsidian.cpp
index 77bdfe60e05..cc301a0db87 100644
--- a/engines/mtropolis/plugin/obsidian.cpp
+++ b/engines/mtropolis/plugin/obsidian.cpp
@@ -59,6 +59,17 @@ MiniscriptInstructionOutcome MovementModifier::writeRefAttribute(MiniscriptThrea
 	return Modifier::writeRefAttribute(thread, result, attrib);
 }
 
+#ifdef MTROPOLIS_DEBUG_ENABLE
+void MovementModifier::debugInspect(IDebugInspectionReport *report) const {
+	Modifier::debugInspect(report);
+
+	report->declareDynamic("rate", Common::String::format("%i", static_cast<int>(_rate)));
+	report->declareDynamic("frequency", Common::String::format("%i", static_cast<int>(_frequency)));
+	report->declareDynamic("type", Common::String::format(_type ? "true" : "false"));
+	report->declareDynamic("dest", Common::String::format("(%i,%i)", static_cast<int>(_dest.x), static_cast<int>(_dest.y)));
+}
+#endif
+
 Common::SharedPtr<Modifier> MovementModifier::shallowClone() const {
 	return Common::SharedPtr<Modifier>(new MovementModifier(*this));
 }
@@ -90,6 +101,15 @@ MiniscriptInstructionOutcome RectShiftModifier::writeRefAttribute(MiniscriptThre
 	return Modifier::writeRefAttribute(thread, result, attrib);
 }
 
+#ifdef MTROPOLIS_DEBUG_ENABLE
+void RectShiftModifier::debugInspect(IDebugInspectionReport *report) const {
+	Modifier::debugInspect(report);
+
+	report->declareDynamic("direction", Common::String::format("%i", static_cast<int>(_direction)));
+	report->declareDynamic("rate", Common::String::format("%i", static_cast<int>(_rate)));
+}
+#endif
+
 Common::SharedPtr<Modifier> RectShiftModifier::shallowClone() const {
 	return Common::SharedPtr<Modifier>(new RectShiftModifier(*this));
 }
diff --git a/engines/mtropolis/plugin/obsidian.h b/engines/mtropolis/plugin/obsidian.h
index 3c539247b08..15c531b1125 100644
--- a/engines/mtropolis/plugin/obsidian.h
+++ b/engines/mtropolis/plugin/obsidian.h
@@ -42,6 +42,7 @@ public:
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Movement Modifier"; }
+	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
 private:
@@ -62,6 +63,7 @@ public:
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Rect Shift Modifier"; }
+	void debugInspect(IDebugInspectionReport *report) const override;
 #endif
 
 private:


Commit: f14a24f07c8ecf6a43f16eeb92f7af5ab54ea322
    https://github.com/scummvm/scummvm/commit/f14a24f07c8ecf6a43f16eeb92f7af5ab54ea322
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-16T04:29:01-04:00

Commit Message:
MTROPOLIS: Implement Obsidian rectshift and movement modifiers

Changed paths:
    engines/mtropolis/plugin/obsidian.cpp
    engines/mtropolis/plugin/obsidian.h
    engines/mtropolis/plugin/obsidian_data.cpp
    engines/mtropolis/plugin/obsidian_data.h
    engines/mtropolis/plugin/standard_data.h
    engines/mtropolis/render.cpp
    engines/mtropolis/runtime.cpp
    engines/mtropolis/runtime.h


diff --git a/engines/mtropolis/plugin/obsidian.cpp b/engines/mtropolis/plugin/obsidian.cpp
index cc301a0db87..a5b4b76f330 100644
--- a/engines/mtropolis/plugin/obsidian.cpp
+++ b/engines/mtropolis/plugin/obsidian.cpp
@@ -19,6 +19,8 @@
  *
  */
 
+#include "graphics/managed_surface.h"
+
 #include "mtropolis/plugin/obsidian.h"
 #include "mtropolis/plugins.h"
 
@@ -28,16 +30,78 @@ namespace MTropolis {
 
 namespace Obsidian {
 
+MovementModifier::MovementModifier() : _runtime(nullptr) {
+}
+
+MovementModifier::~MovementModifier() {
+	if (_moveEvent)
+		_moveEvent->cancel();
+}
+
 bool MovementModifier::load(const PlugInModifierLoaderContext &context, const Data::Obsidian::MovementModifier &data) {
-	// FIXME: Map these
-	_rate = 0;
-	_frequency = 0;
-	_type = false;
-	_dest = Common::Point(0, 0);
+
+	if (data.enableWhen.type != Data::PlugInTypeTaggedValue::kEvent || !_enableWhen.load(data.enableWhen.value.asEvent))
+		return false;
+
+	if (data.disableWhen.type != Data::PlugInTypeTaggedValue::kEvent || !_disableWhen.load(data.disableWhen.value.asEvent))
+		return false;
+
+	if (data.rate.type != Data::PlugInTypeTaggedValue::kFloat)
+		return false;
+
+	_rate = data.rate.value.asFloat.toXPFloat().toDouble();
+
+	if (data.frequency.type != Data::PlugInTypeTaggedValue::kInteger)
+		return false;
+
+	_frequency = data.frequency.value.asInt;
+
+	if (data.type.type != Data::PlugInTypeTaggedValue::kBoolean)
+		return false;
+
+	_type = (data.type.value.asBoolean != 0);
+
+	if (data.dest.type != Data::PlugInTypeTaggedValue::kPoint || !data.dest.value.asPoint.toScummVMPoint(_dest))
+		return false;
+
+	if (data.triggerEvent.type != Data::PlugInTypeTaggedValue::kEvent || !_triggerEvent.load(data.triggerEvent.value.asEvent))
+		return false;
 
 	return true;
 }
 
+bool MovementModifier::respondsToEvent(const Event &evt) const {
+	return _enableWhen.respondsTo(evt) || _disableWhen.respondsTo(evt);
+}
+
+VThreadState MovementModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
+	if (_enableWhen.respondsTo(msg->getEvent())) {
+		Structural *structural = findStructuralOwner();
+		if (structural == nullptr || !structural->isElement() || !static_cast<Element *>(structural)->isVisual()) {
+			warning("Movement modifier wasn't attached to a visual element");
+			return kVThreadError;
+		}
+		VisualElement *visual = static_cast<VisualElement *>(structural);
+
+		Common::Rect startRect = visual->getRelativeRect();
+		_moveStartPoint = Common::Point(startRect.left, startRect.top);
+		_moveStartTime = runtime->getPlayTime();
+
+		if (!_moveEvent) {
+			_runtime = runtime;
+			_moveEvent = runtime->getScheduler().scheduleMethod<MovementModifier, &MovementModifier::triggerMove>(runtime->getPlayTime() + 1, this);
+		}
+	}
+	if (_disableWhen.respondsTo(msg->getEvent())) {
+		if (_moveEvent) {
+			_moveEvent->cancel();
+			_moveEvent.reset();
+		}
+	}
+
+	return kVThreadReturn;
+}
+
 MiniscriptInstructionOutcome MovementModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
 	if (attrib == "type") {
 		DynamicValueWriteBoolHelper::create(&_type, result);
@@ -48,7 +112,7 @@ MiniscriptInstructionOutcome MovementModifier::writeRefAttribute(MiniscriptThrea
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "rate") {
-		DynamicValueWriteIntegerHelper<int32>::create(&_rate, result);
+		DynamicValueWriteFloatHelper<double>::create(&_rate, result);
 		return kMiniscriptInstructionOutcomeContinue;
 	}
 	if (attrib == "frequency") {
@@ -63,10 +127,13 @@ MiniscriptInstructionOutcome MovementModifier::writeRefAttribute(MiniscriptThrea
 void MovementModifier::debugInspect(IDebugInspectionReport *report) const {
 	Modifier::debugInspect(report);
 
-	report->declareDynamic("rate", Common::String::format("%i", static_cast<int>(_rate)));
+	report->declareDynamic("enableWhen", Common::String::format("Event(%i,%i)", static_cast<int>(_enableWhen.eventType), static_cast<int>(_enableWhen.eventInfo)));
+	report->declareDynamic("disableWhen", Common::String::format("Event(%i,%i)", static_cast<int>(_disableWhen.eventType), static_cast<int>(_disableWhen.eventInfo)));
+	report->declareDynamic("rate", Common::String::format("%g", _rate));
 	report->declareDynamic("frequency", Common::String::format("%i", static_cast<int>(_frequency)));
 	report->declareDynamic("type", Common::String::format(_type ? "true" : "false"));
 	report->declareDynamic("dest", Common::String::format("(%i,%i)", static_cast<int>(_dest.x), static_cast<int>(_dest.y)));
+	report->declareDynamic("triggerEvent", Common::String::format("Event(%i,%i)", static_cast<int>(_triggerEvent.eventType), static_cast<int>(_triggerEvent.eventInfo)));
 }
 #endif
 
@@ -78,21 +145,100 @@ const char *MovementModifier::getDefaultName() const {
 	return "Movement";
 }
 
+void MovementModifier::triggerMove(Runtime *runtime) {
+	_moveEvent.reset();
+
+	Structural *structural = findStructuralOwner();
+	if (structural == nullptr || !structural->isElement() || !static_cast<Element *>(structural)->isVisual()) {
+		warning("Movement modifier wasn't attached to a visual element");
+		return;
+	}
+	VisualElement *visual = static_cast<VisualElement *>(structural);
+
+
+	Common::Point delta = _dest - _moveStartPoint;
+
+	double deltaLength = sqrt(delta.x * delta.x + delta.y * delta.y);
+
+	double progression = 1.0;
+	if (deltaLength > 0.0 && _rate > 0.0) {
+		double distance = static_cast<double>(runtime->getPlayTime() - _moveStartTime) * _rate / 1000.0;
+		progression = distance / deltaLength;
+		if (progression > 1.0)
+			progression = 1.0;
+		if (progression < 0.0)
+			progression = 0.0;
+	}
+
+	int32 targetX = _moveStartPoint.x + static_cast<int32>(round((_dest.x - _moveStartPoint.x) * progression));
+	int32 targetY = _moveStartPoint.y + static_cast<int32>(round((_dest.y - _moveStartPoint.y) * progression));
+
+	Common::Rect relRect = visual->getRelativeRect();
+	int32 xDelta = targetX - relRect.left;
+	int32 yDelta = targetY - relRect.top;
+
+	relRect.left += xDelta;
+	relRect.right += xDelta;
+	relRect.top += yDelta;
+	relRect.bottom += yDelta;
+
+	visual->setRelativeRect(relRect);
+
+	if (progression == 1.0) {
+		Common::SharedPtr<MessageProperties> props(new MessageProperties(_triggerEvent, DynamicValue(), visual->getSelfReference()));
+		Common::SharedPtr<MessageDispatch> dispatch(new MessageDispatch(props, visual, true, true, false));
+		runtime->sendMessageOnVThread(dispatch);
+	} else {
+		_moveEvent = runtime->getScheduler().scheduleMethod<MovementModifier, &MovementModifier::triggerMove>(runtime->getPlayTime() + 1, this);
+	}
+}
+
+RectShiftModifier::RectShiftModifier() : _enableWhen(Event::create()), _disableWhen(Event::create()), _direction(0), _runtime(nullptr), _isActive(false) {
+}
+
+RectShiftModifier::~RectShiftModifier() {
+	if (_isActive)
+		_runtime->removePostEffect(this);
+}
+
+bool RectShiftModifier::respondsToEvent(const Event &evt) const {
+	return _enableWhen.respondsTo(evt) || _disableWhen.respondsTo(evt);
+}
+
+VThreadState RectShiftModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
+	if (_enableWhen.respondsTo(msg->getEvent()) && !_isActive) {
+		_runtime = runtime;
+		_runtime->addPostEffect(this);
+		_isActive = true;
+	}
+	if (_disableWhen.respondsTo(msg->getEvent()) && _isActive) {
+		_isActive = false;
+		_runtime->removePostEffect(this);
+		_runtime = nullptr;
+	}
+
+	return kVThreadReturn;
+}
+
 bool RectShiftModifier::load(const PlugInModifierLoaderContext &context, const Data::Obsidian::RectShiftModifier &data) {
-	if (data.rate.type != Data::PlugInTypeTaggedValue::kInteger)
+	if (data.enableWhen.type != Data::PlugInTypeTaggedValue::kEvent || !_enableWhen.load(data.enableWhen.value.asEvent))
+		return false;
+
+	if (data.disableWhen.type != Data::PlugInTypeTaggedValue::kEvent || !_disableWhen.load(data.disableWhen.value.asEvent))
+		return false;
+
+	if (data.direction.type != Data::PlugInTypeTaggedValue::kInteger)
 		return false;
 
-	_direction = 0;
-	_rate = data.rate.value.asInt;
+	_direction = data.direction.value.asInt;
+
+	if (data.enableWhen.type != Data::PlugInTypeTaggedValue::kEvent || !_enableWhen.load(data.enableWhen.value.asEvent))
+		return false;
 
 	return true;
 }
 
 MiniscriptInstructionOutcome RectShiftModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) {
-	if (attrib == "rate") {
-		DynamicValueWriteIntegerHelper<int32>::create(&_rate, result);
-		return kMiniscriptInstructionOutcomeContinue;
-	}
 	if (attrib == "direction") {
 		DynamicValueWriteIntegerHelper<int32>::create(&_direction, result);
 		return kMiniscriptInstructionOutcomeContinue;
@@ -101,17 +247,69 @@ MiniscriptInstructionOutcome RectShiftModifier::writeRefAttribute(MiniscriptThre
 	return Modifier::writeRefAttribute(thread, result, attrib);
 }
 
+void RectShiftModifier::renderPostEffect(Graphics::ManagedSurface &surface) const {
+	Structural *structural = findStructuralOwner();
+	if (!structural)
+		return;
+
+	if (!structural->isElement() || !static_cast<Element *>(structural)->isVisual())
+		return;
+
+	VisualElement *visual = static_cast<VisualElement *>(structural);
+
+	Common::Point absOrigin = visual->getCachedAbsoluteOrigin();
+	Common::Rect relRect = visual->getRelativeRect();
+	Common::Rect absRect(absOrigin.x, absOrigin.y, absOrigin.x + relRect.width(), absOrigin.y + relRect.height());
+
+	if (absRect.left < 0)
+		absRect.left = 0;
+	if (absRect.right >= surface.w)
+		absRect.right = surface.w;
+	if (absRect.top < 0)
+		absRect.top = 0;
+	if (absRect.bottom >= surface.h)
+		absRect.bottom = surface.h;
+
+	if (_direction == 1) {
+		if (absRect.bottom + 1 >= surface.h)
+			absRect.bottom--;
+	} else if (_direction == 4) {
+		if (absRect.right + 1 >= surface.w)
+			absRect.right--;
+	} else
+		return;
+
+	if (!absRect.isValidRect())
+		return;
+
+	uint pitch = (absRect.right - absRect.left) * surface.format.bytesPerPixel;
+
+	for (int32 y = absRect.top; y < absRect.bottom; y++) {
+		void *destPixels = surface.getBasePtr(absRect.left, y);
+		void *srcPixels = destPixels;
+
+		if (_direction == 1)
+			srcPixels = surface.getBasePtr(absRect.left, y + 1);
+		else if (_direction == 4)
+			srcPixels = surface.getBasePtr(absRect.left + 1, y);
+
+		memmove(destPixels, srcPixels, pitch);
+	}
+}
+
 #ifdef MTROPOLIS_DEBUG_ENABLE
 void RectShiftModifier::debugInspect(IDebugInspectionReport *report) const {
 	Modifier::debugInspect(report);
 
 	report->declareDynamic("direction", Common::String::format("%i", static_cast<int>(_direction)));
-	report->declareDynamic("rate", Common::String::format("%i", static_cast<int>(_rate)));
 }
 #endif
 
 Common::SharedPtr<Modifier> RectShiftModifier::shallowClone() const {
-	return Common::SharedPtr<Modifier>(new RectShiftModifier(*this));
+	Common::SharedPtr<RectShiftModifier> clone(new RectShiftModifier(*this));
+	clone->_isActive = false;
+	clone->_runtime = nullptr;
+	return clone;
 }
 
 const char *RectShiftModifier::getDefaultName() const {
diff --git a/engines/mtropolis/plugin/obsidian.h b/engines/mtropolis/plugin/obsidian.h
index 15c531b1125..e966fbc9615 100644
--- a/engines/mtropolis/plugin/obsidian.h
+++ b/engines/mtropolis/plugin/obsidian.h
@@ -36,8 +36,14 @@ class WordGameData;
 
 class MovementModifier : public Modifier {
 public:
+	MovementModifier();
+	~MovementModifier();
+
 	bool load(const PlugInModifierLoaderContext &context, const Data::Obsidian::MovementModifier &data);
 
+	bool respondsToEvent(const Event &evt) const override;
+	VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
+
 	MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
@@ -49,18 +55,39 @@ private:
 	Common::SharedPtr<Modifier> shallowClone() const override;
 	const char *getDefaultName() const override;
 
+	void triggerMove(Runtime *runtime);
+
 	Common::Point _dest;
 	bool _type;
-	int32 _rate;
+	double _rate;
 	int32 _frequency;
+
+	Event _enableWhen;
+	Event _disableWhen;
+
+	Event _triggerEvent;
+
+	Common::Point _moveStartPoint;
+	uint64 _moveStartTime;
+
+	Common::SharedPtr<ScheduledEvent> _moveEvent;
+	Runtime *_runtime;
 };
 
-class RectShiftModifier : public Modifier {
+class RectShiftModifier : public Modifier, public IPostEffect {
 public:
+	RectShiftModifier();
+	~RectShiftModifier();
+
+	bool respondsToEvent(const Event &evt) const override;
+	VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
+
 	bool load(const PlugInModifierLoaderContext &context, const Data::Obsidian::RectShiftModifier &data);
 
 	MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
 
+	void renderPostEffect(Graphics::ManagedSurface &surface) const override;
+
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Rect Shift Modifier"; }
 	void debugInspect(IDebugInspectionReport *report) const override;
@@ -70,8 +97,13 @@ private:
 	Common::SharedPtr<Modifier> shallowClone() const override;
 	const char *getDefaultName() const override;
 
-	int32 _rate;
+	Event _enableWhen;
+	Event _disableWhen;
+
 	int32 _direction;
+
+	Runtime *_runtime;
+	bool _isActive;
 };
 
 class TextWorkModifier : public Modifier {
diff --git a/engines/mtropolis/plugin/obsidian_data.cpp b/engines/mtropolis/plugin/obsidian_data.cpp
index 97b4529831a..40591ef91ff 100644
--- a/engines/mtropolis/plugin/obsidian_data.cpp
+++ b/engines/mtropolis/plugin/obsidian_data.cpp
@@ -31,15 +31,15 @@ DataReadErrorCode MovementModifier::load(PlugIn &plugIn, const PlugInModifier &p
 	if (prefix.plugInRevision != 0)
 		return kDataReadErrorUnsupportedRevision;
 
-	if (!unknown1Event.load(reader)
-		|| !unknown2Event.load(reader)
-		|| !unknown3Point.load(reader)
-		|| !unknown4Bool.load(reader)
+	if (!enableWhen.load(reader)
+		|| !disableWhen.load(reader)
+		|| !dest.load(reader)
+		|| !type.load(reader)
 		|| !unknown5Point.load(reader)
 		|| !unknown6Int.load(reader)
-		|| !unknown7Float.load(reader)
-		|| !unknown8Int.load(reader)
-		|| !unknown9Event.load(reader)
+		|| !rate.load(reader)
+		|| !frequency.load(reader)
+		|| !triggerEvent.load(reader)
 		|| !unknown10Label.load(reader)
 		|| !unknown11Null.load(reader)
 		|| !unknown12Int.load(reader))
@@ -52,7 +52,7 @@ DataReadErrorCode RectShiftModifier::load(PlugIn &plugIn, const PlugInModifier &
 	if (prefix.plugInRevision != 1)
 		return kDataReadErrorUnsupportedRevision;
 
-	if (!unknown1Event.load(reader) || !unknown2Event.load(reader) || !rate.load(reader))
+	if (!enableWhen.load(reader) || !disableWhen.load(reader) || !direction.load(reader))
 		return kDataReadErrorReadFailed;
 
 	return kDataReadErrorNone;
diff --git a/engines/mtropolis/plugin/obsidian_data.h b/engines/mtropolis/plugin/obsidian_data.h
index 87f0ef29c9a..e29e2c0d581 100644
--- a/engines/mtropolis/plugin/obsidian_data.h
+++ b/engines/mtropolis/plugin/obsidian_data.h
@@ -40,27 +40,27 @@ namespace Obsidian {
 // TextWork - Text manipulation operations
 
 struct MovementModifier : public PlugInModifierData {
-	PlugInTypeTaggedValue unknown1Event;	// Probably "enable when"
-	PlugInTypeTaggedValue unknown2Event;	// Probably "disable when"
-	PlugInTypeTaggedValue unknown3Point;
-	PlugInTypeTaggedValue unknown4Bool;
-	PlugInTypeTaggedValue unknown5Point;
-	PlugInTypeTaggedValue unknown6Int;
-	PlugInTypeTaggedValue unknown7Float;
-	PlugInTypeTaggedValue unknown8Int;
-	PlugInTypeTaggedValue unknown9Event;
-	PlugInTypeTaggedValue unknown10Label;
-	PlugInTypeTaggedValue unknown11Null;
-	PlugInTypeTaggedValue unknown12Int;
+	PlugInTypeTaggedValue enableWhen;		// Event
+	PlugInTypeTaggedValue disableWhen;		// Event
+	PlugInTypeTaggedValue dest;				// Point
+	PlugInTypeTaggedValue type;				// Bool, seems to always be "false"
+	PlugInTypeTaggedValue unknown5Point;	// Point, always (0,0)
+	PlugInTypeTaggedValue unknown6Int;		// Int, always 5
+	PlugInTypeTaggedValue rate;				// Float
+	PlugInTypeTaggedValue frequency;		// Int
+	PlugInTypeTaggedValue triggerEvent;
+	PlugInTypeTaggedValue unknown10Label;	// Label, always (5,108) which doesn't seem to correspond to anything
+	PlugInTypeTaggedValue unknown11Null;	// Null, possibly message payload
+	PlugInTypeTaggedValue unknown12Int;		// Int, always 3, possibly message flags
 
 protected:
 	DataReadErrorCode load(PlugIn &plugIn, const PlugInModifier &prefix, DataReader &reader) override;
 };
 
 struct RectShiftModifier : public PlugInModifierData {
-	PlugInTypeTaggedValue unknown1Event; // Probably "enable when"
-	PlugInTypeTaggedValue unknown2Event; // Probably "disable when"
-	PlugInTypeTaggedValue rate;
+	PlugInTypeTaggedValue enableWhen; // Event, enable when
+	PlugInTypeTaggedValue disableWhen; // Event, disable when
+	PlugInTypeTaggedValue direction;	// Int, 4 = horizontal, 1 = vertical
 
 protected:
 	DataReadErrorCode load(PlugIn &plugIn, const PlugInModifier &prefix, DataReader &reader) override;
diff --git a/engines/mtropolis/plugin/standard_data.h b/engines/mtropolis/plugin/standard_data.h
index 00a76be3a2b..259ba5339b6 100644
--- a/engines/mtropolis/plugin/standard_data.h
+++ b/engines/mtropolis/plugin/standard_data.h
@@ -61,7 +61,7 @@ struct MediaCueMessengerModifier : public PlugInModifierData {
 	enum MessageFlags {
 		kMessageFlagImmediate = 0x1,
 		kMessageFlagCascade = 0x2,
-		kMessageFlagRelay = 0x3,
+		kMessageFlagRelay = 0x4,
 	};
 
 	enum TriggerTiming {
diff --git a/engines/mtropolis/render.cpp b/engines/mtropolis/render.cpp
index 005185d8be9..40e7caa4fc4 100644
--- a/engines/mtropolis/render.cpp
+++ b/engines/mtropolis/render.cpp
@@ -298,6 +298,9 @@ void renderProject(Runtime *runtime, Window *mainWindow) {
 
 		for (Common::Array<RenderItem>::const_iterator it = directBucket.begin(), itEnd = directBucket.end(); it != itEnd; ++it)
 			renderDirectElement(*it, mainWindow);
+
+		for (const IPostEffect *postEffect : runtime->getPostEffects())
+			postEffect->renderPostEffect(*mainWindow->getSurface());
 	}
 
 	runtime->clearSceneGraphDirty();
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index a4e52895c1f..cb0316cc31e 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -5525,6 +5525,24 @@ void Runtime::removeBoundaryDetector(IBoundaryDetector *boundaryDetector) {
 	}
 }
 
+void Runtime::addPostEffect(IPostEffect *postEffect) {
+	_postEffects.push_back(postEffect);
+}
+
+void Runtime::removePostEffect(IPostEffect *postEffect) {
+	size_t numPostEffects = _postEffects.size();
+	for (size_t i = 0; i < numPostEffects; i++) {
+		if (_postEffects[i] == postEffect) {
+			_postEffects.remove_at(i);
+			return;
+		}
+	}
+}
+
+const Common::Array<IPostEffect *> &Runtime::getPostEffects() const {
+	return _postEffects;
+}
+
 void Runtime::checkBoundaries() {
 	// Boundary Detection Messenger behavior is very quirky in mTropolis 1.1.  Basically, if an object moves in the direction of
 	// the boundary, then it may trigger collision checks with the boundary.  If it moves but does not move in the direction of
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index 84fe30dccea..684fcc599b8 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -98,6 +98,7 @@ struct IMessageConsumer;
 struct IModifierContainer;
 struct IPlugInModifierFactory;
 struct IPlugInModifierFactoryAndDataFactory;
+struct IPostEffect;
 struct ISaveUIProvider;
 struct IStructuralReferenceVisitor;
 struct MessageProperties;
@@ -1588,6 +1589,10 @@ public:
 	void removeBoundaryDetector(IBoundaryDetector *boundaryDetector);
 	void checkBoundaries();
 
+	void addPostEffect(IPostEffect *postEffect);
+	void removePostEffect(IPostEffect *postEffect);
+	const Common::Array<IPostEffect *> &getPostEffects() const;
+
 	const Common::String *resolveAttributeIDName(uint32 attribID) const;
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
@@ -1804,6 +1809,8 @@ private:
 	Common::Array<BoundaryCheckState> _boundaryChecks;
 	uint32 _collisionCheckTime;
 
+	Common::Array<IPostEffect *> _postEffects;
+
 	Hacks _hacks;
 
 	Common::HashMap<uint32, Common::String> _getSetAttribIDsToAttribName;
@@ -2156,6 +2163,10 @@ struct IBoundaryDetector : public IInterfaceBase {
 	virtual void triggerCollision(Runtime *runtime) = 0;
 };
 
+struct IPostEffect : public IInterfaceBase {
+	virtual void renderPostEffect(Graphics::ManagedSurface &surface) const = 0;
+};
+
 struct MediaCueState {
 	enum TriggerTiming {
 		kTriggerTimingStart = 0,




More information about the Scummvm-git-logs mailing list