Sloth's subfloor vismask adventure (#35347)

* Add a subfloor vismask

Significantly cuts down on sent entity count.

* More optimisations

* Fix command

* Fixes

* namespace cleanup

* Review

* Vismasks

* Content update

* Bandaid

* awewa

* Revert these

* reh

* Update Content.Shared/SubFloor/TrayScannerComponent.cs

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
This commit is contained in:
metalgearsloth
2025-03-21 00:57:04 +11:00
committed by GitHub
parent 2e7f01b99e
commit 9292e3a43c
22 changed files with 277 additions and 47 deletions

View File

@@ -1,12 +1,15 @@
using Content.Shared.DrawDepth;
using Content.Client.UserInterface.Systems.Sandbox;
using Content.Shared.SubFloor;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Shared.Player;
namespace Content.Client.SubFloor;
public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly IUserInterfaceManager _ui = default!;
private bool _showAll;
@@ -18,8 +21,13 @@ public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
{
if (_showAll == value) return;
_showAll = value;
_ui.GetUIController<SandboxUIController>().SetToggleSubfloors(value);
UpdateAll();
var ev = new ShowSubfloorRequestEvent()
{
Value = value,
};
RaiseNetworkEvent(ev);
}
}
@@ -28,6 +36,20 @@ public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
base.Initialize();
SubscribeLocalEvent<SubFloorHideComponent, AppearanceChangeEvent>(OnAppearanceChanged);
SubscribeNetworkEvent<ShowSubfloorRequestEvent>(OnRequestReceived);
SubscribeLocalEvent<LocalPlayerDetachedEvent>(OnPlayerDetached);
}
private void OnPlayerDetached(LocalPlayerDetachedEvent ev)
{
// Vismask resets so need to reset this.
ShowAll = false;
}
private void OnRequestReceived(ShowSubfloorRequestEvent ev)
{
// When client receives request Queue an update on all vis.
UpdateAll();
}
private void OnAppearanceChanged(EntityUid uid, SubFloorHideComponent component, ref AppearanceChangeEvent args)

View File

@@ -39,7 +39,6 @@ public sealed class SandboxUIController : UIController, IOnStateChanged<Gameplay
[UISystemDependency] private readonly DebugPhysicsSystem _debugPhysics = default!;
[UISystemDependency] private readonly MarkerSystem _marker = default!;
[UISystemDependency] private readonly SandboxSystem _sandbox = default!;
[UISystemDependency] private readonly SubFloorHideSystem _subfloorHide = default!;
private SandboxWindow? _window;
@@ -117,10 +116,11 @@ public sealed class SandboxUIController : UIController, IOnStateChanged<Gameplay
_window.OnOpen += () => { SandboxButton!.Pressed = true; };
_window.OnClose += () => { SandboxButton!.Pressed = false; };
// TODO: These need moving to opened so at least if they're not synced properly on open they work.
_window.ToggleLightButton.Pressed = !_light.Enabled;
_window.ToggleFovButton.Pressed = !_eye.CurrentEye.DrawFov;
_window.ToggleShadowsButton.Pressed = !_light.DrawShadows;
_window.ToggleSubfloorButton.Pressed = _subfloorHide.ShowAll;
_window.ShowMarkersButton.Pressed = _marker.MarkersVisible;
_window.ShowBbButton.Pressed = (_debugPhysics.Flags & PhysicsDebugFlags.Shapes) != 0x0;
@@ -219,4 +219,16 @@ public sealed class SandboxUIController : UIController, IOnStateChanged<Gameplay
_window.Close();
}
}
#region Buttons
public void SetToggleSubfloors(bool value)
{
if (_window == null)
return;
_window.ToggleSubfloorButton.Pressed = value;
}
#endregion
}

View File

@@ -1,4 +1,5 @@
using Robust.Client.AutoGenerated;
using Content.Client.SubFloor;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
@@ -7,8 +8,18 @@ namespace Content.Client.UserInterface.Systems.Sandbox.Windows;
[GenerateTypedNameReferences]
public sealed partial class SandboxWindow : DefaultWindow
{
[Dependency] private readonly IEntityManager _entManager = null!;
public SandboxWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
protected override void Opened()
{
base.Opened();
// Make sure state is up to date.
ToggleSubfloorButton.Pressed = _entManager.System<SubFloorHideSystem>().ShowAll;
}
}

View File

@@ -35,7 +35,6 @@ public sealed class PrototypeSaveTest
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityMan = server.ResolveDependency<IEntityManager>();
var prototypeMan = server.ResolveDependency<IPrototypeManager>();
var seriMan = server.ResolveDependency<ISerializationManager>();

