using UnityEngine; using System.Collections.Generic; using UnityEngine.Events; using MalbersAnimations.Events; using MalbersAnimations.Scriptables; using System; using System.Collections; using MalbersAnimations.Utilities; namespace MalbersAnimations.Controller { /// Variables public partial class MAnimal { // public List StateLocalActive; /// Sets a Bool Parameter on the Animator using the parameter Hash public System.Action SetBoolParameter { get; set; } = delegate { }; /// Sets a float Parameter on the Animator using the parameter Hash public System.Action SetFloatParameter { get; set; } = delegate { }; /// Sets a Integer Parameter on the Animator using the parameter Hash public System.Action SetIntParameter { get; set; } = delegate { }; /// Sets a Trigger Parameter on the Animator using the parameter Hash public System.Action SetTriggerParameter { get; set; } = delegate { }; /// Check when a Animation State is Starting public System.Action StateCycle { get; set; } /// Invoked after the ActiveState execute its code public System.Action PreStateMovement = delegate { }; /// Invoked after the ActiveState execute its code public System.Action PostStateMovement = delegate { }; /// Get all the Animator Parameters the Animal Controller has private List animatorHashParams; //private Hashtable animatorParams; #region Static Properties /// List of all the animals on the scene public static List Animals; /// Main Animal used as the player controlled by Input public static MAnimal MainAnimal; #endregion #region States /// NECESARY WHEN YOU ARE USING MULTIPLE ANIMALS public bool CloneStates = true; [Tooltip("The Animal gameObject will have no Parent on the Hierarchy. (Recommended)")] public bool NoParent = true; /// List of States for this animal public List states = new List(); ///On Which State the animal should Start on Enable public StateID OverrideStartState; /// Current State. Changing this variable wil not exectute the Active State logic internal State activeState; /// Store the Last State, This will not change the Last State parameter on the animator internal State lastState; internal State queueState; /// Check if a State wsa queued and if it can be released because it was activated public bool QueueReleased => QueueState != null && QueueState.OnActiveQueue && !QueueState.OnQueue; /// Store the current queue State public State QueueState { get => queueState; internal set => queueState = value;} /// Store the Last State public State LastState { get => lastState; internal set { //Debug.Log("value = " + value); // if (value != lastState) //Change when its different (Stack overflow BUG) { lastState = value; LastState.EnterExitEvent?.OnExit.Invoke(); LastState.ExitState(); var LastStateID = (QueueState == null) ? lastState.ID.ID : QueueState.ID.ID; // Debug.Log("LastStateID = " + LastStateID); SetIntParameter(hash_LastState, LastStateID); //Sent to the Animator the previews Active State } } } /// Store a State (PreState) that can be used later protected State Pin_State; /// Used to call the Last State one more time before it changes to the new state public bool JustActivateState { get; internal set; } /// ID of the Current Active State public StateID ActiveStateID { get; private set; } /// State Float Value public float State_Float { get; private set; } /// Set/Get the Active State public State ActiveState { get => activeState; internal set { activeState = value; if (value == null) return; JustActivateState = true; if (LastState != null && LastState.ExitFrame) LastState.OnStateMove(DeltaTime); //Play One Last Time the Last State this.Delay_Action(() => { JustActivateState = false; }); //Reset Just Activate State The next Frame ActiveStateID = activeState.ID; OnStateActivate.Invoke(activeState.ID); SetIntParameter(hash_State, activeState.ID.ID); //Sent to the Animator the value to Apply // Debug.Log("hash_State = " + activeState.ID.ID); SetOptionalAnimParameter(hash_StateOn); //Use trigger in case the Animal is using Triggers Strafe = Strafe; // Execute the code inside in case has changed if (!activeState.ValidStance(currentStance)) Stance_Reset(); foreach (var st in states) st.NewActiveState(activeState.ID); //Notify all states that a new state has been activated Set_Sleep_FromStates(activeState); Check_Queue_States(activeState.ID); //Check if a queue State was released if (IsPlayingMode && ActiveMode.StateCanInterrupt(ActiveStateID))//If a mode is playing check a State Change { Mode_Interrupt(); } } } /// When a new State is Activated, Make sure the other States are sent to sleep internal void Set_Sleep_FromStates(State state) { foreach (var st in states) { st.IsSleepFromState = st.SleepFromState.Contains(state.ID); //Sent to sleep states that has some Avoid State st.IsSleepFromState ^= !st.IncludeSleepState; } } /// Check if there's a State that cannot be enabled when playing a mode internal virtual void Set_State_Sleep_FromMode(bool playingMode) { foreach (var state in states) state.IsSleepFromMode = playingMode && state.SleepFromMode.Contains(ActiveMode.ID); } /// Check if there's a State that cannot be enabled when playing a mode internal virtual void Set_State_Sleep_FromStance() { foreach (var state in states) state.IsSleepFromStance = state.SleepFromStance.Contains(Stance); } /// When a new State is Activated, Make sure the other States are sent to sleep internal virtual void Check_Queue_States(StateID ID) { foreach (var st in states) { st.OnQueue = st.QueueFrom.Contains(ID); //Sent to sleep states that has some Avoid State } } #endregion #region General /// Layers the Animal considers ground [SerializeField] private LayerReference groundLayer = new LayerReference(1); /// Layers the Animal considers ground public LayerMask GroundLayer => groundLayer.Value; /// Distance from the Pivots to the ground public float height = 1f; /// Height from the ground to the hip multiplied for the Scale Factor public float Height => (height) * ScaleFactor; /// The Scale Factor of the Animal.. if the animal has being scaled this is the multiplier for the raycasting things public float ScaleFactor => transform.localScale.y; /// Does this Animal have an InputSource public IInputSource InputSource; [SerializeField] private Vector3 center; /// Center of the Animal to be used for AI and Targeting based on World Position public Vector3 Center { private set => center = value; get => transform.TransformPoint(center); } #endregion #region Stance [SerializeField] private StanceID currentStance; [SerializeField] private StanceID defaultStance; /// Last Stance the Animal was public int LastStance { get; private set; } public StanceID DefaultStance { get => defaultStance; set => defaultStance = value; } /// Current Active Stance public StanceID Stance { get => currentStance; set { if (Sleep || !enabled) return; //Do nothing if is not active if (!ActiveState.ValidStance(value)) return; //Do not Activate any new stance if the STATE does not allow it LastStance = currentStance; currentStance = value; var exit = OnEnterExitStances.Find(st => st.ID.ID == LastStance); exit?.OnExit.Invoke(); OnStanceChange.Invoke(value); var enter = OnEnterExitStances.Find(st => st.ID.ID == value); enter?.OnEnter.Invoke(); Set_State_Sleep_FromStance(); if (debugStances) { Debug.Log($"[{name}][Set Stance] → {value.name} [{value.ID}]"); } SetOptionalAnimParameter(hash_Stance, currentStance.ID); //Set on the Animator the Current Stance SetOptionalAnimParameter(hash_LastStance, LastStance); //Set on the Animator the Last Stance SetOptionalAnimParameter(hash_StateOn); //Set on the Animator the Trigger Stance if (!JustActivateState) SetIntParameter(hash_LastState, ActiveStateID); //Sent to the Animator the previews Active State (BUG) ActiveState.SetSpeed(); //Check if the speed modifier has changed when you have a new Stance if (IsPlayingMode && ActiveMode.StanceCanInterrupt(currentStance))//If a mode is playing check a State Change { Mode_Interrupt(); } } } #endregion #region Movement /// Global Animator Speed for the Animal public FloatReference AnimatorSpeed = new FloatReference(1); //public FloatReference MovementDeathValue = new FloatReference(0.05f); [SerializeField] private BoolReference alwaysForward = new BoolReference(false); /// Sets to Zero the Z on the Movement Axis when this is set to true [Tooltip("Sets to Zero the Z on the Movement Axis when this is set to true")] [SerializeField] private BoolReference lockForwardMovement = new BoolReference(false); /// Sets to Zero the X on the Movement Axis when this is set to true [Tooltip("Sets to Zero the X on the Movement Axis when this is set to true")] [SerializeField] private BoolReference lockHorizontalMovement = new BoolReference(false); /// Sets to Zero the Y on the Movement Axis when this is set to true [Tooltip("Sets to Zero the Y on the Movement Axis when this is set to true")] [SerializeField] private BoolReference lockUpDownMovement = new BoolReference(false); ////public bool AdditiveX; ////public bool AdditiveY; //[Tooltip("The Up Down Input is interpreted as Additive (Spyro Underwater Movement Style)")] //public bool AdditiveUp; /// Multiplier to Add or Remove Forward Movement to the Animal, Used when the Animal Rotates in Place public float ForwardMultiplier { get; set; } /// (Z), horizontal (X) and Vertical (Y) Movement Input public Vector3 MovementAxis; /// (Z), horizontal (X) and Vertical (Y) Raw Movement Input public Vector3 MovementAxisRaw;// { get; set; } //{ // get => movementAxisRaw; // set // { // movementAxisRaw = value; // Debug.Log($"movementAxisRaw: {movementAxisRaw} "); // } //} //Vector3 movementAxisRaw; /// Current Raw Input Axis gotted from an Input Entry public Vector3 RawInputAxis;// { get; set; } //{ // get => rawInputAxis; // set // { // rawInputAxis = value; // Debug.Log($"RawInputAxis: {rawInputAxis} "); // } //} //Vector3 rawInputAxis; /// The Animal is using Input instead of a Direction to move public bool UseRawInput { get; internal set; } //{ // get => useRawInput; // set // { // useRawInput = value; // Debug.Log($"useRawInput: {useRawInput} "); // } //} //bool useRawInput; /// Forward (Z), horizontal (X) and Vertical (Y) Smoothed Movement Input AFTER aplied Speeds Multipliers (THIS GOES TO THE ANIMATOR) public Vector3 MovementAxisSmoothed; /// The animal will always move forward public bool AlwaysForward { get => alwaysForward.Value; set { alwaysForward.Value = value; MovementAxis.z = alwaysForward.Value ? 1 : 0; MovementDetected = AlwaysForward; } } /// [Raw Direction the Character will go Using Inputs or a Move Direction public Vector3 Move_Direction; // public Vector3 MoveDirection => Move_Direction; private bool movementDetected; /// Checking if the movement input was activated public bool MovementDetected { get => movementDetected; internal set { if (movementDetected != value) { movementDetected = value; OnMovementDetected.Invoke(value); SetBoolParameter(hash_Movement, MovementDetected); } } } /// The Animal uses the Camera Forward Diretion to Move public BoolReference useCameraInput = new BoolReference(true); /// Use the Camera Up Vector to Move while flying or Swiming UnderWater public BoolReference useCameraUp = new BoolReference(); /// Store the Starting Use camera Input Value public bool DefaultCameraInput { get; set; } public void ResetCameraInput() => UseCameraInput = DefaultCameraInput; //Restore to the default camera input on the Animal /// Use the Camera Up Vector to Move while flying or Swiming UnderWater public bool UseCameraUp { get => useCameraUp.Value; set => useCameraUp.Value = value; } /// The Animal uses the Camera Forward Direction to Move public bool UseCameraInput { get => useCameraInput.Value; set { useCameraInput.Value = UsingMoveWithDirection = value; } } /// Is the animal using a Direction Vector for moving(True) or a World Axis Input (False) public bool UsingMoveWithDirection { set; get; } //{ // get => usingMoveWithDirection; // set // { // usingMoveWithDirection = value; // Debug.Log("UsingMoveWithDirection = " + value); // } //} //private bool usingMoveWithDirection; /// Is the animal using a Direction Vector for rotate in place(true?) public bool Rotate_at_Direction { set; get; } /// Main Camera on the Game public TransformReference m_MainCamera = new TransformReference(); public Transform MainCamera => m_MainCamera.Value; [SerializeField] private bool additivePosLog; [SerializeField] private bool additiveRotLog; private void DebLogAdditivePos() { additivePosLog ^= true; MTools.SetDirty(this); } private void DebLogAdditiveRot() { additiveRotLog ^= true; MTools.SetDirty(this); } [ContextMenuItem("Debug AdditivePos", nameof(DebLogAdditivePos))] [ContextMenuItem("Debug AdditiveRot", nameof(DebLogAdditiveRot))] /// Is this animal is the main Player? public BoolReference isPlayer = new BoolReference(true); /// Additive Position Modifications for the animal (Terrian Snapping, Speed Modifiers Positions, etc) public Vector3 AdditivePosition//; { get => additivePosition; set { additivePosition = value; if (additivePosLog) Debug.Log($"Additive Pos: {(additivePosition / DeltaTime)} "); } } internal Vector3 additivePosition; /// Additive Rotation Modifications for the animal (Terrian Aligment, Speed Modifiers Rotations, etc) public Quaternion AdditiveRotation { get => additiveRotation; set { additiveRotation = value; if (additiveRotLog) Debug.Log($"Additive ROT: {(additiveRotation):F3} "); } } Quaternion additiveRotation; /// Inertia Speed to smoothly change the Speed Modifiers public Vector3 InertiaPositionSpeed { get; internal set; } //{ // get => InertiaPPS; // set // { // InertiaPPS = value; // Debug.Log($"InertiaPositionSpeed: {(InertiaPPS):F3} "); // } //} //Vector3 InertiaPPS; [SerializeField] private BoolReference SmoothVertical = new BoolReference(true); [Tooltip("Global turn multiplier to increase rotation on the animal")] public FloatReference TurnMultiplier = new FloatReference(0f); [Tooltip("Smooth Damp Value to Turn in place, when using LookAt Direction Instead of Move()")] public FloatReference inPlaceDamp = new FloatReference(2f); /// Difference from the Last Frame and the Current Frame public Vector3 DeltaPos { get; internal set; } //{ // set // { // m_DeltaPos = value; // Debug.Log($"DeltaPos POS: {(m_DeltaPos / DeltaTime):F3} "); // } // get => m_DeltaPos; //} //Vector3 m_DeltaPos; /// World Position on the last Frame public Vector3 LastPos { get; internal set; } /// Velocity acumulated from the last Frame public Vector3 Inertia => DeltaPos / DeltaTime; /// Difference between the Current Rotation and the desire Input Rotation public float DeltaAngle { get; internal set; } /// Pitch direction used when Free Movement is Enable (Direction of the Move Input) public Vector3 PitchDirection { get; internal set; } /// Pitch Angle public float PitchAngle { get; internal set; } /// Bank public float Bank { get; internal set; } /// Speed from the Vertical input multiplied by the speeds inputs(Walk Trot Run) this is the value thats goes to the Animator, is not the actual Speed of the animals public float VerticalSmooth { get => MovementAxisSmoothed.z; internal set => MovementAxisSmoothed.z = value; } /// Direction from the Horizontal input multiplied by the speeds inputs this is the value thats goes to the Animator, is not the actual Speed of the animals public float HorizontalSmooth { get => MovementAxisSmoothed.x; internal set => MovementAxisSmoothed.x = value; } /// Direction from the Up Down input multiplied by the speeds inputs this is the value thats goes to the Animator, is not the actual Speed of the animals public float UpDownSmooth { get => MovementAxisSmoothed.y; internal set { MovementAxisSmoothed.y = value; // Debug.Log("UD" + value); } } /// Vertical (Y) Difference between Target and Current UpDown public float DeltaUpDown { get; internal set; } /// If true it will keep the Controller smooth push of the movement stick public bool UseSmoothVertical { get => SmoothVertical.Value; set => SmoothVertical.Value = value; } /// The current value of the Delta time the animal is using (Fixed or not) public float DeltaTime { get; private set; } #endregion #region Alignment Ground /// Smoothness value to Snap to ground public FloatReference AlignPosLerp = new FloatReference(15f); /// Smoothness Position value when Entering from Non Grounded States public FloatReference AlignPosDelta = new FloatReference(2.5f); /// Smoothness Rotation value when Entering from Non Grounded States public FloatReference AlignRotDelta = new FloatReference(2.5f); /// Smoothness Position value when Entering from Non Grounded States public float AlignPosLerpDelta { get; private set; } /// Smoothness Rotation value when Entering from non Grounded States public float AlignRotLerpDelta { get; private set; } /// Smoothness value to Snap to ground public FloatReference AlignRotLerp = new FloatReference(15f); public IntReference AlignLoop = new IntReference(1); [Tooltip("Tag your small rocks, debris,steps and stair objects with this Tag. It will help the animal to recognize better the Terrain")] public StringReference DebrisTag = new StringReference("Stair"); /// Maximun angle on the terrain the animal can walk [Range(1f, 90f), Tooltip("Maximun angle on the terrain the animal can walk")] public float maxAngleSlope = 45f; /// Main Pivot Slope Angle public float MainPivotSlope { get; private set; } /// Used to add extra Rotations to the Animal public Transform Rotator; public Transform RootBone; /// Check if can Fall on slope while on the ground "Decline Slope" public bool DeepSlope => TerrainSlope < -maxAngleSlope; /// Check if the Animal is on any Slope public bool isinSlope => Mathf.Abs(TerrainSlope) > maxAngleSlope; /// Speed of the Animal used on the Rigid Body (Used on Custom Speed Modifiers) public float HorizontalSpeed { get; private set; } /// Velocity of the Animal used on the RIgid Body (Useful for Speed Modifiers) public Vector3 HorizontalVelocity { get; private set; } /// Calculation of the Average Surface Normal public Vector3 SurfaceNormal { get; internal set; } /// Calculate slope Angle and normalize it with the Max Angle Slope public float SlopeNormalized => TerrainSlope / maxAngleSlope; //Normalize the AngleSlop by the MAX Angle Slope and make it positive(HighHill) or negative(DownHill) /// Slope Calculate from the Surface Normal. Positive = Higher Slope, Negative = Lower Slope public float TerrainSlope { get; private set; } [SerializeField] private BoolReference grounded = new BoolReference(false); /// Is the Animal on a surface, when True the Raycasting for the Ground is Applied public bool Grounded { get => grounded.Value; set { if (grounded.Value != value) { grounded.Value = value; if (!value) { platform = null; //If groundes is false remove the stored Platform } else { ResetGravityValues(); Force_Reset(); UpDownAdditive = 0; //Reset UpDown Additive UsingUpDownExternal = false; //Reset UpDown Additive GravityMultiplier = 1; ExternalForceAirControl = true; //Reset the External Force Air Control } SetBoolParameter(hash_Grounded, grounded.Value); } OnGrounded.Invoke(value); // Debug.Log("Grounded: " + value); } } #endregion #region External Force /// Add an External Force to the Animal public Vector3 ExternalForce { get; set; } /// Current External Force the animal current has public Vector3 CurrentExternalForce { get; set; } public bool LocalForce { get; set; } //{ // set // { // m_CurrentExternalForce = value; // Debug.Log($"CurrentExternalForce: {m_CurrentExternalForce} "); // } // get => m_CurrentExternalForce; //} //Vector3 m_CurrentExternalForce; /// External Force Aceleration /summary> public float ExternalForceAcel { get; set; } /// External Force Air Control, Can it be controlled while on the air?? public bool ExternalForceAirControl { get; set; } public bool HasExternalForce => ExternalForce != Vector3.zero; #endregion #region References /// Returns the Animator Component of the Animal [RequiredField] public Animator Anim; [RequiredField] public Rigidbody RB; //Reference for the RigidBody /// Transform.UP (Stored) public Vector3 Up => transform.up; /// Transform.Right (Stored) public Vector3 Right => transform.right; /// Transform.Forward (Stored) public Vector3 Forward => transform.forward; #endregion #region Modes /// Allows the Animal Start Playing a Mode public IntReference StartWithMode = new IntReference(0); /// Status value for the Mode (-1: Loop, -2: Interrupted, 0: Available public int ModeStatus { get; private set; } /// Float value for the Mode public float ModePower { get; set; } // private int modeStatus; private Mode activeMode; /// List of States for this animal public List modes = new List(); /// Is Playing a mode on the Animator public bool IsPlayingMode => activeMode != null; /// A mode is Set, but the Animation has not started yet public bool IsPreparingMode { get; set; } //{ // get => m_IsPreparingMode; // internal set // { // m_IsPreparingMode = value; // Debug.Log("m_IsPreparingMode: " + m_IsPreparingMode); // } //} //bool m_IsPreparingMode; public Zone Zone { get; internal set; } /// ID Value for the Last Mode Played public int LastModeID { get; internal set; } /// ID Value for the Last Ablity Played public int LastAbilityIndex { get; internal set; } /// Set/Get the Active Mode, Prepare the values for the Animator... Does not mean the Mode is Playing public Mode ActiveMode { get => activeMode; internal set { var lastMode = activeMode; activeMode = value; ModeTime = 0; if (value != null) { OnModeStart.Invoke(ActiveModeID = value.ID, value.AbilityIndex); ActiveState.OnModeStart(activeMode); } else { ActiveModeID = 0; } if (lastMode != null) { LastModeID = lastMode.ID; LastAbilityIndex = lastMode.AbilityIndex; OnModeEnd.Invoke(lastMode.ID, LastAbilityIndex); ActiveState.OnModeEnd(lastMode); // Stance = Stance; //Updates the Stance Code ?? } // Debug.Log("IsPlayingMode = " + IsPlayingMode); } } /// Set the Values to the Animator to Enable a mode... Does not mean that the mode is enabled internal virtual void SetModeParameters(Mode value, int status) { if (value != null) { var ability = (value.ActiveAbility != null ? (int)value.ActiveAbility.Index : 0); int mode = Mathf.Abs(value.ID * 1000) + Mathf.Abs(ability); //Convert it into a 4 int value Ex: Attack 1001 //If the Mode is negative or the Ability is negative then Set the Animator Parameter negative too. (Right Left Abilities) ModeAbility = (value.ID < 0 || ability < 0) ? -mode : mode; SetOptionalAnimParameter(hash_ModeOn); //Activate the Optional Trigger //IMPORTANT WHEN IS MAKING SOME RANDOM STUFF if (hash_ModeOn != 0 && status != 0) //Only send the mode status when we are using Mode ON SetModeStatus(status); else SetModeStatus(status); //Normal way IsPreparingMode = true; ModeTime = 0; } else { SetModeStatus(Int_ID.Available); ModeAbility = 0; } } /// Current Mode ID and Ability Append Together (ModeID*100+AbilityIndex) public int ModeAbility { get => m_ModeIDAbility; internal set { m_ModeIDAbility = value; SetIntParameter?.Invoke(hash_Mode, m_ModeIDAbility); } } private int m_ModeIDAbility; /// Current Animation Time of the Mode,used in combos public float ModeTime { get; internal set; } /// Active Mode ID public int ActiveModeID { get; private set; } public Mode Pin_Mode { get; private set; } #endregion #region Sleep [SerializeField] private BoolReference sleep = new BoolReference(false); /// Put the Controller to sleep, is like disalbling the script but internally public bool Sleep { get => sleep.Value; set { if (!value && Sleep) //Means is out of sleep { MTools.ResetFloatParameters(Anim); //Set All Float values to their defaut (For all the Float Values on the Controller while is not riding) ResetController(); } sleep.Value = value; //Debug.Log("Sleep" + Sleep); LockInput = LockMovement = value; //Also Set to sleep the Movement and Input if (Sleep) { SetOptionalAnimParameter(hash_Random, 0); //Set Random to 0 //Reset FreeMovement. if (Rotator) Rotator.localRotation = Quaternion.identity; Bank = 0; PitchAngle = 0; PitchDirection = Vector3.forward; } } } #endregion #region Strafe public BoolEvent OnStrafe = new BoolEvent(); [SerializeField] private BoolReference m_strafe = new BoolReference(false); [SerializeField] private BoolReference m_CanStrafe = new BoolReference(false); [SerializeField] private BoolReference m_StrafeNormalize = new BoolReference(false); [SerializeField] private FloatReference m_StrafeLerp = new FloatReference(5f); public bool StrafeNormalize => m_StrafeNormalize.Value; /// Is the Animal on the Strafe Mode public bool Strafe { get => m_CanStrafe.Value && m_strafe.Value && ActiveState.CanStrafe; set { if (sleep) return; var newStrafe = value && m_CanStrafe.Value && ActiveState.CanStrafe; if (newStrafe != m_strafe.Value) { m_strafe.Value = newStrafe; OnStrafe.Invoke(m_strafe.Value); SetOptionalAnimParameter(hash_Strafe, m_strafe.Value); SetOptionalAnimParameter(hash_StateOn); // Check again that the Strafe is On! if (!JustActivateState) SetIntParameter(hash_LastState, ActiveStateID); //Sent to the Animator the previews Active State (BUG) if (!m_strafe.Value) //false { ResetCameraInput(); } else { Aimer?.SetEnable(true); //Enable the Aimer just in case } } } } public bool CanStrafe { get => m_CanStrafe.Value; set => m_CanStrafe.Value = value; } private float StrafeDeltaValue; private float HorizontalAimAngle_Raw; public Aim Aimer; #endregion #region Pivots internal RaycastHit hit_Hip; //Hip and Chest Ray Cast Information internal RaycastHit hit_Chest; //Hip and Chest Ray Cast Information public List pivots = new List { new MPivots("Hip", new Vector3(0,0.7f,-0.7f), 1), new MPivots("Chest", new Vector3(0,0.7f,0.7f), 1), new MPivots("Water", new Vector3(0,1,0), 0.05f) }; public MPivots Pivot_Hip; public MPivots Pivot_Chest; public int AlignUniqueID { get; private set; } /// Does it have a Hip Pivot? public bool Has_Pivot_Hip; /// Does it have a Hip Pivot? public bool Has_Pivot_Chest; /// Do the Main (Hip Ray) found ground public bool MainRay { get; private set; } /// Do the Fron (Chest Ray) found ground public bool FrontRay { get; private set; } /// Main pivot Point is the Pivot Chest Position, if not the Pivot Hip Position one public Vector3 Main_Pivot_Point { get { Vector3 pivotPoint; if (Has_Pivot_Chest) { pivotPoint = Pivot_Chest.World(transform); } else if (Has_Pivot_Hip) { pivotPoint = Pivot_Hip.World(transform); } else { pivotPoint = transform.TransformPoint(new Vector3(0, Height, 0)); } return pivotPoint + DeltaVelocity; } } public Vector3 DeltaVelocity { get; private set; } /// Does the Animal Had a Pivot Chest at the beggining? private bool Starting_PivotChest; /// Disable Temporally the Pivot Chest in case the animal is on 2 legs public void DisablePivotChest() => Has_Pivot_Chest = false; /// Used for when the Animal is on 2 feet instead of 4 public void ResetPivotChest() => Has_Pivot_Chest = Starting_PivotChest; public void UsePivotChest(bool value) => Has_Pivot_Chest = value; /// Check if there's no Pivot Active public bool NoPivot => !Has_Pivot_Chest && !Has_Pivot_Hip; /// Gets the the Main Pivot Multiplier * Scale factor (Main Pivot is the Chest, if not then theHip Pivot) public float Pivot_Multiplier { get { float multiplier = Has_Pivot_Chest ? Pivot_Chest.multiplier : (Has_Pivot_Hip ? Pivot_Hip.multiplier : 1f); return multiplier * ScaleFactor * (NoPivot ? 1.5f : 1f); } } #endregion #region Speed Modifiers /// The full Velocity we want to without lerping, for the Additional Position NOT INLCUDING ROOTMOTION public Vector3 TargetSpeed { get; internal set; } public Vector3 DesiredRBVelocity { get; internal set; } /// True if the Current Speed is Locked public bool CurrentSpeedSetIsLocked => CurrentSpeedSet.LockSpeed; /// Speed Set for Stances public List speedSets; /// Active Speed Set private MSpeedSet currentSpeedSet = new MSpeedSet(); internal MSpeedSet defaultSpeedSet = new MSpeedSet() { name = "Default Set", Speeds = new List(1) { new MSpeed("Default", 1, 4, 4) } }; //Create a Default Speed at Awake /// True if the State is modifing the current Speed Modifier public bool CustomSpeed; public MSpeed currentSpeedModifier = MSpeed.Default; internal MSpeed SprintSpeed = MSpeed.Default; //public List speedModifiers = new List(); protected int speedIndex; /// What is the Speed modifier the Animal is current using (Walk? trot? Run?) public MSpeed CurrentSpeedModifier { get { if (CurrentSpeedSetIsLocked) return CurrentSpeedSet.LockedSpeedModifier; //Return the Locked if (Sprint && !CustomSpeed) return SprintSpeed; return currentSpeedModifier; } internal set { //Debug.Log("******value = " + value.name); if (currentSpeedModifier.name != value.name) { currentSpeedModifier = value; OnSpeedChange.Invoke(CurrentSpeedModifier); EnterSpeedEvent(CurrentSpeedIndex); ActiveState?.SpeedModifierChanged(CurrentSpeedModifier, CurrentSpeedIndex); // Debug.Log("******CurrentSpeedModifier = " + currentSpeedModifier.name); } } } /// Current Speed Index used of the Current Speed Set E.G. (1 for Walk, 2 for trot) public int CurrentSpeedIndex { get => CurrentSpeedSet.LockSpeed ? CurrentSpeedSet.LockIndex : speedIndex; //Return the LockSpeed Index in case the speed is locked internal set { if (CustomSpeed || CurrentSpeedSet == null) return; var speedModifiers = CurrentSpeedSet.Speeds; var newValue = Mathf.Clamp(value, 1, speedModifiers.Count); //Clamp the Speed Index if (newValue > CurrentSpeedSet.TopIndex) newValue = CurrentSpeedSet.TopIndex; newValue = Mathf.Clamp(value, 1, newValue); // TOP INDEX CANNOT BE SET OT ZERO if (speedIndex != newValue) { speedIndex = newValue; var sprintSpeed = Mathf.Clamp(speedIndex + 1, 1, speedModifiers.Count); CurrentSpeedModifier = speedModifiers[speedIndex - 1]; //Debug.Log("CurrentSpeedIndex: " + speedIndex); SprintSpeed = speedModifiers[sprintSpeed - 1]; if (CurrentSpeedSet != null) CurrentSpeedSet.CurrentIndex = speedIndex; //Keep the Speed saved on the state too in case the active speed was changed } } } /// Current Speed Set used on the Animal public MSpeedSet CurrentSpeedSet { get => currentSpeedSet; internal set { if (value.name != currentSpeedSet.name) //Calculate this only when the Set changes { //Debug.Log("SpeedSet = " + currentSpeedSet.name); //Debug.Log("currentSpeedSet = " + currentSpeedSet.CurrentIndex); currentSpeedSet = value; speedIndex = -1; //Reset the speed Index JustChangedSpeedSet = true; CurrentSpeedIndex = currentSpeedSet.CurrentIndex; JustChangedSpeedSet = false; EnterSpeedEvent(CurrentSpeedIndex); } } } bool JustChangedSpeedSet; private void EnterSpeedEvent(int index) { if (JustChangedSpeedSet) return; if (OnEnterExitSpeeds != null) { var SpeedEnterEvent = OnEnterExitSpeeds.Find(s => s.SpeedIndex == index && s.SpeedSet == CurrentSpeedSet.name); if (OldEnterExitSpeed != null && SpeedEnterEvent != OldEnterExitSpeed) { OldEnterExitSpeed.OnExit.Invoke(); OldEnterExitSpeed = null; } if (SpeedEnterEvent != null) { SpeedEnterEvent.OnEnter.Invoke(); OldEnterExitSpeed = SpeedEnterEvent; } } } private OnEnterExitSpeed OldEnterExitSpeed; /// Use Default SpeedSet 0 everything public void ResetSpeedSet() => CurrentSpeedSet = defaultSpeedSet; /// Value for the Speed Global Multiplier Parameter on the Animator internal float SpeedMultiplier { get; set; } internal bool sprint; internal bool realSprint; /// Sprint Input public bool Sprint { get => UseSprintState && sprint && UseSprint && MovementDetected && !CurrentSpeedSetIsLocked; set { var newRealSprint = UseSprintState && value && UseSprint && MovementDetected && !CurrentSpeedSetIsLocked; //Check if the animal has movement //Debug.Log($"UseSprintState {UseSprintState} && value{value} && " + // $"UseSprint{UseSprint} && MovementDetected{MovementDetected} && !SpeedChangeLocked {SpeedChangeLocked}"); sprint = value; //Store only the Input value for the sprint I think it works if (realSprint != newRealSprint) { realSprint = newRealSprint; OnSprintEnabled.Invoke(realSprint); SetOptionalAnimParameter(hash_Sprint, realSprint); //Set on the Animator Sprint Value int currentPI = CurrentSpeedIndex; var speed = CurrentSpeedModifier; if (realSprint) { speed = SprintSpeed; currentPI++; } OnSpeedChange.Invoke(speed); //Invoke the Speed again EnterSpeedEvent(currentPI); ActiveState?.SpeedModifierChanged(speed, currentPI); } } } public void SetSprint(bool value) => Sprint = value; /// Try State current Cycle internal int CurrentCycle { get; private set; } #endregion #region Gravity [SerializeField] private Vector3Reference m_gravityDir = new Vector3Reference(Vector3.down); [SerializeField] private FloatReference m_gravityPower = new FloatReference(9.8f); [SerializeField] private IntReference m_gravityTime = new IntReference(10); [SerializeField] private IntReference m_gravityTimeLimit = new IntReference(0); public int StartGravityTime { get => m_gravityTime.Value ; internal set => m_gravityTime.Value = value; } public int LimitGravityTime { get => m_gravityTimeLimit.Value ; internal set => m_gravityTimeLimit.Value = value; } /// Multiplier Added to the Gravity Direction public float GravityMultiplier { get; internal set; } public int GravityTime { get; internal set; } //{ // get => m_GravityTime; // set // { // m_GravityTime = value; // Debug.Log("m_GravityTime " + m_GravityTime); // } //} //int m_GravityTime; public float GravityPower { get => m_gravityPower.Value * GravityMultiplier; set => m_gravityPower.Value = value; } /// Stored Gravity Velocity when the animal is using Gravity public Vector3 GravityStoredVelocity { get; internal set; } /// Direction of the Gravity public Vector3 Gravity { get => m_gravityDir.Value; set => m_gravityDir.Value = value; } /// Up Vector is the Opposite direction of the Gravity dir public Vector3 UpVector => -m_gravityDir.Value; /// if True the gravity will be the Negative Ground Normal Value public BoolReference ground_Changes_Gravity = new BoolReference(false); #endregion #region Advanced Parameters [Range(0,180),Tooltip("Slow the Animal when the Turn Angle is ouside this limit")] public float TurnLimit = 120; public BoolReference rootMotion = new BoolReference(true); /// Raudius for the Sphere Cast public FloatReference rayCastRadius = new FloatReference(0.05f); /// RayCast Radius for the Alignment Raycasting public float RayCastRadius => rayCastRadius.Value + 0.001f; /// This parameter exist to Add Additive pose to correct the animal public IntReference animalType = new IntReference(0); #endregion #region Use Stuff Properties /// Does the Active State uses Additive Position Speed? public bool UseAdditivePos// { get; internal set; } { get => useAdditivePos; set { useAdditivePos = value; if (!useAdditivePos) ResetInertiaSpeed(); } } private bool useAdditivePos; /// Does the Active State uses Additive Position Speed? public bool UseAdditiveRot { get; internal set; } /// Does the Active State uses Sprint? public bool UseSprintState { get; internal set; } /// Custom Alignment done by some States.. (E.g. Swim) public bool UseCustomAlign { get; set; } /// The Animal is on Free Movement... which means is flying or swiming underwater public bool FreeMovement { get; set; } /// Enable Disable the Global Sprint public bool UseSprint { get => useSprintGlobal; set { useSprintGlobal.Value = value; Sprint = sprint; //Update the Sprint value IMPORTANT } } /// Enable Disable the Global Sprint (SAME AS USE SPRINT) public bool CanSprint { get => UseSprint; set => UseSprint = value; } /// Locks Input on the Animal, Ingore inputs like Jumps, Attacks , Actions etc public bool LockInput { get => lockInput.Value; set { lockInput.Value = value; OnInputLocked.Invoke(lockInput); } } /// Enable/Disable RootMotion on the Animator public bool RootMotion { get => rootMotion; set => Anim.applyRootMotion = rootMotion.Value = value; } /// /// This store the DeltaRootMotion everytime its Deactivated/Activated /// private Vector3 DeltaRootMotion; private bool useGravity; /// Does it use Gravity or not? public bool UseGravity { get => useGravity; set { useGravity = value; if (!useGravity) ResetGravityValues();//Reset Gravity Logic when Use gravity is false // Debug.Log("useGravity = " + useGravity); } } /// Locks the Movement on the Animal public bool LockMovement { get => lockMovement; set { lockMovement.Value = value; OnMovementLocked.Invoke(lockMovement); } } /// Sets to Zero the Z on the Movement Axis when this is set to true public bool LockForwardMovement { get => lockForwardMovement; set { lockForwardMovement.Value = value; LockMovementAxis.z = value ? 0 : 1; } } /// Sets to Zero the X on the Movement Axis when this is set to true public bool LockHorizontalMovement { get => lockHorizontalMovement; set { lockHorizontalMovement.Value = value; LockMovementAxis.x = value ? 0 : 1; } } /// Sets to Zero the Y on the Movement Axis when this is set to true public bool LockUpDownMovement { get => lockUpDownMovement; set { lockUpDownMovement.Value = value; LockMovementAxis.y = value ? 0 : 1; } } private Vector3 LockMovementAxis; private bool useOrientToGround; /// if True It will Aling it to the ground rotation depending the Front and Back Pivots public bool UseOrientToGround { get => useOrientToGround && m_OrientToGround.Value; set => useOrientToGround = value; } public bool GlobalOrientToGround { get => m_OrientToGround.Value; set { m_OrientToGround.Value = value; Has_Pivot_Chest = value ? Pivot_Chest != null : false; //Hide the Pivor Chest } } [SerializeField, Tooltip("Global Orient to ground. Disable This for Humanoids")] private BoolReference m_OrientToGround = new BoolReference(true); [SerializeField,Tooltip("Locks Input on the Animal, Ignore inputs like Jumps, Attacks, Actions etc")] private BoolReference lockInput = new BoolReference(false); [SerializeField,Tooltip("Locks the Movement entries on the animal. (Horizontal, Vertical,Up Down)")] private BoolReference lockMovement = new BoolReference(false); [SerializeField] private BoolReference useSprintGlobal = new BoolReference(true); #endregion #region Animator States Info internal AnimatorStateInfo m_CurrentState; // Information about the base layer of the animator cached. internal AnimatorStateInfo m_NextState; internal AnimatorStateInfo m_PreviousCurrentState; // Information about the base layer of the animator from last frame. internal AnimatorStateInfo m_PreviousNextState; /// If we are in any animator transition internal bool m_IsAnimatorTransitioning; internal bool FirstAnimatorTransition; protected bool m_PreviousIsAnimatorTransitioning; /// Returns the Current Animation State Tag of animal, if is in transition it will return the NextState Tag public AnimatorStateInfo AnimState { get; set; } public int currentAnimTag; /// Current Active Animation Hash Tag public int AnimStateTag { get => currentAnimTag; private set { if (value != currentAnimTag) { currentAnimTag = value; activeState.AnimationTagEnter(value); if (ActiveState.IsPending) //If the new Animation Tag is not on the New Active State try to activate it on the last State LastState.AnimationTagEnter(value); //Debug.Log("value = " + value); } } } #endregion #region Platform public Transform platform; protected Vector3 platform_LastPos; protected Quaternion platform_Rot; #endregion #region Extras /// Used for Disabling Additive Position and Additive Rotation on the ANimal (The Pulling Wagons on the Horse Car take care of it)????? internal bool DisablePositionRotation = false; ///// Used for Disabling Additive Position and Additive Rotation on the ANimal (The Pulling Wagons on the Horse Car take care of it)????? //internal bool DisableRotation = false; //[Tooltip("When Falling and the animal get stuck falling, the animal will be force to move forward.")] //public FloatReference FallForward = new FloatReference(2); protected List Attack_Triggers; //List of all the Damage Triggers on this Animal. /// All Colliders Inside the Animals> summary> public List colliders = new List(); /// Animator Normalized State Time for the Base Layer public float StateTime { get; private set; } ///// Store from where the damage came from //public Vector3 HitDirection { set; get; } #endregion #region Events public IntEvent OnAnimationChange; public UnityEvent OnMaxSlopeReached = new UnityEvent(); private bool HasReachedMaxSlope; public BoolEvent OnInputLocked = new BoolEvent(); //Used for Sync Animators public BoolEvent OnMovementLocked = new BoolEvent(); //Used for Sync Animators public BoolEvent OnSprintEnabled = new BoolEvent(); //Used for Sync Animators public BoolEvent OnGrounded = new BoolEvent(); //Used for Sync Animators public BoolEvent OnMovementDetected = new BoolEvent(); //Used for Sync Animators /// Invoked when a new State is Activated public IntEvent OnStateActivate = new IntEvent(); /// Invoked when a new State has entered any of its Animations public IntEvent OnStateChange = new IntEvent(); /// Invoked when a new Mode start public Int2Event OnModeStart = new Int2Event(); /// Invoked when a new Mode ends public Int2Event OnModeEnd = new Int2Event(); /// Invoked when a new Stance is Activated public IntEvent OnStanceChange = new IntEvent(); //Invoked when is Changed to a new Stance public SpeedModifierEvent OnSpeedChange = new SpeedModifierEvent(); //Invoked when a new Speed is changed public Vector3Event OnTeleport = new Vector3Event(); //Invoked when a new Speed is changed ///List of Events to Use on the States public List OnEnterExitStates; ///List of Events to Use on the Stances public List OnEnterExitStances; ///List of Events to Use on the Speeds public List OnEnterExitSpeeds; #endregion #region Random public int RandomID { get; private set; } public int RandomPriority { get; private set; } /// Let States have Random Animations public bool Randomizer { get; set; } #endregion #region Animator Parameters [SerializeField, Tooltip("Forward (Z) Movement for the Animator")] private string m_Vertical = "Vertical"; [SerializeField, Tooltip("Horizontal (X) Movement for the Animator")] private string m_Horizontal = "Horizontal"; [SerializeField, Tooltip("Vertical (Y) Movement for the Animator")] private string m_UpDown = "UpDown"; [SerializeField, Tooltip("Vertical (Y) Difference between Target and Current UpDown")] private string m_DeltaUpDown = "DeltaUpDown"; [SerializeField, Tooltip("Is the animal on the Ground? ")] private string m_Grounded = "Grounded"; [SerializeField, Tooltip("Is the animal moving?")] private string m_Movement = "Movement"; [SerializeField, Tooltip("Active/Current State the animal is")] private string m_State = "State"; [SerializeField, Tooltip("Trigger to Notify the Activation of a State")] private string m_StateOn = "StateOn"; //[SerializeField, Tooltip("Trigger to Notify the Activation of a State")] //private string m_StanceOn = "StanceOn"; [SerializeField, Tooltip("Trigger to Notify the Activation of a Mode")] private string m_ModeOn = "ModeOn"; [SerializeField, Tooltip("The Active State can have multiple status to change inside the State itself")] private string m_StateStatus = "StateEnterStatus"; [SerializeField, Tooltip("The Active State can use this parameter to activate exiting animations")] private string m_StateExitStatus = "StateExitStatus"; [SerializeField, Tooltip("Float value for the States to be used when needed")] private string m_StateFloat = "StateFloat"; [SerializeField, Tooltip("Last State the animal was")] private string m_LastState = "LastState"; [SerializeField, Tooltip("Active State Time for the States Animations")] private string m_StateTime = "StateTime"; [SerializeField, Tooltip("Speed Multiplier for the Animations")] private string m_SpeedMultiplier = "SpeedMultiplier"; [SerializeField, Tooltip("Active Mode the animal is... The Value is the Mode ID plus the Ability Index. Example Action Eat = 4002")] private string m_Mode = "Mode"; [SerializeField, Tooltip("Store the Modes Status (Available=0 Started=1 Looping=-1 Interrupted=-2)")] private string m_ModeStatus = "ModeStatus"; [SerializeField, Tooltip("Mode Float Value, Used to have a float Value for the modes to be used when needed")] private string m_ModePower = "ModePower"; [SerializeField, Tooltip("Sprint Value")] private string m_Sprint = "Sprint"; [SerializeField, Tooltip("Active/Current stance of the animal")] private string m_Stance = "Stance"; [SerializeField, Tooltip("Previus/Last stance of the animal")] private string m_LastStance = "LastStance"; [SerializeField, Tooltip("Normalized value of the Slope of the Terrain")] private string m_Slope = "Slope"; [SerializeField, Tooltip("Type of animal for the Additive corrective pose")] private string m_Type = "Type"; [SerializeField, Tooltip("Random Value for Animations States with multiple animations")] private string m_Random = "Random"; [SerializeField, Tooltip("Target Angle calculated from the current forward direction to the desired direction")] private string m_DeltaAngle = "DeltaAngle"; [SerializeField, Tooltip("Does the Animal Uses Strafe")] private string m_Strafe = "Strafe"; [SerializeField, Tooltip("Horizontal Angle for Strafing.")] private string m_strafeAngle = "StrafeAngle"; internal int hash_Vertical; internal int hash_Horizontal; internal int hash_UpDown; internal int hash_DeltaUpDown; internal int hash_Movement; internal int hash_Grounded; internal int hash_SpeedMultiplier; internal int hash_DeltaAngle; internal int hash_State; internal int hash_StateOn; internal int hash_StateEnterStatus; internal int hash_StateExitStatus; internal int hash_StateFloat; internal int hash_StateTime; internal int hash_LastState; internal int hash_Mode; internal int hash_ModeOn; internal int hash_ModeStatus; internal int hash_ModePower; internal int hash_Stance; //internal int hash_StanceOn; internal int hash_LastStance; internal int hash_Slope; internal int hash_Sprint; internal int hash_Random; internal int hash_Strafe; internal int hash_StrafeAngle; #endregion } [System.Serializable] public class OnEnterExitSpeed { [Tooltip("Which is the Speed Set (By its Name) changed. Case Sensitive")] public string SpeedSet; [Tooltip("Which is the Speed Modifier (By its Name) changed. This is Ignored if is set to 1. Case Sensitive")] public int SpeedIndex; public UnityEvent OnEnter; public UnityEvent OnExit; } [System.Serializable] public class OnEnterExitState { public StateID ID; public UnityEvent OnEnter; public UnityEvent OnExit; } [System.Serializable] public class OnEnterExitStance { public StanceID ID; public UnityEvent OnEnter; public UnityEvent OnExit; } [System.Serializable] public class SpeedModifierEvent : UnityEvent { } }