MonsterController.cs 7.8 KB

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