No atmos stacking (attempt 2) (#16687)

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Tom Leys
2023-06-30 00:04:36 +12:00
committed by GitHub
parent ccf58fa657
commit 67df47f553
12 changed files with 165 additions and 14 deletions

View File

@@ -14,7 +14,6 @@ namespace Content.Client.Atmos.UI
{ {
private GasFilterWindow? _window; private GasFilterWindow? _window;
private const float MaxTransferRate = Atmospherics.MaxTransferRate;
public GasFilterBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey) public GasFilterBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
{ {
@@ -49,7 +48,6 @@ namespace Content.Client.Atmos.UI
private void OnFilterTransferRatePressed(string value) private void OnFilterTransferRatePressed(string value)
{ {
float rate = float.TryParse(value, out var parsed) ? parsed : 0f; float rate = float.TryParse(value, out var parsed) ? parsed : 0f;
if (rate > MaxTransferRate) rate = MaxTransferRate;
SendMessage(new GasFilterChangeRateMessage(rate)); SendMessage(new GasFilterChangeRateMessage(rate));
} }

View File

@@ -9,7 +9,7 @@
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-filter-ui-filter-transfer-rate}"/> <Label Text="{Loc comp-gas-filter-ui-filter-transfer-rate}"/>
<LineEdit Name="FilterTransferRateInput" MinSize="40 0" /> <LineEdit Name="FilterTransferRateInput" MinSize="60 0" />
<Button Name="SetFilterRate" Text="{Loc comp-gas-filter-ui-filter-set-rate}" Disabled="True"/> <Button Name="SetFilterRate" Text="{Loc comp-gas-filter-ui-filter-set-rate}" Disabled="True"/>
</BoxContainer> </BoxContainer>

View File

@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Content.Client.Popups;
using Content.Shared.Construction; using Content.Shared.Construction;
using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Prototypes;
using Content.Shared.Examine; using Content.Shared.Examine;
@@ -24,6 +25,7 @@ namespace Content.Client.Construction
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
private readonly Dictionary<int, ConstructionGhostComponent> _ghosts = new(); private readonly Dictionary<int, ConstructionGhostComponent> _ghosts = new();
private readonly Dictionary<string, ConstructionGuide> _guideCache = new(); private readonly Dictionary<string, ConstructionGuide> _guideCache = new();
@@ -188,11 +190,8 @@ namespace Content.Client.Construction
if (!_interactionSystem.InRangeUnobstructed(user, loc, 20f, predicate: predicate)) if (!_interactionSystem.InRangeUnobstructed(user, loc, 20f, predicate: predicate))
return false; return false;
foreach (var condition in prototype.Conditions) if (!CheckConstructionConditions(prototype, loc, dir, user, showPopup: true))
{
if (!condition.Condition(user, loc, dir))
return false; return false;
}
ghost = EntityManager.SpawnEntity("constructionghost", loc); ghost = EntityManager.SpawnEntity("constructionghost", loc);
var comp = EntityManager.GetComponent<ConstructionGhostComponent>(ghost.Value); var comp = EntityManager.GetComponent<ConstructionGhostComponent>(ghost.Value);
@@ -217,6 +216,30 @@ namespace Content.Client.Construction
return true; return true;
} }
private bool CheckConstructionConditions(ConstructionPrototype prototype, EntityCoordinates loc, Direction dir,
EntityUid user, bool showPopup = false)
{
foreach (var condition in prototype.Conditions)
{
if (!condition.Condition(user, loc, dir))
{
if (showPopup)
{
var message = condition.GenerateGuideEntry()?.Localization;
if (message != null)
{
// Show the reason to the user:
_popupSystem.PopupCoordinates(Loc.GetString(message), loc);
}
}
return false;
}
}
return true;
}
/// <summary> /// <summary>
/// Checks if any construction ghosts are present at the given position /// Checks if any construction ghosts are present at the given position
/// </summary> /// </summary>

View File

@@ -1,13 +1,16 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Construction.Components;
using Content.Server.Coordinates.Helpers; using Content.Server.Coordinates.Helpers;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Pulling; using Content.Server.Pulling;
using Content.Shared.Construction.Components; using Content.Shared.Construction.Components;
using Content.Shared.Construction.Conditions;
using Content.Shared.Construction.EntitySystems; using Content.Shared.Construction.EntitySystems;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Pulling.Components; using Content.Shared.Pulling.Components;
using Content.Shared.Tag;
using Content.Shared.Tools; using Content.Shared.Tools;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Robust.Shared.Map; using Robust.Shared.Map;
@@ -85,7 +88,15 @@ namespace Content.Server.Construction
// TODO: Anchoring snaps rn anyway! // TODO: Anchoring snaps rn anyway!
if (component.Snap) if (component.Snap)
{ {
_transform.SetCoordinates(uid, xform.Coordinates.SnapToGrid(EntityManager, _mapManager)); var coordinates = xform.Coordinates.SnapToGrid(EntityManager, _mapManager);
if (AnyUnstackable(uid, coordinates))
{
_popup.PopupEntity(Loc.GetString("construction-step-condition-no-unstackable-in-tile"), uid, args.User);
return;
}
_transform.SetCoordinates(uid, coordinates);
} }
RaiseLocalEvent(uid, new BeforeAnchoredEvent(args.User, used)); RaiseLocalEvent(uid, new BeforeAnchoredEvent(args.User, used));
@@ -195,6 +206,12 @@ namespace Content.Server.Construction
return; return;
} }
if (AnyUnstackable(uid, transform.Coordinates))
{
_popup.PopupEntity(Loc.GetString("construction-step-condition-no-unstackable-in-tile"), uid, userUid);
return;
}
_tool.UseTool(usingUid, userUid, uid, anchorable.Delay, usingTool.Qualities, new TryAnchorCompletedEvent()); _tool.UseTool(usingUid, userUid, uid, anchorable.Delay, usingTool.Qualities, new TryAnchorCompletedEvent());
} }

