Anomalous infections (#31876)

* 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
This commit is contained in:
Ed
2024-09-17 12:49:19 +03:00
committed by GitHub
parent 5eaac00432
commit 92be69a5ab
49 changed files with 1859 additions and 25 deletions

View File

@@ -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);
}
}

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using System.Numerics;
using Content.Server.Anomaly.Components; using Content.Server.Anomaly.Components;
using Content.Server.DeviceLinking.Systems; using Content.Server.DeviceLinking.Systems;
using Content.Server.Power.Components; using Content.Server.Power.Components;
@@ -10,6 +11,7 @@ using Content.Shared.Popups;
using Content.Shared.Power; using Content.Shared.Power;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Timing;
namespace Content.Server.Anomaly; namespace Content.Server.Anomaly;
@@ -25,6 +27,7 @@ public sealed partial class AnomalySynchronizerSystem : EntitySystem
[Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly PowerReceiverSystem _power = default!; [Dependency] private readonly PowerReceiverSystem _power = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -40,6 +43,34 @@ public sealed partial class AnomalySynchronizerSystem : EntitySystem
SubscribeLocalEvent<AnomalyStabilityChangedEvent>(OnAnomalyStabilityChanged); 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> /// <summary>
/// If powered, try to attach a nearby anomaly. /// If powered, try to attach a nearby anomaly.
/// </summary> /// </summary>
@@ -73,10 +104,10 @@ public sealed partial class AnomalySynchronizerSystem : EntitySystem
if (args.Powered) if (args.Powered)
return; return;
if (!TryComp<AnomalyComponent>(ent.Comp.ConnectedAnomaly, out var anomaly)) if (ent.Comp.ConnectedAnomaly is null)
return; return;
DisconnectFromAnomaly(ent, anomaly); DisconnectFromAnomaly(ent, ent.Comp.ConnectedAnomaly.Value);
} }
private void OnExamined(Entity<AnomalySynchronizerComponent> ent, ref ExaminedEvent args) 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. //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. //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) if (ent.Comp.ConnectedAnomaly == null)
return; return;
if (TryComp<AnomalyComponent>(other, out var anomaly))
{
if (ent.Comp.PulseOnDisconnect) if (ent.Comp.PulseOnDisconnect)
_anomaly.DoAnomalyPulse(ent.Comp.ConnectedAnomaly.Value, anomaly); _anomaly.DoAnomalyPulse(ent.Comp.ConnectedAnomaly.Value, anomaly);
}
_popup.PopupEntity(Loc.GetString("anomaly-sync-disconnected"), ent, PopupType.Large); _popup.PopupEntity(Loc.GetString("anomaly-sync-disconnected"), ent, PopupType.Large);
_audio.PlayPvs(ent.Comp.ConnectedSound, ent); _audio.PlayPvs(ent.Comp.ConnectedSound, ent);

View File

@@ -55,6 +55,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
SubscribeLocalEvent<AnomalyComponent, ComponentShutdown>(OnShutdown); SubscribeLocalEvent<AnomalyComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<AnomalyComponent, StartCollideEvent>(OnStartCollide); SubscribeLocalEvent<AnomalyComponent, StartCollideEvent>(OnStartCollide);
InitializeGenerator(); InitializeGenerator();
InitializeScanner(); InitializeScanner();
InitializeVessel(); InitializeVessel();
@@ -86,7 +87,10 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
private void OnShutdown(Entity<AnomalyComponent> anomaly, ref ComponentShutdown args) 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) private void OnStartCollide(Entity<AnomalyComponent> anomaly, ref StartCollideEvent args)

View File

@@ -7,7 +7,7 @@ namespace Content.Server.Anomaly.Components;
/// <summary> /// <summary>
/// a device that allows you to translate anomaly activity into multitool signals. /// a device that allows you to translate anomaly activity into multitool signals.
/// </summary> /// </summary>
[RegisterComponent, Access(typeof(AnomalySynchronizerSystem))] [RegisterComponent, AutoGenerateComponentPause, Access(typeof(AnomalySynchronizerSystem))]
public sealed partial class AnomalySynchronizerComponent : Component public sealed partial class AnomalySynchronizerComponent : Component
{ {
/// <summary> /// <summary>
@@ -34,6 +34,15 @@ public sealed partial class AnomalySynchronizerComponent : Component
[DataField] [DataField]
public float AttachRange = 0.4f; 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] [DataField]
public ProtoId<SourcePortPrototype> DecayingPort = "Decaying"; public ProtoId<SourcePortPrototype> DecayingPort = "Decaying";

View File

@@ -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);
}
}

View File

@@ -202,6 +202,7 @@ namespace Content.Server.Explosion.EntitySystems
args.Handled = true; args.Handled = true;
} }
private void HandleRattleTrigger(EntityUid uid, RattleComponent component, TriggerEvent args) private void HandleRattleTrigger(EntityUid uid, RattleComponent component, TriggerEvent args)
{ {
if (!TryComp<SubdermalImplantComponent>(uid, out var implanted)) 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) private void OnTriggerCollide(EntityUid uid, TriggerOnCollideComponent component, ref StartCollideEvent args)
{ {
if (args.OurFixtureId == component.FixtureID && (!component.IgnoreOtherNonHard || args.OtherFixture.Hard)) 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) private void OnSpawnTriggered(EntityUid uid, TriggerOnSpawnComponent component, MapInitEvent args)

View File

@@ -1,6 +1,6 @@
using System.Numerics; using System.Numerics;
using Content.Shared.Anomaly.Effects;
using Content.Shared.Anomaly.Prototypes; using Content.Shared.Anomaly.Prototypes;
using Content.Shared.Damage;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; 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 /// Anomalies and their related components were designed here: https://hackmd.io/@ss14-design/r1sQbkJOs
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
[Access(typeof(SharedAnomalySystem))] [Access(typeof(SharedAnomalySystem), typeof(SharedInnerBodyAnomalySystem))]
public sealed partial class AnomalyComponent : Component public sealed partial class AnomalyComponent : Component
{ {
/// <summary> /// <summary>
@@ -184,21 +184,21 @@ public sealed partial class AnomalyComponent : Component
/// <summary> /// <summary>
/// The minimum amount of research points generated per second /// The minimum amount of research points generated per second
/// </summary> /// </summary>
[DataField("minPointsPerSecond")] [DataField]
public int MinPointsPerSecond = 10; public int MinPointsPerSecond = 10;
/// <summary> /// <summary>
/// The maximum amount of research points generated per second /// The maximum amount of research points generated per second
/// This doesn't include the point bonus for being unstable. /// This doesn't include the point bonus for being unstable.
/// </summary> /// </summary>
[DataField("maxPointsPerSecond")] [DataField]
public int MaxPointsPerSecond = 70; public int MaxPointsPerSecond = 70;
/// <summary> /// <summary>
/// The multiplier applied to the point value for the /// The multiplier applied to the point value for the
/// anomaly being above the <see cref="GrowthThreshold"/> /// anomaly being above the <see cref="GrowthThreshold"/>
/// </summary> /// </summary>
[DataField("growingPointMultiplier")] [DataField]
public float GrowingPointMultiplier = 1.5f; public float GrowingPointMultiplier = 1.5f;
#endregion #endregion
@@ -252,10 +252,13 @@ public sealed partial class AnomalyComponent : Component
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("offset")] [DataField("offset")]
public Vector2 FloatingOffset = new(0, 0.15f); public Vector2 FloatingOffset = new(0, 0);
public readonly string AnimationKey = "anomalyfloat"; public readonly string AnimationKey = "anomalyfloat";
#endregion #endregion
[DataField]
public bool DeleteEntity = true;
} }
/// <summary> /// <summary>

View File

@@ -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";
}

View File

@@ -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!;
}

View File

@@ -0,0 +1,5 @@
namespace Content.Shared.Anomaly.Effects;
public abstract class SharedInnerBodyAnomalySystem : EntitySystem
{
}

View File

@@ -1,13 +1,10 @@
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Components;
using Content.Shared.Anomaly.Prototypes; using Content.Shared.Anomaly.Prototypes;
using Content.Shared.Damage;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Interaction;
using Content.Shared.Physics; using Content.Shared.Physics;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Weapons.Melee.Components; using Content.Shared.Weapons.Melee.Components;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
@@ -21,6 +18,7 @@ using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Shared.Actions;
namespace Content.Shared.Anomaly; namespace Content.Shared.Anomaly;
@@ -36,6 +34,7 @@ public abstract class SharedAnomalySystem : EntitySystem
[Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -145,7 +144,7 @@ public abstract class SharedAnomalySystem : EntitySystem
if (!Timing.IsFirstTimePredicted) if (!Timing.IsFirstTimePredicted)
return; return;
Audio.PlayPvs(component.SupercriticalSound, uid); Audio.PlayPvs(component.SupercriticalSound, Transform(uid).Coordinates);
if (_net.IsServer) if (_net.IsServer)
Log.Info($"Raising supercritical event. Entity: {ToPrettyString(uid)}"); 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="uid">The anomaly being shut down</param>
/// <param name="component"></param> /// <param name="component"></param>
/// <param name="supercritical">Whether or not the anomaly ended via supercritical event</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. // Logging before resolve, in case the anomaly has deleted itself.
if (_net.IsServer) if (_net.IsServer)
@@ -186,8 +186,13 @@ public abstract class SharedAnomalySystem : EntitySystem
if (Terminating(uid) || _net.IsClient) if (Terminating(uid) || _net.IsClient)
return; 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);
}
if (component.DeleteEntity)
QueueDel(uid); QueueDel(uid);
} }
@@ -458,3 +463,5 @@ public partial record struct AnomalySpawnSettings()
/// </summary> /// </summary>
public bool SpawnOnSeverityChanged { get; set; } = false; public bool SpawnOnSeverityChanged { get; set; } = false;
} }
public sealed partial class ActionAnomalyPulseEvent : InstantActionEvent { }

