rokojori_action_library/Runtime/Behavior/StateMachine/StateMachine.cs

233 lines
4.2 KiB
C#

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<Receptor> _receptors = new List<Receptor>();
protected override void _OnTrigger()
{
if ( currentState != null )
{
// Already running.
return;
}
var states = this.GetDirectChildren<State>();
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<StateTransition,bool> _transitions = new Dictionary<StateTransition, bool>();
void CheckTransitions()
{
var transitions = currentState.GetDirectChildren<StateTransition>();
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<OnStateEntered>( oe =>
{
if ( oe.Matches( previous ) )
{
oe.Trigger();
}
} );
state.ForEachDirectChild<OnStateActive>( oa => oa.Trigger() );
}
void ExitState( State state, State next )
{
if ( printStateChanges )
{
this.LogInfo( "Exiting State:", HierarchyName.Of( state ) );
}
state.ForEachDirectChild<OnStateExited>( oe =>
{
if ( oe.Matches( next ) )
{
oe.Trigger();
}
} );
state.ForEachDirectChild<OnStateActive>( oa => oa.CancelAllRunning() );
}
void ReinitializeState( State state )
{
state.ForEachDirectChild<OnStateExited>( oe =>
{
if ( oe.Matches( state ) )
{
oe.Trigger();
}
} );
state.ForEachDirectChild<OnStateEntered>( oe =>
{
if ( oe.Matches( state ) )
{
oe.Trigger();
}
} );
state.ForEachDirectChild<OnStateActive>( oa =>
{
if ( ! oa.reinitialize )
{
return;
}
if ( oa.isExclusive && oa.isRunning )
{
return;
}
oa.CancelAllRunning();
oa.Trigger();
} );
}
void StartReceptors( List<State> states )
{
_receptors.Clear();
states.ForEach( s =>
s.ForEachDirectChild<StateTransition>( 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 );
}
}