]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Anomalous infections (#31876)
authorEd <96445749+TheShuEd@users.noreply.github.com>
Tue, 17 Sep 2024 09:49:19 +0000 (12:49 +0300)
committerGitHub <noreply@github.com>
Tue, 17 Sep 2024 09:49:19 +0000 (12:49 +0300)
* inner anomaly

* anomaly pulse action

* test anom mine

* Update anomalies.yml

* fix action cooldown

* pyro_eyes

* clientsystem

* experiments

* blya

* some telegraphy

* shock eyes!

* shadow eyes

* separate files

* frosty eyes

* fix

* flora eyes

* bluespace eyes

* flesh eyes

* redoing injction

* auto add layers

* пипяу

* new injector component

* stupid me

* nice marker injectors

* anomaly spawn on shutdown

* gravity anom

* dead anomaly spawning

* add VOX states

* sprite specific layers support

* technology anom infection

* auto detach anomalies that have moved away

* Update anomaly_injections.yml

* anomalyspawner integration

* rock anomaly!

* Update anomaly_injections.yml

* fix crash bug

* tag filter

* fix anom dublication spawns

* Update anomaly.yml

* Update InnerBodyAnomalyComponent.cs

* Update anomaly_injections.yml

* dont spawn anomalies after decay

* fix morb sprite, add end message

* gravity resprite

* admin logging, double injection fix

* make flesh and living light mobs friendly to anomaly hosts

* popups

* severity feedback

* sloth review

* A

* keep organs after gib

* punpun host

* sloth synchronization

* Update arachnid.yml

* increase infections spawnrate

49 files changed:
Content.Client/Anomaly/Effects/ClientInnerBodySystem.cs [new file with mode: 0644]
Content.Server/Anomaly/AnomalySynchronizerSystem.cs
Content.Server/Anomaly/AnomalySystem.cs
Content.Server/Anomaly/Components/AnomalySynchronizerComponent.cs
Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs [new file with mode: 0644]
Content.Server/Explosion/EntitySystems/TriggerSystem.cs
Content.Shared/Anomaly/Components/AnomalyComponent.cs
Content.Shared/Anomaly/Components/InnerBodyAnomalyComponent.cs [new file with mode: 0644]
Content.Shared/Anomaly/Components/InnerBodyAnomalyInjectorComponent.cs [new file with mode: 0644]
Content.Shared/Anomaly/Effects/SharedInnerBodyAnomalySystem.cs [new file with mode: 0644]
Content.Shared/Anomaly/SharedAnomalySystem.cs
Resources/Audio/Effects/attributions.yml
Resources/Audio/Effects/inneranomaly.ogg [new file with mode: 0644]
Resources/Locale/en-US/anomaly/inner_anomaly.ftl [new file with mode: 0644]
Resources/Prototypes/Actions/anomaly.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml
Resources/Prototypes/Entities/Mobs/NPCs/animals.yml
Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml
Resources/Prototypes/Entities/Mobs/NPCs/living_light.yml
Resources/Prototypes/Entities/Mobs/NPCs/pets.yml
Resources/Prototypes/Entities/Mobs/Species/arachnid.yml
Resources/Prototypes/Entities/Mobs/Species/base.yml
Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml
Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomaly_injections.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomaly_injectors.yml [new file with mode: 0644]
Resources/Prototypes/tags.yml
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/bluespace.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/bluespace_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/fire.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/fire_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flesh.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flesh_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flora.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flora_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/frost.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/frost_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/grav.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/grav_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/rock.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/rock_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shadow.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shadow_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shock.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shock_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/tech.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/tech_VOX.png [new file with mode: 0644]
Resources/Textures/Structures/Specific/anomaly.rsi/anom5-pulse.png
Resources/Textures/Structures/Specific/anomaly.rsi/anom5.png

diff --git a/Content.Client/Anomaly/Effects/ClientInnerBodySystem.cs b/Content.Client/Anomaly/Effects/ClientInnerBodySystem.cs
new file mode 100644 (file)
index 0000000..efb1a8d
--- /dev/null
@@ -0,0 +1,50 @@
+using Content.Shared.Anomaly.Components;
+using Content.Shared.Anomaly.Effects;
+using Content.Shared.Body.Components;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Anomaly.Effects;
+
+public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
+{
+    public override void Initialize()
+    {
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, AfterAutoHandleStateEvent>(OnAfterHandleState);
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, ComponentShutdown>(OnCompShutdown);
+    }
+
+    private void OnAfterHandleState(Entity<InnerBodyAnomalyComponent> ent, ref AfterAutoHandleStateEvent args)
+    {
+        if (!TryComp<SpriteComponent>(ent, out var sprite))
+            return;
+
+        if (ent.Comp.FallbackSprite is null)
+            return;
+
+        if (!sprite.LayerMapTryGet(ent.Comp.LayerMap, out var index))
+            index = sprite.LayerMapReserveBlank(ent.Comp.LayerMap);
+
+        if (TryComp<BodyComponent>(ent, out var body) &&
+            body.Prototype is not null &&
+            ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
+        {
+            sprite.LayerSetSprite(index, speciesSprite);
+        }
+        else
+        {
+            sprite.LayerSetSprite(index, ent.Comp.FallbackSprite);
+        }
+
+        sprite.LayerSetVisible(index, true);
+        sprite.LayerSetShader(index, "unshaded");
+    }
+
+    private void OnCompShutdown(Entity<InnerBodyAnomalyComponent> ent, ref ComponentShutdown args)
+    {
+        if (!TryComp<SpriteComponent>(ent, out var sprite))
+            return;
+
+        var index = sprite.LayerMapGet(ent.Comp.LayerMap);
+        sprite.LayerSetVisible(index, false);
+    }
+}
index 59ef08402ee2d7581ae61db11d53f232e016e8e5..b1814c2741d0e49061033101594f0378938808f3 100644 (file)
@@ -1,4 +1,5 @@
 using System.Linq;
+using System.Numerics;
 using Content.Server.Anomaly.Components;
 using Content.Server.DeviceLinking.Systems;
 using Content.Server.Power.Components;
@@ -10,6 +11,7 @@ using Content.Shared.Popups;
 using Content.Shared.Power;
 using Robust.Shared.Audio.Systems;
 using Content.Shared.Verbs;
+using Robust.Shared.Timing;
 
 namespace Content.Server.Anomaly;
 
@@ -25,6 +27,7 @@ public sealed partial class AnomalySynchronizerSystem : EntitySystem
     [Dependency] private readonly SharedTransformSystem _transform = default!;
     [Dependency] private readonly SharedPopupSystem _popup = default!;
     [Dependency] private readonly PowerReceiverSystem _power = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
 
     public override void Initialize()
     {
@@ -40,6 +43,34 @@ public sealed partial class AnomalySynchronizerSystem : EntitySystem
         SubscribeLocalEvent<AnomalyStabilityChangedEvent>(OnAnomalyStabilityChanged);
     }
 
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<AnomalySynchronizerComponent, TransformComponent>();
+        while (query.MoveNext(out var uid, out var sync, out var xform))
+        {
+            if (sync.ConnectedAnomaly is null)
+                continue;
+
+            if (_timing.CurTime < sync.NextCheckTime)
+                continue;
+            sync.NextCheckTime += sync.CheckFrequency;
+
+            if (Transform(sync.ConnectedAnomaly.Value).MapUid != Transform(uid).MapUid)
+            {
+                DisconnectFromAnomaly((uid, sync), sync.ConnectedAnomaly.Value);
+                continue;
+            }
+
+            if (!xform.Coordinates.TryDistance(EntityManager, Transform(sync.ConnectedAnomaly.Value).Coordinates, out var distance))
+                continue;
+
+            if (distance > sync.AttachRange)
+                DisconnectFromAnomaly((uid, sync), sync.ConnectedAnomaly.Value);
+        }
+    }
+
     /// <summary>
     /// If powered, try to attach a nearby anomaly.
     /// </summary>
@@ -73,10 +104,10 @@ public sealed partial class AnomalySynchronizerSystem : EntitySystem
         if (args.Powered)
             return;
 
-        if (!TryComp<AnomalyComponent>(ent.Comp.ConnectedAnomaly, out var anomaly))
+        if (ent.Comp.ConnectedAnomaly is null)
             return;
 
-        DisconnectFromAnomaly(ent, anomaly);
+        DisconnectFromAnomaly(ent, ent.Comp.ConnectedAnomaly.Value);
     }
 
     private void OnExamined(Entity<AnomalySynchronizerComponent> ent, ref ExaminedEvent args)
