| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- public class MonsterFormationGroup : MonoBehaviour
- {
- public MonsterData monsterData;
- public Vector3 anchorPosition;
- public float spacing = 1.5f;
- public float frontRowOffset = 2f;
- public float cellSize = 5f;
- public float attackRange = 6f;
- public float moveDelay = 1f;
- public float stopAfterDistance = 5f;
- public float stopDuration = 2f;
- public float attackInterval = 1.5f;
- public float attackRepeatDelay = 3f;
-
- // Attack range consistency
- private const float ATTACK_RANGE_BUFFER = 0.5f;
- private float effectiveAttackRange => attackRange + ATTACK_RANGE_BUFFER;
- public List<MonsterController> frontRow = new();
- public List<MonsterController> backRow = new();
- public List<Vector3> frontRowPositions = new();
- private Transform player;
- public bool isChasing = false;
- public bool hasDetectedPlayer = false; // Track if player has been detected
- private float distanceTravelled = 0f;
- private Coroutine attackLoopCoroutine;
- private TeamCohesionManager cohesionManager;
- private void Start()
- {
- StartCoroutine(DelayedStart());
- }
- private IEnumerator DelayedStart()
- {
- while (UIUpdater.Instance == null)
- yield return null; // attend que UIUpdater existe
- player = GameObject.FindWithTag("Player")?.transform;
- cohesionManager = FindFirstObjectByType<TeamCohesionManager>();
- if (GetComponent<Collider>() == null)
- {
- SphereCollider trigger = gameObject.AddComponent<SphereCollider>();
- trigger.isTrigger = true;
- trigger.radius = 20f;
- }
- }
- private void OnTriggerEnter(Collider other)
- {
- if (other.CompareTag("Player"))
- {
- hasDetectedPlayer = true;
-
- if (!isChasing && attackLoopCoroutine == null)
- {
- StartChase();
- }
- }
- }
-
- private void OnTriggerExit(Collider other)
- {
- if (other.CompareTag("Player"))
- {
- hasDetectedPlayer = false;
-
- if (attackLoopCoroutine != null)
- {
- StopCoroutine(attackLoopCoroutine);
- attackLoopCoroutine = null;
- }
-
- isChasing = false;
- }
- }
-
- private void OnTriggerStay(Collider other)
- {
- if (other.CompareTag("Player"))
- {
- hasDetectedPlayer = true;
- }
- }
- public void Spawn()
- {
- int total = monsterData.monstersPerGroup;
- for (int i = 0; i < total; i++)
- {
- int row = i < 3 ? 0 : 1;
- int indexInRow = row == 0 ? i : i - 3;
- float xOffset = 0f;
- if (row == 0)
- {
- xOffset = (indexInRow - 1) * spacing;
- }
- else
- {
- if (monsterData.monstersPerGroup == 5 && indexInRow < 2)
- xOffset = (indexInRow - 0.5f) * spacing;
- else
- xOffset = (indexInRow - 1.5f) * spacing;
- }
- float zOffset = -row * spacing + (row == 0 ? frontRowOffset : 0);
- Vector3 offset = new Vector3(xOffset, 0, zOffset);
- Vector3 rotatedOffset = RotateOffset(offset, monsterData.defaultOrientation);
- Vector3 spawnPos = anchorPosition + rotatedOffset;
- Quaternion rotation = GetRotationFromOrientation(monsterData.defaultOrientation);
- GameObject m = Instantiate(monsterData.prefab, spawnPos, rotation, transform);
- MonsterController ctrl = m.GetComponent<MonsterController>();
- ctrl.data = monsterData;
- ctrl.formationGroup = this;
- if (row == 0)
- {
- frontRow.Add(ctrl);
- frontRowPositions.Add(spawnPos);
- }
- else
- {
- backRow.Add(ctrl);
- }
- }
- }
- public void NotifyMonsterDeath(MonsterController dead)
- {
- Vector3 deadPosition = dead.transform.position;
- if (frontRow.Contains(dead))
- {
- int index = frontRow.IndexOf(dead);
- frontRow.RemoveAt(index);
- if (backRow.Count > 0)
- {
- MonsterController replacement = GetClosestBacklinerTo(deadPosition);
- backRow.Remove(replacement);
- frontRow.Insert(index, replacement);
- StartCoroutine(MoveReplacementWithDelay(replacement, deadPosition, 2f));
- }
- }
- else
- {
- backRow.Remove(dead);
- }
- }
- IEnumerator MoveReplacementWithDelay(MonsterController replacement, Vector3 destination, float delay)
- {
- yield return new WaitForSeconds(delay);
- replacement.MoveTo(destination);
- replacement.FaceTarget(player.position);
- }
- public void StartChase()
- {
- if (!isChasing)
- {
- if (UIUpdater.Instance == null || UIUpdater.Instance.IsReady == false)
- {
- StartCoroutine(WaitForUIThenChase());
- return;
- }
- StartCoroutine(ChaseRoutine());
- }
- }
- IEnumerator WaitForUIThenChase()
- {
- while (UIUpdater.Instance == null || UIUpdater.Instance.IsReady == false)
- {
- yield return null;
- }
- StartCoroutine(ChaseRoutine());
- }
- IEnumerator ChaseRoutine()
- {
- isChasing = true;
- distanceTravelled = 0f;
- while (isChasing && hasDetectedPlayer)
- {
- if (!hasDetectedPlayer)
- {
- isChasing = false;
- yield break;
- }
-
- float distanceToPlayer = Vector3.Distance(transform.position, player.position);
- if (distanceToPlayer <= attackRange || IsAdjacentToPlayer())
- {
- isChasing = false;
- attackLoopCoroutine = StartCoroutine(LoopAttack());
- yield break;
- }
- Vector3 dirToPlayer = (player.position - transform.position).normalized;
- Vector3 step = new Vector3(Mathf.Round(dirToPlayer.x), 0, Mathf.Round(dirToPlayer.z)) * cellSize;
- MoveGroupBy(step);
- distanceTravelled += cellSize + 1f;
- if (distanceTravelled >= stopAfterDistance)
- {
- yield return new WaitForSeconds(stopDuration);
- distanceTravelled = 0f;
- }
- else
- {
- yield return new WaitForSeconds(moveDelay);
- }
- }
-
- isChasing = false;
- }
- void MoveGroupBy(Vector3 step)
- {
- foreach (var monster in frontRow.Concat(backRow))
- {
- Vector3 targetPos = monster.transform.position + step;
- monster.MoveTo(targetPos);
- monster.FaceTarget(player.position);
- }
- }
- private void Update()
- {
- if (!hasDetectedPlayer) return;
- if (player == null) return;
- if (isChasing || attackLoopCoroutine != null) return;
-
- if (!isChasing && attackLoopCoroutine == null)
- {
- bool anyMonsterInAttackRange = false;
-
- if (frontRow.Count > 0)
- {
- foreach (var monster in frontRow)
- {
- if (monster != null)
- {
- float monsterToPlayerDist = Vector3.Distance(monster.transform.position, player.position);
-
- if (monsterToPlayerDist <= effectiveAttackRange)
- {
- anyMonsterInAttackRange = true;
- break;
- }
- }
- }
- }
- else
- {
- return;
- }
-
- if (anyMonsterInAttackRange)
- {
- attackLoopCoroutine = StartCoroutine(LoopAttack());
- }
- else
- {
- StartChase();
- }
- }
- }
- bool IsPlayerInRange()
- {
- return Vector3.Distance(transform.position, player.position) <= attackRange;
- }
- IEnumerator LoopAttack()
- {
- while (true)
- {
- if (GameManager.Instance != null && GameManager.Instance.IsPlayerDead())
- {
- attackLoopCoroutine = null;
- yield break;
- }
-
- if (!hasDetectedPlayer)
- {
- attackLoopCoroutine = null;
- yield break;
- }
-
- if (cohesionManager == null || cohesionManager.groupMembers.Count == 0)
- {
- attackLoopCoroutine = null;
- yield break;
- }
-
- if (frontRow.Count == 0 && backRow.Count == 0)
- {
- attackLoopCoroutine = null;
- yield break;
- }
-
- bool anyInRange = frontRow.Any(monster =>
- monster != null && Vector3.Distance(monster.transform.position, player.position) <= effectiveAttackRange);
- if (!anyInRange)
- {
- attackLoopCoroutine = null;
- yield break;
- }
- foreach (var monster in frontRow.ToList())
- {
- if (monster == null) continue;
-
- if (monsterData.attackType == MonsterData.AttackType.Melee)
- {
- float distanceToPlayer = Vector3.Distance(monster.transform.position, player.position);
- if (distanceToPlayer > effectiveAttackRange)
- {
- continue;
- }
-
- monster.StopMove();
- monster.FaceTarget(player.position, instant: true);
-
- if (!monster.IsFacingTarget(player.position, 45f))
- {
- continue;
- }
-
- monster.Attack();
- var closest = cohesionManager.groupMembers
- .OrderBy(charac => Vector3.Distance(monster.transform.position, new Vector3(charac.gridX, 0, charac.gridY)))
- .FirstOrDefault();
- if (closest == null) continue;
- var uiController = UIUpdater.Instance?.GetUIForCharacterByName(closest.characterName);
- if (uiController != null)
- {
- int damage = monster.data.attackDamage;
- closest.currentHP -= damage;
- closest.currentHP = Mathf.Max(0, closest.currentHP);
- uiController.UpdateHPBar();
- uiController.ShowDamageOnCard(damage);
-
- if (closest.currentHP <= 0)
- {
- uiController.HandleCharacterDeath();
- }
- }
- yield return new WaitForSeconds(attackInterval);
- }
- }
- yield return new WaitForSeconds(attackRepeatDelay);
- }
- }
- MonsterController GetClosestBacklinerTo(Vector3 position)
- {
- MonsterController closest = null;
- float minDist = float.MaxValue;
- foreach (var b in backRow)
- {
- float d = Vector3.Distance(b.transform.position, position);
- if (d < minDist)
- {
- minDist = d;
- closest = b;
- }
- }
- return closest;
- }
- bool IsAdjacentToPlayer()
- {
- foreach (var monster in frontRow.Concat(backRow))
- {
- if (Vector3.Distance(monster.transform.position, player.position) <= cellSize + 0.5f)
- return true;
- }
- return false;
- }
- Vector3 RotateOffset(Vector3 offset, MonsterData.Orientation direction)
- {
- switch (direction)
- {
- case MonsterData.Orientation.North: return offset;
- case MonsterData.Orientation.South: return new Vector3(-offset.x, 0, -offset.z);
- case MonsterData.Orientation.East: return new Vector3(offset.z, 0, -offset.x);
- case MonsterData.Orientation.West: return new Vector3(-offset.z, 0, offset.x);
- default: return offset;
- }
- }
- Quaternion GetRotationFromOrientation(MonsterData.Orientation orientation)
- {
- switch (orientation)
- {
- case MonsterData.Orientation.North: return Quaternion.Euler(0, 0, 0);
- case MonsterData.Orientation.South: return Quaternion.Euler(0, 180, 0);
- case MonsterData.Orientation.East: return Quaternion.Euler(0, 90, 0);
- case MonsterData.Orientation.West: return Quaternion.Euler(0, 270, 0);
- default: return Quaternion.identity;
- }
- }
- }
|