ai/ai_brain.cs
// AiBrain.cs
using System;
using System.Collections.Generic;
using Sandbox;
public sealed class AiBrain : Component
{
[Property, Group( "Debug & Test" )]
[Description( "Si coché, le bot s'immobilise et n'attaque plus. Il sert de mannequin d'entraînement pour tester les coups, l'équilibre, le knockdown et les armes." )]
public bool IsPacifist { get; set; } = false;
public Fighter Fighter { get; private set; }
public FighterMovement Movement { get; private set; }
public FighterCombat Combat { get; private set; }
public AiBlackboard Blackboard { get; private set; }
public NavMeshAgent Agent { get; private set; }
public AiState ActiveState { get; private set; }
public AiState PreviousState { get; private set; }
public string ActiveStateName => ActiveState?.StateName ?? "None";
public TimeSince TimeInState { get; private set; } = 0f;
private readonly Dictionary<Type, AiState> _states = new();
private FighterStatus _lastFighterStatus = FighterStatus.Active;
protected override void OnAwake()
{
Fighter = Components.Get<Fighter>();
Movement = Components.Get<FighterMovement>();
Combat = Components.Get<FighterCombat>();
Blackboard = Components.Get<AiBlackboard>();
Agent = Components.Get<NavMeshAgent>();
}
protected override void OnStart()
{
Register( new AiEngageState() );
Register( new AiSeekWeaponState() );
TransitionTo<AiEngageState>();
}
protected override void OnUpdate()
{
if ( Fighter == null ) return;
// 1. Verrou d'incapacitation : arrêt moteur si étourdi, au sol ou mort
if ( Fighter.Status != FighterStatus.Active )
{
if ( Agent != null && Agent.IsValid && Agent.Enabled )
Agent.Stop();
_lastFighterStatus = Fighter.Status;
return;
}
// 2. Détection de reprise de conscience (relèvement après knockdown ou fin de stagger)
if ( _lastFighterStatus != FighterStatus.Active )
{
OnFighterRecovered();
_lastFighterStatus = FighterStatus.Active;
}
// 3. Mode Pacifiste / Mannequin d'entraînement
if ( IsPacifist )
{
if ( Agent != null && Agent.IsValid && Agent.Enabled )
Agent.Stop();
if ( Movement != null )
Movement.WishVelocity = Vector3.Zero;
return;
}
// 4. Exécution normale du comportement de combat
ActiveState?.OnUpdate();
}
private void OnFighterRecovered()
{
ActiveState?.OnExit();
TimeInState = 0f;
ActiveState?.OnEnter();
}
public void TransitionTo<T>() where T : AiState
{
var type = typeof( T );
if ( !_states.TryGetValue( type, out var nextState ) ) return;
if ( ActiveState == nextState ) return;
ActiveState?.OnExit();
PreviousState = ActiveState;
ActiveState = nextState;
TimeInState = 0f;
ActiveState.OnEnter();
}
private void Register( AiState state )
{
state.Bind( this );
_states[state.GetType()] = state;
}
}