@@ -125,13 +156,16 @@ public sealed partial class AnomalySynchronizerSystem : EntitySystem
 
     //TODO: disconnection from the anomaly should also be triggered if the anomaly is far away from the synchronizer.
     //Currently only bluespace anomaly can do this, but for some reason it is the only one that cannot be connected to the synchronizer.
-    private void DisconnectFromAnomaly(Entity<AnomalySynchronizerComponent> ent, AnomalyComponent anomaly)
+    private void DisconnectFromAnomaly(Entity<AnomalySynchronizerComponent> ent, EntityUid other)
     {
         if (ent.Comp.ConnectedAnomaly == null)
             return;
 
-        if (ent.Comp.PulseOnDisconnect)
-            _anomaly.DoAnomalyPulse(ent.Comp.ConnectedAnomaly.Value, anomaly);
+        if (TryComp<AnomalyComponent>(other, out var anomaly))
+        {
+            if (ent.Comp.PulseOnDisconnect)
+                _anomaly.DoAnomalyPulse(ent.Comp.ConnectedAnomaly.Value, anomaly);
+        }
 
         _popup.PopupEntity(Loc.GetString("anomaly-sync-disconnected"), ent, PopupType.Large);
         _audio.PlayPvs(ent.Comp.ConnectedSound, ent);
index 3e9760a056c022c409a44018b99f8cef4e36cad0..b0de3de8f3e511dafe5297543468d40f3b007158 100644 (file)
@@ -55,6 +55,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
         SubscribeLocalEvent<AnomalyComponent, ComponentShutdown>(OnShutdown);
         SubscribeLocalEvent<AnomalyComponent, StartCollideEvent>(OnStartCollide);
 
+
         InitializeGenerator();
         InitializeScanner();
         InitializeVessel();
@@ -86,7 +87,10 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
 
     private void OnShutdown(Entity<AnomalyComponent> anomaly, ref ComponentShutdown args)
     {
-        EndAnomaly(anomaly);
+        if (anomaly.Comp.CurrentBehavior is not null)
+            RemoveBehavior(anomaly, anomaly.Comp.CurrentBehavior.Value);
+
+        EndAnomaly(anomaly, spawnCore: false);
     }
 
     private void OnStartCollide(Entity<AnomalyComponent> anomaly, ref StartCollideEvent args)
index 235e740cf35836914dad1a0db2d7691dad235e10..3127f091e5539db1d9768912e6b32bf9a030e41b 100644 (file)
@@ -7,7 +7,7 @@ namespace Content.Server.Anomaly.Components;
 /// <summary>
 /// a device that allows you to translate anomaly activity into multitool signals.
 /// </summary>
-[RegisterComponent, Access(typeof(AnomalySynchronizerSystem))]
+[RegisterComponent, AutoGenerateComponentPause, Access(typeof(AnomalySynchronizerSystem))]
 public sealed partial class AnomalySynchronizerComponent : Component
 {
     /// <summary>
@@ -34,6 +34,15 @@ public sealed partial class AnomalySynchronizerComponent : Component
     [DataField]
     public float AttachRange = 0.4f;
 
+    /// <summary>
+    /// Periodicheski checks to see if the anomaly has moved to disconnect it.
+    /// </summary>
+    [DataField]
+    public TimeSpan CheckFrequency = TimeSpan.FromSeconds(1f);
+
+    [DataField, AutoPausedField]
+    public TimeSpan NextCheckTime = TimeSpan.Zero;
+
     [DataField]
     public ProtoId<SourcePortPrototype> DecayingPort = "Decaying";
 
diff --git a/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs b/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs
new file mode 100644 (file)
index 0000000..38c4c51
--- /dev/null
@@ -0,0 +1,236 @@
+using Content.Server.Administration.Logs;
+using Content.Server.Body.Systems;
+using Content.Server.Chat.Managers;
+using Content.Server.Jittering;
+using Content.Server.Mind;
+using Content.Server.Stunnable;
+using Content.Shared.Actions;
+using Content.Shared.Anomaly;
+using Content.Shared.Anomaly.Components;
+using Content.Shared.Anomaly.Effects;
+using Content.Shared.Body.Components;
+using Content.Shared.Chat;
+using Content.Shared.Database;
+using Content.Shared.Mobs;
+using Content.Shared.Popups;
+using Content.Shared.Whitelist;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Anomaly.Effects;
+
+public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
+{
+    [Dependency] private readonly IAdminLogManager _adminLog = default!;
+    [Dependency] private readonly AnomalySystem _anomaly = default!;
+    [Dependency] private readonly ActionContainerSystem _actionContainer = default!;
+    [Dependency] private readonly SharedActionsSystem _actions = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+    [Dependency] private readonly BodySystem _body = default!;
+    [Dependency] private readonly IChatManager _chat = default!;
+    [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
+    [Dependency] private readonly JitteringSystem _jitter = default!;
+    [Dependency] private readonly MindSystem _mind = default!;
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly IPrototypeManager _proto = default!;
+    [Dependency] private readonly StunSystem _stun = default!;
+
+    private readonly Color _messageColor = Color.FromSrgb(new Color(201, 22, 94));
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<InnerBodyAnomalyInjectorComponent, StartCollideEvent>(OnStartCollideInjector);
+
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, ComponentShutdown>(OnCompShutdown);
+
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, AnomalyPulseEvent>(OnAnomalyPulse);
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, AnomalyShutdownEvent>(OnAnomalyShutdown);
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, AnomalySupercriticalEvent>(OnAnomalySupercritical);
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, AnomalySeverityChangedEvent>(OnSeverityChanged);
+
+        SubscribeLocalEvent<InnerBodyAnomalyComponent, MobStateChangedEvent>(OnMobStateChanged);
+
+        SubscribeLocalEvent<AnomalyComponent, ActionAnomalyPulseEvent>(OnActionPulse);
+    }
+
+    private void OnActionPulse(Entity<AnomalyComponent> ent, ref ActionAnomalyPulseEvent args)
+    {
+        if (args.Handled)
+            return;
+
+        _anomaly.DoAnomalyPulse(ent, ent.Comp);
+
+        args.Handled = true;
+    }
+
+    private void OnStartCollideInjector(Entity<InnerBodyAnomalyInjectorComponent> ent, ref StartCollideEvent args)
+    {
+        if (ent.Comp.Whitelist is not null && !_whitelist.IsValid(ent.Comp.Whitelist, args.OtherEntity))
+            return;
+        if (TryComp<InnerBodyAnomalyComponent>(args.OtherEntity, out var innerAnom) && innerAnom.Injected)
+            return;
+        if (!_mind.TryGetMind(args.OtherEntity, out _, out var mindComponent))
+            return;
+
+        EntityManager.AddComponents(args.OtherEntity, ent.Comp.InjectionComponents);
+        QueueDel(ent);
+    }
+
+    private void OnMapInit(Entity<InnerBodyAnomalyComponent> ent, ref MapInitEvent args)
+    {
+        AddAnomalyToBody(ent);
+    }
+
+    private void AddAnomalyToBody(Entity<InnerBodyAnomalyComponent> ent)
+    {
+        if (!_proto.TryIndex(ent.Comp.InjectionProto, out var injectedAnom))
+            return;
+
+        if (ent.Comp.Injected)
+            return;
+
+        ent.Comp.Injected = true;
+
+        EntityManager.AddComponents(ent, injectedAnom.Components);
+
+        _stun.TryParalyze(ent, TimeSpan.FromSeconds(ent.Comp.StunDuration), true);
+        _jitter.DoJitter(ent, TimeSpan.FromSeconds(ent.Comp.StunDuration), true);
+
+        if (ent.Comp.StartSound is not null)
+            _audio.PlayPvs(ent.Comp.StartSound, ent);
+
+        if (ent.Comp.StartMessage is not null &&
+            _mind.TryGetMind(ent, out _, out var mindComponent) &&
+            mindComponent.Session != null)
+        {
+            var message = Loc.GetString(ent.Comp.StartMessage);
+            var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
+            _chat.ChatMessageToOne(ChatChannel.Server,
+                message,
+                wrappedMessage,
+                default,
+                false,
+                mindComponent.Session.Channel,
+                _messageColor);
+
+            _popup.PopupEntity(message, ent, ent, PopupType.MediumCaution);
+
+            _adminLog.Add(LogType.Anomaly,LogImpact.Extreme,$"{ToPrettyString(ent)} became anomaly host.");
+        }
+        Dirty(ent);
+    }
+
+    private void OnAnomalyPulse(Entity<InnerBodyAnomalyComponent> ent, ref AnomalyPulseEvent args)
+    {
+        _stun.TryParalyze(ent, TimeSpan.FromSeconds(ent.Comp.StunDuration / 2 * args.Severity), true);
+        _jitter.DoJitter(ent, TimeSpan.FromSeconds(ent.Comp.StunDuration / 2 * args.Severity), true);
+    }
+
+    private void OnAnomalySupercritical(Entity<InnerBodyAnomalyComponent> ent, ref AnomalySupercriticalEvent args)
+    {
+        if (!TryComp<BodyComponent>(ent, out var body))
+            return;
+
+        _body.GibBody(ent, true, body, splatModifier: 5f);
+    }
+
+    private void OnSeverityChanged(Entity<InnerBodyAnomalyComponent> ent, ref AnomalySeverityChangedEvent args)
+    {
+        if (!_mind.TryGetMind(ent, out _, out var mindComponent) || mindComponent.Session == null)
+            return;
+
+        var message = string.Empty;
+
+        if (args.Severity >= 0.5 && ent.Comp.LastSeverityInformed < 0.5)
+        {
+            ent.Comp.LastSeverityInformed = 0.5f;
+            message = Loc.GetString("inner-anomaly-severity-info-50");
+        }
+        if (args.Severity >= 0.75 && ent.Comp.LastSeverityInformed < 0.75)
+        {
+            ent.Comp.LastSeverityInformed = 0.75f;
+            message = Loc.GetString("inner-anomaly-severity-info-75");
+        }
+        if (args.Severity >= 0.9 && ent.Comp.LastSeverityInformed < 0.9)
+        {
+            ent.Comp.LastSeverityInformed = 0.9f;
+            message = Loc.GetString("inner-anomaly-severity-info-90");
+        }
+        if (args.Severity >= 1 && ent.Comp.LastSeverityInformed < 1)
+        {
+            ent.Comp.LastSeverityInformed = 1f;
+            message = Loc.GetString("inner-anomaly-severity-info-100");
+        }
+
+        if (message == string.Empty)
+            return;
+
+        var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
+        _chat.ChatMessageToOne(ChatChannel.Server,
+            message,
+            wrappedMessage,
+            default,
+            false,
+            mindComponent.Session.Channel,
+            _messageColor);
+
+        _popup.PopupEntity(message, ent, ent, PopupType.MediumCaution);
+    }
+
+    private void OnMobStateChanged(Entity<InnerBodyAnomalyComponent> ent, ref MobStateChangedEvent args)
+    {
+        if (args.NewMobState != MobState.Dead)
+            return;
+
+        _anomaly.ChangeAnomalyHealth(ent, -2); //Shutdown it
+    }
+
+    private void OnAnomalyShutdown(Entity<InnerBodyAnomalyComponent> ent, ref AnomalyShutdownEvent args)
+    {
+        RemoveAnomalyFromBody(ent);
+        RemCompDeferred<InnerBodyAnomalyComponent>(ent);
+    }
+
+    private void OnCompShutdown(Entity<InnerBodyAnomalyComponent> ent, ref ComponentShutdown args)
+    {
+        RemoveAnomalyFromBody(ent);
+    }
+
+    private void RemoveAnomalyFromBody(Entity<InnerBodyAnomalyComponent> ent)
+    {
+        if (!ent.Comp.Injected)
+            return;
+
+        if (_proto.TryIndex(ent.Comp.InjectionProto, out var injectedAnom))
+            EntityManager.RemoveComponents(ent, injectedAnom.Components);
+
+        _stun.TryParalyze(ent, TimeSpan.FromSeconds(ent.Comp.StunDuration), true);
+
+        if (ent.Comp.EndMessage is not null &&
+            _mind.TryGetMind(ent, out _, out var mindComponent) &&
+            mindComponent.Session != null)
+        {
+            var message = Loc.GetString(ent.Comp.EndMessage);
+            var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
+            _chat.ChatMessageToOne(ChatChannel.Server,
+                message,
+                wrappedMessage,
+                default,
+                false,
+                mindComponent.Session.Channel,
+                _messageColor);
+
+
+            _popup.PopupEntity(message, ent, ent, PopupType.MediumCaution);
+
+            _adminLog.Add(LogType.Anomaly, LogImpact.Medium,$"{ToPrettyString(ent)} is no longer a host for the anomaly.");
+        }
+
+        ent.Comp.Injected = false;
+        RemCompDeferred<AnomalyComponent>(ent);
+    }
+}
index 853b4fbc29d5a866b9efe1d50b16c9ab4022608a..57847551aa70fe6c2d08f5124c288f31758469b5 100644 (file)
@@ -202,6 +202,7 @@ namespace Content.Server.Explosion.EntitySystems
             args.Handled = true;
         }
 
+
         private void HandleRattleTrigger(EntityUid uid, RattleComponent component, TriggerEvent args)
         {
             if (!TryComp<SubdermalImplantComponent>(uid, out var implanted))
@@ -230,7 +231,7 @@ namespace Content.Server.Explosion.EntitySystems
         private void OnTriggerCollide(EntityUid uid, TriggerOnCollideComponent component, ref StartCollideEvent args)
         {
             if (args.OurFixtureId == component.FixtureID && (!component.IgnoreOtherNonHard || args.OtherFixture.Hard))
-                Trigger(uid);
+                Trigger(uid, args.OtherEntity);
         }
 
         private void OnSpawnTriggered(EntityUid uid, TriggerOnSpawnComponent component, MapInitEvent args)
index 724dfd38d2f6609314e3e87a65a0be365ca7eddc..e6228b5fb0d77a97ef8071a134bd1f730206e9d7 100644 (file)
@@ -1,6 +1,6 @@
 using System.Numerics;
+using Content.Shared.Anomaly.Effects;
 using Content.Shared.Anomaly.Prototypes;
-using Content.Shared.Damage;
 using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
@@ -16,7 +16,7 @@ namespace Content.Shared.Anomaly.Components;
 /// Anomalies and their related components were designed here: https://hackmd.io/@ss14-design/r1sQbkJOs
 /// </summary>
 [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
-[Access(typeof(SharedAnomalySystem))]
+[Access(typeof(SharedAnomalySystem), typeof(SharedInnerBodyAnomalySystem))]
 public sealed partial class AnomalyComponent : Component
 {
     /// <summary>
@@ -184,21 +184,21 @@ public sealed partial class AnomalyComponent : Component
     /// <summary>
     /// The minimum amount of research points generated per second
     /// </summary>
-    [DataField("minPointsPerSecond")]
+    [DataField]
     public int MinPointsPerSecond = 10;
 
     /// <summary>
     /// The maximum amount of research points generated per second
     /// This doesn't include the point bonus for being unstable.
     /// </summary>
-    [DataField("maxPointsPerSecond")]
+    [DataField]
     public int MaxPointsPerSecond = 70;
 
     /// <summary>
     /// The multiplier applied to the point value for the
     /// anomaly being above the <see cref="GrowthThreshold"/>
     /// </summary>
-    [DataField("growingPointMultiplier")]
+    [DataField]
     public float GrowingPointMultiplier = 1.5f;
     #endregion
 
@@ -252,10 +252,13 @@ public sealed partial class AnomalyComponent : Component
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite)]
     [DataField("offset")]
-    public Vector2 FloatingOffset = new(0, 0.15f);
+    public Vector2 FloatingOffset = new(0, 0);
 
     public readonly string AnimationKey = "anomalyfloat";
     #endregion
+
+    [DataField]
+    public bool DeleteEntity = true;
 }
 
 /// <summary>
diff --git a/Content.Shared/Anomaly/Components/InnerBodyAnomalyComponent.cs b/Content.Shared/Anomaly/Components/InnerBodyAnomalyComponent.cs
new file mode 100644 (file)
index 0000000..e88cedb
--- /dev/null
@@ -0,0 +1,72 @@
+using Content.Shared.Anomaly.Effects;
+using Content.Shared.Body.Prototypes;
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Anomaly.Components;
+
+/// <summary>
+/// An anomaly within the body of a living being. Controls the ability to return to the standard state.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(SharedInnerBodyAnomalySystem))]
+public sealed partial class InnerBodyAnomalyComponent : Component
+{
+    [DataField]
+    public bool Injected;
+
+    /// <summary>
+    /// A prototype of an entity whose components will be added to the anomaly host **AND** then removed at the right time
+    /// </summary>
+    [DataField(required: true)]
+    public EntProtoId? InjectionProto;
+
+    /// <summary>
+    /// Duration of stun from the effect of the anomaly
+    /// </summary>
+    [DataField]
+    public float StunDuration = 4f;
+
+    /// <summary>
+    /// A message sent in chat to a player who has become infected by an anomaly
+    /// </summary>
+    [DataField]
+    public LocId? StartMessage = null;
+
+    /// <summary>
+    /// A message sent in chat to a player who has cleared an anomaly
+    /// </summary>
+    [DataField]
+    public LocId? EndMessage = "inner-anomaly-end-message";
+
+    /// <summary>
+    /// Sound, playing on becoming anomaly
+    /// </summary>
+    [DataField]
+    public SoundSpecifier? StartSound = new SoundPathSpecifier("/Audio/Effects/inneranomaly.ogg");
+
+    /// <summary>
+    /// Used to display messages to the player about their level of disease progression
+    /// </summary>
+    [DataField]
+    public float LastSeverityInformed = 0f;
+
+    /// <summary>
+    /// The fallback sprite to be added on the original entity. Allows you to visually identify the feature and type of anomaly to other players
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public SpriteSpecifier? FallbackSprite = null;
+
+    /// <summary>
+    /// Ability to use unique sprites for different body types
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public Dictionary<ProtoId<BodyPrototype>, SpriteSpecifier> SpeciesSprites = new();
+
+    /// <summary>
+    /// The key of the entity layer into which the sprite will be inserted
+    /// </summary>
+    [DataField]
+    public string LayerMap = "inner_anomaly_layer";
+}
diff --git a/Content.Shared/Anomaly/Components/InnerBodyAnomalyInjectorComponent.cs b/Content.Shared/Anomaly/Components/InnerBodyAnomalyInjectorComponent.cs
new file mode 100644 (file)
index 0000000..e4c398c
--- /dev/null
@@ -0,0 +1,21 @@
+using Content.Shared.Anomaly.Effects;
+using Content.Shared.Whitelist;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Anomaly.Components;
+
+/// <summary>
+/// On contact with an entity, if it meets the conditions, it will transfer the specified components to it
+/// </summary>
+[RegisterComponent, Access(typeof(SharedInnerBodyAnomalySystem))]
+public sealed partial class InnerBodyAnomalyInjectorComponent : Component
+{
+    [DataField]
+    public EntityWhitelist? Whitelist;
+
+    /// <summary>
+    /// components that will be automatically removed after “curing”
+    /// </summary>
+    [DataField(required: true)]
+    public ComponentRegistry InjectionComponents = default!;
+}
diff --git a/Content.Shared/Anomaly/Effects/SharedInnerBodyAnomalySystem.cs b/Content.Shared/Anomaly/Effects/SharedInnerBodyAnomalySystem.cs
new file mode 100644 (file)
index 0000000..a1ec7cd
--- /dev/null
@@ -0,0 +1,5 @@
+namespace Content.Shared.Anomaly.Effects;
+
+public abstract class SharedInnerBodyAnomalySystem : EntitySystem
+{
+}
index c3d6591b7252de8e63c8954b1b025725128cd8af..9a0cde29988cb3605fe1866c2e7292f43455e734 100644 (file)
@@ -1,13 +1,10 @@
 using Content.Shared.Administration.Logs;
 using Content.Shared.Anomaly.Components;
 using Content.Shared.Anomaly.Prototypes;
-using Content.Shared.Damage;
 using Content.Shared.Database;
-using Content.Shared.Interaction;
 using Content.Shared.Physics;
 using Content.Shared.Popups;
 using Content.Shared.Weapons.Melee.Components;
-using Content.Shared.Weapons.Melee.Events;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Map;
 using Robust.Shared.Map.Components;
@@ -21,6 +18,7 @@ using Robust.Shared.Timing;
 using Robust.Shared.Utility;
 using System.Linq;
 using System.Numerics;
+using Content.Shared.Actions;
 
 namespace Content.Shared.Anomaly;
 
@@ -36,6 +34,7 @@ public abstract class SharedAnomalySystem : EntitySystem
     [Dependency] protected readonly SharedPopupSystem Popup = default!;
     [Dependency] private readonly IPrototypeManager _prototype = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
 
     public override void Initialize()
     {
@@ -145,7 +144,7 @@ public abstract class SharedAnomalySystem : EntitySystem
         if (!Timing.IsFirstTimePredicted)
             return;
 
-        Audio.PlayPvs(component.SupercriticalSound, uid);
+        Audio.PlayPvs(component.SupercriticalSound, Transform(uid).Coordinates);
 
         if (_net.IsServer)
             Log.Info($"Raising supercritical event. Entity: {ToPrettyString(uid)}");
@@ -169,7 +168,8 @@ public abstract class SharedAnomalySystem : EntitySystem
     /// <param name="uid">The anomaly being shut down</param>
     /// <param name="component"></param>
     /// <param name="supercritical">Whether or not the anomaly ended via supercritical event</param>
-    public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool supercritical = false)
+    /// <param name="spawnCore">Create anomaly cores based on the result of completing an anomaly?</param>
+    public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool supercritical = false, bool spawnCore = true)
     {
         // Logging before resolve, in case the anomaly has deleted itself.
         if (_net.IsServer)
@@ -186,9 +186,14 @@ public abstract class SharedAnomalySystem : EntitySystem
         if (Terminating(uid) || _net.IsClient)
             return;
 
-        Spawn(supercritical ? component.CorePrototype : component.CoreInertPrototype, Transform(uid).Coordinates);
+        if (spawnCore)
+        {
+            var core = Spawn(supercritical ? component.CorePrototype : component.CoreInertPrototype, Transform(uid).Coordinates);
+            _transform.PlaceNextTo(core, uid);
+        }
 
-        QueueDel(uid);
+        if (component.DeleteEntity)
+            QueueDel(uid);
     }
 
     /// <summary>
