support randomizing transitions

First version of randomization to get in UAT for validation.

Diffs=
edac19b06 support randomizing transitions (#7082)

Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head
index 929a551..58930e3 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-2828b7b013811f1f4b0b0e2ee0f6d9b23c73878b
+edac19b0600a1ede64bb0b07e61556013a3e84fe
diff --git a/dev/defs/animation/layer_state.json b/dev/defs/animation/layer_state.json
index ce57d57..b15b793 100644
--- a/dev/defs/animation/layer_state.json
+++ b/dev/defs/animation/layer_state.json
@@ -34,6 +34,14 @@
         "string": "y"
       },
       "runtime": false
+    },
+    "flags": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 536,
+        "string": "flags"
+      }
     }
   }
 }
\ No newline at end of file
diff --git a/dev/defs/animation/state_transition.json b/dev/defs/animation/state_transition.json
index 072d10e..3ac429e 100644
--- a/dev/defs/animation/state_transition.json
+++ b/dev/defs/animation/state_transition.json
@@ -82,6 +82,15 @@
         "string": "interpolatorid"
       },
       "description": "The id of the custom interpolator used when interpolation is Cubic."
+    },
+    "randomWeight": {
+      "type": "uint",
+      "initialValue": "1",
+      "key": {
+        "int": 537,
+        "string": "randomweight"
+      },
+      "description": "Weight of the transition in the overall random options"
     }
   }
 }
\ No newline at end of file
diff --git a/include/rive/animation/layer_state_flags.hpp b/include/rive/animation/layer_state_flags.hpp
new file mode 100644
index 0000000..880bd35
--- /dev/null
+++ b/include/rive/animation/layer_state_flags.hpp
@@ -0,0 +1,24 @@
+#ifndef _RIVE_LAYER_STATE_FLAGS_HPP_
+#define _RIVE_LAYER_STATE_FLAGS_HPP_
+
+#include <type_traits>
+
+namespace rive
+{
+enum class LayerStateFlags : unsigned char
+{
+    None = 0,
+
+    /// Whether the transition is disabled.
+    Random = 1 << 0,
+
+};
+
+inline constexpr LayerStateFlags operator&(LayerStateFlags lhs, LayerStateFlags rhs)
+{
+    return static_cast<LayerStateFlags>(
+        static_cast<std::underlying_type<LayerStateFlags>::type>(lhs) &
+        static_cast<std::underlying_type<LayerStateFlags>::type>(rhs));
+}
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp
index 98a5693..1232b11 100644
--- a/include/rive/animation/state_machine_instance.hpp
+++ b/include/rive/animation/state_machine_instance.hpp
@@ -43,6 +43,7 @@
     friend class SMIInput;
     friend class KeyedProperty;
     friend class HitComponent;
+    friend class StateMachineLayerInstance;
 
 private:
     /// Provide a hitListener if you want to process a down or an up for the pointer position
@@ -53,6 +54,7 @@
     InstType* getNamedInput(const std::string& name) const;
     void notifyEventListeners(const std::vector<EventReport>& events, NestedArtboard* source);
     void sortHitComponents();
+    double randomValue();
 
 public:
     StateMachineInstance(const StateMachine* machine, ArtboardInstance* instance);
diff --git a/include/rive/animation/state_transition.hpp b/include/rive/animation/state_transition.hpp
index 412ac06..c021c2c 100644
--- a/include/rive/animation/state_transition.hpp
+++ b/include/rive/animation/state_transition.hpp
@@ -35,6 +35,7 @@
         return static_cast<StateTransitionFlags>(flags());
     }
     LayerState* m_StateTo = nullptr;
+    uint32_t m_EvaluatedRandomWeight = 1;
     CubicInterpolator* m_Interpolator = nullptr;
 
     std::vector<TransitionCondition*> m_Conditions;
@@ -45,6 +46,9 @@
     const LayerState* stateTo() const { return m_StateTo; }
     inline CubicInterpolator* interpolator() const { return m_Interpolator; }
 
+    inline uint32_t evaluatedRandomWeight() const { return m_EvaluatedRandomWeight; }
+    void evaluatedRandomWeight(uint32_t value) { m_EvaluatedRandomWeight = value; }
+
     StatusCode onAddedDirty(CoreContext* context) override;
     StatusCode onAddedClean(CoreContext* context) override;
 
