| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- using UnityEngine;
- using System.Collections;
- using DamageNumbersPro;
- using UnityEditor.Embree;
- public class MonsterController : MonoBehaviour
- {
- public MonsterData data;
- public MonsterFormationGroup formationGroup;
- private MonsterAnimatorController animator;
- private Vector3 currentTarget;
- public int currentHP;
- private float attackTimer = 0f;
- public float rotationSpeed = 40f;
- public DamageNumber damagePopupPrefab;
- public GameObject goldBagPrefab;
- public Transform goldSpawnPoint;
- public float goldDropYOffset = 0.15f; // Y offset for gold drop position
- private void Start()
- {
- animator = GetComponent<MonsterAnimatorController>();
- currentHP = data.maxHP;
- // Start with attack ready (set timer to cooldown value)
- if (data != null)
- {
- attackTimer = data.attackCooldown;
- }
- FindFirstObjectByType<CombatManager>()?.RegisterMonster(this);
- }
- public void ShowDamageNumber(float amount)
- {
- if (damagePopupPrefab == null) return;
- // Position du popup : au-dessus du monstre
- Vector3 popupPosition = transform.position + Vector3.up * 2f;
- // Spawn world-space popup (si prefab DamageNumberMesh)
- DamageNumber newPopup = damagePopupPrefab.Spawn(popupPosition, amount);
- // Make damage number face the camera without mirroring
- if (newPopup != null)
- {
- Transform cameraTransform = Camera.main?.transform;
- if (cameraTransform != null)
- {
- // Copy camera's rotation (billboard effect) but keep it upright
- Vector3 cameraEuler = cameraTransform.eulerAngles;
- newPopup.transform.rotation = Quaternion.Euler(0f, cameraEuler.y, 0f);
- }
- }
- }
- public void MoveTo(Vector3 newPosition)
- {
- currentTarget = newPosition;
- StopAllCoroutines();
- StartCoroutine(MoveStep());
- }
- IEnumerator MoveStep()
- {
- animator?.PlayMove();
- while (Vector3.Distance(transform.position, currentTarget) > 0.05f)
- {
- transform.position = Vector3.MoveTowards(transform.position, currentTarget, data.moveSpeed * Time.deltaTime);
- yield return null;
- }
- animator?.StopMove();
- }
- public void FaceTarget(Vector3 target, bool instant = false)
- {
- Vector3 direction = (target - transform.position).normalized;
- direction.y = 0; // rester sur le plan horizontal
- if (direction != Vector3.zero)
- {
- Quaternion targetRotation = Quaternion.LookRotation(direction);
- if (instant)
- {
- // Instant rotation for combat
- transform.rotation = targetRotation;
- }
- else
- {
- // Smooth rotation for movement
- transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);
- }
- }
- }
- public bool IsFacingTarget(Vector3 target, float angleThreshold = 30f)
- {
- Vector3 directionToTarget = (target - transform.position).normalized;
- directionToTarget.y = 0;
- if (directionToTarget == Vector3.zero) return true;
- float angle = Vector3.Angle(transform.forward, directionToTarget);
- return angle <= angleThreshold;
- }
- public void StopMove()
- {
- animator?.StopMove();
- }
- public void Attack()
- {
- animator?.PlayAttack();
- }
- public void UpdateAttackTimer(float deltaTime)
- {
- attackTimer += deltaTime;
- }
- public bool CanAttack()
- {
- return attackTimer >= data.attackCooldown;
- }
- public void ResetAttackTimer()
- {
- attackTimer = 0f;
- }
- public int GetDamage()
- {
- // Échec d'attaque ?
- if (Random.value < data.missChance)
- {
- Debug.Log($"{gameObject.name} a raté son attaque !");
- return 0;
- }
- float min = data.attackDamage / 2f;
- float max = data.attackDamage * 1.5f;
- int damage = Mathf.RoundToInt(Random.Range(min, max));
- return damage;
- }
- public void TakeDamage(int amount)
- {
- currentHP -= amount;
- ShowDamageNumber(amount);
- if (currentHP <= 0)
- {
- Debug.Log("Monstre tué");
- Die(); // uniquement animation de mort
- }
- else
- {
- animator?.PlayHurt(); // seulement s'il reste en vie
- }
- }
- public void Die()
- {
- Debug.Log("Méthode Die Appellée");
- animator?.PlayDeath();
- Debug.Log("Check Notify MonsterGroup 1");
- // Disable collider immediately to prevent gold bag clipping
- Collider collider = GetComponent<Collider>();
- if (collider != null)
- {
- collider.enabled = false;
- }
- // Grant experience to all party members
- GrantExperienceToParty();
- // Drop gold bag (50% chance)
- StartCoroutine(CoroutineDropGoldBag());
- formationGroup?.NotifyMonsterDeath(this);
- Debug.Log("Check Notify MonsterGroup 2");
- Destroy(gameObject, 2f);
- }
- private IEnumerator CoroutineDropGoldBag()
- {
- // 50% chance to drop gold
- if (Random.value < 0.5f)
- {
- if (goldBagPrefab != null)
- {
-
- Vector3 dropPosition;
- if (goldSpawnPoint != null)
- {
- dropPosition = goldSpawnPoint.position + Vector3.up * goldDropYOffset;
- }
- else
- {
- dropPosition = transform.position + Vector3.up * goldDropYOffset;
- }
- //yield return new WaitForSeconds(1f);
- GameObject goldBag = Instantiate(goldBagPrefab, dropPosition, Quaternion.Euler(-90f, 0f, 0f));
- Debug.Log($"[Loot] {gameObject.name} dropped gold at {dropPosition}");
- }
- }
- else
- {
- Debug.Log($"[Loot] {gameObject.name} didn't drop gold (50% chance)");
- }
- yield return null;
- }
- private void GrantExperienceToParty()
- {
- TeamCohesionManager cohesionManager = FindFirstObjectByType<TeamCohesionManager>();
- if (cohesionManager == null || data == null) return;
- int expPerMember = Mathf.RoundToInt(data.exp);
- foreach (var character in cohesionManager.groupMembers)
- {
- character.experience += expPerMember;
- // Update the experience bar
- UIUpdater.Instance?.UpdateCharacterExperience(character);
- // Check for level up
- CheckLevelUp(character);
- }
- }
- private void CheckLevelUp(CharacterInGroup character)
- {
- while (character.experience >= 100)
- {
- character.experience -= 100;
- character.level++;
- // Increase max HP and stamina on level up
- int hpIncrease = 5 + character.constitution; // Base 5 + constitution bonus
- int staminaIncrease = 5 + (character.constitution / 2); // Base 5 + half constitution
- character.maxHP += hpIncrease;
- character.currentHP += hpIncrease; // Also heal by the increase amount
- character.maxFatigue += staminaIncrease;
- character.currentFatigue += staminaIncrease; // Also restore by the increase amount
- Debug.Log($"[Level Up] {character.characterName} reached level {character.level}! HP +{hpIncrease}, Stamina +{staminaIncrease}");
- // Update all UI bars
- UIUpdater.Instance?.UpdateCharacterHP(character);
- UIUpdater.Instance?.UpdateCharacterFatigue(character);
- UIUpdater.Instance?.UpdateCharacterExperience(character);
- }
- }
- public void Setup(MonsterGroupManager mgr) { }
- }
|