--- /dev/null
+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);
+}
--- /dev/null
+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;
+ }
+}
+using Content.Shared.EntityTable.Conditions;
using Content.Shared.EntityTable.ValueSelector;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
[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++)
{
}
}
+ 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);
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);
}
- 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