Add Fire-fighting remote for Fire-doors (#16189)

This commit is contained in:
Tom Leys
2023-08-11 21:29:33 +12:00
committed by GitHub
parent 89250e375c
commit 4a55a000cb
10 changed files with 171 additions and 112 deletions

View File

@@ -5,7 +5,7 @@ namespace Content.Client.Doors;
public sealed class FirelockSystem : EntitySystem public sealed class FirelockSystem : EntitySystem
{ {
[Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!; [Dependency] protected readonly SharedAppearanceSystem _appearanceSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -18,10 +18,23 @@ public sealed class FirelockSystem : EntitySystem
if (args.Sprite == null) if (args.Sprite == null)
return; return;
// Apply the closed lights bool to the sprite var boltedVisible = false;
bool unlitVisible = var unlitVisible = false;
(AppearanceSystem.TryGetData<bool>(uid, DoorVisuals.ClosedLights, out var closedLights, args.Component) &&
closedLights); if (!_appearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
args.Sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible); state = DoorState.Closed;
if (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.Powered, out var powered, args.Component) && powered)
{
boltedVisible = _appearanceSystem.TryGetData<bool>(uid, DoorVisuals.BoltLights, out var lights, args.Component) && lights;
unlitVisible =
state == DoorState.Closing
|| state == DoorState.Opening
|| state == DoorState.Denying
|| (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.ClosedLights, out var closedLights, args.Component) && closedLights);
}
args.Sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible && !boltedVisible);
args.Sprite.LayerSetVisible(DoorVisualLayers.BaseBolted, boltedVisible);
} }
} }

View File

@@ -4,7 +4,10 @@ using Content.Server.Atmos.Monitor.Systems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Server.Remotes;
using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Components;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.Doors; using Content.Shared.Doors;
@@ -25,6 +28,7 @@ namespace Content.Server.Doors.Systems
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!; [Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
private static float _visualUpdateInterval = 0.5f; private static float _visualUpdateInterval = 0.5f;
private float _accumulatedFrameTime; private float _accumulatedFrameTime;
@@ -133,7 +137,10 @@ namespace Content.Server.Doors.Systems
private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args) private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args)
{ {
if (!this.IsPowered(uid, EntityManager) || IsHoldingPressureOrFire(uid, component)) // Give the Door remote the ability to force a firelock open even if it is holding back dangerous gas
var overrideAccess = (args.User != null) && _accessReaderSystem.IsAllowed(args.User.Value, uid);
if (!this.IsPowered(uid, EntityManager) || (!overrideAccess && IsHoldingPressureOrFire(uid, component)))
args.Cancel(); args.Cancel();
} }

View File

