233 lines
4.2 KiB
C#
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 );
|
|
}
|
|
}
|