Anomaly Vessel visuals + audio (#13927)
@@ -25,7 +25,9 @@ public sealed partial class AnomalySystem
|
|||||||
SubscribeLocalEvent<AnomalyVesselComponent, InteractUsingEvent>(OnVesselInteractUsing);
|
SubscribeLocalEvent<AnomalyVesselComponent, InteractUsingEvent>(OnVesselInteractUsing);
|
||||||
SubscribeLocalEvent<AnomalyVesselComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<AnomalyVesselComponent, ExaminedEvent>(OnExamined);
|
||||||
SubscribeLocalEvent<AnomalyVesselComponent, ResearchServerGetPointsPerSecondEvent>(OnVesselGetPointsPerSecond);
|
SubscribeLocalEvent<AnomalyVesselComponent, ResearchServerGetPointsPerSecondEvent>(OnVesselGetPointsPerSecond);
|
||||||
|
SubscribeLocalEvent<AnomalyVesselComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||||
SubscribeLocalEvent<AnomalyShutdownEvent>(OnVesselAnomalyShutdown);
|
SubscribeLocalEvent<AnomalyShutdownEvent>(OnVesselAnomalyShutdown);
|
||||||
|
SubscribeLocalEvent<AnomalyStabilityChangedEvent>(OnVesselAnomalyStabilityChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnExamined(EntityUid uid, AnomalyVesselComponent component, ExaminedEvent args)
|
private void OnExamined(EntityUid uid, AnomalyVesselComponent component, ExaminedEvent args)
|
||||||
@@ -94,6 +96,11 @@ public sealed partial class AnomalySystem
|
|||||||
args.Points += (int) (GetAnomalyPointValue(anomaly) * component.PointMultiplier);
|
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)
|
private void OnVesselAnomalyShutdown(ref AnomalyShutdownEvent args)
|
||||||
{
|
{
|
||||||
foreach (var component in EntityQuery<AnomalyVesselComponent>())
|
foreach (var component in EntityQuery<AnomalyVesselComponent>())
|
||||||
@@ -112,6 +119,18 @@ public sealed partial class AnomalySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>
|
/// <summary>
|
||||||
/// Updates the appearance of an anomaly vessel
|
/// Updates the appearance of an anomaly vessel
|
||||||
/// based on whether or not it has an anomaly
|
/// based on whether or not it has an anomaly
|
||||||
@@ -125,11 +144,61 @@ public sealed partial class AnomalySystem
|
|||||||
|
|
||||||
var on = component.Anomaly != null;
|
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))
|
if (TryComp<SharedPointLightComponent>(uid, out var pointLightComponent))
|
||||||
{
|
{
|
||||||
pointLightComponent.Enabled = on;
|
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);
|
_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,4 +124,11 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
|||||||
_ => throw new ArgumentOutOfRangeException()
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
UpdateVessels();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Content.Shared.Construction.Prototypes;
|
using Content.Shared.Construction.Prototypes;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
namespace Content.Server.Anomaly.Components;
|
namespace Content.Server.Anomaly.Components;
|
||||||
@@ -37,4 +39,28 @@ public sealed class AnomalyVesselComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("partRatingPointModifier")]
|
[DataField("partRatingPointModifier")]
|
||||||
public float PartRatingPointModifier = 1.5f;
|
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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ public enum AnomalousParticleType : byte
|
|||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum AnomalyVesselVisuals : byte
|
public enum AnomalyVesselVisuals : byte
|
||||||
{
|
{
|
||||||
HasAnomaly
|
HasAnomaly,
|
||||||
|
AnomalyState
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -12,3 +12,8 @@
|
|||||||
license: "CC0-1.0"
|
license: "CC0-1.0"
|
||||||
copyright: "receipt printing.wav by 13F_Panska_Tlolkova_Matilda. This version is cleaned up, shortened, and converted to OGG."
|
copyright: "receipt printing.wav by 13F_Panska_Tlolkova_Matilda. This version is cleaned up, shortened, and converted to OGG."
|
||||||
source: "https://freesound.org/people/13F_Panska_Tlolkova_Matilda/sounds/378331"
|
source: "https://freesound.org/people/13F_Panska_Tlolkova_Matilda/sounds/378331"
|
||||||
|
|
||||||
|
- files: ["vessel_warning.ogg"]
|
||||||
|
license: "CC-BY-4.0"
|
||||||
|
copyright: "Created by AUDACITIER (freesound), converted to MONO and .ogg and edited by EmoGarbage404 (github)."
|
||||||
|
source: "https://freesound.org/people/AUDACITIER/sounds/629196/"
|
||||||
BIN
Resources/Audio/Machines/vessel_warning.ogg
Normal file
@@ -9,10 +9,11 @@
|
|||||||
sprite: Structures/Machines/Anomaly/anomaly_vessel.rsi
|
sprite: Structures/Machines/Anomaly/anomaly_vessel.rsi
|
||||||
layers:
|
layers:
|
||||||
- state: base
|
- state: base
|
||||||
- state: powered
|
- state: powered-1
|
||||||
shader: unshaded
|
shader: unshaded
|
||||||
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
||||||
- state: anomaly
|
- state: anomaly-1
|
||||||
|
visible: false
|
||||||
shader: unshaded
|
shader: unshaded
|
||||||
map: ["enum.AnomalyVesselVisualLayers.Base"]
|
map: ["enum.AnomalyVesselVisualLayers.Base"]
|
||||||
- state: panel
|
- state: panel
|
||||||
@@ -54,6 +55,15 @@
|
|||||||
enum.AnomalyVesselVisualLayers.Base:
|
enum.AnomalyVesselVisualLayers.Base:
|
||||||
True: { visible: true }
|
True: { visible: true }
|
||||||
False: { visible: false }
|
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.WiresVisuals.MaintenancePanelState:
|
||||||
enum.WiresVisualLayers.MaintenancePanel:
|
enum.WiresVisualLayers.MaintenancePanel:
|
||||||
True: { visible: false }
|
True: { visible: false }
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 283 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 259 B |
@@ -1,14 +1,20 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"copyright": "Created by EmoGarbage404",
|
"copyright": "Created by EmoGarbage404 (github)",
|
||||||
"size": {
|
"size": {
|
||||||
"x": 32,
|
"x": 32,
|
||||||
"y": 32
|
"y": 32
|
||||||
},
|
},
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "anomaly"
|
"name": "anomaly-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "anomaly-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "anomaly-3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "base"
|
"name": "base"
|
||||||
@@ -17,7 +23,13 @@
|
|||||||
"name": "panel"
|
"name": "panel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "powered"
|
"name": "powered-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "powered-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "powered-3"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 133 B After Width: | Height: | Size: 133 B |
|
After Width: | Height: | Size: 143 B |
|
After Width: | Height: | Size: 143 B |