@@ -458,3 +463,5 @@ public partial record struct AnomalySpawnSettings()
     /// </summary>
     public bool SpawnOnSeverityChanged { get; set; } = false;
 }
+
+public sealed partial class ActionAnomalyPulseEvent : InstantActionEvent { }
index 6f18510d17b9b8069f21c6a5243370c0d2861a72..75cf22aa4fe58651023bbdf65374a1c4233b397e 100644 (file)
   copyright: '"beep_landmine.ogg" by kaktuscsc of Discord for SS14'
   license: "CC-BY-SA-3.0"
   source: https://github.com/YuriyKiss/space-station-14/commit/971a135a9c83aed46e967aac9302ab5b35562b5f
+
+- files: [inneranomaly.ogg]
+  copyright: 'created by waveplaySFX on Freesound'
+  license: "CC0-1.0"
+  source: https://freesound.org/people/waveplaySFX/sounds/553744/
diff --git a/Resources/Audio/Effects/inneranomaly.ogg b/Resources/Audio/Effects/inneranomaly.ogg
new file mode 100644 (file)
index 0000000..43fc403
Binary files /dev/null and b/Resources/Audio/Effects/inneranomaly.ogg differ
diff --git a/Resources/Locale/en-US/anomaly/inner_anomaly.ftl b/Resources/Locale/en-US/anomaly/inner_anomaly.ftl
new file mode 100644 (file)
index 0000000..e55c439
--- /dev/null
@@ -0,0 +1,17 @@
+inner-anomaly-start-message-pyro = You can feel the insane flame inside of you. You became the host of a pyroclastic anomaly.
+inner-anomaly-start-message-shock = Lightning bolts quivering at your fingertips! You became the host of a electric anomaly.
+inner-anomaly-start-message-shadow = There's an impenetrable darkness oozing out of you... You became the host of a shadow anomaly.
+inner-anomaly-start-message-frost = The icy frost is binding your bones. You became the host of a ice anomaly.
+inner-anomaly-start-message-flora = Leaves and flowers sprout through your skin! You became the host of a floral anomaly.
+inner-anomaly-start-message-bluespace = Your thoughts are racing like mad! You became the host of a bluespace anomaly.
+inner-anomaly-start-message-flesh = Your body is growing frantically. You became the host of a flesh anomaly.
+inner-anomaly-start-message-grav = Everything becames unnaturally heavy and light at the same time... You became the host of a gravity anomaly.
+inner-anomaly-start-message-tech = Your head is buzzing with the amount of chaotic information! You became the host of a tech anomaly.
+inner-anomaly-start-message-rock = The crystals are growing through your bones! You became the host of a rock anomaly.
+
+inner-anomaly-end-message = The abnormal activity within you disappears without a trace....
+
+inner-anomaly-severity-info-50 = You feel that the anomaly is taking over half your body.
+inner-anomaly-severity-info-75 = You feel that the anomaly is taking over a large part of your body.
+inner-anomaly-severity-info-90 = You feel that the anomaly has almost completely taken over your body.
+inner-anomaly-severity-info-100 = The anomaly inside you is growing uncontrollably, causing immense pain, and tearing you apart!
\ No newline at end of file
diff --git a/Resources/Prototypes/Actions/anomaly.yml b/Resources/Prototypes/Actions/anomaly.yml
new file mode 100644 (file)
index 0000000..65c6ae1
--- /dev/null
@@ -0,0 +1,9 @@
+- type: entity
+  id: ActionAnomalyPulse
+  name: Anomaly pulse
+  description: Release a pulse of energy of your abnormal nature
+  components:
+  - type: InstantAction
+    icon: Structures/Specific/anomaly.rsi/anom1.png
+    event: !type:ActionAnomalyPulseEvent
+    useDelay: 30
\ No newline at end of file
index 5a9360e652d00573bed67e2651cffb2c497646f5..4697606af9f6830db80d82f47eebb8702747bbad 100644 (file)
@@ -9,6 +9,7 @@
     - sprite: Structures/Specific/anomaly.rsi
       state: anom1
   - type: RandomSpawner
