using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Entity : MonoBehaviour {
    public string charName = "???";
    [Header("A = party")]
    [Tooltip("space = not fighting\nA = party\nB = AI vs A & C & D\nC = AI vs B & D\nD = AI vs A & B & C")]
    public string team = "";
    public List<Emotion.types> emotions = new List<Emotion.types>();
    [HideInInspector]
    public int enemyIdentifier = -1;
    [HideInInspector]
    public Parameters parameters;
    [HideInInspector]
    public Parameters maxedParameters;

    [System.Serializable]
    public class Parameters {
        [Header("HP")]
        public int currentHP = 512;
        public int maxHP = 512;
        public float maxHPBuff = 1.0f;
        [Header("Stamina")]
        public int currentStamina = 0;
        public int maxStamina = 100;
        public float maxStaminaBuff = 1.0f;
        [Header("MP")]
        public int currentMP = 20;
        public int maxMP = 20;
        public float maxMPBuff = 1.0f;
        [Header("Parameters")]
        public int force = 20;
        public int defence = 20;
        public int magic = 20;
        public int magicDefence = 20;
        public int agility = 20;
        public int aim = 20;
        public int evasion = 20;
        public float fireBuff = 1.0f;
        public float iceBuff = 1.0f;
        public float thunderBuff = 1.0f;
        public float earthBuff = 1.0f;
        public float darknessBuff = 1.0f;
        public float lightBuff = 1.0f;
        [Header("Party related")]
        public int level = 1;
        public bool following = true;
        [HideInInspector]
        public int outfit = 0;
        [HideInInspector]
        public int affection;
        [HideInInspector]
        public float availableEXP;
        [HideInInspector]
        public float nextLevelEXP = 0.0f;
        public bool canUseSwords = true;
        public bool canUseGuns = true;
        public bool canUseHDev = true;
        //[HideInInspector]
        public List<string> licensedAbilities = new List<string>();
        [HideInInspector]
        public string key;
        [HideInInspector]
        public string weaponID;
        [HideInInspector]
        public int weaponAmmo = 0;
        [HideInInspector]
        public string lastSwordID;
        [HideInInspector]
        public bool replacePosRot = false;
        [HideInInspector]
        public Vector3 pos;
        [HideInInspector]
        public Quaternion rot;

        public override string ToString() {
            return "HP: " + maxHP + " MP: " + maxMP + " Stamina: " + maxStamina + " Force: " + force + " Defence: " + defence + " Magic: " + magic + " Magic defence: " + magicDefence + " Agility: " + agility + " Aim: " + aim + " Evasion: " + evasion;
        }

        public string ToCsv() {
            return maxHP + "; " + maxMP + "; " + maxStamina + "; " + force + "; " + defence + "; " + magic + "; " + magicDefence + "; " + agility + "; " + aim + "; " + evasion + ";";
        }
    }
    [HideInInspector]
    public Entity parametersLink;

    [Header("Party related")]
    public List<string> techniques = new List<string>();
    public Color representativeColor = Color.white;
    public bool canChangeWeapon = true;
    public bool checkForOutfits = false;
    private int currentOutfitIndex = -1;
    private Avatar originalAvatar = null;
    private Vector3 orignalSwordBackPosition;
    private Quaternion orignalSwordBackRotation;
    public float swimHeight = 0.75f;
    [Header("Other fields")]
    public Texture2D icon;
    public int quickHitAnimation = -1;
    public bool persistent = false;
    public bool canDie = true;
    public float getHitAnimationFraction = 40.0f;
    [HideInInspector]
    public bool swimming = false;
    private bool usingHDev;
    public MovementController controller;
    public CharacterController collider;
    private bool replaceWeapon = false;
    private float weaponChangeTime = 0.0f;
    [HideInInspector]
    public bool multiplayer = false;
    [HideInInspector]
    public bool fighting = false;
    private bool wasFighting = false;
    [HideInInspector]
    public int playerID = 0;
    public Transform head;
    private Transform chest;
    private Vector3 chestLocalPos;
    public float breathIntensity = 1.0f;
    private float breathSpeed = 1.0f;
    private float baseBreathSpeed = 2.0f;
    private float breathTime = 0.0f;
    [HideInInspector]
    public Transform lookingTarget;
    private Transform oldLookingTarget;
    private float lookFactor = 0.0f;
    [HideInInspector]
    public RuntimeAnimatorController originalRealtimeAnimator;
    public RuntimeAnimatorController battleRealtimeAnimator;
    [HideInInspector]
    public AnimatorStateInfo currentState;
    [HideInInspector]
    public Coroutine scriptedMovement;
    [HideInInspector]
    public Object scriptedMovementScene;
    [HideInInspector]
    public Vector3 battleMotion;
    [HideInInspector]
    public bool actionCompleted = false;
    [HideInInspector]
    public bool forceShowSword = false;
    private Entity aimingEntity;
    private float aimingEntityDistance;
    private Coroutine action;
    private bool physicalAttack = false;
    private Vector3 currentMotion = Vector3.zero;
    private Vector2 trueRandomMotion = Vector2.zero;
    private Vector2 randomMotion = Vector2.zero;
    private float randomMotionTime = 1.0f;
    private Entity attackingEntity;

    private Quaternion ahb;
    private Vector2 randomLookDirection;
    protected Quaternion startHeadRotation;
    protected Animator animator;
    [HideInInspector]
    public bool protecting;
    [HideInInspector]
    public bool shooting;
    private bool leftMF = false;
    public bool customFlag1 = false;
    public bool customFlag2 = false;
    private float shootTime;
    private float shootRecoil = 0.0f;
    private float aimingTime;
    private float backwards;
    private float staminaTime = 0.0f;
    private float mpTime = 2.0f;

    // Standard MonoBehaviour functions

    void Start() {
        if (parametersLink != null) {
            this.parameters = parametersLink.parameters;
        }
        fighting = false;
        animator = GetComponent<Animator>();
        currentState = new AnimatorStateInfo();
        if (team.Length > 0) {
            team = team.ToUpper();
        }
        if (animator != null) {
            originalRealtimeAnimator = animator.runtimeAnimatorController;
            currentState = animator.GetCurrentAnimatorStateInfo(0);
            originalAvatar = animator.avatar;
        }
        if (head != null) {
            startHeadRotation = head.localRotation;
            if (head.parent != null && head.parent.parent != null && head.parent.parent.parent != null && head.parent.parent.parent.parent != null && head.parent.parent.parent.parent.parent != null) {
                chest = head.parent.parent.parent;
                chestLocalPos = chest.parent.localPosition;
                baseBreathSpeed = Random.Range(1.8f, 2.2f);
                breathTime = Random.Range(-10.0f, 10.0f);
            }
        }
        if (collider == null) {
            collider = GetComponent<CharacterController>();
        }
        if (controller == null) {
            controller = GetComponent<MovementController>();
        }
        aimingEntity = null;
        oldLookingTarget = lookingTarget;
        wasFighting = fighting;
        protecting = false;
        shooting = false;
        actionCompleted = false;
        enemyIdentifier = -1;
        forceShowSword = false;
        if (parameters.level <= 1 && parameters.nextLevelEXP <= 0.0f && team == "A") {
            parameters.nextLevelEXP = NeededExpToLevel(1);
        }
        if (checkForOutfits) {
            currentOutfitIndex = 0;
            if (parameters.outfit != 0) {
                ChangeOutfit(parameters.outfit);
            }
        }
        /*if (rightFArm != null && parameters.canUseHDev && team == "A") {
            GameObject hDevObject;
            hDevObject = GameObject.Instantiate(Resources.Load<GameObject>("HDev"), rightFArm);
            if (hDevObject != null) {
                hDevObject.transform.localPosition = new Vector3(-0.0238f, 0.112f, 0.012f);
                hDevObject.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, 89.32f);
                hDev = hDevObject.GetComponent<HDevController>();
                hDev.user = this;
            }
        }*/
    }

    void Update() {
#if UNITY_EDITOR
        //DEBUG
        if (Input.GetKeyDown(KeyCode.J)) {
            LevelUp(100);
        }
#endif
        if (fighting) {
            if (!wasFighting) {
                if (animator != null) {
                    animator.SetBool("Aiming", false);
                }
            }
            if (staminaTime <= 0.0) {
                parameters.currentStamina += 1;
                staminaTime = 0.5f;
            }
            if (parameters.currentHP <= 0) {
                battleMotion = Vector3.zero;
                shooting = false;
                parameters.currentStamina = 0;
                collider.enabled = false;
            }
            Vector3 targetMotion = Vector3.zero;
            targetMotion = battleMotion;
            /*if (action != null && physicalAttack) {
                targetMotion = battleMotion;
            }
            else {
                if (battleMotion != Vector3.zero) {
                    Vector3 forward;
                    if (aimingEntity != null) {
                        forward = (aimingEntity.transform.position - transform.position).normalized;
                    }
                    else {
                        Transform cameraTransform = Camera.main.transform;
                        forward = cameraTransform.TransformDirection(Vector3.forward);
                    }
                    forward = new Vector3(forward.x, 0.0f, forward.z);
                    forward = forward.normalized;
                    Vector3 right = new Vector3(forward.z, 0, -forward.x);
                    Vector3 moveDirection = Vector3.zero;
                    if (battleMotion.magnitude > 0.5f) {
                        moveDirection = (battleMotion.x * right) + (battleMotion.y * forward);
                    }
                    targetMotion = moveDirection * 2.1f;
                }
            }*/
            currentMotion = Vector3.Lerp(currentMotion, targetMotion, Time.deltaTime * 7.0f);
            if (currentMotion.magnitude > 0.2) {
                transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(currentMotion), 10.0f * Time.deltaTime);
            }
            else if (aimingEntity != null && parameters.currentHP > 0 && aimingEntity != this) {
                Vector3 dif = aimingEntity.transform.position - transform.position;
                dif = new Vector3(dif.x, 0.0f, dif.z);
                transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dif), 7.0f * Time.deltaTime);
            }
            if (collider != null && collider.enabled) {
                collider.Move(((currentMotion / 2.0f) + Physics.gravity) * Time.deltaTime);
            }
            if (animator != null) {
                animator.SetFloat("Speed", currentMotion.magnitude);
                animator.SetFloat("Movement X", randomMotion.x);
                animator.SetFloat("Movement Y", randomMotion.y);
                if (currentMotion.magnitude >= 1.0f) {
                    animator.SetBool("Aiming", false);
                }
                else {
                    animator.SetBool("Aiming", true);
                }
            }
            usingHDev = false;
        }
        else {
            if (wasFighting) {
                if (animator != null) {
                    animator.SetBool("Aiming", false);
                }
                if (parameters.currentHP <= 0) {
                    collider.enabled = false;
                }
                aimingEntity = null;
            }
            if (parameters.currentHP > 0 && !PauseMenu.paused) {
                mpTime -= Time.deltaTime;
            }
            if (mpTime <= 0.0) {
                parameters.currentMP += 1;
                mpTime = 5.0f;
                CheckMax();
            }
            battleMotion = Vector3.zero;
            currentMotion = Vector3.Lerp(currentMotion, Vector3.zero, Time.deltaTime * 7.0f);
            trueRandomMotion = Vector2.zero;
            randomMotion = Vector2.zero;
            randomMotionTime = 2.0f;
            protecting = false;
            shooting = false;
            parameters.currentStamina = 0;
            staminaTime = 0.1f;
            if (animator != null && animator.enabled) {
                animator.SetBool("UsingHDev", usingHDev);
            }
        }
        if (animator != null && animator.enabled) {
            animator.SetBool("Dead", parameters.currentHP <= 0 && canDie);
            animator.SetBool("Blocking", protecting && fighting);
        }
        if (collider != null && fighting) {
            collider.enabled = parameters.currentHP > 0;
        }
        wasFighting = fighting;
    }

    void LateUpdate() {
        float angleB = 0.0f;
        if (lookingTarget != oldLookingTarget) {
            lookFactor = 0.0f;
            oldLookingTarget = lookingTarget;
        }
        else if (lookFactor < 1.0f){
            lookFactor = Mathf.Lerp(lookFactor, 1.0f, Time.deltaTime * 7.0f);
        }
        if (lookingTarget != null && head != null) {
            Vector3 charDir = lookingTarget.position - head.position;
            angleB = Vector3.Angle(charDir, transform.forward);
        }
        if (lookingTarget != null && angleB < 85 && head != null) {
            Vector3 ldir = lookingTarget.position - head.position;
            head.rotation = Quaternion.Euler(Quaternion.LookRotation(ldir, head.parent.up).eulerAngles + new Vector3(34.0f, 0.0f, 0.0f));
            ahb = Quaternion.Slerp(ahb, head.localRotation, Time.deltaTime * 5 * lookFactor);
            head.localRotation = ahb;
        }
        else if (head != null) {
            if (animator != null && animator.enabled) {
                ahb = Quaternion.Slerp(ahb, head.localRotation, Time.deltaTime * 5 * lookFactor);
            }
            else {
                ahb = Quaternion.Slerp(ahb, startHeadRotation, Time.deltaTime * 5 * lookFactor);
            }
            head.localRotation = ahb;

        }
        if (chest != null && breathIntensity > 0.0f) {
            if (fighting) {
                breathSpeed = Mathf.Lerp(breathSpeed, 2.3f, Time.deltaTime * 2.0f);
            }
            else {
                breathSpeed = Mathf.Lerp(breathSpeed, 1.0f, Time.deltaTime * 0.1f);
            }
            breathTime += Time.deltaTime * baseBreathSpeed * breathSpeed;
            float breathTimeFactor = (1.0f + Mathf.Cos(breathTime) * breathIntensity);
            if (usingHDev) {
                breathTimeFactor = breathTimeFactor * 0.2f;
            }
            chest.parent.parent.localScale = new Vector3(1.0f + breathTimeFactor * 0.025f, 1.0f, 1.0f + breathTimeFactor * 0.03f);
            chest.localScale = new Vector3(1.0f - breathTimeFactor * 0.025f, 1.0f, 1.0f - breathTimeFactor * 0.03f);
            chest.parent.localPosition = chestLocalPos + new Vector3(0.0f, breathTimeFactor * 0.005f, breathTimeFactor * 0.004f);
        }
        CheckMax();
    }

    // Custom functions

    public void Initialize() {
        MovementController movementController = this.gameObject.GetComponent<MovementController>();
        if (this.controller == null && movementController != null) {
            this.controller = movementController;
        }
        CharacterController characterController = this.gameObject.GetComponent<CharacterController>();
        if (characterController == null) {
            characterController = this.gameObject.AddComponent<CharacterController>();
            characterController.slopeLimit = 45;
            characterController.stepOffset = 0.3f;
            characterController.skinWidth = 0.08f;
            characterController.center = new Vector3(0.0f, 0.65f, 0.0f);    // (0.0f, 0.655f, 0.0f)
            characterController.radius = 0.25f; // 0.35f
            characterController.height = 1.2f;  // 1.2f
        }
        if (this.collider == null) {
            this.collider = characterController;
        }
        if (this.head == null) {
            this.head = RecursiveFindChild(this.gameObject.transform, "neck03");
        }
        if (this.head != null) {
            Transform jaw = RecursiveFindChild(this.head, "jaw");
            if (jaw != null) {
                MouthMovement mouthMovement = jaw.GetComponent<MouthMovement>();
                if (mouthMovement == null) {
                    mouthMovement = jaw.gameObject.AddComponent<MouthMovement>();
                }
                if (mouthMovement.head == null) {
                    mouthMovement.head = this.head;
                }
            }
            Transform leftEye = RecursiveFindChild(this.head, "eye.L");
            if (leftEye != null) {
                EyeRotation eyeRotation = leftEye.GetComponent<EyeRotation>();
                if (eyeRotation == null) {
                    eyeRotation = leftEye.gameObject.AddComponent<EyeRotation>();
                }
                if (eyeRotation.otherEye == null) {
                    eyeRotation.otherEye = RecursiveFindChild(this.head, "eye.R");
                }
                if (eyeRotation.entity == null) {
                    eyeRotation.entity = this;
                }
                if (eyeRotation.upperEyelid1 == null) {
                    eyeRotation.upperEyelid1 = RecursiveFindChild(this.head, "orbicularis03.L");
                }
                if (eyeRotation.upperEyelid2 == null) {
                    eyeRotation.upperEyelid2 = RecursiveFindChild(this.head, "orbicularis03.R");
                }
                if (eyeRotation.lowerEyelid1 == null) {
                    eyeRotation.lowerEyelid1 = RecursiveFindChild(this.head, "orbicularis04.L");
                }
                if (eyeRotation.lowerEyelid2 == null) {
                    eyeRotation.lowerEyelid2 = RecursiveFindChild(this.head, "orbicularis04.R");
                }
            }
        }
        /*if (this.team == "A" && this.hDev == null && this.rightArm != null) { // Lo commento perché non linka il prefab :c
            Transform hDevTransform = RecursiveFindChild(this.rightFArm, "HDev");
            if (hDevTransform == null) {
                hDevTransform = ((GameObject)GameObject.Instantiate(Resources.Load("HDev", typeof(GameObject)))).transform;
                hDevTransform.parent = this.rightFArm;
                hDevTransform.localPosition = new Vector3(-0.0238f, 0.112f, 0.012f);
                hDevTransform.localEulerAngles = new Vector3(0.0f, 0.0f, 89.32f);
                hDevTransform.localScale = new Vector3(0.02f, 0.02f, 0.02f);
                hDevTransform.gameObject.layer = 17;
                RecursiveSetLayer(hDevTransform, hDevTransform.gameObject.layer);
                hDevTransform.gameObject.name = "HDev";
            }
            HDevController hDevController = hDevTransform.GetComponent<HDevController>();
            if (hDevController.user == null) {
                hDevController.user = this;
            }
            this.hDev = hDevController;
        }*/
        if (controller is ThirdPersonController) {
            this.gameObject.layer = 9;
        }
        else {
            this.gameObject.layer = 10;
        }
    }

    public static Transform RecursiveFindChild(Transform aParent, string aName) {
        Queue<Transform> queue = new Queue<Transform>();
        queue.Enqueue(aParent);
        while (queue.Count > 0) {
            var c = queue.Dequeue();
            if (c.name == aName)
                return c;
            foreach (Transform t in c)
                queue.Enqueue(t);
        }
        return null;
    }

    public static Transform RecursiveSetLayer(Transform aParent, int layer) {
        Queue<Transform> queue = new Queue<Transform>();
        queue.Enqueue(aParent);
        while (queue.Count > 0) {
            var c = queue.Dequeue();
            c.gameObject.layer = layer;
            foreach (Transform t in c)
                queue.Enqueue(t);
        }
        return null;
    }
    
    public void ChangeOutfit(int index) {
        GameObject outfit = (GameObject)Resources.Load("Outfits/" + charName + "$" + index, typeof(GameObject));
        if (index == 0 && currentOutfitIndex != 0) {
            foreach (Transform child in transform) {
                if (child.childCount <= 0) {
                    if (child.gameObject.CompareTag("Old clothes")) {
                        child.gameObject.SetActive(true);
                    }
                    else {
                        GameObject.Destroy(child.gameObject);
                    }
                }
            }
            if (animator != null) {
                animator.avatar = originalAvatar;
                animator.Rebind();
            }
            parameters.outfit = index;
            currentOutfitIndex = index;
        }
        else if (outfit != null) {
            Entity newOutfitEntity = outfit.GetComponent<Entity>();
            if (newOutfitEntity != null) {
                // Destroy every object inside
                foreach (Transform child in transform) {
                    if (child.childCount <= 0) {
                        if (currentOutfitIndex == 0) {
                            child.gameObject.tag = "Old clothes";
                            child.gameObject.SetActive(false);
                        }
                        else if (!child.gameObject.CompareTag("Old clothes")){
                            GameObject.Destroy(child.gameObject);
                        }
                    }
                }
                // Puts new objects
                foreach (Transform child in outfit.transform) {
                    if (child.childCount <= 0) {
                        GameObject newObj = GameObject.Instantiate(child.gameObject, transform);
                        newObj.transform.localPosition = child.localPosition;
                        newObj.transform.localRotation = child.localRotation;
                        newObj.transform.localScale = child.localScale;
                        SkinnedMeshRenderer smr = newObj.GetComponent<SkinnedMeshRenderer>();
                        if (smr != null) {
                            Transform[] newBones = new Transform[smr.bones.Length];
                            for (int i = 0; i < smr.bones.Length; i++) {
                                string originalBoneName = smr.bones[i].name;
                                newBones[i] = RecursiveFindChild(transform, originalBoneName);
                                if (newBones[i] == null) {
                                    Debug.LogError("Bone ''" + originalBoneName + "'' non found!");
                                }
                            }
                            smr.bones = newBones;
                            smr.rootBone = RecursiveFindChild(transform, smr.rootBone.name);
                        }
                    }
                }
                if (animator != null) {
                    Animator targetAnimator = newOutfitEntity.GetComponent<Animator>();
                    if (targetAnimator != null) {
                        animator.avatar = targetAnimator.avatar;
                        animator.Rebind();
                    }
                }
                parameters.outfit = index;
                currentOutfitIndex = index;
            }
        }
    }

    public static float NeededExpToLevel(int level) {
        float res;
        res = Mathf.RoundToInt(50.0f * Mathf.Pow(level, 1.7f));
        if (res == 0) {
            res = 1;
        }
        return res;
    }

    public void SetAnimator (RuntimeAnimatorController anc) {
        animator.runtimeAnimatorController = anc;
    }

    public void SetBattleAnimator() {
        if (battleRealtimeAnimator != null && battleRealtimeAnimator != animator.runtimeAnimatorController) {
            animator.SetFloat("Speed", 0.0f);
            animator.runtimeAnimatorController = battleRealtimeAnimator;
        }
    }

    public void RevertAnimator() {
        if (originalRealtimeAnimator != null && originalRealtimeAnimator != animator.runtimeAnimatorController) {
            StartCoroutine(RevertAnimatorCoroutine());
        }
    }

    IEnumerator RevertAnimatorCoroutine() {
        yield return new WaitForSeconds(3.0f);
        animator.runtimeAnimatorController = originalRealtimeAnimator;
    }

    public bool IsUsingHDev() {
        return usingHDev;
    }

    void CheckMax() {
        if (parameters.currentHP < 0) {
            parameters.currentHP = 0;
        }
        else if (parameters.currentHP > parameters.maxHP * parameters.maxHPBuff) {
            parameters.currentHP = Mathf.RoundToInt(parameters.maxHP * parameters.maxHPBuff);
        }
        if (parameters.currentStamina < 0) {
            parameters.currentStamina = 0;
        }
        else if (parameters.currentStamina > parameters.maxStamina * parameters.maxStaminaBuff) {
            parameters.currentStamina = Mathf.RoundToInt(parameters.maxStamina * parameters.maxStaminaBuff);
        }
        if (parameters.currentMP < 0) {
            parameters.currentMP = 0;
        }
        else if (parameters.currentMP > parameters.maxMP * parameters.maxMPBuff) {
            parameters.currentMP = Mathf.RoundToInt(parameters.maxMP * parameters.maxMPBuff);
        }
    }

    void takeHit(float enemyStrength, int isMagic, string source) {

    }

    void visualTakeHit(float enemyStrength, int isMagic, string source) {

    }

    void jump() {

    }

    public void GetAngry(Entity e, float probability) {
        if (e == null || this.aimingEntity==e || Random.Range(0.0f, 1.0f) >= probability) {
            this.aimingEntity = e;
        }
    }

    public void GetHitAnimation(Entity e) {
        if (animator != null && !this.protecting) {
            if (this.aimingEntity == e) {
                Vector3 dif = e.transform.position - transform.position;
                dif = new Vector3(dif.x, 0.0f, dif.z);
                transform.rotation = Quaternion.LookRotation(dif);
            }
            animator.SetTrigger("GetHit");
        }
    }

    public void Aim(Entity e) {
        aimingEntity = e;
    }

    public void ClearAim() {
        aimingEntity = null;
    }

    public bool isAlive() {
        return (parameters.currentHP > 0);
    }

    public void PlayFootstep() {

    }
    
    public bool IsAiming() {
        return aimingEntity != null;
    }

    public Entity GetAimingEntity() {
        return aimingEntity;
    }

    public bool LevelUp(int toLevel = -1) {
        if (toLevel < 0) {
            toLevel = this.parameters.level + 1;
        }
        else if (toLevel <= this.parameters.level) {
            return false;
        }
        int targetLevel = 100;
        float factor;
        if (toLevel < targetLevel) {
            factor = ((toLevel - this.parameters.level) * 1.0f) / (targetLevel - toLevel);
        }
        else if (toLevel == targetLevel) {
            factor = 1.0f;
        }
        else {
            return false;
        }
        Random.InitState(this.charName.GetHashCode() + 100 + toLevel);
        int rand1 = Random.Range(0, 3);
        int rand2 = Random.Range(0, 2);
        int rand3 = Random.Range(-1, 2);
        int rand4 = Random.Range(-1, 2);
        if (rand1 == 0 || rand2 == 0 || toLevel == targetLevel) {
            int targetMaxHP = Mathf.RoundToInt(Mathf.Lerp(parameters.maxHP, maxedParameters.maxHP, factor));
            if (parameters.maxHP < targetMaxHP) {
                parameters.maxHP = targetMaxHP;
            }
        }
        if (rand3 == 0 || toLevel == targetLevel) {
            int targetMaxStamina = Mathf.RoundToInt(Mathf.Lerp(parameters.maxStamina, maxedParameters.maxStamina, factor));
            if (parameters.maxStamina < targetMaxStamina) {
                parameters.maxStamina = targetMaxStamina;
            }
        }
        if (rand2 == 0 || rand3 == -1 || rand4 == -1 || toLevel == targetLevel) {
            int targetForce = Mathf.RoundToInt(Mathf.Lerp(parameters.force, maxedParameters.force, factor));
            if (parameters.force < targetForce) {
                parameters.force = targetForce;
            }
        }
        if (rand1 == 1 || toLevel == targetLevel) {
            int targetDefence = Mathf.RoundToInt(Mathf.Lerp(parameters.defence, maxedParameters.defence, factor));
            if (parameters.defence < targetDefence) {
                parameters.defence = targetDefence;
            }
        }
        if (rand2 == 1 || toLevel == targetLevel) {
            int targetMagic = Mathf.RoundToInt(Mathf.Lerp(parameters.magic, maxedParameters.magic, factor));
            if (parameters.magic < targetMagic) {
                parameters.magic = targetMagic;
            }
        }
        if (rand1 == 2 || toLevel == targetLevel) {
            int targetMagicDefence = Mathf.RoundToInt(Mathf.Lerp(parameters.magicDefence, maxedParameters.magicDefence, factor));
            if (parameters.magicDefence < targetMagicDefence) {
                parameters.magicDefence = targetMagicDefence;
            }
        }
        if (rand4 == 0 || toLevel == targetLevel) {
            int targetAim = Mathf.RoundToInt(Mathf.Lerp(parameters.aim, maxedParameters.aim, factor));
            if (parameters.aim < targetAim) {
                parameters.aim = targetAim;
            }
        }
        if (rand3 == 1 || toLevel == targetLevel) {
            int targetAgility = Mathf.RoundToInt(Mathf.Lerp(parameters.agility, maxedParameters.agility, factor));
            if (parameters.agility < targetAgility) {
                parameters.agility = targetAgility;
            }
        }
        if (rand4 == 1 || toLevel == targetLevel) {
            int targetEvasion = Mathf.RoundToInt(Mathf.Lerp(parameters.evasion, maxedParameters.evasion, factor));
            if (parameters.evasion < targetEvasion) {
                parameters.evasion = targetEvasion;
            }
        }
        parameters.level = toLevel;
        return true;
    }

    public void PlaySound(AudioClip audioClip) {
        ScriptedScene.PlayClipAtPoint(audioClip, transform.position);
    }

}