Remove SpreaderNodeGroup (#20230)

* Remove SpreaderNodeGroup

* Fix airtight updates

* more smoke stuff

* more smoke fixes

* wtf is smoke code

* Fix merge

* Fix divide by zero
This commit is contained in:
Leon Friedrich
2023-10-02 07:56:41 +11:00
committed by GitHub
parent 824484c6ac
commit fc034dd9d1
18 changed files with 224 additions and 322 deletions

View File

@@ -21,7 +21,7 @@ namespace Content.Server.Atmos.EntitySystems
SubscribeLocalEvent<AirtightComponent, ComponentShutdown>(OnAirtightShutdown); SubscribeLocalEvent<AirtightComponent, ComponentShutdown>(OnAirtightShutdown);
SubscribeLocalEvent<AirtightComponent, AnchorStateChangedEvent>(OnAirtightPositionChanged); SubscribeLocalEvent<AirtightComponent, AnchorStateChangedEvent>(OnAirtightPositionChanged);
SubscribeLocalEvent<AirtightComponent, ReAnchorEvent>(OnAirtightReAnchor); SubscribeLocalEvent<AirtightComponent, ReAnchorEvent>(OnAirtightReAnchor);
SubscribeLocalEvent<AirtightComponent, MoveEvent>(OnAirtightRotated); SubscribeLocalEvent<AirtightComponent, MoveEvent>(OnAirtightMoved);
} }
private void OnAirtightInit(EntityUid uid, AirtightComponent airtight, ComponentInit args) private void OnAirtightInit(EntityUid uid, AirtightComponent airtight, ComponentInit args)
@@ -31,7 +31,7 @@ namespace Content.Server.Atmos.EntitySystems
if (airtight.FixAirBlockedDirectionInitialize) if (airtight.FixAirBlockedDirectionInitialize)
{ {
var moveEvent = new MoveEvent(uid, default, default, Angle.Zero, xform.LocalRotation, xform, false); 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; return;
} }
@@ -78,19 +78,20 @@ namespace Content.Server.Atmos.EntitySystems
} }
} }
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) if (!airtight.RotateAirBlocked || airtight.InitialAirBlockedDirection == (int)AtmosDirection.Invalid)
return false; return false;
airtight.CurrentAirBlockedDirection = (int) Rotate((AtmosDirection)airtight.InitialAirBlockedDirection, ev.NewRotation); airtight.CurrentAirBlockedDirection = (int) Rotate((AtmosDirection)airtight.InitialAirBlockedDirection, ev.NewRotation);
var pos = airtight.LastPosition;
UpdatePosition(airtight, ev.Component); UpdatePosition(airtight, ev.Component);
var airtightEv = new AirtightChanged(uid, airtight); var airtightEv = new AirtightChanged(uid, airtight, pos);
RaiseLocalEvent(uid, ref airtightEv, true); RaiseLocalEvent(uid, ref airtightEv, true);
return true; return true;
} }
@@ -100,11 +101,13 @@ namespace Content.Server.Atmos.EntitySystems
if (airtight.AirBlocked == airblocked) if (airtight.AirBlocked == airblocked)
return; return;
if (!Resolve(airtight.Owner, ref xform)) return; if (!Resolve(uid, ref xform))
return;
var pos = airtight.LastPosition;
airtight.AirBlocked = airblocked; airtight.AirBlocked = airblocked;
UpdatePosition(airtight, xform); UpdatePosition(airtight, xform);
var airtightEv = new AirtightChanged(uid, airtight); var airtightEv = new AirtightChanged(uid, airtight, pos);
RaiseLocalEvent(uid, ref airtightEv, true); RaiseLocalEvent(uid, ref airtightEv, true);
} }
@@ -154,5 +157,6 @@ namespace Content.Server.Atmos.EntitySystems
} }
[ByRefEvent] [ByRefEvent]
public readonly record struct AirtightChanged(EntityUid Entity, AirtightComponent Airtight); public readonly record struct AirtightChanged(EntityUid Entity, AirtightComponent Airtight,
(EntityUid Grid, Vector2i Tile) Position);
} }

View File