+    chance: 1
     prototypes:
     - AnomalyPyroclastic
     - AnomalyGravity
@@ -21,7 +22,9 @@
     - AnomalyFlora
     - AnomalyShadow
     - AnomalyTech
-    chance: 1
+    rareChance: 0.3
+    rarePrototypes:
+    - RandomAnomalyInjectorSpawner
     offset: 0.15 # not to put it higher. The anomaly sychnronizer looks for anomalies within this radius, and if the radius is higher, the anomaly can be attracted from a neighboring tile.
 
 - type: entity
     - AnomalyRockQuartz
     - AnomalyRockUranium
     chance: 1
-    offset: 0.15
\ No newline at end of file
+    offset: 0.15
+
+- type: entity
+  id: RandomAnomalyInjectorSpawner
+  parent: MarkerBase
+  components:
+  - type: Sprite
+    layers:
+    - state: red
+    - sprite: Structures/Specific/Anomalies/tech_anom.rsi
+      state: pulse
+  - type: RandomSpawner
+    prototypes:
+    - AnomalyTrapPyroclastic
+    - AnomalyTrapElectricity
+    - AnomalyTrapShadow
+    - AnomalyTrapIce
+    - AnomalyTrapFlora
+    - AnomalyTrapBluespace
+    - AnomalyTrapFlesh
+    - AnomalyTrapGravity
+    - AnomalyTrapTech
+    - AnomalyTrapRock
+    chance: 1
+  
\ No newline at end of file
index 495f5345534bf49059120dd0672cb7fc66a3235a..8e05642c0e97c702cb0f2633402903e67ed606f4 100644 (file)
     tags:
     - VimPilot
     - DoorBumpOpener