View File

@@ -0,0 +1,31 @@
using Content.Shared.Construction.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.Map;
namespace Content.Shared.Construction.Conditions;
/// <summary>
/// Check for "Unstackable" condition commonly used by atmos devices and others which otherwise don't check on
/// collisions with other items.
/// </summary>
[UsedImplicitly]
[DataDefinition]
public sealed class NoUnstackableInTile : IConstructionCondition
{
public const string GuidebookString = "construction-step-condition-no-unstackable-in-tile";
public bool Condition(EntityUid user, EntityCoordinates location, Direction direction)
{
var sysMan = IoCManager.Resolve<IEntitySystemManager>();
var anchorable = sysMan.GetEntitySystem<SharedAnchorableSystem>();
return !anchorable.AnyUnstackablesAnchoredAt(location);
}
public ConstructionGuideEntry GenerateGuideEntry()
{
return new ConstructionGuideEntry
{
Localization = GuidebookString
};
}
}

View File

@@ -2,6 +2,7 @@
using Content.Shared.Tag; using Content.Shared.Tag;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components;
namespace Content.Shared.Construction.Conditions namespace Content.Shared.Construction.Conditions
{ {
@@ -11,11 +12,20 @@ namespace Content.Shared.Construction.Conditions
{ {
public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) public bool Condition(EntityUid user, EntityCoordinates location, Direction direction)
{ {
var tagSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<TagSystem>(); var entManager = IoCManager.Resolve<IEntityManager>();
var gridUid = location.GetGridUid(entManager);
foreach (var entity in location.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Static)) if (!entManager.TryGetComponent<MapGridComponent>(gridUid, out var grid))
return true;
var tagQuery = entManager.GetEntityQuery<TagComponent>();
var sysMan = entManager.EntitySysManager;
var tagSystem = sysMan.GetEntitySystem<TagSystem>();
var lookup = sysMan.GetEntitySystem<EntityLookupSystem>();
foreach (var entity in lookup.GetEntitiesIntersecting(gridUid.Value, grid.LocalToTile(location)))
{ {
if (tagSystem.HasTag(entity, "Window")) if (tagSystem.HasTag(entity, "Window", tagQuery))
return false; return false;
} }

View File

@@ -3,17 +3,28 @@ using Content.Shared.Containers.ItemSlots;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Pulling.Components; using Content.Shared.Pulling.Components;
using Content.Shared.Tag;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Construction.EntitySystems; namespace Content.Shared.Construction.EntitySystems;
public abstract class SharedAnchorableSystem : EntitySystem public abstract class SharedAnchorableSystem : EntitySystem
{ {
[Dependency] private readonly TagSystem _tagSystem = default!;
protected EntityQuery<TagComponent> TagQuery;
public const string Unstackable = "Unstackable";
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
TagQuery = GetEntityQuery<TagComponent>();
SubscribeLocalEvent<AnchorableComponent, InteractUsingEvent>(OnInteractUsing, SubscribeLocalEvent<AnchorableComponent, InteractUsingEvent>(OnInteractUsing,
before: new[] { typeof(ItemSlotsSystem) }, after: new[] { typeof(SharedConstructionSystem) }); before: new[] { typeof(ItemSlotsSystem) }, after: new[] { typeof(SharedConstructionSystem) });
} }
@@ -42,6 +53,38 @@ public abstract class SharedAnchorableSystem : EntitySystem
// TODO tool system is fixed now, make this actually shared. // TODO tool system is fixed now, make this actually shared.
} }
public bool AnyUnstackablesAnchoredAt(EntityCoordinates location)
{
var gridUid = location.GetGridUid(EntityManager);
if (!TryComp<MapGridComponent>(gridUid, out var grid))
return false;
var enumerator = grid.GetAnchoredEntitiesEnumerator(grid.LocalToTile(location));
while (enumerator.MoveNext(out var entity))
{
// If we find another unstackable here, return true.
if (_tagSystem.HasTag(entity.Value, Unstackable, TagQuery))
{
return true;
}
}
return false;
}
public bool AnyUnstackable(EntityUid uid, EntityCoordinates location)
{
// If we are unstackable, iterate through any other entities anchored on the current square
if (_tagSystem.HasTag(uid, Unstackable, TagQuery))
{
return AnyUnstackablesAnchoredAt(location);
}
return false;
}
[Serializable, NetSerializable] [Serializable, NetSerializable]
protected sealed class TryUnanchorCompletedEvent : SimpleDoAfterEvent protected sealed class TryUnanchorCompletedEvent : SimpleDoAfterEvent
{ {

View File

@@ -0,0 +1 @@
construction-step-condition-no-unstackable-in-tile = You cannot make a stack of similar devices.

View File

@@ -6,6 +6,9 @@
mode: SnapgridCenter mode: SnapgridCenter
components: components:
- type: AtmosDevice - type: AtmosDevice
- type: Tag
tags:
- Unstackable
- type: SubFloorHide - type: SubFloorHide
blockInteractions: false blockInteractions: false
blockAmbience: false blockAmbience: false

View File

@@ -6,6 +6,9 @@
mode: SnapgridCenter mode: SnapgridCenter
components: components:
- type: AtmosDevice - type: AtmosDevice
- type: Tag
tags:
- Unstackable
- type: SubFloorHide - type: SubFloorHide
blockInteractions: false blockInteractions: false
blockAmbience: false blockAmbience: false
@@ -54,11 +57,16 @@
type: GasFilterBoundUserInterface type: GasFilterBoundUserInterface
- type: GasFilter - type: GasFilter
enabled: false enabled: false
transferRate: 1000
maxTransferRate: 1000
- type: Flippable - type: Flippable
mirrorEntity: GasFilterFlipped mirrorEntity: GasFilterFlipped
- type: Construction - type: Construction
graph: GasTrinary graph: GasTrinary
node: filter node: filter
conditions:
- !type:TileNotBlocked
- !type:NoUnstackableInTile
- type: AmbientSound - type: AmbientSound
enabled: false enabled: false
volume: -9 volume: -9
@@ -201,6 +209,9 @@
mode: SnapgridCenter mode: SnapgridCenter
components: components:
- type: AtmosDevice - type: AtmosDevice
- type: Tag
tags:
- Unstackable
- type: SubFloorHide - type: SubFloorHide
blockInteractions: false blockInteractions: false
blockAmbience: false blockAmbience: false

View File

@@ -530,6 +530,7 @@
state: pumpPressure state: pumpPressure
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
name: volumetric gas pump name: volumetric gas pump
@@ -551,6 +552,7 @@
state: pumpVolume state: pumpVolume
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
id: GasPassiveGate id: GasPassiveGate
@@ -572,6 +574,7 @@
state: pumpPassiveGate state: pumpPassiveGate
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
id: GasValve id: GasValve
@@ -593,6 +596,7 @@
state: pumpManualValve state: pumpManualValve
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
id: SignalControlledValve id: SignalControlledValve
@@ -614,6 +618,7 @@
state: pumpSignalValve state: pumpSignalValve
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
id: GasPort id: GasPort
@@ -635,6 +640,7 @@
state: gasCanisterPort state: gasCanisterPort
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
id: GasDualPortVentPump id: GasDualPortVentPump
@@ -674,6 +680,7 @@
mirror: GasFilterFlipped mirror: GasFilterFlipped
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
id: GasFilterFlipped id: GasFilterFlipped
@@ -692,6 +699,7 @@
mirror: GasFilter mirror: GasFilter
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
id: GasMixer id: GasMixer
@@ -709,6 +717,7 @@
mirror: GasMixerFlipped mirror: GasMixerFlipped
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked {}
- !type:NoUnstackableInTile
- type: construction - type: construction
id: GasMixerFlipped id: GasMixerFlipped
@@ -726,7 +735,8 @@
state: gasMixerF state: gasMixerF
mirror: GasMixer mirror: GasMixer
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked
- !type:NoUnstackableInTile
- type: construction - type: construction
id: PressureControlledValve id: PressureControlledValve
@@ -742,7 +752,8 @@
sprite: Structures/Piping/Atmospherics/pneumaticvalve.rsi sprite: Structures/Piping/Atmospherics/pneumaticvalve.rsi
state: off state: off
conditions: conditions:
- !type:TileNotBlocked {} - !type:TileNotBlocked
- !type:NoUnstackableInTile
# INTERCOM # INTERCOM
- type: construction - type: construction

View File

@@ -797,6 +797,9 @@
- type: Tag - type: Tag
id: TrashBag id: TrashBag
- type: Tag
id: Unstackable # To prevent things like atmos devices (filters etc) being stacked on one tile. See NoUnstackableInTile
- type: Tag - type: Tag
id: UraniumGlassShard id: UraniumGlassShard