using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UOP1.StateMachine.ScriptableObjects
{
[CreateAssetMenu(fileName = "NewTransitionTable", menuName = "State Machines/Transition Table")]
public class TransitionTableSO : ScriptableObject
{
[SerializeField] private TransitionItem[] _transitions = default;
///
/// Will get the initial state and instantiate all subsequent states, transitions, actions and conditions.
///
internal State GetInitialState(StateMachine stateMachine)
{
var states = new List();
var transitions = new List();
var createdInstances = new Dictionary();
var fromStates = _transitions.GroupBy(transition => transition.FromState);
foreach (var fromState in fromStates)
{
if (fromState.Key == null)
throw new ArgumentNullException(nameof(fromState.Key), $"TransitionTable: {name}");
var state = fromState.Key.GetState(stateMachine, createdInstances);
states.Add(state);
transitions.Clear();
foreach (var transitionItem in fromState)
{
if (transitionItem.ToState == null)
throw new ArgumentNullException(nameof(transitionItem.ToState), $"TransitionTable: {name}, From State: {fromState.Key.name}");
var toState = transitionItem.ToState.GetState(stateMachine, createdInstances);
ProcessConditionUsages(stateMachine, transitionItem.Conditions, createdInstances, out var conditions, out var resultGroups);
transitions.Add(new StateTransition(toState, conditions, resultGroups));
}
state._transitions = transitions.ToArray();
}
return states.Count > 0 ? states[0]
: throw new InvalidOperationException($"TransitionTable {name} is empty.");
}
private static void ProcessConditionUsages(
StateMachine stateMachine,
ConditionUsage[] conditionUsages,
Dictionary createdInstances,
out StateCondition[] conditions,
out int[] resultGroups)
{
int count = conditionUsages.Length;
conditions = new StateCondition[count];
for (int i = 0; i < count; i++)
conditions[i] = conditionUsages[i].Condition.GetCondition(
stateMachine, conditionUsages[i].ExpectedResult == Result.True, createdInstances);
List resultGroupsList = new List();
for (int i = 0; i < count; i++)
{
int idx = resultGroupsList.Count;
resultGroupsList.Add(1);
while (i < count - 1 && conditionUsages[i].Operator == Operator.And)
{
i++;
resultGroupsList[idx]++;
}
}
resultGroups = resultGroupsList.ToArray();
}
[Serializable]
public struct TransitionItem
{
public StateSO FromState;
public StateSO ToState;
public ConditionUsage[] Conditions;
}
[Serializable]
public struct ConditionUsage
{
public Result ExpectedResult;
public StateConditionSO Condition;
public Operator Operator;
}
public enum Result { True, False }
public enum Operator { And, Or }
}
}