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,