diff --git a/include/rive/generated/animation/layer_state_base.hpp b/include/rive/generated/animation/layer_state_base.hpp
index a14df6c..f561138 100644
--- a/include/rive/generated/animation/layer_state_base.hpp
+++ b/include/rive/generated/animation/layer_state_base.hpp
@@ -1,6 +1,7 @@
 #ifndef _RIVE_LAYER_STATE_BASE_HPP_
 #define _RIVE_LAYER_STATE_BASE_HPP_
 #include "rive/animation/state_machine_layer_component.hpp"
+#include "rive/core/field_types/core_uint_type.hpp"
 namespace rive
 {
 class LayerStateBase : public StateMachineLayerComponent
@@ -27,7 +28,42 @@
 
     uint16_t coreType() const override { return typeKey; }
 
+    static const uint16_t flagsPropertyKey = 535;
+
+private:
+    uint32_t m_Flags = 0;
+
+public:
+    inline uint32_t flags() const { return m_Flags; }
+    void flags(uint32_t value)
+    {
+        if (m_Flags == value)
+        {
+            return;
+        }
+        m_Flags = value;
+        flagsChanged();
+    }
+
+    void copy(const LayerStateBase& object)
+    {
+        m_Flags = object.m_Flags;
+        StateMachineLayerComponent::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case flagsPropertyKey:
+                m_Flags = CoreUintType::deserialize(reader);
+                return true;
+        }
+        return StateMachineLayerComponent::deserialize(propertyKey, reader);
+    }
+
 protected:
+    virtual void flagsChanged() {}
 };
 } // namespace rive
 
diff --git a/include/rive/generated/animation/state_transition_base.hpp b/include/rive/generated/animation/state_transition_base.hpp
index 9b526ac..2deeb09 100644
--- a/include/rive/generated/animation/state_transition_base.hpp
+++ b/include/rive/generated/animation/state_transition_base.hpp
@@ -34,6 +34,7 @@
     static const uint16_t exitTimePropertyKey = 160;
     static const uint16_t interpolationTypePropertyKey = 349;
     static const uint16_t interpolatorIdPropertyKey = 350;
+    static const uint16_t randomWeightPropertyKey = 536;
 
 private:
     uint32_t m_StateToId = -1;
@@ -42,6 +43,7 @@
     uint32_t m_ExitTime = 0;
     uint32_t m_InterpolationType = 1;
     uint32_t m_InterpolatorId = -1;
+    uint32_t m_RandomWeight = 1;
 
 public:
     inline uint32_t stateToId() const { return m_StateToId; }
@@ -110,6 +112,17 @@
         interpolatorIdChanged();
     }
 
+    inline uint32_t randomWeight() const { return m_RandomWeight; }
+    void randomWeight(uint32_t value)
+    {
+        if (m_RandomWeight == value)
+        {
+            return;
+        }
+        m_RandomWeight = value;
+        randomWeightChanged();
+    }
+
     Core* clone() const override;
     void copy(const StateTransitionBase& object)
     {
@@ -119,6 +132,7 @@
         m_ExitTime = object.m_ExitTime;
         m_InterpolationType = object.m_InterpolationType;
         m_InterpolatorId = object.m_InterpolatorId;
+        m_RandomWeight = object.m_RandomWeight;
         StateMachineLayerComponent::copy(object);
     }
 
@@ -144,6 +158,9 @@
             case interpolatorIdPropertyKey:
                 m_InterpolatorId = CoreUintType::deserialize(reader);
                 return true;
+            case randomWeightPropertyKey:
+                m_RandomWeight = CoreUintType::deserialize(reader);
+                return true;
         }
         return StateMachineLayerComponent::deserialize(propertyKey, reader);
     }
@@ -155,6 +172,7 @@
     virtual void exitTimeChanged() {}
     virtual void interpolationTypeChanged() {}
     virtual void interpolatorIdChanged() {}
+    virtual void randomWeightChanged() {}
 };
 } // namespace rive
 
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index a911f8c..0b077aa 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -460,6 +460,9 @@
             case ListenerFireEventBase::eventIdPropertyKey:
                 object->as<ListenerFireEventBase>()->eventId(value);
                 break;
+            case LayerStateBase::flagsPropertyKey:
+                object->as<LayerStateBase>()->flags(value);
+                break;
             case ListenerInputChangeBase::inputIdPropertyKey:
                 object->as<ListenerInputChangeBase>()->inputId(value);
                 break;
@@ -538,6 +541,9 @@
             case StateTransitionBase::interpolatorIdPropertyKey:
                 object->as<StateTransitionBase>()->interpolatorId(value);
                 break;
+            case StateTransitionBase::randomWeightPropertyKey:
+                object->as<StateTransitionBase>()->randomWeight(value);
+                break;
             case StateMachineFireEventBase::eventIdPropertyKey:
                 object->as<StateMachineFireEventBase>()->eventId(value);
                 break;
@@ -1256,6 +1262,8 @@
                 return object->as<SoloBase>()->activeComponentId();
             case ListenerFireEventBase::eventIdPropertyKey:
                 return object->as<ListenerFireEventBase>()->eventId();
+            case LayerStateBase::flagsPropertyKey:
+                return object->as<LayerStateBase>()->flags();
             case ListenerInputChangeBase::inputIdPropertyKey:
                 return object->as<ListenerInputChangeBase>()->inputId();
             case ListenerInputChangeBase::nestedInputIdPropertyKey:
@@ -1308,6 +1316,8 @@
                 return object->as<StateTransitionBase>()->interpolationType();
             case StateTransitionBase::interpolatorIdPropertyKey:
                 return object->as<StateTransitionBase>()->interpolatorId();
+            case StateTransitionBase::randomWeightPropertyKey:
+                return object->as<StateTransitionBase>()->randomWeight();
             case StateMachineFireEventBase::eventIdPropertyKey:
                 return object->as<StateMachineFireEventBase>()->eventId();
             case StateMachineFireEventBase::occursValuePropertyKey:
@@ -1779,6 +1789,7 @@
             case NestedAnimationBase::animationIdPropertyKey:
             case SoloBase::activeComponentIdPropertyKey:
             case ListenerFireEventBase::eventIdPropertyKey:
+            case LayerStateBase::flagsPropertyKey:
             case ListenerInputChangeBase::inputIdPropertyKey:
             case ListenerInputChangeBase::nestedInputIdPropertyKey:
             case AnimationStateBase::animationIdPropertyKey:
@@ -1805,6 +1816,7 @@
             case StateTransitionBase::exitTimePropertyKey:
             case StateTransitionBase::interpolationTypePropertyKey:
             case StateTransitionBase::interpolatorIdPropertyKey:
+            case StateTransitionBase::randomWeightPropertyKey:
             case StateMachineFireEventBase::eventIdPropertyKey:
             case StateMachineFireEventBase::occursValuePropertyKey:
             case LinearAnimationBase::fpsPropertyKey:
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index b75642a..3617c64 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -3,6 +3,7 @@
 #include "rive/animation/any_state.hpp"
 #include "rive/animation/cubic_interpolator.hpp"
 #include "rive/animation/entry_state.hpp"
+#include "rive/animation/layer_state_flags.hpp"
 #include "rive/animation/nested_state_machine.hpp"
 #include "rive/animation/state_instance.hpp"
 #include "rive/animation/state_machine_bool.hpp"
@@ -140,6 +141,13 @@
         }
     }
 
+    bool canChangeState(const LayerState* stateTo)
+    {
+        return !((m_currentState == nullptr ? nullptr : m_currentState->state()) == stateTo);
+    }
+
+    double randomValue() { return ((double)rand() / (RAND_MAX)); }
+
     bool changeState(const LayerState* stateTo)
     {
         if ((m_currentState == nullptr ? nullptr : m_currentState->state()) == stateTo)
@@ -172,73 +180,105 @@
         }
         auto stateFrom = stateFromInstance->state();
         auto outState = m_currentState;
