diff --git a/Content.Client/Light/EmergencyLightSystem.cs b/Content.Client/Light/EmergencyLightSystem.cs
index 2859dc4b71..34f2b9bf05 100644
--- a/Content.Client/Light/EmergencyLightSystem.cs
+++ b/Content.Client/Light/EmergencyLightSystem.cs
@@ -13,7 +13,7 @@ namespace Content.Client.Light
{
public sealed class EmergencyLightSystem : SharedEmergencyLightSystem
{
- private const float DegreesPerSecond = 270;
+ private const float DegreesPerSecond = 90;
private static Animation Animation =>
new()
{
diff --git a/Content.Client/Light/Visualizers/EmergencyLightVisualizer.cs b/Content.Client/Light/Visualizers/EmergencyLightVisualizer.cs
index 097492eb47..af106a08e1 100644
--- a/Content.Client/Light/Visualizers/EmergencyLightVisualizer.cs
+++ b/Content.Client/Light/Visualizers/EmergencyLightVisualizer.cs
@@ -20,7 +20,19 @@ namespace Content.Client.Light.Visualizers
if (!component.TryGetData(EmergencyLightVisuals.On, out bool on))
on = false;
- sprite.LayerSetState(0, on ? "emergency_light_on" : "emergency_light_off");
+ sprite.LayerSetState(EmergencyLightVisualLayers.Light, on ? "emergency_light_on" : "emergency_light_off");
+ sprite.LayerSetShader(EmergencyLightVisualLayers.Light, on ? "unshaded" : "shaded");
+
+ if (component.TryGetData(EmergencyLightVisuals.Color, out Color color))
+ {
+ sprite.LayerSetColor(EmergencyLightVisualLayers.Light, color);
+ }
}
}
}
+
+public enum EmergencyLightVisualLayers
+{
+ Base,
+ Light
+}
diff --git a/Content.Server/AlertLevel/AlertLevelPrototype.cs b/Content.Server/AlertLevel/AlertLevelPrototype.cs
index a1f654708d..56a81e1697 100644
--- a/Content.Server/AlertLevel/AlertLevelPrototype.cs
+++ b/Content.Server/AlertLevel/AlertLevelPrototype.cs
@@ -58,6 +58,16 @@ public sealed class AlertLevelDetail
///
[DataField("color")] public Color Color { get; } = Color.White;
+ ///
+ /// The color to turn emergency lights on this station when they are active.
+ ///
+ [DataField("emergencyLightColor")] public Color EmergencyLightColor { get; } = Color.FromHex("#FF4020");
+
+ ///
+ /// Will this alert level force emergency lights on for the station that's active?
+ ///
+ [DataField("forceEnableEmergencyLights")] public bool ForceEnableEmergencyLights { get; } = false;
+
///
/// How long it takes for the shuttle to arrive when called.
///
diff --git a/Content.Server/Light/Components/EmergencyLightComponent.cs b/Content.Server/Light/Components/EmergencyLightComponent.cs
index bc43c83196..3d55b5c559 100644
--- a/Content.Server/Light/Components/EmergencyLightComponent.cs
+++ b/Content.Server/Light/Components/EmergencyLightComponent.cs
@@ -12,6 +12,12 @@ namespace Content.Server.Light.Components
[ViewVariables]
public EmergencyLightState State;
+ ///
+ /// Is this emergency light forced on for some reason and cannot be disabled through normal means
+ /// (i.e. delta alert level?)
+ ///
+ public bool ForciblyEnabled = false;
+
[ViewVariables(VVAccess.ReadWrite)]
[DataField("wattage")]
public float Wattage = 5;
diff --git a/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs b/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs
index 99e05a1bbe..6b5c784dca 100644
--- a/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs
+++ b/Content.Server/Light/EntitySystems/EmergencyLightSystem.cs
@@ -1,23 +1,33 @@
+using System.Drawing;
+using Content.Server.AlertLevel;
+using Content.Server.Audio;
using Content.Server.Light.Components;
using Content.Server.Power.Components;
+using Content.Server.Station.Components;
+using Content.Server.Station.Systems;
using Content.Shared.Examine;
using Content.Shared.Light;
using Content.Shared.Light.Component;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.GameStates;
+using Color = Robust.Shared.Maths.Color;
namespace Content.Server.Light.EntitySystems
{
[UsedImplicitly]
public sealed class EmergencyLightSystem : SharedEmergencyLightSystem
{
+ [Dependency] private readonly AmbientSoundSystem _ambient = default!;
+ [Dependency] private readonly StationSystem _station = default!;
+
private readonly HashSet _activeLights = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(HandleEmergencyLightMessage);
+ SubscribeLocalEvent(OnAlertLevelChanged);
SubscribeLocalEvent(GetCompState);
SubscribeLocalEvent(HandleLightToggle);
SubscribeLocalEvent(OnEmergencyExamine);
@@ -35,6 +45,24 @@ namespace Content.Server.Light.EntitySystems
Loc.GetString("emergency-light-component-on-examine",
("batteryStateText",
Loc.GetString(component.BatteryStateText[component.State]))));
+
+ // Show alert level on the light itself.
+ if (!TryComp(_station.GetOwningStation(uid), out var alerts))
+ return;
+
+ if (alerts.AlertLevels == null)
+ return;
+
+ var name = alerts.CurrentLevel;
+
+ var color = Color.White;
+ if (alerts.AlertLevels.Levels.TryGetValue(alerts.CurrentLevel, out var details))
+ color = details.Color;
+
+ args.PushMarkup(
+ Loc.GetString("emergency-light-component-on-examine-alert",
+ ("color", color.ToHex()),
+ ("level", name)));
}
private void HandleLightToggle(EntityUid uid, EmergencyLightComponent component, PointLightToggleEvent args)
@@ -66,6 +94,36 @@ namespace Content.Server.Light.EntitySystems
}
}
+ private void OnAlertLevelChanged(AlertLevelChangedEvent ev)
+ {
+ if (!TryComp(ev.Station, out var alert))
+ return;
+
+ if (alert.AlertLevels == null || !alert.AlertLevels.Levels.TryGetValue(ev.AlertLevel, out var details))
+ return;
+
+ foreach (var (light, pointLight, appearance, xform) in EntityQuery())
+ {
+ if (CompOrNull(xform.GridUid)?.Station != ev.Station)
+ continue;
+
+ pointLight.Color = details.EmergencyLightColor;
+ appearance.SetData(EmergencyLightVisuals.Color, details.EmergencyLightColor);
+
+ if (details.ForceEnableEmergencyLights && !light.ForciblyEnabled)
+ {
+ light.ForciblyEnabled = true;
+ TurnOn(light);
+ }
+ else if (!details.ForceEnableEmergencyLights && light.ForciblyEnabled)
+ {
+ // Previously forcibly enabled, and we went down an alert level.
+ light.ForciblyEnabled = false;
+ UpdateState(light);
+ }
+ }
+ }
+
public void SetState(EmergencyLightComponent component, EmergencyLightState state)
{
if (component.State == state) return;
@@ -122,7 +180,7 @@ namespace Content.Server.Light.EntitySystems
return;
}
- if (receiver.Powered)
+ if (receiver.Powered && !component.ForciblyEnabled)
{
receiver.Load = (int) Math.Abs(component.Wattage);
TurnOff(component);
@@ -144,6 +202,8 @@ namespace Content.Server.Light.EntitySystems
if (TryComp(component.Owner, out AppearanceComponent? appearance))
appearance.SetData(EmergencyLightVisuals.On, false);
+
+ _ambient.SetAmbience(component.Owner, false);
}
private void TurnOn(EmergencyLightComponent component)
@@ -154,7 +214,12 @@ namespace Content.Server.Light.EntitySystems
}
if (TryComp(component.Owner, out AppearanceComponent? appearance))
+ {
appearance.SetData(EmergencyLightVisuals.On, true);
+ }
+
+ _ambient.SetAmbience(component.Owner, true);
}
}
}
+
diff --git a/Content.Shared/Light/Component/SharedEmergencyLightComponent.cs b/Content.Shared/Light/Component/SharedEmergencyLightComponent.cs
index ec380880a5..ef41400bfb 100644
--- a/Content.Shared/Light/Component/SharedEmergencyLightComponent.cs
+++ b/Content.Shared/Light/Component/SharedEmergencyLightComponent.cs
@@ -24,5 +24,6 @@ namespace Content.Shared.Light.Component
public enum EmergencyLightVisuals
{
On,
+ Color
}
}
diff --git a/Resources/Audio/Ambience/Objects/alarm.ogg b/Resources/Audio/Ambience/Objects/alarm.ogg
new file mode 100644
index 0000000000..17b704f6c7
Binary files /dev/null and b/Resources/Audio/Ambience/Objects/alarm.ogg differ
diff --git a/Resources/Audio/Ambience/Objects/license.txt b/Resources/Audio/Ambience/Objects/license.txt
index f315c60331..cdac322938 100644
--- a/Resources/Audio/Ambience/Objects/license.txt
+++ b/Resources/Audio/Ambience/Objects/license.txt
@@ -5,3 +5,4 @@ gas_vent - https://freesound.org/people/kyles/sounds/453642/ - CC0-1.0
flowing_water_open - https://freesound.org/people/sterferny/sounds/382322/ - CC0-1.0
server_fans - https://freesound.org/people/DeVern/sounds/610761/ - CC-BY-3.0
drain.ogg - https://freesound.org/people/PhreaKsAccount/sounds/46266/ - CC-BY-3.0 (by PhreaKsAccount)
+alarm.ogg - https://github.com/Baystation12/Baystation12/commit/41b11ef289bccfdfa2940480beb9c1e3f50c3b93, fire_alarm.ogg CC-BY-SA-3.0
\ No newline at end of file
diff --git a/Resources/Locale/en-US/light/components/emergency-light-component.ftl b/Resources/Locale/en-US/light/components/emergency-light-component.ftl
index b976e5d702..ba4a083a6d 100644
--- a/Resources/Locale/en-US/light/components/emergency-light-component.ftl
+++ b/Resources/Locale/en-US/light/components/emergency-light-component.ftl
@@ -1,5 +1,6 @@
emergency-light-component-on-examine = The battery indicator displays: {$batteryStateText}.
-emergency-light-component-light-state-full = [color=darkgreen]Full[/color]
-emergency-light-component-light-state-empty = [color=darkgreen]Empty[/color]
-emergency-light-component-light-state-charging = [color=darkgreen]Charging[/color]
-emergency-light-component-light-state-on = [color=darkgreen]On[/color]
\ No newline at end of file
+emergency-light-component-on-examine-alert = The current station alert level is: [color={$color}]{$level}[/color].
+emergency-light-component-light-state-full = [color=darkgreen]full[/color]
+emergency-light-component-light-state-empty = [color=darkgreen]empty[/color]
+emergency-light-component-light-state-charging = [color=darkgreen]charging[/color]
+emergency-light-component-light-state-on = [color=darkgreen]on[/color]
diff --git a/Resources/Prototypes/AlertLevels/alert_levels.yml b/Resources/Prototypes/AlertLevels/alert_levels.yml
index 921b3d5145..585ab26d3c 100644
--- a/Resources/Prototypes/AlertLevels/alert_levels.yml
+++ b/Resources/Prototypes/AlertLevels/alert_levels.yml
@@ -14,11 +14,13 @@
announcement: alert-level-violet-announcement
sound: /Audio/Misc/notice1.ogg
color: Violet
+ emergencyLightColor: Violet
shuttleTime: 600
yellow:
announcement: alert-level-yellow-announcement
sound: /Audio/Misc/notice1.ogg
color: Yellow
+ emergencyLightColor: Yellow
shuttleTime: 400
red:
announcement: alert-level-red-announcement
@@ -33,6 +35,8 @@
volume: -2
disableSelection: true
color: PaleVioletRed
+ emergencyLightColor: PaleVioletRed
+ forceEnableEmergencyLights: true
delta:
announcement: alert-level-delta-announcement
selectable: false
@@ -42,6 +46,8 @@
volume: -5
disableSelection: true
color: DarkRed
+ emergencyLightColor: Orange
+ forceEnableEmergencyLights: true
shuttleTime: 1200
epsilon:
announcement: alert-level-epsilon-announcement
@@ -52,4 +58,6 @@
volume: -2
disableSelection: true
color: DarkViolet
+ emergencyLightColor: DarkViolet
+ forceEnableEmergencyLights: true
shuttleTime: 1200
diff --git a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml
index 1d04c6c595..d84cea45ff 100644
--- a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml
+++ b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml
@@ -275,7 +275,7 @@
- type: entity
id: EmergencyLight
name: emergency light
- description: A small red light with an internal battery that turns on as soon as it stops receiving any power.
+ description: A small light with an internal battery that turns on as soon as it stops receiving any power. Nanotrasen technology allows it to adapt its color to alert crew to the conditions of the station.
parent: AlwaysPoweredWallLight
suffix:
components:
@@ -283,6 +283,7 @@
enabled: false
radius: 10
energy: 2.5
+ offset: "0, 0.2"
color: "#FF4020"
mask: /Textures/Effects/LightMasks/cone.png
- type: ApcPowerReceiver
@@ -294,10 +295,19 @@
- type: Sprite
sprite: Structures/Wallmounts/Lighting/emergency_light.rsi
layers:
+ - state: base
+ map: ["enum.EmergencyLightVisualLayers.Base"]
- state: emergency_light_off
+ map: ["enum.EmergencyLightVisualLayers.Light"]
+ color: "#FF4020"
- type: Appearance
visuals:
- type: EmergencyLightVisualizer
+ - type: AmbientSound
+ sound:
+ path: /Audio/Ambience/Objects/alarm.ogg
+ volume: -18
+ range: 5
placement:
mode: SnapgridCenter
snap:
diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/base.png b/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/base.png
new file mode 100644
index 0000000000..8b85bd6141
Binary files /dev/null and b/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/base.png differ
diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/emergency_light_off.png b/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/emergency_light_off.png
index 6427384c2f..e80bf61e19 100644
Binary files a/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/emergency_light_off.png and b/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/emergency_light_off.png differ
diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/emergency_light_on.png b/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/emergency_light_on.png
index 364d44b505..2d89fde13c 100644
Binary files a/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/emergency_light_on.png and b/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/emergency_light_on.png differ
diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/meta.json b/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/meta.json
index 5e722f0710..6d66dbed45 100644
--- a/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/meta.json
+++ b/Resources/Textures/Structures/Wallmounts/Lighting/emergency_light.rsi/meta.json
@@ -5,7 +5,12 @@
"y": 32
},
"license": "CC-BY-SA-3.0",
+ "copyright": "juliang, modified by mirrorcult",
"states": [
+ {
+ "name": "base",
+ "directions": 4
+ },
{
"name": "emergency_light_off",
"directions": 4,