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; public SpriteHealthBar healthBar; 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; private void Start(){ rb2 = GetComponent(); //target = GameObject.FindWithTag("Player").transform; UpdateAnimation(directionString,animationString); defaultPos = transform.position; } public int level; public void SetLevel(int _level){ if(enemyLevel != null){ enemyLevel.text = _level.ToString(); } level = _level; int healthIncrement =level * HEALTH_INC; maxHealth = health + healthIncrement; health += healthIncrement; enemyAttackDamage += (int)(level * DAMAGE_INC); Debug.Log($"{health}/{maxHealth}"); } public Vector3 defScale; Vector3 defaultPos; [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; } 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;} enemyFollow(); } // [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 = "Back"; [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; public float maxHealth; [Server] private void FixedUpdate() { if (health <= 0 ){ return; } healthBar.SetHealth(health, 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"; } } 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; 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 ***"); } 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); transform.position = defaultPos; health = (int)maxHealth; //animationString = "Idle"; } public void OnHealthChange(int oldVlaue, int newValue){ healthBar.SetHealth(newValue); } }