@@ -39,10 +39,14 @@ namespace Content.Server.Remotes
component.Mode = OperatingMode.ToggleBolts; component.Mode = OperatingMode.ToggleBolts;
switchMessageId = "door-remote-switch-state-toggle-bolts"; switchMessageId = "door-remote-switch-state-toggle-bolts";
break; break;
// Skip toggle bolts mode and move on from there (to emergency access)
case OperatingMode.ToggleBolts: case OperatingMode.ToggleBolts:
component.Mode = OperatingMode.ToggleEmergencyAccess; component.Mode = OperatingMode.ToggleEmergencyAccess;
switchMessageId = "door-remote-switch-state-toggle-emergency-access"; switchMessageId = "door-remote-switch-state-toggle-emergency-access";
break; break;
// Skip ToggleEmergencyAccess mode and move on from there (to door toggle)
case OperatingMode.ToggleEmergencyAccess: case OperatingMode.ToggleEmergencyAccess:
component.Mode = OperatingMode.OpenClose; component.Mode = OperatingMode.OpenClose;
switchMessageId = "door-remote-switch-state-open-close"; switchMessageId = "door-remote-switch-state-open-close";
@@ -56,15 +60,18 @@ namespace Content.Server.Remotes
private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, BeforeRangedInteractEvent args) private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, BeforeRangedInteractEvent args)
{ {
bool isAirlock = TryComp<AirlockComponent>(args.Target, out var airlockComp);
if (args.Handled if (args.Handled
|| args.Target == null || args.Target == null
|| !TryComp<DoorComponent>(args.Target, out var doorComp) // If it isn't a door we don't use it || !TryComp<DoorComponent>(args.Target, out var doorComp) // If it isn't a door we don't use it
|| !TryComp<AirlockComponent>(args.Target, out var airlockComp) // Remotes only work on airlocks
// The remote can be used anywhere the user can see the door. // The remote can be used anywhere the user can see the door.
// This doesn't work that well, but I don't know of an alternative // This doesn't work that well, but I don't know of an alternative
|| !_interactionSystem.InRangeUnobstructed(args.User, args.Target.Value, || !_interactionSystem.InRangeUnobstructed(args.User, args.Target.Value,
SharedInteractionSystem.MaxRaycastRange, CollisionGroup.Opaque)) SharedInteractionSystem.MaxRaycastRange, CollisionGroup.Opaque))
{
return; return;
}
args.Handled = true; args.Handled = true;
@@ -74,8 +81,10 @@ namespace Content.Server.Remotes
return; return;
} }
if (TryComp<AccessReaderComponent>(args.Target, out var accessComponent) && // Holding the door remote grants you access to the relevant doors IN ADDITION to what ever access you had.
!_doorSystem.HasAccess(args.Target.Value, args.Used, doorComp, accessComponent)) // This access is enforced in _doorSystem.HasAccess when it calls _accessReaderSystem.IsAllowed
if (TryComp<AccessReaderComponent>(args.Target, out var accessComponent)
&& !_doorSystem.HasAccess(args.Target.Value, args.User, doorComp, accessComponent))
{ {
_doorSystem.Deny(args.Target.Value, doorComp, args.User); _doorSystem.Deny(args.Target.Value, doorComp, args.User);
ShowPopupToUser("door-remote-denied", args.User); ShowPopupToUser("door-remote-denied", args.User);
@@ -85,7 +94,10 @@ namespace Content.Server.Remotes
switch (component.Mode) switch (component.Mode)
{ {
case OperatingMode.OpenClose: case OperatingMode.OpenClose:
if (_doorSystem.TryToggleDoor(args.Target.Value, doorComp, args.Used)) // Note we provide args.User here to TryToggleDoor as the "user"
// This means that the door will look at all access items carryed by the player for access, including
// this remote, but also including anything else they are carrying such as a PDA or ID card.
if (_doorSystem.TryToggleDoor(args.Target.Value, doorComp, args.User))
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)}: {doorComp.State}"); _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)}: {doorComp.State}");
break; break;
case OperatingMode.ToggleBolts: case OperatingMode.ToggleBolts:
@@ -99,8 +111,12 @@ namespace Content.Server.Remotes
} }
break; break;
case OperatingMode.ToggleEmergencyAccess: case OperatingMode.ToggleEmergencyAccess:
_airlock.ToggleEmergencyAccess(args.Target.Value, airlockComp); if (airlockComp != null)
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to set emergency access {(airlockComp.EmergencyAccess ? "on" : "off")}"); {
_airlock.ToggleEmergencyAccess(args.Target.Value, airlockComp);
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to set emergency access {(airlockComp.EmergencyAccess ? "on" : "off")}");
}
break; break;
default: default:
throw new InvalidOperationException( throw new InvalidOperationException(

View File

@@ -21,6 +21,7 @@ namespace Content.Shared.Doors
/// </summary> /// </summary>
public sealed class BeforeDoorOpenedEvent : CancellableEntityEventArgs public sealed class BeforeDoorOpenedEvent : CancellableEntityEventArgs
{ {
public EntityUid? User = null;
} }
/// <summary> /// <summary>

View File

@@ -8,7 +8,7 @@ public abstract class SharedDoorBoltSystem : EntitySystem
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!;
[Dependency] private readonly SharedPopupSystem Popup = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();

View File

@@ -249,7 +249,7 @@ public abstract class SharedDoorSystem : EntitySystem
if (door.State == DoorState.Welded) if (door.State == DoorState.Welded)
return false; return false;
var ev = new BeforeDoorOpenedEvent(); var ev = new BeforeDoorOpenedEvent(){User=user};
RaiseLocalEvent(uid, ev, false); RaiseLocalEvent(uid, ev, false);
if (ev.Cancelled) if (ev.Cancelled)
return false; return false;
@@ -496,10 +496,10 @@ public abstract class SharedDoorSystem : EntitySystem
if (TryComp<AirlockComponent>(uid, out var airlock) && airlock.EmergencyAccess) if (TryComp<AirlockComponent>(uid, out var airlock) && airlock.EmergencyAccess)
return true; return true;
// Can't click to close firelocks. // Anyone can click to open firelocks
if (Resolve(uid, ref door) && door.State == DoorState.Open && if (Resolve(uid, ref door) && door.State == DoorState.Closed &&
TryComp<FirelockComponent>(uid, out var firelock)) TryComp<FirelockComponent>(uid, out var firelock))
return false; return true;
if (!Resolve(uid, ref access, false)) if (!Resolve(uid, ref access, false))
return true; return true;

View File

@@ -89,6 +89,7 @@
- id: GasAnalyzer - id: GasAnalyzer
- id: MedkitOxygenFilled - id: MedkitOxygenFilled
- id: HolofanProjector - id: HolofanProjector
- id: DoorRemoteFirefight
- type: entity - type: entity
id: LockerAtmosphericsFilled id: LockerAtmosphericsFilled

View File

@@ -138,6 +138,24 @@
groups: groups:
- Engineering - Engineering
- type: entity
parent: DoorRemoteDefault
id: DoorRemoteFirefight
name: fire-fighting door remote
description: A gadget which can open and bolt FireDoors remotely.
components:
- type: Sprite
layers:
- state: door_remotebase
- state: door_remotelightscolour
color: "#ff9900"
- state: door_remotescreencolour
color: "#e02020"
- type: Access
groups:
- Engineering
- type: entity - type: entity
parent: DoorRemoteDefault parent: DoorRemoteDefault
id: DoorRemoteAll id: DoorRemoteAll

View File

@@ -1,94 +1,94 @@
- type: entity - type: entity
id: HighSecDoor id: HighSecDoor
parent: BaseStructure parent: BaseStructure
name: high security door name: high security door
description: Keeps the bad out and keeps the good in. description: Keeps the bad out and keeps the good in.
placement: placement:
mode: SnapgridCenter mode: SnapgridCenter
components: components:
- type: InteractionOutline - type: InteractionOutline
- type: Sprite - type: Sprite
sprite: Structures/Doors/Airlocks/highsec/highsec.rsi sprite: Structures/Doors/Airlocks/highsec/highsec.rsi
layers: layers:
- state: closed - state: closed
map: ["enum.DoorVisualLayers.Base"] map: ["enum.DoorVisualLayers.Base"]
- state: closed_unlit - state: closed_unlit
shader: unshaded shader: unshaded
map: ["enum.DoorVisualLayers.BaseUnlit"] map: ["enum.DoorVisualLayers.BaseUnlit"]
- state: welded - state: welded
map: ["enum.WeldableLayers.BaseWelded"] map: ["enum.WeldableLayers.BaseWelded"]
- state: bolted_unlit - state: bolted_unlit
shader: unshaded shader: unshaded
map: ["enum.DoorVisualLayers.BaseBolted"] map: ["enum.DoorVisualLayers.BaseBolted"]
- state: emergency_unlit - state: emergency_unlit
map: ["enum.DoorVisualLayers.BaseEmergencyAccess"] map: ["enum.DoorVisualLayers.BaseEmergencyAccess"]
shader: unshaded shader: unshaded
- state: panel_open - state: panel_open
map: ["enum.WiresVisualLayers.MaintenancePanel"] map: ["enum.WiresVisualLayers.MaintenancePanel"]
- type: AnimationPlayer - type: AnimationPlayer
- type: Physics - type: Physics
- type: Fixtures - type: Fixtures
fixtures: fixtures:
fix1: fix1:
shape: shape:
!type:PhysShapeAabb !type:PhysShapeAabb
bounds: "-0.49,-0.49,0.49,0.49" # don't want this colliding with walls or they won't close bounds: "-0.49,-0.49,0.49,0.49" # don't want this colliding with walls or they won't close
density: 100 density: 100
mask: mask:
- FullTileMask - FullTileMask
layer: layer:
- WallLayer - WallLayer
- type: ContainerFill - type: ContainerFill
containers: containers:
board: [ DoorElectronics ] board: [ DoorElectronics ]
- type: ContainerContainer - type: ContainerContainer
containers: containers:
board: !type:Container board: !type:Container
- type: Door - type: Door
crushDamage: crushDamage:
types: types:
Blunt: 50 Blunt: 50
openSound: openSound:
path: /Audio/Machines/airlock_open.ogg path: /Audio/Machines/airlock_open.ogg
closeSound: closeSound:
path: /Audio/Machines/airlock_close.ogg path: /Audio/Machines/airlock_close.ogg
denySound: denySound:
path: /Audio/Machines/airlock_deny.ogg path: /Audio/Machines/airlock_deny.ogg
- type: Weldable - type: Weldable
time: 10 time: 10
- type: Airlock - type: Airlock
- type: DoorBolt - type: DoorBolt
- type: Appearance - type: Appearance
- type: WiresVisuals - type: WiresVisuals
- type: ApcPowerReceiver - type: ApcPowerReceiver
powerLoad: 20 powerLoad: 20
- type: ExtensionCableReceiver - type: ExtensionCableReceiver
- type: Electrified - type: Electrified
enabled: false enabled: false
usesApcPower: true usesApcPower: true
- type: WiresPanel - type: WiresPanel
- type: Wires - type: Wires
BoardName: "HighSec Control" BoardName: "HighSec Control"
LayoutId: HighSec LayoutId: HighSec
alwaysRandomize: true alwaysRandomize: true
- type: UserInterface - type: UserInterface
interfaces: interfaces:
- key: enum.WiresUiKey.Key - key: enum.WiresUiKey.Key
type: WiresBoundUserInterface type: WiresBoundUserInterface
- type: Airtight - type: Airtight
fixVacuum: true fixVacuum: true
- type: Occluder - type: Occluder
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic
damageModifierSet: Metallic damageModifierSet: Metallic
- type: Destructible - type: Destructible
thresholds: thresholds:
- trigger: - trigger:
!type:DamageTrigger !type:DamageTrigger
damage: 1500 damage: 1500
behaviors: behaviors:
- !type:DoActsBehavior - !type:DoActsBehavior
acts: ["Destruction"] acts: ["Destruction"]
- type: IconSmooth - type: IconSmooth
key: walls key: walls
mode: NoSprite mode: NoSprite

View File

@@ -104,6 +104,9 @@
arc: 360 arc: 360
- type: StaticPrice - type: StaticPrice
price: 150 price: 150
- type: DoorBolt
- type: AccessReader
access: [ [ "Engineering" ] ]
- type: entity - type: entity
id: Firelock id: Firelock