--- /dev/null
+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);
+ }
+}
using System.Linq;
+using System.Numerics;
using Content.Server.Anomaly.Components;
using Content.Server.DeviceLinking.Systems;
using Content.Server.Power.Components;
using Content.Shared.Power;
using Robust.Shared.Audio.Systems;
using Content.Shared.Verbs;
+using Robust.Shared.Timing;
namespace Content.Server.Anomaly;
[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()
{
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>
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)
//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);
SubscribeLocalEvent<AnomalyComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<AnomalyComponent, StartCollideEvent>(OnStartCollide);
+
InitializeGenerator();
InitializeScanner();
InitializeVessel();
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)
/// <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>
[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";
--- /dev/null
+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);
+ }
+}
args.Handled = true;
}
+
private void HandleRattleTrigger(EntityUid uid, RattleComponent component, TriggerEvent args)
{
if (!TryComp<SubdermalImplantComponent>(uid, out var implanted))
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)
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;
/// 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>
/// <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
/// </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>
--- /dev/null
+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";
+}
--- /dev/null
+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!;
+}
--- /dev/null
+namespace Content.Shared.Anomaly.Effects;
+
+public abstract class SharedInnerBodyAnomalySystem : EntitySystem
+{
+}
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;
using Robust.Shared.Utility;
using System.Linq;
using System.Numerics;
+using Content.Shared.Actions;
namespace Content.Shared.Anomaly;
[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()
{
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)}");
/// <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)
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>
/// </summary>
public bool SpawnOnSeverityChanged { get; set; } = false;
}
+
+public sealed partial class ActionAnomalyPulseEvent : InstantActionEvent { }
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/
--- /dev/null
+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
--- /dev/null
+- 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
- sprite: Structures/Specific/anomaly.rsi
state: anom1
- type: RandomSpawner
+ chance: 1
prototypes:
- AnomalyPyroclastic
- AnomalyGravity
- 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
tags:
- VimPilot
- DoorBumpOpener
+ - AnomalyHost
- type: Reactive
groups:
Flammable: [ Touch ]
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
- type: NpcFactionMember
factions:
- SimpleHostile
+ - type: NPCImprintingOnSpawnBehaviour
+ spawnFriendsSearchRadius: 10
+ whitelist:
+ components:
+ - Anomaly # Friendly to inner anomaly host
- type: MovementIgnoreGravity
- type: MovementSpeedModifier
baseWalkSpeed: 3.5
- CannotSuicide
- DoorBumpOpener
- VimPilot
+ - AnomalyHost
- type: Loadout
prototypes: [ MobMonkeyGear ]
- type: Grammar
- FootstepSound
- DoorBumpOpener
- SpiderCraft
+ - AnomalyHost
- type: Butcherable
butcheringType: Spike
spawned:
- CanPilot
- FootstepSound
- DoorBumpOpener
+ - AnomalyHost
- type: entity
save: false
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
--- /dev/null
+- 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
--- /dev/null
+- 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
- type: Tag
id: Ambrosia
+- type: Tag
+ id: AnomalyHost
+
- type: Tag
id: AppraisalTool
--- /dev/null
+{
+ "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
+ ]
+ ]
+ }
+ ]
+}