using Content.Shared.EntityTable.Conditions; using JetBrains.Annotations; using Robust.Shared.Prototypes; namespace Content.Shared.EntityTable.EntitySelectors; /// /// Base type for entity table selector that can have child selectors and properly apply entity table conditions for them. /// [ImplicitDataDefinitionForInheritors, UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public abstract partial class EntityTableSelectorWithChildrenBase : EntityTableSelectorWithNestedBase { /// /// The child entries of this selector. /// [DataField(required: true)] public List Children = new(); /// public override bool CheckConditions(IEntityManager entMan, IPrototypeManager proto, EntityTableContext ctx) { if (!base.CheckConditions(entMan, proto, ctx)) return false; using var scoped = ScopedConditions(ctx); foreach (var selector in Children) { // If any child succeeds this is a valid node if (selector.CheckConditions(entMan, proto, ctx)) return true; } return false; } } /// /// Base type for entity table selector that can apply list of additional entity table conditions upon /// nested entity table selectors. /// When making type, derived from this one, please remember to use in your implementation of /// , and inside /// (if you override it). /// [ImplicitDataDefinitionForInheritors, UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public abstract partial class EntityTableSelectorWithNestedBase : EntityTableSelector { /// /// A list of conditions that must evaluate to 'true' for the selector to apply. /// [DataField] public List ConditionsForChildren = new(); /// /// Temporarily injects (merged with any conditions already /// scoped in ) into the context so they are evaluated for every selector /// below this one. The context is restored when the returned handle is disposed. /// protected IDisposable ScopedConditions(EntityTableContext ctx) { if (ConditionsForChildren.Count == 0) return default(ScopedConditionsRestore); if (!ctx.TryGetData>(AdditionalConditionsKey, out var existingConditions)) { // Nothing was scoped before: our own list can be used directly, no copy. ctx.SetData(AdditionalConditionsKey, ConditionsForChildren); return new ScopedConditionsRestore(ctx, null); } // Merge our conditions with whatever is already scoped in the context. var combined = new List(ConditionsForChildren); combined.AddRange(existingConditions); ctx.SetData(AdditionalConditionsKey, combined); return new ScopedConditionsRestore(ctx, existingConditions); } /// /// Restores the after a scoped-conditions block. /// protected readonly struct ScopedConditionsRestore( EntityTableContext? ctx, List? existingConditions ) : IDisposable { public void Dispose() { if(ctx == null) return; if (existingConditions is null) ctx.RemoveData(AdditionalConditionsKey); else ctx.SetData(AdditionalConditionsKey, existingConditions); } } }