+    - AnomalyHost
   - type: Reactive
     groups:
       Flammable: [ Touch ]
index 06ab02dedc906c1c7804ee09d03baef563e4b7a3..a1bcd547a6478b593c6b69b4e4466e9383323b56 100644 (file)
         true
       NavSmash: !type:Bool
         true
+  - type: NPCImprintingOnSpawnBehaviour
+    spawnFriendsSearchRadius: 10
+    whitelist:
+      components:
+      - Anomaly # Friendly to inner anomaly host
   - type: NpcFactionMember
     factions:
     - SimpleHostile
       30: Dead
   - type: MovementSpeedModifier
     baseWalkSpeed: 2
-    baseSprintSpeed: 2.5
+    baseSprintSpeed: 2.5
\ No newline at end of file
index 9b90d202f3a93679fb137f901cbd83c75977dfc0..9500345e4fc70839ef44eb30350d7563180bc564 100644 (file)
   - type: NpcFactionMember
     factions:
     - SimpleHostile
+  - type: NPCImprintingOnSpawnBehaviour
+    spawnFriendsSearchRadius: 10
+    whitelist:
+      components:
+      - Anomaly # Friendly to inner anomaly host
   - type: MovementIgnoreGravity
   - type: MovementSpeedModifier
     baseWalkSpeed: 3.5
