using Godot; using System.Collections.Generic; using Rokojori.Extensions; namespace Rokojori; [Tool] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/StateMachine.svg")] public partial class StateMachine:SequenceAction { [ExportGroup( "Debugging")] [Export] public State currentState; [Export] public bool printStateChanges = false; [Export] public bool printTransitionChanges = false; int _runID = -1; List _receptors = new List(); protected override void _OnTrigger() { if ( currentState != null ) { // Already running. return; } var states = this.GetDirectChildren(); if ( states.Count == 0 ) { return; } _runID = DispatchStart(); StartReceptors( states ); EnterState( states[ 0 ], null ); } public override void _Process( double delta ) { if ( Engine.IsEditorHint() || currentState == null ) { return; } CheckTransitions(); } Dictionary _transitions = new Dictionary(); void CheckTransitions() { var transitions = currentState.GetDirectChildren(); for ( int i = 0; i < transitions.Count; i++ ) { var transition = transitions[ i ]; var isTriggered = transition.IsTriggered(); var before = ! _transitions.ContainsKey( transition ) ? false : _transitions[ transition ]; if ( before != isTriggered ) { _transitions[ transition ] = isTriggered; RJLog.Log( HierarchyName.Of( transition ), isTriggered ); } if ( isTriggered ) { DoTransition( transition.targetState ); transition.onTransition?.Trigger(); return; } } } void DoTransition( State target ) { var previous = currentState; if ( target == previous ) { ReinitializeState( previous ); return; } ExitState( previous, target ); if ( target == null ) { EndStateMachine(); return; } EnterState( target, previous ); } void EnterState( State state, State previous ) { currentState = state; if ( printStateChanges ) { this.LogInfo( "Entering State:", HierarchyName.Of( state ) ); } state.ForEachDirectChild( oe => { if ( oe.Matches( previous ) ) { oe.Trigger(); } } ); state.ForEachDirectChild( oa => oa.Trigger() ); } void ExitState( State state, State next ) { if ( printStateChanges ) { this.LogInfo( "Exiting State:", HierarchyName.Of( state ) ); } state.ForEachDirectChild( oe => { if ( oe.Matches( next ) ) { oe.Trigger(); } } ); state.ForEachDirectChild( oa => oa.CancelAllRunning() ); } void ReinitializeState( State state ) { state.ForEachDirectChild( oe => { if ( oe.Matches( state ) ) { oe.Trigger(); } } ); state.ForEachDirectChild( oe => { if ( oe.Matches( state ) ) { oe.Trigger(); } } ); state.ForEachDirectChild( oa => { if ( ! oa.reinitialize ) { return; } if ( oa.isExclusive && oa.isRunning ) { return; } oa.CancelAllRunning(); oa.Trigger(); } ); } void StartReceptors( List states ) { _receptors.Clear(); states.ForEach( s => s.ForEachDirectChild( t => { if ( t.receptor != null ) { _receptors.Add( t.receptor ); } } ) ); _receptors.ForEach( r => r.Start() ); } void StopReceptors() { _receptors.ForEach( r => r.Stop() ); _receptors.Clear(); } void EndStateMachine() { currentState = null; SetProcess( false ); StopReceptors(); DispatchEnd( _runID ); } public override void CancelAction( int id ) { if ( id != _runID || currentState == null ) { return; } currentState = null; SetProcess( false ); StopReceptors(); DispatchCancelled( id ); } }