SubscribeLocalEvent<AirtightComponent, ComponentShutdown>(OnAirtightShutdown);
SubscribeLocalEvent<AirtightComponent, AnchorStateChangedEvent>(OnAirtightPositionChanged);
SubscribeLocalEvent<AirtightComponent, ReAnchorEvent>(OnAirtightReAnchor);
- SubscribeLocalEvent<AirtightComponent, MoveEvent>(OnAirtightRotated);
+ SubscribeLocalEvent<AirtightComponent, MoveEvent>(OnAirtightMoved);
}
private void OnAirtightInit(EntityUid uid, AirtightComponent airtight, ComponentInit args)
if (airtight.FixAirBlockedDirectionInitialize)
{
var moveEvent = new MoveEvent(uid, default, default, Angle.Zero, xform.LocalRotation, xform, false);
- if (AirtightRotate(uid, airtight, ref moveEvent))
+ if (AirtightMove(uid, airtight, ref moveEvent))
return;
}
}
}
- private void OnAirtightRotated(EntityUid uid, AirtightComponent airtight, ref MoveEvent ev)
+ private void OnAirtightMoved(EntityUid uid, AirtightComponent airtight, ref MoveEvent ev)
{
- AirtightRotate(uid, airtight, ref ev);
+ AirtightMove(uid, airtight, ref ev);
}
- private bool AirtightRotate(EntityUid uid, AirtightComponent airtight, ref MoveEvent ev)
+ private bool AirtightMove(EntityUid uid, AirtightComponent airtight, ref MoveEvent ev)
{
if (!airtight.RotateAirBlocked || airtight.InitialAirBlockedDirection == (int)AtmosDirection.Invalid)
return false;
airtight.CurrentAirBlockedDirection = (int) Rotate((AtmosDirection)airtight.InitialAirBlockedDirection, ev.NewRotation);
+ var pos = airtight.LastPosition;
UpdatePosition(airtight, ev.Component);
- var airtightEv = new AirtightChanged(uid, airtight);
+ var airtightEv = new AirtightChanged(uid, airtight, pos);
RaiseLocalEvent(uid, ref airtightEv, true);
return true;
}
if (airtight.AirBlocked == airblocked)
return;
- if (!Resolve(airtight.Owner, ref xform)) return;
+ if (!Resolve(uid, ref xform))
+ return;
+ var pos = airtight.LastPosition;
airtight.AirBlocked = airblocked;
UpdatePosition(airtight, xform);
- var airtightEv = new AirtightChanged(uid, airtight);
+ var airtightEv = new AirtightChanged(uid, airtight, pos);
RaiseLocalEvent(uid, ref airtightEv, true);
}
}
[ByRefEvent]
- public readonly record struct AirtightChanged(EntityUid Entity, AirtightComponent Airtight);
+ public readonly record struct AirtightChanged(EntityUid Entity, AirtightComponent Airtight,
+ (EntityUid Grid, Vector2i Tile) Position);
}
if (overflow.Volume == FixedPoint2.Zero)
{
- RemCompDeferred<EdgeSpreaderComponent>(uid);
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
continue;
args.Updates--;
- EnsureComp<EdgeSpreaderComponent>(neighbor);
+ EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
if (args.Updates <= 0)
break;
if (overflow.Volume == FixedPoint2.Zero)
{
- RemCompDeferred<EdgeSpreaderComponent>(uid);
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
}
break;
}
- RemCompDeferred<EdgeSpreaderComponent>(uid);
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
if (!_solutionContainerSystem.TryAddSolution(neighbor, neighborSolution, split))
continue;
- EnsureComp<EdgeSpreaderComponent>(neighbor);
+ EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
args.Updates--;
if (args.Updates <= 0)
if (checkForOverflow && IsOverflowing(puddleUid, puddleComponent))
{
- EnsureComp<EdgeSpreaderComponent>(puddleUid);
+ EnsureComp<ActiveEdgeSpreaderComponent>(puddleUid);
}
if (!sound)
if (TryAddSolution(ent.Value, solution, sound, puddleComponent: puddle))
{
- EnsureComp<EdgeSpreaderComponent>(ent.Value);
+ EnsureComp<ActiveEdgeSpreaderComponent>(ent.Value);
}
puddleUid = ent.Value;
EnsureComp<PuddleComponent>(puddleUid);
if (TryAddSolution(puddleUid, solution, sound))
{
- EnsureComp<EdgeSpreaderComponent>(puddleUid);
+ EnsureComp<ActiveEdgeSpreaderComponent>(puddleUid);
}
return true;
}
using System.Linq;
-using Content.Server.Administration.Logs;
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Chemistry.ReactionEffects;
using Content.Server.Spreader;
-using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Coordinates.Helpers;
-using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Smoking;
-using Robust.Shared.Spawners;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
using Robust.Shared.Spawners;
using Robust.Shared.Timing;
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
/// <inheritdoc/>
public override void Initialize()
SubscribeLocalEvent<SmokeComponent, ReactionAttemptEvent>(OnReactionAttempt);
SubscribeLocalEvent<SmokeComponent, SpreadNeighborsEvent>(OnSmokeSpread);
SubscribeLocalEvent<SmokeDissipateSpawnComponent, TimedDespawnEvent>(OnSmokeDissipate);
- SubscribeLocalEvent<SpreadGroupUpdateRate>(OnSpreadUpdateRate);
- }
-
- private void OnSpreadUpdateRate(ref SpreadGroupUpdateRate ev)
- {
- if (ev.Name != "smoke")
- return;
-
- ev.UpdatesPerSecond = 8;
}
private void OnSmokeDissipate(EntityUid uid, SmokeDissipateSpawnComponent component, ref TimedDespawnEvent args)
private void OnSmokeSpread(EntityUid uid, SmokeComponent component, ref SpreadNeighborsEvent args)
{
- if (component.SpreadAmount == 0 ||
- !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution) ||
- args.NeighborFreeTiles.Count == 0)
+ if (component.SpreadAmount == 0
+ || !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution))
{
- RemCompDeferred<EdgeSpreaderComponent>(uid);
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
if (prototype == null)
{
- RemCompDeferred<EdgeSpreaderComponent>(uid);
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
TryComp<TimedDespawnComponent>(uid, out var timer);
+ _appearance.TryGetData(uid, SmokeVisuals.Color, out var color);
- var smokePerSpread = component.SpreadAmount / args.NeighborFreeTiles.Count;
- component.SpreadAmount -= smokePerSpread;
-
+ // wtf is the logic behind any of this.
+ var smokePerSpread = 1 + component.SpreadAmount / Math.Max(1, args.NeighborFreeTiles.Count);
foreach (var neighbor in args.NeighborFreeTiles)
{
var coords = neighbor.Grid.GridTileToLocal(neighbor.Tile);
- var ent = Spawn(prototype.ID, coords.SnapToGrid());
+ var ent = Spawn(prototype.ID, coords);
var neighborSmoke = EnsureComp<SmokeComponent>(ent);
- neighborSmoke.SpreadAmount = Math.Max(0, smokePerSpread - 1);
+ neighborSmoke.SpreadAmount = Math.Max(0, smokePerSpread - 2); // why - 2? who the fuck knows.
+ component.SpreadAmount--;
args.Updates--;
// Listen this is the old behaviour iunno
Start(ent, neighborSmoke, solution.Clone(), timer?.Lifetime ?? 10f);
- if (_appearance.TryGetData(uid, SmokeVisuals.Color, out var color))
- {
+ if (color != null)
_appearance.SetData(ent, SmokeVisuals.Color, color);
- }
- // Only 1 spread then ig?
- if (smokePerSpread == 0)
+ if (component.SpreadAmount == 0)
{
- component.SpreadAmount--;
-
- if (component.SpreadAmount == 0)
- {
- RemCompDeferred<EdgeSpreaderComponent>(uid);
- break;
- }
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+ break;
}
if (args.Updates <= 0)
break;
}
- // Give our spread to neighbor tiles.
- if (args.NeighborFreeTiles.Count == 0 && args.Neighbors.Count > 0 && component.SpreadAmount > 0)
- {
- var smokeQuery = GetEntityQuery<SmokeComponent>();
+ if (args.NeighborFreeTiles.Count > 0 || args.Neighbors.Count == 0 || component.SpreadAmount < 1)
+ return;
- foreach (var neighbor in args.Neighbors)
- {
- if (!smokeQuery.TryGetComponent(neighbor, out var smoke))
- continue;
+ // We have no more neighbours to spread to. So instead we will randomly distribute our volume to neighbouring smoke tiles.
+
+ var smokeQuery = GetEntityQuery<SmokeComponent>();
- smoke.SpreadAmount++;
- args.Updates--;
+ _random.Shuffle(args.Neighbors);
+ foreach (var neighbor in args.Neighbors)
+ {
+ if (!smokeQuery.TryGetComponent(neighbor, out var smoke))
+ continue;
- if (component.SpreadAmount == 0)
- {
- RemCompDeferred<EdgeSpreaderComponent>(uid);
- break;
- }
+ smoke.SpreadAmount++;
+ args.Updates--;
+ component.SpreadAmount--;
+ EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
- if (args.Updates <= 0)
- break;
+ if (component.SpreadAmount == 0)
+ {
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+ break;
}
}
}
public void Start(EntityUid uid, SmokeComponent component, Solution solution, float duration)
{
TryAddSolution(uid, component, solution);
- EnsureComp<EdgeSpreaderComponent>(uid);
+ EnsureComp<ActiveEdgeSpreaderComponent>(uid);
var timer = EnsureComp<TimedDespawnComponent>(uid);
timer.Lifetime = duration;
}
AMEngine,
Pipe,
WireNet,
- Spreader,
/// <summary>
/// Group used by the TEG.
--- /dev/null
+namespace Content.Server.Spreader;
+
+/// <summary>
+/// Added to entities being considered for spreading via <see cref="SpreaderSystem"/>.
+/// This needs to be manually added and removed.
+/// </summary>
+[RegisterComponent]
+public sealed partial class ActiveEdgeSpreaderComponent : Component
+{
+}
+using Content.Shared.Spreader;
+using Robust.Shared.Prototypes;
+
namespace Content.Server.Spreader;
/// <summary>
-/// Added to entities being considered for spreading via <see cref="SpreaderSystem"/>.
-/// This needs to be manually added and removed.
+/// Entity capable of becoming cloning and replicating itself to adjacent edges. See <see cref="SpreaderSystem"/>
/// </summary>
[RegisterComponent, Access(typeof(SpreaderSystem))]
public sealed partial class EdgeSpreaderComponent : Component
{
+ [DataField(required:true)]
+ public ProtoId<EdgeSpreaderPrototype> Id;
}
[RegisterComponent, Access(typeof(KudzuSystem))]
public sealed partial class GrowingKudzuComponent : Component
{
- /// <summary>
- /// At level 3 spreading can occur; prior to that we have a chance of increasing our growth level and changing our sprite.
- /// </summary>
- [DataField("growthLevel")]
- public int GrowthLevel = 1;
-
/// <summary>
/// The next time kudzu will try to tick its growth level.
/// </summary>
[RegisterComponent]
public sealed partial class KudzuComponent : Component
{
+ /// <summary>
+ /// At level 3 spreading can occur; prior to that we have a chance of increasing our growth level and changing our sprite.
+ /// </summary>
+ [DataField]
+ public int GrowthLevel = 1;
+
/// <summary>
/// Chance to spread whenever an edge spread is possible.
/// </summary>
using Content.Shared.Spreader;
using Robust.Shared.Random;
using Robust.Shared.Timing;
+using Robust.Shared.Utility;
namespace Content.Server.Spreader;
[Dependency] private readonly DamageableSystem _damageable = default!;
[ValidatePrototypeId<EdgeSpreaderPrototype>]
- private const string KudzuGroup = "kudzu";
+ private const string KudzuGroup = "Kudzu";
/// <inheritdoc/>
public override void Initialize()
SubscribeLocalEvent<KudzuComponent, ComponentStartup>(SetupKudzu);
SubscribeLocalEvent<KudzuComponent, SpreadNeighborsEvent>(OnKudzuSpread);
SubscribeLocalEvent<GrowingKudzuComponent, EntityUnpausedEvent>(OnKudzuUnpaused);
- SubscribeLocalEvent<SpreadGroupUpdateRate>(OnKudzuUpdateRate);
SubscribeLocalEvent<KudzuComponent, DamageChangedEvent>(OnDamageChanged);
}
if (!TryComp(uid, out growing))
{
growing = AddComp<GrowingKudzuComponent>(uid);
- growing.GrowthLevel = 3;
+ component.GrowthLevel = 3;
}
- growing.GrowthLevel = Math.Max(1, growing.GrowthLevel - growthDamage);
+ component.GrowthLevel = Math.Max(1, component.GrowthLevel - growthDamage);
if (EntityManager.TryGetComponent<AppearanceComponent>(uid, out var appearance))
{
- _appearance.SetData(uid, KudzuVisuals.GrowthLevel, growing.GrowthLevel, appearance);
+ _appearance.SetData(uid, KudzuVisuals.GrowthLevel, component.GrowthLevel, appearance);
}
}
}
private void OnKudzuSpread(EntityUid uid, KudzuComponent component, ref SpreadNeighborsEvent args)
{
- if (TryComp<GrowingKudzuComponent>(uid, out var growing) && growing.GrowthLevel < 3)
- {
+ if (component.GrowthLevel < 3)
return;
- }
if (args.NeighborFreeTiles.Count == 0)
{
- RemCompDeferred<EdgeSpreaderComponent>(uid);
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
+ if (!_robustRandom.Prob(component.SpreadChance))
+ return;
+
var prototype = MetaData(uid).EntityPrototype?.ID;
if (prototype == null)
{
- RemCompDeferred<EdgeSpreaderComponent>(uid);
+ RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
- if (!_robustRandom.Prob(component.SpreadChance))
- return;
-
foreach (var neighbor in args.NeighborFreeTiles)
{
var neighborUid = Spawn(prototype, neighbor.Grid.GridTileToLocal(neighbor.Tile));
- EnsureComp<EdgeSpreaderComponent>(neighborUid);
+ DebugTools.Assert(HasComp<EdgeSpreaderComponent>(neighborUid));
+ DebugTools.Assert(HasComp<ActiveEdgeSpreaderComponent>(neighborUid));
+ DebugTools.Assert(Comp<EdgeSpreaderComponent>(neighborUid).Id == KudzuGroup);
args.Updates--;
-
if (args.Updates <= 0)
return;
}
}
- private void OnKudzuUpdateRate(ref SpreadGroupUpdateRate args)
- {
- if (args.Name != KudzuGroup)
- return;
-
- args.UpdatesPerSecond = 1;
- }
-
private void OnKudzuUnpaused(EntityUid uid, GrowingKudzuComponent component, ref EntityUnpausedEvent args)
{
component.NextTick += args.PausedTime;
public override void Update(float frameTime)
{
var appearanceQuery = GetEntityQuery<AppearanceComponent>();
- var query = EntityQueryEnumerator<GrowingKudzuComponent, KudzuComponent>();
+ var query = EntityQueryEnumerator<GrowingKudzuComponent>();
+ var kudzuQuery = GetEntityQuery<KudzuComponent>();
+ var damageableQuery = GetEntityQuery<DamageableComponent>();
var curTime = _timing.CurTime;
- while (query.MoveNext(out var uid, out var grow, out var kudzu))
+ while (query.MoveNext(out var uid, out var grow))
{
if (grow.NextTick > curTime)
- {
continue;
- }
grow.NextTick = curTime + TimeSpan.FromSeconds(0.5);
+ if (!kudzuQuery.TryGetComponent(uid, out var kudzu))
+ {
+ RemCompDeferred(uid, grow);
+ continue;
+ }
+
if (!_robustRandom.Prob(kudzu.GrowthTickChance))
{
continue;
}
- if (TryComp<DamageableComponent>(uid, out var damage))
+ if (damageableQuery.TryGetComponent(uid, out var damage))
{
if (damage.TotalDamage > 1.0)
{
}
}
- grow.GrowthLevel += 1;
+ kudzu.GrowthLevel += 1;
- if (grow.GrowthLevel >= 3)
+ if (kudzu.GrowthLevel >= 3)
{
// why cache when you can simply cease to be? Also saves a bit of memory/time.
- RemCompDeferred<GrowingKudzuComponent>(uid);
+ RemCompDeferred(uid, grow);
}
if (appearanceQuery.TryGetComponent(uid, out var appearance))
{
- _appearance.SetData(uid, KudzuVisuals.GrowthLevel, grow.GrowthLevel, appearance);
+ _appearance.SetData(uid, KudzuVisuals.GrowthLevel, kudzu.GrowthLevel, appearance);
}
}
}
+++ /dev/null
-namespace Content.Server.Spreader;
-
-/// <summary>
-/// Raised every tick to determine how many updates a particular spreading node group is allowed.
-/// </summary>
-[ByRefEvent]
-public record struct SpreadGroupUpdateRate(string Name, int UpdatesPerSecond = 16);
+++ /dev/null
-using Content.Server.NodeContainer;
-using Content.Server.NodeContainer.EntitySystems;
-using Content.Server.NodeContainer.Nodes;
-using Robust.Shared.Map.Components;
-
-namespace Content.Server.Spreader;
-
-/// <summary>
-/// Handles the node for <see cref="EdgeSpreaderComponent"/>.
-/// Functions as a generic tile-based entity spreader for systems such as puddles or smoke.
-/// </summary>
-public sealed partial class SpreaderNode : Node
-{
- // [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
-
- /// <inheritdoc/>
- public override IEnumerable<Node> GetReachableNodes(TransformComponent xform, EntityQuery<NodeContainerComponent> nodeQuery, EntityQuery<TransformComponent> xformQuery,
- MapGridComponent? grid, IEntityManager entMan)
- {
- if (grid == null)
- yield break;
-
- entMan.System<SpreaderSystem>().GetNeighbors(xform.Owner, Name, out _, out _, out var neighbors);
-
- var _nodeContainer = entMan.System<NodeContainerSystem>();
-
- foreach (var neighbor in neighbors)
- {
- if (!nodeQuery.TryGetComponent(neighbor, out var nodeContainer) ||
- !_nodeContainer.TryGetNode<SpreaderNode>(nodeContainer, Name, out var neighborNode))
- {
- continue;
- }
-
- yield return neighborNode;
- }
- }
-}
+++ /dev/null
-using Content.Server.NodeContainer.NodeGroups;
-using Content.Server.NodeContainer.Nodes;
-
-namespace Content.Server.Spreader;
-
-[NodeGroup(NodeGroupID.Spreader)]
-public sealed class SpreaderNodeGroup : BaseNodeGroup
-{
- private IEntityManager _entManager = default!;
-
- /// <inheritdoc/>
- public override void Initialize(Node sourceNode, IEntityManager entMan)
- {
- base.Initialize(sourceNode, entMan);
- _entManager = entMan;
- }
-
- /// <inheritdoc/>
- public override void RemoveNode(Node node)
- {
- base.RemoveNode(node);
-
- foreach (var neighborNode in node.ReachableNodes)
- {
- if (_entManager.Deleted(neighborNode.Owner))
- continue;
-
- _entManager.EnsureComponent<EdgeSpreaderComponent>(neighborNode.Owner);
- }
- }
-}
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
-using Content.Server.NodeContainer;
-using Content.Server.NodeContainer.EntitySystems;
-using Content.Server.NodeContainer.NodeGroups;
using Content.Server.Shuttles.Components;
using Content.Shared.Atmos;
using Content.Shared.Spreader;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
+using Robust.Shared.Utility;
namespace Content.Server.Spreader;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
- [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
- private static readonly TimeSpan SpreadCooldown = TimeSpan.FromSeconds(1);
+ private static readonly TimeSpan SpreadCooldown = TimeSpan.FromSeconds(SpreadCooldownSeconds);
- private readonly List<string> _spreaderGroups = new();
+ /// <summary>
+ /// Cached maximum number of updates per spreader prototype. This is applied per-grid.
+ /// </summary>
+ private Dictionary<string, int> _prototypeUpdates = default!;
+
+ /// <summary>
+ /// Remaining number of updates per grid & prototype.
+ /// </summary>
+ private Dictionary<EntityUid, Dictionary<string, int>> _gridUpdates = new();
+
+ private const float SpreadCooldownSeconds = 1;
[ValidatePrototypeId<TagPrototype>]
private const string IgnoredTag = "SpreaderIgnore";
SubscribeLocalEvent<SpreaderGridComponent, EntityUnpausedEvent>(OnGridUnpaused);
+ SubscribeLocalEvent<EdgeSpreaderComponent, EntityTerminatingEvent>(OnTerminating);
SetupPrototypes();
_prototype.PrototypesReloaded += OnPrototypeReload;
}
private void SetupPrototypes()
{
- _spreaderGroups.Clear();
-
- foreach (var id in _prototype.EnumeratePrototypes<EdgeSpreaderPrototype>())
+ _prototypeUpdates = new Dictionary<string, int>();
+ foreach (var proto in _prototype.EnumeratePrototypes<EdgeSpreaderPrototype>())
{
- _spreaderGroups.Add(id.ID);
+ _prototypeUpdates.Add(proto.ID, proto.UpdatesPerSecond);
}
}
private void OnAirtightChanged(ref AirtightChanged ev)
{
- var neighbors = GetNeighbors(ev.Entity, ev.Airtight);
+ var neighbors = GetSpreadableNeighbors(ev.Entity, ev.Airtight, ev.Position);
foreach (var neighbor in neighbors)
{
- EnsureComp<EdgeSpreaderComponent>(neighbor);
+ EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
}
}
private void OnGridInit(GridInitializeEvent ev)
{
- var comp = EnsureComp<SpreaderGridComponent>(ev.EntityUid);
+ EnsureComp<SpreaderGridComponent>(ev.EntityUid);
+ }
+
+ private void OnTerminating(EntityUid uid, EdgeSpreaderComponent component, ref EntityTerminatingEvent args)
+ {
+ var neighbors = GetSpreadableNeighbors(uid);
+
+ foreach (var neighbor in neighbors)
+ {
+ EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
+ }
}
/// <inheritdoc/>
var curTime = _timing.CurTime;
// Check which grids are valid for spreading
- var spreadable = new ValueList<EntityUid>();
var spreadGrids = EntityQueryEnumerator<SpreaderGridComponent>();
+ _gridUpdates.Clear();
while (spreadGrids.MoveNext(out var uid, out var grid))
{
if (grid.NextUpdate > curTime)
continue;
- spreadable.Add(uid);
+ _gridUpdates[uid] = _prototypeUpdates.ShallowClone();
grid.NextUpdate += SpreadCooldown;
}
- if (spreadable.Count == 0)
+ if (_gridUpdates.Count == 0)
return;
- var query = EntityQueryEnumerator<EdgeSpreaderComponent>();
- var nodeQuery = GetEntityQuery<NodeContainerComponent>();
- var xformQuery = GetEntityQuery<TransformComponent>();
- var gridQuery = GetEntityQuery<SpreaderGridComponent>();
-
- // Each INode group has a certain number of updates
- // allowed per SpreadCooldown
- var groupUpdates = new Dictionary<INodeGroup, int>();
+ var query = EntityQueryEnumerator<ActiveEdgeSpreaderComponent>();
+ var xforms = GetEntityQuery<TransformComponent>();
+ var spreaderQuery = GetEntityQuery<EdgeSpreaderComponent>();
- var spreaders = new List<(EntityUid Uid, EdgeSpreaderComponent Comp)>(Count<EdgeSpreaderComponent>());
+ var spreaders = new List<(EntityUid Uid, ActiveEdgeSpreaderComponent Comp)>(Count<ActiveEdgeSpreaderComponent>());
// Build a list of all existing Edgespreaders, shuffle them
while (query.MoveNext(out var uid, out var comp))
// that doesn't meet a few trivial prerequisites
foreach (var (uid, comp) in spreaders)
{
- if (!xformQuery.TryGetComponent(uid, out var xform) ||
- xform.GridUid == null ||
- !gridQuery.HasComponent(xform.GridUid.Value))
- {
- RemCompDeferred<EdgeSpreaderComponent>(uid);
+ // Get xform first, as entity may have been deleted due to interactions triggered by other spreaders.
+ if (!xforms.TryGetComponent(uid, out var xform))
continue;
- }
- foreach (var sGroup in _spreaderGroups)
+ if (xform.GridUid == null)
{
- // Get the NodeContainer and Node from every EdgeSpreader entity found
- if (!nodeQuery.TryGetComponent(uid, out var nodeContainer))
- {
- RemCompDeferred<EdgeSpreaderComponent>(uid);
- continue;
- }
-
- if (!_nodeContainer.TryGetNode<SpreaderNode>(nodeContainer, sGroup, out var node))
- {
- continue;
- }
+ RemComp(uid, comp);
+ continue;
+ }
- // Not allowed this tick?
- if (node.NodeGroup == null ||
- !spreadable.Contains(xform.GridUid.Value))
- {
- continue;
- }
+ if (!_gridUpdates.TryGetValue(xform.GridUid.Value, out var groupUpdates))
+ continue;
- // Try get an integer update rate associated with a node group,
- // getting it instead from the spreader itself on failure
- if (!groupUpdates.TryGetValue(node.NodeGroup, out var updates))
- {
- var spreadEv = new SpreadGroupUpdateRate(node.Name);
- RaiseLocalEvent(ref spreadEv);
- updates = (int) (spreadEv.UpdatesPerSecond * SpreadCooldown / TimeSpan.FromSeconds(1));
- }
+ if (!spreaderQuery.TryGetComponent(uid, out var spreader))
+ {
+ RemComp(uid, comp);
+ continue;
+ }
- // "updates" integer dictates the amount of nodes that
- // are to be spawned around a NodeGroup
- if (updates <= 0)
- {
- continue;
- }
+ if (!groupUpdates.TryGetValue(spreader.Id, out var updates) || updates < 1)
+ continue;
- // Edge detection logic is to be handled
- // by the subscribing system, see KudzuSystem
- // for a simple example
+ // Edge detection logic is to be handled
+ // by the subscribing system, see KudzuSystem
+ // for a simple example
+ Spread(uid, xform, spreader.Id, ref updates);
- Spread(uid, node, node.NodeGroup, ref updates);
- groupUpdates[node.NodeGroup] = updates;
- }
+ if (updates < 1)
+ groupUpdates.Remove(spreader.Id);
+ else
+ groupUpdates[spreader.Id] = updates;
}
}
- private void Spread(EntityUid uid, SpreaderNode node, INodeGroup group, ref int updates)
+ private void Spread(EntityUid uid, TransformComponent xform, string prototype, ref int updates)
{
- GetNeighbors(uid, node.Name, out var freeTiles, out _, out var neighbors);
+ GetNeighbors(uid, xform, prototype, out var freeTiles, out _, out var neighbors);
var ev = new SpreadNeighborsEvent()
{
/// <summary>
/// Gets the neighboring node data for the specified entity and the specified node group.
/// </summary>
- public void GetNeighbors(EntityUid uid, string groupName, out ValueList<(MapGridComponent Grid, Vector2i Tile)> freeTiles, out ValueList<Vector2i> occupiedTiles, out ValueList<EntityUid> neighbors)
+ public void GetNeighbors(EntityUid uid, TransformComponent transform, string prototype, out ValueList<(MapGridComponent Grid, Vector2i Tile)> freeTiles, out ValueList<Vector2i> occupiedTiles, out ValueList<EntityUid> neighbors)
{
+ // TODO remove occupiedTiles -- its currently unused and just slows this method down.
+ DebugTools.Assert(_prototype.HasIndex<EdgeSpreaderPrototype>(prototype));
freeTiles = new ValueList<(MapGridComponent Grid, Vector2i Tile)>();
occupiedTiles = new ValueList<Vector2i>();
neighbors = new ValueList<EntityUid>();
- if (!EntityManager.TryGetComponent<TransformComponent>(uid, out var transform))
- return;
-
if (!_mapManager.TryGetGrid(transform.GridUid, out var grid))
return;
var tile = grid.TileIndicesFor(transform.Coordinates);
- var nodeQuery = GetEntityQuery<NodeContainerComponent>();
+ var spreaderQuery = GetEntityQuery<EdgeSpreaderComponent>();
var airtightQuery = GetEntityQuery<AirtightComponent>();
var dockQuery = GetEntityQuery<DockingComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();
while (directionEnumerator.MoveNext(out var ent))
{
- if (!nodeQuery.TryGetComponent(ent, out var nodeContainer))
+ if (!spreaderQuery.TryGetComponent(ent, out var spreader))
continue;
- if (!nodeContainer.Nodes.ContainsKey(groupName))
+ if (spreader.Id != prototype)
continue;
neighbors.Add(ent.Value);
}
}
- public List<EntityUid> GetNeighbors(EntityUid uid, AirtightComponent comp)
+ /// <summary>
+ /// Given an entity, this returns a list of all adjacent entities with a <see cref="EdgeSpreaderComponent"/>.
+ /// </summary>
+ public List<EntityUid> GetSpreadableNeighbors(EntityUid uid, AirtightComponent? comp = null,
+ (EntityUid Grid, Vector2i Tile)? position = null)
{
+ Resolve(uid, ref comp, false);
var neighbors = new List<EntityUid>();
- if (!EntityManager.TryGetComponent<TransformComponent>(uid, out var transform))
- return neighbors; // how did we get here?
+ Vector2i tile;
+ MapGridComponent? grid;
- if (!_mapManager.TryGetGrid(transform.GridUid, out var grid))
- return neighbors;
+ if (position == null)
+ {
+ var transform = Transform(uid);
+ if (!_mapManager.TryGetGrid(transform.GridUid, out grid))
+ return neighbors;
+ tile = grid.TileIndicesFor(transform.Coordinates);
+ }
+ else
+ {
+ if (!_mapManager.TryGetGrid(position.Value.Grid, out grid))
+ return neighbors;
+ tile = position.Value.Tile;
+ }
- var tile = grid.TileIndicesFor(transform.Coordinates);
- var nodeQuery = GetEntityQuery<NodeContainerComponent>();
+ var spreaderQuery = GetEntityQuery<EdgeSpreaderComponent>();
for (var i = 0; i < Atmospherics.Directions; i++)
{
var direction = (AtmosDirection) (1 << i);
- if (!comp.AirBlockedDirection.IsFlagSet(direction))
+ if (comp != null && !comp.AirBlockedDirection.IsFlagSet(direction))
continue;
var directionEnumerator =
while (directionEnumerator.MoveNext(out var ent))
{
- if (!nodeQuery.TryGetComponent(ent, out var nodeContainer))
- continue;
-
- foreach (var name in _spreaderGroups)
- {
- if (!nodeContainer.Nodes.ContainsKey(name))
- continue;
-
+ DebugTools.Assert(Transform(ent.Value).Anchored);
+ if (spreaderQuery.HasComponent(ent))
neighbors.Add(ent.Value);
- break;
- }
}
}
public sealed class EdgeSpreaderPrototype : IPrototype
{
[IdDataField] public string ID { get; } = string.Empty;
+ [DataField(required:true)] public int UpdatesPerSecond;
}
- type: Transform
anchored: true
- type: Smoke
- - type: NodeContainer
- nodes:
- smoke:
- !type:SpreaderNode
- nodeGroupID: Spreader
+ - type: ActiveEdgeSpreader
- type: EdgeSpreader
+ id: Smoke
- type: SolutionContainerManager
solutions:
solutionArea:
layer:
- SlipLayer
- type: Smoke
- - type: NodeContainer
- nodes:
- smoke:
- !type:SpreaderNode
- nodeGroupID: Spreader
+ - type: ActiveEdgeSpreader
- type: EdgeSpreader
+ id: Smoke
- type: SolutionContainerManager
solutions:
solutionArea:
puddle: { maxVol: 1000 }
- type: Puddle
- type: Appearance
+ - type: ActiveEdgeSpreader
- type: EdgeSpreader
+ id: Puddle
- type: StepTrigger
- - type: NodeContainer
- nodes:
- puddle:
- !type:SpreaderNode
- nodeGroupID: Spreader
- type: Drink
delay: 3
transferAmount: 1
ignoreWhitelist:
components:
- IgnoreKudzu
+ - type: ActiveEdgeSpreader
- type: EdgeSpreader
- - type: NodeContainer
- nodes:
- kudzu:
- !type:SpreaderNode
- nodeGroupID: Spreader
+ id: Kudzu
- type: Food
requiredStomachs: 2 # ruminants have 4 stomachs but i dont care to give them literally 4 stomachs. 2 is good
delay: 0.5
- type: GrowingKudzu
growthTickChance: 0.3
- type: AtmosExposed
+ - type: ActiveEdgeSpreader
- type: EdgeSpreader
- - type: NodeContainer
- nodes:
- kudzu:
- !type:SpreaderNode
- nodeGroupID: Spreader
+ id: Kudzu
- type: SlowContacts
walkSpeedModifier: 0.3
sprintSpeedModifier: 0.3
- type: edgeSpreader
- id: kudzu
+ id: Kudzu
+ updatesPerSecond: 1
- type: edgeSpreader
- id: puddle
+ id: Puddle
+ updatesPerSecond: 16
- type: edgeSpreader
- id: smoke
\ No newline at end of file
+ id: Smoke
+ updatesPerSecond: 8