2023-01-13 16:57:10 -08:00
using Content.Shared.Database ;
2025-03-20 20:56:51 +01:00
using Content.Shared.Humanoid ;
2023-01-13 16:57:10 -08:00
using Content.Shared.Mobs.Components ;
2025-03-20 20:56:51 +01:00
using Robust.Shared.Player ;
2023-01-13 16:57:10 -08:00
namespace Content.Shared.Mobs.Systems ;
public partial class MobStateSystem
{
#region Public API
/// <summary>
/// Check if an Entity can be set to a particular MobState
/// </summary>
/// <param name="entity">Target Entity</param>
/// <param name="mobState">MobState to check</param>
/// <param name="component">MobState Component owned by the target</param>
/// <returns>If the entity can be set to that MobState</returns>
public bool HasState ( EntityUid entity , MobState mobState , MobStateComponent ? component = null )
{
2024-06-16 04:20:54 -07:00
return _mobStateQuery . Resolve ( entity , ref component , false ) & &
component . AllowedStates . Contains ( mobState ) ;
2023-01-13 16:57:10 -08:00
}
/// <summary>
/// Run a MobState update check. This will trigger update events if the state has been changed.
/// </summary>
/// <param name="entity">Target Entity we want to change the MobState of</param>
/// <param name="component">MobState Component attached to the entity</param>
/// <param name="origin">Entity that caused the state update (if applicable)</param>
public void UpdateMobState ( EntityUid entity , MobStateComponent ? component = null , EntityUid ? origin = null )
{
2024-06-16 04:20:54 -07:00
if ( ! _mobStateQuery . Resolve ( entity , ref component ) )
2023-01-13 16:57:10 -08:00
return ;
var ev = new UpdateMobStateEvent { Target = entity , Component = component , Origin = origin } ;
RaiseLocalEvent ( entity , ref ev ) ;
2023-08-30 21:06:15 -04:00
ChangeState ( entity , component , ev . State , origin : origin ) ;
2023-01-13 16:57:10 -08:00
}
/// <summary>
2023-11-09 18:14:06 -05:00
/// Change the MobState without triggering UpdateMobState events.
/// WARNING: use this sparingly when you need to override other systems (MobThresholds)
2023-01-13 16:57:10 -08:00
/// </summary>
/// <param name="entity">Target Entity we want to change the MobState of</param>
/// <param name="mobState">The new MobState we want to set</param>
/// <param name="component">MobState Component attached to the entity</param>
/// <param name="origin">Entity that caused the state update (if applicable)</param>
public void ChangeMobState ( EntityUid entity , MobState mobState , MobStateComponent ? component = null ,
EntityUid ? origin = null )
{
2024-06-16 04:20:54 -07:00
if ( ! _mobStateQuery . Resolve ( entity , ref component ) )
2023-01-13 16:57:10 -08:00
return ;
2023-11-09 18:14:06 -05:00
ChangeState ( entity , component , mobState , origin : origin ) ;
2023-01-13 16:57:10 -08:00
}
#endregion
#region Virtual API
/// <summary>
/// Called when a new MobState is entered.
/// </summary>
/// <param name="entity">The owner of the MobState Component</param>
/// <param name="component">MobState Component owned by the target</param>
/// <param name="state">The new MobState</param>
protected virtual void OnEnterState ( EntityUid entity , MobStateComponent component , MobState state )
{
OnStateEnteredSubscribers ( entity , component , state ) ;
}
/// <summary>
/// Called when this entity changes MobState
/// </summary>
/// <param name="entity">The owner of the MobState Component</param>
/// <param name="component">MobState Component owned by the target</param>
/// <param name="oldState">The previous MobState</param>
/// <param name="newState">The new MobState</param>
protected virtual void OnStateChanged ( EntityUid entity , MobStateComponent component , MobState oldState ,
MobState newState )
{
}
/// <summary>
/// Called when a new MobState is exited.
/// </summary>
/// <param name="entity">The owner of the MobState Component</param>
/// <param name="component">MobState Component owned by the target</param>
/// <param name="state">The old MobState</param>
protected virtual void OnExitState ( EntityUid entity , MobStateComponent component , MobState state )
{
OnStateExitSubscribers ( entity , component , state ) ;
}
#endregion
#region Private Implementation
//Actually change the MobState
private void ChangeState ( EntityUid target , MobStateComponent component , MobState newState , EntityUid ? origin = null )
{
var oldState = component . CurrentState ;
//make sure we are allowed to enter the new state
if ( oldState = = newState | | ! component . AllowedStates . Contains ( newState ) )
return ;
OnExitState ( target , component , oldState ) ;
component . CurrentState = newState ;
OnEnterState ( target , component , newState ) ;
var ev = new MobStateChangedEvent ( target , component , oldState , newState , origin ) ;
OnStateChanged ( target , component , oldState , newState ) ;
RaiseLocalEvent ( target , ev , true ) ;
2025-03-20 20:56:51 +01:00
if ( origin ! = null & & HasComp < ActorComponent > ( origin ) & & HasComp < ActorComponent > ( target ) & & oldState < newState )
_adminLogger . Add ( LogType . Damaged , LogImpact . High , $"{ToPrettyString(origin):player} caused {ToPrettyString(target):player} state to change from {oldState} to {newState}" ) ;
else
_adminLogger . Add ( LogType . Damaged , oldState = = MobState . Alive ? LogImpact . Low : LogImpact . Medium , $"{ToPrettyString(target):user} state changed from {oldState} to {newState}" ) ;
2023-10-19 12:34:31 -07:00
Dirty ( target , component ) ;
2023-01-13 16:57:10 -08:00
}
#endregion
}
/// <summary>
/// Event that gets triggered when we want to update the mobstate. This allows for systems to override MobState changes
/// </summary>
/// <param name="Target">The Entity whose MobState is changing</param>
/// <param name="Component">The MobState Component owned by the Target</param>
/// <param name="State">The new MobState we want to set</param>
/// <param name="Origin">Entity that caused the state update (if applicable)</param>
[ByRefEvent]
public record struct UpdateMobStateEvent ( EntityUid Target , MobStateComponent Component , MobState State ,
EntityUid ? Origin = null ) ;