feature: add support for firing triggers on state transitions (#10329) cc34f96631
* feature: add support for fiiring triggers on state transitions

Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head
index 76045c1..4216257 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-64f828c9a278586f61b7636da99d68404c906176
+cc34f96631a6279e94227c10862cdde8ebf6b478
diff --git a/dev/defs/animation/state_machine_fire_action.json b/dev/defs/animation/state_machine_fire_action.json
new file mode 100644
index 0000000..4dc81dc
--- /dev/null
+++ b/dev/defs/animation/state_machine_fire_action.json
@@ -0,0 +1,39 @@
+{
+  "name": "StateMachineFireAction",
+  "key": {
+    "int": 615,
+    "string": "statemachinefireaction"
+  },
+  "abstract": true,
+  "properties": {
+    "layerComponentId": {
+      "type": "Id",
+      "initialValue": "Core.missingId",
+      "key": {
+        "int": 391,
+        "string": "layercomponentid"
+      },
+      "description": "Id of the transition or layer this belongs to.",
+      "runtime": false
+    },
+    "occursValue": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 393,
+        "string": "occursvalue"
+      },
+      "description": "When the event fires."
+    },
+    "fireOrder": {
+      "type": "FractionalIndex",
+      "initialValue": "FractionalIndex.invalid",
+      "key": {
+        "int": 394,
+        "string": "fireorder"
+      },
+      "description": "Order value for sorting transitions in states.",
+      "runtime": false
+    }
+  }
+}
\ No newline at end of file
diff --git a/dev/defs/animation/state_machine_fire_event.json b/dev/defs/animation/state_machine_fire_event.json
index 41db898..e713cba 100644
--- a/dev/defs/animation/state_machine_fire_event.json
+++ b/dev/defs/animation/state_machine_fire_event.json
@@ -4,17 +4,8 @@
     "int": 169,
     "string": "statemachinefireevent"
   },
+  "extends": "animation/state_machine_fire_action.json",
   "properties": {
-    "layerComponentId": {
-      "type": "Id",
-      "initialValue": "Core.missingId",
-      "key": {
-        "int": 391,
-        "string": "layercomponentid"
-      },
-      "description": "Id of the transition or layer this belongs to.",
-      "runtime": false
-    },
     "eventId": {
       "type": "Id",
       "typeRuntime": "uint",
@@ -25,25 +16,6 @@
         "string": "eventid"
       },
       "description": "Id of the Event referenced."
-    },
-    "occursValue": {
-      "type": "uint",
-      "initialValue": "0",
-      "key": {
-        "int": 393,
-        "string": "occursvalue"
-      },
-      "description": "When the event fires."
-    },
-    "fireOrder": {
-      "type": "FractionalIndex",
-      "initialValue": "FractionalIndex.invalid",
-      "key": {
-        "int": 394,
-        "string": "fireorder"
-      },
-      "description": "Order value for sorting transitions in states.",
-      "runtime": false
     }
   }
 }
