From d00a058d3e176a71b1ddddbc280ad70d5e409fa2 Mon Sep 17 00:00:00 2001 From: elasota Date: Mon, 30 May 2022 23:42:07 -0400 Subject: [PATCH] MTROPOLIS: Handle show/hide events, stub out element transition modifier. --- engines/mtropolis/modifiers.cpp | 81 ++++++++++++++++++++++++++++++++- engines/mtropolis/modifiers.h | 9 ++++ engines/mtropolis/runtime.cpp | 36 ++++++++++++++- engines/mtropolis/runtime.h | 2 + 4 files changed, 126 insertions(+), 2 deletions(-) diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp index 17ed30289c1..2fb3d15172e 100644 --- a/engines/mtropolis/modifiers.cpp +++ b/engines/mtropolis/modifiers.cpp @@ -885,14 +885,93 @@ bool ElementTransitionModifier::load(ModifierLoaderContext &context, const Data: return true; } +bool ElementTransitionModifier::respondsToEvent(const Event &evt) const { + return _enableWhen.respondsTo(evt) || _disableWhen.respondsTo(evt); +} + +VThreadState ElementTransitionModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr &msg) { + // How element transition modifiers work: + // - When activated, if Reveal, then Show is sent (regardless of whether element is visible or not). + // - Then, for both reveal and conceal, Transition Started is sent by the modifier. + // - When a conceal transition completes, Hide is sent to the element. + // - Then, for both reveal and conceal, Transition Ended is sent by the modifier. + // - If a transition is active and the Disable signal is called, then the transition completes immediately. + // + // Q. Does that mean if an element has a Revealer followed by a Concealer and they take opposing messages, + // that the element will be hidden if a reveal interrupts the concealer due to its hide message happening + // second, but that won't happen if they're in Concealer-Revealer order? + // A. Yes. + if (_enableWhen.respondsTo(msg->getEvent())) { + if (_scheduledEvent) { + _scheduledEvent->cancel(); + _scheduledEvent.reset(); + } + + _scheduledEvent = runtime->getScheduler().scheduleMethod(runtime->getPlayTime() + 1, this); + + // Pushed tasks, so these are executed in reverse order (Show -> Transition Started) + { + Common::SharedPtr msgProps(new MessageProperties(Event::create(EventIDs::kTransitionStarted, 0), DynamicValue(), getSelfReference())); + Common::SharedPtr dispatch(new MessageDispatch(msgProps, findStructuralOwner(), false, true, false)); + runtime->sendMessageOnVThread(dispatch); + } + + if (_revealType == kRevealTypeReveal) + { + Common::SharedPtr msgProps(new MessageProperties(Event::create(EventIDs::kElementShow, 0), DynamicValue(), getSelfReference())); + Common::SharedPtr dispatch(new MessageDispatch(msgProps, findStructuralOwner(), false, false, true)); + runtime->sendMessageOnVThread(dispatch); + } + + return kVThreadReturn; + } + + if (_disableWhen.respondsTo(msg->getEvent())) { + if (_scheduledEvent) { + _scheduledEvent->cancel(); + + completeTransition(runtime); + } + + return kVThreadReturn; + } + + return Modifier::consumeMessage(runtime, msg); +} + Common::SharedPtr ElementTransitionModifier::shallowClone() const { - return Common::SharedPtr(new ElementTransitionModifier(*this)); + Common::SharedPtr clone(new ElementTransitionModifier(*this)); + clone->_scheduledEvent.reset(); + + return clone; } const char *ElementTransitionModifier::getDefaultName() const { return "Element Transition Modifier"; } +void ElementTransitionModifier::continueTransition(Runtime *runtime) { + // TODO: Make this functional + completeTransition(runtime); +} + +void ElementTransitionModifier::completeTransition(Runtime *runtime) { + _scheduledEvent.reset(); + + // Pushed tasks, so these are executed in reverse order (Hide -> Transition Ended) + { + Common::SharedPtr msgProps(new MessageProperties(Event::create(EventIDs::kTransitionEnded, 0), DynamicValue(), getSelfReference())); + Common::SharedPtr dispatch(new MessageDispatch(msgProps, findStructuralOwner(), false, true, false)); + runtime->sendMessageOnVThread(dispatch); + } + + if (_revealType == kRevealTypeConceal) { + Common::SharedPtr msgProps(new MessageProperties(Event::create(EventIDs::kElementHide, 0), DynamicValue(), getSelfReference())); + Common::SharedPtr dispatch(new MessageDispatch(msgProps, findStructuralOwner(), false, false, true)); + runtime->sendMessageOnVThread(dispatch); + } +} + bool IfMessengerModifier::load(ModifierLoaderContext &context, const Data::IfMessengerModifier &data) { if (!loadTypicalHeader(data.modHeader)) return false; diff --git a/engines/mtropolis/modifiers.h b/engines/mtropolis/modifiers.h index a6b2543ffb3..f0b0d55e1bc 100644 --- a/engines/mtropolis/modifiers.h +++ b/engines/mtropolis/modifiers.h @@ -368,6 +368,9 @@ class ElementTransitionModifier : public Modifier { public: bool load(ModifierLoaderContext &context, const Data::ElementTransitionModifier &data); + bool respondsToEvent(const Event &evt) const override; + VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr &msg) override; + enum TransitionType { kTransitionTypeRectangularIris = 0x03e8, kTransitionTypeOvalIris = 0x03f2, @@ -382,12 +385,16 @@ public: #ifdef MTROPOLIS_DEBUG_ENABLE const char *debugGetTypeName() const override { return "Element Transition Modifier"; } + SupportStatus debugGetSupportStatus() const override { return kSupportStatusPartial; } #endif private: Common::SharedPtr shallowClone() const override; const char *getDefaultName() const override; + void continueTransition(Runtime *runtime); + void completeTransition(Runtime *runtime); + Event _enableWhen; Event _disableWhen; @@ -395,6 +402,8 @@ private: uint16 _steps; TransitionType _transitionType; RevealType _revealType; + + Common::SharedPtr _scheduledEvent; }; class IfMessengerModifier : public Modifier { diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp index c873177856b..d93b7eb7447 100644 --- a/engines/mtropolis/runtime.cpp +++ b/engines/mtropolis/runtime.cpp @@ -6418,6 +6418,36 @@ uint16 VisualElement::getLayer() const { return _layer; } +VThreadState VisualElement::consumeCommand(Runtime *runtime, const Common::SharedPtr &msg) { + if (Event::create(EventIDs::kElementShow, 0).respondsTo(msg->getEvent())) { + if (!_visible) { + _visible = true; + runtime->setSceneGraphDirty(); + } + + Common::SharedPtr msgProps(new MessageProperties(Event::create(EventIDs::kElementShow, 0), DynamicValue(), getSelfReference())); + Common::SharedPtr dispatch(new MessageDispatch(msgProps, this, false, true, false)); + runtime->sendMessageOnVThread(dispatch); + + return kVThreadReturn; + } + + if (Event::create(EventIDs::kElementHide, 0).respondsTo(msg->getEvent())) { + if (_visible) { + _visible = false; + runtime->setSceneGraphDirty(); + } + + Common::SharedPtr msgProps(new MessageProperties(Event::create(EventIDs::kElementHide, 0), DynamicValue(), getSelfReference())); + Common::SharedPtr dispatch(new MessageDispatch(msgProps, this, false, true, false)); + runtime->sendMessageOnVThread(dispatch); + + return kVThreadReturn; + } + + return Element::consumeCommand(runtime, msg); +} + bool VisualElement::isMouseInsideBox(int32 relativeX, int32 relativeY) const { return relativeX >= _rect.left && relativeX < _rect.right && relativeY >= _rect.top && relativeY < _rect.bottom; } @@ -6593,7 +6623,11 @@ void VisualElement::finalizeRender() { MiniscriptInstructionOutcome VisualElement::scriptSetVisibility(MiniscriptThread *thread, const DynamicValue &result) { // FIXME: Need to make this fire Show/Hide events! if (result.getType() == DynamicValueTypes::kBoolean) { - _visible = result.getBool(); + const bool targetValue = result.getBool(); + if (_visible != targetValue) { + _visible = targetValue; + thread->getRuntime()->setSceneGraphDirty(); + } return kMiniscriptInstructionOutcomeContinue; } diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h index 44ffa04922e..61d0fc8f65b 100644 --- a/engines/mtropolis/runtime.h +++ b/engines/mtropolis/runtime.h @@ -2417,6 +2417,8 @@ public: bool isVisual() const override; virtual bool isTextLabel() const; + VThreadState consumeCommand(Runtime *runtime, const Common::SharedPtr &msg); + bool isVisible() const; bool isDirectToScreen() const; uint16 getLayer() const;