View File

@@ -100,6 +100,17 @@ namespace Content.Server.Ghost
SubscribeLocalEvent<RoundEndTextAppendEvent>(_ => MakeVisible(true));
SubscribeLocalEvent<ToggleGhostVisibilityToAllEvent>(OnToggleGhostVisibilityToAll);
SubscribeLocalEvent<GhostComponent, GetVisMaskEvent>(OnGhostVis);
}
private void OnGhostVis(Entity<GhostComponent> ent, ref GetVisMaskEvent args)
{
// If component not deleting they can see ghosts.
if (ent.Comp.LifeStage <= ComponentLifeStage.Running)
{
args.VisibilityMask |= (int)VisibilityFlags.Ghost;
}
}
private void OnGhostHearingAction(EntityUid uid, GhostComponent component, ToggleGhostHearingActionEvent args)
@@ -186,8 +197,7 @@ namespace Content.Server.Ghost
_visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility);
}
SetCanSeeGhosts(uid, true);
_eye.RefreshVisibilityMask(uid);
var time = _gameTiming.CurTime;
component.TimeOfDeath = time;
}
@@ -207,21 +217,10 @@ namespace Content.Server.Ghost
}
// Entity can't see ghosts anymore.
SetCanSeeGhosts(uid, false);
_eye.RefreshVisibilityMask(uid);
_actions.RemoveAction(uid, component.BooActionEntity);
}
private void SetCanSeeGhosts(EntityUid uid, bool canSee, EyeComponent? eyeComponent = null)
{
if (!Resolve(uid, ref eyeComponent, false))
return;
if (canSee)
_eye.SetVisibilityMask(uid, eyeComponent.VisibilityMask | (int) VisibilityFlags.Ghost, eyeComponent);
else
_eye.SetVisibilityMask(uid, eyeComponent.VisibilityMask & ~(int) VisibilityFlags.Ghost, eyeComponent);
}
private void OnMapInit(EntityUid uid, GhostComponent component, MapInitEvent args)
{
_actions.AddAction(uid, ref component.BooActionEntity, component.BooAction);

View File

@@ -63,9 +63,16 @@ public sealed partial class RevenantSystem : EntitySystem
SubscribeLocalEvent<RevenantComponent, StatusEffectEndedEvent>(OnStatusEnded);
SubscribeLocalEvent<RoundEndTextAppendEvent>(_ => MakeVisible(true));
SubscribeLocalEvent<RevenantComponent, GetVisMaskEvent>(OnRevenantGetVis);
InitializeAbilities();
}
private void OnRevenantGetVis(Entity<RevenantComponent> ent, ref GetVisMaskEvent args)
{
args.VisibilityMask |= (int)VisibilityFlags.Ghost;
}
private void OnStartup(EntityUid uid, RevenantComponent component, ComponentStartup args)
{
//update the icon
@@ -84,10 +91,7 @@ public sealed partial class RevenantSystem : EntitySystem
}
//ghost vision
if (TryComp(uid, out EyeComponent? eye))
{
_eye.SetVisibilityMask(uid, eye.VisibilityMask | (int) (VisibilityFlags.Ghost), eye);
}
_eye.RefreshVisibilityMask(uid);
}
private void OnMapInit(EntityUid uid, RevenantComponent component, MapInitEvent args)

View File

@@ -1,16 +1,76 @@
using Content.Shared.Construction.Components;
using Content.Shared.Eye;
using Content.Shared.SubFloor;
using Robust.Server.Player;
using Robust.Shared.Enums;
using Robust.Shared.Map.Components;
using Robust.Shared.Player;
namespace Content.Server.SubFloor;
public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly SharedEyeSystem _eye = default!;
private HashSet<ICommonSession> _showFloors = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SubFloorHideComponent, AnchorAttemptEvent>(OnAnchorAttempt);
SubscribeLocalEvent<SubFloorHideComponent, UnanchorAttemptEvent>(OnUnanchorAttempt);
SubscribeNetworkEvent<ShowSubfloorRequestEvent>(OnShowSubfloor);
SubscribeLocalEvent<GetVisMaskEvent>(OnGetVisibility);
_player.PlayerStatusChanged += OnPlayerStatus;
}
private void OnPlayerStatus(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus == SessionStatus.Connected)
return;
_showFloors.Remove(e.Session);
if (e.Session.AttachedEntity != null)
_eye.RefreshVisibilityMask(e.Session.AttachedEntity.Value);
}
private void OnGetVisibility(ref GetVisMaskEvent ev)
{
if (!TryComp(ev.Entity, out ActorComponent? actor))
return;
if (_showFloors.Contains(actor.PlayerSession))
{
ev.VisibilityMask |= (int)VisibilityFlags.Subfloor;
}
}
private void OnShowSubfloor(ShowSubfloorRequestEvent ev, EntitySessionEventArgs args)
{
// TODO: Commands are a bit of an eh? for client-only but checking shared perms
var ent = args.SenderSession.AttachedEntity;
if (!TryComp(ent, out EyeComponent? eyeComp))
return;
if (ev.Value)
{
_showFloors.Add(args.SenderSession);
}
else
{
_showFloors.Remove(args.SenderSession);
}
_eye.RefreshVisibilityMask((ent.Value, eyeComp));
RaiseNetworkEvent(new ShowSubfloorRequestEvent()
{
Value = ev.Value,
}, args.SenderSession);
}
private void OnAnchorAttempt(EntityUid uid, SubFloorHideComponent component, AnchorAttemptEvent args)

