using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq; using MalbersAnimations.Scriptables; using UnityEngine.Events; namespace MalbersAnimations.Controller { /// All Callbacks/Public Methods are Here public partial class MAnimal { #region INPUTS /// Disconnect and Reconnect the States and Modes to the Input Source public virtual void ResetInputSource() { UpdateInputSource(false); //Disconnect UpdateInputSource(true); //Reconnect } /// Updates all the Input from Malbers Input in case needed (Rewired conextion( public virtual void UpdateInputSource(bool connect) { if (InputSource == null) InputSource = gameObject.FindInterface(); //Find if we have a InputSource if (InputSource != null) { foreach (var state in states) state.ConnectInput(InputSource, connect); foreach (var mode in modes) mode.ConnectInput(InputSource, connect); } } #endregion #region Player /// Set an Animal as the Main Player and remove the otherOne public virtual void SetMainPlayer() { if (MainAnimal) //if there's a main animal already seted { MainAnimal.isPlayer.Value = false; } this.isPlayer.Value = true; MainAnimal = this; } public void DisableMainPlayer() { if (MainAnimal == this) MainAnimal = null; } #endregion #region Teleport /// Teleports the Animal to a Transform position public virtual void Teleport(Transform newPos) { if (newPos) Teleport(newPos.position); } /// Teleports the Animal to a Transform Position, It also Rotates the Animal to that transform Rotation public virtual void TeleportRot(Transform newPos) { if (newPos) { Teleport(newPos.position); transform.rotation = newPos.rotation; } } public virtual void Teleport(Vector3 newPos) { Teleport_Internal(newPos); OnTeleport.Invoke(newPos); } /// Used by the States to Teleport withouth sending the Event internal void Teleport_Internal(Vector3 newPos) { transform.position = newPos; LastPos = transform.position; platform = null; //if (debugStates) Debugging($"{name}: Teleported to {newPos}"); } private void Debugging(string value) { #if UNITY_EDITOR Debug.Log($"[{name}]{value}",this); #endif } #endregion // #region Animal Aligment //public virtual void Aling_Position(Vector3 Destination, float time, AnimationCurve curve) //{ // if (!Aligning) StartCoroutine(AlignPos(Destination,time,curve)); //} //public virtual void Aling_Position(Vector3 Destination, Quaternion DestRot, float time, AnimationCurve curve) //{ // if (!Aligning) StartCoroutine(AlignTransform(Destination, DestRot, time, curve)); //} //private bool Aligning; //IEnumerator AlignPos(Vector3 NewPosition, float time, AnimationCurve curve = null) //{ // float elapsedTime = 0; // Aligning = true; // var Wait = new WaitForFixedUpdate(); // Vector3 CurrentPos = transform.position; // while ((time > 0) && (elapsedTime <= time)) // { // float result = curve != null ? curve.Evaluate(elapsedTime / time) : elapsedTime / time; //Evaluation of the Pos curve // transform.position = Vector3.LerpUnclamped(CurrentPos, NewPosition, result); // elapsedTime += Time.fixedDeltaTime; // yield return Wait; // } // transform.position = NewPosition; // Aligning = false; // yield return null; //} //IEnumerator AlignTransform(Vector3 PosDestination, Quaternion RotDestination, float time, AnimationCurve curve = null) //{ // float elapsedTime = 0; // Aligning = true; // Vector3 CurrentPos = transform.position; // Quaternion CurrentRot = transform.rotation; // var Wait = new WaitForFixedUpdate(); // while ((time > 0) && (elapsedTime <= time)) // { // float result = curve != null ? curve.Evaluate(elapsedTime / time) : elapsedTime / time; //Evaluation of the Pos curve // transform.position = Vector3.LerpUnclamped(CurrentPos, PosDestination, result); // transform.rotation = Quaternion.LerpUnclamped(CurrentRot, RotDestination, result); // elapsedTime += Time.fixedDeltaTime; // yield return Wait; // } // transform.position = PosDestination; // transform.rotation = RotDestination; // Aligning = false; // yield return null; //} // #endregion #region Gravity /// Resets the gravity to the default Vector.Down value public void ResetGravityDirection() => Gravity = Vector3.down; /// Clears the Gravity Logic internal void ResetGravityValues() { GravityTime = m_gravityTime; GravityStoredVelocity = Vector3.zero; // GravitySpeed = 0; } internal void ResetUPVector() { RB.velocity = Vector3.ProjectOnPlane(RB.velocity, UpVector); //Extra OPTIONAL AdditivePosition = Vector3.ProjectOnPlane(AdditivePosition, UpVector); DeltaPos = Vector3.ProjectOnPlane(DeltaPos, UpVector); } /// The Ground will change the Gravity Direction. Using the ground Normal as reference /// Enable/Disable the logic public void GroundChangesGravity(bool value) => ground_Changes_Gravity.Value = value; /// Aling the character instantly to the Gravity Direction public void AlignToGravity() { Quaternion AlignRot = Quaternion.FromToRotation(transform.up, UpVector) * transform.rotation; //Calculate the orientation to Terrain base.transform.rotation = AlignRot; } #endregion #region Stances /// Toggle the New Stance with the Default Stance▼▲ public void Stance_Toggle(StanceID NewStance) => Stance = (Stance.ID == NewStance.ID) ? DefaultStance : NewStance; public void Stance_Set(StanceID id) => Stance = id; public void Stance_Set(int id) { var NewStance = ScriptableObject.CreateInstance(); NewStance.name = "Stance(" + id+")"; NewStance.ID = id; Stance = NewStance; } /// Changes the Last State on the Animator public void Stance_SetLast(int id) { LastStance = id; SetOptionalAnimParameter(hash_LastStance, LastStance); //Set on the Animator the Last Stance } public void Stance_Reset() => Stance = defaultStance; #endregion #region Animator Methods /// Method required for the Interface IAnimator Listener to send messages From the Animator to any class who uses this Interface public virtual bool OnAnimatorBehaviourMessage(string message, object value) { foreach (var state in states) state.ReceiveMessages(message, value); return this.InvokeWithParams(message, value); } public void SetAnimatorSpeed(float value) => AnimatorSpeed = value; /// Set a Int on the Animator public virtual void SetAnimParameter(int hash, int value) => Anim.SetInteger(hash, value); /// Set a float on the Animator public virtual void SetAnimParameter(int hash, float value) => Anim.SetFloat(hash, value); /// Set a Bool on the Animator public virtual void SetAnimParameter(int hash, bool value) => Anim.SetBool(hash, value); /// Set a Trigger to the Animator public virtual void SetAnimParameter(int hash) => Anim.SetTrigger(hash); public virtual void SetOptionalAnimParameter(int Hash, float value) { if (Hash != 0) SetFloatParameter(Hash, value); } public virtual void SetOptionalAnimParameter(int Hash, int value) { if (Hash != 0) SetIntParameter(Hash, value); } public virtual void SetOptionalAnimParameter(int Hash, bool value) { if (Hash!=0) SetBoolParameter(Hash, value); } public virtual void SetOptionalAnimParameter(int Hash) { if (Hash != 0) SetTriggerParameter(Hash); } /// Set the Parameter Random to a value and pass it also to the Animator public void SetRandom(int value, int priority) { if (!enabled || Sleep) return; if (priority >= RandomPriority) { RandomPriority = priority; RandomID = Randomizer ? value : 0; SetOptionalAnimParameter(hash_Random, RandomID); } } public void ResetRandomPriority(int priority) { if (priority >= RandomPriority) { RandomPriority = 0; } } /// Used by Animator Events public virtual void EnterTag(string tag) => AnimStateTag = Animator.StringToHash(tag); #endregion #region States /// Set the StateFloat value and pass it to the StateFloat parameter on the Animator public void State_SetFloat(float value) { // Debug.Log($"State_Float: {value:F3}"); State_Float = value; SetFloatParameter(hash_StateFloat, State_Float); } /// Set the StateFloat value and pass it to the StateFloat parameter on the Animator. Uses a Smooth Value public void State_SetFloat(float value, float smoothValue) { // Debug.Log($"State_Float: {value:F3}"); State_Float = Mathf.Lerp(State_Float, value, smoothValue * DeltaTime); SetFloatParameter(hash_StateFloat, State_Float); } /// Find an old State and replace it for a new one at Runtime public void State_Replace(State NewState) { if (CloneStates) { State instance = (State)ScriptableObject.CreateInstance(NewState.GetType()); instance = ScriptableObject.Instantiate(NewState); //Create a clone from the Original Scriptable Objects! IMPORTANT instance.name = instance.name.Replace("(Clone)", "(C)"); NewState = instance; } var oldState = states.Find(s => s.ID == NewState.ID); if (oldState) { var index = states.IndexOf(oldState); var oldStatePriority = oldState.Priority; if (CloneStates) Destroy(oldState); //Destroy the Clone oldState = NewState; oldState.AwakeState(this); oldState.Priority = oldStatePriority; oldState.InitializeState(); oldState.ExitState(); states[index] = oldState; //Replace the list Item UpdateInputSource(true); //Need to Update the Sources } } /// Force the Activation of an state regarding if is enable or not public virtual void State_Force(StateID ID) => State_Force(ID.ID); /// Returns if the Animal has a state by its ID public bool HasState(StateID ID) => HasState(ID.ID); /// Returns if the Animal has a state by its int ID value public bool HasState(int ID) => State_Get(ID) != null; /// Returns if the Animal has a state by its name public bool HasState(string statename) => states.Exists(s => s.name == statename); /// Set the State Status on the Animator public virtual void State_SetStatus(int status) { // Debug.Log("status = " + status); SetIntParameter(hash_StateEnterStatus, status); } /// Set the State Exit Status on the Animator public virtual void State_SetExitStatus(int ExitStatus) => SetOptionalAnimParameter(hash_StateExitStatus, ExitStatus); public virtual void State_Enable(StateID ID) => State_Enable(ID.ID); public virtual void State_Disable(StateID ID) => State_Disable(ID.ID); public virtual void State_Enable(int ID) => State_Get(ID)?.Enable(true); public virtual void State_Disable(int ID) => State_Get(ID)?.Enable(false); /// Force the Activation of an state regarding if is enable or not public virtual void State_Force(int ID) { State state = State_Get(ID); if (state == ActiveState) { state.ForceActivate(); StartCoroutine(C_EnterCoreAnim(state)); //Little Hack! } else state.ForceActivate(); } IEnumerator C_EnterCoreAnim(State state) { state.IsPending = true; yield return null; // yield return null; state.AnimationTagEnter(AnimStateTag); } /// Allow Lower States to be activated public virtual void State_AllowExit(StateID ID) => State_AllowExit(ID.ID); /// Allow Lower States to be activated public virtual void State_AllowExit(int ID) { State state = State_Get(ID); if (state && state != ActiveState) return; //Do not Exit if we are not the Active State state?.AllowExit(); } public virtual void State_Allow_Exit(int nextState) { if (ActiveState.AllowExit()) { if (nextState != -1) State_Activate(nextState); } } public virtual void State_Allow_Exit(int nextState , int exitStatus) { if (ActiveState.AllowExit()) { State_SetExitStatus(exitStatus); if (nextState != -1) State_Activate(nextState); } } public virtual void State_Active_IsPersistent(bool value) => ActiveState.IsPersistent = value; public virtual void State_InputTrue(StateID ID) => State_Get(ID)?.SetInput(true); public virtual void State_InputFalse(StateID ID) => State_Get(ID)?.SetInput(false); public virtual void ActiveStateAllowExit() => ActiveState.AllowExit(); /// Try to Activate a State direclty from the Animal Script public virtual void State_Activate(StateID ID) => State_Activate(ID.ID); /// Try to Activate a State by its ID, Checking the necessary conditions for activation public virtual bool State_TryActivate(int ID) { State NewState = State_Get(ID); if (NewState && NewState.CanBeActivated) { return NewState.TryActivate(); } return false; } /// Try to Activate a State direclty from the Animal Script public virtual void State_Activate(int ID) { State NewState = State_Get(ID); if (NewState && NewState.CanBeActivated) NewState.Activate(); } /// Return a State by its ID value public virtual State State_Get(int ID) => states.Find(s => s.ID == ID); /// Return a State by its ID public virtual State State_Get(StateID ID) { if (ID == null) return null; return State_Get(ID.ID); } /// Call the Reset State on the given State public virtual void State_Reset(int ID) => State_Get(ID)?.ResetState(); /// Call the Reset State on the given State public virtual void State_Reset(StateID ID) => State_Reset(ID.ID); /// Find to the Possible State and store it to the (PreState) using an StateID value public virtual void State_Pin(StateID stateID) => State_Pin(stateID.ID); /// Find to the Possible State and store it to the (PreState) using an int value public virtual void State_Pin(int stateID) => Pin_State = State_Get(stateID); ///Use the (PreState) the and Try to activate it using an Input public virtual void State_Pin_ByInput(bool input) => Pin_State?.ActivatebyInput(input); public virtual void State_Pin_ByInputToggle() => Pin_State?.ActivatebyInput(!Pin_State.InputValue); /// Send to the Possible State (PreState) the value of the Input public virtual void State_Activate_by_Input(StateID stateID, bool input) => State_Activate_by_Input(stateID.ID, input); /// Send to the Possible State (PreState) the value of the Input public virtual void State_Activate_by_Input(int stateID, bool input) { State_Pin(stateID); State_Pin_ByInput(input); } ///If the Pin State is the Active State then it will set the StateExit Status public virtual void State_Pin_ExitStatus(int stateExitStatus) { if (Pin_State != null && Pin_State.IsActiveState) State_SetExitStatus(stateExitStatus); } #endregion #region Modes public bool HasMode(ModeID ID) => HasMode(ID.ID); /// Returns if the Animal has a mode By its ID public bool HasMode(int ID) => Mode_Get(ID) != null; /// Returns a Mode by its ID public virtual Mode Mode_Get(ModeID ModeID) => Mode_Get(ModeID.ID); /// Returns a Mode by its ID public virtual Mode Mode_Get(int ModeID) => modes.Find(m => m.ID == ModeID); /// Set the Parameter Int ID to a value and pass it also to the Animator public void SetModeStatus(int value) { // Debug.Log("SetModeStatus" + value); SetIntParameter?.Invoke(hash_ModeStatus, ModeStatus = value); } public void Mode_SetPower(float value) => SetOptionalAnimParameter(hash_ModePower, ModePower = value); /// Activate a Random Ability on the Animal using a Mode ID public virtual void Mode_Activate(ModeID ModeID) => Mode_Activate(ModeID.ID, -99); /// Enable a mode on the Animal /// ID of the Mode /// Ability Index. If this value is -1 then the Mode will activate a random Ability public virtual void Mode_Activate(ModeID ModeID, int AbilityIndex) => Mode_Activate(ModeID.ID, AbilityIndex); public virtual void Mode_Activate_By_Input(ModeID ModeID, bool InputValue) => Mode_Get(ModeID.ID).ActivatebyInput(InputValue); #region INTERFACE ICHARACTER ACTION public bool PlayAction(int Set, int Index) => Mode_TryActivate(Set, Index); public bool ForceAction(int Set, int Index) => Mode_ForceActivate(Set, Index); public bool IsPlayingAction => IsPlayingMode; #endregion /// Activate a mode on the Animal combining the Mode and Ability e.g 4002 public virtual void Mode_Activate(int ModeID) { if (ModeID == 0) return; var id = Mathf.Abs(ModeID / 1000); if (id == 0) { Mode_Activate(ModeID, -99); } else { Mode_Activate(id, ModeID % 100); } } /// Activate a mode on the Animal /// ID of the Mode /// Ability Index. If this value is -99 then the Mode will activate a random Ability public virtual void Mode_Activate(int ModeID, int AbilityIndex) { var mode = Mode_Get(ModeID); if (mode != null) { Pin_Mode = mode; Pin_Mode.TryActivate(AbilityIndex); } else { Debug.LogWarning("You are trying to Activate a Mode but here's no Mode with the ID or is Disabled: " + ModeID); } } /// Activate a mode on the Animal /// ID of the Mode /// Ability Index. If this value is -99 then the Mode will activate a random Ability public virtual void Mode_Activate(int ModeID, int AbilityIndex, AbilityStatus status) { var mode = Mode_Get(ModeID); if (mode != null) { Pin_Mode = mode; var ability = Pin_Mode.GetAbility(AbilityIndex); if (ability != null) { ability.Status = status; //Change the Status of the ability } Pin_Mode.TryActivate(AbilityIndex); } else { Debug.LogWarning("You are trying to Activate a Mode but here's no Mode with the ID or is Disabled: " + ModeID); } } public virtual bool Mode_ForceActivate(ModeID ModeID, int AbilityIndex) => Mode_ForceActivate(ModeID.ID, AbilityIndex); public virtual void Mode_ForceActivate(ModeID ModeID) => Mode_ForceActivate(ModeID.ID, 0); public virtual bool Mode_ForceActivate(int ModeID, int AbilityIndex) { var mode = Mode_Get(ModeID); if (mode != null) { Pin_Mode = mode; return Pin_Mode.ForceActivate(AbilityIndex); } return false; } /// Returns True and Activate the mode in case ir can be Activated, if not it will return false public bool Mode_TryActivate(int ModeID, int AbilityIndex = -99) { var mode = Mode_Get(ModeID); if (mode != null) { Pin_Mode = mode; return Pin_Mode.TryActivate(AbilityIndex); } return false; } public bool Mode_TryActivate(int ModeID, int AbilityIndex, AbilityStatus status, float time = 0) { var mode = Mode_Get(ModeID); if (mode != null) { Pin_Mode = mode; return Pin_Mode.TryActivate(AbilityIndex, status, time); } return false; } /// Stop all modes public virtual void Mode_Stop() { if (IsPlayingMode) { activeMode.InputValue = false; Mode_Interrupt(); } else { ModeAbility = 0; //Reset Mode SetModeStatus(Int_ID.Available); return; } //ActiveMode.PlayingMode = false; ActiveMode = null; ModeTime = 0; //Reset Mode Time } /// Re-Check all the Sprint conditions and Invoke the Sprint Event public virtual void SprintUpdate() => Sprint = sprint; //Check Again the sprint everytime a new state is active IMPORTANT public virtual void Sprint_Set(bool value) => Sprint = value; /// Set IntID to -2 to exit the Mode Animation public virtual void Mode_Interrupt() => SetModeStatus(Int_ID.Interrupted); /// Deactivate all modes public virtual void Mode_Disable_All() { foreach (var mod in modes) mod.Disable(); } /// Reactivate all modes public virtual void Mode_Enable_All() { foreach (var mod in modes) mod.Enable(); } /// Disable a Mode by his ID public virtual void Mode_Disable(ModeID id) => Mode_Disable((int)id); /// Disable a Mode by his ID public virtual void Mode_Disable(int id) => Mode_Get(id)?.Disable(); public virtual void Mode_Disable(string mod) { foreach (var M in modes) { if (mod.Contains(M.Name)) M.Disable(); } } public virtual void Mode_Enable(string mod) { foreach (var M in modes) { if (mod.Contains(M.Name)) M.Enable(); } } /// Set the Active Ability Index of a Mode public virtual void Mode_ActiveAbilityIndex(int Mode, int ActiveAbility) => Mode_Get(Mode).SetAbilityIndex(ActiveAbility); /// Enable a Mode by his ID public virtual void Mode_Enable(ModeID id) => Mode_Enable(id.ID); /// Enable a Mode by his ID public virtual void Mode_Enable(int id) => Mode_Get(id)?.Enable(); /// Pin a mode to Activate later public virtual void Mode_Pin(ModeID ID) { if (Pin_Mode != null && Pin_Mode.ID == ID) return; //the mode is already pinned var pin = Mode_Get(ID); Pin_Mode = null; //Important! Clean the Pin Mode if (pin != null && pin.Active) Pin_Mode = pin; } /// Pin an Ability on the Pin Mode to Activate later public virtual void Mode_Pin_Ability(int AbilityIndex) { if (AbilityIndex == 0) return; if (Pin_Mode != null) Pin_Mode.SetAbilityIndex(AbilityIndex); } /// Enable/Disables an Ability on a mode, Returns true if the Method succeeded public virtual bool Mode_Ability_Enable(int ModeID, int AbilityID, bool enable) { var mode = Mode_Get(ModeID); if (mode != null) { var ability = mode.GetAbility(AbilityID); if (ability != null) { ability.Active = enable; return true; } } return false; } /// Pin an Ability on the Pin Mode to Activate later public virtual void Mode_Pin_Ability_Enable(int AbilityIndex) { if (AbilityIndex == 0) return; var ability = Pin_Mode?.GetAbility(AbilityIndex); if (ability != null) ability.Active = true; } /// Pin an Ability on the Pin Mode to Activate later public virtual void Mode_Pin_Disable_Ability(int AbilityIndex) { if (AbilityIndex == 0) return; var ability = Pin_Mode?.GetAbility(AbilityIndex); if (ability != null) ability.Active = false; } /// Changes Pinned Mode Status in all the Abilities public virtual void Mode_Pin_Status(int aMode) { if (Pin_Mode != null) { foreach (var ability in Pin_Mode.Abilities) { ability.Status = (AbilityStatus)aMode; } } } /// Changes the Pinned Mode time when using Hold by time Status public virtual void Mode_Pin_Time(float time) { if (Pin_Mode != null) foreach (var ab in Pin_Mode.Abilities) ab.AbilityTime= time; } public virtual void Mode_Pin_Enable(bool value) => Pin_Mode?.SetActive(value); public virtual void Mode_Pin_EnableInvert(bool value) => Pin_Mode?.SetActive(!value); public virtual void Mode_Pin_Input(bool value) => Pin_Mode?.ActivatebyInput(value); /// Tries to Activate the Pin Mode public virtual void Mode_Pin_Activate() => Pin_Mode?.TryActivate(); /// Tries to Activate the Pin Mode with an Ability public virtual void Mode_Pin_AbilityActivate(int AbilityIndex) => Pin_Mode?.TryActivate(AbilityIndex); #endregion #region Movement public virtual void Strafe_Toggle() => Strafe ^= true; /// Gets the movement from the Input Script or AI public virtual void Move(Vector3 move) { UseRawInput = false; RawInputAxis = move;//.normalized; //Store the Last Raw Direction sent (Important to Normalize?? why??) Rotate_at_Direction = false; DeltaAngle = 0; } /// Gets the movement from the Input using a 2 Vector (ex UI Axis Joystick) public virtual void Move(Vector2 move) => Move(new Vector3(move.x, 0, move.y)); /// Gets the movement from the Input ignoring the Direction Vector, using a 2 Vector (ex UI Axis Joystick) public virtual void MoveWorld(Vector2 move) => MoveWorld(new Vector3(move.x, 0, move.y)); /// Stop the animal from moving, cleaning the Movement Axis public virtual void StopMoving() { RawInputAxis = Vector3.zero; DeltaAngle = 0; } /// Add Inertia to the Movementd public virtual void AddInertia(ref Vector3 Inertia, float speed = 1f) { AdditivePosition += Inertia; Inertia = Vector3.Lerp(Inertia, Vector3.zero, DeltaTime * speed); } #endregion #region Speeds /// Change the Speed Up public virtual void SpeedUp() => Speed_Add(+1); /// Changes the Speed Down public virtual void SpeedDown() => Speed_Add(-1); /// Get a SpeedSet by its name public virtual MSpeedSet SpeedSet_Get(string name) => speedSets.Find(x => x.name == name); public virtual MSpeed Speed_GetModifier(string name, int index) { var set = SpeedSet_Get(name); if (set != null && index < set.Speeds.Count) return set[index - 1]; return MSpeed.Default; } /// Set a custom speed created via script and it uses it as the Current Speed Modifier (used on the Fall and Jump State) public virtual void SetCustomSpeed(MSpeed customSpeed, bool keepInertiaSpeed = false) { CustomSpeed = true; CurrentSpeedModifier = customSpeed; //currentSpeedSet = null; //IMPORTANT SET THE CURRENT SPEED SET TO NULL if (keepInertiaSpeed) { CalculateTargetSpeed(); //Important needs to calculate the Target Speed again InertiaPositionSpeed = TargetSpeed; //Set the Target speed to the Fall Speed so there's no Lerping when the speed changes } } private void Speed_Add(int change) => CurrentSpeedIndex += change; /// Set an specific Speed for the active speed modifier public virtual void Speed_CurrentIndex_Set(int speedIndex) => CurrentSpeedIndex = speedIndex; /// Set an specific Speed for the active State using IntVars public virtual void Speed_CurrentIndex_Set(IntVar speedIndex) => CurrentSpeedIndex = speedIndex; /// Lock the Speed on the Current Speed Set public virtual void Speed_Lock(bool lockSpeed) => CurrentSpeedSet.LockSpeed = lockSpeed; /// Lock the Speed on a Speed Set using the SpeedSet Name public virtual void Speed_Lock(string SpeedSetName, bool lockSpeed) => Speed_Lock(SpeedSetName, lockSpeed, 0); /// Lock the Speed on a Speed Set using the SpeedSet Name, and the Speed LockIndex public virtual void Speed_Lock(string SpeedSetName, bool lockSpeed, int LockIndex) { var speedSet = SpeedSet_Get(SpeedSetName); if (speedSet != null) { if (LockIndex != 0) speedSet.LockIndex = Mathf.Clamp(LockIndex, 1, speedSet.Speeds.Count); speedSet.LockSpeed = lockSpeed; if (lockSpeed) { OnSpeedChange.Invoke(speedSet.LockedSpeedModifier); EnterSpeedEvent(CurrentSpeedIndex); } } } public virtual void SpeedSet_Lock(string SpeedSetName) => Speed_Lock(SpeedSetName, true, 0); public virtual void SpeedSet_Unlock(string SpeedSetName) => Speed_Lock(SpeedSetName, false, 0); /// Set a Speed Modifier and its indexx public virtual void SpeedSet_Set_Active(string SpeedSetName, int activeIndex) { var speedSet = SpeedSet_Get(SpeedSetName); if (speedSet != null) { speedSet.CurrentIndex = activeIndex; if (CurrentSpeedSet == speedSet) { CurrentSpeedIndex = activeIndex; speedSet.StartVerticalIndex = activeIndex; //Set the Start Vertical Index as the new Speed } } } public virtual void Speed_Update_Current() => CurrentSpeedIndex = CurrentSpeedIndex; /// Change the Top Index of a Speed Set public virtual void Speed_SetTopIndex(int topIndex) { CurrentSpeedSet.TopIndex = topIndex; Speed_Update_Current(); } public virtual void Speed_SetTopIndex(string SpeedSetName, int topIndex) { var speedSet = SpeedSet_Get(SpeedSetName); if (speedSet != null) { speedSet.TopIndex = topIndex; Speed_Update_Current(); } } /// Change the Speed of a Speed Set public virtual void SpeedSet_Set_Active(string SpeedSetName, string activeSpeed) { var speedSet = speedSets.Find(x => x.name.ToLower() == SpeedSetName.ToLower()); if (speedSet != null) { var mspeedIndex = speedSet.Speeds.FindIndex(x => x.name.ToLower() == activeSpeed.ToLower()); if (mspeedIndex != -1) { speedSet.CurrentIndex = mspeedIndex + 1; if (CurrentSpeedSet == speedSet) { CurrentSpeedIndex = mspeedIndex + 1; speedSet.StartVerticalIndex = CurrentSpeedIndex; //Set the Start Vertical Index as the new Speed } } } else { Debug.LogWarning("There's no Speed Set called : " + SpeedSetName); } } #endregion #region Extras /// /// Adds a Custom Force to the Animal /// /// Direction of the Force Applied to the Animal /// Amount of Force aplied to the Direction /// Smoothens value to apply the force. Higher values faster the force is appled /// Every time a force is applied, the Gravity Aceleration will be reseted /// /// public virtual void Force_Add( Vector3 Direction, float Force, float Aceleration, bool ResetGravity, bool ForceAirControl = true, float LimitForce = 0) { var CurrentForce = CurrentExternalForce + GravityStoredVelocity; //Calculate the Starting force if (LimitForce > 0 && CurrentForce.magnitude > LimitForce) CurrentForce = CurrentForce.normalized * LimitForce; //Add the Bounce CurrentExternalForce = CurrentForce; ExternalForce = Direction.normalized * Force; ExternalForceAcel = Aceleration; if (ActiveState.ID == StateEnum.Fall) //If we enter to a zone from the Fall state.. Reset the Fall Current Distance { var fall = ActiveState as Fall; fall.FallCurrentDistance = 0; } if (ResetGravity) GravityTime = 0; ExternalForceAirControl = ForceAirControl; } public virtual void Force_Remove(float Aceleration = 0) { ExternalForceAcel = Aceleration; ExternalForce = Vector3.zero; } internal void Force_Reset() { CurrentExternalForce = Vector3.zero; ExternalForce = Vector3.zero; ExternalForceAcel = 0; } /// Disable the animal Compoent after a time public virtual void DisableSelf(float time) => this.Delay_Action(time, () => enabled = false); /// If the Animal has touched the ground then Grounded will be set to true public bool CheckIfGrounded() { AlignRayCasting(); if (MainRay && FrontRay && !DeepSlope) { return Grounded = true; //Activate the Grounded Parameter so the Idle and the Locomotion State can be activated } return false; } public void Always_Forward(bool value) => AlwaysForward = value; /// Activate Attack triggers public virtual void ActivateDamager(int ID) { if (Sleep) return; if (ID == -1) //Enable all Attack Triggers { foreach (var dam in Attack_Triggers) dam.DoDamage(true); } else if (ID == 0) //Disable all Attack Triggers { foreach (var dam in Attack_Triggers) dam.DoDamage(false); } else { var Att_T = Attack_Triggers.FindAll(x => x.Index == ID); //Enable just a trigger with an index if (Att_T != null) foreach (var dam in Att_T) dam.DoDamage(true); } } /// Store all the Animal Colliders internal void GetAnimalColliders() { var colls = GetComponentsInChildren(true).ToList(); //Get all the Active colliders colliders = new List(); foreach (var item in colls) { if (!item.isTrigger/* && item.gameObject.layer == gameObject.layer*/) colliders.Add(item); //Add the Animal Colliders Only } } /// Enable/Disable All Colliders on the animal. Avoid the Triggers public virtual void EnableColliders(bool active) { //Debug.Log("EnableColliders = " + active); foreach (var item in colliders) { if (item) item.enabled = active; } } /// Disable this Script and MalbersInput Script if it has it. public virtual void DisableAnimal() { enabled = false; MalbersInput MI = GetComponent(); if (MI) MI.enabled = false; } public void SetTimeline(bool isonTimeline) { Sleep = isonTimeline; //Unparent the Rotator since breaks the Cinemachine Logic if (Rotator != null) RootBone.parent = isonTimeline ? null : Rotator; } /// InertiaPositionSpeed = TargetSpeed public void ResetInertiaSpeed() => InertiaPositionSpeed = TargetSpeed; public void UseCameraBasedInput() => UseCameraInput = true; private void OnDestroy() { OnDisable(); } #endregion } }