using System; using System.Collections.Generic; using EntityStates; using HG; using JetBrains.Annotations; using RoR2.CharacterAI; using RoR2.Projectile; using UnityEngine; using UnityEngine.Networking; namespace RoR2; public class EntityStateMachine : MonoBehaviour, IManagedMonoBehaviour { public struct CommonComponentCache { public readonly Transform transform; public readonly CharacterBody characterBody; public readonly CharacterMotor characterMotor; public readonly CharacterDirection characterDirection; public readonly Rigidbody rigidbody; public readonly RigidbodyMotor rigidbodyMotor; public readonly RigidbodyDirection rigidbodyDirection; public readonly RailMotor railMotor; public readonly ModelLocator modelLocator; public readonly InputBankTest inputBank; public readonly TeamComponent teamComponent; public readonly HealthComponent healthComponent; public readonly SkillLocator skillLocator; public readonly CharacterEmoteDefinitions characterEmoteDefinitions; public readonly CameraTargetParams cameraTargetParams; public readonly SfxLocator sfxLocator; public readonly BodyAnimatorSmoothingParameters bodyAnimatorSmoothingParameters; public readonly ProjectileController projectileController; public CommonComponentCache(GameObject gameObject) { transform = gameObject.transform; characterBody = gameObject.GetComponent(); characterMotor = gameObject.GetComponent(); characterDirection = gameObject.GetComponent(); rigidbody = gameObject.GetComponent(); rigidbodyMotor = gameObject.GetComponent(); rigidbodyDirection = gameObject.GetComponent(); railMotor = gameObject.GetComponent(); modelLocator = gameObject.GetComponent(); inputBank = gameObject.GetComponent(); teamComponent = gameObject.GetComponent(); healthComponent = gameObject.GetComponent(); skillLocator = gameObject.GetComponent(); characterEmoteDefinitions = gameObject.GetComponent(); cameraTargetParams = gameObject.GetComponent(); sfxLocator = gameObject.GetComponent(); bodyAnimatorSmoothingParameters = gameObject.GetComponent(); projectileController = gameObject.GetComponent(); } } public delegate void ModifyNextStateDelegate(EntityStateMachine entityStateMachine, ref EntityState newNextState); private EntityState nextState; [Tooltip("The name of this state machine.")] public string customName; [Tooltip("The type of the state to enter when this component is first activated.")] public SerializableEntityStateType initialStateType = new SerializableEntityStateType(typeof(TestState1)); [Tooltip("The preferred main state of this state machine.")] public SerializableEntityStateType mainStateType; public CommonComponentCache commonComponents; [NonSerialized] public int networkIndex = -1; [NonSerialized] public bool AllowStartWithoutNetworker; [NonSerialized] public bool ShouldStateTransitionOnUpdate; public ModifyNextStateDelegate nextStateModifier; public bool debug; public EntityState state { get; private set; } public NetworkStateMachine networker { get; set; } public NetworkIdentity networkIdentity { get; set; } public bool AlwaysUpdate => false; public bool destroying { get; private set; } public void SetNextState(EntityState newNextState) { nextStateModifier?.Invoke(this, ref newNextState); nextState = newNextState; } public void SetNextStateToMain() { SetNextState(EntityStateCatalog.InstantiateState(ref mainStateType)); } public bool CanInterruptState(InterruptPriority interruptPriority) { return (nextState ?? state).GetMinimumInterruptPriority() <= interruptPriority; } public bool SetInterruptState(EntityState newNextState, InterruptPriority interruptPriority) { if (CanInterruptState(interruptPriority)) { _ = debug; SetNextState(newNextState); return true; } return false; } public bool HasPendingState() { return nextState != null; } public bool IsInMainState() { if (state != null) { return state.GetType() == mainStateType.stateType; } return false; } public bool IsInInitialState() { if (state != null) { return state.GetType() == initialStateType.stateType; } return false; } public void SetState([NotNull] EntityState newState) { nextState = null; newState.outer = this; if (state == null) { Debug.LogErrorFormat("State machine {0} on object {1} does not have a state!", customName, base.gameObject); } state.ModifyNextState(newState); state.OnExit(); _ = debug; state = newState; state.OnEnter(); if (networkIndex != -1) { if (!networker) { Debug.LogErrorFormat("State machine {0} on object {1} does not have a networker assigned!", customName, base.gameObject); } networker.SendSetEntityState(networkIndex); } } private void Awake() { if (!networker) { networker = GetComponent(); } if (!networkIdentity) { networkIdentity = GetComponent(); } commonComponents = new CommonComponentCache(base.gameObject); state = new Uninitialized(); state.outer = this; } private void Start() { if (nextState != null && (AllowStartWithoutNetworker || ((bool)networker && !networker.hasAuthority))) { SetState(nextState); return; } Type stateType = initialStateType.stateType; if (state is Uninitialized && stateType != null && stateType.IsSubclassOf(typeof(EntityState))) { SetState(EntityStateCatalog.InstantiateState(stateType)); } } public void Update() { ManagedUpdate(); } public void FixedUpdate() { ManagedFixedUpdate(Time.deltaTime); } public void ManagedUpdate() { state.Update(); if (ShouldStateTransitionOnUpdate) { TryStateTransition(); } } public void ManagedFixedUpdate(float deltaTime) { if (!destroying) { if (!ShouldStateTransitionOnUpdate) { TryStateTransition(); } state.FixedUpdate(); } } private void TryStateTransition() { if (nextState != null) { SetState(nextState); } } private void OnDestroy() { destroying = true; if (state != null) { state.OnExit(); state = null; } } private void OnValidate() { if (!(mainStateType.stateType == null)) { return; } if (customName == "Body") { if ((bool)GetComponent()) { mainStateType = new SerializableEntityStateType(typeof(GenericCharacterMain)); } else if ((bool)GetComponent()) { mainStateType = new SerializableEntityStateType(typeof(FlyState)); } } else if (customName == "Weapon") { mainStateType = new SerializableEntityStateType(typeof(Idle)); } else if (customName == "AI") { BaseAI component = GetComponent(); if ((bool)component) { mainStateType = component.scanState; } } } public static EntityStateMachine FindByCustomName(GameObject gameObject, string customName) { List gameObjectComponents = GetComponentsCache.GetGameObjectComponents(gameObject); EntityStateMachine result = null; int i = 0; for (int count = gameObjectComponents.Count; i < count; i++) { if (string.CompareOrdinal(customName, gameObjectComponents[i].customName) == 0) { result = gameObjectComponents[i]; break; } } GetComponentsCache.ReturnBuffer(gameObjectComponents); return result; } }