[Scummvm-git-logs] scummvm master -> 6a966b2d1796ec2429b82936bb4e2cda197e5f07

elasota noreply at scummvm.org
Sat Jul 16 01:49:57 UTC 2022


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

Summary:
7180505432 MTROPOLIS: Change enable short transitions default to on
6a966b2d17 MTROPOLIS: Add boundary detection modifier, fix real-time vector motion var sourcing


Commit: 7180505432d31a06fee7713382bc33cd7317737f
    https://github.com/scummvm/scummvm/commit/7180505432d31a06fee7713382bc33cd7317737f
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-15T21:49:23-04:00

Commit Message:
MTROPOLIS: Change enable short transitions default to on

Changed paths:
    engines/mtropolis/detection.cpp


diff --git a/engines/mtropolis/detection.cpp b/engines/mtropolis/detection.cpp
index 3e3a808b83d..1957103811d 100644
--- a/engines/mtropolis/detection.cpp
+++ b/engines/mtropolis/detection.cpp
@@ -76,7 +76,7 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			_s("Enable short transitions"),
 			_s("Enables transitions that are set to maximum rate instead of skipping them"),
 			"mtropolis_mod_minimum_transition_duration",
-			false,
+			true,
 			0,
 			0
 		}


Commit: 6a966b2d1796ec2429b82936bb4e2cda197e5f07
    https://github.com/scummvm/scummvm/commit/6a966b2d1796ec2429b82936bb4e2cda197e5f07
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-15T21:49:23-04:00

Commit Message:
MTROPOLIS: Add boundary detection modifier, fix real-time vector motion var sourcing

Changed paths:
    engines/mtropolis/modifiers.cpp
    engines/mtropolis/modifiers.h
    engines/mtropolis/runtime.cpp
    engines/mtropolis/runtime.h


diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index c4391c1f554..6f93f8b12a1 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -769,6 +769,19 @@ void VectorMotionModifier::trigger(Runtime *runtime) {
 	uint64 currentTime = runtime->getPlayTime();
 	_scheduledEvent = runtime->getScheduler().scheduleMethod<VectorMotionModifier, &VectorMotionModifier::trigger>(currentTime + 1, this);
 
+	Modifier *vecSrcModifier = _vecVar.lock().get();
+
+	// Variable-sourced motion is continuously updated and doesn't need to be re-triggered.
+	// The Pong minigame in Obsidian's Bureau chapter depends on this.
+	if (vecSrcModifier && vecSrcModifier->isVariable()) {
+		DynamicValue vec;
+		VariableModifier *varModifier = static_cast<VariableModifier *>(vecSrcModifier);
+		varModifier->varGetValue(nullptr, vec);
+
+		if (vec.getType() == DynamicValueTypes::kVector)
+			_resolvedVector = vec.getVector();
+	}
+
 	double radians = _resolvedVector.angleDegrees * (M_PI / 180.0);
 
 	// Distance is per-tick, which is 1/60 of a sec, so the multiplier is 60.0/1000.0 or 0.06
@@ -1171,6 +1184,18 @@ void TimerMessengerModifier::trigger(Runtime *runtime) {
 	_sendSpec.sendFromMessenger(runtime, this, _incomingData, nullptr);
 }
 
+BoundaryDetectionMessengerModifier::BoundaryDetectionMessengerModifier()
+	: _enableWhen(Event::create()), _disableWhen(Event::create()), _exitTriggerMode(kExitTriggerExiting),
+	_detectTopEdge(false), _detectBottomEdge(false), _detectLeftEdge(false), _detectRightEdge(false),
+	_detectionMode(kContinuous), _runtime(nullptr), _isActive(false) {
+}
+
+BoundaryDetectionMessengerModifier::~BoundaryDetectionMessengerModifier() {
+	if (_isActive)
+		_runtime->removeBoundaryDetector(this);
+}
+
+
 bool BoundaryDetectionMessengerModifier::load(ModifierLoaderContext &context, const Data::BoundaryDetectionMessengerModifier &data) {
 	if (!loadTypicalHeader(data.modHeader))
 		return false;
@@ -1192,6 +1217,59 @@ bool BoundaryDetectionMessengerModifier::load(ModifierLoaderContext &context, co
 	return true;
 }
 
+bool BoundaryDetectionMessengerModifier::respondsToEvent(const Event &evt) const {
+	return _enableWhen.respondsTo(evt) || _disableWhen.respondsTo(evt);
+}
+
+VThreadState BoundaryDetectionMessengerModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
+	if (_enableWhen.respondsTo(msg->getEvent()) && !_isActive) {
+		_runtime = runtime;
+		_runtime->addBoundaryDetector(this);
+		_isActive = true;
+
+		_incomingData = msg->getValue();
+		if (_incomingData.getType() == DynamicValueTypes::kList)
+			_incomingData.setList(_incomingData.getList()->clone());
+	}
+	if (_disableWhen.respondsTo(msg->getEvent()) && _isActive) {
+		_runtime->removeBoundaryDetector(this);
+		_isActive = false;
+		_runtime = nullptr;
+	}
+
+	return kVThreadReturn;
+}
+
+void BoundaryDetectionMessengerModifier::linkInternalReferences(ObjectLinkingScope *outerScope) {
+	_send.linkInternalReferences(outerScope);
+}
+
+void BoundaryDetectionMessengerModifier::visitInternalReferences(IStructuralReferenceVisitor *visitor) {
+	_send.visitInternalReferences(visitor);
+}
+
+void BoundaryDetectionMessengerModifier::getCollisionProperties(Modifier *&modifier, uint &edgeFlags, bool &mustBeCompletelyOutside, bool &continuous) const {
+	modifier = const_cast<BoundaryDetectionMessengerModifier *>(this);
+
+	uint flags = 0;
+	if (_detectBottomEdge)
+		flags |= kEdgeBottom;
+	if (_detectTopEdge)
+		flags |= kEdgeTop;
+	if (_detectRightEdge)
+		flags |= kEdgeRight;
+	if (_detectLeftEdge)
+		flags |= kEdgeLeft;
+
+	edgeFlags = flags;
+	mustBeCompletelyOutside = (_exitTriggerMode == kExitTriggerOnceExited);
+	continuous = (_detectionMode == kContinuous);
+}
+
+void BoundaryDetectionMessengerModifier::triggerCollision(Runtime *runtime) {
+	_send.sendFromMessenger(runtime, this, _incomingData, nullptr);
+}
+
 Common::SharedPtr<Modifier> BoundaryDetectionMessengerModifier::shallowClone() const {
 	return Common::SharedPtr<Modifier>(new BoundaryDetectionMessengerModifier(*this));
 }
diff --git a/engines/mtropolis/modifiers.h b/engines/mtropolis/modifiers.h
index aebbd1ea091..72be5f140c1 100644
--- a/engines/mtropolis/modifiers.h
+++ b/engines/mtropolis/modifiers.h
@@ -477,10 +477,22 @@ private:
 	Common::SharedPtr<ScheduledEvent> _scheduledEvent;
 };
 