@@ -103,7 +103,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (overflow.Volume == FixedPoint2.Zero) if (overflow.Volume == FixedPoint2.Zero)
{ {
RemCompDeferred<EdgeSpreaderComponent>(uid); RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return; return;
} }
@@ -137,7 +137,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
continue; continue;
args.Updates--; args.Updates--;
EnsureComp<EdgeSpreaderComponent>(neighbor); EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
if (args.Updates <= 0) if (args.Updates <= 0)
break; break;
@@ -145,7 +145,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (overflow.Volume == FixedPoint2.Zero) if (overflow.Volume == FixedPoint2.Zero)
{ {
RemCompDeferred<EdgeSpreaderComponent>(uid); RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return; return;
} }
} }
@@ -168,7 +168,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
break; break;
} }
RemCompDeferred<EdgeSpreaderComponent>(uid); RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return; return;
} }
@@ -192,7 +192,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (!_solutionContainerSystem.TryAddSolution(neighbor, neighborSolution, split)) if (!_solutionContainerSystem.TryAddSolution(neighbor, neighborSolution, split))
continue; continue;
EnsureComp<EdgeSpreaderComponent>(neighbor); EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
args.Updates--; args.Updates--;
if (args.Updates <= 0) if (args.Updates <= 0)
@@ -438,7 +438,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (checkForOverflow && IsOverflowing(puddleUid, puddleComponent)) if (checkForOverflow && IsOverflowing(puddleUid, puddleComponent))
{ {
EnsureComp<EdgeSpreaderComponent>(puddleUid); EnsureComp<ActiveEdgeSpreaderComponent>(puddleUid);
} }
if (!sound) if (!sound)
@@ -638,7 +638,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (TryAddSolution(ent.Value, solution, sound, puddleComponent: puddle)) if (TryAddSolution(ent.Value, solution, sound, puddleComponent: puddle))
{ {
EnsureComp<EdgeSpreaderComponent>(ent.Value); EnsureComp<ActiveEdgeSpreaderComponent>(ent.Value);
} }
puddleUid = ent.Value; puddleUid = ent.Value;
@@ -650,7 +650,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
EnsureComp<PuddleComponent>(puddleUid); EnsureComp<PuddleComponent>(puddleUid);
if (TryAddSolution(puddleUid, solution, sound)) if (TryAddSolution(puddleUid, solution, sound))
{ {
EnsureComp<EdgeSpreaderComponent>(puddleUid); EnsureComp<ActiveEdgeSpreaderComponent>(puddleUid);
} }
return true; return true;
} }

View File