View File

@@ -9,5 +9,6 @@ namespace Content.Shared.Eye
None = 0,
Normal = 1 << 0,
Ghost = 1 << 1,
Subfloor = 1 << 2,
}
}

View File

@@ -5,7 +5,7 @@ using Robust.Shared.Prototypes;
namespace Content.Shared.Ghost;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedGhostSystem))]
[AutoGenerateComponentState(true)]
[AutoGenerateComponentState(true), AutoGenerateComponentPause]
public sealed partial class GhostComponent : Component
{
// Actions
@@ -41,7 +41,7 @@ public sealed partial class GhostComponent : Component
// End actions
[ViewVariables(VVAccess.ReadWrite), DataField]
[ViewVariables(VVAccess.ReadWrite), DataField, AutoPausedField]
public TimeSpan TimeOfDeath = TimeSpan.Zero;
[DataField("booRadius"), ViewVariables(VVAccess.ReadWrite)]

View File

@@ -1,5 +1,6 @@
using Content.Shared.Audio;
using Content.Shared.Explosion;
using Content.Shared.Eye;
using Content.Shared.Interaction.Events;
using Content.Shared.Maps;
using JetBrains.Annotations;
@@ -19,11 +20,16 @@ namespace Content.Shared.SubFloor
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
[Dependency] protected readonly SharedMapSystem Map = default!;
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] private readonly SharedVisibilitySystem _visibility = default!;
private EntityQuery<SubFloorHideComponent> _hideQuery;
public override void Initialize()
{
base.Initialize();
_hideQuery = GetEntityQuery<SubFloorHideComponent>();
SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
SubscribeLocalEvent<SubFloorHideComponent, ComponentStartup>(OnSubFloorStarted);
SubscribeLocalEvent<SubFloorHideComponent, ComponentShutdown>(OnSubFloorTerminating);
@@ -67,7 +73,7 @@ namespace Content.Shared.SubFloor
return;
// Regardless of whether we're on a subfloor or not, unhide.
component.IsUnderCover = false;
SetUnderCover((uid, component), false);
UpdateAppearance(uid, component);
}
@@ -80,7 +86,7 @@ namespace Content.Shared.SubFloor
}
else if (component.IsUnderCover)
{
component.IsUnderCover = false;
SetUnderCover((uid, component), false);
UpdateAppearance(uid, component);
}
}
@@ -93,7 +99,7 @@ namespace Content.Shared.SubFloor
if (args.NewTile.Tile.IsEmpty)
return; // Anything that was here will be unanchored anyways.
UpdateTile(args.NewTile.GridUid, Comp<MapGridComponent>(args.NewTile.GridUid), args.NewTile.GridIndices);
UpdateTile(args.NewTile.GridUid, args.Entity.Comp, args.NewTile.GridIndices);
}
/// <summary>
@@ -105,13 +111,24 @@ namespace Content.Shared.SubFloor
return;
if (xform.Anchored && TryComp<MapGridComponent>(xform.GridUid, out var grid))
component.IsUnderCover = HasFloorCover(xform.GridUid.Value, grid, Map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates));
SetUnderCover((uid, component), HasFloorCover(xform.GridUid.Value, grid, Map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates)));
else
component.IsUnderCover = false;
SetUnderCover((uid, component), false);
UpdateAppearance(uid, component);
}
private void SetUnderCover(Entity<SubFloorHideComponent> entity, bool value)
{
// If it's not undercover or it always has visible layers then normal visibility.
_visibility.SetLayer(entity.Owner, value && entity.Comp.VisibleLayers.Count == 0 ? (ushort) VisibilityFlags.Subfloor : (ushort) VisibilityFlags.Normal);
if (entity.Comp.IsUnderCover == value)
return;
entity.Comp.IsUnderCover = value;
}
public bool HasFloorCover(EntityUid gridUid, MapGridComponent grid, Vector2i position)
{
// TODO Redo this function. Currently wires on an asteroid are always "below the floor"
@@ -125,13 +142,13 @@ namespace Content.Shared.SubFloor
foreach (var uid in Map.GetAnchoredEntities(gridUid, grid, position))
{
if (!TryComp(uid, out SubFloorHideComponent? hideComp))
if (!_hideQuery.TryComp(uid, out var hideComp))
continue;
if (hideComp.IsUnderCover == covered)
continue;
hideComp.IsUnderCover = covered;
SetUnderCover((uid, hideComp), covered);
UpdateAppearance(uid, hideComp);
}
}
@@ -154,6 +171,12 @@ namespace Content.Shared.SubFloor
Appearance.SetData(uid, SubFloorVisuals.Covered, hideComp.IsUnderCover, appearance);
}
}
[Serializable, NetSerializable]
protected sealed class ShowSubfloorRequestEvent : EntityEventArgs
{
public bool Value;
}
}
[Serializable, NetSerializable]

