]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add condition support to entity tables (#36819)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Fri, 2 May 2025 08:37:14 +0000 (04:37 -0400)
committerGitHub <noreply@github.com>
Fri, 2 May 2025 08:37:14 +0000 (11:37 +0300)
Content.Shared/EntityTable/Conditions/EntityTableCondition.cs [new file with mode: 0644]
Content.Shared/EntityTable/Conditions/PlayerCountCondition.cs [new file with mode: 0644]
Content.Shared/EntityTable/EntitySelectors/EntityTableSelector.cs
Content.Shared/EntityTable/EntitySelectors/GroupSelector.cs
Resources/Prototypes/Catalog/Fills/Lockers/heads.yml

diff --git a/Content.Shared/EntityTable/Conditions/EntityTableCondition.cs b/Content.Shared/EntityTable/Conditions/EntityTableCondition.cs
new file mode 100644 (file)
index 0000000..9505b65
--- /dev/null
@@ -0,0 +1,28 @@
+using Content.Shared.EntityTable.EntitySelectors;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityTable.Conditions;
+
+/// <summary>
+/// Used for implementing conditional logic for <see cref="EntityTableSelector"/>.
+/// </summary>
+[ImplicitDataDefinitionForInheritors, UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
+public abstract partial class EntityTableCondition
+{
+    /// <summary>
+    /// If true, inverts the result of the condition.
+    /// </summary>
+    [DataField]
+    public bool Invert;
+
+    public bool Evaluate(IEntityManager entMan, IPrototypeManager proto)
+    {
+        var res = EvaluateImplementation(entMan, proto);
+
+        // XOR eval to invert the result.
+        return res ^ Invert;
+    }
+
+    public abstract bool EvaluateImplementation(IEntityManager entMan, IPrototypeManager proto);
+}
diff --git a/Content.Shared/EntityTable/Conditions/PlayerCountCondition.cs b/Content.Shared/EntityTable/Conditions/PlayerCountCondition.cs
new file mode 100644 (file)
index 0000000..72ac75c
--- /dev/null
@@ -0,0 +1,34 @@
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityTable.Conditions;
+
+/// <summary>
+/// Condition that passes only if the server player count is within a certain range.
+/// </summary>
+public sealed partial class PlayerCountCondition : EntityTableCondition
+{
+    /// <summary>
+    /// Minimum players of needed for this condition to succeed. Inclusive.
+    /// </summary>
+    [DataField]
+    public int Min = int.MinValue;
+
+    /// <summary>
+    /// Maximum numbers of players there can be for this condition to succeed. Inclusive.
+    /// </summary>
+    [DataField]
+    public int Max = int.MaxValue;
+
+    private static ISharedPlayerManager? _playerManager;
+
+    public override bool EvaluateImplementation(IEntityManager entMan, IPrototypeManager proto)
+    {
+        // Don't resolve this repeatedly
+        _playerManager ??= IoCManager.Resolve<ISharedPlayerManager>();
+
+        var playerCount = _playerManager.PlayerCount;
+
+        return playerCount >= Min && playerCount <= Max;
+    }
+}
index d0a7a4d57898695117ab5c98b0b45e7aee227b0b..6ad80c4a2a07159b2d4cffc15726bc1619e2e35e 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Shared.EntityTable.Conditions;
 using Content.Shared.EntityTable.ValueSelector;
 using JetBrains.Annotations;
 using Robust.Shared.Prototypes;
@@ -26,10 +27,26 @@ public abstract partial class EntityTableSelector
     [DataField]
     public double Prob = 1;
 
+    /// <summary>
+    /// A list of conditions that must evaluate to 'true' for the selector to apply.
+    /// </summary>
+    [DataField]
+    public List<EntityTableCondition> Conditions = new();
+
+    /// <summary>
+    /// If true, all the conditions must be successful in order for the selector to process.
+    /// Otherwise, only one of them must be.
+    /// </summary>
+    [DataField]
+    public bool RequireAll = true;
+
     public IEnumerable<EntProtoId> GetSpawns(System.Random rand,
         IEntityManager entMan,
         IPrototypeManager proto)
     {
+        if (!CheckConditions(entMan, proto))
+            yield break;
+
         var rolls = Rolls.Get(rand);
         for (var i = 0; i < rolls; i++)
         {
@@ -43,6 +60,28 @@ public abstract partial class EntityTableSelector
         }
     }
 
+    public bool CheckConditions(IEntityManager entMan, IPrototypeManager proto)
+    {
+        if (Conditions.Count == 0)
+            return true;
+
+        var success = false;
+        foreach (var condition in Conditions)
+        {
+            var res = condition.Evaluate(entMan, proto);
+
+            if (RequireAll && !res)
+                return false; // intentional break out of loop and function
+
+            success |= res;
+        }
+
+        if (RequireAll)
+            return true;
+
+        return success;
+    }
+
     protected abstract IEnumerable<EntProtoId> GetSpawnsImplementation(System.Random rand,
         IEntityManager entMan,
         IPrototypeManager proto);
index 8f761f9866eb7041a4cf35329169c611d9eabd2c..8012eeae684024ca2c040ab328d51dc16a8f37af 100644 (file)
@@ -18,6 +18,10 @@ public sealed partial class GroupSelector : EntityTableSelector
         var children = new Dictionary<EntityTableSelector, float>(Children.Count);
         foreach (var child in Children)
         {
+            // Don't include invalid groups
+            if (!child.CheckConditions(entMan, proto))
+                continue;
+
             children.Add(child, child.Weight);
         }
 
index 6770680b6372ed3e7cd5e97366e75e5436d571ec..05c362ac6abb87d80b0defb4d1f8e2fcfdf5e823 100644 (file)
     - id: SpaceCash1000
     - id: WeaponDisabler
     - id: ClothingEyesGlassesCommand
+    - id: HeadSkeleton # A skull to accompany your skeleton crew
+      conditions:
+      - !type:PlayerCountCondition
+        max: 15
 
 # No laser table + Laser table
 - type: entityTable