public virtual void TakeDamage(float damage, GameObject attacker = null, ParticleType particleType = ParticleType.None, Transform hitSource = null)
    {
        Character character = GetComponentInParent <Character>();

        // Nobody should be able to hit him/herself
        if (attacker == character.gameObject)
        {
            return;
        }

        if (character != null)
        {
            bool rightHit = true;
            // Figuring out whether it's a hit from the right or left
            float sign = Mathf.Sign(Vector3.Cross((transform.position - character.transform.position).normalized, character.transform.forward).y);
            if (sign < 0)
            {
                rightHit = false;
            }

            character.Animator.SetTrigger(rightHit ? "HitRight" : "HitLeft");
        }

        if (_stateMachine != null)
        {
            _stateMachine.AISCanner.AddPotentialThreat(attacker);

            // If we just took damage from nowhere (when we don't have a player or a character as target, then we look around and set the attacker as an angry at threat)
            if ((_stateMachine.CurrentTarget == null ||
                 (_stateMachine.CurrentTarget != null &&
                  (_stateMachine.CurrentTarget.Type != AITargetType.Enemy && _stateMachine.CurrentTarget.Type != AITargetType.Player))))
            {
                _stateMachine.CurrentTarget = null;
                _stateMachine.Agent.ResetPath();
                _stateMachine.SwitchState(AIState.AIStateType.Alert);
                _justGotHit          = true;
                _resetJustGotHitTime = Time.time + 5f;
            }
        }


        _health -= damage;
        _health  = Mathf.Max(0, _health);

        if (particleType != ParticleType.None)
        {
            GameManager.Instance.ParticlesManager.InstantiatePaticle(particleType, hitSource == null ? transform.position :hitSource.position);
        }

        if (_health <= 0)
        {
            Die();
        }
    }
    private void OnTriggerStay(Collider other)
    {
        if (_stateMachine.Health.IsDead)
        {
            return;
        }

        bool enemyOrPlayerThreatInSight = false;

        // This is for potential threats
        if (other.CompareTag("Player") || other.CompareTag("Enemy"))
        {
            if (_angryAt.Count == 0)
            {
                goto NotAngryAtAnyoneInSight;
            }

            // First check if we are angry at the potential target
            GameObject potentialTargetObject = _angryAt.Find(potentialTarget => potentialTarget == other.gameObject);
            if (potentialTargetObject == null)
            {
                goto NotAngryAtAnyoneInSight;
            }

            // If we are already seeing a player or enemy and the target we just saw is a different one, then we don't do anything
            if (_stateMachine.CurrentTarget != null && _stateMachine.CurrentTarget.TargetTransform.gameObject != other.gameObject &&
                (_stateMachine.CurrentTarget.Type == AITargetType.Player || _stateMachine.CurrentTarget.Type == AITargetType.Enemy))
            {
                return;
            }

            // At this point, we are only interacting with either the target that we already have as a target or a new one (since we don't have a target at all)


            RaycastHit hitInfo;
            if (Physics.Raycast(transform.position, (other.transform.position + Vector3.up) - transform.position, out hitInfo, _sphereCollider.radius, _seeablesMask))
            {
                if (hitInfo.transform.gameObject == other.gameObject)
                {
                    float angle = Vector3.Angle(transform.forward, (other.transform.position + Vector3.up) - transform.position);
                    if (angle <= _fieldOfView)
                    {
                        _stateMachine.SetTarget(other.transform, AITargetType.Player);
                        enemyOrPlayerThreatInSight = true;
                    }
                }
            }

            // Resetting target for when the player or the enemy is inside the scanner collider but not inside the field of view
            // If we were seeing the player or the enemy and no longer see him, then we reset the target after the tolerance time
            if (_stateMachine.CurrentTarget != null && _stateMachine.CurrentTarget.Type == AITargetType.Player && !enemyOrPlayerThreatInSight)
            {
                _targetOutOfSightTime += Time.deltaTime;

                if (_targetOutOfSightTime >= _targetOutOfSightTolerance)
                {
                    _targetOutOfSightTime = 0f;
                    _stateMachine.ResetTarget();
                }
            }
        }

        if (enemyOrPlayerThreatInSight)
        {
            return;
        }

NotAngryAtAnyoneInSight:

        if (other.CompareTag("AIVehicleDetector") &&
            (_stateMachine.CurrentTarget == null || _stateMachine.CurrentTarget.Type != AITargetType.Vehicle) &&
            _stateMachine.CurrentState.GetStateType() != AIState.AIStateType.Vehicle)
        {
            InteractiveVehicle interactiveVehicle = other.GetComponent <InteractiveVehicle>();
            if (interactiveVehicle != null && !interactiveVehicle.Occupied)
            {
                AIStateVehicle aiStateDriving = (AIStateVehicle)(_stateMachine.GetState(AIState.AIStateType.Vehicle));
                if ((aiStateDriving.WantToDrive && interactiveVehicle.DriverSeat) || (aiStateDriving.WantToBePassenger && !interactiveVehicle.DriverSeat))
                {
                    _stateMachine.SetTarget(other.transform, AITargetType.Vehicle);
                    return;
                }
            }
        }

        if (other.CompareTag("Chair") &&
            (_stateMachine.CurrentTarget == null || _stateMachine.CurrentTarget.Type != AITargetType.Chair) &&
            _stateMachine.CurrentState.GetStateType() != AIState.AIStateType.Sitting)
        {
            InteractiveChair interactiveChair = other.GetComponent <InteractiveChair>();
            if (interactiveChair != null && !interactiveChair.IsOccupied)
            {
                AIStateSitting aiStateDriving = (AIStateSitting)(_stateMachine.GetState(AIState.AIStateType.Sitting));
                if (aiStateDriving.IsTired)
                {
                    _stateMachine.SetTarget(other.transform, AITargetType.Chair);
                    return;
                }
            }
        }

        // For social Interaction
        if (other.CompareTag("Enemy"))
        {
            // if we are already interacting socially, then there is nothing to do here
            if (_stateMachine.CurrentTarget != null && _stateMachine.CurrentTarget.Type == AITargetType.Friend)
            {
                return;
            }

            AIStateMachine otherStateMachine = other.GetComponent <AIStateMachine>();

            AIStateSocialInteraction mySocialInteractionState     = (AIStateSocialInteraction)(_stateMachine.GetState(AIState.AIStateType.SocialInteraction));
            AIStatePursuit           myPursuitState               = (AIStatePursuit)(_stateMachine.GetState(AIState.AIStateType.Pursuit));
            AIStateSocialInteraction friendSocialInteractionState = (AIStateSocialInteraction)(otherStateMachine.GetState(AIState.AIStateType.SocialInteraction));
            AIStatePursuit           friendPursuitState           = (AIStatePursuit)(otherStateMachine.GetState(AIState.AIStateType.Pursuit));
            if (mySocialInteractionState.IsFeelingLonely && friendSocialInteractionState.IsFeelingLonely &&
                !otherStateMachine.Health.IsDead)
            {
                // If the friend hasn't gotten into social mode yet by setting a target of type friend, then we are the ones who are going to be making the first move
                if (otherStateMachine.CurrentTarget == null ||
                    otherStateMachine.CurrentTarget.Type == AITargetType.None ||
                    otherStateMachine.CurrentTarget.Type == AITargetType.NavigationPoint ||
                    otherStateMachine.CurrentTarget.Type == AITargetType.BoomBox)
                {
                    myPursuitState.ShouldMakeAMove = true;
                    mySocialInteractionState.FriendSocialInteraction = friendSocialInteractionState;
                    // We set our pursuit mode by setting a target
                    _stateMachine.SetTarget(other.transform, AITargetType.Friend);

                    // the friend should instantly get into social mode
                    friendSocialInteractionState.FriendSocialInteraction = mySocialInteractionState;
                    otherStateMachine.SetTarget(transform, AITargetType.Friend);
                    otherStateMachine.SwitchState(AIState.AIStateType.SocialInteraction);
                }
            }
        }

        if (other.CompareTag("BoomBox"))
        {
            // If we have a more important target: Another AI Agent as an enemy, a friend or a player, then we ignore the boombox
            if (_stateMachine.CurrentTarget != null &&
                (_stateMachine.CurrentTarget.Type == AITargetType.Player ||
                 _stateMachine.CurrentTarget.Type == AITargetType.Enemy ||
                 _stateMachine.CurrentTarget.Type == AITargetType.Friend))
            {
                return;
            }

            AIStateDancing aiStateDancing = (AIStateDancing)(_stateMachine.GetState(AIState.AIStateType.Dancing));
            if (aiStateDancing.IsHungry)
            {
                _stateMachine.SetTarget(other.transform, AITargetType.BoomBox);
            }
        }
    }