-class BoundaryDetectionMessengerModifier : public Modifier {
+class BoundaryDetectionMessengerModifier : public Modifier, public IBoundaryDetector {
 public:
+	BoundaryDetectionMessengerModifier();
+	~BoundaryDetectionMessengerModifier();
+
 	bool load(ModifierLoaderContext &context, const Data::BoundaryDetectionMessengerModifier &data);
 
+	bool respondsToEvent(const Event &evt) const override;
+	VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
+
+	void linkInternalReferences(ObjectLinkingScope *outerScope) override;
+	void visitInternalReferences(IStructuralReferenceVisitor *visitor) override;
+
+	void getCollisionProperties(Modifier *&modifier, uint &edgeFlags, bool &mustBeCompletelyOutside, bool &continuous) const override;
+	void triggerCollision(Runtime *runtime) override;
+
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Boundary Detection Modifier"; }
 #endif
@@ -508,6 +520,10 @@ private:
 	bool _detectLeftEdge;
 	bool _detectRightEdge;
 	MessengerSendSpec _send;
+
+	Runtime *_runtime;
+	bool _isActive;
+	DynamicValue _incomingData;
 };
 
 class CollisionDetectionMessengerModifier : public Modifier, public ICollider {
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index 32c11152edd..257f3152868 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -4036,6 +4036,7 @@ bool Runtime::runFrame() {
 		if (_collisionCheckTime < _playTime) {
 			_collisionCheckTime = _playTime;
 
+			checkBoundaries();
 			checkCollisions();
 		}
 
@@ -5498,6 +5499,109 @@ void Runtime::checkCollisions() {
 	}
 }
 
+void Runtime::addBoundaryDetector(IBoundaryDetector *boundaryDetector) {
+	BoundaryCheckState state;
+	state.currentContacts = 0;
+	state.detector = boundaryDetector;
+	state.position = Common::Point(0, 0);
+	state.positionResolved = false;
+
+	Modifier *modifier;
+	uint edgeFlags;
+	bool mustBeCompletelyOutside;
+	bool continuous;
+	boundaryDetector->getCollisionProperties(modifier, edgeFlags, mustBeCompletelyOutside, continuous);
+
+	_boundaryChecks.push_back(state);
+}
+
+void Runtime::removeBoundaryDetector(IBoundaryDetector *boundaryDetector) {
+	size_t numColliders = _boundaryChecks.size();
+	for (size_t i = 0; i < numColliders; i++) {
+		if (_boundaryChecks[i].detector == boundaryDetector) {
+			_boundaryChecks.remove_at(i);
+			return;
+		}
+	}
+}
+
+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
+	// the boundary, then it is considered no longer in contact with the boundary - period - which means it can trigger again
+	// once it moves in the boundary direction.
+	for (BoundaryCheckState &checkState : _boundaryChecks) {
+		Modifier *modifier;
+		uint edgeFlags;
+		bool mustBeCompletelyOutside;
+		bool continuous;
+		checkState.detector->getCollisionProperties(modifier, edgeFlags, mustBeCompletelyOutside, continuous);
+
+		Structural *structural = modifier->findStructuralOwner();
+		if (structural == nullptr || !structural->isElement() || !static_cast<Element *>(structural)->isVisual())
+			continue;
+
+		VisualElement *visual = static_cast<VisualElement *>(structural);
+
+		Common::Rect thisRect = visual->getRelativeRect();
+		Common::Point point(thisRect.left, thisRect.top);
+
+		if (!checkState.positionResolved) {
+			checkState.positionResolved = true;
+			checkState.position = point;
+			continue;
+		}
+
+		if (point == checkState.position)
+			continue;
+
+		Structural *parentStructural = visual->getParent();
+		if (parentStructural == nullptr || !parentStructural->isElement() || !static_cast<Element *>(parentStructural)->isVisual())
+			continue;
+
+		VisualElement *parentVisual = static_cast<VisualElement *>(parentStructural);
+
+		Common::Point delta = point - checkState.position;
+
+		int16 parentWidth = parentVisual->getRelativeRect().width();
+		int16 parentHeight = parentVisual->getRelativeRect().height();
+
+		uint contacts = 0;
+		if (delta.x < 0) {
+			int16 edge = mustBeCompletelyOutside ? thisRect.right : thisRect.left;
+			if (edge < 0)
+				contacts |= IBoundaryDetector::kEdgeLeft;
+		}
+		if (delta.y < 0) {
+			int16 edge = mustBeCompletelyOutside ? thisRect.bottom : thisRect.top;
+			if (edge < 0)
+				contacts |= IBoundaryDetector::kEdgeTop;
+		}
+		if (delta.x > 0) {
+			int16 edge = mustBeCompletelyOutside ? thisRect.left : thisRect.right;
+			if (edge >= parentWidth)
+				contacts |= IBoundaryDetector::kEdgeRight;
+		}
+		if (delta.y > 0) {
+			int16 edge = mustBeCompletelyOutside ? thisRect.top : thisRect.bottom;
+			if (edge >= parentHeight)
+				contacts |= IBoundaryDetector::kEdgeBottom;
+		}
+
+		uint activatedContacts = contacts;
+
+		// If non-continuous, then only activate new contacts
+		if (!continuous)
+			activatedContacts &= ~checkState.currentContacts;
+
+		checkState.position = point;
+		checkState.currentContacts = contacts;
+
+		if (activatedContacts & edgeFlags)
+			checkState.detector->triggerCollision(this);
+	}
+}
+
 void Runtime::recursiveFindColliders(Structural *structural, size_t sceneStackDepth, Common::Array<ColliderInfo> &colliders, int32 parentOriginX, int32 parentOriginY, bool isRoot) {
 	int32 childOffsetX = parentOriginX;
 	int32 childOffsetY = parentOriginY;
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index 1c97601a944..84fe30dccea 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -91,6 +91,7 @@ class Window;
 class WorldManagerInterface;
 struct DynamicValue;
 struct DynamicValueWriteProxy;
+struct IBoundaryDetector;
 struct ICollider;
 struct ILoadUIProvider;
 struct IMessageConsumer;
@@ -1583,6 +1584,10 @@ public:
 	void removeCollider(ICollider *collider);
 	void checkCollisions();
 
+	void addBoundaryDetector(IBoundaryDetector *boundaryDetector);
+	void removeBoundaryDetector(IBoundaryDetector *boundaryDetector);
+	void checkBoundaries();
+
 	const Common::String *resolveAttributeIDName(uint32 attribID) const;
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
@@ -1648,6 +1653,13 @@ private:
 		ICollider *collider;
 	};
 
+	struct BoundaryCheckState {
+		IBoundaryDetector *detector;
+		uint currentContacts;
+		Common::Point position;
+		bool positionResolved;
+	};
+
 	struct ColliderInfo {
 		size_t sceneStackDepth;
 		uint16 layer;
@@ -1789,6 +1801,7 @@ private:
 	bool _isQuitting;
 
 	Common::Array<Common::SharedPtr<CollisionCheckState> > _colliders;
+	Common::Array<BoundaryCheckState> _boundaryChecks;
 	uint32 _collisionCheckTime;
 
 	Hacks _hacks;
@@ -2131,6 +2144,18 @@ struct ICollider : public IInterfaceBase {
 	virtual void triggerCollision(Runtime *runtime, Structural *collidingElement, bool wasInContact, bool isInContact, bool &outShouldStop) = 0;
 };
 
+struct IBoundaryDetector : public IInterfaceBase {
+	enum EdgeFlags {
+		kEdgeTop = 0x1,
+		kEdgeBottom = 0x2,
+		kEdgeLeft = 0x4,
+		kEdgeRight = 0x8,
+	};
+
+	virtual void getCollisionProperties(Modifier *&modifier, uint &edgeFlags, bool &mustBeCompletelyOutside, bool &continuous) const = 0;
+	virtual void triggerCollision(Runtime *runtime) = 0;
+};
+
 struct MediaCueState {
 	enum TriggerTiming {
 		kTriggerTimingStart = 0,




More information about the Scummvm-git-logs mailing list