enemyScript.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. using System.Collections;
  2. using UnityEngine;
  3. using Spine.Unity;
  4. using Spine.Unity.Examples;
  5. using Mirror;
  6. public class enemyScript : NetworkBehaviour
  7. {
  8. public const int HEALTH_INC = 2;
  9. public const float DAMAGE_INC = 1.2f;
  10. public const float XP_GAIN = 1.5f;
  11. public const int XP_GAIN_Base = 5;
  12. [SyncVar(hook = nameof(OnHealthChange))]
  13. public int health;
  14. [SyncVar(hook = nameof(OnMagicalHealthChange))]
  15. public int magicalHealth;
  16. public SpriteHealthBar healthBar;
  17. public SpriteHealthBar MagicalhealthBar;
  18. public float speed;
  19. public float chaseRadius;
  20. public float attackRadius;
  21. public bool rotate;
  22. //public LayerMask layerMask;
  23. public playerNetwork target;
  24. private Rigidbody2D rb2;
  25. public SkeletonAnimation animator;
  26. private Vector2 movement;
  27. public Vector3 dir;
  28. public TextMesh enemyName;
  29. public TextMesh enemyLevel;
  30. public bool isInChaseRange;
  31. public bool isInAttackRange;
  32. public Transform uiEnemy;
  33. public int enemyAttackDamage = 10;
  34. MeshRenderer meshRenderer;
  35. public GameObject hitVfx;
  36. void Awake(){
  37. meshRenderer = GetComponent<MeshRenderer>();
  38. scanCooldown = Random.Range(0.5f, 1.5f);
  39. }
  40. private void Start(){
  41. rb2 = GetComponent<Rigidbody2D>();
  42. //target = GameObject.FindWithTag("Player").transform;
  43. UpdateAnimation(directionString,animationString);
  44. defaultPos = transform.position;
  45. }
  46. [SyncVar(hook =nameof(OnLevelChanged))]
  47. public int level;
  48. void OnLevelChanged(int oldVal, int newVal){
  49. if(isServer){return;}
  50. SetLevel(newVal);
  51. }
  52. public void SetLevel(int _level){
  53. if(enemyLevel != null){
  54. enemyLevel.text = _level.ToString();
  55. }
  56. level = _level;
  57. int healthIncrement =level * HEALTH_INC;
  58. maxHealth = 100 + healthIncrement;
  59. health = (int)maxHealth;
  60. magicalHealth = (int)maxHealth;
  61. enemyAttackDamage += (int)(level * DAMAGE_INC);
  62. // Debug.Log($"{health}/{maxHealth}");
  63. }
  64. public Vector3 defScale;
  65. Vector3 defaultPos;
  66. float playerDistCheckTimer=0f;
  67. void LateUpdate(){
  68. LOD();
  69. }
  70. public const float disappearDistFromPlayer = 15f;
  71. void LOD(){
  72. if(playerDistCheckTimer > 0){playerDistCheckTimer -= Time.deltaTime;return;}
  73. playerDistCheckTimer = Random.Range(1.5f,2.5f);
  74. if(playerNetwork.localPlayerTransform == null){return;}
  75. float distToPlayer = Vector3.Distance(playerNetwork.localPlayerTransform.position, transform.position);
  76. meshRenderer.enabled = distToPlayer < disappearDistFromPlayer;
  77. }
  78. #if UNITY_SERVER || UNITY_EDITOR
  79. [Server]
  80. private void Update(){
  81. // animator.skeleton.SetSkin
  82. // set animation state to running if in chase Range
  83. //isInChaseRange = true
  84. // isInChaseRange = Physics2D.OverlapCircle(transform.position, chaseRadius , layerMask);
  85. // isInAttackRange = Physics2D.OverlapCircle(transform.position, attackRadius, layerMask);
  86. if (health <= 0 ){
  87. return;
  88. }
  89. if(target != null){
  90. isInChaseRange = Vector3.Distance(transform.position, target.transform.position) < chaseRadius;
  91. isInAttackRange = Vector3.Distance(transform.position, target.transform.position) < attackRadius;
  92. }else{
  93. isInChaseRange = false;
  94. isInAttackRange = false;
  95. }
  96. ScanPlayers();
  97. if(target !=null){
  98. enemyFollow();
  99. }
  100. }
  101. #endif
  102. float scanTimer =0;
  103. float scanCooldown;
  104. public void ScanPlayers(){
  105. if(scanTimer >0){scanTimer-=Time.deltaTime; return;}
  106. scanTimer = scanCooldown;
  107. playerNetwork[] playersinNetwork = FindObjectsOfType<playerNetwork>();
  108. float closestDist = float.MaxValue;
  109. playerNetwork closestPlayer = null;
  110. foreach(playerNetwork player in playersinNetwork ){
  111. if(player.health <= 0 ){continue;}
  112. float dist = Vector3.Distance(transform.position, player.transform.position);
  113. if(dist < closestDist){
  114. closestPlayer = player;
  115. closestDist = dist;
  116. }
  117. }
  118. if(closestDist < chaseRadius){
  119. target = closestPlayer ;
  120. }
  121. else {
  122. target = null;
  123. }
  124. //if(target == null) {return;}
  125. }
  126. // [ClientRpc]
  127. // void RpcUpdateAnim(string animDir , string animName, bool isLoop){
  128. // UpdateAnimation(animDir , animName, isLoop);
  129. // }
  130. [SyncVar(hook =nameof(OnFlipped))]
  131. bool isFlipped= false;
  132. void OnFlipped(bool oldVal, bool newVal){
  133. if(isServer){return;}
  134. transform.localScale = new Vector3(defScale.x * (newVal ? -1 : 1),defScale.y,defScale.z);
  135. HandleFlip();
  136. }
  137. void HandleFlip(){
  138. if(uiEnemy == null){
  139. return;
  140. }
  141. if(transform.localScale.x < 0 ){
  142. uiEnemy.localScale = new Vector3(-1,1,1);
  143. }
  144. else{
  145. uiEnemy.localScale = new Vector3(1,1,1);
  146. }
  147. }
  148. private void enemyFollow(){
  149. if(Mathf.Abs(dir.y) > Mathf.Abs(dir.x)){
  150. if(dir.y < 0){
  151. directionString = "Back";
  152. }else{
  153. directionString = "Front";
  154. }
  155. }else{
  156. directionString = "Side";
  157. if(dir.x < 0){
  158. transform.localScale = new Vector3(defScale.x,defScale.y,0);
  159. isFlipped=false;
  160. }else{
  161. transform.localScale = new Vector3(-defScale.x,defScale.y,0);
  162. isFlipped = true;
  163. }
  164. HandleFlip();
  165. }
  166. if(animationHistory != directionString + animationString){
  167. UpdateAnimation(directionString, animationString);
  168. // RpcUpdateAnim(directionString, animationString,true);
  169. }
  170. animationHistory=directionString + animationString;
  171. if(target != null){
  172. dir = transform.position - target.transform.position;
  173. }
  174. float angle = Mathf.Atan2(dir.y , dir.x ) * Mathf.Rad2Deg;
  175. dir.Normalize();
  176. movement = dir;
  177. if(rotate){
  178. //set anim direction x, y dir
  179. }
  180. }
  181. string animationHistory ="";
  182. [SyncVar(hook =nameof(OnAnimationDirectionChanged))]
  183. public string directionString = "Side";
  184. [SyncVar(hook =nameof(OnAnimationNameChanged))]
  185. public string animationString = "Idle";
  186. void OnAnimationDirectionChanged(string oldVal, string newVal){
  187. UpdateAnimation(newVal, animationString);
  188. }
  189. void OnAnimationNameChanged(string oldVal, string newVal){
  190. UpdateAnimation(directionString, newVal);
  191. }
  192. float attackTimer = 0f;
  193. float attackDuration = 1.4f;
  194. [SyncVar]
  195. public float maxHealth;
  196. #if UNITY_SERVER || UNITY_EDITOR
  197. [Server]
  198. private void FixedUpdate() {
  199. if (health <= 0 || magicalHealth <= 0){
  200. return;
  201. }
  202. healthBar.SetHealth(health, maxHealth);
  203. MagicalhealthBar.SetHealth(magicalHealth, maxHealth); //magical health maxout err
  204. if(isInChaseRange && !isInAttackRange ){
  205. MoveEnemy(movement);
  206. //Set animation to moving
  207. animationString = "Walk";
  208. }
  209. if(isInAttackRange){
  210. rb2.velocity = Vector2.zero;
  211. //Set animation to attack
  212. animationString = "Attack";
  213. if(attackTimer < attackDuration){
  214. attackTimer += Time.deltaTime;
  215. }else{
  216. attackTimer = 0 ;
  217. Attack();
  218. }
  219. //TODO: ATTACK HERE
  220. }
  221. if(!isInAttackRange && !isInChaseRange){
  222. //SetAnimation to idle
  223. animationString = "Idle";
  224. }
  225. }
  226. #endif
  227. public void Attack(){
  228. target.TakeDamage(enemyAttackDamage);
  229. }
  230. private void MoveEnemy(Vector2 dir){
  231. rb2.MovePosition((Vector2)transform.position + (dir * speed * Time.deltaTime));
  232. }
  233. void UpdateAnimation(string direction, string animationName){
  234. // try{
  235. StartCoroutine(CoroutineUpdateAnim(direction, animationName));
  236. }
  237. IEnumerator CoroutineUpdateAnim(string direction, string animationName){
  238. while(animator == null){
  239. yield return new WaitForSeconds(0.1f);
  240. Debug.LogError("animator is null!");
  241. }
  242. while(animator.skeleton == null){
  243. yield return new WaitForSeconds(0.1f);
  244. Debug.LogError("animator skelton is null!");
  245. }
  246. while(animator.AnimationState == null){
  247. yield return new WaitForSeconds(0.1f);
  248. Debug.LogError("animator state is null!");
  249. }
  250. animator.skeleton.SetSkin(direction);
  251. animator.skeleton.SetSlotsToSetupPose();
  252. animator.AnimationState.SetAnimation(0, $"{direction}_{animationName}", !animationName.ToLower().Contains("death"));
  253. // }catch(Exception e){
  254. // Debug.LogError(e.ToString());
  255. // }
  256. Debug.Log($"Updating enemy animation {direction}_{animationName}");
  257. }
  258. [Command(requiresAuthority =false)]
  259. void CmdTakeDamage(int damage,uint id){
  260. takedmg(damage,id);
  261. Debug.Log("Enemy Attack Recieved ");
  262. }
  263. public void TakeDamage(int damage, uint id){
  264. if(isServer){
  265. takedmg(damage,id);
  266. }
  267. else{
  268. CmdTakeDamage(damage,id);
  269. }
  270. }
  271. void takedmg(int damage,uint id){
  272. if(health<=0){return;}
  273. health -= damage;
  274. //hit vfx
  275. // GameObject newObject = Instantiate(hitVfx , transform.position , Quaternion.identity );
  276. // newObject.transform.localPosition = Vector3.zero;
  277. // newObject.transform.parent = transform;
  278. if(health<= 0 ){
  279. StartCoroutine(couroutineDeath());
  280. foreach(playerNetwork player in FindObjectsOfType<playerNetwork>()){
  281. if(player.netId == id){
  282. //This one attacked me
  283. player.OnEnemyKilled(level);
  284. }
  285. }
  286. }
  287. Debug.Log("Enemy Takes Damage ***");
  288. }
  289. [Command(requiresAuthority =false)]
  290. void CmdTakeMagicalDamage(int damage,uint id){
  291. takeMagicalDmg(damage,id);
  292. Debug.Log("Enemy Attack Recieved ");
  293. }
  294. public void TakeMagicalDamage(int damage, uint id){
  295. if(isServer){
  296. takeMagicalDmg(damage,id);
  297. }
  298. else{
  299. CmdTakeMagicalDamage(damage,id);
  300. }
  301. }
  302. void takeMagicalDmg(int damage,uint id){
  303. if(magicalHealth<=0){return;}
  304. magicalHealth -= damage;
  305. if(magicalHealth<= 0 ){
  306. StartCoroutine(couroutineDeath());
  307. foreach(playerNetwork player in FindObjectsOfType<playerNetwork>()){
  308. if(player.netId == id){
  309. //This one attacked me
  310. player.OnEnemyKilled(level);
  311. }
  312. }
  313. }
  314. Debug.Log("Enemy Takes Damage ***");
  315. }
  316. IEnumerator couroutineDeath(){
  317. animationString = "Death";
  318. UpdateAnimation(directionString , animationString);
  319. // RpcUpdateAnim(directionString, animationString,false);
  320. Vector3 lootSpawnPos = transform.position;
  321. lootSpawnPos.z = GameManager.instance.LootSpawnPointsParent.GetChild(0).position.z;
  322. //instantiate loot item
  323. GameObject newLoot = Instantiate(GameManager.instance.GetRandomLoot(), lootSpawnPos, Quaternion.identity);
  324. NetworkServer.Spawn(newLoot);
  325. yield return new WaitForSecondsRealtime(5);
  326. if (!isServer)
  327. {
  328. CmdDie();
  329. }
  330. else
  331. {
  332. GameManager.OnEnemyDeath(this, defaultPos);
  333. }
  334. /* transform.position = defaultPos;
  335. health = (int)maxHealth;
  336. magicalHealth = (int)maxHealth;*/
  337. //animationString = "Idle";
  338. }
  339. [Command]
  340. void CmdDie()
  341. {
  342. GameManager.OnEnemyDeath(this,defaultPos);
  343. }
  344. public void OnHealthChange(int oldVlaue, int newValue){
  345. healthBar.SetHealth(newValue,maxHealth);
  346. }
  347. public void OnMagicalHealthChange(int oldVlaue, int newValue){
  348. MagicalhealthBar.SetHealth(newValue,maxHealth);
  349. }
  350. }