View File

@@ -1,17 +1,16 @@
using Content.Shared.Eye;
using Content.Shared.Hands;
using Content.Shared.Interaction;
using Robust.Shared.Containers;
using Content.Shared.Inventory.Events;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Linq;
namespace Content.Shared.SubFloor;
public abstract class SharedTrayScannerSystem : EntitySystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedEyeSystem _eye = default!;
public const float SubfloorRevealAlpha = 0.8f;
@@ -22,6 +21,50 @@ public abstract class SharedTrayScannerSystem : EntitySystem
SubscribeLocalEvent<TrayScannerComponent, ComponentGetState>(OnTrayScannerGetState);
SubscribeLocalEvent<TrayScannerComponent, ComponentHandleState>(OnTrayScannerHandleState);
SubscribeLocalEvent<TrayScannerComponent, ActivateInWorldEvent>(OnTrayScannerActivate);
SubscribeLocalEvent<TrayScannerComponent, GotEquippedHandEvent>(OnTrayHandEquipped);
SubscribeLocalEvent<TrayScannerComponent, GotUnequippedHandEvent>(OnTrayHandUnequipped);
SubscribeLocalEvent<TrayScannerComponent, GotEquippedEvent>(OnTrayEquipped);
SubscribeLocalEvent<TrayScannerComponent, GotUnequippedEvent>(OnTrayUnequipped);
SubscribeLocalEvent<TrayScannerUserComponent, GetVisMaskEvent>(OnUserGetVis);
}
private void OnUserGetVis(Entity<TrayScannerUserComponent> ent, ref GetVisMaskEvent args)
{
args.VisibilityMask |= (int)VisibilityFlags.Subfloor;
}
private void OnEquip(EntityUid user)
{
EnsureComp<TrayScannerUserComponent>(user);
_eye.RefreshVisibilityMask(user);
}
private void OnUnequip(EntityUid user)
{
RemComp<TrayScannerUserComponent>(user);
_eye.RefreshVisibilityMask(user);
}
private void OnTrayHandUnequipped(Entity<TrayScannerComponent> ent, ref GotUnequippedHandEvent args)
{
OnUnequip(args.User);
}
private void OnTrayHandEquipped(Entity<TrayScannerComponent> ent, ref GotEquippedHandEvent args)
{
OnEquip(args.User);
}
private void OnTrayUnequipped(Entity<TrayScannerComponent> ent, ref GotUnequippedEvent args)
{
OnUnequip(args.Equipee);
}
private void OnTrayEquipped(Entity<TrayScannerComponent> ent, ref GotEquippedEvent args)
{
OnEquip(args.Equipee);
}
private void OnTrayScannerActivate(EntityUid uid, TrayScannerComponent scanner, ActivateInWorldEvent args)

View File

