using System; using System.Collections; using System.Diagnostics; namespace Strawberry { public class StateMachine : Component where TIndex : struct, IHashable { private Dictionary states = new Dictionary() ~ delete _; private TIndex state; private bool inStateCall; public TIndex PreviousState { get; private set; } public TIndex NextState { get; private set; } public this(TIndex startState) : base(true, false) { NextState = PreviousState = state = startState; } public ~this() { for (let s in states.Values) delete s; } public override void Started() { CallEnter(); } public override void Update() { CallUpdate(); } public override void Draw() { } public void Add(TIndex state, delegate TIndex() enter = null, delegate TIndex() update = null, delegate TIndex() exit = null) { let s = new State(); s.Enter = enter; s.Update = update; s.Exit = exit; states[state] = s; } public TIndex State { get => state; set => Set(value); } private Result Set(TIndex to) { Debug.Assert(states.ContainsKey(to), "State does not exist in this State Machine. Call Add() first!"); Runtime.Assert(!inStateCall, "Cannot set State directly from inside a State Enter/Exit/Update call. Return the desired State change instead."); if (to != state) { NextState = to; if (CallExit()) return true; PreviousState = state; state = to; CallEnter(); return true; } else return false; } private bool CallEnter() { let s = states[state]; if (s != null && s.Enter != null) { inStateCall = true; let set = s.Enter(); inStateCall = false; return Set(set); } else return false; } private bool CallUpdate() { let s = states[state]; if (s != null && s.Update != null) { inStateCall = true; let set = s.Update(); inStateCall = false; return Set(set); } else return false; } private bool CallExit() { let s = states[state]; if (s != null && s.Exit != null) { inStateCall = true; let set = s.Exit(); inStateCall = false; return Set(set); } else return false; } public class State { public delegate TIndex() Enter; public delegate TIndex() Update; public delegate TIndex() Exit; public ~this() { if (Enter != null) delete Enter; if (Update != null) delete Update; if (Exit != null) delete Exit; } } } }