From 7d168b1c8ce779cdf35ad4db7c7a9069d349dfe2 Mon Sep 17 00:00:00 2001
From: M1and1B <153716654+M1and1B@users.noreply.github.com>
Date: Thu, 2 Jan 2025 14:20:33 +0200
Subject: [PATCH] saves for later
---
.../ActiveEdgeSupermatterSpreaderComponent.cs | 10 +
.../EdgeSupermatterSpreaderComponent.cs | 14 +
.../GrowingSupermatterKudzuComponent.cs | 14 +
.../ADT/Spreader/SupermatterKudzuComponent.cs | 29 ++
.../ADT/Spreader/SupermatterKudzuSystem.cs | 111 ++++++
.../SupermatterSpreadNeighborsEvent.cs | 22 ++
.../SupermatterSpreaderGridComponent.cs | 8 +
.../ADT/Spreader/SupermatterSpreaderSystem.cs | 351 ++++++++++++++++++
.../Spreader/SupermatterKudzuSystem.cs | 0
.../EdgeSupermatterSpreaderPrototype.cs | 19 +
.../ADT/Spreader/SupermatterKudzuVisuals.cs | 10 +
.../Supermatter/SupermatterKudzu.yml | 26 +-
12 files changed, 610 insertions(+), 4 deletions(-)
create mode 100644 Content.Server/ADT/Spreader/ActiveEdgeSupermatterSpreaderComponent.cs
create mode 100644 Content.Server/ADT/Spreader/EdgeSupermatterSpreaderComponent.cs
create mode 100644 Content.Server/ADT/Spreader/GrowingSupermatterKudzuComponent.cs
create mode 100644 Content.Server/ADT/Spreader/SupermatterKudzuComponent.cs
create mode 100644 Content.Server/ADT/Spreader/SupermatterKudzuSystem.cs
create mode 100644 Content.Server/ADT/Spreader/SupermatterSpreadNeighborsEvent.cs
create mode 100644 Content.Server/ADT/Spreader/SupermatterSpreaderGridComponent.cs
create mode 100644 Content.Server/ADT/Spreader/SupermatterSpreaderSystem.cs
delete mode 100644 Content.Server/Spreader/SupermatterKudzuSystem.cs
create mode 100644 Content.Shared/ADT/Spreader/EdgeSupermatterSpreaderPrototype.cs
create mode 100644 Content.Shared/ADT/Spreader/SupermatterKudzuVisuals.cs
diff --git a/Content.Server/ADT/Spreader/ActiveEdgeSupermatterSpreaderComponent.cs b/Content.Server/ADT/Spreader/ActiveEdgeSupermatterSpreaderComponent.cs
new file mode 100644
index 00000000000..e20753e6089
--- /dev/null
+++ b/Content.Server/ADT/Spreader/ActiveEdgeSupermatterSpreaderComponent.cs
@@ -0,0 +1,10 @@
+namespace Content.Server.ADT.Spreader;
+
+///
+/// Added to entities being considered for spreading via .
+/// This needs to be manually added and removed.
+///
+[RegisterComponent]
+public sealed partial class ActiveEdgeSupermatterSpreaderComponent : Component
+{
+}
diff --git a/Content.Server/ADT/Spreader/EdgeSupermatterSpreaderComponent.cs b/Content.Server/ADT/Spreader/EdgeSupermatterSpreaderComponent.cs
new file mode 100644
index 00000000000..9ec92d2b477
--- /dev/null
+++ b/Content.Server/ADT/Spreader/EdgeSupermatterSpreaderComponent.cs
@@ -0,0 +1,14 @@
+using Content.Shared.ADT.Spreader;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.ADT.Spreader;
+
+///
+/// Entity capable of becoming cloning and replicating itself to adjacent edges. See
+///
+[RegisterComponent, Access(typeof(SupermatterSpreaderSystem))]
+public sealed partial class EdgeSupermatterSpreaderComponent : Component
+{
+ [DataField(required:true)]
+ public ProtoId Id;
+}
diff --git a/Content.Server/ADT/Spreader/GrowingSupermatterKudzuComponent.cs b/Content.Server/ADT/Spreader/GrowingSupermatterKudzuComponent.cs
new file mode 100644
index 00000000000..3c3c5ecf737
--- /dev/null
+++ b/Content.Server/ADT/Spreader/GrowingSupermatterKudzuComponent.cs
@@ -0,0 +1,14 @@
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Server.ADT.Spreader;
+
+[RegisterComponent, Access(typeof(SupermatterKudzuSystem)), AutoGenerateComponentPause]
+public sealed partial class GrowingSupermatterKudzuComponent : Component
+{
+ ///
+ /// The next time kudzu will try to tick its growth level.
+ ///
+ [DataField("nextTick", customTypeSerializer:typeof(TimeOffsetSerializer))]
+ [AutoPausedField]
+ public TimeSpan NextTick = TimeSpan.Zero;
+}
diff --git a/Content.Server/ADT/Spreader/SupermatterKudzuComponent.cs b/Content.Server/ADT/Spreader/SupermatterKudzuComponent.cs
new file mode 100644
index 00000000000..a3e464036ca
--- /dev/null
+++ b/Content.Server/ADT/Spreader/SupermatterKudzuComponent.cs
@@ -0,0 +1,29 @@
+namespace Content.Server.ADT.Spreader;
+
+///
+/// Handles entities that spread out when they reach the relevant growth level.
+///
+[RegisterComponent]
+public sealed partial class SupermatterKudzuComponent : Component
+{
+ ///
+ /// At level 3 spreading can occur; prior to that we have a chance of increasing our growth level and changing our sprite.
+ ///
+ [DataField]
+ public int GrowthLevel = 1;
+
+ ///
+ /// Chance to spread whenever an edge spread is possible.
+ ///
+ [DataField]
+ public float SpreadChance = 1f;
+
+ [DataField]
+ public float GrowthTickChance = 1f;
+
+ ///
+ /// number of sprite variations for kudzu
+ ///
+ [DataField]
+ public int SpriteVariants = 3;
+}
diff --git a/Content.Server/ADT/Spreader/SupermatterKudzuSystem.cs b/Content.Server/ADT/Spreader/SupermatterKudzuSystem.cs
new file mode 100644
index 00000000000..cf378fb98cb
--- /dev/null
+++ b/Content.Server/ADT/Spreader/SupermatterKudzuSystem.cs
@@ -0,0 +1,111 @@
+using Content.Shared.Damage;
+using Content.Shared.ADT.Spreader;
+using Robust.Shared.Random;
+using Robust.Shared.Timing;
+using Robust.Shared.Utility;
+
+namespace Content.Server.ADT.Spreader;
+
+public sealed class SupermatterKudzuSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IRobustRandom _robustRandom = default!;
+ [Dependency] private readonly SharedMapSystem _map = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+
+ [ValidatePrototypeId]
+ private const string SupermatterKudzuGroup = "SupermatterKudzu";
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(SetupKudzu);
+ SubscribeLocalEvent(OnKudzuSpread);
+ }
+
+ private void OnKudzuSpread(EntityUid uid, SupermatterKudzuComponent component, ref SupermatterSpreadNeighborsEvent args)
+ {
+ if (component.GrowthLevel < 3)
+ return;
+
+ if (args.NeighborFreeTiles.Count == 0)
+ {
+ RemCompDeferred(uid);
+ return;
+ }
+
+ if (!_robustRandom.Prob(component.SpreadChance))
+ return;
+
+ var prototype = MetaData(uid).EntityPrototype?.ID;
+
+ if (prototype == null)
+ {
+ RemCompDeferred(uid);
+ return;
+ }
+
+ foreach (var neighbor in args.NeighborFreeTiles)
+ {
+ var neighborUid = Spawn(prototype, _map.GridTileToLocal(neighbor.Tile.GridUid, neighbor.Grid, neighbor.Tile.GridIndices));
+ DebugTools.Assert(HasComp(neighborUid));
+ DebugTools.Assert(HasComp(neighborUid));
+ DebugTools.Assert(Comp(neighborUid).Id == SupermatterKudzuGroup);
+ args.Updates--;
+ if (args.Updates <= 0)
+ return;
+ }
+ }
+
+ private void SetupKudzu(EntityUid uid, SupermatterKudzuComponent component, ComponentStartup args)
+ {
+ if (!EntityManager.TryGetComponent(uid, out var appearance))
+ {
+ return;
+ }
+
+ _appearance.SetData(uid, SupermatterKudzuVisuals.Variant, _robustRandom.Next(1, component.SpriteVariants), appearance);
+ _appearance.SetData(uid, SupermatterKudzuVisuals.GrowthLevel, 1, appearance);
+ }
+
+ ///
+ public override void Update(float frameTime)
+ {
+ var appearanceQuery = GetEntityQuery();
+ var query = EntityQueryEnumerator();
+ var kudzuQuery = GetEntityQuery();
+ var curTime = _timing.CurTime;
+
+ 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;
+ }
+
+ kudzu.GrowthLevel += 1;
+
+ if (kudzu.GrowthLevel >= 3)
+ {
+ // why cache when you can simply cease to be? Also saves a bit of memory/time.
+ RemCompDeferred(uid, grow);
+ }
+
+ if (appearanceQuery.TryGetComponent(uid, out var appearance))
+ {
+ _appearance.SetData(uid, SupermatterKudzuVisuals.GrowthLevel, kudzu.GrowthLevel, appearance);
+ }
+ }
+ }
+}
diff --git a/Content.Server/ADT/Spreader/SupermatterSpreadNeighborsEvent.cs b/Content.Server/ADT/Spreader/SupermatterSpreadNeighborsEvent.cs
new file mode 100644
index 00000000000..8bef4b33eff
--- /dev/null
+++ b/Content.Server/ADT/Spreader/SupermatterSpreadNeighborsEvent.cs
@@ -0,0 +1,22 @@
+using Robust.Shared.Collections;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+
+namespace Content.Server.ADT.Spreader;
+
+///
+/// Raised when trying to spread to neighboring tiles.
+/// If the spread is no longer able to happen you MUST cancel this event!
+///
+[ByRefEvent]
+public record struct SupermatterSpreadNeighborsEvent
+{
+ public ValueList<(MapGridComponent Grid, TileRef Tile)> NeighborFreeTiles;
+ public ValueList Neighbors;
+
+ ///
+ /// How many updates allowed are remaining.
+ /// Subscribers can handle as they wish.
+ ///
+ public int Updates;
+}
diff --git a/Content.Server/ADT/Spreader/SupermatterSpreaderGridComponent.cs b/Content.Server/ADT/Spreader/SupermatterSpreaderGridComponent.cs
new file mode 100644
index 00000000000..ee4e51b442e
--- /dev/null
+++ b/Content.Server/ADT/Spreader/SupermatterSpreaderGridComponent.cs
@@ -0,0 +1,8 @@
+namespace Content.Server.ADT.Spreader;
+
+[RegisterComponent]
+public sealed partial class SupermatterSpreaderGridComponent : Component
+{
+ [DataField]
+ public float UpdateAccumulator = SupermatterSpreaderSystem.SpreadCooldownSeconds;
+}
diff --git a/Content.Server/ADT/Spreader/SupermatterSpreaderSystem.cs b/Content.Server/ADT/Spreader/SupermatterSpreaderSystem.cs
new file mode 100644
index 00000000000..664a1a70af4
--- /dev/null
+++ b/Content.Server/ADT/Spreader/SupermatterSpreaderSystem.cs
@@ -0,0 +1,351 @@
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.EntitySystems;
+using Content.Server.Shuttles.Components;
+using Content.Shared.Atmos;
+using Content.Shared.Maps;
+using Content.Shared.ADT.Spreader;
+using Content.Shared.Tag;
+using Robust.Shared.Collections;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using Robust.Shared.Utility;
+
+namespace Content.Server.ADT.Spreader;
+
+///
+/// Handles generic spreading logic, where one anchored entity spreads to neighboring tiles.
+///
+public sealed class SupermatterSpreaderSystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+ [Dependency] private readonly IRobustRandom _robustRandom = default!;
+ [Dependency] private readonly SharedMapSystem _map = default!;
+ [Dependency] private readonly TagSystem _tag = default!;
+
+ ///
+ /// Cached maximum number of updates per spreader prototype. This is applied per-grid.
+ ///
+ private Dictionary _prototypeUpdates = default!;
+
+ ///
+ /// Remaining number of updates per grid & prototype.
+ ///
+ // TODO PERFORMANCE Assign each prototype to an index and convert dictionary to array
+ private readonly Dictionary> _gridUpdates = [];
+
+ private EntityQuery _query;
+
+ public const float SpreadCooldownSeconds = 1;
+
+ private static readonly ProtoId IgnoredTag = "SpreaderIgnore";
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnAirtightChanged);
+ SubscribeLocalEvent(OnGridInit);
+ SubscribeLocalEvent(OnPrototypeReload);
+
+ SubscribeLocalEvent(OnTerminating);
+ SetupPrototypes();
+
+ _query = GetEntityQuery();
+ }
+
+ private void OnPrototypeReload(PrototypesReloadedEventArgs obj)
+ {
+ if (obj.WasModified())
+ SetupPrototypes();
+ }
+
+ private void SetupPrototypes()
+ {
+ _prototypeUpdates = [];
+ foreach (var proto in _prototype.EnumeratePrototypes())
+ {
+ _prototypeUpdates.Add(proto.ID, proto.UpdatesPerSecond);
+ }
+ }
+
+ private void OnAirtightChanged(ref AirtightChanged ev)
+ {
+ ActivateSpreadableNeighbors(ev.Entity, ev.Position);
+ }
+
+ private void OnGridInit(GridInitializeEvent ev)
+ {
+ EnsureComp(ev.EntityUid);
+ }
+
+ private void OnTerminating(Entity entity, ref EntityTerminatingEvent args)
+ {
+ ActivateSpreadableNeighbors(entity);
+ }
+
+ ///
+ public override void Update(float frameTime)
+ {
+ // Check which grids are valid for spreading
+ var spreadGrids = EntityQueryEnumerator();
+
+ _gridUpdates.Clear();
+ while (spreadGrids.MoveNext(out var uid, out var grid))
+ {
+ grid.UpdateAccumulator -= frameTime;
+ if (grid.UpdateAccumulator > 0)
+ continue;
+
+ _gridUpdates[uid] = _prototypeUpdates.ShallowClone();
+ grid.UpdateAccumulator += SpreadCooldownSeconds;
+ }
+
+ if (_gridUpdates.Count == 0)
+ return;
+
+ var query = EntityQueryEnumerator();
+ var xforms = GetEntityQuery();
+ var spreaderQuery = GetEntityQuery();
+
+ var spreaders = new List<(EntityUid Uid, ActiveEdgeSupermatterSpreaderComponent Comp)>(Count());
+
+ // Build a list of all existing Edgespreaders, shuffle them
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ spreaders.Add((uid, comp));
+ }
+
+ _robustRandom.Shuffle(spreaders);
+
+ // Remove the EdgeSpreaderComponent from any entity
+ // that doesn't meet a few trivial prerequisites
+ foreach (var (uid, comp) in spreaders)
+ {
+ // Get xform first, as entity may have been deleted due to interactions triggered by other spreaders.
+ if (!xforms.TryGetComponent(uid, out var xform))
+ continue;
+
+ if (xform.GridUid == null)
+ {
+ RemComp(uid, comp);
+ continue;
+ }
+
+ if (!_gridUpdates.TryGetValue(xform.GridUid.Value, out var groupUpdates))
+ continue;
+
+ if (!spreaderQuery.TryGetComponent(uid, out var spreader))
+ {
+ RemComp(uid, comp);
+ 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
+ Spread(uid, xform, spreader.Id, ref updates);
+
+ if (updates < 1)
+ groupUpdates.Remove(spreader.Id);
+ else
+ groupUpdates[spreader.Id] = updates;
+ }
+ }
+
+ private void Spread(EntityUid uid, TransformComponent xform, ProtoId prototype, ref int updates)
+ {
+ GetNeighbors(uid, xform, prototype, out var freeTiles, out _, out var neighbors);
+
+ var ev = new SupermatterSpreadNeighborsEvent()
+ {
+ NeighborFreeTiles = freeTiles,
+ Neighbors = neighbors,
+ Updates = updates,
+ };
+
+ RaiseLocalEvent(uid, ref ev);
+ updates = ev.Updates;
+ }
+
+ ///
+ /// Gets the neighboring node data for the specified entity and the specified node group.
+ ///
+ public void GetNeighbors(EntityUid uid, TransformComponent comp, ProtoId prototype, out ValueList<(MapGridComponent, TileRef)> freeTiles, out ValueList occupiedTiles, out ValueList neighbors)
+ {
+ freeTiles = [];
+ occupiedTiles = [];
+ neighbors = [];
+ // TODO remove occupiedTiles -- its currently unused and just slows this method down.
+ if (!_prototype.TryIndex(prototype, out var spreaderPrototype))
+ return;
+
+ if (!TryComp(comp.GridUid, out var grid))
+ return;
+
+ var tile = _map.TileIndicesFor(comp.GridUid.Value, grid, comp.Coordinates);
+ var spreaderQuery = GetEntityQuery();
+ var airtightQuery = GetEntityQuery();
+ var dockQuery = GetEntityQuery();
+ var xformQuery = GetEntityQuery();
+ var blockedAtmosDirs = AtmosDirection.Invalid;
+
+ // Due to docking ports they may not necessarily be opposite directions.
+ var neighborTiles = new ValueList<(EntityUid entity, MapGridComponent grid, Vector2i Indices, AtmosDirection OtherDir, AtmosDirection OurDir)>();
+
+ // Check if anything on our own tile blocking that direction.
+ var ourEnts = _map.GetAnchoredEntitiesEnumerator(comp.GridUid.Value, grid, tile);
+
+ while (ourEnts.MoveNext(out var ent))
+ {
+ // Spread via docks in a special-case.
+ if (dockQuery.TryGetComponent(ent, out var dock) &&
+ dock.Docked &&
+ xformQuery.TryGetComponent(ent, out var xform) &&
+ xformQuery.TryGetComponent(dock.DockedWith, out var dockedXform) &&
+ TryComp(dockedXform.GridUid, out var dockedGrid))
+ {
+ neighborTiles.Add((dockedXform.GridUid.Value, dockedGrid, _map.CoordinatesToTile(dockedXform.GridUid.Value, dockedGrid, dockedXform.Coordinates), xform.LocalRotation.ToAtmosDirection(), dockedXform.LocalRotation.ToAtmosDirection()));
+ }
+
+ // If we're on a blocked tile work out which directions we can go.
+ if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked ||
+ _tag.HasTag(ent.Value, IgnoredTag))
+ {
+ continue;
+ }
+
+ foreach (var value in new[] { AtmosDirection.North, AtmosDirection.East, AtmosDirection.South, AtmosDirection.West })
+ {
+ if ((value & airtight.AirBlockedDirection) == 0x0)
+ continue;
+
+ blockedAtmosDirs |= value;
+ break;
+ }
+ break;
+ }
+
+ // Add the normal neighbors.
+ for (var i = 0; i < 4; i++)
+ {
+ var atmosDir = (AtmosDirection) (1 << i);
+ var neighborPos = tile.Offset(atmosDir);
+ neighborTiles.Add((comp.GridUid.Value, grid, neighborPos, atmosDir, i.ToOppositeDir()));
+ }
+
+ foreach (var (neighborEnt, neighborGrid, neighborPos, ourAtmosDir, otherAtmosDir) in neighborTiles)
+ {
+ // This tile is blocked to that direction.
+ if ((blockedAtmosDirs & ourAtmosDir) != 0x0)
+ continue;
+
+ if (!_map.TryGetTileRef(neighborEnt, neighborGrid, neighborPos, out var tileRef) || tileRef.Tile.IsEmpty)
+ continue;
+
+ if (spreaderPrototype.PreventSpreadOnSpaced && tileRef.Tile.IsSpace())
+ continue;
+
+ var directionEnumerator = _map.GetAnchoredEntitiesEnumerator(neighborEnt, neighborGrid, neighborPos);
+ var occupied = false;
+
+ while (directionEnumerator.MoveNext(out var ent))
+ {
+ if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked || _tag.HasTag(ent.Value, IgnoredTag))
+ {
+ continue;
+ }
+
+ if ((airtight.AirBlockedDirection & otherAtmosDir) == 0x0)
+ continue;
+
+ occupied = true;
+ break;
+ }
+
+ if (occupied)
+ continue;
+
+ var oldCount = occupiedTiles.Count;
+ directionEnumerator = _map.GetAnchoredEntitiesEnumerator(neighborEnt, neighborGrid, neighborPos);
+
+ while (directionEnumerator.MoveNext(out var ent))
+ {
+ if (!spreaderQuery.TryGetComponent(ent, out var spreader))
+ continue;
+
+ if (spreader.Id != prototype)
+ continue;
+
+ neighbors.Add(ent.Value);
+ occupiedTiles.Add(neighborPos);
+ break;
+ }
+
+ if (oldCount == occupiedTiles.Count)
+ freeTiles.Add((neighborGrid, tileRef));
+ }
+ }
+
+ ///
+ /// This function activates all spreaders that are adjacent to a given entity. This also activates other spreaders
+ /// on the same tile as the current entity (for thin airtight entities like windoors).
+ ///
+ public void ActivateSpreadableNeighbors(EntityUid uid, (EntityUid Grid, Vector2i Tile)? position = null)
+ {
+ Vector2i tile;
+ EntityUid ent;
+ MapGridComponent? grid;
+
+ if (position == null)
+ {
+ var transform = Transform(uid);
+ if (!TryComp(transform.GridUid, out grid) || TerminatingOrDeleted(transform.GridUid.Value))
+ return;
+
+ tile = _map.TileIndicesFor(transform.GridUid.Value, grid, transform.Coordinates);
+ ent = transform.GridUid.Value;
+ }
+ else
+ {
+ if (!TryComp(position.Value.Grid, out grid))
+ return;
+ (ent, tile) = position.Value;
+ }
+
+ var anchored = _map.GetAnchoredEntitiesEnumerator(ent, grid, tile);
+ while (anchored.MoveNext(out var entity))
+ {
+ if (entity == ent)
+ continue;
+ DebugTools.Assert(Transform(entity.Value).Anchored);
+ if (_query.HasComponent(ent) && !TerminatingOrDeleted(entity.Value))
+ EnsureComp(entity.Value);
+ }
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection) (1 << i);
+ var adjacentTile = SharedMapSystem.GetDirection(tile, direction.ToDirection());
+ anchored = _map.GetAnchoredEntitiesEnumerator(ent, grid, adjacentTile);
+
+ while (anchored.MoveNext(out var entity))
+ {
+ DebugTools.Assert(Transform(entity.Value).Anchored);
+ if (_query.HasComponent(ent) && !TerminatingOrDeleted(entity.Value))
+ EnsureComp(entity.Value);
+ }
+ }
+ }
+
+ public bool RequiresFloorToSpread(EntProtoId spreader)
+ {
+ if (!_prototype.Index(spreader).TryGetComponent(out var spreaderComp, EntityManager.ComponentFactory))
+ return false;
+
+ return _prototype.Index(spreaderComp.Id).PreventSpreadOnSpaced;
+ }
+}
diff --git a/Content.Server/Spreader/SupermatterKudzuSystem.cs b/Content.Server/Spreader/SupermatterKudzuSystem.cs
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/Content.Shared/ADT/Spreader/EdgeSupermatterSpreaderPrototype.cs b/Content.Shared/ADT/Spreader/EdgeSupermatterSpreaderPrototype.cs
new file mode 100644
index 00000000000..20dd2b432ab
--- /dev/null
+++ b/Content.Shared/ADT/Spreader/EdgeSupermatterSpreaderPrototype.cs
@@ -0,0 +1,19 @@
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.ADT.Spreader;
+
+///
+/// Adds this node group to for tick updates.
+///
+[Prototype("edgeSupermatterSpreader")]
+public sealed partial class EdgeSupermatterSpreaderPrototype : IPrototype
+{
+ [IdDataField] public string ID { get; } = string.Empty;
+ [DataField(required:true)] public int UpdatesPerSecond;
+
+ ///
+ /// If true, this spreader can't spread onto spaced tiles like lattice.
+ ///
+ [DataField]
+ public bool PreventSpreadOnSpaced = true;
+}
\ No newline at end of file
diff --git a/Content.Shared/ADT/Spreader/SupermatterKudzuVisuals.cs b/Content.Shared/ADT/Spreader/SupermatterKudzuVisuals.cs
new file mode 100644
index 00000000000..e3c6e419a24
--- /dev/null
+++ b/Content.Shared/ADT/Spreader/SupermatterKudzuVisuals.cs
@@ -0,0 +1,10 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.ADT.Spreader;
+
+[Serializable, NetSerializable]
+public enum SupermatterKudzuVisuals : byte
+{
+ GrowthLevel,
+ Variant
+}
diff --git a/Resources/Prototypes/ADT/Entities/Structures/Power/Generation/Supermatter/SupermatterKudzu.yml b/Resources/Prototypes/ADT/Entities/Structures/Power/Generation/Supermatter/SupermatterKudzu.yml
index e190c22dabd..5eeae9b37df 100644
--- a/Resources/Prototypes/ADT/Entities/Structures/Power/Generation/Supermatter/SupermatterKudzu.yml
+++ b/Resources/Prototypes/ADT/Entities/Structures/Power/Generation/Supermatter/SupermatterKudzu.yml
@@ -1,7 +1,25 @@
+- type: entity
+ id: BaseSupermatterKudzu
+ abstract: true
+ placement:
+ mode: SnapgridCenter
+ snap:
+ - Wall
+ components:
+ - type: Appearance
+ - type: Transform
+ anchored: true
+ - type: Physics
+ - type: SupermatterKudzu
+ - type: GrowingSupermatterKudzu
+ - type: ActiveEdgeSupermatterSpreader
+ - type: EdgeSupermatterSpreader
+ id: SupermatterKudzu
+
- type: entity
id: SupermatterKudzu
name: Сверхматерия?
- parent: BaseKudzu
+ parent: BaseSupermatterKudzu
description: Это такой конец...
placement:
mode: SnapgridCenter
@@ -35,8 +53,8 @@
Radiation: 10
ignoreWhitelist:
tags:
- - Flesh
- - type: Kudzu
+ - SupermatterKudzu
+ - type: SupermatterKudzu
growthTickChance: 0.1
spreadChance: 0.4
- type: SpeedModifierContacts
@@ -44,7 +62,7 @@
sprintSpeedModifier: 0.3
ignoreWhitelist:
tags:
- - Flesh
+ - SupermatterKudzu
- type: Respirator
damage:
types: