Predict doors and airlocks (#25419)
* predict doors and airlocks * prying, too * ack * eek
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
using Content.Client.Wires.Visualizers;
|
using Content.Client.Wires.Visualizers;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Doors.Systems;
|
using Content.Shared.Doors.Systems;
|
||||||
using Content.Shared.Prying.Components;
|
|
||||||
using Robust.Client.Animations;
|
using Robust.Client.Animations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
@@ -16,13 +15,6 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<AirlockComponent, ComponentStartup>(OnComponentStartup);
|
SubscribeLocalEvent<AirlockComponent, ComponentStartup>(OnComponentStartup);
|
||||||
SubscribeLocalEvent<AirlockComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
SubscribeLocalEvent<AirlockComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||||
SubscribeLocalEvent<AirlockComponent, BeforePryEvent>(OnAirlockPryAttempt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAirlockPryAttempt(EntityUid uid, AirlockComponent component, ref BeforePryEvent args)
|
|
||||||
{
|
|
||||||
// TODO: Temporary until airlocks predicted.
|
|
||||||
args.Cancelled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnComponentStartup(EntityUid uid, AirlockComponent comp, ComponentStartup args)
|
private void OnComponentStartup(EntityUid uid, AirlockComponent comp, ComponentStartup args)
|
||||||
@@ -104,7 +96,7 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
|| state == DoorState.Denying
|
|| state == DoorState.Denying
|
||||||
|| (state == DoorState.Open && comp.OpenUnlitVisible)
|
|| (state == DoorState.Open && comp.OpenUnlitVisible)
|
||||||
|| (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.ClosedLights, out var closedLights, args.Component) && closedLights))
|
|| (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.ClosedLights, out var closedLights, args.Component) && closedLights))
|
||||||
&& !boltedVisible && !emergencyLightsVisible; ;
|
&& !boltedVisible && !emergencyLightsVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible);
|
args.Sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible);
|
||||||
@@ -120,5 +112,17 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
&& !boltedVisible
|
&& !boltedVisible
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case DoorState.Open:
|
||||||
|
args.Sprite.LayerSetState(DoorVisualLayers.BaseUnlit, comp.ClosingSpriteState);
|
||||||
|
args.Sprite.LayerSetAnimationTime(DoorVisualLayers.BaseUnlit, 0);
|
||||||
|
break;
|
||||||
|
case DoorState.Closed:
|
||||||
|
args.Sprite.LayerSetState(DoorVisualLayers.BaseUnlit, comp.OpeningSpriteState);
|
||||||
|
args.Sprite.LayerSetAnimationTime(DoorVisualLayers.BaseUnlit, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
using Content.Client.Wires.Visualizers;
|
|
||||||
using Content.Shared.Doors.Components;
|
|
||||||
using Content.Shared.Doors.Systems;
|
|
||||||
using Robust.Client.Animations;
|
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Client.Doors;
|
|
||||||
|
|
||||||
public sealed class DoorBoltSystem : SharedDoorBoltSystem
|
|
||||||
{
|
|
||||||
// Instantiate sub-class on client for prediction.
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,6 @@ using Content.Shared.Doors.Systems;
|
|||||||
using Robust.Client.Animations;
|
using Robust.Client.Animations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
@@ -73,7 +71,7 @@ public sealed class DoorSystem : SharedDoorSystem
|
|||||||
|
|
||||||
private void OnAppearanceChange(EntityUid uid, DoorComponent comp, ref AppearanceChangeEvent args)
|
private void OnAppearanceChange(EntityUid uid, DoorComponent comp, ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
if (args.Sprite == null || !_gameTiming.IsFirstTimePredicted)
|
if (args.Sprite == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!AppearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
if(!AppearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
||||||
@@ -83,9 +81,9 @@ public sealed class DoorSystem : SharedDoorSystem
|
|||||||
{
|
{
|
||||||
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
|
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
|
||||||
{
|
{
|
||||||
Logger.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
Log.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
||||||
}
|
}
|
||||||
foreach (ISpriteLayer layer in args.Sprite.AllLayers)
|
foreach (var layer in args.Sprite.AllLayers)
|
||||||
{
|
{
|
||||||
layer.Rsi = res?.RSI;
|
layer.Rsi = res?.RSI;
|
||||||
}
|
}
|
||||||
@@ -113,31 +111,24 @@ public sealed class DoorSystem : SharedDoorSystem
|
|||||||
break;
|
break;
|
||||||
case DoorState.Opening:
|
case DoorState.Opening:
|
||||||
if (animPlayer != null && comp.OpeningAnimationTime != 0.0)
|
if (animPlayer != null && comp.OpeningAnimationTime != 0.0)
|
||||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.OpeningAnimation, DoorComponent.AnimationKey);
|
_animationSystem.Play((uid, animPlayer), (Animation)comp.OpeningAnimation, DoorComponent.AnimationKey);
|
||||||
break;
|
break;
|
||||||
case DoorState.Closing:
|
case DoorState.Closing:
|
||||||
if (animPlayer != null && comp.ClosingAnimationTime != 0.0 && comp.CurrentlyCrushing.Count == 0)
|
if (animPlayer != null && comp.ClosingAnimationTime != 0.0 && comp.CurrentlyCrushing.Count == 0)
|
||||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.ClosingAnimation, DoorComponent.AnimationKey);
|
_animationSystem.Play((uid, animPlayer), (Animation)comp.ClosingAnimation, DoorComponent.AnimationKey);
|
||||||
break;
|
break;
|
||||||
case DoorState.Denying:
|
case DoorState.Denying:
|
||||||
if (animPlayer != null && comp.DenyingAnimation != default)
|
if (animPlayer != null)
|
||||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.DenyingAnimation, DoorComponent.AnimationKey);
|
_animationSystem.Play((uid, animPlayer), (Animation)comp.DenyingAnimation, DoorComponent.AnimationKey);
|
||||||
break;
|
break;
|
||||||
case DoorState.Welded:
|
case DoorState.Welded:
|
||||||
break;
|
break;
|
||||||
case DoorState.Emagging:
|
case DoorState.Emagging:
|
||||||
if (animPlayer != null && comp.EmaggingAnimation != default)
|
if (animPlayer != null)
|
||||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.EmaggingAnimation, DoorComponent.AnimationKey);
|
_animationSystem.Play((uid, animPlayer), (Animation)comp.EmaggingAnimation, DoorComponent.AnimationKey);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException($"Invalid door visual state {state}");
|
throw new ArgumentOutOfRangeException($"Invalid door visual state {state}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO AUDIO PREDICT see comments in server-side PlaySound()
|
|
||||||
protected override void PlaySound(EntityUid uid, SoundSpecifier soundSpecifier, AudioParams audioParams, EntityUid? predictingPlayer, bool predicted)
|
|
||||||
{
|
|
||||||
if (GameTiming.InPrediction && GameTiming.IsFirstTimePredicted)
|
|
||||||
Audio.PlayEntity(soundSpecifier, Filter.Local(), uid, false, audioParams);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Content.Server.Administration.Systems;
|
|||||||
|
|
||||||
public sealed partial class AdminVerbSystem
|
public sealed partial class AdminVerbSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly DoorBoltSystem _boltsSystem = default!;
|
[Dependency] private readonly DoorSystem _door = default!;
|
||||||
[Dependency] private readonly AirlockSystem _airlockSystem = default!;
|
[Dependency] private readonly AirlockSystem _airlockSystem = default!;
|
||||||
[Dependency] private readonly StackSystem _stackSystem = default!;
|
[Dependency] private readonly StackSystem _stackSystem = default!;
|
||||||
[Dependency] private readonly SharedAccessSystem _accessSystem = default!;
|
[Dependency] private readonly SharedAccessSystem _accessSystem = default!;
|
||||||
@@ -78,7 +78,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
: new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/bolt.png")),
|
: new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/bolt.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
_boltsSystem.SetBoltsWithAudio(args.Target, bolts, !bolts.BoltsDown);
|
_door.SetBoltsDown((args.Target, bolts), !bolts.BoltsDown);
|
||||||
},
|
},
|
||||||
Impact = LogImpact.Medium,
|
Impact = LogImpact.Medium,
|
||||||
Message = Loc.GetString(bolts.BoltsDown
|
Message = Loc.GetString(bolts.BoltsDown
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ namespace Content.Server.DeviceLinking.Systems
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class DoorSignalControlSystem : EntitySystem
|
public sealed class DoorSignalControlSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
|
||||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||||
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
|
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
|
||||||
|
|
||||||
@@ -79,7 +78,7 @@ namespace Content.Server.DeviceLinking.Systems
|
|||||||
bolt = state == SignalState.High;
|
bolt = state == SignalState.High;
|
||||||
}
|
}
|
||||||
|
|
||||||
_bolts.SetBoltsWithAudio(uid, bolts, bolt);
|
_doorSystem.SetBoltsDown((uid, bolts), bolt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
using Content.Server.DeviceLinking.Events;
|
using Content.Server.DeviceLinking.Events;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Server.Wires;
|
using Content.Server.Wires;
|
||||||
using Content.Shared.Doors;
|
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Doors.Systems;
|
using Content.Shared.Doors.Systems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Wires;
|
using Content.Shared.Wires;
|
||||||
using Content.Shared.Prying.Components;
|
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
namespace Content.Server.Doors.Systems;
|
namespace Content.Server.Doors.Systems;
|
||||||
@@ -15,8 +12,6 @@ namespace Content.Server.Doors.Systems;
|
|||||||
public sealed class AirlockSystem : SharedAirlockSystem
|
public sealed class AirlockSystem : SharedAirlockSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly WiresSystem _wiresSystem = default!;
|
[Dependency] private readonly WiresSystem _wiresSystem = default!;
|
||||||
[Dependency] private readonly PowerReceiverSystem _power = default!;
|
|
||||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -26,13 +21,7 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
SubscribeLocalEvent<AirlockComponent, SignalReceivedEvent>(OnSignalReceived);
|
SubscribeLocalEvent<AirlockComponent, SignalReceivedEvent>(OnSignalReceived);
|
||||||
|
|
||||||
SubscribeLocalEvent<AirlockComponent, PowerChangedEvent>(OnPowerChanged);
|
SubscribeLocalEvent<AirlockComponent, PowerChangedEvent>(OnPowerChanged);
|
||||||
SubscribeLocalEvent<AirlockComponent, DoorStateChangedEvent>(OnStateChanged);
|
|
||||||
SubscribeLocalEvent<AirlockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
|
||||||
SubscribeLocalEvent<AirlockComponent, BeforeDoorDeniedEvent>(OnBeforeDoorDenied);
|
|
||||||
SubscribeLocalEvent<AirlockComponent, ActivateInWorldEvent>(OnActivate, before: new[] { typeof(DoorSystem) });
|
SubscribeLocalEvent<AirlockComponent, ActivateInWorldEvent>(OnActivate, before: new[] { typeof(DoorSystem) });
|
||||||
SubscribeLocalEvent<AirlockComponent, GetPryTimeModifierEvent>(OnGetPryMod);
|
|
||||||
SubscribeLocalEvent<AirlockComponent, BeforePryEvent>(OnBeforePry);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAirlockInit(EntityUid uid, AirlockComponent component, ComponentInit args)
|
private void OnAirlockInit(EntityUid uid, AirlockComponent component, ComponentInit args)
|
||||||
@@ -53,6 +42,9 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
|
|
||||||
private void OnPowerChanged(EntityUid uid, AirlockComponent component, ref PowerChangedEvent args)
|
private void OnPowerChanged(EntityUid uid, AirlockComponent component, ref PowerChangedEvent args)
|
||||||
{
|
{
|
||||||
|
component.Powered = args.Powered;
|
||||||
|
Dirty(uid, component);
|
||||||
|
|
||||||
if (TryComp<AppearanceComponent>(uid, out var appearanceComponent))
|
if (TryComp<AppearanceComponent>(uid, out var appearanceComponent))
|
||||||
{
|
{
|
||||||
Appearance.SetData(uid, DoorVisuals.Powered, args.Powered, appearanceComponent);
|
Appearance.SetData(uid, DoorVisuals.Powered, args.Powered, appearanceComponent);
|
||||||
@@ -73,80 +65,6 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args)
|
|
||||||
{
|
|
||||||
// TODO move to shared? having this be server-side, but having client-side door opening/closing & prediction
|
|
||||||
// means that sometimes the panels & bolt lights may be visible despite a door being completely open.
|
|
||||||
|
|
||||||
// Only show the maintenance panel if the airlock is closed
|
|
||||||
if (TryComp<WiresPanelComponent>(uid, out var wiresPanel))
|
|
||||||
{
|
|
||||||
_wiresSystem.ChangePanelVisibility(uid, wiresPanel, component.OpenPanelVisible || args.State != DoorState.Open);
|
|
||||||
}
|
|
||||||
// If the door is closed, we should look if the bolt was locked while closing
|
|
||||||
UpdateAutoClose(uid, component);
|
|
||||||
|
|
||||||
// Make sure the airlock auto closes again next time it is opened
|
|
||||||
if (args.State == DoorState.Closed)
|
|
||||||
component.AutoClose = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the auto close timer.
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateAutoClose(EntityUid uid, AirlockComponent? airlock = null, DoorComponent? door = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref airlock, ref door))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (door.State != DoorState.Open)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!airlock.AutoClose)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!CanChangeState(uid, airlock))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var autoev = new BeforeDoorAutoCloseEvent();
|
|
||||||
RaiseLocalEvent(uid, autoev, false);
|
|
||||||
if (autoev.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DoorSystem.SetNextStateChange(uid, airlock.AutoCloseDelay * airlock.AutoCloseDelayModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args)
|
|
||||||
{
|
|
||||||
if (!CanChangeState(uid, component))
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnBeforeDoorClosed(EntityUid uid, AirlockComponent component, BeforeDoorClosedEvent args)
|
|
||||||
{
|
|
||||||
base.OnBeforeDoorClosed(uid, component, args);
|
|
||||||
|
|
||||||
if (args.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// only block based on bolts / power status when initially closing the door, not when its already
|
|
||||||
// mid-transition. Particularly relevant for when the door was pried-closed with a crowbar, which bypasses
|
|
||||||
// the initial power-check.
|
|
||||||
|
|
||||||
if (TryComp(uid, out DoorComponent? door)
|
|
||||||
&& !door.Partial
|
|
||||||
&& !CanChangeState(uid, component))
|
|
||||||
{
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args)
|
|
||||||
{
|
|
||||||
if (!CanChangeState(uid, component))
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args)
|
private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args)
|
||||||
{
|
{
|
||||||
if (TryComp<WiresPanelComponent>(uid, out var panel) &&
|
if (TryComp<WiresPanelComponent>(uid, out var panel) &&
|
||||||
@@ -168,32 +86,4 @@ public sealed class AirlockSystem : SharedAirlockSystem
|
|||||||
component.AutoClose = false;
|
component.AutoClose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetPryMod(EntityUid uid, AirlockComponent component, ref GetPryTimeModifierEvent args)
|
|
||||||
{
|
|
||||||
if (_power.IsPowered(uid))
|
|
||||||
args.PryTimeModifier *= component.PoweredPryModifier;
|
|
||||||
|
|
||||||
if (_bolts.IsBolted(uid))
|
|
||||||
args.PryTimeModifier *= component.BoltedPryModifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBeforePry(EntityUid uid, AirlockComponent component, ref BeforePryEvent args)
|
|
||||||
{
|
|
||||||
if (args.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this.IsPowered(uid, EntityManager) || args.PryPowered)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Message = "airlock-component-cannot-pry-is-powered-message";
|
|
||||||
|
|
||||||
args.Cancelled = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanChangeState(EntityUid uid, AirlockComponent component)
|
|
||||||
{
|
|
||||||
return this.IsPowered(uid, EntityManager) && !_bolts.IsBolted(uid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Shared.Doors;
|
|
||||||
using Content.Shared.Doors.Components;
|
|
||||||
using Content.Shared.Doors.Systems;
|
|
||||||
|
|
||||||
namespace Content.Server.Doors.Systems;
|
|
||||||
|
|
||||||
public sealed class DoorBoltSystem : SharedDoorBoltSystem
|
|
||||||
{
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<DoorBoltComponent, PowerChangedEvent>(OnPowerChanged);
|
|
||||||
SubscribeLocalEvent<DoorBoltComponent, DoorStateChangedEvent>(OnStateChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPowerChanged(EntityUid uid, DoorBoltComponent component, ref PowerChangedEvent args)
|
|
||||||
{
|
|
||||||
if (args.Powered)
|
|
||||||
{
|
|
||||||
if (component.BoltWireCut)
|
|
||||||
SetBoltsWithAudio(uid, component, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateBoltLightStatus(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateBoltLightStatus(EntityUid uid, DoorBoltComponent component)
|
|
||||||
{
|
|
||||||
if (!TryComp<AppearanceComponent>(uid, out var appearance))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Appearance.SetData(uid, DoorVisuals.BoltLights, GetBoltLightsVisible(uid, component), appearance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetBoltLightsVisible(EntityUid uid, DoorBoltComponent component)
|
|
||||||
{
|
|
||||||
return component.BoltLightsEnabled &&
|
|
||||||
component.BoltsDown &&
|
|
||||||
this.IsPowered(uid, EntityManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBoltLightsEnabled(EntityUid uid, DoorBoltComponent component, bool value)
|
|
||||||
{
|
|
||||||
if (component.BoltLightsEnabled == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
component.BoltLightsEnabled = value;
|
|
||||||
UpdateBoltLightStatus(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBoltsDown(EntityUid uid, DoorBoltComponent component, bool value)
|
|
||||||
{
|
|
||||||
if (component.BoltsDown == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
component.BoltsDown = value;
|
|
||||||
UpdateBoltLightStatus(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStateChanged(EntityUid uid, DoorBoltComponent component, DoorStateChangedEvent args)
|
|
||||||
{
|
|
||||||
// If the door is closed, we should look if the bolt was locked while closing
|
|
||||||
UpdateBoltLightStatus(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBoltsWithAudio(EntityUid uid, DoorBoltComponent component, bool newBolts)
|
|
||||||
{
|
|
||||||
if (newBolts == component.BoltsDown)
|
|
||||||
return;
|
|
||||||
|
|
||||||
component.BoltsDown = newBolts;
|
|
||||||
Audio.PlayPvs(newBolts ? component.BoltDownSound : component.BoltUpSound, uid);
|
|
||||||
UpdateBoltLightStatus(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsBolted(EntityUid uid, DoorBoltComponent? component = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref component))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return component.BoltsDown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,49 +1,22 @@
|
|||||||
using Content.Server.Access;
|
using Content.Server.Access;
|
||||||
using Content.Server.Administration.Logs;
|
|
||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.Database;
|
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Doors.Systems;
|
using Content.Shared.Doors.Systems;
|
||||||
using Content.Shared.Emag.Systems;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Prying.Components;
|
|
||||||
using Content.Shared.Prying.Systems;
|
|
||||||
using Content.Shared.Tools.Systems;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Physics.Events;
|
|
||||||
|
|
||||||
namespace Content.Server.Doors.Systems;
|
namespace Content.Server.Doors.Systems;
|
||||||
|
|
||||||
public sealed class DoorSystem : SharedDoorSystem
|
public sealed class DoorSystem : SharedDoorSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
|
||||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
|
||||||
[Dependency] private readonly AirtightSystem _airtightSystem = default!;
|
[Dependency] private readonly AirtightSystem _airtightSystem = default!;
|
||||||
[Dependency] private readonly PryingSystem _pryingSystem = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<DoorComponent, WeldableAttemptEvent>(OnWeldAttempt);
|
SubscribeLocalEvent<DoorBoltComponent, PowerChangedEvent>(OnBoltPowerChanged);
|
||||||
SubscribeLocalEvent<DoorComponent, WeldableChangedEvent>(OnWeldChanged);
|
|
||||||
SubscribeLocalEvent<DoorComponent, GotEmaggedEvent>(OnEmagged);
|
|
||||||
SubscribeLocalEvent<DoorComponent, PriedEvent>(OnAfterPry);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnActivate(EntityUid uid, DoorComponent door, ActivateInWorldEvent args)
|
|
||||||
{
|
|
||||||
// TODO once access permissions are shared, move this back to shared.
|
|
||||||
if (args.Handled || !door.ClickOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryToggleDoor(uid, door, args.User))
|
|
||||||
_pryingSystem.TryPry(uid, args.User, out _);
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetCollidable(
|
protected override void SetCollidable(
|
||||||
@@ -65,144 +38,16 @@ public sealed class DoorSystem : SharedDoorSystem
|
|||||||
base.SetCollidable(uid, collidable, door, physics, occluder);
|
base.SetCollidable(uid, collidable, door, physics, occluder);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO AUDIO PREDICT Figure out a better way to handle sound and prediction. For now, this works well enough?
|
private void OnBoltPowerChanged(Entity<DoorBoltComponent> ent, ref PowerChangedEvent args)
|
||||||
//
|
|
||||||
// Currently a client will predict when a door is going to close automatically. So any client in PVS range can just
|
|
||||||
// play their audio locally. Playing it server-side causes an odd delay, while in shared it causes double-audio.
|
|
||||||
//
|
|
||||||
// But if we just do that, then if a door is closed prematurely as the result of an interaction (i.e., using "E" on
|
|
||||||
// an open door), then the audio would only be played for the client performing the interaction.
|
|
||||||
//
|
|
||||||
// So we do this:
|
|
||||||
// - Play audio client-side IF the closing is being predicted (auto-close or predicted interaction)
|
|
||||||
// - Server assumes automated closing is predicted by clients and does not play audio unless otherwise specified.
|
|
||||||
// - Major exception is player interactions, which other players cannot predict
|
|
||||||
// - In that case, send audio to all players, except possibly the interacting player if it was a predicted
|
|
||||||
// interaction.
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Selectively send sound to clients, taking care to not send the double-audio.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">The audio source</param>
|
|
||||||
/// <param name="soundSpecifier">The sound</param>
|
|
||||||
/// <param name="audioParams">The audio parameters.</param>
|
|
||||||
/// <param name="predictingPlayer">The user (if any) that instigated an interaction</param>
|
|
||||||
/// <param name="predicted">Whether this interaction would have been predicted. If the predicting player is null,
|
|
||||||
/// this assumes it would have been predicted by all players in PVS range.</param>
|
|
||||||
protected override void PlaySound(EntityUid uid, SoundSpecifier soundSpecifier, AudioParams audioParams, EntityUid? predictingPlayer, bool predicted)
|
|
||||||
{
|
{
|
||||||
// If this sound would have been predicted by all clients, do not play any audio.
|
if (args.Powered)
|
||||||
if (predicted && predictingPlayer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (predicted)
|
|
||||||
Audio.PlayPredicted(soundSpecifier, uid, predictingPlayer, audioParams);
|
|
||||||
else
|
|
||||||
Audio.PlayPvs(soundSpecifier, uid, audioParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region DoAfters
|
|
||||||
private void OnWeldAttempt(EntityUid uid, DoorComponent component, WeldableAttemptEvent args)
|
|
||||||
{
|
|
||||||
if (component.CurrentlyCrushing.Count > 0)
|
|
||||||
{
|
{
|
||||||
args.Cancel();
|
if (ent.Comp.BoltWireCut)
|
||||||
return;
|
SetBoltsDown(ent, true);
|
||||||
}
|
}
|
||||||
if (component.State != DoorState.Closed && component.State != DoorState.Welded)
|
|
||||||
{
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWeldChanged(EntityUid uid, DoorComponent component, ref WeldableChangedEvent args)
|
UpdateBoltLightStatus(ent);
|
||||||
{
|
ent.Comp.Powered = args.Powered;
|
||||||
if (component.State == DoorState.Closed)
|
Dirty(ent, ent.Comp);
|
||||||
SetState(uid, DoorState.Welded, component);
|
|
||||||
else if (component.State == DoorState.Welded)
|
|
||||||
SetState(uid, DoorState.Closed, component);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Open a door if a player or door-bumper (PDA, ID-card) collide with the door. Sadly, bullets no longer
|
|
||||||
/// generate "access denied" sounds as you fire at a door.
|
|
||||||
/// </summary>
|
|
||||||
protected override void HandleCollide(EntityUid uid, DoorComponent door, ref StartCollideEvent args)
|
|
||||||
{
|
|
||||||
// TODO ACCESS READER move access reader to shared and predict door opening/closing
|
|
||||||
// Then this can be moved to the shared system without mispredicting.
|
|
||||||
if (!door.BumpOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (door.State is not (DoorState.Closed or DoorState.Denying))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var otherUid = args.OtherEntity;
|
|
||||||
|
|
||||||
if (Tags.HasTag(otherUid, "DoorBumpOpener"))
|
|
||||||
TryOpen(uid, door, otherUid, quiet: door.State == DoorState.Denying);
|
|
||||||
}
|
|
||||||
private void OnEmagged(EntityUid uid, DoorComponent door, ref GotEmaggedEvent args)
|
|
||||||
{
|
|
||||||
if (TryComp<AirlockComponent>(uid, out var airlockComponent))
|
|
||||||
{
|
|
||||||
if (_bolts.IsBolted(uid) || !this.IsPowered(uid, EntityManager))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (door.State == DoorState.Closed)
|
|
||||||
{
|
|
||||||
SetState(uid, DoorState.Emagging, door);
|
|
||||||
PlaySound(uid, door.SparkSound, AudioParams.Default.WithVolume(8), args.UserUid, false);
|
|
||||||
args.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void StartOpening(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool predicted = false)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref door))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var lastState = door.State;
|
|
||||||
|
|
||||||
SetState(uid, DoorState.Opening, door);
|
|
||||||
|
|
||||||
if (door.OpenSound != null)
|
|
||||||
PlaySound(uid, door.OpenSound, AudioParams.Default.WithVolume(-5), user, predicted);
|
|
||||||
|
|
||||||
if (lastState == DoorState.Emagging && TryComp<DoorBoltComponent>(uid, out var doorBoltComponent))
|
|
||||||
_bolts.SetBoltsWithAudio(uid, doorBoltComponent, !doorBoltComponent.BoltsDown);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Open or close a door after it has been successfuly pried.
|
|
||||||
/// </summary>
|
|
||||||
private void OnAfterPry(EntityUid uid, DoorComponent door, ref PriedEvent args)
|
|
||||||
{
|
|
||||||
if (door.State == DoorState.Closed)
|
|
||||||
{
|
|
||||||
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User)} pried {ToPrettyString(uid)} open");
|
|
||||||
StartOpening(uid, door, args.User);
|
|
||||||
}
|
|
||||||
else if (door.State == DoorState.Open)
|
|
||||||
{
|
|
||||||
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User)} pried {ToPrettyString(uid)} closed");
|
|
||||||
StartClosing(uid, door, args.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void CheckDoorBump(Entity<DoorComponent, PhysicsComponent> ent)
|
|
||||||
{
|
|
||||||
var (uid, door, physics) = ent;
|
|
||||||
if (door.BumpOpen)
|
|
||||||
{
|
|
||||||
foreach (var other in PhysicsSystem.GetContactingEntities(uid, physics, approximate: true))
|
|
||||||
{
|
|
||||||
if (Tags.HasTag(other, "DoorBumpOpener") && TryOpen(uid, door, other, quiet: true))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,19 +18,18 @@ public sealed partial class DoorBoltLightWireAction : ComponentWireAction<DoorBo
|
|||||||
|
|
||||||
public override bool Cut(EntityUid user, Wire wire, DoorBoltComponent door)
|
public override bool Cut(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||||
{
|
{
|
||||||
EntityManager.System<DoorBoltSystem>().SetBoltLightsEnabled(wire.Owner, door, false);
|
EntityManager.System<DoorSystem>().SetBoltLightsEnabled((wire.Owner, door), false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Mend(EntityUid user, Wire wire, DoorBoltComponent door)
|
public override bool Mend(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||||
{
|
{
|
||||||
|
EntityManager.System<DoorSystem>().SetBoltLightsEnabled((wire.Owner, door), true);
|
||||||
EntityManager.System<DoorBoltSystem>().SetBoltLightsEnabled(wire.Owner, door, true);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Pulse(EntityUid user, Wire wire, DoorBoltComponent door)
|
public override void Pulse(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||||
{
|
{
|
||||||
EntityManager.System<DoorBoltSystem>().SetBoltLightsEnabled(wire.Owner, door, !door.BoltLightsEnabled);
|
EntityManager.System<DoorSystem>().SetBoltLightsEnabled((wire.Owner, door), !door.BoltLightsEnabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,24 +19,24 @@ public sealed partial class DoorBoltWireAction : ComponentWireAction<DoorBoltCom
|
|||||||
|
|
||||||
public override bool Cut(EntityUid user, Wire wire, DoorBoltComponent airlock)
|
public override bool Cut(EntityUid user, Wire wire, DoorBoltComponent airlock)
|
||||||
{
|
{
|
||||||
EntityManager.System<DoorBoltSystem>().SetBoltWireCut(airlock, true);
|
EntityManager.System<DoorSystem>().SetBoltWireCut((wire.Owner, airlock), true);
|
||||||
if (!airlock.BoltsDown && IsPowered(wire.Owner))
|
if (!airlock.BoltsDown && IsPowered(wire.Owner))
|
||||||
EntityManager.System<DoorBoltSystem>().SetBoltsWithAudio(wire.Owner, airlock, true);
|
EntityManager.System<DoorSystem>().SetBoltsDown((wire.Owner, airlock), true, user);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Mend(EntityUid user, Wire wire, DoorBoltComponent door)
|
public override bool Mend(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||||
{
|
{
|
||||||
EntityManager.System<DoorBoltSystem>().SetBoltWireCut(door, false);
|
EntityManager.System<DoorSystem>().SetBoltWireCut((wire.Owner, door), false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Pulse(EntityUid user, Wire wire, DoorBoltComponent door)
|
public override void Pulse(EntityUid user, Wire wire, DoorBoltComponent door)
|
||||||
{
|
{
|
||||||
if (IsPowered(wire.Owner))
|
if (IsPowered(wire.Owner))
|
||||||
EntityManager.System<DoorBoltSystem>().SetBoltsWithAudio(wire.Owner, door, !door.BoltsDown);
|
EntityManager.System<DoorSystem>().SetBoltsDown((wire.Owner, door), !door.BoltsDown);
|
||||||
else if (!door.BoltsDown)
|
else if (!door.BoltsDown)
|
||||||
EntityManager.System<DoorBoltSystem>().SetBoltsWithAudio(wire.Owner, door, true);
|
EntityManager.System<DoorSystem>().SetBoltsDown((wire.Owner, door), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ public sealed class MagicSystem : EntitySystem
|
|||||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly DoorBoltSystem _boltsSystem = default!;
|
|
||||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||||
@@ -307,7 +306,7 @@ public sealed class MagicSystem : EntitySystem
|
|||||||
foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range))
|
foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range))
|
||||||
{
|
{
|
||||||
if (TryComp<DoorBoltComponent>(entity, out var bolts))
|
if (TryComp<DoorBoltComponent>(entity, out var bolts))
|
||||||
_boltsSystem.SetBoltsDown(entity, bolts, false);
|
_doorSystem.SetBoltsDown((entity, bolts), false);
|
||||||
|
|
||||||
if (TryComp<DoorComponent>(entity, out var doorComp) && doorComp.State is not DoorState.Open)
|
if (TryComp<DoorComponent>(entity, out var doorComp) && doorComp.State is not DoorState.Open)
|
||||||
_doorSystem.StartOpening(entity);
|
_doorSystem.StartOpening(entity);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ namespace Content.Server.Remotes
|
|||||||
public sealed class DoorRemoteSystem : EntitySystem
|
public sealed class DoorRemoteSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
|
||||||
[Dependency] private readonly AirlockSystem _airlock = default!;
|
[Dependency] private readonly AirlockSystem _airlock = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||||
@@ -105,7 +104,7 @@ namespace Content.Server.Remotes
|
|||||||
{
|
{
|
||||||
if (!boltsComp.BoltWireCut)
|
if (!boltsComp.BoltWireCut)
|
||||||
{
|
{
|
||||||
_bolts.SetBoltsWithAudio(args.Target.Value, boltsComp, !boltsComp.BoltsDown);
|
_doorSystem.SetBoltsDown((args.Target.Value, boltsComp), !boltsComp.BoltsDown, args.User);
|
||||||
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to {(boltsComp.BoltsDown ? "" : "un")}bolt it");
|
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to {(boltsComp.BoltsDown ? "" : "un")}bolt it");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
public sealed partial class DockingSystem : EntitySystem
|
public sealed partial class DockingSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
|
||||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||||
[Dependency] private readonly FixtureSystem _fixtureSystem = default!;
|
[Dependency] private readonly FixtureSystem _fixtureSystem = default!;
|
||||||
[Dependency] private readonly PathfindingSystem _pathfinding = default!;
|
[Dependency] private readonly PathfindingSystem _pathfinding = default!;
|
||||||
@@ -363,7 +362,7 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
doorA.ChangeAirtight = false;
|
doorA.ChangeAirtight = false;
|
||||||
if (TryComp<DoorBoltComponent>(dockAUid, out var airlockA))
|
if (TryComp<DoorBoltComponent>(dockAUid, out var airlockA))
|
||||||
{
|
{
|
||||||
_bolts.SetBoltsWithAudio(dockAUid, airlockA, true);
|
_doorSystem.SetBoltsDown((dockAUid, airlockA), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -375,7 +374,7 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
doorB.ChangeAirtight = false;
|
doorB.ChangeAirtight = false;
|
||||||
if (TryComp<DoorBoltComponent>(dockBUid, out var airlockB))
|
if (TryComp<DoorBoltComponent>(dockBUid, out var airlockB))
|
||||||
{
|
{
|
||||||
_bolts.SetBoltsWithAudio(dockBUid, airlockB, true);
|
_doorSystem.SetBoltsDown((dockBUid, airlockB), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -470,7 +469,7 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (TryComp<DoorBoltComponent>(dockUid, out var airlock))
|
if (TryComp<DoorBoltComponent>(dockUid, out var airlock))
|
||||||
_bolts.SetBoltsWithAudio(dockUid, airlock, false);
|
_doorSystem.SetBoltsDown((dockUid, airlock), false);
|
||||||
|
|
||||||
if (TryComp(dockUid, out DoorComponent? door) && _doorSystem.TryClose(dockUid, door))
|
if (TryComp(dockUid, out DoorComponent? door) && _doorSystem.TryClose(dockUid, door))
|
||||||
door.ChangeAirtight = true;
|
door.ChangeAirtight = true;
|
||||||
|
|||||||
@@ -459,7 +459,7 @@ public sealed partial class ShuttleSystem
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
_doors.TryClose(doorUid);
|
_doors.TryClose(doorUid);
|
||||||
_bolts.SetBoltsWithAudio(doorUid, door, enabled);
|
_doors.SetBoltsDown((doorUid, door), enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
|||||||
[Dependency] private readonly BodySystem _bobby = default!;
|
[Dependency] private readonly BodySystem _bobby = default!;
|
||||||
[Dependency] private readonly DockingSystem _dockSystem = default!;
|
[Dependency] private readonly DockingSystem _dockSystem = default!;
|
||||||
[Dependency] private readonly DoorSystem _doors = default!;
|
[Dependency] private readonly DoorSystem _doors = default!;
|
||||||
[Dependency] private readonly DoorBoltSystem _bolts = default!;
|
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||||
|
|||||||
@@ -620,13 +620,6 @@ public sealed class WiresSystem : SharedWiresSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangePanelVisibility(EntityUid uid, WiresPanelComponent component, bool visible)
|
|
||||||
{
|
|
||||||
component.Visible = visible;
|
|
||||||
UpdateAppearance(uid, component);
|
|
||||||
Dirty(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetWiresPanelSecurity(EntityUid uid, WiresPanelSecurityComponent component, WiresPanelSecurityEvent args)
|
public void SetWiresPanelSecurity(EntityUid uid, WiresPanelSecurityComponent component, WiresPanelSecurityEvent args)
|
||||||
{
|
{
|
||||||
component.Examine = args.Examine;
|
component.Examine = args.Examine;
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ namespace Content.Shared.Doors.Components;
|
|||||||
[Access(typeof(SharedAirlockSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)]
|
[Access(typeof(SharedAirlockSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)]
|
||||||
public sealed partial class AirlockComponent : Component
|
public sealed partial class AirlockComponent : Component
|
||||||
{
|
{
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Powered;
|
||||||
|
|
||||||
// Need to network airlock safety state to avoid mis-predicts when a door auto-closes as the client walks through the door.
|
// Need to network airlock safety state to avoid mis-predicts when a door auto-closes as the client walks through the door.
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
|
|||||||
@@ -8,36 +8,43 @@ namespace Content.Shared.Doors.Components;
|
|||||||
/// Companion component to DoorComponent that handles bolt-specific behavior.
|
/// Companion component to DoorComponent that handles bolt-specific behavior.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
[Access(typeof(SharedDoorBoltSystem))]
|
[Access(typeof(SharedDoorSystem))]
|
||||||
|
[AutoGenerateComponentState]
|
||||||
public sealed partial class DoorBoltComponent : Component
|
public sealed partial class DoorBoltComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound to play when the bolts on the airlock go up.
|
/// Sound to play when the bolts on the airlock go up.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("boltUpSound"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
public SoundSpecifier BoltUpSound = new SoundPathSpecifier("/Audio/Machines/boltsup.ogg");
|
public SoundSpecifier BoltUpSound = new SoundPathSpecifier("/Audio/Machines/boltsup.ogg");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound to play when the bolts on the airlock go down.
|
/// Sound to play when the bolts on the airlock go down.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("boltDownSound"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
public SoundSpecifier BoltDownSound = new SoundPathSpecifier("/Audio/Machines/boltsdown.ogg");
|
public SoundSpecifier BoltDownSound = new SoundPathSpecifier("/Audio/Machines/boltsdown.ogg");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the door bolts are currently deployed.
|
/// Whether the door bolts are currently deployed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
public bool BoltsDown;
|
public bool BoltsDown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the bolt lights are currently enabled.
|
/// Whether the bolt lights are currently enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
public bool BoltLightsEnabled = true;
|
public bool BoltLightsEnabled = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the bolt wire is cut, which will force the airlock to always be bolted as long as it has power.
|
/// True if the bolt wire is cut, which will force the airlock to always be bolted as long as it has power.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
public bool BoltWireCut;
|
public bool BoltWireCut;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for prediction. true if the door has power.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Powered;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,14 +124,14 @@ public sealed partial class DoorComponent : Component
|
|||||||
/// If false, this door is incapable of crushing entities. This just determines whether it will apply damage and
|
/// If false, this door is incapable of crushing entities. This just determines whether it will apply damage and
|
||||||
/// stun, not whether it can close despite entities being in the way.
|
/// stun, not whether it can close despite entities being in the way.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public bool CanCrush = true;
|
public bool CanCrush = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to check for colliding entities before closing. This may be overridden by other system by subscribing to
|
/// Whether to check for colliding entities before closing. This may be overridden by other system by subscribing to
|
||||||
/// <see cref="BeforeDoorClosedEvent"/>. For example, hacked airlocks will set this to false.
|
/// <see cref="BeforeDoorClosedEvent"/>. For example, hacked airlocks will set this to false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public bool PerformCollisionCheck = true;
|
public bool PerformCollisionCheck = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -190,7 +190,7 @@ public sealed partial class DoorComponent : Component
|
|||||||
/// The sprite state used for the door when it's being emagged.
|
/// The sprite state used for the door when it's being emagged.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public string EmaggingSpriteState = "emagging";
|
public string EmaggingSpriteState = "sparks";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sprite state used for the door when it's open.
|
/// The sprite state used for the door when it's open.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Prying.Components;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
namespace Content.Shared.Doors.Systems;
|
namespace Content.Shared.Doors.Systems;
|
||||||
|
|
||||||
@@ -8,18 +10,112 @@ public abstract class SharedAirlockSystem : EntitySystem
|
|||||||
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
||||||
[Dependency] protected readonly SharedDoorSystem DoorSystem = default!;
|
[Dependency] protected readonly SharedDoorSystem DoorSystem = default!;
|
||||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||||
|
[Dependency] private readonly SharedWiresSystem _wiresSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<AirlockComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
|
SubscribeLocalEvent<AirlockComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
|
||||||
|
SubscribeLocalEvent<AirlockComponent, DoorStateChangedEvent>(OnStateChanged);
|
||||||
|
SubscribeLocalEvent<AirlockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
||||||
|
SubscribeLocalEvent<AirlockComponent, BeforeDoorDeniedEvent>(OnBeforeDoorDenied);
|
||||||
|
SubscribeLocalEvent<AirlockComponent, GetPryTimeModifierEvent>(OnGetPryMod);
|
||||||
|
SubscribeLocalEvent<AirlockComponent, BeforePryEvent>(OnBeforePry);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnBeforeDoorClosed(EntityUid uid, AirlockComponent airlock, BeforeDoorClosedEvent args)
|
private void OnBeforeDoorClosed(EntityUid uid, AirlockComponent airlock, BeforeDoorClosedEvent args)
|
||||||
{
|
{
|
||||||
|
if (args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!airlock.Safety)
|
if (!airlock.Safety)
|
||||||
args.PerformCollisionCheck = false;
|
args.PerformCollisionCheck = false;
|
||||||
|
|
||||||
|
// only block based on bolts / power status when initially closing the door, not when its already
|
||||||
|
// mid-transition. Particularly relevant for when the door was pried-closed with a crowbar, which bypasses
|
||||||
|
// the initial power-check.
|
||||||
|
|
||||||
|
if (TryComp(uid, out DoorComponent? door)
|
||||||
|
&& !door.Partial
|
||||||
|
&& !CanChangeState(uid, airlock))
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args)
|
||||||
|
{
|
||||||
|
// Only show the maintenance panel if the airlock is closed
|
||||||
|
if (TryComp<WiresPanelComponent>(uid, out var wiresPanel))
|
||||||
|
{
|
||||||
|
_wiresSystem.ChangePanelVisibility(uid, wiresPanel, component.OpenPanelVisible || args.State != DoorState.Open);
|
||||||
|
}
|
||||||
|
// If the door is closed, we should look if the bolt was locked while closing
|
||||||
|
UpdateAutoClose(uid, component);
|
||||||
|
|
||||||
|
// Make sure the airlock auto closes again next time it is opened
|
||||||
|
if (args.State == DoorState.Closed)
|
||||||
|
component.AutoClose = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args)
|
||||||
|
{
|
||||||
|
if (!CanChangeState(uid, component))
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args)
|
||||||
|
{
|
||||||
|
if (!CanChangeState(uid, component))
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetPryMod(EntityUid uid, AirlockComponent component, ref GetPryTimeModifierEvent args)
|
||||||
|
{
|
||||||
|
if (component.Powered)
|
||||||
|
args.PryTimeModifier *= component.PoweredPryModifier;
|
||||||
|
|
||||||
|
if (DoorSystem.IsBolted(uid))
|
||||||
|
args.PryTimeModifier *= component.BoltedPryModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the auto close timer.
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateAutoClose(EntityUid uid, AirlockComponent? airlock = null, DoorComponent? door = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref airlock, ref door))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (door.State != DoorState.Open)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!airlock.AutoClose)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!CanChangeState(uid, airlock))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var autoev = new BeforeDoorAutoCloseEvent();
|
||||||
|
RaiseLocalEvent(uid, autoev);
|
||||||
|
if (autoev.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DoorSystem.SetNextStateChange(uid, airlock.AutoCloseDelay * airlock.AutoCloseDelayModifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeforePry(EntityUid uid, AirlockComponent component, ref BeforePryEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!component.Powered || args.PryPowered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Message = "airlock-component-cannot-pry-is-powered-message";
|
||||||
|
|
||||||
|
args.Cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component)
|
public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component)
|
||||||
@@ -45,4 +141,9 @@ public abstract class SharedAirlockSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
component.Safety = value;
|
component.Safety = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool CanChangeState(EntityUid uid, AirlockComponent component)
|
||||||
|
{
|
||||||
|
return component.Powered && !DoorSystem.IsBolted(uid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
using Content.Shared.Doors.Components;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Shared.Prying.Components;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Audio.Systems;
|
|
||||||
|
|
||||||
namespace Content.Shared.Doors.Systems;
|
|
||||||
|
|
||||||
public abstract class SharedDoorBoltSystem : EntitySystem
|
|
||||||
{
|
|
||||||
|
|
||||||
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
|
||||||
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
|
||||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<DoorBoltComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
|
||||||
SubscribeLocalEvent<DoorBoltComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
|
|
||||||
SubscribeLocalEvent<DoorBoltComponent, BeforeDoorDeniedEvent>(OnBeforeDoorDenied);
|
|
||||||
SubscribeLocalEvent<DoorBoltComponent, BeforePryEvent>(OnDoorPry);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDoorPry(EntityUid uid, DoorBoltComponent component, ref BeforePryEvent args)
|
|
||||||
{
|
|
||||||
if (args.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!component.BoltsDown || args.Force)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Message = "airlock-component-cannot-pry-is-bolted-message";
|
|
||||||
|
|
||||||
args.Cancelled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBeforeDoorOpened(EntityUid uid, DoorBoltComponent component, BeforeDoorOpenedEvent args)
|
|
||||||
{
|
|
||||||
if (component.BoltsDown)
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBeforeDoorClosed(EntityUid uid, DoorBoltComponent component, BeforeDoorClosedEvent args)
|
|
||||||
{
|
|
||||||
if (component.BoltsDown)
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBeforeDoorDenied(EntityUid uid, DoorBoltComponent component, BeforeDoorDeniedEvent args)
|
|
||||||
{
|
|
||||||
if (component.BoltsDown)
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBoltWireCut(DoorBoltComponent component, bool value)
|
|
||||||
{
|
|
||||||
component.BoltWireCut = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
109
Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs
Normal file
109
Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
using Content.Shared.Doors.Components;
|
||||||
|
using Content.Shared.Prying.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.Doors.Systems;
|
||||||
|
|
||||||
|
public abstract partial class SharedDoorSystem
|
||||||
|
{
|
||||||
|
public void InitializeBolts()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<DoorBoltComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
||||||
|
SubscribeLocalEvent<DoorBoltComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
|
||||||
|
SubscribeLocalEvent<DoorBoltComponent, BeforeDoorDeniedEvent>(OnBeforeDoorDenied);
|
||||||
|
SubscribeLocalEvent<DoorBoltComponent, BeforePryEvent>(OnDoorPry);
|
||||||
|
SubscribeLocalEvent<DoorBoltComponent, DoorStateChangedEvent>(OnStateChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDoorPry(EntityUid uid, DoorBoltComponent component, ref BeforePryEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!component.BoltsDown || args.Force)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Message = "airlock-component-cannot-pry-is-bolted-message";
|
||||||
|
|
||||||
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeforeDoorOpened(EntityUid uid, DoorBoltComponent component, BeforeDoorOpenedEvent args)
|
||||||
|
{
|
||||||
|
if (component.BoltsDown)
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeforeDoorClosed(EntityUid uid, DoorBoltComponent component, BeforeDoorClosedEvent args)
|
||||||
|
{
|
||||||
|
if (component.BoltsDown)
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeforeDoorDenied(EntityUid uid, DoorBoltComponent component, BeforeDoorDeniedEvent args)
|
||||||
|
{
|
||||||
|
if (component.BoltsDown)
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBoltWireCut(Entity<DoorBoltComponent> ent, bool value)
|
||||||
|
{
|
||||||
|
ent.Comp.BoltWireCut = value;
|
||||||
|
Dirty(ent, ent.Comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateBoltLightStatus(Entity<DoorBoltComponent> ent)
|
||||||
|
{
|
||||||
|
AppearanceSystem.SetData(ent, DoorVisuals.BoltLights, GetBoltLightsVisible(ent));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetBoltLightsVisible(Entity<DoorBoltComponent> ent)
|
||||||
|
{
|
||||||
|
return ent.Comp.BoltLightsEnabled &&
|
||||||
|
ent.Comp.BoltsDown &&
|
||||||
|
ent.Comp.Powered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBoltLightsEnabled(Entity<DoorBoltComponent> ent, bool value)
|
||||||
|
{
|
||||||
|
if (ent.Comp.BoltLightsEnabled == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.BoltLightsEnabled = value;
|
||||||
|
Dirty(ent, ent.Comp);
|
||||||
|
UpdateBoltLightStatus(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBoltsDown(Entity<DoorBoltComponent> ent, bool value, EntityUid? user = null, bool predicted = false)
|
||||||
|
{
|
||||||
|
if (ent.Comp.BoltsDown == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.BoltsDown = value;
|
||||||
|
Dirty(ent, ent.Comp);
|
||||||
|
UpdateBoltLightStatus(ent);
|
||||||
|
|
||||||
|
var sound = value ? ent.Comp.BoltDownSound : ent.Comp.BoltUpSound;
|
||||||
|
if (predicted)
|
||||||
|
Audio.PlayPredicted(sound, ent, user: user);
|
||||||
|
else
|
||||||
|
Audio.PlayPvs(sound, ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStateChanged(Entity<DoorBoltComponent> entity, ref DoorStateChangedEvent args)
|
||||||
|
{
|
||||||
|
// If the door is closed, we should look if the bolt was locked while closing
|
||||||
|
UpdateBoltLightStatus(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsBolted(EntityUid uid, DoorBoltComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return component.BoltsDown;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,35 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
|
using Content.Shared.Emag.Systems;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Prying.Components;
|
using Content.Shared.Prying.Components;
|
||||||
|
using Content.Shared.Prying.Systems;
|
||||||
using Content.Shared.Stunnable;
|
using Content.Shared.Stunnable;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
|
using Content.Shared.Tools.Systems;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
using Robust.Shared.Physics.Systems;
|
using Robust.Shared.Physics.Systems;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
|
||||||
namespace Content.Shared.Doors.Systems;
|
namespace Content.Shared.Doors.Systems;
|
||||||
|
|
||||||
public abstract class SharedDoorSystem : EntitySystem
|
public abstract partial class SharedDoorSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly ISharedAdminLogManager _adminLog = default!;
|
||||||
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
||||||
|
[Dependency] private readonly INetManager _net = default!;
|
||||||
[Dependency] protected readonly SharedPhysicsSystem PhysicsSystem = default!;
|
[Dependency] protected readonly SharedPhysicsSystem PhysicsSystem = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||||
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||||
@@ -30,6 +39,11 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
[Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
|
[Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
|
||||||
[Dependency] private readonly OccluderSystem _occluder = default!;
|
[Dependency] private readonly OccluderSystem _occluder = default!;
|
||||||
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
|
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
|
||||||
|
[Dependency] private readonly PryingSystem _pryingSystem = default!;
|
||||||
|
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||||
|
|
||||||
|
[ValidatePrototypeId<TagPrototype>]
|
||||||
|
public const string DoorBumpTag = "DoorBumpOpener";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A body must have an intersection percentage larger than this in order to be considered as colliding with a
|
/// A body must have an intersection percentage larger than this in order to be considered as colliding with a
|
||||||
@@ -50,6 +64,8 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
InitializeBolts();
|
||||||
|
|
||||||
SubscribeLocalEvent<DoorComponent, ComponentInit>(OnComponentInit);
|
SubscribeLocalEvent<DoorComponent, ComponentInit>(OnComponentInit);
|
||||||
SubscribeLocalEvent<DoorComponent, ComponentRemove>(OnRemove);
|
SubscribeLocalEvent<DoorComponent, ComponentRemove>(OnRemove);
|
||||||
|
|
||||||
@@ -60,8 +76,12 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<DoorComponent, StartCollideEvent>(HandleCollide);
|
SubscribeLocalEvent<DoorComponent, StartCollideEvent>(HandleCollide);
|
||||||
SubscribeLocalEvent<DoorComponent, PreventCollideEvent>(PreventCollision);
|
SubscribeLocalEvent<DoorComponent, PreventCollideEvent>(PreventCollision);
|
||||||
SubscribeLocalEvent<DoorComponent, BeforePryEvent>(OnBeforePry);
|
SubscribeLocalEvent<DoorComponent, BeforePryEvent>(OnBeforePry);
|
||||||
|
SubscribeLocalEvent<DoorComponent, PriedEvent>(OnAfterPry);
|
||||||
|
SubscribeLocalEvent<DoorComponent, WeldableAttemptEvent>(OnWeldAttempt);
|
||||||
|
SubscribeLocalEvent<DoorComponent, WeldableChangedEvent>(OnWeldChanged);
|
||||||
SubscribeLocalEvent<DoorComponent, GetPryTimeModifierEvent>(OnPryTimeModifier);
|
SubscribeLocalEvent<DoorComponent, GetPryTimeModifierEvent>(OnPryTimeModifier);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<DoorComponent, GotEmaggedEvent>(OnEmagged);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnComponentInit(Entity<DoorComponent> ent, ref ComponentInit args)
|
protected virtual void OnComponentInit(Entity<DoorComponent> ent, ref ComponentInit args)
|
||||||
@@ -100,6 +120,23 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
_activeDoors.Remove(door);
|
_activeDoors.Remove(door);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEmagged(EntityUid uid, DoorComponent door, ref GotEmaggedEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<AirlockComponent>(uid, out var airlock))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (IsBolted(uid) || !airlock.Powered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (door.State == DoorState.Closed)
|
||||||
|
{
|
||||||
|
if (!SetState(uid, DoorState.Emagging, door))
|
||||||
|
return;
|
||||||
|
Audio.PlayPredicted(door.SparkSound, uid, args.UserUid, AudioParams.Default.WithVolume(8));
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region StateManagement
|
#region StateManagement
|
||||||
private void OnHandleState(Entity<DoorComponent> ent, ref AfterAutoHandleStateEvent args)
|
private void OnHandleState(Entity<DoorComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||||
{
|
{
|
||||||
@@ -113,14 +150,14 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
AppearanceSystem.SetData(ent, DoorVisuals.State, door.State);
|
AppearanceSystem.SetData(ent, DoorVisuals.State, door.State);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetState(EntityUid uid, DoorState state, DoorComponent? door = null)
|
protected bool SetState(EntityUid uid, DoorState state, DoorComponent? door = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref door))
|
if (!Resolve(uid, ref door))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// If no change, return to avoid firing a new DoorStateChangedEvent.
|
// If no change, return to avoid firing a new DoorStateChangedEvent.
|
||||||
if (state == door.State)
|
if (state == door.State)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
@@ -159,14 +196,20 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
Dirty(uid, door);
|
Dirty(uid, door);
|
||||||
RaiseLocalEvent(uid, new DoorStateChangedEvent(state));
|
RaiseLocalEvent(uid, new DoorStateChangedEvent(state));
|
||||||
AppearanceSystem.SetData(uid, DoorVisuals.State, door.State);
|
AppearanceSystem.SetData(uid, DoorVisuals.State, door.State);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Interactions
|
#region Interactions
|
||||||
protected virtual void OnActivate(EntityUid uid, DoorComponent door, ActivateInWorldEvent args)
|
protected void OnActivate(EntityUid uid, DoorComponent door, ActivateInWorldEvent args)
|
||||||
{
|
{
|
||||||
// avoid client-mispredicts, as the server will definitely handle this event
|
if (args.Handled || !door.ClickOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryToggleDoor(uid, door, args.User, predicted: true))
|
||||||
|
_pryingSystem.TryPry(uid, args.User, out _);
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +224,44 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
args.Cancelled = true;
|
args.Cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Open or close a door after it has been successfully pried.
|
||||||
|
/// </summary>
|
||||||
|
private void OnAfterPry(EntityUid uid, DoorComponent door, ref PriedEvent args)
|
||||||
|
{
|
||||||
|
if (door.State == DoorState.Closed)
|
||||||
|
{
|
||||||
|
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User)} pried {ToPrettyString(uid)} open");
|
||||||
|
StartOpening(uid, door, args.User, true);
|
||||||
|
}
|
||||||
|
else if (door.State == DoorState.Open)
|
||||||
|
{
|
||||||
|
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User)} pried {ToPrettyString(uid)} closed");
|
||||||
|
StartClosing(uid, door, args.User, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWeldAttempt(EntityUid uid, DoorComponent component, WeldableAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (component.CurrentlyCrushing.Count > 0)
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (component.State != DoorState.Closed && component.State != DoorState.Welded)
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWeldChanged(EntityUid uid, DoorComponent component, ref WeldableChangedEvent args)
|
||||||
|
{
|
||||||
|
if (component.State == DoorState.Closed)
|
||||||
|
SetState(uid, DoorState.Welded, component);
|
||||||
|
else if (component.State == DoorState.Welded)
|
||||||
|
SetState(uid, DoorState.Closed, component);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the door state/visuals and play an access denied sound when a user without access interacts with the
|
/// Update the door state/visuals and play an access denied sound when a user without access interacts with the
|
||||||
/// door.
|
/// door.
|
||||||
@@ -199,13 +280,15 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
if (ev.Cancelled)
|
if (ev.Cancelled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetState(uid, DoorState.Denying, door);
|
if (!SetState(uid, DoorState.Denying, door))
|
||||||
|
return;
|
||||||
|
|
||||||
if (door.DenySound != null)
|
if (predicted)
|
||||||
PlaySound(uid, door.DenySound, AudioParams.Default.WithVolume(-3), user, predicted);
|
Audio.PlayPredicted(door.DenySound, uid, user, AudioParams.Default.WithVolume(-3));
|
||||||
|
else if (_net.IsServer)
|
||||||
|
Audio.PlayPvs(door.DenySound, uid, AudioParams.Default.WithVolume(-3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool TryToggleDoor(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool predicted = false)
|
public bool TryToggleDoor(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool predicted = false)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref door))
|
if (!Resolve(uid, ref door))
|
||||||
@@ -215,7 +298,8 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
return TryOpen(uid, door, user, predicted, quiet: door.State == DoorState.Denying);
|
return TryOpen(uid, door, user, predicted, quiet: door.State == DoorState.Denying);
|
||||||
}
|
}
|
||||||
else if (door.State == DoorState.Open)
|
|
||||||
|
if (door.State == DoorState.Open)
|
||||||
{
|
{
|
||||||
return TryClose(uid, door, user, predicted);
|
return TryClose(uid, door, user, predicted);
|
||||||
}
|
}
|
||||||
@@ -254,7 +338,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
if (!HasAccess(uid, user, door))
|
if (!HasAccess(uid, user, door))
|
||||||
{
|
{
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
Deny(uid, door);
|
Deny(uid, door, user, predicted: true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,22 +353,29 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
/// <param name="user"> The user (if any) opening the door</param>
|
/// <param name="user"> The user (if any) opening the door</param>
|
||||||
/// <param name="predicted">Whether the interaction would have been
|
/// <param name="predicted">Whether the interaction would have been
|
||||||
/// predicted. See comments in the PlaySound method on the Server system for details</param>
|
/// predicted. See comments in the PlaySound method on the Server system for details</param>
|
||||||
public virtual void StartOpening(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool predicted = false)
|
public void StartOpening(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool predicted = false)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref door))
|
if (!Resolve(uid, ref door))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetState(uid, DoorState.Opening, door);
|
var lastState = door.State;
|
||||||
|
|
||||||
if (door.OpenSound != null)
|
if (!SetState(uid, DoorState.Opening, door))
|
||||||
PlaySound(uid, door.OpenSound, AudioParams.Default.WithVolume(-5), user, predicted);
|
return;
|
||||||
|
|
||||||
|
if (predicted)
|
||||||
|
Audio.PlayPredicted(door.OpenSound, uid, user, AudioParams.Default.WithVolume(-5));
|
||||||
|
else if (_net.IsServer)
|
||||||
|
Audio.PlayPvs(door.OpenSound, uid, AudioParams.Default.WithVolume(-5));
|
||||||
|
|
||||||
|
if (lastState == DoorState.Emagging && TryComp<DoorBoltComponent>(uid, out var doorBoltComponent))
|
||||||
|
SetBoltsDown((uid, doorBoltComponent), !doorBoltComponent.BoltsDown, user, true);
|
||||||
|
|
||||||
// I'm not sure what the intent here is/was? It plays a sound if the user is opening a door with a hands
|
// I'm not sure what the intent here is/was? It plays a sound if the user is opening a door with a hands
|
||||||
// component, but no actual hands!? What!? Is this the sound of them head-butting the door to get it to open??
|
// component, but no actual hands!? What!? Is this the sound of them head-butting the door to get it to open??
|
||||||
// I'm 99% sure something is wrong here, but I kind of want to keep it this way.
|
// I'm 99% sure something is wrong here, but I kind of want to keep it this way.
|
||||||
|
if (user != null && (!TryComp(user.Value, out HandsComponent? hands) || hands.Hands.Count == 0))
|
||||||
if (user != null && TryComp(user.Value, out HandsComponent? hands) && hands.Hands.Count == 0)
|
Audio.PlayPredicted(door.TryOpenDoorSound, uid, user, AudioParams.Default.WithVolume(-2));
|
||||||
PlaySound(uid, door.TryOpenDoorSound, AudioParams.Default.WithVolume(-2), user, predicted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -310,7 +401,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref door))
|
if (!Resolve(uid, ref door))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CanClose(uid, door, user, false))
|
if (!CanClose(uid, door, user))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
StartClosing(uid, door, user, predicted);
|
StartClosing(uid, door, user, predicted);
|
||||||
@@ -323,9 +414,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
/// <param name="uid"> The uid of the door</param>
|
/// <param name="uid"> The uid of the door</param>
|
||||||
/// <param name="door"> The doorcomponent of the door</param>
|
/// <param name="door"> The doorcomponent of the door</param>
|
||||||
/// <param name="user"> The user (if any) opening the door</param>
|
/// <param name="user"> The user (if any) opening the door</param>
|
||||||
/// <param name="predicted">Whether the interaction would have been
|
public bool CanClose(EntityUid uid, DoorComponent? door = null, EntityUid? user = null)
|
||||||
/// predicted. See comments in the PlaySound method on the Server system for details</param>
|
|
||||||
public bool CanClose(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool quiet = true)
|
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref door))
|
if (!Resolve(uid, ref door))
|
||||||
return false;
|
return false;
|
||||||
@@ -336,7 +425,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var ev = new BeforeDoorClosedEvent(door.PerformCollisionCheck);
|
var ev = new BeforeDoorClosedEvent(door.PerformCollisionCheck);
|
||||||
RaiseLocalEvent(uid, ev, false);
|
RaiseLocalEvent(uid, ev);
|
||||||
if (ev.Cancelled)
|
if (ev.Cancelled)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -346,15 +435,18 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
return !ev.PerformCollisionCheck || !GetColliding(uid).Any();
|
return !ev.PerformCollisionCheck || !GetColliding(uid).Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void StartClosing(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool predicted = false)
|
public void StartClosing(EntityUid uid, DoorComponent? door = null, EntityUid? user = null, bool predicted = false)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref door))
|
if (!Resolve(uid, ref door))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetState(uid, DoorState.Closing, door);
|
if (!SetState(uid, DoorState.Closing, door))
|
||||||
|
return;
|
||||||
|
|
||||||
if (door.CloseSound != null)
|
if (predicted)
|
||||||
PlaySound(uid, door.CloseSound, AudioParams.Default.WithVolume(-5), user, predicted);
|
Audio.PlayPredicted(door.CloseSound, uid, user, AudioParams.Default.WithVolume(-5));
|
||||||
|
else if (_net.IsServer)
|
||||||
|
Audio.PlayPvs(door.CloseSound, uid, AudioParams.Default.WithVolume(-5));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -367,7 +459,6 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
door.Partial = true;
|
door.Partial = true;
|
||||||
Dirty(uid, door);
|
|
||||||
|
|
||||||
// Make sure no entity waled into the airlock when it started closing.
|
// Make sure no entity waled into the airlock when it started closing.
|
||||||
if (!CanClose(uid, door))
|
if (!CanClose(uid, door))
|
||||||
@@ -380,6 +471,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
|
|
||||||
SetCollidable(uid, true, door, physics);
|
SetCollidable(uid, true, door, physics);
|
||||||
door.NextStateChange = GameTiming.CurTime + door.CloseTimeTwo;
|
door.NextStateChange = GameTiming.CurTime + door.CloseTimeTwo;
|
||||||
|
Dirty(uid, door);
|
||||||
_activeDoors.Add((uid, door));
|
_activeDoors.Add((uid, door));
|
||||||
|
|
||||||
// Crush any entities. Note that we don't check airlock safety here. This should have been checked before
|
// Crush any entities. Note that we don't check airlock safety here. This should have been checked before
|
||||||
@@ -489,10 +581,22 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void HandleCollide(EntityUid uid, DoorComponent door, ref StartCollideEvent args)
|
/// <summary>
|
||||||
|
/// Open a door if a player or door-bumper (PDA, ID-card) collide with the door. Sadly, bullets no longer
|
||||||
|
/// generate "access denied" sounds as you fire at a door.
|
||||||
|
/// </summary>
|
||||||
|
private void HandleCollide(EntityUid uid, DoorComponent door, ref StartCollideEvent args)
|
||||||
{
|
{
|
||||||
// TODO ACCESS READER move access reader to shared and predict door opening/closing
|
if (!door.BumpOpen)
|
||||||
// Then this can be moved to the shared system without mispredicting.
|
return;
|
||||||
|
|
||||||
|
if (door.State is not (DoorState.Closed or DoorState.Denying))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var otherUid = args.OtherEntity;
|
||||||
|
|
||||||
|
if (Tags.HasTag(otherUid, DoorBumpTag))
|
||||||
|
TryOpen(uid, door, otherUid, quiet: door.State == DoorState.Denying);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -587,6 +691,19 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
_activeDoors.Add((uid, door));
|
_activeDoors.Add((uid, door));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void CheckDoorBump(Entity<DoorComponent, PhysicsComponent> ent)
|
||||||
|
{
|
||||||
|
var (uid, door, physics) = ent;
|
||||||
|
if (door.BumpOpen)
|
||||||
|
{
|
||||||
|
foreach (var other in PhysicsSystem.GetContactingEntities(uid, physics, approximate: true))
|
||||||
|
{
|
||||||
|
if (Tags.HasTag(other, DoorBumpTag) && TryOpen(uid, door, other, quiet: true))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Iterate over active doors and progress them to the next state if they need to be updated.
|
/// Iterate over active doors and progress them to the next state if they need to be updated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -619,8 +736,6 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void CheckDoorBump(Entity<DoorComponent, PhysicsComponent> ent) { }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Makes a door proceed to the next state (if applicable).
|
/// Makes a door proceed to the next state (if applicable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -632,7 +747,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
if (door.CurrentlyCrushing.Count > 0)
|
if (door.CurrentlyCrushing.Count > 0)
|
||||||
// This is a closed door that is crushing people and needs to auto-open. Note that we don't check "can open"
|
// This is a closed door that is crushing people and needs to auto-open. Note that we don't check "can open"
|
||||||
// here. The door never actually finished closing and we don't want people to get stuck inside of doors.
|
// here. The door never actually finished closing and we don't want people to get stuck inside of doors.
|
||||||
StartOpening(ent, door, predicted: true);
|
StartOpening(ent, door);
|
||||||
|
|
||||||
switch (door.State)
|
switch (door.State)
|
||||||
{
|
{
|
||||||
@@ -665,7 +780,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
|
|
||||||
case DoorState.Open:
|
case DoorState.Open:
|
||||||
// This door is open, and queued for an auto-close.
|
// This door is open, and queued for an auto-close.
|
||||||
if (!TryClose(ent, door, predicted: true))
|
if (!TryClose(ent, door))
|
||||||
{
|
{
|
||||||
// The door failed to close (blocked?). Try again in one second.
|
// The door failed to close (blocked?). Try again in one second.
|
||||||
door.NextStateChange = time + TimeSpan.FromSeconds(1);
|
door.NextStateChange = time + TimeSpan.FromSeconds(1);
|
||||||
@@ -679,6 +794,4 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
protected abstract void PlaySound(EntityUid uid, SoundSpecifier soundSpecifier, AudioParams audioParams, EntityUid? predictingPlayer, bool predicted);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ public sealed class PryingSystem : EntitySystem
|
|||||||
|
|
||||||
if (!CanPry(target, user, out var message, comp))
|
if (!CanPry(target, user, out var message, comp))
|
||||||
{
|
{
|
||||||
if (message != null)
|
if (!string.IsNullOrWhiteSpace(message))
|
||||||
Popup.PopupEntity(Loc.GetString(message), target, user);
|
Popup.PopupClient(Loc.GetString(message), target, user);
|
||||||
// If we have reached this point we want the event that caused this
|
// If we have reached this point we want the event that caused this
|
||||||
// to be marked as handled.
|
// to be marked as handled.
|
||||||
return true;
|
return true;
|
||||||
@@ -162,23 +162,14 @@ public sealed class PryingSystem : EntitySystem
|
|||||||
|
|
||||||
if (!CanPry(uid, args.User, out var message, comp))
|
if (!CanPry(uid, args.User, out var message, comp))
|
||||||
{
|
{
|
||||||
if (message != null)
|
if (!string.IsNullOrWhiteSpace(message))
|
||||||
Popup.PopupEntity(Loc.GetString(message), uid, args.User);
|
Popup.PopupClient(Loc.GetString(message), uid, args.User);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: When we get airlock prediction make this fully predicted.
|
|
||||||
// When that happens also fix the checking function in the Client AirlockSystem.
|
|
||||||
if (args.Used != null && comp != null)
|
if (args.Used != null && comp != null)
|
||||||
{
|
{
|
||||||
if (HasComp<AirlockComponent>(uid))
|
_audioSystem.PlayPredicted(comp.UseSound, args.Used.Value, args.User);
|
||||||
{
|
|
||||||
_audioSystem.PlayPvs(comp.UseSound, args.Used.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_audioSystem.PlayPredicted(comp.UseSound, args.Used.Value, args.User);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ev = new PriedEvent(args.User);
|
var ev = new PriedEvent(args.User);
|
||||||
|
|||||||
@@ -76,6 +76,13 @@ public abstract class SharedWiresSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ChangePanelVisibility(EntityUid uid, WiresPanelComponent component, bool visible)
|
||||||
|
{
|
||||||
|
component.Visible = visible;
|
||||||
|
UpdateAppearance(uid, component);
|
||||||
|
Dirty(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
protected void UpdateAppearance(EntityUid uid, WiresPanelComponent panel)
|
protected void UpdateAppearance(EntityUid uid, WiresPanelComponent panel)
|
||||||
{
|
{
|
||||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
||||||
|
|||||||
Reference in New Issue
Block a user