chunk 1: core gameplay scripts scenes runtime assets

This commit is contained in:
2026-04-06 11:02:34 +03:00
parent fa0388bc79
commit 0d11a097d8
703 changed files with 2292651 additions and 0 deletions

View File

@@ -0,0 +1,271 @@
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
/// <summary>
/// TeamAIController - AI controller for FRIENDLY teammates on the player's team.
/// These AI characters fight FOR the player, targeting enemy team members.
/// Used in Cash System mode for AI teammates.
/// </summary>
[RequireComponent(typeof(TeamMember))]
public class TeamAIController : MonoBehaviour
{
[Header("AI State")]
[SerializeField] private AIState currentState = AIState.Idle;
[Header("Detection Settings")]
[SerializeField] private float detectionRange = 15f;
[SerializeField] private float attackRange = 2f;
[SerializeField] private float followDistance = 5f;
[Header("Behavior Settings")]
[SerializeField] private float decisionInterval = 0.5f;
[SerializeField] private bool followPlayer = true;
[SerializeField] private float maxDistanceFromPlayer = 20f;
[Header("References")]
private TeamMember teamMember;
private NavMeshAgent navAgent;
private Animator animator;
private CharacterAttacks characterAttacks;
// Combat
private TeamMember currentTarget;
private float lastAttackTime;
private float attackCooldown = 1.5f;
// Cash System integration
private CashCarrier cashCarrier;
private CashSystemAI cashAI;
public enum AIState
{
Idle,
FollowPlayer,
ChaseEnemy,
Attack,
CollectCash,
DepositCash,
Retreat
}
private void Awake()
{
teamMember = GetComponent<TeamMember>();
navAgent = GetComponent<NavMeshAgent>();
animator = GetComponent<Animator>();
characterAttacks = GetComponent<CharacterAttacks>();
cashCarrier = GetComponent<CashCarrier>();
cashAI = GetComponent<CashSystemAI>();
}
private void OnEnable()
{
Debug.Log($"[TeamAIController] Enabled on {gameObject.name}");
StartCoroutine(AIDecisionLoop());
}
private void OnDisable()
{
Debug.Log($"[TeamAIController] Disabled on {gameObject.name}");
StopAllCoroutines();
}
private IEnumerator AIDecisionLoop()
{
yield return new WaitForSeconds(0.5f); // Initial delay
while (enabled)
{
MakeDecision();
yield return new WaitForSeconds(decisionInterval);
}
}
private void MakeDecision()
{
// Lazy re-cache: CashSystemAI is added AFTER our Awake() by ConfigureCharacterControllers,
// so the Awake-time GetComponent returns null. Re-check here once.
if (cashAI == null) cashAI = GetComponent<CashSystemAI>();
// If CashSystemAI is attached and active, delegate ALL decisions to it
if (cashAI != null && cashAI.enabled)
{
// CashSystemAI handles the high-level decision making via its own Update()
return;
}
// Standard team AI decision making
TeamMember nearestEnemy = teamMember.FindNearestEnemy();
TeamMember playerControlled = TeamMember.GetPlayerControlledMember();
// Priority 1: Attack nearby enemies
if (nearestEnemy != null)
{
float distanceToEnemy = Vector3.Distance(transform.position, nearestEnemy.transform.position);
if (distanceToEnemy <= attackRange)
{
SetState(AIState.Attack);
currentTarget = nearestEnemy;
PerformAttack();
return;
}
else if (distanceToEnemy <= detectionRange)
{
SetState(AIState.ChaseEnemy);
currentTarget = nearestEnemy;
MoveToTarget(nearestEnemy.transform.position);
return;
}
}
// Priority 2: Follow the player-controlled character
if (followPlayer && playerControlled != null && playerControlled != teamMember)
{
float distanceToPlayer = Vector3.Distance(transform.position, playerControlled.transform.position);
if (distanceToPlayer > followDistance)
{
SetState(AIState.FollowPlayer);
MoveToTarget(playerControlled.transform.position);
return;
}
}
// Default: Idle
SetState(AIState.Idle);
StopMovement();
}
private void SetState(AIState newState)
{
if (currentState != newState)
{
currentState = newState;
Debug.Log($"[TeamAIController] {gameObject.name} -> {newState}");
}
}
private void MoveToTarget(Vector3 position)
{
if (navAgent != null && navAgent.isActiveAndEnabled && navAgent.isOnNavMesh)
{
navAgent.isStopped = false;
navAgent.SetDestination(position);
// Update animator
if (animator != null)
{
float speed = navAgent.velocity.magnitude / navAgent.speed;
animator.SetFloat("Speed", speed);
}
}
}
private void StopMovement()
{
if (navAgent != null && navAgent.isActiveAndEnabled && navAgent.isOnNavMesh)
{
navAgent.isStopped = true;
navAgent.velocity = Vector3.zero;
}
if (animator != null)
{
animator.SetFloat("Speed", 0f);
}
}
private void PerformAttack()
{
if (Time.time - lastAttackTime < attackCooldown) return;
// Face the target
if (currentTarget != null)
{
Vector3 direction = (currentTarget.transform.position - transform.position).normalized;
direction.y = 0;
if (direction.sqrMagnitude > 0.001f)
{
transform.rotation = Quaternion.LookRotation(direction);
}
}
// Perform attack using CharacterAttacks if available
if (characterAttacks != null)
{
// Choose a random attack
string[] attacks = { AnimationNames.Jab, AnimationNames.LowKick, AnimationNames.Haymaker };
string randomAttack = attacks[Random.Range(0, attacks.Length)];
characterAttacks.PunchAttacksDirectly(randomAttack);
lastAttackTime = Time.time;
}
else if (animator != null)
{
// Fallback to direct animation trigger
animator.SetTrigger("attack");
lastAttackTime = Time.time;
}
}
private void Update()
{
// When CashSystemAI is driving, don't interfere with NavMeshAgent
if (cashAI != null && cashAI.enabled) return;
// Continuous state updates
switch (currentState)
{
case AIState.Attack:
if (currentTarget != null)
{
float distance = Vector3.Distance(transform.position, currentTarget.transform.position);
if (distance > attackRange * 1.5f)
{
// Move closer
MoveToTarget(currentTarget.transform.position);
}
else
{
StopMovement();
PerformAttack();
}
}
break;
case AIState.ChaseEnemy:
if (currentTarget != null)
{
MoveToTarget(currentTarget.transform.position);
}
break;
}
}
/// <summary>
/// Set a specific target for this AI to attack
/// </summary>
public void SetTarget(TeamMember target)
{
currentTarget = target;
if (target != null)
{
SetState(AIState.ChaseEnemy);
}
}
/// <summary>
/// Command this AI to move to a specific position
/// </summary>
public void MoveTo(Vector3 position)
{
MoveToTarget(position);
}
/// <summary>
/// Get current AI state
/// </summary>
public AIState GetCurrentState() => currentState;
}