@@ -1,23 +1,16 @@
using System.Linq; 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.Components;
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Server.Chemistry.ReactionEffects; using Content.Server.Chemistry.ReactionEffects;
using Content.Server.Spreader; using Content.Server.Spreader;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction; 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.FixedPoint;
using Content.Shared.Smoking; using Content.Shared.Smoking;
using Robust.Shared.Spawners;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Spawners; using Robust.Shared.Spawners;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
@@ -36,6 +29,7 @@ public sealed class SmokeSystem : EntitySystem
[Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -45,15 +39,6 @@ public sealed class SmokeSystem : EntitySystem
SubscribeLocalEvent<SmokeComponent, ReactionAttemptEvent>(OnReactionAttempt); SubscribeLocalEvent<SmokeComponent, ReactionAttemptEvent>(OnReactionAttempt);
SubscribeLocalEvent<SmokeComponent, SpreadNeighborsEvent>(OnSmokeSpread); SubscribeLocalEvent<SmokeComponent, SpreadNeighborsEvent>(OnSmokeSpread);
SubscribeLocalEvent<SmokeDissipateSpawnComponent, TimedDespawnEvent>(OnSmokeDissipate); 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 OnSmokeDissipate(EntityUid uid, SmokeDissipateSpawnComponent component, ref TimedDespawnEvent args)
@@ -68,11 +53,10 @@ public sealed class SmokeSystem : EntitySystem
private void OnSmokeSpread(EntityUid uid, SmokeComponent component, ref SpreadNeighborsEvent args) private void OnSmokeSpread(EntityUid uid, SmokeComponent component, ref SpreadNeighborsEvent args)
{ {
if (component.SpreadAmount == 0 || if (component.SpreadAmount == 0
!_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution) || || !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution))
args.NeighborFreeTiles.Count == 0)
{ {
RemCompDeferred<EdgeSpreaderComponent>(uid); RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return; return;
} }
@@ -80,52 +64,48 @@ public sealed class SmokeSystem : EntitySystem
if (prototype == null) if (prototype == null)
{ {
RemCompDeferred<EdgeSpreaderComponent>(uid); RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return; return;
} }
TryComp<TimedDespawnComponent>(uid, out var timer); TryComp<TimedDespawnComponent>(uid, out var timer);
_appearance.TryGetData(uid, SmokeVisuals.Color, out var color);
var smokePerSpread = component.SpreadAmount / args.NeighborFreeTiles.Count; // wtf is the logic behind any of this.
component.SpreadAmount -= smokePerSpread; var smokePerSpread = 1 + component.SpreadAmount / Math.Max(1, args.NeighborFreeTiles.Count);
foreach (var neighbor in args.NeighborFreeTiles) foreach (var neighbor in args.NeighborFreeTiles)
{ {
var coords = neighbor.Grid.GridTileToLocal(neighbor.Tile); 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); 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--; args.Updates--;
// Listen this is the old behaviour iunno // Listen this is the old behaviour iunno
Start(ent, neighborSmoke, solution.Clone(), timer?.Lifetime ?? 10f); 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); _appearance.SetData(ent, SmokeVisuals.Color, color);
}
// Only 1 spread then ig?
if (smokePerSpread == 0)
{
component.SpreadAmount--;
if (component.SpreadAmount == 0) if (component.SpreadAmount == 0)
{ {
RemCompDeferred<EdgeSpreaderComponent>(uid); RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
break; break;
} }
}
if (args.Updates <= 0) if (args.Updates <= 0)
break; break;
} }
// Give our spread to neighbor tiles. if (args.NeighborFreeTiles.Count > 0 || args.Neighbors.Count == 0 || component.SpreadAmount < 1)
if (args.NeighborFreeTiles.Count == 0 && args.Neighbors.Count > 0 && component.SpreadAmount > 0) return;
{
// We have no more neighbours to spread to. So instead we will randomly distribute our volume to neighbouring smoke tiles.
var smokeQuery = GetEntityQuery<SmokeComponent>(); var smokeQuery = GetEntityQuery<SmokeComponent>();
_random.Shuffle(args.Neighbors);
foreach (var neighbor in args.Neighbors) foreach (var neighbor in args.Neighbors)
{ {
if (!smokeQuery.TryGetComponent(neighbor, out var smoke)) if (!smokeQuery.TryGetComponent(neighbor, out var smoke))
@@ -133,14 +113,12 @@ public sealed class SmokeSystem : EntitySystem
smoke.SpreadAmount++; smoke.SpreadAmount++;
args.Updates--; args.Updates--;
component.SpreadAmount--;
EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
if (component.SpreadAmount == 0) if (component.SpreadAmount == 0)
{ {
RemCompDeferred<EdgeSpreaderComponent>(uid); RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
break;
}
if (args.Updates <= 0)
break; break;
} }
} }
@@ -248,7 +226,7 @@ public sealed class SmokeSystem : EntitySystem
public void Start(EntityUid uid, SmokeComponent component, Solution solution, float duration) public void Start(EntityUid uid, SmokeComponent component, Solution solution, float duration)
{ {
TryAddSolution(uid, component, solution); TryAddSolution(uid, component, solution);
EnsureComp<EdgeSpreaderComponent>(uid); EnsureComp<ActiveEdgeSpreaderComponent>(uid);
var timer = EnsureComp<TimedDespawnComponent>(uid); var timer = EnsureComp<TimedDespawnComponent>(uid);
timer.Lifetime = duration; timer.Lifetime = duration;
} }

View File