\ No newline at end of file
diff --git a/dev/defs/animation/state_machine_fire_trigger.json b/dev/defs/animation/state_machine_fire_trigger.json
new file mode 100644
index 0000000..67c31e0
--- /dev/null
+++ b/dev/defs/animation/state_machine_fire_trigger.json
@@ -0,0 +1,21 @@
+{
+  "name": "StateMachineFireTrigger",
+  "key": {
+    "int": 614,
+    "string": "statemachinefiretrigger"
+  },
+  "extends": "animation/state_machine_fire_action.json",
+  "properties": {
+    "viewModelPathIds": {
+      "type": "List<Id>",
+      "typeRuntime": "Bytes",
+      "encoded": true,
+      "initialValue": "[]",
+      "key": {
+        "int": 871,
+        "string": "viewmodelpathids"
+      },
+      "description": "Path to the selected view model trigger property."
+    }
+  }
+}
\ No newline at end of file
diff --git a/include/rive/animation/state_machine_fire_action.hpp b/include/rive/animation/state_machine_fire_action.hpp
new file mode 100644
index 0000000..35fb477
--- /dev/null
+++ b/include/rive/animation/state_machine_fire_action.hpp
@@ -0,0 +1,25 @@
+#ifndef _RIVE_STATE_MACHINE_FIRE_ACTION_HPP_
+#define _RIVE_STATE_MACHINE_FIRE_ACTION_HPP_
+#include "rive/generated/animation/state_machine_fire_action_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class StateMachineInstance;
+enum class StateMachineFireOccurance : int
+{
+    atStart = 0,
+    atEnd = 1
+};
+class StateMachineFireAction : public StateMachineFireActionBase
+{
+public:
+    StateMachineFireOccurance occurs() const
+    {
+        return (StateMachineFireOccurance)occursValue();
+    }
+    StatusCode import(ImportStack& importStack) override;
+    virtual void perform(StateMachineInstance* stateMachineInstance) const = 0;
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/animation/state_machine_fire_event.hpp b/include/rive/animation/state_machine_fire_event.hpp
index 9927cfd..86173ed 100644
--- a/include/rive/animation/state_machine_fire_event.hpp
+++ b/include/rive/animation/state_machine_fire_event.hpp
@@ -4,22 +4,11 @@
 
 namespace rive
 {
-class StateMachineInstance;
-enum class StateMachineFireOccurance : int
-{
-    atStart = 0,
-    atEnd = 1
-};
 
 class StateMachineFireEvent : public StateMachineFireEventBase
 {
 public:
-    StatusCode import(ImportStack& importStack) override;
-    StateMachineFireOccurance occurs() const
-    {
-        return (StateMachineFireOccurance)occursValue();
-    }
-    void perform(StateMachineInstance* stateMachineInstance) const;
+    void perform(StateMachineInstance* stateMachineInstance) const override;
 };
 
 } // namespace rive
diff --git a/include/rive/animation/state_machine_fire_trigger.hpp b/include/rive/animation/state_machine_fire_trigger.hpp
new file mode 100644
index 0000000..0758890
--- /dev/null
+++ b/include/rive/animation/state_machine_fire_trigger.hpp
@@ -0,0 +1,20 @@
+#ifndef _RIVE_STATE_MACHINE_FIRE_TRIGGER_HPP_
+#define _RIVE_STATE_MACHINE_FIRE_TRIGGER_HPP_
+#include "rive/generated/animation/state_machine_fire_trigger_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class StateMachineFireTrigger : public StateMachineFireTriggerBase
+{
+public:
+    void perform(StateMachineInstance* stateMachineInstance) const override;
+    void decodeViewModelPathIds(Span<const uint8_t> value) override;
+    void copyViewModelPathIds(
+        const StateMachineFireTriggerBase& object) override;
+
+protected:
+    std::vector<uint32_t> m_viewModelPathIdsBuffer;
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/animation/state_machine_layer_component.hpp b/include/rive/animation/state_machine_layer_component.hpp
index d94266d..4ff309e 100644
--- a/include/rive/animation/state_machine_layer_component.hpp
+++ b/include/rive/animation/state_machine_layer_component.hpp
@@ -5,20 +5,20 @@
 
 namespace rive
 {
-class StateMachineFireEvent;
+class StateMachineFireAction;
 class StateMachineLayerComponent : public StateMachineLayerComponentBase
 {
     friend class StateMachineLayerComponentImporter;
 
 public:
-    const std::vector<StateMachineFireEvent*>& events() const
+    const std::vector<StateMachineFireAction*>& events() const
     {
         return m_events;
     }
     ~StateMachineLayerComponent() override;
 
 private:
-    std::vector<StateMachineFireEvent*> m_events;
+    std::vector<StateMachineFireAction*> m_events;
 };
 } // namespace rive
 
diff --git a/include/rive/generated/animation/state_machine_fire_action_base.hpp b/include/rive/generated/animation/state_machine_fire_action_base.hpp
new file mode 100644
index 0000000..a991072
--- /dev/null
+++ b/include/rive/generated/animation/state_machine_fire_action_base.hpp
@@ -0,0 +1,68 @@
+#ifndef _RIVE_STATE_MACHINE_FIRE_ACTION_BASE_HPP_
+#define _RIVE_STATE_MACHINE_FIRE_ACTION_BASE_HPP_
+#include "rive/core.hpp"
+#include "rive/core/field_types/core_uint_type.hpp"
+namespace rive
+{
+class StateMachineFireActionBase : public Core
+{
+protected:
+    typedef Core Super;
+
+public:
+    static const uint16_t typeKey = 615;
+
+    /// Helper to quickly determine if a core object extends another without
+    /// RTTI at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case StateMachineFireActionBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t occursValuePropertyKey = 393;
+
+protected:
+    uint32_t m_OccursValue = 0;
+
+public:
+    inline uint32_t occursValue() const { return m_OccursValue; }
+    void occursValue(uint32_t value)
+    {
+        if (m_OccursValue == value)
+        {
+            return;
+        }
+        m_OccursValue = value;
+        occursValueChanged();
+    }
+
+    void copy(const StateMachineFireActionBase& object)
+    {
+        m_OccursValue = object.m_OccursValue;
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case occursValuePropertyKey:
+                m_OccursValue = CoreUintType::deserialize(reader);
+                return true;
+        }
+        return false;
+    }
+
+protected:
+    virtual void occursValueChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/animation/state_machine_fire_event_base.hpp b/include/rive/generated/animation/state_machine_fire_event_base.hpp
index 34138da..6da429a 100644
--- a/include/rive/generated/animation/state_machine_fire_event_base.hpp
+++ b/include/rive/generated/animation/state_machine_fire_event_base.hpp
@@ -1,13 +1,13 @@
 #ifndef _RIVE_STATE_MACHINE_FIRE_EVENT_BASE_HPP_
 #define _RIVE_STATE_MACHINE_FIRE_EVENT_BASE_HPP_
-#include "rive/core.hpp"
+#include "rive/animation/state_machine_fire_action.hpp"
 #include "rive/core/field_types/core_uint_type.hpp"
 namespace rive
 {
-class StateMachineFireEventBase : public Core
+class StateMachineFireEventBase : public StateMachineFireAction
 {
 protected:
-    typedef Core Super;
+    typedef StateMachineFireAction Super;
 
 public:
     static const uint16_t typeKey = 169;
@@ -19,6 +19,7 @@
         switch (typeKey)
         {
             case StateMachineFireEventBase::typeKey:
+            case StateMachineFireActionBase::typeKey:
                 return true;
             default:
                 return false;
@@ -28,11 +29,9 @@
     uint16_t coreType() const override { return typeKey; }
 
     static const uint16_t eventIdPropertyKey = 392;
-    static const uint16_t occursValuePropertyKey = 393;
 
 protected:
     uint32_t m_EventId = -1;
-    uint32_t m_OccursValue = 0;
 
 public:
     inline uint32_t eventId() const { return m_EventId; }
@@ -46,22 +45,11 @@
         eventIdChanged();
     }
 
-    inline uint32_t occursValue() const { return m_OccursValue; }
-    void occursValue(uint32_t value)
-    {
-        if (m_OccursValue == value)
-        {
-            return;
-        }
-        m_OccursValue = value;
-        occursValueChanged();
-    }
-
     Core* clone() const override;
     void copy(const StateMachineFireEventBase& object)
     {
         m_EventId = object.m_EventId;
-        m_OccursValue = object.m_OccursValue;
+        StateMachineFireAction::copy(object);
     }
 
     bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
@@ -71,16 +59,12 @@
             case eventIdPropertyKey:
                 m_EventId = CoreUintType::deserialize(reader);
                 return true;
-            case occursValuePropertyKey:
-                m_OccursValue = CoreUintType::deserialize(reader);
-                return true;
         }
-        return false;
+        return StateMachineFireAction::deserialize(propertyKey, reader);
     }
 
 protected:
     virtual void eventIdChanged() {}
-    virtual void occursValueChanged() {}
 };
 } // namespace rive
 
diff --git a/include/rive/generated/animation/state_machine_fire_trigger_base.hpp b/include/rive/generated/animation/state_machine_fire_trigger_base.hpp
new file mode 100644
index 0000000..311bee3
--- /dev/null
+++ b/include/rive/generated/animation/state_machine_fire_trigger_base.hpp
@@ -0,0 +1,62 @@
+#ifndef _RIVE_STATE_MACHINE_FIRE_TRIGGER_BASE_HPP_
+#define _RIVE_STATE_MACHINE_FIRE_TRIGGER_BASE_HPP_
+#include "rive/animation/state_machine_fire_action.hpp"
+#include "rive/core/field_types/core_bytes_type.hpp"
+#include "rive/span.hpp"
+namespace rive
+{
+class StateMachineFireTriggerBase : public StateMachineFireAction
+{
+protected:
+    typedef StateMachineFireAction Super;
+
+public:
+    static const uint16_t typeKey = 614;
+
+    /// Helper to quickly determine if a core object extends another without
+    /// RTTI at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case StateMachineFireTriggerBase::typeKey:
+            case StateMachineFireActionBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t viewModelPathIdsPropertyKey = 871;
+
+public:
+    virtual void decodeViewModelPathIds(Span<const uint8_t> value) = 0;
+    virtual void copyViewModelPathIds(
+        const StateMachineFireTriggerBase& object) = 0;
+
+    Core* clone() const override;
+    void copy(const StateMachineFireTriggerBase& object)
+    {
+        copyViewModelPathIds(object);
+        StateMachineFireAction::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case viewModelPathIdsPropertyKey:
+                decodeViewModelPathIds(CoreBytesType::deserialize(reader));
+                return true;
+        }
+        return StateMachineFireAction::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void viewModelPathIdsChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index c880aef..18728c2 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -53,7 +53,9 @@
 #include "rive/animation/state_machine.hpp"
 #include "rive/animation/state_machine_bool.hpp"
 #include "rive/animation/state_machine_component.hpp"
+#include "rive/animation/state_machine_fire_action.hpp"
 #include "rive/animation/state_machine_fire_event.hpp"
+#include "rive/animation/state_machine_fire_trigger.hpp"
 #include "rive/animation/state_machine_input.hpp"
 #include "rive/animation/state_machine_layer.hpp"
 #include "rive/animation/state_machine_layer_component.hpp"
@@ -423,6 +425,8 @@
                 return new ListenerFireEvent();
             case TransitionSelfComparatorBase::typeKey:
                 return new TransitionSelfComparator();
+            case StateMachineFireTriggerBase::typeKey:
+                return new StateMachineFireTrigger();
             case TransitionValueTriggerComparatorBase::typeKey:
                 return new TransitionValueTriggerComparator();
             case KeyFrameUintBase::typeKey:
@@ -1079,6 +1083,9 @@
             case LayerStateBase::flagsPropertyKey:
                 object->as<LayerStateBase>()->flags(value);
                 break;
+            case StateMachineFireActionBase::occursValuePropertyKey:
+                object->as<StateMachineFireActionBase>()->occursValue(value);
+                break;
             case TransitionValueTriggerComparatorBase::valuePropertyKey:
                 object->as<TransitionValueTriggerComparatorBase>()->value(
                     value);
@@ -1186,9 +1193,6 @@
             case StateMachineFireEventBase::eventIdPropertyKey:
                 object->as<StateMachineFireEventBase>()->eventId(value);
                 break;
-            case StateMachineFireEventBase::occursValuePropertyKey:
-                object->as<StateMachineFireEventBase>()->occursValue(value);
-                break;
             case LinearAnimationBase::fpsPropertyKey:
                 object->as<LinearAnimationBase>()->fps(value);
                 break;
@@ -2572,6 +2576,8 @@
                 return object->as<ListenerFireEventBase>()->eventId();
             case LayerStateBase::flagsPropertyKey:
                 return object->as<LayerStateBase>()->flags();
+            case StateMachineFireActionBase::occursValuePropertyKey:
+                return object->as<StateMachineFireActionBase>()->occursValue();
             case TransitionValueTriggerComparatorBase::valuePropertyKey:
                 return object->as<TransitionValueTriggerComparatorBase>()
                     ->value();
@@ -2647,8 +2653,6 @@
                 return object->as<StateTransitionBase>()->randomWeight();
             case StateMachineFireEventBase::eventIdPropertyKey:
                 return object->as<StateMachineFireEventBase>()->eventId();
-            case StateMachineFireEventBase::occursValuePropertyKey:
-                return object->as<StateMachineFireEventBase>()->occursValue();
             case LinearAnimationBase::fpsPropertyKey:
                 return object->as<LinearAnimationBase>()->fps();
             case LinearAnimationBase::durationPropertyKey:
@@ -3533,6 +3537,7 @@
                 instanceHeightScaleTypePropertyKey:
             case ListenerFireEventBase::eventIdPropertyKey:
             case LayerStateBase::flagsPropertyKey:
+            case StateMachineFireActionBase::occursValuePropertyKey:
             case TransitionValueTriggerComparatorBase::valuePropertyKey:
             case KeyFrameBase::framePropertyKey:
             case InterpolatingKeyFrameBase::interpolationTypePropertyKey:
@@ -3568,7 +3573,6 @@
             case StateTransitionBase::interpolatorIdPropertyKey:
             case StateTransitionBase::randomWeightPropertyKey:
             case StateMachineFireEventBase::eventIdPropertyKey:
-            case StateMachineFireEventBase::occursValuePropertyKey:
             case LinearAnimationBase::fpsPropertyKey:
             case LinearAnimationBase::durationPropertyKey:
             case LinearAnimationBase::loopValuePropertyKey:
@@ -3937,6 +3941,7 @@
             case ExportAudioBase::volumePropertyKey:
                 return CoreDoubleType::id;
             case NestedArtboardBase::dataBindPathIdsPropertyKey:
+            case StateMachineFireTriggerBase::viewModelPathIdsPropertyKey:
             case StateMachineListenerBase::viewModelPathIdsPropertyKey:
             case MeshBase::triangleIndexBytesPropertyKey:
             case DataConverterOperationViewModelBase::sourcePathIdsPropertyKey:
@@ -4145,6 +4150,8 @@
                 return object->is<ListenerFireEventBase>();
             case LayerStateBase::flagsPropertyKey:
                 return object->is<LayerStateBase>();
+            case StateMachineFireActionBase::occursValuePropertyKey:
+                return object->is<StateMachineFireActionBase>();
             case TransitionValueTriggerComparatorBase::valuePropertyKey:
                 return object->is<TransitionValueTriggerComparatorBase>();
             case KeyFrameBase::framePropertyKey:
@@ -4214,8 +4221,6 @@
                 return object->is<StateTransitionBase>();
             case StateMachineFireEventBase::eventIdPropertyKey:
                 return object->is<StateMachineFireEventBase>();
-            case StateMachineFireEventBase::occursValuePropertyKey:
-                return object->is<StateMachineFireEventBase>();
             case LinearAnimationBase::fpsPropertyKey:
                 return object->is<LinearAnimationBase>();
             case LinearAnimationBase::durationPropertyKey:
diff --git a/include/rive/importers/state_machine_layer_component_importer.hpp b/include/rive/importers/state_machine_layer_component_importer.hpp
index b9132a2..621bf79 100644
--- a/include/rive/importers/state_machine_layer_component_importer.hpp
+++ b/include/rive/importers/state_machine_layer_component_importer.hpp
@@ -6,14 +6,14 @@
 namespace rive
 {
 class StateMachineLayerComponent;
-class StateMachineFireEvent;
+class StateMachineFireAction;
 
 class StateMachineLayerComponentImporter : public ImportStackObject
 {
 public:
     StateMachineLayerComponentImporter(StateMachineLayerComponent* component);
 
-    void addFireEvent(StateMachineFireEvent* fireEvent);
+    void addFireEvent(StateMachineFireAction* fireEvent);
 
 private:
     StateMachineLayerComponent* m_stateMachineLayerComponent;
diff --git a/src/animation/state_machine_fire_action.cpp b/src/animation/state_machine_fire_action.cpp
new file mode 100644
index 0000000..f6b8089
--- /dev/null
+++ b/src/animation/state_machine_fire_action.cpp
@@ -0,0 +1,20 @@
+#include "rive/generated/animation/state_machine_fire_action_base.hpp"
+#include "rive/animation/state_machine_fire_event.hpp"
+#include "rive/animation/state_machine_layer_component.hpp"
+#include "rive/animation/state_machine_instance.hpp"
+#include "rive/event.hpp"
+#include "rive/importers/state_machine_layer_component_importer.hpp"
+
+using namespace rive;
+
+StatusCode StateMachineFireAction::import(ImportStack& importStack)
+{
+    auto stateImporter = importStack.latest<StateMachineLayerComponentImporter>(
+        StateMachineLayerComponent::typeKey);
+    if (stateImporter == nullptr)
+    {
+        return StatusCode::MissingObject;
+    }
+    stateImporter->addFireEvent(this);
+    return Super::import(importStack);
+}
\ No newline at end of file
diff --git a/src/animation/state_machine_fire_trigger.cpp b/src/animation/state_machine_fire_trigger.cpp
new file mode 100644
index 0000000..7b23348
--- /dev/null
+++ b/src/animation/state_machine_fire_trigger.cpp
@@ -0,0 +1,37 @@
+#include "rive/animation/state_machine_fire_trigger.hpp"
+#include "rive/animation/state_machine_instance.hpp"
+#include "rive/viewmodel/viewmodel_instance_trigger.hpp"
+
+using namespace rive;
+
+void StateMachineFireTrigger::perform(
+    StateMachineInstance* stateMachineInstance) const
+{
+    auto dataContext = stateMachineInstance->dataContext();
+    if (dataContext != nullptr)
+    {
+        auto vmProp =
+            dataContext->getViewModelProperty(m_viewModelPathIdsBuffer);
+        if (vmProp && vmProp->is<ViewModelInstanceTrigger>())
+        {
+            vmProp->as<ViewModelInstanceTrigger>()->trigger();
+        }
+    }
+}
+
+void StateMachineFireTrigger::decodeViewModelPathIds(Span<const uint8_t> value)
+{
+    BinaryReader reader(value);
+    while (!reader.reachedEnd())
+    {
+        auto val = reader.readVarUintAs<uint32_t>();
+        m_viewModelPathIdsBuffer.push_back(val);
+    }
+}
+
+void StateMachineFireTrigger::copyViewModelPathIds(
+    const StateMachineFireTriggerBase& object)
+{
+    m_viewModelPathIdsBuffer =
+        object.as<StateMachineFireTrigger>()->m_viewModelPathIdsBuffer;
+}
\ No newline at end of file
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index e0f70fc..fe3050f 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -187,7 +187,7 @@
     }
 
     void fireEvents(StateMachineFireOccurance occurs,
-                    const std::vector<StateMachineFireEvent*>& fireEvents)
+                    const std::vector<StateMachineFireAction*>& fireEvents)
     {
         for (auto event : fireEvents)
         {
diff --git a/src/generated/animation/state_machine_fire_action_base.cpp b/src/generated/animation/state_machine_fire_action_base.cpp
new file mode 100644
index 0000000..cd8eec4
--- /dev/null
+++ b/src/generated/animation/state_machine_fire_action_base.cpp
@@ -0,0 +1,4 @@
+#include "rive/generated/animation/state_machine_fire_action_base.hpp"
+#include "rive/animation/state_machine_fire_action.hpp"
+
+using namespace rive;
diff --git a/src/generated/animation/state_machine_fire_event.cpp b/src/generated/animation/state_machine_fire_event.cpp
index 568d11b..5b2a2c2 100644
--- a/src/generated/animation/state_machine_fire_event.cpp
+++ b/src/generated/animation/state_machine_fire_event.cpp
@@ -7,18 +7,6 @@
 
 using namespace rive;
 
-StatusCode StateMachineFireEvent::import(ImportStack& importStack)
-{
-    auto stateImporter = importStack.latest<StateMachineLayerComponentImporter>(
-        StateMachineLayerComponent::typeKey);
-    if (stateImporter == nullptr)
-    {
-        return StatusCode::MissingObject;
-    }
-    stateImporter->addFireEvent(this);
-    return Super::import(importStack);
-}
-
 void StateMachineFireEvent::perform(
     StateMachineInstance* stateMachineInstance) const
 {
diff --git a/src/generated/animation/state_machine_fire_trigger_base.cpp b/src/generated/animation/state_machine_fire_trigger_base.cpp
new file mode 100644
index 0000000..734398d
--- /dev/null
+++ b/src/generated/animation/state_machine_fire_trigger_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/animation/state_machine_fire_trigger_base.hpp"
+#include "rive/animation/state_machine_fire_trigger.hpp"
+
+using namespace rive;
+
+Core* StateMachineFireTriggerBase::clone() const
+{
+    auto cloned = new StateMachineFireTrigger();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/importers/state_machine_layer_component_importer.cpp b/src/importers/state_machine_layer_component_importer.cpp
index 4ea47ca..12ab08b 100644
--- a/src/importers/state_machine_layer_component_importer.cpp
+++ b/src/importers/state_machine_layer_component_importer.cpp
@@ -1,6 +1,6 @@
 #include "rive/importers/state_machine_layer_component_importer.hpp"
 #include "rive/animation/state_machine_layer_component.hpp"
-#include "rive/animation/state_machine_fire_event.hpp"
+#include "rive/animation/state_machine_fire_action.hpp"
 
 using namespace rive;
 
@@ -18,7 +18,7 @@
 {}
 
 void StateMachineLayerComponentImporter::addFireEvent(
-    StateMachineFireEvent* fireEvent)
+    StateMachineFireAction* fireEvent)
 {
     m_stateMachineLayerComponent->m_events.push_back(fireEvent);
 }
\ No newline at end of file
diff --git a/tests/unit_tests/assets/state_transition_fire_trigger.riv b/tests/unit_tests/assets/state_transition_fire_trigger.riv
new file mode 100644
index 0000000..33f86b0
--- /dev/null
+++ b/tests/unit_tests/assets/state_transition_fire_trigger.riv
Binary files differ
diff --git a/tests/unit_tests/runtime/data_binding_test.cpp b/tests/unit_tests/runtime/data_binding_test.cpp
index 11a5935..4b6138f 100644
--- a/tests/unit_tests/runtime/data_binding_test.cpp
+++ b/tests/unit_tests/runtime/data_binding_test.cpp
@@ -1295,3 +1295,46 @@
 
     CHECK(silver.matches("data_bind_solo-solos-to-values"));
 }
+
+TEST_CASE("State machine fire triggers", "[data binding]")
+{
+
+    rive::SerializingFactory silver;
+    auto file =
+        ReadRiveFile("assets/state_transition_fire_trigger.riv", &silver);
+
+    auto artboard = file->artboardNamed("main");
+
+    silver.frameSize(artboard->width(), artboard->height());
+
+    REQUIRE(artboard != nullptr);
+    auto stateMachine = artboard->stateMachineAt(0);
+    int viewModelId = artboard.get()->viewModelId();
+
+    auto vmi = viewModelId == -1
+                   ? file->createViewModelInstance(artboard.get())
+                   : file->createViewModelInstance(viewModelId, 0);
+
+    stateMachine->bindViewModelInstance(vmi);
+    stateMachine->advanceAndApply(0.0f);
+    stateMachine->advanceAndApply(0.016f);
+
+    auto renderer = silver.makeRenderer();
+    artboard->draw(renderer.get());
+    // Advance and apply twice to take the transition and apply the next state.
+    stateMachine->advanceAndApply(0.1f);
+    stateMachine->advanceAndApply(1.0f);
+
+    silver.addFrame();
+    stateMachine->advanceAndApply(0.1f);
+    stateMachine->advanceAndApply(1.0f);
+    artboard->draw(renderer.get());
+
+    silver.addFrame();
+    // Advance and apply twice to take the transition and apply the next state.
+    stateMachine->advanceAndApply(0.1f);
+    stateMachine->advanceAndApply(1.0f);
+    artboard->draw(renderer.get());
+
+    CHECK(silver.matches("state_transition_fire_trigger"));
+}
diff --git a/tests/unit_tests/silvers/state_transition_fire_trigger.sriv b/tests/unit_tests/silvers/state_transition_fire_trigger.sriv
new file mode 100644
index 0000000..b6b5d1d
--- /dev/null
+++ b/tests/unit_tests/silvers/state_transition_fire_trigger.sriv
Binary files differ