View File

@@ -231,3 +231,8 @@
copyright: '"beep_landmine.ogg" by kaktuscsc of Discord for SS14' copyright: '"beep_landmine.ogg" by kaktuscsc of Discord for SS14'
license: "CC-BY-SA-3.0" license: "CC-BY-SA-3.0"
source: https://github.com/YuriyKiss/space-station-14/commit/971a135a9c83aed46e967aac9302ab5b35562b5f 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/

Binary file not shown.

View File

@@ -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!

View File

@@ -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

View File

@@ -9,6 +9,7 @@
- sprite: Structures/Specific/anomaly.rsi - sprite: Structures/Specific/anomaly.rsi
state: anom1 state: anom1
- type: RandomSpawner - type: RandomSpawner
chance: 1
prototypes: prototypes:
- AnomalyPyroclastic - AnomalyPyroclastic
- AnomalyGravity - AnomalyGravity
@@ -21,7 +22,9 @@
- AnomalyFlora - AnomalyFlora
- AnomalyShadow - AnomalyShadow
- AnomalyTech - 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. 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 - type: entity
@@ -41,3 +44,27 @@
- AnomalyRockUranium - AnomalyRockUranium
chance: 1 chance: 1
offset: 0.15 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

View File

@@ -1316,6 +1316,7 @@
tags: tags:
- VimPilot - VimPilot
- DoorBumpOpener - DoorBumpOpener
- AnomalyHost
- type: Reactive - type: Reactive
groups: groups:
Flammable: [ Touch ] Flammable: [ Touch ]

View File

@@ -13,6 +13,11 @@
true true
NavSmash: !type:Bool NavSmash: !type:Bool
true true
- type: NPCImprintingOnSpawnBehaviour
spawnFriendsSearchRadius: 10
whitelist:
components:
- Anomaly # Friendly to inner anomaly host
- type: NpcFactionMember - type: NpcFactionMember
factions: factions:
- SimpleHostile - SimpleHostile

View File

@@ -16,6 +16,11 @@
- type: NpcFactionMember - type: NpcFactionMember
factions: factions:
- SimpleHostile - SimpleHostile
- type: NPCImprintingOnSpawnBehaviour
spawnFriendsSearchRadius: 10
whitelist:
components:
- Anomaly # Friendly to inner anomaly host
- type: MovementIgnoreGravity - type: MovementIgnoreGravity
- type: MovementSpeedModifier - type: MovementSpeedModifier
baseWalkSpeed: 3.5 baseWalkSpeed: 3.5

View File

@@ -809,6 +809,7 @@
- CannotSuicide - CannotSuicide
- DoorBumpOpener - DoorBumpOpener
- VimPilot - VimPilot
- AnomalyHost
- type: Loadout - type: Loadout
prototypes: [ MobMonkeyGear ] prototypes: [ MobMonkeyGear ]
- type: Grammar - type: Grammar

View File

@@ -23,6 +23,7 @@
- FootstepSound - FootstepSound
- DoorBumpOpener - DoorBumpOpener
- SpiderCraft - SpiderCraft
- AnomalyHost
- type: Butcherable - type: Butcherable
butcheringType: Spike butcheringType: Spike
spawned: spawned:

View File

@@ -212,6 +212,7 @@
- CanPilot - CanPilot
- FootstepSound - FootstepSound
- DoorBumpOpener - DoorBumpOpener
- AnomalyHost
- type: entity - type: entity
save: false save: false

View File

@@ -5,6 +5,7 @@
description: An impossible object. Should you be standing this close to it? description: An impossible object. Should you be standing this close to it?
components: components:
- type: Anomaly - type: Anomaly
offset: 0, 0.15
pulseSound: pulseSound:
collection: RadiationPulse collection: RadiationPulse
params: params:

View File

@@ -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

View File

@@ -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

View File

@@ -15,6 +15,9 @@
- type: Tag - type: Tag
id: Ambrosia id: Ambrosia
- type: Tag
id: AnomalyHost
- type: Tag - type: Tag
id: AppraisalTool id: AppraisalTool

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -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
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 513 B