@@ -61,7 +61,6 @@ namespace Content.Server.NodeContainer.NodeGroups
AMEngine, AMEngine,
Pipe, Pipe,
WireNet, WireNet,
Spreader,
/// <summary> /// <summary>
/// Group used by the TEG. /// Group used by the TEG.

View File

@@ -0,0 +1,10 @@
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
{
}

View File

@@ -1,10 +1,14 @@
using Content.Shared.Spreader;
using Robust.Shared.Prototypes;
namespace Content.Server.Spreader; namespace Content.Server.Spreader;
/// <summary> /// <summary>
/// Added to entities being considered for spreading via <see cref="SpreaderSystem"/>. /// Entity capable of becoming cloning and replicating itself to adjacent edges. See <see cref="SpreaderSystem"/>
/// This needs to be manually added and removed.
/// </summary> /// </summary>
[RegisterComponent, Access(typeof(SpreaderSystem))] [RegisterComponent, Access(typeof(SpreaderSystem))]
public sealed partial class EdgeSpreaderComponent : Component public sealed partial class EdgeSpreaderComponent : Component
{ {
[DataField(required:true)]
public ProtoId<EdgeSpreaderPrototype> Id;
} }

View File

@@ -5,12 +5,6 @@ namespace Content.Server.Spreader;
[RegisterComponent, Access(typeof(KudzuSystem))] [RegisterComponent, Access(typeof(KudzuSystem))]
public sealed partial class GrowingKudzuComponent : Component 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> /// <summary>
/// The next time kudzu will try to tick its growth level. /// The next time kudzu will try to tick its growth level.
/// </summary> /// </summary>

View File

@@ -8,6 +8,12 @@ namespace Content.Server.Spreader;
[RegisterComponent] [RegisterComponent]
public sealed partial class KudzuComponent : Component 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> /// <summary>
/// Chance to spread whenever an edge spread is possible. /// Chance to spread whenever an edge spread is possible.
/// </summary> /// </summary>

View File

@@ -2,6 +2,7 @@ using Content.Shared.Damage;
using Content.Shared.Spreader; using Content.Shared.Spreader;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Spreader; namespace Content.Server.Spreader;
@@ -13,7 +14,7 @@ public sealed class KudzuSystem : EntitySystem
[Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DamageableSystem _damageable = default!;
[ValidatePrototypeId<EdgeSpreaderPrototype>] [ValidatePrototypeId<EdgeSpreaderPrototype>]
private const string KudzuGroup = "kudzu"; private const string KudzuGroup = "Kudzu";
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -21,7 +22,6 @@ public sealed class KudzuSystem : EntitySystem
SubscribeLocalEvent<KudzuComponent, ComponentStartup>(SetupKudzu); SubscribeLocalEvent<KudzuComponent, ComponentStartup>(SetupKudzu);
SubscribeLocalEvent<KudzuComponent, SpreadNeighborsEvent>(OnKudzuSpread); SubscribeLocalEvent<KudzuComponent, SpreadNeighborsEvent>(OnKudzuSpread);
SubscribeLocalEvent<GrowingKudzuComponent, EntityUnpausedEvent>(OnKudzuUnpaused); SubscribeLocalEvent<GrowingKudzuComponent, EntityUnpausedEvent>(OnKudzuUnpaused);
SubscribeLocalEvent<SpreadGroupUpdateRate>(OnKudzuUpdateRate);
SubscribeLocalEvent<KudzuComponent, DamageChangedEvent>(OnDamageChanged); SubscribeLocalEvent<KudzuComponent, DamageChangedEvent>(OnDamageChanged);
} }
@@ -36,59 +36,50 @@ public sealed class KudzuSystem : EntitySystem
if (!TryComp(uid, out growing)) if (!TryComp(uid, out growing))
{ {
growing = AddComp<GrowingKudzuComponent>(uid); 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)) 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) 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; return;
}
if (args.NeighborFreeTiles.Count == 0) if (args.NeighborFreeTiles.Count == 0)
{ {
RemCompDeferred<EdgeSpreaderComponent>(uid); RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
var prototype = MetaData(uid).EntityPrototype?.ID;
if (prototype == null)
{
RemCompDeferred<EdgeSpreaderComponent>(uid);
return; return;
} }
if (!_robustRandom.Prob(component.SpreadChance)) if (!_robustRandom.Prob(component.SpreadChance))
return; return;
var prototype = MetaData(uid).EntityPrototype?.ID;
if (prototype == null)
{
RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
return;
}
foreach (var neighbor in args.NeighborFreeTiles) foreach (var neighbor in args.NeighborFreeTiles)
{ {
var neighborUid = Spawn(prototype, neighbor.Grid.GridTileToLocal(neighbor.Tile)); 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--; args.Updates--;
if (args.Updates <= 0) if (args.Updates <= 0)
return; 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) private void OnKudzuUnpaused(EntityUid uid, GrowingKudzuComponent component, ref EntityUnpausedEvent args)
{ {
component.NextTick += args.PausedTime; component.NextTick += args.PausedTime;
@@ -109,24 +100,30 @@ public sealed class KudzuSystem : EntitySystem
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
var appearanceQuery = GetEntityQuery<AppearanceComponent>(); 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; 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) if (grow.NextTick > curTime)
{
continue; continue;
}
grow.NextTick = curTime + TimeSpan.FromSeconds(0.5); grow.NextTick = curTime + TimeSpan.FromSeconds(0.5);
if (!kudzuQuery.TryGetComponent(uid, out var kudzu))
{
RemCompDeferred(uid, grow);
continue;
}
if (!_robustRandom.Prob(kudzu.GrowthTickChance)) if (!_robustRandom.Prob(kudzu.GrowthTickChance))
{ {
continue; continue;
} }
if (TryComp<DamageableComponent>(uid, out var damage)) if (damageableQuery.TryGetComponent(uid, out var damage))
{ {
if (damage.TotalDamage > 1.0) if (damage.TotalDamage > 1.0)
{ {
@@ -146,17 +143,17 @@ public sealed class KudzuSystem : EntitySystem
} }
} }
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. // 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)) if (appearanceQuery.TryGetComponent(uid, out var appearance))
{ {
_appearance.SetData(uid, KudzuVisuals.GrowthLevel, grow.GrowthLevel, appearance); _appearance.SetData(uid, KudzuVisuals.GrowthLevel, kudzu.GrowthLevel, appearance);
} }
} }
} }

