using System.Numerics; using Content.Shared.Damage; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Anomaly.Components; /// /// This is used for tracking the general behavior of anomalies. /// This doesn't contain the specific implementations for what /// they do, just the generic behaviors associated with them. /// /// Anomalies and their related components were designed here: https://hackmd.io/@ss14-design/r1sQbkJOs /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [Access(typeof(SharedAnomalySystem))] public sealed partial class AnomalyComponent : Component { /// /// How likely an anomaly is to grow more dangerous. Moves both up and down. /// Ranges from 0 to 1. /// Values less than 0.5 indicate stability, whereas values greater /// than 0.5 indicate instability, which causes increases in severity. /// /// /// Note that this doesn't refer to stability as a percentage: This is an arbitrary /// value that only matters in relation to the and /// [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public float Stability = 0f; /// /// How severe the effects of an anomaly are. Moves only upwards. /// Ranges from 0 to 1. /// A value of 0 indicates effects of extrememly minimal severity, whereas greater /// values indicate effects of linearly increasing severity. /// /// /// Wacky-Stability scale lives on in my heart. - emo /// [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public float Severity = 0f; #region Health /// /// The internal "health" of an anomaly. /// Ranges from 0 to 1. /// When the health of an anomaly reaches 0, it is destroyed without ever /// reaching a supercritical point. /// [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public float Health = 1f; /// /// If the of the anomaly exceeds this value, it /// becomes too unstable to support itself and starts decreasing in . /// [DataField("decayhreshold"), ViewVariables(VVAccess.ReadWrite)] public float DecayThreshold = 0.15f; /// /// The amount of health lost when the stability is below the /// [DataField("healthChangePerSecond"), ViewVariables(VVAccess.ReadWrite)] public float HealthChangePerSecond = -0.01f; #endregion #region Growth /// /// If the of the anomaly exceeds this value, it /// becomes unstable and starts increasing in . /// [DataField("growthThreshold"), ViewVariables(VVAccess.ReadWrite)] public float GrowthThreshold = 0.5f; /// /// A coefficient used for calculating the increase in severity when above the GrowthThreshold /// [DataField("severityGrowthCoefficient"), ViewVariables(VVAccess.ReadWrite)] public float SeverityGrowthCoefficient = 0.07f; #endregion #region Pulse /// /// The time at which the next artifact pulse will occur. /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] [ViewVariables(VVAccess.ReadWrite)] public TimeSpan NextPulseTime = TimeSpan.Zero; /// /// The minimum interval between pulses. /// [DataField] public TimeSpan MinPulseLength = TimeSpan.FromMinutes(1); /// /// The maximum interval between pulses. /// [DataField] public TimeSpan MaxPulseLength = TimeSpan.FromMinutes(2); /// /// A percentage by which the length of a pulse might vary. /// [DataField] public float PulseVariation = 0.1f; /// /// The range that an anomaly's stability can vary each pulse. Scales with severity. /// /// /// This is more likely to trend upwards than donwards, because that's funny /// [DataField] public Vector2 PulseStabilityVariation = new(-0.1f, 0.15f); /// /// The sound played when an anomaly pulses /// [DataField] public SoundSpecifier? PulseSound = new SoundCollectionSpecifier("RadiationPulse"); /// /// The sound plays when an anomaly goes supercritical /// [DataField] public SoundSpecifier? SupercriticalSound = new SoundCollectionSpecifier("explosion"); #endregion /// /// The range of initial values for stability /// /// /// +/- 0.2 from perfect stability (0.5) /// [DataField] public (float, float) InitialStabilityRange = (0.4f, 0.6f); /// /// The range of initial values for severity /// /// /// Between 0 and 0.5, which should be all mild effects /// [DataField] public (float, float) InitialSeverityRange = (0.1f, 0.5f); /// /// The particle type that increases the severity of the anomaly. /// [DataField] public AnomalousParticleType SeverityParticleType; /// /// The particle type that destabilizes the anomaly. /// [DataField] public AnomalousParticleType DestabilizingParticleType; /// /// The particle type that weakens the anomalys health. /// [DataField] public AnomalousParticleType WeakeningParticleType; #region Points and Vessels /// /// The vessel that the anomaly is connceted to. Stored so that multiple /// vessels cannot connect to the same anomaly. /// [ViewVariables(VVAccess.ReadWrite)] public EntityUid? ConnectedVessel; /// /// The minimum amount of research points generated per second /// [DataField("minPointsPerSecond")] public int MinPointsPerSecond = 10; /// /// The maximum amount of research points generated per second /// This doesn't include the point bonus for being unstable. /// [DataField("maxPointsPerSecond")] public int MaxPointsPerSecond = 80; /// /// The multiplier applied to the point value for the /// anomaly being above the /// [DataField("growingPointMultiplier")] public float GrowingPointMultiplier = 1.5f; #endregion /// /// The amount of damage dealt when either a player touches the anomaly /// directly or by hitting the anomaly. /// [DataField(required: true)] public DamageSpecifier AnomalyContactDamage = default!; /// /// The sound effect played when a player /// burns themselves on an anomaly via contact. /// [DataField] public SoundSpecifier AnomalyContactDamageSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); /// /// A prototype entity that appears when an anomaly supercrit collapse. /// [DataField, ViewVariables(VVAccess.ReadWrite)] public EntProtoId? CorePrototype; /// /// A prototype entity that appears when an anomaly decays. /// [DataField, ViewVariables(VVAccess.ReadWrite)] public EntProtoId? CoreInertPrototype; #region Floating Animation /// /// How long it takes to go from the bottom of the animation to the top. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("animationTime")] public float AnimationTime = 2f; /// /// How far it goes in any direction. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("offset")] public Vector2 FloatingOffset = new(0, 0.15f); public readonly string AnimationKey = "anomalyfloat"; #endregion } /// /// Event raised at regular intervals on an anomaly to do whatever its effect is. /// /// The anomaly pulsing /// /// [ByRefEvent] public readonly record struct AnomalyPulseEvent(EntityUid Anomaly, float Stability, float Severity); /// /// Event raised on an anomaly when it reaches a supercritical point. /// [ByRefEvent] public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly); /// /// Event broadcast after an anomaly goes supercritical /// /// The anomaly being shut down. /// Whether or not the anomaly shut down passively or via a supercritical event. [ByRefEvent] public readonly record struct AnomalyShutdownEvent(EntityUid Anomaly, bool Supercritical); /// /// Event broadcast when an anomaly's severity is changed. /// /// The anomaly being changed [ByRefEvent] public readonly record struct AnomalySeverityChangedEvent(EntityUid Anomaly, float Severity); /// /// Event broadcast when an anomaly's stability is changed. /// [ByRefEvent] public readonly record struct AnomalyStabilityChangedEvent(EntityUid Anomaly, float Stability); /// /// Event broadcast when an anomaly's health is changed. /// /// The anomaly being changed [ByRefEvent] public readonly record struct AnomalyHealthChangedEvent(EntityUid Anomaly, float Health);