using System.Collections; using UnityEngine; using UnityEngine.AI; public class SimpleTeamAI : MonoBehaviour { public enum TeamType { PlayerTeam, EnemyTeam } public enum AIState { Idle, Combat, Collect, Deposit, Vault, Hit, Patrol } [Header("Team Settings")] [SerializeField] private TeamType teamType; [SerializeField] private float roamRadius = 8f; [SerializeField] private float roamChangeTime = 3f; private float nextRoamTime; [Header("Combat Settings")] [SerializeField] private float attackRange = 2f; [SerializeField] private float attackCooldown = 1.2f; [SerializeField] private float targetPriorityRange = 100f; [Header("Collection Settings")] [SerializeField] private float scanRadius = 15f; [SerializeField] private int depositCashAmount = 500; [Header("Debug")] public AIState currentState; public Transform currentTarget; [SerializeField] private Transform combatTarget; private NavMeshAgent agent; private Animator anim; private CashManager cashManager; private Health health; private TeamMember teamMember; private bool isAttacking; private bool isHit; private bool hasCash; private bool isBusy; private Coroutine attackRoutine; private string enemyTag; private string depositStationTag; private Vault openingVault; private Coroutine depositRoutine; private bool isDepositing; [SerializeField] private ParticleSystem depositAura; void Awake() { agent = GetComponent(); anim = GetComponent(); cashManager = GetComponent(); health = GetComponent(); teamMember = GetComponent(); StopDepositEffect(); } void Start() { SetupTeam(); if (agent != null) { agent.enabled = true; agent.isStopped = false; agent.updatePosition = true; //agent.updateRotation = false; if (!agent.isOnNavMesh) { NavMeshHit hit; if (NavMesh.SamplePosition(transform.position, out hit, 20f, NavMesh.AllAreas)) { agent.Warp(hit.position); } else { Debug.LogError($"{gameObject.name} is not on NavMesh."); } } } currentState = AIState.Idle; } void SetupTeam() { // Auto-detect team from TeamMember first if (teamMember != null) { if (teamMember.CurrentTeam == TeamMember.Team.PlayerTeam) { teamType = TeamType.PlayerTeam; } else { teamType = TeamType.EnemyTeam; } } else { // Fallback: auto-detect from tag if TeamMember is missing if (CompareTag("Player")) { teamType = TeamType.PlayerTeam; } else if (CompareTag("Enemy")) { teamType = TeamType.EnemyTeam; } else { Debug.LogWarning($"{gameObject.name}: No TeamMember and no Player/Enemy tag found. Using Inspector teamType."); } } if (teamType == TeamType.PlayerTeam) { enemyTag = "Enemy"; depositStationTag = "PlayerDepositSt"; } else { enemyTag = "Player"; depositStationTag = "EnemyDepositSt"; } Debug.Log($"{gameObject.name}: SimpleTeamAI auto-detected team as {teamType}. EnemyTag={enemyTag}, DepositTag={depositStationTag}"); } void Update() { if (agent == null || anim == null) return; if (!agent.enabled || !agent.isOnNavMesh) return; if (health != null && health.isDead) { DisableMovement(); return; } if (agent.speed == 0) agent.speed = 6f; if (isHit) { SetState(AIState.Hit); } else if (isBusy) { // Keep current state while opening vault / hit / special action } else { cashManager = GetComponent(); if (cashManager != null && cashManager.currentCash >= depositCashAmount) { SetState(AIState.Deposit); } else { combatTarget = FindNearestCombatTarget(); if (combatTarget != null) { float targetDist = Vector3.Distance(transform.position, combatTarget.position); if (targetDist <= targetPriorityRange) { SetState(AIState.Combat); } else { SetState(AIState.Collect); } } else { SetState(AIState.Collect); } } } HandleState(); anim.SetFloat("Speed", agent.velocity.magnitude); } void SetState(AIState newState) { currentState = newState; } void HandleState() { switch (currentState) { case AIState.Combat: HandleCombat(); break; case AIState.Collect: HandleCollection(); break; case AIState.Deposit: HandleDeposit(); break; case AIState.Hit: DisableMovement(); break; } } void HandleCombat() { if (combatTarget == null) { combatTarget = FindNearestCombatTarget(); if (combatTarget == null) { SetState(AIState.Collect); return; } } Health targetHealth = combatTarget.GetComponent(); if (targetHealth != null && targetHealth.isDead) { combatTarget = null; SetState(AIState.Collect); return; } float dist = Vector3.Distance(transform.position, combatTarget.position); Vector3 dir = combatTarget.position - transform.position; dir.y = 0; if (dir.sqrMagnitude > 0.01f) { transform.rotation = Quaternion.Slerp( transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 10f ); } if (!isAttacking) { if (dist > attackRange) { agent.isStopped = false; agent.SetDestination(combatTarget.position); } else { agent.isStopped = true; agent.velocity = Vector3.zero; if (attackRoutine == null) attackRoutine = StartCoroutine(AttackRoutine()); } } } void HandleCollection() { if (currentTarget == null) { currentTarget = FindVaultTarget(); if (currentTarget != null) { Vault vault = currentTarget.GetComponent(); if (vault == null || !vault.TryReserve(this)) { currentTarget = null; return; } } } if (currentTarget == null) { RoamAround(); return; } Vault currentVault = currentTarget.GetComponent(); if (currentVault == null || !currentVault.IsReservedBy(this)) { currentTarget = null; return; } agent.isStopped = false; agent.SetDestination(currentTarget.position); float dist = Vector3.Distance(transform.position, currentTarget.position); if (!isBusy && currentTarget.CompareTag("Vault") && dist <= 1.5f) { SetState(AIState.Vault); StartCoroutine(VaultRoutine()); } } void RoamAround() { if (Time.time < nextRoamTime && agent.hasPath) return; Vector3 randomPos = transform.position + Random.insideUnitSphere * roamRadius; randomPos.y = transform.position.y; NavMeshHit hit; if (NavMesh.SamplePosition(randomPos, out hit, roamRadius, NavMesh.AllAreas)) { agent.isStopped = false; agent.SetDestination(hit.position); nextRoamTime = Time.time + roamChangeTime; } } Transform FindVaultTarget() { Collider[] hits = Physics.OverlapSphere(transform.position, scanRadius); Transform best = null; float minDist = Mathf.Infinity; foreach (Collider hit in hits) { if (hit.CompareTag("Vault")) { Vault vault = hit.GetComponent(); if (vault == null) continue; if (vault.IsReserved()) continue; float d = Vector3.Distance(transform.position, hit.transform.position); if (d < minDist) { minDist = d; best = hit.transform; } } } return best; } Transform FindNearestCombatTarget() { if (teamMember == null) return null; Transform best = null; float minDist = Mathf.Infinity; foreach (TeamMember other in TeamMember.AllMembers) { if (other == null) continue; if (other == teamMember) continue; if (!teamMember.IsEnemy(other)) continue; Health targetHealth = other.GetComponent(); if (targetHealth != null && targetHealth.isDead) continue; float d = Vector3.Distance( transform.position, other.transform.position); if (d < minDist) { minDist = d; best = other.transform; } } return best; } IEnumerator VaultRoutine() { if (isBusy) yield break; if (currentTarget == null) yield break; Vault vault = currentTarget.GetComponent(); openingVault = vault; if (vault.IsPlayerInside()) { vault.Release(this); currentTarget = null; openingVault = null; yield break; } if (vault == null || !vault.IsReservedBy(this)) { currentTarget = null; yield break; } isBusy = true; vault.MarkBeingOpened(this); agent.isStopped = true; agent.velocity = Vector3.zero; if (vault != null && vault.InteractionPoint != null) { transform.position = vault.InteractionPoint.position; agent.Warp(transform.position); Vector3 lookDir = vault.transform.position - transform.position; lookDir.y = 0; if (lookDir.sqrMagnitude > 0.01f) transform.rotation = Quaternion.LookRotation(lookDir); } anim.SetTrigger("OpenVault"); yield return new WaitForSeconds(6f); //cashManager = GetComponent(); //if (cashManager != null && vault != null) //{ // cashManager.AddCash(vault.GetVaultCash()); //} //if (currentTarget != null) //{ // Destroy(currentTarget.gameObject); //} hasCash = true; currentTarget = null; agent.isStopped = false; isBusy = false; } public void AddVaultCashAndDestroyVault() { if (openingVault == null) return; if (isHit || currentState == AIState.Hit) return; if (health != null && health.isDead) return; if (cashManager == null) cashManager = GetComponent(); if (cashManager != null) cashManager.AddCash(openingVault.GetVaultCash()); VaultSpawner.Instance?.NotifyVaultDestroyed(); Destroy(openingVault.gameObject); openingVault = null; currentTarget = null; hasCash = true; isBusy = false; if (agent != null && agent.enabled && agent.isOnNavMesh) agent.isStopped = false; SetState(AIState.Deposit); } void HandleDeposit() { GameObject deposit = GameObject.FindGameObjectWithTag(depositStationTag); if (deposit == null) { Debug.LogWarning($"{gameObject.name}: Deposit station not found with tag {depositStationTag}"); return; } Transform point = deposit.transform.Find("DepositPoint"); if (point == null) { Debug.LogWarning($"{gameObject.name}: DepositPoint child missing under {depositStationTag}"); return; } currentTarget = point; agent.isStopped = false; agent.SetDestination(point.position); } IEnumerator AttackRoutine() { if (isAttacking || isHit) yield break; if (combatTarget == null) { attackRoutine = null; yield break; } float dist = Vector3.Distance(transform.position, combatTarget.position); if (dist > attackRange) { attackRoutine = null; yield break; } isAttacking = true; agent.isStopped = true; int attackIndex = Random.Range(0, 6); switch (attackIndex) { case 0: anim.SetTrigger("Haymaker"); break; case 1: anim.SetTrigger("LeftKick"); break; case 2: anim.SetTrigger("LowKick"); break; case 3: anim.SetTrigger("BicycleKick"); break; case 4: anim.SetTrigger("SpinningBackFist"); break; case 5: anim.SetTrigger("ElbowSmash"); break; } yield return new WaitForSeconds(1f); isAttacking = false; //agent.isStopped = false; yield return new WaitForSeconds(attackCooldown); attackRoutine = null; } public void CancelAttackAndHit(string bodyPart = "Body") { isHit = true; SetState(AIState.Hit); if (attackRoutine != null) { StopCoroutine(attackRoutine); attackRoutine = null; } isAttacking = false; DisableMovement(); anim.ResetTrigger("Haymaker"); anim.ResetTrigger("LeftKick"); anim.ResetTrigger("LowKick"); anim.ResetTrigger("BicycleKick"); anim.ResetTrigger("SpinningBackFist"); anim.ResetTrigger("ElbowSmash"); CharacterCombat combat = GetComponent(); combat?.OnDamageTaken(bodyPart); StartCoroutine(RecoverFromHit()); } IEnumerator RecoverFromHit() { yield return new WaitForSeconds(0.5f); isHit = false; SetState(AIState.Idle); EnableMovement(); } public void DisableMovement() { if (agent != null && agent.enabled && agent.isOnNavMesh) { agent.isStopped = true; agent.ResetPath(); agent.velocity = Vector3.zero; } } public void EnableMovement() { if (agent != null && agent.enabled && agent.isOnNavMesh) { agent.speed = 6f; agent.isStopped = false; } } public void OnHitStart() { isHit = true; SetState(AIState.Hit); DisableMovement(); } public void OnHitEnd() { isHit = false; SetState(AIState.Idle); EnableMovement(); } public void ResetAfterDeposit() { hasCash = false; currentTarget = null; SetState(AIState.Idle); } public void StartDeposit(float depositTime) { if (isDepositing) return; depositRoutine = StartCoroutine(DepositRoutine(depositTime)); } IEnumerator DepositRoutine(float depositTime) { isDepositing = true; isBusy = true; agent.isStopped = true; agent.velocity = Vector3.zero; //anim.SetTrigger("Deposit"); yield return new WaitForSeconds(depositTime); CashManager cash = GetComponent(); if (cash != null) { int depositedAmount = cash.DepositAllCash(); DepositStationManager.Instance.AddCash( depositedAmount, teamMember.CurrentTeam ); } StopDepositEffect(); isDepositing = false; isBusy = false; ResetAfterDeposit(); } public void StartDepositEffect() { if (depositAura != null) depositAura.Play(); } public void StopDepositEffect() { if (depositAura != null) depositAura.Stop(); } }