View File

@@ -1,7 +0,0 @@
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);

View File

@@ -1,38 +0,0 @@
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;
}
}
}

View File

@@ -1,31 +0,0 @@
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);
}
}
}

View File

@@ -1,8 +1,5 @@
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems; 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.Server.Shuttles.Components;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Spreader; using Content.Shared.Spreader;
@@ -13,6 +10,7 @@ using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Spreader; namespace Content.Server.Spreader;
@@ -25,11 +23,20 @@ public sealed class SpreaderSystem : EntitySystem
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _robustRandom = 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>] [ValidatePrototypeId<TagPrototype>]
private const string IgnoredTag = "SpreaderIgnore"; private const string IgnoredTag = "SpreaderIgnore";
@@ -42,6 +49,7 @@ public sealed class SpreaderSystem : EntitySystem
SubscribeLocalEvent<SpreaderGridComponent, EntityUnpausedEvent>(OnGridUnpaused); SubscribeLocalEvent<SpreaderGridComponent, EntityUnpausedEvent>(OnGridUnpaused);
SubscribeLocalEvent<EdgeSpreaderComponent, EntityTerminatingEvent>(OnTerminating);
SetupPrototypes(); SetupPrototypes();
_prototype.PrototypesReloaded += OnPrototypeReload; _prototype.PrototypesReloaded += OnPrototypeReload;
} }
@@ -62,21 +70,20 @@ public sealed class SpreaderSystem : EntitySystem
private void SetupPrototypes() private void SetupPrototypes()
{ {
_spreaderGroups.Clear(); _prototypeUpdates = new Dictionary<string, int>();
foreach (var proto in _prototype.EnumeratePrototypes<EdgeSpreaderPrototype>())
foreach (var id in _prototype.EnumeratePrototypes<EdgeSpreaderPrototype>())
{ {
_spreaderGroups.Add(id.ID); _prototypeUpdates.Add(proto.ID, proto.UpdatesPerSecond);
} }
} }
private void OnAirtightChanged(ref AirtightChanged ev) 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) foreach (var neighbor in neighbors)
{ {
EnsureComp<EdgeSpreaderComponent>(neighbor); EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
} }
} }
@@ -87,7 +94,17 @@ public sealed class SpreaderSystem : EntitySystem
private void OnGridInit(GridInitializeEvent ev) 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/> /// <inheritdoc/>
@@ -96,31 +113,26 @@ public sealed class SpreaderSystem : EntitySystem
var curTime = _timing.CurTime; var curTime = _timing.CurTime;
// Check which grids are valid for spreading // Check which grids are valid for spreading
var spreadable = new ValueList<EntityUid>();
var spreadGrids = EntityQueryEnumerator<SpreaderGridComponent>(); var spreadGrids = EntityQueryEnumerator<SpreaderGridComponent>();
_gridUpdates.Clear();
while (spreadGrids.MoveNext(out var uid, out var grid)) while (spreadGrids.MoveNext(out var uid, out var grid))
{ {
if (grid.NextUpdate > curTime) if (grid.NextUpdate > curTime)
continue; continue;
spreadable.Add(uid); _gridUpdates[uid] = _prototypeUpdates.ShallowClone();
grid.NextUpdate += SpreadCooldown; grid.NextUpdate += SpreadCooldown;
} }
if (spreadable.Count == 0) if (_gridUpdates.Count == 0)
return; return;
var query = EntityQueryEnumerator<EdgeSpreaderComponent>(); var query = EntityQueryEnumerator<ActiveEdgeSpreaderComponent>();
var nodeQuery = GetEntityQuery<NodeContainerComponent>(); var xforms = GetEntityQuery<TransformComponent>();
var xformQuery = GetEntityQuery<TransformComponent>(); var spreaderQuery = GetEntityQuery<EdgeSpreaderComponent>();
var gridQuery = GetEntityQuery<SpreaderGridComponent>();
// Each INode group has a certain number of updates var spreaders = new List<(EntityUid Uid, ActiveEdgeSpreaderComponent Comp)>(Count<ActiveEdgeSpreaderComponent>());
// allowed per SpreadCooldown
var groupUpdates = new Dictionary<INodeGroup, int>();
var spreaders = new List<(EntityUid Uid, EdgeSpreaderComponent Comp)>(Count<EdgeSpreaderComponent>());
// Build a list of all existing Edgespreaders, shuffle them // Build a list of all existing Edgespreaders, shuffle them
while (query.MoveNext(out var uid, out var comp)) while (query.MoveNext(out var uid, out var comp))
@@ -134,64 +146,43 @@ public sealed class SpreaderSystem : EntitySystem
// that doesn't meet a few trivial prerequisites // that doesn't meet a few trivial prerequisites
foreach (var (uid, comp) in spreaders) foreach (var (uid, comp) in spreaders)
{ {
if (!xformQuery.TryGetComponent(uid, out var xform) || // Get xform first, as entity may have been deleted due to interactions triggered by other spreaders.
xform.GridUid == null || if (!xforms.TryGetComponent(uid, out var xform))
!gridQuery.HasComponent(xform.GridUid.Value)) continue;
if (xform.GridUid == null)
{ {
RemCompDeferred<EdgeSpreaderComponent>(uid); RemComp(uid, comp);
continue; continue;
} }
foreach (var sGroup in _spreaderGroups) if (!_gridUpdates.TryGetValue(xform.GridUid.Value, out var groupUpdates))
continue;
if (!spreaderQuery.TryGetComponent(uid, out var spreader))
{ {
// Get the NodeContainer and Node from every EdgeSpreader entity found RemComp(uid, comp);
if (!nodeQuery.TryGetComponent(uid, out var nodeContainer))
{
RemCompDeferred<EdgeSpreaderComponent>(uid);
continue; continue;
} }
if (!_nodeContainer.TryGetNode<SpreaderNode>(nodeContainer, sGroup, out var node)) if (!groupUpdates.TryGetValue(spreader.Id, out var updates) || updates < 1)
{
continue; continue;
}
// Not allowed this tick?
if (node.NodeGroup == null ||
!spreadable.Contains(xform.GridUid.Value))
{
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));
}
// "updates" integer dictates the amount of nodes that
// are to be spawned around a NodeGroup
if (updates <= 0)
{
continue;
}
// Edge detection logic is to be handled // Edge detection logic is to be handled
// by the subscribing system, see KudzuSystem // by the subscribing system, see KudzuSystem
// for a simple example // for a simple example
Spread(uid, xform, spreader.Id, ref updates);
Spread(uid, node, node.NodeGroup, ref updates); if (updates < 1)
groupUpdates[node.NodeGroup] = updates; 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() var ev = new SpreadNeighborsEvent()
{ {
@@ -207,20 +198,19 @@ public sealed class SpreaderSystem : EntitySystem
/// <summary> /// <summary>
/// Gets the neighboring node data for the specified entity and the specified node group. /// Gets the neighboring node data for the specified entity and the specified node group.
/// </summary> /// </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)>(); freeTiles = new ValueList<(MapGridComponent Grid, Vector2i Tile)>();
occupiedTiles = new ValueList<Vector2i>(); occupiedTiles = new ValueList<Vector2i>();
neighbors = new ValueList<EntityUid>(); neighbors = new ValueList<EntityUid>();
if (!EntityManager.TryGetComponent<TransformComponent>(uid, out var transform))
return;
if (!_mapManager.TryGetGrid(transform.GridUid, out var grid)) if (!_mapManager.TryGetGrid(transform.GridUid, out var grid))
return; return;
var tile = grid.TileIndicesFor(transform.Coordinates); var tile = grid.TileIndicesFor(transform.Coordinates);
var nodeQuery = GetEntityQuery<NodeContainerComponent>(); var spreaderQuery = GetEntityQuery<EdgeSpreaderComponent>();
var airtightQuery = GetEntityQuery<AirtightComponent>(); var airtightQuery = GetEntityQuery<AirtightComponent>();
var dockQuery = GetEntityQuery<DockingComponent>(); var dockQuery = GetEntityQuery<DockingComponent>();
var xformQuery = GetEntityQuery<TransformComponent>(); var xformQuery = GetEntityQuery<TransformComponent>();
@@ -309,10 +299,10 @@ public sealed class SpreaderSystem : EntitySystem
while (directionEnumerator.MoveNext(out var ent)) while (directionEnumerator.MoveNext(out var ent))
{ {
if (!nodeQuery.TryGetComponent(ent, out var nodeContainer)) if (!spreaderQuery.TryGetComponent(ent, out var spreader))
continue; continue;
if (!nodeContainer.Nodes.ContainsKey(groupName)) if (spreader.Id != prototype)
continue; continue;
neighbors.Add(ent.Value); neighbors.Add(ent.Value);
@@ -325,23 +315,38 @@ public sealed class SpreaderSystem : EntitySystem
} }
} }
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>(); var neighbors = new List<EntityUid>();
if (!EntityManager.TryGetComponent<TransformComponent>(uid, out var transform)) Vector2i tile;
return neighbors; // how did we get here? MapGridComponent? grid;
if (!_mapManager.TryGetGrid(transform.GridUid, out var grid)) if (position == null)
{
var transform = Transform(uid);
if (!_mapManager.TryGetGrid(transform.GridUid, out grid))
return neighbors; 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 spreaderQuery = GetEntityQuery<EdgeSpreaderComponent>();
var nodeQuery = GetEntityQuery<NodeContainerComponent>();
for (var i = 0; i < Atmospherics.Directions; i++) for (var i = 0; i < Atmospherics.Directions; i++)
{ {
var direction = (AtmosDirection) (1 << i); var direction = (AtmosDirection) (1 << i);
if (!comp.AirBlockedDirection.IsFlagSet(direction)) if (comp != null && !comp.AirBlockedDirection.IsFlagSet(direction))
continue; continue;
var directionEnumerator = var directionEnumerator =
@@ -349,17 +354,9 @@ public sealed class SpreaderSystem : EntitySystem
while (directionEnumerator.MoveNext(out var ent)) while (directionEnumerator.MoveNext(out var ent))
{ {
if (!nodeQuery.TryGetComponent(ent, out var nodeContainer)) DebugTools.Assert(Transform(ent.Value).Anchored);
continue; if (spreaderQuery.HasComponent(ent))
foreach (var name in _spreaderGroups)
{
if (!nodeContainer.Nodes.ContainsKey(name))
continue;
neighbors.Add(ent.Value); neighbors.Add(ent.Value);
break;
}
} }
} }

View File

@@ -9,4 +9,5 @@ namespace Content.Shared.Spreader;
public sealed class EdgeSpreaderPrototype : IPrototype public sealed class EdgeSpreaderPrototype : IPrototype
{ {
[IdDataField] public string ID { get; } = string.Empty; [IdDataField] public string ID { get; } = string.Empty;
[DataField(required:true)] public int UpdatesPerSecond;
} }

View File

@@ -13,12 +13,9 @@
- type: Transform - type: Transform
anchored: true anchored: true
- type: Smoke - type: Smoke
- type: NodeContainer - type: ActiveEdgeSpreader
nodes:
smoke:
!type:SpreaderNode
nodeGroupID: Spreader
- type: EdgeSpreader - type: EdgeSpreader
id: Smoke
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
solutionArea: solutionArea:
@@ -64,12 +61,9 @@
layer: layer:
- SlipLayer - SlipLayer
- type: Smoke - type: Smoke
- type: NodeContainer - type: ActiveEdgeSpreader
nodes:
smoke:
!type:SpreaderNode
nodeGroupID: Spreader
- type: EdgeSpreader - type: EdgeSpreader
id: Smoke
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
solutionArea: solutionArea:

View File

@@ -144,13 +144,10 @@
puddle: { maxVol: 1000 } puddle: { maxVol: 1000 }
- type: Puddle - type: Puddle
- type: Appearance - type: Appearance
- type: ActiveEdgeSpreader
- type: EdgeSpreader - type: EdgeSpreader
id: Puddle
- type: StepTrigger - type: StepTrigger
- type: NodeContainer
nodes:
puddle:
!type:SpreaderNode
nodeGroupID: Spreader
- type: Drink - type: Drink
delay: 3 delay: 3
transferAmount: 1 transferAmount: 1

View File

@@ -79,12 +79,9 @@
ignoreWhitelist: ignoreWhitelist:
components: components:
- IgnoreKudzu - IgnoreKudzu
- type: ActiveEdgeSpreader
- type: EdgeSpreader - type: EdgeSpreader
- type: NodeContainer id: Kudzu
nodes:
kudzu:
!type:SpreaderNode
nodeGroupID: Spreader
- type: Food - type: Food
requiredStomachs: 2 # ruminants have 4 stomachs but i dont care to give them literally 4 stomachs. 2 is good requiredStomachs: 2 # ruminants have 4 stomachs but i dont care to give them literally 4 stomachs. 2 is good
delay: 0.5 delay: 0.5
@@ -186,12 +183,9 @@
- type: GrowingKudzu - type: GrowingKudzu
growthTickChance: 0.3 growthTickChance: 0.3
- type: AtmosExposed - type: AtmosExposed
- type: ActiveEdgeSpreader
- type: EdgeSpreader - type: EdgeSpreader
- type: NodeContainer id: Kudzu
nodes:
kudzu:
!type:SpreaderNode
nodeGroupID: Spreader
- type: SlowContacts - type: SlowContacts
walkSpeedModifier: 0.3 walkSpeedModifier: 0.3
sprintSpeedModifier: 0.3 sprintSpeedModifier: 0.3

View File

@@ -1,8 +1,11 @@
- type: edgeSpreader - type: edgeSpreader
id: kudzu id: Kudzu
updatesPerSecond: 1
- type: edgeSpreader - type: edgeSpreader
id: puddle id: Puddle
updatesPerSecond: 16
- type: edgeSpreader - type: edgeSpreader
id: smoke id: Smoke
updatesPerSecond: 8