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; 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(); scanCooldown = Random.Range(0.5f, 1.5f); } private void Start(){ rb2 = GetComponent(); //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); // 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); if (health <= 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(); 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() { if (health <= 0 || magicalHealth <= 0){ return; } healthBar.SetHealth(health, maxHealth); MagicalhealthBar.SetHealth(magicalHealth, maxHealth); //magical health maxout err 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); } } void takedmg(int damage,uint id){ if(health<=0){return;} health -= damage; //hit vfx // GameObject newObject = Instantiate(hitVfx , transform.position , Quaternion.identity ); // newObject.transform.localPosition = Vector3.zero; // newObject.transform.parent = transform; if(health<= 0 ){ StartCoroutine(couroutineDeath()); foreach(playerNetwork player in FindObjectsOfType()){ if(player.netId == id){ //This one attacked me player.OnEnemyKilled(level); } } } Debug.Log("Enemy Takes Damage ***"); } [Command(requiresAuthority =false)] void CmdTakeMagicalDamage(int damage,uint id){ takeMagicalDmg(damage,id); Debug.Log("Enemy Attack Recieved "); } public void TakeMagicalDamage(int damage, uint id){ if(isServer){ takeMagicalDmg(damage,id); } else{ CmdTakeMagicalDamage(damage,id); } } void takeMagicalDmg(int damage,uint id){ if(magicalHealth<=0){return;} magicalHealth -= damage; if(magicalHealth<= 0 ){ StartCoroutine(couroutineDeath()); foreach(playerNetwork player in FindObjectsOfType()){ if(player.netId == id){ //This one attacked me player.OnEnemyKilled(level); } } } Debug.Log("Enemy Takes Damage ***"); } IEnumerator couroutineDeath(){ animationString = "Death"; 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(5); if (!isServer) { CmdDie(); } else { GameManager.OnEnemyDeath(this, defaultPos); } /* transform.position = defaultPos; health = (int)maxHealth; magicalHealth = (int)maxHealth;*/ //animationString = "Idle"; } [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); } }