+        uint32_t totalWeight = 0;
         for (size_t i = 0, length = stateFrom->transitionCount(); i < length; i++)
         {
             auto transition = stateFrom->transition(i);
             auto allowed =
                 transition->allowed(stateFromInstance, m_stateMachineInstance, ignoreTriggers);
-            if (allowed == AllowTransition::yes && changeState(transition->stateTo()))
+            if (allowed == AllowTransition::yes && canChangeState(transition->stateTo()))
             {
-                m_stateMachineChangedOnAdvance = true;
-                // state actually has changed
-                m_transition = transition;
-                fireEvents(StateMachineFireOccurance::atStart, transition->events());
-                if (transition->duration() == 0)
+                transition->evaluatedRandomWeight(transition->randomWeight());
+                totalWeight += transition->randomWeight();
+                if ((static_cast<LayerStateFlags>(stateFromInstance->state()->flags()) &
+                     LayerStateFlags::Random) != LayerStateFlags::Random)
                 {
-                    m_transitionCompleted = true;
-                    fireEvents(StateMachineFireOccurance::atEnd, transition->events());
+                    break;
                 }
-                else
-                {
-                    m_transitionCompleted = false;
-                }
-
-                if (m_stateFrom != m_anyStateInstance)
-                {
-                    // Old state from is done.
-                    delete m_stateFrom;
-                }
-                m_stateFrom = outState;
-
-                // If we had an exit time and wanted to pause on exit, make
-                // sure to hold the exit time. Delegate this to the
-                // transition by telling it that it was completed.
-                if (outState != nullptr && transition->applyExitCondition(outState))
-                {
-                    // Make sure we apply this state. This only returns true
-                    // when it's an animation state instance.
-                    auto instance =
-                        static_cast<AnimationStateInstance*>(m_stateFrom)->animationInstance();
-
-                    m_holdAnimation = instance->animation();
-                    m_holdTime = instance->time();
-                }
-                m_mixFrom = m_mix;
-
-                // Keep mixing last animation that was mixed in.
-                if (m_mix != 0.0f)
-                {
-                    m_holdAnimationFrom = transition->pauseOnExit();
-                }
-                if (m_stateFrom != nullptr && m_stateFrom->state()->is<AnimationState>() &&
-                    m_currentState != nullptr)
-                {
-                    auto instance =
-                        static_cast<AnimationStateInstance*>(m_stateFrom)->animationInstance();
-
-                    auto spilledTime = instance->spilledTime();
-                    m_currentState->advance(spilledTime, m_stateMachineInstance);
-                }
-                m_mix = 0.0f;
-                updateMix(0.0f);
-                m_waitingForExit = false;
-                return true;
             }
-            else if (allowed == AllowTransition::waitingForExit)
+            else
             {
-                m_waitingForExit = true;
+                transition->evaluatedRandomWeight(0);
+                if (allowed == AllowTransition::waitingForExit)
+                {
+                    m_waitingForExit = true;
+                }
             }
         }
+        if (totalWeight > 0)
+        {
+
+            double randomWeight = randomValue() * totalWeight * 1.0;
+            float currentWeight = 0;
+            size_t index = 0;
+            StateTransition* transition;
+            while (index < stateFrom->transitionCount())
+            {
+                transition = stateFrom->transition(index);
+                auto transitionWeight = transition->evaluatedRandomWeight();
+                if (currentWeight + transitionWeight > randomWeight)
+                {
+                    break;
+                }
+                currentWeight += transitionWeight;
+                index++;
+            }
+            changeState(transition->stateTo());
+            m_stateMachineChangedOnAdvance = true;
+            // state actually has changed
+            m_transition = transition;
+            fireEvents(StateMachineFireOccurance::atStart, transition->events());
+            if (transition->duration() == 0)
+            {
+                m_transitionCompleted = true;
+                fireEvents(StateMachineFireOccurance::atEnd, transition->events());
+            }
+            else
+            {
+                m_transitionCompleted = false;
+            }
+
+            if (m_stateFrom != m_anyStateInstance)
+            {
+                // Old state from is done.
+                delete m_stateFrom;
+            }
+            m_stateFrom = outState;
+
+            // If we had an exit time and wanted to pause on exit, make
+            // sure to hold the exit time. Delegate this to the
+            // transition by telling it that it was completed.
+            if (outState != nullptr && transition->applyExitCondition(outState))
+            {
+                // Make sure we apply this state. This only returns true
+                // when it's an animation state instance.
+                auto instance =
+                    static_cast<AnimationStateInstance*>(m_stateFrom)->animationInstance();
+
+                m_holdAnimation = instance->animation();
+                m_holdTime = instance->time();
+            }
+            m_mixFrom = m_mix;
+
+            // Keep mixing last animation that was mixed in.
+            if (m_mix != 0.0f)
+            {
+                m_holdAnimationFrom = transition->pauseOnExit();
+            }
+            if (m_stateFrom != nullptr && m_stateFrom->state()->is<AnimationState>() &&
+                m_currentState != nullptr)
+            {
+                auto instance =
+                    static_cast<AnimationStateInstance*>(m_stateFrom)->animationInstance();
+
+                auto spilledTime = instance->spilledTime();
+                m_currentState->advance(spilledTime, m_stateMachineInstance);
+            }
+            m_mix = 0.0f;
+            updateMix(0.0f);
+            m_waitingForExit = false;
+            return true;
+        }
         return false;
     }
 
@@ -837,4 +877,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file