index ebc1b80541cf3e7737a679500c6b4bc5a59e35a2..71336d9e63ae5f2c2b78b0351a40d581fa993d42 100644 (file)
     - CannotSuicide
     - DoorBumpOpener
     - VimPilot
+    - AnomalyHost
   - type: Loadout
     prototypes: [ MobMonkeyGear ]
   - type: Grammar
index 70c516e78856a93f1d3caa41c011d4c5c08c5e8b..0d7f7717586a9dee78a5f93c0c7f0b79a3c5d839 100644 (file)
@@ -23,6 +23,7 @@
     - FootstepSound
     - DoorBumpOpener
     - SpiderCraft
+    - AnomalyHost
   - type: Butcherable
     butcheringType: Spike
     spawned:
index 2349eddd3a444cc05326d21d5e291d8b43c24cbf..5cebd2cf853b441fdf99d507f67a1c406b5999a0 100644 (file)
     - CanPilot
     - FootstepSound
     - DoorBumpOpener
+    - AnomalyHost
 
 - type: entity
   save: false
index 140f3c94429555c89c0fb298e63584860336ab4b..f675b2d2c81dd6b761ed600a3a8e096099519919 100644 (file)
@@ -5,6 +5,7 @@
   description: An impossible object. Should you be standing this close to it?
   components:
   - type: Anomaly
+    offset: 0, 0.15
     pulseSound:
       collection: RadiationPulse
       params:
     lifetime: 2.3
   - type: Tag
     tags:
-      - HideContextMenu
\ No newline at end of file
+      - HideContextMenu
diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomaly_injections.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomaly_injections.yml
new file mode 100644 (file)
index 0000000..f1e535f
--- /dev/null
@@ -0,0 +1,353 @@
+- type: entity
+  id: AnomalyInjectionBase
+  abstract: true
+  components:
+  - type: PointLight
+    radius: 1.3
+    energy: 2.5
+    castShadows: false
+  - type: ActionGrant
+    actions:
+    - ActionAnomalyPulse
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionPyroclastic
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#E25822"
+  - type: PyroclasticAnomaly
+    maximumIgnitionRadius: 3
+  - type: TempAffectingAnomaly
+    tempChangePerSecond: 10
+    hotspotExposeTemperature: 500
+  - type: GasProducerAnomaly
+    releasedGas: 3
+    releaseOnMaxSeverity: true
+    spawnRadius: 4
+    tileCount: 5
+    tempChange: 420
+  - type: ProjectileAnomaly
+    projectilePrototype: ProjectileAnomalyFireball
+    projectileSpeed: 0.5
+    minProjectiles: 1
+    maxProjectiles: 3
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionElectric
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#ffffaa"
+  - type: ElectricityAnomaly
+    minBoltCount: 1
+    maxBoltCount: 3
+    maxElectrocuteRange: 4
+    maxElectrocuteDamage: 10
+    maxElectrocuteDuration: 4
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionShadow
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#793a80"
+  - type: EntitySpawnAnomaly
+    entries:
+    - settings:
+        spawnOnPulse: true
+        spawnOnSuperCritical: true
+        minAmount: 5
+        maxAmount: 10
+        maxRange: 2
+      spawns:
+      - ShadowKudzuWeak
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 15
+        maxAmount: 20
+        maxRange: 25
+      spawns:
+      - ShadowKudzu
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionIce
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#befaff"
+  - type: ExplosionAnomaly
+    supercriticalExplosion: Cryo
+    explosionTotalIntensity: 150
+    explosionDropoff: 2
+    explosionMaxTileIntensity: 20
+  - type: ProjectileAnomaly
+    projectilePrototype: ProjectileIcicle
+    minProjectiles: 1
+    maxProjectiles: 4
+  - type: EntitySpawnAnomaly
+    entries:
+    - settings:
+        spawnOnStabilityChanged: true
+        minAmount: 3
+        maxAmount: 8
+        maxRange: 2
+      spawns:
+      - IceCrust
+  - type: TempAffectingAnomaly
+    tempChangePerSecond: -10
+    hotspotExposeTemperature: -500
+  - type: GasProducerAnomaly
+    releasedGas: 8 # Frezon. Please replace if there is a better way to specify this
+    releaseOnMaxSeverity: true
+    spawnRadius: 0
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionFlora
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#6270bb"
+  - type: TileSpawnAnomaly
+    entries:
+    - settings:
+        spawnOnPulse: true
+        minAmount: 2
+        maxAmount: 5
+        maxRange: 2
+      floor: FloorAstroGrass
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 5
+        maxAmount: 15
+        maxRange: 7
+      floor: FloorAstroGrass
+  - type: EntitySpawnAnomaly
+    entries:
+    - settings:
+        spawnOnPulse: true
+        minAmount: 1
+        maxAmount: 3
+        maxRange: 1
+      spawns:
+      - KudzuFlowerFriendly
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 2
+        maxAmount: 6
+        maxRange: 3
+      spawns:
+      - KudzuFlowerAngry
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionBluespace
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#00ccff"
+  - type: BluespaceAnomaly
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionFlesh
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#cb5b7e"
+  - type: TileSpawnAnomaly
+    entries:
+    - settings:
+        spawnOnPulse: true
+        spawnOnStabilityChanged: true
+        minAmount: 1
+        maxAmount: 3
+        maxRange: 2
+      floor: FloorFlesh
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 5
+        maxAmount: 15
+        maxRange: 5
+      floor: FloorFlesh
+  - type: EntitySpawnAnomaly
+    entries:
+    - settings:
+        spawnOnPulse: true
+        minAmount: 1
+        maxAmount: 2
+        minRange: 1.5
+        maxRange: 2.5
+      spawns:
+      - FleshBlocker
+    - settings:
+        spawnOnPulse: true
+        maxAmount: 1
+        minRange: 2.5
+        maxRange: 4.5
+      spawns:
+      - MobFleshJared
+      - MobFleshGolem
+      - MobFleshClamp
+      - MobFleshLover
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 5
+        maxAmount: 8
+        minRange: 5
+        maxRange: 15
+      spawns:
+      - FleshBlocker
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 2
+        maxAmount: 5
+        maxRange: 8
+      spawns:
+      - MobFleshJared
+      - MobFleshGolem
+      - MobFleshClamp
+      - MobFleshLover
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 2
+        maxAmount: 4
+        maxRange: 10
+      spawns:
+      - FleshKudzu
+    - settings:
+        spawnOnShutdown: true
+        maxAmount: 2
+        maxRange: 1
+      spawns:
+      - MobFleshJared
+      - MobFleshGolem
+      - MobFleshClamp
+      - MobFleshLover
+      - FleshKudzu
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionGravity
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#1e070e"
+  - type: GravityAnomaly
+    maxGravityWellRange: 4
+    maxThrowRange: 3
+    maxThrowStrength: 5
+    spaceRange: 1
+  - type: GravityWell
+    maxRange: 0.7
+  - type: SingularityDistortion
+    intensity: 100
+    falloffPower: 2.7
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionTech
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#56c1e8"
+  - type: TechAnomaly
+    linkRadius: 
+      min: 2
+      max: 5
+    linkCountPerPulse:
+      min: 1
+      max: 4
+    linkCountSupercritical: 15
+  - type: DeviceLinkSource
+    ports:
+      - Pulse
+      - Timer
+  - type: WirelessNetworkConnection
+    range: 10
+  - type: DeviceNetwork
+    deviceNetId: Wireless
+    receiveFrequencyId: BasicDevice
+
+- type: entity
+  parent: AnomalyInjectionBase
+  id: AnomalyInjectionRock
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: PointLight
+    color: "#5ca8cb"
+  - type: TileSpawnAnomaly
+    entries:
+    - settings:
+        spawnOnPulse: true
+        minAmount: 7
+        maxAmount: 10
+        maxRange: 4
+      floor: FloorAsteroidTile
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 15
+        maxAmount: 25
+        maxRange: 6
+      floor: FloorAsteroidTile
+  - type: EntitySpawnAnomaly
+    entries:
+    - settings:
+        spawnOnPulse: true
+        minAmount: 4
+        maxAmount: 8
+        minRange: 1
+        maxRange: 3
+      spawns:
+      - WallSpawnAsteroid
+      - WallSpawnAsteroid
+      - WallSpawnAsteroid
+      - WallSpawnAsteroid
+      - WallSpawnAsteroid
+      - WallSpawnAsteroidSilver
+      - WallSpawnAsteroidSilverCrab
+      - WallSpawnAsteroidIron
+      - WallSpawnAsteroidIronCrab
+      - WallSpawnAsteroidQuartz
+      - WallSpawnAsteroidQuartzCrab
+    - settings:
+        spawnOnPulse: true
+        maxAmount: 3
+        minRange: 2.5
+        maxRange: 4.5
+      spawns:
+      - CrystalPink
+      - CrystalCyan
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 15
+        maxAmount: 20
+        minRange: 2
+        maxRange: 7
+      spawns:
+      - CrystalPink
+      - CrystalCyan
+      - WallSpawnAsteroid
+      - WallSpawnAsteroid
+      - WallSpawnAsteroid
+      - WallSpawnAsteroid
+      - WallSpawnAsteroidSilver
+      - WallSpawnAsteroidSilverCrab
+      - WallSpawnAsteroidIron
+      - WallSpawnAsteroidIronCrab
+      - WallSpawnAsteroidQuartz
+      - WallSpawnAsteroidQuartzCrab
+    - settings:
+        spawnOnSuperCritical: true
+        minAmount: 3
+        maxAmount: 5
+        maxRange: 3
+      spawns:
+      - MobSpawnCrabSilver
+      - MobSpawnCrabIron
+      - MobSpawnCrabQuartz
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomaly_injectors.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomaly_injectors.yml
new file mode 100644 (file)
index 0000000..24a76df
--- /dev/null
@@ -0,0 +1,320 @@
+- type: entity
+  name: anomaly injector
+  parent: MarkerBase
+  id: BaseAnomalyInjector
+  abstract: true
+  components:
+  - type: Physics
+    bodyType: Static
+    fixedRotation: true
+  - type: AmbientSound
+    range: 5
+    volume: -5
+    sound:
+      path: /Audio/Ambience/anomaly_drone.ogg
+  - type: Fixtures
+    fixtures:
+      anom:
+        shape:
+          !type:PhysShapeCircle
+          radius: 2.27 # i love 27
+        hard: false
+        mask:
+        - MobMask
+        layer:
+        - MobLayer
+  - type: InnerBodyAnomalyInjector
+    whitelist:
+      tags:
+      - AnomalyHost
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapPyroclastic
+  suffix: Pyroclastic
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/Anomalies/pyro_anom.rsi
+      state: pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCorePyroclastic
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionPyroclastic
+        startMessage: inner-anomaly-start-message-pyro
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: fire
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: fire_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapElectricity
+  suffix: Electricity
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/anomaly.rsi
+      state: anom3-pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreElectricity
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionElectric
+        startMessage: inner-anomaly-start-message-shock
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: shock
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: shock_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapShadow
+  suffix: Shadow
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/Anomalies/shadow_anom.rsi
+      state: pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreShadow
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionShadow
+        startMessage: inner-anomaly-start-message-shadow
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: shadow
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: shadow_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapIce
+  suffix: Ice
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/Anomalies/ice_anom.rsi
+      state: pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreIce
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionIce
+        startMessage: inner-anomaly-start-message-frost
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: frost
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: frost_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapFlora
+  suffix: Flora
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/Anomalies/flora_anom.rsi
+      state: pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreFlora
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionFlora
+        startMessage: inner-anomaly-start-message-flora
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: flora
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: flora_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapBluespace
+  suffix: Bluespace
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/anomaly.rsi
+      state: anom4-pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreBluespace
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionBluespace
+        startMessage: inner-anomaly-start-message-bluespace
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: bluespace
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: bluespace_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapFlesh
+  suffix: Flesh
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/anomaly.rsi
+      state: anom5-pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreFlesh
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionFlesh
+        startMessage: inner-anomaly-start-message-flesh
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: flesh
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: flesh_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapGravity
+  suffix: Gravity
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/anomaly.rsi
+      state: anom2-pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreGravity
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionGravity
+        startMessage: inner-anomaly-start-message-grav
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: grav
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: grav_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapTech
+  suffix: Tech
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/Anomalies/tech_anom.rsi
+      state: pulse
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreTech
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionTech
+        startMessage: inner-anomaly-start-message-tech
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: tech
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: tech_VOX
+
+- type: entity
+  parent: BaseAnomalyInjector
+  id: AnomalyTrapRock
+  suffix: Rock
+  components:
+  - type: Sprite
+    layers:
+    - state: pink
+    - sprite: Structures/Specific/anomaly.rsi
+      state: anom6-pulse
+      color: "#5ca8cb"
+    - sprite: Mobs/Species/Human/parts.rsi
+      state: full
+  - type: InnerBodyAnomalyInjector
+    injectionComponents:
+      - type: Anomaly
+        deleteEntity: false
+        maxPointsPerSecond: 100
+        corePrototype: AnomalyCoreRock
+      - type: InnerBodyAnomaly
+        injectionProto: AnomalyInjectionRock
+        startMessage: inner-anomaly-start-message-rock
+        fallbackSprite:
+          sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+          state: rock
+        speciesSprites:
+          Vox:
+            sprite: Structures/Specific/Anomalies/inner_anom_layer.rsi
+            state: rock_VOX
\ No newline at end of file
index 8787dd7340d2d7c857f8979720dafea7075aa864..1ddee10e63076fa417bb300c1218a41325bd65e9 100644 (file)
@@ -15,6 +15,9 @@
 - type: Tag
   id: Ambrosia
 
