SubscribeLocalEvent<AnomalyVesselComponent, InteractUsingEvent>(OnVesselInteractUsing);
SubscribeLocalEvent<AnomalyVesselComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<AnomalyVesselComponent, ResearchServerGetPointsPerSecondEvent>(OnVesselGetPointsPerSecond);
+ SubscribeLocalEvent<AnomalyVesselComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<AnomalyShutdownEvent>(OnVesselAnomalyShutdown);
+ SubscribeLocalEvent<AnomalyStabilityChangedEvent>(OnVesselAnomalyStabilityChanged);
}
private void OnExamined(EntityUid uid, AnomalyVesselComponent component, ExaminedEvent args)
args.Points += (int) (GetAnomalyPointValue(anomaly) * component.PointMultiplier);
}
+ private void OnUnpaused(EntityUid uid, AnomalyVesselComponent component, ref EntityUnpausedEvent args)
+ {
+ component.NextBeep += args.PausedTime;
+ }
+
private void OnVesselAnomalyShutdown(ref AnomalyShutdownEvent args)
{
foreach (var component in EntityQuery<AnomalyVesselComponent>())
}
}
+ private void OnVesselAnomalyStabilityChanged(ref AnomalyStabilityChangedEvent args)
+ {
+ foreach (var component in EntityQuery<AnomalyVesselComponent>())
+ {
+ var ent = component.Owner;
+ if (args.Anomaly != component.Anomaly)
+ continue;
+
+ UpdateVesselAppearance(ent, component);
+ }
+ }
+
/// <summary>
/// Updates the appearance of an anomaly vessel
/// based on whether or not it has an anomaly
var on = component.Anomaly != null;
- Appearance.SetData(uid, AnomalyVesselVisuals.HasAnomaly, on);
+ if (!TryComp<AppearanceComponent>(uid, out var appearanceComponent))
+ return;
+
+ Appearance.SetData(uid, AnomalyVesselVisuals.HasAnomaly, on, appearanceComponent);
if (TryComp<SharedPointLightComponent>(uid, out var pointLightComponent))
{
pointLightComponent.Enabled = on;
}
+
+ // arbitrary value for the generic visualizer to use.
+ // i didn't feel like making an enum for this.
+ var value = 1;
+ if (TryComp<AnomalyComponent>(component.Anomaly, out var anomalyComp))
+ {
+ if (anomalyComp.Stability <= anomalyComp.DecayThreshold)
+ {
+ value = 2;
+ }
+ else if (anomalyComp.Stability >= anomalyComp.GrowthThreshold)
+ {
+ value = 3;
+ }
+ }
+ Appearance.SetData(uid, AnomalyVesselVisuals.AnomalyState, value, appearanceComponent);
+
_ambient.SetAmbience(uid, on);
}
+
+ private void UpdateVessels()
+ {
+ foreach (var vessel in EntityQuery<AnomalyVesselComponent>())
+ {
+ var vesselEnt = vessel.Owner;
+ if (vessel.Anomaly is not { } anomUid)
+ continue;
+
+ if (!TryComp<AnomalyComponent>(anomUid, out var anomaly))
+ continue;
+
+ if (Timing.CurTime < vessel.NextBeep)
+ continue;
+
+ // a lerp between the max and min values for each threshold.
+ // longer beeps that get shorter as the anomaly gets more extreme
+ float timerPercentage;
+ if (anomaly.Stability <= anomaly.DecayThreshold)
+ timerPercentage = (anomaly.DecayThreshold - anomaly.Stability) / anomaly.DecayThreshold;
+ else if (anomaly.Stability >= anomaly.GrowthThreshold)
+ timerPercentage = (anomaly.Stability - anomaly.GrowthThreshold) / (1 - anomaly.GrowthThreshold);
+ else //it's not unstable
+ continue;
+
+ Audio.PlayPvs(vessel.BeepSound, vesselEnt);
+ var beepInterval = (vessel.MaxBeepInterval - vessel.MinBeepInterval) * (1 - timerPercentage) + vessel.MinBeepInterval;
+ vessel.NextBeep = beepInterval + Timing.CurTime;
+ }
+ }
}
using Content.Shared.Construction.Prototypes;
+using Robust.Shared.Audio;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Anomaly.Components;
/// </summary>
[DataField("partRatingPointModifier")]
public float PartRatingPointModifier = 1.5f;
+
+ /// <summary>
+ /// The maximum time between each beep
+ /// </summary>
+ [DataField("maxBeepInterval")]
+ public TimeSpan MaxBeepInterval = TimeSpan.FromSeconds(2f);
+
+ /// <summary>
+ /// The minimum time between each beep
+ /// </summary>
+ [DataField("minBeepInterval")]
+ public TimeSpan MinBeepInterval = TimeSpan.FromSeconds(0.75f);
+
+ /// <summary>
+ /// When the next beep sound will play
+ /// </summary>
+ [DataField("nextBeep", customTypeSerializer:typeof(TimeOffsetSerializer))]
+ public TimeSpan NextBeep = TimeSpan.Zero;
+
+ /// <summary>
+ /// The sound that is played repeatedly when the anomaly is destabilizing/decaying
+ /// </summary>
+ [DataField("beepSound")]
+ public SoundSpecifier BeepSound = new SoundPathSpecifier("/Audio/Machines/vessel_warning.ogg");
}
sprite: Structures/Machines/Anomaly/anomaly_vessel.rsi
layers:
- state: base
- - state: powered
+ - state: powered-1
shader: unshaded
map: ["enum.PowerDeviceVisualLayers.Powered"]
- - state: anomaly
+ - state: anomaly-1
+ visible: false
shader: unshaded
map: ["enum.AnomalyVesselVisualLayers.Base"]
- state: panel
enum.AnomalyVesselVisualLayers.Base:
True: { visible: true }
False: { visible: false }
+ enum.AnomalyVesselVisuals.AnomalyState:
+ enum.PowerDeviceVisualLayers.Powered:
+ 1: { state: powered-1 }
+ 2: { state: powered-2 }
+ 3: { state: powered-3 }
+ enum.AnomalyVesselVisualLayers.Base:
+ 1: { state: anomaly-1 }
+ 2: { state: anomaly-2 }
+ 3: { state: anomaly-3 }
enum.WiresVisuals.MaintenancePanelState:
enum.WiresVisualLayers.MaintenancePanel:
True: { visible: false }