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; }