MonsterController.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. using UnityEngine;
  2. using System.Collections;
  3. using DamageNumbersPro;
  4. using UnityEditor.Embree;
  5. public class MonsterController : MonoBehaviour
  6. {
  7. public MonsterData data;
  8. public MonsterFormationGroup formationGroup;
  9. private MonsterAnimatorController animator;
  10. private Vector3 currentTarget;
  11. public int currentHP;
  12. private float attackTimer = 0f; // Start at 0 so first attack is ready after cooldown
  13. public float rotationSpeed = 8f; // Higher = smoother rotation
  14. public DamageNumber damagePopupPrefab;
  15. public GameObject goldBagPrefab;
  16. public float goldSpawnDelay = 0.5f;
  17. private void Start()
  18. {
  19. animator = GetComponent<MonsterAnimatorController>();
  20. currentHP = data.maxHP;
  21. // Start with attack ready (set timer to cooldown value)
  22. if (data != null)
  23. {
  24. attackTimer = data.attackCooldown;
  25. }
  26. FindFirstObjectByType<CombatManager>()?.RegisterMonster(this);
  27. }
  28. public void ShowDamageNumber(float amount)
  29. {
  30. if (damagePopupPrefab == null) return;
  31. // Position du popup : au-dessus du monstre
  32. Vector3 popupPosition = transform.position + Vector3.up * 2f;
  33. // Spawn world-space popup (si prefab DamageNumberMesh)
  34. DamageNumber newPopup = damagePopupPrefab.Spawn(popupPosition, amount);
  35. }
  36. public void MoveTo(Vector3 newPosition)
  37. {
  38. currentTarget = newPosition;
  39. StopAllCoroutines();
  40. StartCoroutine(MoveStep());
  41. }
  42. IEnumerator MoveStep()
  43. {
  44. animator?.PlayMove();
  45. while (Vector3.Distance(transform.position, currentTarget) > 0.05f)
  46. {
  47. transform.position = Vector3.MoveTowards(transform.position, currentTarget, data.moveSpeed * Time.deltaTime);
  48. yield return null;
  49. }
  50. animator?.StopMove();
  51. }
  52. public void FaceTarget(Vector3 target, bool instant = false)
  53. {
  54. Vector3 direction = (target - transform.position).normalized;
  55. direction.y = 0; // rester sur le plan horizontal
  56. if (direction != Vector3.zero)
  57. {
  58. Quaternion targetRotation = Quaternion.LookRotation(direction);
  59. if (instant)
  60. {
  61. // Instant rotation for combat
  62. transform.rotation = targetRotation;
  63. }
  64. else
  65. {
  66. // Smooth rotation for movement
  67. transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);
  68. }
  69. }
  70. }
  71. public bool IsFacingTarget(Vector3 target, float angleThreshold = 30f)
  72. {
  73. Vector3 directionToTarget = (target - transform.position).normalized;
  74. directionToTarget.y = 0;
  75. if (directionToTarget == Vector3.zero) return true;
  76. float angle = Vector3.Angle(transform.forward, directionToTarget);
  77. return angle <= angleThreshold;
  78. }
  79. public void StopMove()
  80. {
  81. animator?.StopMove();
  82. }
  83. public void Attack()
  84. {
  85. animator?.PlayAttack();
  86. }
  87. public void UpdateAttackTimer(float deltaTime)
  88. {
  89. attackTimer += deltaTime;
  90. }
  91. public bool CanAttack()
  92. {
  93. return attackTimer >= data.attackCooldown;
  94. }
  95. public void ResetAttackTimer()
  96. {
  97. attackTimer = 0f;
  98. }
  99. public int GetDamage()
  100. {
  101. // Échec d'attaque ?
  102. if (Random.value < data.missChance)
  103. {
  104. Debug.Log($"{gameObject.name} a raté son attaque !");
  105. return 0;
  106. }
  107. float min = data.attackDamage / 2f;
  108. float max = data.attackDamage * 1.5f;
  109. int damage = Mathf.RoundToInt(Random.Range(min, max));
  110. return damage;
  111. }
  112. public void TakeDamage(int amount)
  113. {
  114. currentHP -= amount;
  115. ShowDamageNumber(amount);
  116. if (currentHP <= 0)
  117. {
  118. Debug.Log("Monstre tué");
  119. Die(); // uniquement animation de mort
  120. }
  121. else
  122. {
  123. animator?.PlayHurt(); // seulement s'il reste en vie
  124. }
  125. }
  126. public void Die()
  127. {
  128. Debug.Log("Méthode Die Appellée");
  129. animator?.PlayDeath();
  130. Debug.Log("Check Notify MonsterGroup 1");
  131. // Grant experience to all party members
  132. GrantExperienceToParty();
  133. // 50% chance to drop gold bag
  134. DropGoldBag();
  135. //GetComponent<Collider>().enabled = false;
  136. formationGroup?.NotifyMonsterDeath(this);
  137. Debug.Log("Check Notify MonsterGroup 2");
  138. Destroy(gameObject, 2f);
  139. }
  140. private void DropGoldBag()
  141. {
  142. // 50% chance to drop gold
  143. if (Random.value < 0.5f)
  144. {
  145. if (goldBagPrefab != null)
  146. {
  147. StartCoroutine(SpawnGoldWithDelay());
  148. }
  149. else
  150. {
  151. Debug.LogWarning($"[Loot] {gameObject.name} should drop gold but goldBagPrefab is not assigned!");
  152. }
  153. }
  154. else
  155. {
  156. Debug.Log($"[Loot] {gameObject.name} didn't drop gold (50% chance)");
  157. }
  158. }
  159. private IEnumerator SpawnGoldWithDelay()
  160. {
  161. Vector3 dropPosition = transform.position + Vector3.up * 0.5f;
  162. yield return new WaitForSeconds(goldSpawnDelay);
  163. Quaternion goldRotation = Quaternion.Euler(-90f, 0f, 0f);
  164. GameObject goldBag = Instantiate(goldBagPrefab, dropPosition, goldRotation);
  165. Debug.Log($"[Loot] {gameObject.name} dropped a gold bag!");
  166. }
  167. private void GrantExperienceToParty()
  168. {
  169. TeamCohesionManager cohesionManager = FindFirstObjectByType<TeamCohesionManager>();
  170. if (cohesionManager == null || data == null) return;
  171. int expPerMember = Mathf.RoundToInt(data.exp);
  172. Debug.Log($"[Experience] Distributing {expPerMember} XP to each party member");
  173. foreach (var character in cohesionManager.groupMembers)
  174. {
  175. character.experience += expPerMember;
  176. Debug.Log($"[Experience] {character.characterName} gained {expPerMember} XP (Total: {character.experience})");
  177. // Update the experience bar
  178. UIUpdater.Instance?.UpdateCharacterExperience(character);
  179. // Check for level up
  180. CheckLevelUp(character);
  181. }
  182. }
  183. private void CheckLevelUp(CharacterInGroup character)
  184. {
  185. while (character.experience >= 100)
  186. {
  187. character.experience -= 100;
  188. character.level++;
  189. // Increase max HP and stamina on level up
  190. int hpIncrease = 5 + character.constitution; // Base 5 + constitution bonus
  191. int staminaIncrease = 5 + (character.constitution / 2); // Base 5 + half constitution
  192. character.maxHP += hpIncrease;
  193. character.currentHP += hpIncrease; // Also heal by the increase amount
  194. character.maxFatigue += staminaIncrease;
  195. character.currentFatigue += staminaIncrease; // Also restore by the increase amount
  196. Debug.Log($"[Level Up] {character.characterName} reached level {character.level}! HP +{hpIncrease}, Stamina +{staminaIncrease}");
  197. // Update all UI bars
  198. UIUpdater.Instance?.UpdateCharacterHP(character);
  199. UIUpdater.Instance?.UpdateCharacterFatigue(character);
  200. UIUpdater.Instance?.UpdateCharacterExperience(character);
  201. }
  202. }
  203. public void Setup(MonsterGroupManager mgr) {}
  204. }