@@ -1,6 +1,4 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
namespace Content.Shared.SubFloor
{
@@ -27,7 +25,7 @@ namespace Content.Shared.SubFloor
/// <remarks>
/// Useful for entities like vents, which are only partially hidden. Anchor attempts will still be blocked.
/// </remarks>
[DataField("blockInteractions")]
[DataField]
public bool BlockInteractions { get; set; } = true;
/// <summary>
@@ -36,15 +34,15 @@ namespace Content.Shared.SubFloor
/// <remarks>
/// Useful for cables and piping, gives maint it's distinct noise.
/// </remarks>
[DataField("blockAmbience")]
[DataField]
public bool BlockAmbience { get; set; } = true;
/// <summary>
/// Sprite layer keys for the layers that are always visible, even if the entity is below a floor tile. E.g.,
/// the vent part of a vent is always visible, even though the piping is hidden.
/// </summary>
[DataField("visibleLayers")]
public HashSet<Enum> VisibleLayers = new() { SubfloorLayers.FirstLayer };
[DataField]
public HashSet<Enum> VisibleLayers = new();
/// <summary>
/// This is used for storing the original draw depth of a t-ray revealed entity.

View File

@@ -9,12 +9,13 @@ public sealed partial class TrayScannerComponent : Component
/// <summary>
/// Whether the scanner is currently on.
/// </summary>
[ViewVariables, DataField("enabled")] public bool Enabled;
[DataField]
public bool Enabled;
/// <summary>
/// Radius in which the scanner will reveal entities. Centered on the <see cref="LastLocation"/>.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("range")]
[DataField]
public float Range = 4f;
}

View File

@@ -0,0 +1,8 @@
namespace Content.Shared.SubFloor;
// Don't need to network
/// <summary>
/// Added to anyone using <see cref="TrayScannerComponent"/> to handle the vismask changes.
/// </summary>
[RegisterComponent]
public sealed partial class TrayScannerUserComponent : Component;

View File

@@ -34,6 +34,8 @@
type: NavMapBeaconBoundUserInterface
- type: Item
size: Small
- type: Visibility
layer: 1
- type: SubFloorHide
- type: Anchorable
- type: Construction

View File

@@ -39,6 +39,9 @@
- type: Rotatable
- type: Transform
noRot: false
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
sprite: Structures/Piping/Atmospherics/pump.rsi
layers:
@@ -95,6 +98,9 @@
- type: Rotatable
- type: Transform
noRot: false
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
sprite: Structures/Piping/Atmospherics/pump.rsi
layers:
@@ -149,6 +155,9 @@
placement:
mode: SnapgridCenter
components:
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
sprite: Structures/Piping/Atmospherics/pump.rsi
layers:
@@ -184,6 +193,9 @@
mode: SnapgridCenter
components:
# TODO ATMOS: Give unique sprite.
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
sprite: Structures/Piping/Atmospherics/pump.rsi
layers:
@@ -237,6 +249,9 @@
mode: SnapgridCenter
components:
- type: StationAiWhitelist
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
sprite: Structures/Piping/Atmospherics/pump.rsi
layers:
@@ -300,6 +315,9 @@
placement:
mode: SnapgridCenter
components:
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
sprite: Structures/Piping/Atmospherics/gascanisterport.rsi
layers:
@@ -334,6 +352,9 @@
placement:
mode: SnapgridCenter
components:
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
drawdepth: FloorObjects
sprite: Structures/Piping/Atmospherics/vent.rsi
@@ -501,6 +522,9 @@
id: HeatExchangerBend
suffix: Bend
components:
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi

View File

@@ -7,6 +7,8 @@
placement:
mode: SnapgridCenter
components:
- type: Visibility
layer: 1
- type: Item
size: Normal
- type: Transform

View File

@@ -35,6 +35,9 @@
placement:
mode: SnapgridCenter
components:
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
sprite: Structures/Piping/Atmospherics/gasfilter.rsi
layers:
@@ -84,6 +87,9 @@
placement:
mode: SnapgridCenter
components:
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
sprite: Structures/Piping/Atmospherics/gasfilter.rsi
layers:

View File

@@ -36,6 +36,9 @@
tags:
- GasVent
- Unstackable
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
drawdepth: FloorObjects
sprite: Structures/Piping/Atmospherics/vent.rsi
@@ -83,6 +86,9 @@
placement:
mode: SnapgridCenter
components:
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
drawdepth: FloorObjects
sprite: Structures/Piping/Atmospherics/vent.rsi
@@ -120,6 +126,9 @@
tags:
- GasScrubber
- Unstackable
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: Sprite
drawdepth: FloorObjects
sprite: Structures/Piping/Atmospherics/scrubber.rsi

View File

@@ -13,6 +13,8 @@
sprite: Structures/Piping/disposal.rsi
visible: false
- type: Appearance
- type: Visibility
layer: 1
- type: SubFloorHide
- type: Clickable
- type: InteractionOutline

View File

@@ -29,6 +29,8 @@
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- type: Visibility
layer: 1
- type: SubFloorHide
blockAmbience: false
blockInteractions: false

View File

@@ -4,6 +4,8 @@
placement:
mode: SnapgridCenter
components:
- type: Visibility
layer: 1
- type: Cable
cuttingDelay: 1
- type: Clickable