using Robust.Shared.Prototypes;
namespace Content.Shared.EntityConditions;
///
/// This handles entity effects.
/// Specifically it handles the receiving of events for causing entity effects, and provides
/// public API for other systems to take advantage of entity effects.
///
public sealed partial class SharedEntityConditionsSystem : EntitySystem, IEntityConditionRaiser
{
///
/// Checks a list of conditions to verify that they all return true.
///
/// Target entity we're checking conditions on
/// Conditions we're checking
/// Returns true if all conditions return true, false if any fail
public bool TryConditions(EntityUid target, EntityCondition[]? conditions)
{
// If there's no conditions we can't fail any of them...
if (conditions == null)
return true;
foreach (var condition in conditions)
{
if (!TryCondition(target, condition))
return false;
}
return true;
}
///
/// Checks a list of conditions to see if any are true.
///
/// Target entity we're checking conditions on
/// Conditions we're checking
/// Returns true if any conditions return true
public bool TryAnyCondition(EntityUid target, EntityCondition[]? conditions)
{
// If there's no conditions we can't meet any of them...
if (conditions == null)
return false;
foreach (var condition in conditions)
{
if (TryCondition(target, condition))
return true;
}
return false;
}
///
/// Checks a single on an entity.
///
/// Target entity we're checking conditions on
/// Condition we're checking
/// Returns true if we meet the condition and false otherwise
public bool TryCondition(EntityUid target, EntityCondition condition)
{
return condition.Inverted != condition.RaiseEvent(target, this);
}
///
/// Raises a condition to an entity. You should not be calling this unless you know what you're doing.
///
public bool RaiseConditionEvent(EntityUid target, T effect) where T : EntityConditionBase
{
var effectEv = new EntityConditionEvent(effect);
RaiseLocalEvent(target, ref effectEv);
return effectEv.Result;
}
}
///
/// This is a basic abstract entity effect containing all the data an entity effect needs to affect entities with effects...
///
/// The Component that is required for the effect
/// The Condition we're testing
public abstract partial class EntityConditionSystem : EntitySystem where T : Component where TCon : EntityConditionBase
{
///
public override void Initialize()
{
SubscribeLocalEvent>(Condition);
}
protected abstract void Condition(Entity entity, ref EntityConditionEvent args);
}
///
/// Used to raise an EntityCondition without losing the type of condition.
///
public interface IEntityConditionRaiser
{
bool RaiseConditionEvent(EntityUid target, T effect) where T : EntityConditionBase;
}
///
/// Used to store an so it can be raised without losing the type of the condition.
///
/// The Condition wer are raising.
public abstract partial class EntityConditionBase : EntityCondition where T : EntityConditionBase
{
public override bool RaiseEvent(EntityUid target, IEntityConditionRaiser raiser)
{
if (this is not T type)
return false;
// If the result of the event matches the result we're looking for then we pass.
return raiser.RaiseConditionEvent(target, type);
}
}
///
/// A basic condition which can be checked for on an entity via events.
///
[ImplicitDataDefinitionForInheritors]
public abstract partial class EntityCondition
{
public abstract bool RaiseEvent(EntityUid target, IEntityConditionRaiser raiser);
///
/// If true, invert the result. So false returns true and true returns false!
///
[DataField]
public bool Inverted;
///
/// A basic description of this condition, which displays in the guidebook.
///
public abstract string EntityConditionGuidebookText(IPrototypeManager prototype);
}
///
/// An Event carrying an entity effect.
///
/// The Condition we're checking
[ByRefEvent]
public record struct EntityConditionEvent(T Condition) where T : EntityConditionBase
{
///
/// The result of our check, defaults to false if nothing handles it.
///
[DataField]
public bool Result;
///
/// The Condition being raised in this event
///
public readonly T Condition = Condition;
}