using System; using System.Collections; using System.Collections.Generic; using Assets.HeroEditor.Common.Scripts.CharacterScripts; using Unity.VisualScripting; using UnityEngine; using UnityEngine.SceneManagement; namespace Assets.Scripts.CharacterScripts { public class PlayerController : MonoBehaviour { public float moveSpeed = 10f; // How fast the player moves left and right private float jumpForce = 25f; // How fast/far the player jumps in the air private Character Character; // The Character component for the player (has all the animations) private Rigidbody2D rb; // The RigidBody for the player public HealthBarController healthBar; // The player's health public ExpBarController expBar; // The player's exp public int currentHealth; // Current health public int maxHealth = 100; // Max health (initialized to 100 at level 1) public int currentExp; // Current exp public int maxExp = 50; // Max exp (initialized to 50 at level 1) public int playerLevel = 1; // The player level, initialized to 1 public int levelsGained; // The total amount of levels gained at one time public bool levelUp = false; // A boolean to see if the player leveled up public LevelUpController levelScreen; // Reference to the level up screen public int playerDamage = 5; // The player's damage, initialized to 5 public bool isAttacking = false; // A boolean to see if the player is actively attacking public float attackCooldown = 1f; // The cooldown between resetting the isAttacking variable public float playerDefense = 1f; // The player's defensive stat private Coroutine damageCoroutine; // For taking damage consistently from the enemy private bool tookFirstDmg = false; // Boolean to see if we took a single hit from the enemy yet or not private bool inContact = false; // Boolean to see if we're in contact with the enemy public bool enteredBossArena = false; // Boolean to see if we entered the boss arena public AudioSource potion; // Potion sound effect public AudioSource largePotion; // Large potion sound effect public AudioSource swordSound; // Sword slashing sound effect // Start is called before the first frame update void Start(){ // Get the Character component for the player and set the animation state to ready Character = GetComponent(); Character.SetState(CharacterState.Ready); // Get the RigidBody2D component for the player rb = GetComponent(); // This locks the Z-axis, prevents the player sprite from falling over unintentionally due to physics rb.constraints = RigidbodyConstraints2D.FreezeRotation; // Initialize the player's health to max currentHealth = maxHealth; healthBar.SetMaxHealth(maxHealth); // Initialize the player's experience bar expBar.ResetExp(maxExp); } // Update is called once per frame void Update(){ if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.C)){ SceneManager.LoadScene("YouWin"); } // If the player dies, load the game over scene if (currentHealth <= 0){ SceneManager.LoadScene("GameOver"); } // Get the horizontal input (A and D keys, or left and right arrow keys) float horizontalInput = Input.GetAxis("Horizontal"); float verticalInput = Input.GetAxis("Vertical"); // Calculate the movement amount based on input and move speed float moveAmount = horizontalInput * moveSpeed * Time.deltaTime; float verticalMoveAmount = verticalInput * moveSpeed * Time.deltaTime; // Move the character to the left and right transform.Translate(moveAmount, 0f, 0f); // Change the player's animation to running when in motion if (Math.Abs(horizontalInput) > 0.01f){ // Set the character's animation state to "Run" Character.SetState(CharacterState.Run); // When the player turns around, flip the sprite to face the other way if (horizontalInput < 0f){ transform.localScale = new Vector3(-1f, 1f, 1f); } else { transform.localScale = new Vector3(1f, 1f, 1f); } } else { // When not moving, go back to the ready state Character.SetState(CharacterState.Ready); } // Press W to jump if (Input.GetKeyDown(KeyCode.W)){ Jump(); } // Press Space to attack if (Input.GetKeyDown(KeyCode.Space)){ Attack(); } } // Make the player jump void Jump(){ Character.SetState(CharacterState.Jump); // Check if the character is grounded before jumping (to prevent double jumping) // For simplicity, we're assuming the character is grounded if its Rigidbody is not falling if (Mathf.Abs(rb.velocity.y) < 0.01f) { // Apply a vertical force to the Rigidbody to make the character Jump rb.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse); } } // Make the player attack void Attack(){ // Play the swoed sound effect swordSound.PlayDelayed(.2f); // Switch to true isAttacking = true; // Play the attack animation Character.Slash(); // Start the coroutine for the attack cooldown StartCoroutine(ResetAttackCooldown()); } IEnumerator ResetAttackCooldown(){ // Wait a second before turning isAttacking back to false // This is to make it so you only deal damage mid attack, and cannot just walk into the enemy // to deal damage yield return new WaitForSeconds(attackCooldown); isAttacking = false; } // The player takes damage void TakeDamage(int damage){ // Apply the damage to our current health int damageAfterDefense = Mathf.CeilToInt(damage / playerDefense); currentHealth -= damageAfterDefense; // And set the health bar accordingly healthBar.SetHealth(currentHealth); } // The player gains experience public void GainExp(int exp){ // Apply the experience currentExp += exp; // If our current exp meets or exceeds the max, level up if (currentExp >= maxExp){ PlayerLevelUp(); } else { // Otherwise set the experience bar expBar.SetExp(currentExp); } } // Function to level up public void PlayerLevelUp(){ // Start the levels gained as 0 levelsGained = 0; // For as long as our current exp is more than the max while (currentExp >= maxExp){ // Increment the player level and levels gained playerLevel++; levelsGained++; // Subtract the current exp from the max and calculate the new max exp currentExp -= maxExp; maxExp += (playerLevel - 1) * 10; } // Set the level up screen to true levelScreen.levelUpScreen = true; // Set level up to true for each level gained for (int i = 0; i < levelsGained; i++){ levelUp = true; } // Reset the experience bar expBar.ResetExp(maxExp); } private void OnCollisionEnter2D(Collision2D other) { // We're dealing with the player's body collision, so if it hits an enemy, take damage if (other.gameObject.CompareTag("Enemy")){ EnemyController enemy = other.gameObject.GetComponent(); // This is to ensure we don't take double damage on first contact every time because of the coroutine tookFirstDmg = true; TakeDamage(enemy.damage); } else if (other.gameObject.CompareTag("EnemyWeapon")){ // If we make contact with the enemy weapon, take damage EnemyWeaponController enemyWeapon = other.gameObject.GetComponent(); TakeDamage(enemyWeapon.damage); } else if (other.gameObject.CompareTag("PlayerWeapon")){ // This is to prevent the weapon collision from pushing the player; ignore this collision Physics2D.IgnoreCollision(GetComponent(), other.collider, true); } else if (other.gameObject.CompareTag("BossArenaEntrance")){ // The trigger for the boss arena (mainly the boss health bar and the boss music) enteredBossArena = true; } else if (other.gameObject.CompareTag("Healing")){ // Play the potion sound effect if we get a potion potion.Play(); } else if (other.gameObject.CompareTag("LargeHealing")){ // Same but with large potions largePotion.Play(); } } // For staying in collision with the enemy private void OnCollisionStay2D(Collision2D other) { if (other.gameObject.CompareTag("Enemy")){ inContact = true; EnemyController enemy = other.gameObject.GetComponent(); if (damageCoroutine == null){ // If we are in continuous collision, assign the damage over time coroutine // We use a variable to make sure the coroutine doesn't start or stop when not intended damageCoroutine = StartCoroutine(DealDamageOverTime(enemy.damage)); } } } // For exiting the collision with the enemy private void OnCollisionExit2D(Collision2D other) { inContact = false; StopCoroutine(damageCoroutine); if (damageCoroutine != null){ damageCoroutine = null; } } // Coroutine to take damage every second while in consistent collision with an enemy IEnumerator DealDamageOverTime(int enemyDamage){ if (!tookFirstDmg){ TakeDamage(enemyDamage); } tookFirstDmg = false; yield return new WaitForSeconds(1f); } } }