123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590 |
- using System.Collections;
- using UnityEngine;
- using Spine.Unity;
- using Spine.Unity.Examples;
- using Mirror;
- public class enemyScript : NetworkBehaviour
- {
- public const int HEALTH_INC = 2;
- public const float DAMAGE_INC = 1.2f;
- public const float XP_GAIN = 1.5f;
- public const int XP_GAIN_Base = 5;
- [SyncVar(hook = nameof(OnHealthChange))]
- public int health;
- [SyncVar(hook = nameof(OnMagicalHealthChange))]
- public int magicalHealth;
- // NEW: Shield break boolean
- [SyncVar]
- public bool shieldBreak = false;
- public SpriteHealthBar healthBar;
- public SpriteHealthBar MagicalhealthBar;
- public float speed;
- public float chaseRadius;
- public float attackRadius;
- public bool rotate;
- //public LayerMask layerMask;
- public playerNetwork target;
- private Rigidbody2D rb2;
- public SkeletonAnimation animator;
- private Vector2 movement;
- public Vector3 dir;
- public TextMesh enemyName;
- public TextMesh enemyLevel;
- public bool isInChaseRange;
- public bool isInAttackRange;
- public Transform uiEnemy;
- public int enemyAttackDamage = 10;
- MeshRenderer meshRenderer;
- public GameObject hitVfx;
- void Awake()
- {
- meshRenderer = GetComponent<MeshRenderer>();
- scanCooldown = Random.Range(0.5f, 1.5f);
- }
- private void Start()
- {
- rb2 = GetComponent<Rigidbody2D>();
- //target = GameObject.FindWithTag("Player").transform;
- UpdateAnimation(directionString, animationString);
- defaultPos = transform.position;
- }
- [SyncVar(hook = nameof(OnLevelChanged))]
- public int level;
- void OnLevelChanged(int oldVal, int newVal)
- {
- if (isServer) { return; }
- SetLevel(newVal);
- }
- public void SetLevel(int _level)
- {
- if (enemyLevel != null)
- {
- enemyLevel.text = _level.ToString();
- }
- level = _level;
- int healthIncrement = level * HEALTH_INC;
- maxHealth = 100 + healthIncrement;
- health = (int)maxHealth;
- magicalHealth = (int)maxHealth;
- enemyAttackDamage += (int)(level * DAMAGE_INC);
- // MODIFIED: Reset shield break when level is set
- shieldBreak = false;
- // Debug.Log($"{health}/{maxHealth}");
- }
- public Vector3 defScale;
- Vector3 defaultPos;
- float playerDistCheckTimer = 0f;
- void LateUpdate()
- {
- LOD();
- }
- public const float disappearDistFromPlayer = 15f;
- void LOD()
- {
- if (playerDistCheckTimer > 0) { playerDistCheckTimer -= Time.deltaTime; return; }
- playerDistCheckTimer = Random.Range(1.5f, 2.5f);
- if (playerNetwork.localPlayerTransform == null) { return; }
- float distToPlayer = Vector3.Distance(playerNetwork.localPlayerTransform.position, transform.position);
- meshRenderer.enabled = distToPlayer < disappearDistFromPlayer;
- }
- #if UNITY_SERVER || UNITY_EDITOR
- [Server]
- private void Update()
- {
- // animator.skeleton.SetSkin
- // set animation state to running if in chase Range
- //isInChaseRange = true
- // isInChaseRange = Physics2D.OverlapCircle(transform.position, chaseRadius , layerMask);
- // isInAttackRange = Physics2D.OverlapCircle(transform.position, attackRadius, layerMask);
- // MODIFIED: Check both health and magicalHealth for death condition
- if (health <= 0 || (shieldBreak && magicalHealth <= 0))
- {
- return;
- }
- if (target != null)
- {
- isInChaseRange = Vector3.Distance(transform.position, target.transform.position) < chaseRadius;
- isInAttackRange = Vector3.Distance(transform.position, target.transform.position) < attackRadius;
- }
- else
- {
- isInChaseRange = false;
- isInAttackRange = false;
- }
- ScanPlayers();
- if (target != null)
- {
- enemyFollow();
- }
- }
- #endif
- float scanTimer = 0;
- float scanCooldown;
- public void ScanPlayers()
- {
- if (scanTimer > 0) { scanTimer -= Time.deltaTime; return; }
- scanTimer = scanCooldown;
- playerNetwork[] playersinNetwork = FindObjectsOfType<playerNetwork>();
- float closestDist = float.MaxValue;
- playerNetwork closestPlayer = null;
- foreach (playerNetwork player in playersinNetwork)
- {
- if (player.health <= 0) { continue; }
- float dist = Vector3.Distance(transform.position, player.transform.position);
- if (dist < closestDist)
- {
- closestPlayer = player;
- closestDist = dist;
- }
- }
- if (closestDist < chaseRadius)
- {
- target = closestPlayer;
- }
- else
- {
- target = null;
- }
- //if(target == null) {return;}
- }
- // [ClientRpc]
- // void RpcUpdateAnim(string animDir , string animName, bool isLoop){
- // UpdateAnimation(animDir , animName, isLoop);
- // }
- [SyncVar(hook = nameof(OnFlipped))]
- bool isFlipped = false;
- void OnFlipped(bool oldVal, bool newVal)
- {
- if (isServer) { return; }
- transform.localScale = new Vector3(defScale.x * (newVal ? -1 : 1), defScale.y, defScale.z);
- HandleFlip();
- }
- void HandleFlip()
- {
- if (uiEnemy == null)
- {
- return;
- }
- if (transform.localScale.x < 0)
- {
- uiEnemy.localScale = new Vector3(-1, 1, 1);
- }
- else
- {
- uiEnemy.localScale = new Vector3(1, 1, 1);
- }
- }
- private void enemyFollow()
- {
- if (Mathf.Abs(dir.y) > Mathf.Abs(dir.x))
- {
- if (dir.y < 0)
- {
- directionString = "Back";
- }
- else
- {
- directionString = "Front";
- }
- }
- else
- {
- directionString = "Side";
- if (dir.x < 0)
- {
- transform.localScale = new Vector3(defScale.x, defScale.y, 0);
- isFlipped = false;
- }
- else
- {
- transform.localScale = new Vector3(-defScale.x, defScale.y, 0);
- isFlipped = true;
- }
- HandleFlip();
- }
- if (animationHistory != directionString + animationString)
- {
- UpdateAnimation(directionString, animationString);
- // RpcUpdateAnim(directionString, animationString,true);
- }
- animationHistory = directionString + animationString;
- if (target != null)
- {
- dir = transform.position - target.transform.position;
- }
- float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
- dir.Normalize();
- movement = dir;
- if (rotate)
- {
- //set anim direction x, y dir
- }
- }
- string animationHistory = "";
- [SyncVar(hook = nameof(OnAnimationDirectionChanged))]
- public string directionString = "Side";
- [SyncVar(hook = nameof(OnAnimationNameChanged))]
- public string animationString = "Idle";
- void OnAnimationDirectionChanged(string oldVal, string newVal)
- {
- UpdateAnimation(newVal, animationString);
- }
- void OnAnimationNameChanged(string oldVal, string newVal)
- {
- UpdateAnimation(directionString, newVal);
- }
- float attackTimer = 0f;
- float attackDuration = 1.4f;
- [SyncVar]
- public float maxHealth;
- #if UNITY_SERVER || UNITY_EDITOR
- [Server]
- private void FixedUpdate()
- {
- // MODIFIED: Updated death condition
- if (health <= 0 || (shieldBreak && magicalHealth <= 0))
- {
- return;
- }
- healthBar.SetHealth(health, maxHealth);
- MagicalhealthBar.SetHealth(magicalHealth, maxHealth);
- if (isInChaseRange && !isInAttackRange)
- {
- MoveEnemy(movement);
- //Set animation to moving
- animationString = "Walk";
- }
- if (isInAttackRange)
- {
- rb2.velocity = Vector2.zero;
- //Set animation to attack
- animationString = "Attack";
- if (attackTimer < attackDuration)
- {
- attackTimer += Time.deltaTime;
- }
- else
- {
- attackTimer = 0;
- Attack();
- }
- //TODO: ATTACK HERE
- }
- if (!isInAttackRange && !isInChaseRange)
- {
- //SetAnimation to idle
- animationString = "Idle";
- }
- }
- #endif
- public void Attack()
- {
- target.TakeDamage(enemyAttackDamage);
- }
- private void MoveEnemy(Vector2 dir)
- {
- rb2.MovePosition((Vector2)transform.position + (dir * speed * Time.deltaTime));
- }
- void UpdateAnimation(string direction, string animationName)
- {
- // try{
- StartCoroutine(CoroutineUpdateAnim(direction, animationName));
- }
- IEnumerator CoroutineUpdateAnim(string direction, string animationName)
- {
- while (animator == null)
- {
- yield return new WaitForSeconds(0.1f);
- Debug.LogError("animator is null!");
- }
- while (animator.skeleton == null)
- {
- yield return new WaitForSeconds(0.1f);
- Debug.LogError("animator skelton is null!");
- }
- while (animator.AnimationState == null)
- {
- yield return new WaitForSeconds(0.1f);
- Debug.LogError("animator state is null!");
- }
- animator.skeleton.SetSkin(direction);
- animator.skeleton.SetSlotsToSetupPose();
- animator.AnimationState.SetAnimation(0, $"{direction}_{animationName}", !animationName.ToLower().Contains("death"));
- // }catch(Exception e){
- // Debug.LogError(e.ToString());
- // }
- Debug.Log($"Updating enemy animation {direction}_{animationName}");
- }
- [Command(requiresAuthority = false)]
- void CmdTakeDamage(int damage, uint id)
- {
- takedmg(damage, id);
- Debug.Log("Enemy Attack Recieved ");
- }
- public void TakeDamage(int damage, uint id)
- {
- if (isServer)
- {
- takedmg(damage, id);
- }
- else
- {
- CmdTakeDamage(damage, id);
- }
- }
- // MODIFIED: Completely rewritten damage system
- void takedmg(int damage, uint id)
- {
- if (health <= 0) { return; }
- int finalDamage = damage;
- // If shield is not broken, reduce damage by half and damage magical health
- if (!shieldBreak && magicalHealth > 0)
- {
- finalDamage = damage / 2;
- // Calculate magical health damage based on player attack damage + enemy level
- int magicalDamage = damage + level;
- magicalHealth -= magicalDamage;
- // Check if shield breaks
- if (magicalHealth <= 0)
- {
- shieldBreak = true;
- magicalHealth = 0;
- Debug.Log("Shield Broken!");
- }
- }
- // Apply damage to health
- health -= finalDamage;
- // Check for death
- if (health <= 0)
- {
- StartCoroutine(couroutineDeath());
- foreach (playerNetwork player in FindObjectsOfType<playerNetwork>())
- {
- if (player.netId == id)
- {
- player.OnEnemyKilled(level);
- }
- }
- }
- Debug.Log($"Enemy Takes Damage: {finalDamage} | Shield Broken: {shieldBreak} | Health: {health} | Magical Health: {magicalHealth}");
- }
- [Command(requiresAuthority = false)]
- void CmdTakeMagicalDamage(int damage, uint id)
- {
- takeMagicalDmg(damage, id);
- Debug.Log("Enemy Magical Attack Recieved ");
- }
- public void TakeMagicalDamage(int damage, uint id)
- {
- if (isServer)
- {
- takeMagicalDmg(damage, id);
- }
- else
- {
- CmdTakeMagicalDamage(damage, id);
- }
- }
- // MODIFIED: Updated magical damage to use same system
- void takeMagicalDmg(int damage, uint id)
- {
- if (magicalHealth <= 0 && shieldBreak) { return; }
- int finalDamage = damage;
- // If shield is not broken, reduce damage by half and damage magical health
- if (!shieldBreak && magicalHealth > 0)
- {
- finalDamage = damage / 2;
- // Calculate magical health damage based on player attack damage + enemy level
- int magicalDamage = damage + level;
- magicalHealth -= magicalDamage;
- // Check if shield breaks
- if (magicalHealth <= 0)
- {
- shieldBreak = true;
- magicalHealth = 0;
- Debug.Log("Shield Broken!");
- }
- }
- // Apply damage to health
- health -= finalDamage;
- // Check for death
- if (health <= 0)
- {
- StartCoroutine(couroutineDeath());
- foreach (playerNetwork player in FindObjectsOfType<playerNetwork>())
- {
- if (player.netId == id)
- {
- player.OnEnemyKilled(level);
- }
- }
- }
- Debug.Log($"Enemy Takes Magical Damage: {finalDamage} | Shield Broken: {shieldBreak} | Health: {health} | Magical Health: {magicalHealth}");
- }
- IEnumerator couroutineDeath()
- {
- animationString = "Death";
- StartCoroutine(PopDisappearUI());
- UpdateAnimation(directionString, animationString);
- // RpcUpdateAnim(directionString, animationString,false);
- Vector3 lootSpawnPos = transform.position;
- lootSpawnPos.z = GameManager.instance.LootSpawnPointsParent.GetChild(0).position.z;
- //instantiate loot item
- GameObject newLoot = Instantiate(GameManager.instance.GetRandomLoot(), lootSpawnPos, Quaternion.identity);
- NetworkServer.Spawn(newLoot);
- yield return new WaitForSecondsRealtime(7);// dead corpse delay
- if (!isServer)
- {
- CmdDie();
- }
- else
- {
- GameManager.OnEnemyDeath(this, defaultPos);
- }
- }
- [Command]
- void CmdDie()
- {
- GameManager.OnEnemyDeath(this, defaultPos);
- }
- public void OnHealthChange(int oldVlaue, int newValue)
- {
- healthBar.SetHealth(newValue, maxHealth);
- }
- public void OnMagicalHealthChange(int oldVlaue, int newValue)
- {
- MagicalhealthBar.SetHealth(newValue, maxHealth);
- }
- //etc for ui Disspear coroutine
- IEnumerator PopDisappearUI()
- {
- Vector3 originalScale = uiEnemy.localScale;
- // First, scale up slightly
- float popDuration = 0.15f;
- float elapsedTime = 0f;
- Vector3 popScale = originalScale * 1.2f;
- while (elapsedTime < popDuration)
- {
- float t = elapsedTime / popDuration;
- uiEnemy.localScale = Vector3.Lerp(originalScale, popScale, t);
- elapsedTime += Time.deltaTime;
- yield return null;
- }
- // Then scale down to zero quickly
- float shrinkDuration = 0.3f;
- elapsedTime = 0f;
- while (elapsedTime < shrinkDuration)
- {
- float t = elapsedTime / shrinkDuration;
- // Use ease-in curve for faster shrinking
- float easedT = t * t;
- uiEnemy.localScale = Vector3.Lerp(popScale, Vector3.zero, easedT);
- elapsedTime += Time.deltaTime;
- yield return null;
- }
- uiEnemy.localScale = Vector3.zero;
- uiEnemy.gameObject.SetActive(false);
- }
- }
|