+- type: Tag
+  id: AnomalyHost
+
 - type: Tag
   id: AppraisalTool
 
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/bluespace.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/bluespace.png
new file mode 100644 (file)
index 0000000..5e9311e
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/bluespace.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/bluespace_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/bluespace_VOX.png
new file mode 100644 (file)
index 0000000..b594cac
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/bluespace_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/fire.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/fire.png
new file mode 100644 (file)
index 0000000..a6fcde7
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/fire.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/fire_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/fire_VOX.png
new file mode 100644 (file)
index 0000000..91eb395
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/fire_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flesh.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flesh.png
new file mode 100644 (file)
index 0000000..6e17830
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flesh.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flesh_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flesh_VOX.png
new file mode 100644 (file)
index 0000000..b1a1c43
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flesh_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flora.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flora.png
new file mode 100644 (file)
index 0000000..70e369c
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flora.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flora_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flora_VOX.png
new file mode 100644 (file)
index 0000000..9796911
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/flora_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/frost.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/frost.png
new file mode 100644 (file)
index 0000000..4823cb5
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/frost.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/frost_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/frost_VOX.png
new file mode 100644 (file)
index 0000000..346cad0
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/frost_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/grav.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/grav.png
new file mode 100644 (file)
index 0000000..c0775c0
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/grav.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/grav_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/grav_VOX.png
new file mode 100644 (file)
index 0000000..04bfb8a
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/grav_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/meta.json b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/meta.json
new file mode 100644 (file)
index 0000000..4e069d0
--- /dev/null
@@ -0,0 +1,643 @@
+{
+  "version": 1,
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "license": "CC-BY-SA-3.0",
+  "copyright": "Created by TheShuEd (github) for Space Station 14",
+  "states": [
+    {
+      "name": "bluespace",
+      "directions": 4,
+      "delays": [
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ]
+      ]
+    },
+    {
+      "name": "bluespace_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ]
+      ]
+    },
+    {
+      "name": "fire",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "fire_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "flesh",
+      "directions": 4,
+      "delays": [
+        [
+          0.8,
+          0.2,
+          0.2
+        ],
+        [
+          0.8,
+          0.2,
+          0.2
+        ],
+        [
+          0.8,
+          0.2,
+          0.2
+        ],
+        [
+          0.8,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "flesh_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.8,
+          0.2,
+          0.2
+        ],
+        [
+          0.8,
+          0.2,
+          0.2
+        ],
+        [
+          0.8,
+          0.2,
+          0.2
+        ],
+        [
+          0.8,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "flora",
+      "directions": 4,
+      "delays": [
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ]
+      ]
+    },
+    {
+      "name": "flora_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ]
+      ]
+    },
+    {
+      "name": "frost",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "frost_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "grav",
+      "directions": 4,
+      "delays": [
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ]
+      ]
+    },
+    {
+      "name": "grav_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ],
+        [
+          0.3,
+          0.3,
+          0.3,
+          0.3
+        ]
+      ]
+    },
+    {
+      "name": "rock",
+      "directions": 4,
+      "delays": [
+        [
+          0.9,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.9,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.9,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.9,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "rock_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.9,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.9,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.9,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.9,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "shadow",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "shadow_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "shock",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "shock_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ],
+        [
+          0.2,
+          0.2,
+          0.2,
+          0.2,
+          0.2
+        ]
+      ]
+    },
+    {
+      "name": "tech",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.4,
+          0.2,
+          0.4
+        ],
+        [
+          0.2,
+          0.4,
+          0.2,
+          0.4
+        ],
+        [
+          0.2,
+          0.4,
+          0.2,
+          0.4
+        ],
+        [
+          0.2,
+          0.4,
+          0.2,
+          0.4
+        ]
+      ]
+    },
+    {
+      "name": "tech_VOX",
+      "directions": 4,
+      "delays": [
+        [
+          0.2,
+          0.4,
+          0.2,
+          0.4
+        ],
+        [
+          0.2,
+          0.4,
+          0.2,
+          0.4
+        ],
+        [
+          0.2,
+          0.4,
+          0.2,
+          0.4
+        ],
+        [
+          0.2,
+          0.4,
+          0.2,
+          0.4
+        ]
+      ]
+    }
+  ]
+}
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/rock.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/rock.png
new file mode 100644 (file)
index 0000000..770e4b8
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/rock.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/rock_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/rock_VOX.png
new file mode 100644 (file)
index 0000000..b11a918
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/rock_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shadow.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shadow.png
new file mode 100644 (file)
index 0000000..345248b
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shadow.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shadow_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shadow_VOX.png
new file mode 100644 (file)
index 0000000..6efb246
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shadow_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shock.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shock.png
new file mode 100644 (file)
index 0000000..acdc87a
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shock.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shock_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shock_VOX.png
new file mode 100644 (file)
index 0000000..d1f011c
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/shock_VOX.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/tech.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/tech.png
new file mode 100644 (file)
index 0000000..0611fa8
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/tech.png differ
diff --git a/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/tech_VOX.png b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/tech_VOX.png
new file mode 100644 (file)
index 0000000..1787cdd
Binary files /dev/null and b/Resources/Textures/Structures/Specific/Anomalies/inner_anom_layer.rsi/tech_VOX.png differ
index c0e547f8dd9027ba023abfa929c4163cbd449682..d8b6f14c1197cb7c63bc374ff39a7e435120f480 100644 (file)
Binary files a/Resources/Textures/Structures/Specific/anomaly.rsi/anom5-pulse.png and b/Resources/Textures/Structures/Specific/anomaly.rsi/anom5-pulse.png differ
index 6117776649f15449eccd5d7ab6683ca17d41a968..a36dd2b64ada6015366b68bdace3726e605bf279 100644 (file)
Binary files a/Resources/Textures/Structures/Specific/anomaly.rsi/anom5.png and b/Resources/Textures/Structures/Specific/anomaly.rsi/anom5.png differ