diff --git a/Content.Client/GameObjects/Components/DoAfterComponent.cs b/Content.Client/GameObjects/Components/DoAfterComponent.cs
index 1b665d2a31..e751f5a996 100644
--- a/Content.Client/GameObjects/Components/DoAfterComponent.cs
+++ b/Content.Client/GameObjects/Components/DoAfterComponent.cs
@@ -3,8 +3,10 @@ using System;
using System.Collections.Generic;
using Content.Client.GameObjects.EntitySystems.DoAfter;
using Content.Shared.GameObjects.Components;
+using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
+using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
@@ -38,6 +40,18 @@ namespace Content.Client.GameObjects.Components
}
}
+ public override void HandleMessage(ComponentMessage message, IComponent? component)
+ {
+ base.HandleMessage(message, component);
+ switch (message)
+ {
+ case PlayerDetachedMsg _:
+ _doAfters.Clear();
+ CancelledDoAfters.Clear();
+ break;
+ }
+ }
+
///
/// Remove a DoAfter without showing a cancellation graphic.
///
diff --git a/Content.Client/GameObjects/Components/GUI/StrippableComponent.cs b/Content.Client/GameObjects/Components/GUI/StrippableComponent.cs
new file mode 100644
index 0000000000..6fa50ded40
--- /dev/null
+++ b/Content.Client/GameObjects/Components/GUI/StrippableComponent.cs
@@ -0,0 +1,22 @@
+using Content.Client.GameObjects.Components.Items;
+using Content.Client.Interfaces.GameObjects.Components.Interaction;
+using Content.Shared.GameObjects.Components.GUI;
+using Robust.Shared.GameObjects;
+
+namespace Content.Client.GameObjects.Components.GUI
+{
+ [RegisterComponent]
+ public class StrippableComponent : SharedStrippableComponent, IClientDraggable
+ {
+ public bool ClientCanDropOn(CanDropEventArgs eventArgs)
+ {
+ return eventArgs.Target.HasComponent()
+ && eventArgs.Target != eventArgs.Dragged && eventArgs.Target == eventArgs.User;
+ }
+
+ public bool ClientCanDrag(CanDragEventArgs eventArgs)
+ {
+ return true;
+ }
+ }
+}
diff --git a/Content.Client/GameObjects/Components/HUD/Inventory/StrippableBoundUserInterface.cs b/Content.Client/GameObjects/Components/HUD/Inventory/StrippableBoundUserInterface.cs
new file mode 100644
index 0000000000..afd87e0e06
--- /dev/null
+++ b/Content.Client/GameObjects/Components/HUD/Inventory/StrippableBoundUserInterface.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using Content.Client.UserInterface;
+using Content.Shared.GameObjects.Components.GUI;
+using Content.Shared.GameObjects.Components.Inventory;
+using JetBrains.Annotations;
+using Robust.Client.GameObjects.Components.UserInterface;
+using Robust.Shared.GameObjects.Components.UserInterface;
+using Robust.Shared.ViewVariables;
+using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
+
+namespace Content.Client.GameObjects.Components.HUD.Inventory
+{
+ [UsedImplicitly]
+ public class StrippableBoundUserInterface : BoundUserInterface
+ {
+ public Dictionary Inventory { get; private set; }
+ public Dictionary Hands { get; private set; }
+
+ [ViewVariables]
+ private StrippingMenu _strippingMenu;
+
+ public StrippableBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
+ {
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _strippingMenu = new StrippingMenu($"{Owner.Owner.Name}'s inventory");
+ _strippingMenu.OpenCentered();
+ UpdateMenu();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing) return;
+ _strippingMenu.Dispose();
+
+ _strippingMenu.Close();
+ }
+
+ private void UpdateMenu()
+ {
+ if (_strippingMenu == null) return;
+
+ _strippingMenu.ClearButtons();
+
+ if(Inventory != null)
+ foreach (var (slot, name) in Inventory)
+ {
+ _strippingMenu.AddButton(EquipmentSlotDefines.SlotNames[slot], name, (ev) =>
+ {
+ SendMessage(new StrippingInventoryButtonPressed(slot));
+ });
+ }
+
+ if(Hands != null)
+ foreach (var (hand, name) in Hands)
+ {
+ _strippingMenu.AddButton(hand, name, (ev) =>
+ {
+ SendMessage(new StrippingHandButtonPressed(hand));
+ });
+ }
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (!(state is StrippingBoundUserInterfaceState stripState)) return;
+
+ Inventory = stripState.Inventory;
+ Hands = stripState.Hands;
+
+ UpdateMenu();
+ }
+ }
+}
diff --git a/Content.Client/GameObjects/Components/Items/HandsComponent.cs b/Content.Client/GameObjects/Components/Items/HandsComponent.cs
index 0a024e4195..daa03d74e1 100644
--- a/Content.Client/GameObjects/Components/Items/HandsComponent.cs
+++ b/Content.Client/GameObjects/Components/Items/HandsComponent.cs
@@ -23,6 +23,7 @@ namespace Content.Client.GameObjects.Components.Items
[Dependency] private readonly IGameHud _gameHud = default!;
#pragma warning restore 649
+ ///
private readonly List _hands = new List();
[ViewVariables] public IReadOnlyList Hands => _hands;
diff --git a/Content.Client/GameObjects/EntitySystems/CameraRecoilSystem.cs b/Content.Client/GameObjects/EntitySystems/CameraRecoilSystem.cs
index 2d5af8c103..137dc2a5e7 100644
--- a/Content.Client/GameObjects/EntitySystems/CameraRecoilSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/CameraRecoilSystem.cs
@@ -1,25 +1,16 @@
using Content.Client.GameObjects.Components.Mobs;
-using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Client.GameObjects.EntitySystems
{
public sealed class CameraRecoilSystem : EntitySystem
{
- public override void Initialize()
- {
- base.Initialize();
-
- EntityQuery = new TypeEntityQuery(typeof(CameraRecoilComponent));
- }
-
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
- foreach (var entity in RelevantEntities)
+ foreach (var recoil in EntityManager.ComponentManager.EntityQuery())
{
- var recoil = entity.GetComponent();
recoil.FrameUpdate(frameTime);
}
}
diff --git a/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterGui.cs b/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterGui.cs
index 35ea070048..b46b359ff4 100644
--- a/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterGui.cs
+++ b/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterGui.cs
@@ -43,6 +43,25 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
}
+ ///
+ /// Called when the mind is detached from an entity
+ ///
+ /// Rather than just dispose of the Gui we'll just remove its child controls and re-use the control.
+ public void Detached()
+ {
+ foreach (var (_, control) in _doAfterControls)
+ {
+ control.Dispose();
+ }
+ _doAfterControls.Clear();
+ foreach (var (_, control) in _doAfterBars)
+ {
+ control.Dispose();
+ }
+ _doAfterBars.Clear();
+ _cancelledDoAfters.Clear();
+ }
+
///
/// Add the necessary control for a DoAfter progress bar.
///
diff --git a/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterSystem.cs b/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterSystem.cs
index 8ba59fde7f..8aed094c7f 100644
--- a/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterSystem.cs
@@ -57,7 +57,7 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter
{
_player = entity;
// Setup the GUI and pass the new data to it if applicable.
- Gui?.Dispose();
+ Gui?.Detached();
if (entity == null)
{
diff --git a/Content.Client/GameObjects/EntitySystems/InstrumentSystem.cs b/Content.Client/GameObjects/EntitySystems/InstrumentSystem.cs
index 5401274988..e184d7d1eb 100644
--- a/Content.Client/GameObjects/EntitySystems/InstrumentSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/InstrumentSystem.cs
@@ -1,6 +1,5 @@
using Content.Client.GameObjects.Components.Instruments;
using JetBrains.Annotations;
-using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
@@ -12,12 +11,6 @@ namespace Content.Client.GameObjects.EntitySystems
{
[Dependency] private readonly IGameTiming _gameTiming = default;
- public override void Initialize()
- {
- base.Initialize();
- EntityQuery = new TypeEntityQuery(typeof(InstrumentComponent));
- }
-
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -27,9 +20,9 @@ namespace Content.Client.GameObjects.EntitySystems
return;
}
- foreach (var entity in RelevantEntities)
+ foreach (var instrumentComponent in EntityManager.ComponentManager.EntityQuery())
{
- entity.GetComponent().Update(frameTime);
+ instrumentComponent.Update(frameTime);
}
}
}
diff --git a/Content.Client/GameObjects/EntitySystems/MarkerSystem.cs b/Content.Client/GameObjects/EntitySystems/MarkerSystem.cs
index 7573253fb6..bec86abbb4 100644
--- a/Content.Client/GameObjects/EntitySystems/MarkerSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/MarkerSystem.cs
@@ -1,5 +1,4 @@
using Content.Client.GameObjects.Components.Markers;
-using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Client.GameObjects.EntitySystems
@@ -8,13 +7,6 @@ namespace Content.Client.GameObjects.EntitySystems
{
private bool _markersVisible;
- public override void Initialize()
- {
- base.Initialize();
-
- EntityQuery = new TypeEntityQuery();
- }
-
public bool MarkersVisible
{
get => _markersVisible;
@@ -27,9 +19,9 @@ namespace Content.Client.GameObjects.EntitySystems
private void UpdateMarkers()
{
- foreach (var entity in RelevantEntities)
+ foreach (var markerComponent in EntityManager.ComponentManager.EntityQuery())
{
- entity.GetComponent().UpdateVisibility();
+ markerComponent.UpdateVisibility();
}
}
}
diff --git a/Content.Client/GameObjects/EntitySystems/MeleeLungeSystem.cs b/Content.Client/GameObjects/EntitySystems/MeleeLungeSystem.cs
index bff419b8ab..3f180d4399 100644
--- a/Content.Client/GameObjects/EntitySystems/MeleeLungeSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/MeleeLungeSystem.cs
@@ -1,6 +1,5 @@
using Content.Client.GameObjects.Components.Mobs;
using JetBrains.Annotations;
-using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Client.GameObjects.EntitySystems
@@ -8,20 +7,13 @@ namespace Content.Client.GameObjects.EntitySystems
[UsedImplicitly]
public sealed class MeleeLungeSystem : EntitySystem
{
- public override void Initialize()
- {
- base.Initialize();
-
- EntityQuery = new TypeEntityQuery();
- }
-
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
- foreach (var entity in RelevantEntities)
+ foreach (var meleeLungeComponent in EntityManager.ComponentManager.EntityQuery())
{
- entity.GetComponent().Update(frameTime);
+ meleeLungeComponent.Update(frameTime);
}
}
}
diff --git a/Content.Client/GameObjects/EntitySystems/MeleeWeaponSystem.cs b/Content.Client/GameObjects/EntitySystems/MeleeWeaponSystem.cs
index 8b95cb6405..ca7a9d79e4 100644
--- a/Content.Client/GameObjects/EntitySystems/MeleeWeaponSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/MeleeWeaponSystem.cs
@@ -24,16 +24,15 @@ namespace Content.Client.GameObjects.EntitySystems
public override void Initialize()
{
SubscribeNetworkEvent(PlayWeaponArc);
- EntityQuery = new TypeEntityQuery(typeof(MeleeWeaponArcAnimationComponent));
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
- foreach (var entity in RelevantEntities)
+ foreach (var arcAnimationComponent in EntityManager.ComponentManager.EntityQuery())
{
- entity.GetComponent().Update(frameTime);
+ arcAnimationComponent.Update(frameTime);
}
}
diff --git a/Content.Client/GameObjects/EntitySystems/MoverSystem.cs b/Content.Client/GameObjects/EntitySystems/MoverSystem.cs
index b16cea1672..cdabb87d7c 100644
--- a/Content.Client/GameObjects/EntitySystems/MoverSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/MoverSystem.cs
@@ -30,11 +30,10 @@ namespace Content.Client.GameObjects.EntitySystems
return;
}
- var physics = playerEnt.GetComponent();
- playerEnt.TryGetComponent(out ICollidableComponent? collidable);
- physics.Predict = true;
+ var collidable = playerEnt.GetComponent();
+ collidable.Predict = true;
- UpdateKinematics(playerEnt.Transform, mover, physics, collidable);
+ UpdateKinematics(playerEnt.Transform, mover, collidable);
}
public override void Update(float frameTime)
diff --git a/Content.Client/GameObjects/EntitySystems/StatusEffectsSystem.cs b/Content.Client/GameObjects/EntitySystems/StatusEffectsSystem.cs
index a0fd868b2c..367c49f0b9 100644
--- a/Content.Client/GameObjects/EntitySystems/StatusEffectsSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/StatusEffectsSystem.cs
@@ -1,5 +1,4 @@
using Content.Client.GameObjects.Components.Mobs;
-using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
@@ -12,11 +11,6 @@ namespace Content.Client.GameObjects.EntitySystems
[Dependency] private IGameTiming _gameTiming;
#pragma warning restore 649
- public StatusEffectsSystem()
- {
- EntityQuery = new TypeEntityQuery(typeof(ClientStatusEffectsComponent));
- }
-
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
@@ -24,9 +18,9 @@ namespace Content.Client.GameObjects.EntitySystems
if (!_gameTiming.IsFirstTimePredicted)
return;
- foreach (var entity in RelevantEntities)
+ foreach (var clientStatusEffectsComponent in EntityManager.ComponentManager.EntityQuery())
{
- entity.GetComponent().FrameUpdate(frameTime);
+ clientStatusEffectsComponent.FrameUpdate(frameTime);
}
}
}
diff --git a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs
index 9d73013321..f388263d65 100644
--- a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs
+++ b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs
@@ -210,6 +210,9 @@ namespace Content.Client.GameObjects.EntitySystems
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
continue;
+ if (verb.BlockedByContainers && !user.IsInSameOrNoContainer(entity))
+ continue;
+
var verbData = verb.GetData(user, component);
if (verbData.IsInvisible)
@@ -232,6 +235,9 @@ namespace Content.Client.GameObjects.EntitySystems
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
continue;
+ if (globalVerb.BlockedByContainers && !user.IsInSameOrNoContainer(entity))
+ continue;
+
var verbData = globalVerb.GetData(user, entity);
if (verbData.IsInvisible)
diff --git a/Content.Client/GlobalVerbs/ExamineVerb.cs b/Content.Client/GlobalVerbs/ExamineVerb.cs
index 13e6198490..2116290796 100644
--- a/Content.Client/GlobalVerbs/ExamineVerb.cs
+++ b/Content.Client/GlobalVerbs/ExamineVerb.cs
@@ -11,6 +11,8 @@ namespace Content.Client.GlobalVerbs
{
public override bool RequireInteractionRange => false;
+ public override bool BlockedByContainers => false;
+
public override void GetData(IEntity user, IEntity target, VerbData data)
{
data.Visibility = VerbVisibility.Visible;
diff --git a/Content.Client/GlobalVerbs/ViewVariablesVerb.cs b/Content.Client/GlobalVerbs/ViewVariablesVerb.cs
index 93e2f1f39a..fd31a6fd0c 100644
--- a/Content.Client/GlobalVerbs/ViewVariablesVerb.cs
+++ b/Content.Client/GlobalVerbs/ViewVariablesVerb.cs
@@ -13,6 +13,7 @@ namespace Content.Client.GlobalVerbs
class ViewVariablesVerb : GlobalVerb
{
public override bool RequireInteractionRange => false;
+ public override bool BlockedByContainers => false;
public override void GetData(IEntity user, IEntity target, VerbData data)
{
diff --git a/Content.Client/Parallax/ParallaxManager.cs b/Content.Client/Parallax/ParallaxManager.cs
index 0e6ff0e827..d289ef16cc 100644
--- a/Content.Client/Parallax/ParallaxManager.cs
+++ b/Content.Client/Parallax/ParallaxManager.cs
@@ -8,6 +8,7 @@ using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Log;
+using Robust.Shared.Interfaces.Resources;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;
@@ -59,16 +60,11 @@ namespace Content.Client.Parallax
if (!debugParallax && _resourceCache.UserData.Exists(ParallaxConfigOld))
{
- bool match;
- using (var data = _resourceCache.UserData.Open(ParallaxConfigOld, FileMode.Open))
- using (var reader = new StreamReader(data, EncodingHelpers.UTF8))
- {
- match = reader.ReadToEnd() == contents;
- }
+ var match = _resourceCache.UserData.ReadAllText(ParallaxConfigOld) == contents;
if (match)
{
- using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Open))
+ using (var stream = _resourceCache.UserData.OpenRead(ParallaxPath))
{
ParallaxTexture = Texture.LoadFromPNGStream(stream, "Parallax");
}
@@ -95,7 +91,7 @@ namespace Content.Client.Parallax
ParallaxTexture = Texture.LoadFromImage(image, "Parallax");
// Store it and CRC so further game starts don't need to regenerate it.
- using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Create))
+ using (var stream = _resourceCache.UserData.Create(ParallaxPath))
{
image.SaveAsPng(stream);
}
@@ -105,8 +101,7 @@ namespace Content.Client.Parallax
var i = 0;
foreach (var debugImage in debugImages)
{
- using (var stream = _resourceCache.UserData.Open(new ResourcePath($"/parallax_debug_{i}.png"),
- FileMode.Create))
+ using (var stream = _resourceCache.UserData.Create(new ResourcePath($"/parallax_debug_{i}.png")))
{
debugImage.SaveAsPng(stream);
}
@@ -117,7 +112,7 @@ namespace Content.Client.Parallax
image.Dispose();
- using (var stream = _resourceCache.UserData.Open(ParallaxConfigOld, FileMode.Create))
+ using (var stream = _resourceCache.UserData.Create(ParallaxConfigOld))
using (var writer = new StreamWriter(stream, EncodingHelpers.UTF8))
{
writer.Write(contents);
diff --git a/Content.Client/ScreenshotHook.cs b/Content.Client/ScreenshotHook.cs
index 238288ab5f..d1cf81bd96 100644
--- a/Content.Client/ScreenshotHook.cs
+++ b/Content.Client/ScreenshotHook.cs
@@ -57,7 +57,7 @@ namespace Content.Client
}
await using var file =
- _resourceManager.UserData.Open(BaseScreenshotPath / $"{filename}.png", FileMode.CreateNew);
+ _resourceManager.UserData.Open(BaseScreenshotPath / $"{filename}.png", FileMode.CreateNew, FileAccess.Read, FileShare.None);
await Task.Run(() =>
{
diff --git a/Content.Client/UserInterface/EscapeMenu.cs b/Content.Client/UserInterface/EscapeMenu.cs
index 3f085d1a8a..7b3ba644ce 100644
--- a/Content.Client/UserInterface/EscapeMenu.cs
+++ b/Content.Client/UserInterface/EscapeMenu.cs
@@ -1,5 +1,4 @@
-using Content.Client.Sandbox;
-using Robust.Client.Console;
+using Robust.Client.Console;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface.Controls;
@@ -15,16 +14,12 @@ namespace Content.Client.UserInterface
internal sealed class EscapeMenu : SS14Window
{
private readonly IClientConsole _console;
- private readonly ITileDefinitionManager __tileDefinitionManager;
+ private readonly ITileDefinitionManager _tileDefinitionManager;
private readonly IPlacementManager _placementManager;
private readonly IPrototypeManager _prototypeManager;
private readonly IResourceCache _resourceCache;
private readonly IConfigurationManager _configSystem;
private readonly ILocalizationManager _localizationManager;
-#pragma warning disable 649
- [Dependency] private readonly ISandboxManager _sandboxManager;
- [Dependency] private readonly IClientConGroupController _conGroupController;
-#pragma warning restore 649
private BaseButton DisconnectButton;
private BaseButton QuitButton;
@@ -41,7 +36,7 @@ namespace Content.Client.UserInterface
_configSystem = configSystem;
_localizationManager = localizationManager;
_console = console;
- __tileDefinitionManager = tileDefinitionManager;
+ _tileDefinitionManager = tileDefinitionManager;
_placementManager = placementManager;
_prototypeManager = prototypeManager;
_resourceCache = resourceCache;
diff --git a/Content.Client/UserInterface/StrippingMenu.cs b/Content.Client/UserInterface/StrippingMenu.cs
new file mode 100644
index 0000000000..3088bd4270
--- /dev/null
+++ b/Content.Client/UserInterface/StrippingMenu.cs
@@ -0,0 +1,64 @@
+using System;
+using Content.Client.UserInterface.Stylesheets;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Shared.Map;
+using Robust.Shared.Maths;
+
+namespace Content.Client.UserInterface
+{
+ public class StrippingMenu : SS14Window
+ {
+ protected override Vector2? CustomSize => new Vector2(400, 600);
+
+ private readonly VBoxContainer _vboxContainer;
+
+ public StrippingMenu(string title)
+ {
+ Title = title;
+
+ _vboxContainer = new VBoxContainer()
+ {
+ SizeFlagsVertical = SizeFlags.FillExpand,
+ SeparationOverride = 5,
+ };
+
+ Contents.AddChild(_vboxContainer);
+ }
+
+ public void ClearButtons()
+ {
+ _vboxContainer.DisposeAllChildren();
+ }
+
+ public void AddButton(string title, string name, Action onPressed)
+ {
+ var button = new Button()
+ {
+ Text = name,
+ StyleClasses = { StyleBase.ButtonOpenRight }
+ };
+
+ button.OnPressed += onPressed;
+
+ _vboxContainer.AddChild(new HBoxContainer()
+ {
+ SizeFlagsHorizontal = SizeFlags.FillExpand,
+ SeparationOverride = 5,
+ Children =
+ {
+ new Label()
+ {
+ Text = $"{title}:"
+ },
+ new Control()
+ {
+ SizeFlagsHorizontal = SizeFlags.FillExpand
+ },
+ button,
+ }
+ });
+ }
+ }
+}
diff --git a/Content.Server/Administration/WarpCommand.cs b/Content.Server/Administration/WarpCommand.cs
index 4942c6599b..e54bca1671 100644
--- a/Content.Server/Administration/WarpCommand.cs
+++ b/Content.Server/Administration/WarpCommand.cs
@@ -4,10 +4,12 @@ using Content.Server.GameObjects.Components.Markers;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Enums;
+using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
+using Robust.Shared.Maths;
namespace Content.Server.Administration
{
@@ -107,6 +109,10 @@ namespace Content.Server.Administration
if (found.GridID != GridId.Invalid)
{
player.AttachedEntity.Transform.GridPosition = found;
+ if (player.AttachedEntity.TryGetComponent(out ICollidableComponent collidable))
+ {
+ collidable.Stop();
+ }
}
else
{
diff --git a/Content.Server/Atmos/HighPressureMovementController.cs b/Content.Server/Atmos/HighPressureMovementController.cs
index 60086206b9..2e27f513ad 100644
--- a/Content.Server/Atmos/HighPressureMovementController.cs
+++ b/Content.Server/Atmos/HighPressureMovementController.cs
@@ -49,19 +49,23 @@ namespace Content.Server.Atmos
{
- if (maxForce > ThrowForce && throwTarget != GridCoordinates.InvalidGrid)
+ if (maxForce > ThrowForce)
{
- var moveForce = MathF.Min(maxForce * FloatMath.Clamp(moveProb, 0, 100) / 100f, 50f);
- var pos = throwTarget.Position - transform.GridPosition.Position;
- LinearVelocity = pos * moveForce;
- }
- else
- {
- var moveForce = MathF.Min(maxForce * FloatMath.Clamp(moveProb, 0, 100) / 100f, 25f);
- LinearVelocity = direction.ToVec() * moveForce;
- }
+ if (throwTarget != GridCoordinates.InvalidGrid)
+ {
+ var moveForce = maxForce * FloatMath.Clamp(moveProb, 0, 100) / 150f;
+ var pos = ((throwTarget.Position - transform.GridPosition.Position).Normalized + direction.ToVec()).Normalized;
+ LinearVelocity = pos * moveForce;
+ }
- pressureComponent.LastHighPressureMovementAirCycle = cycle;
+ else
+ {
+ var moveForce = MathF.Min(maxForce * FloatMath.Clamp(moveProb, 0, 100) / 2500f, 20f);
+ LinearVelocity = direction.ToVec() * moveForce;
+ }
+
+ pressureComponent.LastHighPressureMovementAirCycle = cycle;
+ }
}
}
@@ -72,7 +76,7 @@ namespace Content.Server.Atmos
if (ControlledComponent != null && !_physicsManager.IsWeightless(ControlledComponent.Owner.Transform.GridPosition))
{
LinearVelocity *= 0.85f;
- if (LinearVelocity.Length < 1f)
+ if (MathF.Abs(LinearVelocity.Length) < 1f)
Stop();
}
}
diff --git a/Content.Server/Atmos/IGridAtmosphereComponent.cs b/Content.Server/Atmos/IGridAtmosphereComponent.cs
index 430c674bce..5518881767 100644
--- a/Content.Server/Atmos/IGridAtmosphereComponent.cs
+++ b/Content.Server/Atmos/IGridAtmosphereComponent.cs
@@ -40,6 +40,11 @@ namespace Content.Server.Atmos
///
void Invalidate(MapIndices indices);
+ ///
+ /// Attempts to fix a sudden vacuum by creating gas.
+ ///
+ void FixVacuum(MapIndices indices);
+
///
/// Adds an active tile so it becomes processed every update until it becomes inactive.
/// Also makes the tile excited.
diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs
index eab7e6b9ad..8e634a1c7d 100644
--- a/Content.Server/Atmos/TileAtmosphere.cs
+++ b/Content.Server/Atmos/TileAtmosphere.cs
@@ -17,6 +17,7 @@ using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
+using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
@@ -78,7 +79,6 @@ namespace Content.Server.Atmos
[ViewVariables]
public Hotspot Hotspot;
- [ViewVariables]
private Direction _pressureDirection;
[ViewVariables]
@@ -106,6 +106,7 @@ namespace Content.Server.Atmos
GridIndex = gridIndex;
GridIndices = gridIndices;
Air = mixture;
+ ResetTileAtmosInfo();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -181,6 +182,8 @@ namespace Content.Server.Atmos
|| ContainerHelpers.IsInContainer(entity))
continue;
+ physics.WakeBody();
+
var pressureMovements = physics.EnsureController();
if (pressure.LastHighPressureMovementAirCycle < _gridAtmosphereComponent.UpdateCounter)
{
@@ -221,7 +224,8 @@ namespace Content.Server.Atmos
{
if (Air == null || (_tileAtmosInfo.LastCycle >= cycleNum)) return; // Already done.
- _tileAtmosInfo = new TileAtmosInfo();
+ ResetTileAtmosInfo();
+
var startingMoles = Air.TotalMoles;
var runAtmos = false;
@@ -264,7 +268,7 @@ namespace Content.Server.Atmos
{
if (adj?.Air == null) continue;
if(adj._tileAtmosInfo.LastQueueCycle == queueCycle) continue;
- adj._tileAtmosInfo = new TileAtmosInfo();
+ adj.ResetTileAtmosInfo();
adj._tileAtmosInfo.LastQueueCycle = queueCycle;
if(tileCount < Atmospherics.ZumosHardTileLimit)
@@ -328,7 +332,7 @@ namespace Content.Server.Atmos
var tile = tiles[i];
tile._tileAtmosInfo.FastDone = true;
if (!(tile._tileAtmosInfo.MoleDelta > 0)) continue;
- Direction eligibleAdjBits = 0;
+ var eligibleDirections = new List();
var amtEligibleAdj = 0;
foreach (var direction in Cardinal)
{
@@ -338,16 +342,17 @@ namespace Content.Server.Atmos
if (tile2._tileAtmosInfo.FastDone || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
continue;
- eligibleAdjBits |= direction;
+ eligibleDirections.Add(direction);
amtEligibleAdj++;
}
if (amtEligibleAdj <= 0)
continue; // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this.
+
var molesToMove = tile._tileAtmosInfo.MoleDelta / amtEligibleAdj;
foreach (var direction in Cardinal)
{
- if ((eligibleAdjBits & direction) == 0 ||
+ if (eligibleDirections.Contains(direction) ||
!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
tile.AdjustEqMovement(direction, molesToMove);
tile._tileAtmosInfo.MoleDelta -= molesToMove;
@@ -394,13 +399,10 @@ namespace Content.Server.Atmos
foreach (var direction in Cardinal)
{
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
- if (giver._tileAtmosInfo.MoleDelta <= 0)
- break; // We're done here now. Let's not do more work than needed.
-
- if (tile2?._tileAtmosInfo == null || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
- continue;
-
+ if (giver._tileAtmosInfo.MoleDelta <= 0) break; // We're done here now. Let's not do more work than needed.
+ if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
+
queue[queueLength++] = tile2;
tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
@@ -458,7 +460,7 @@ namespace Content.Server.Atmos
var queueLength = 0;
queue[queueLength++] = taker;
taker._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
- for (int i = 0; i < queueLength; i++)
+ for (var i = 0; i < queueLength; i++)
{
if (taker._tileAtmosInfo.MoleDelta >= 0)
break; // We're done here now. Let's not do more work than needed.
@@ -469,11 +471,8 @@ namespace Content.Server.Atmos
if (!tile._adjacentTiles.ContainsKey(direction)) continue;
var tile2 = tile._adjacentTiles[direction];
- if (taker._tileAtmosInfo.MoleDelta >= 0)
- break; // We're done here now. Let's not do more work than needed.
-
- if (tile2?._tileAtmosInfo == null || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
- continue;
+ if (taker._tileAtmosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed.
+ if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
queue[queueLength++] = tile2;
tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
@@ -504,16 +503,16 @@ namespace Content.Server.Atmos
for (var i = queueLength - 1; i >= 0; i--)
{
var tile = queue[i];
- if (tile._tileAtmosInfo.CurrentTransferAmount == 0 ||
- tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) continue;
- tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection,
- tile._tileAtmosInfo.CurrentTransferAmount);
+ if (tile._tileAtmosInfo.CurrentTransferAmount == 0 || tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid)
+ continue;
- if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection,
- out var adjacent))
- adjacent._tileAtmosInfo.CurrentTransferAmount +=
- tile._tileAtmosInfo.CurrentTransferAmount;
- tile._tileAtmosInfo.CurrentTransferAmount = 0;
+ tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
+
+ if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent))
+ {
+ adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
+ tile._tileAtmosInfo.CurrentTransferAmount = 0;
+ }
}
}
@@ -552,38 +551,34 @@ namespace Content.Server.Atmos
foreach (var direction in Cardinal)
{
var amount = _tileAtmosInfo[direction];
- transferDirections[direction] = amount;
if (amount == 0) continue;
+ transferDirections[direction] = amount;
_tileAtmosInfo[direction] = 0;
hasTransferDirs = true;
}
if (!hasTransferDirs) return;
- foreach (var direction in Cardinal)
+ foreach (var (direction, amount) in transferDirections)
{
- var amount = transferDirections[direction];
if (!_adjacentTiles.TryGetValue(direction, out var tile) || tile.Air == null) continue;
if (amount > 0)
{
- // Prevent infinite recursion.
- tile._tileAtmosInfo[direction.GetOpposite()] = 0;
-
if (Air.TotalMoles < amount)
- FinalizeEqNeighbors();
+ FinalizeEqNeighbors(transferDirections.Keys);
tile.Air.Merge(Air.Remove(amount));
UpdateVisuals();
tile.UpdateVisuals();
- ConsiderPressureDifference(tile, amount);
+ ConsiderPressureDifference(direction, amount);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void FinalizeEqNeighbors()
+ private void FinalizeEqNeighbors(IEnumerable directions)
{
- foreach (var direction in Cardinal)
+ foreach (var direction in directions)
{
var amount = _tileAtmosInfo[direction];
if(amount < 0 && _adjacentTiles.TryGetValue(direction, out var adjacent))
@@ -592,13 +587,13 @@ namespace Content.Server.Atmos
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ConsiderPressureDifference(TileAtmosphere tile, float difference)
+ private void ConsiderPressureDifference(Direction direction, float difference)
{
_gridAtmosphereComponent.AddHighPressureDelta(this);
if (difference > PressureDifference)
{
PressureDifference = difference;
- _pressureDirection = ((Vector2i) (GridIndices - tile.GridIndices)).GetCardinalDir();
+ _pressureDirection = difference < 0 ? direction.GetOpposite() : direction;
}
}
@@ -606,8 +601,14 @@ namespace Content.Server.Atmos
private void AdjustEqMovement(Direction direction, float molesToMove)
{
_tileAtmosInfo[direction] += molesToMove;
- if(direction != (Direction)(-1) && _adjacentTiles.TryGetValue(direction, out var adj))
- _adjacentTiles[direction]._tileAtmosInfo[direction.GetOpposite()] -= molesToMove;
+ if(direction != Direction.Invalid && _adjacentTiles.TryGetValue(direction, out var adj))
+ adj._tileAtmosInfo[direction.GetOpposite()] -= molesToMove;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void ResetTileAtmosInfo()
+ {
+ _tileAtmosInfo = new TileAtmosInfo {CurrentTransferDirection = Direction.Invalid};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -625,7 +626,7 @@ namespace Content.Server.Atmos
_currentCycle = fireCount;
var adjacentTileLength = 0;
- foreach (var (_, enemyTile) in _adjacentTiles)
+ foreach (var (direction, enemyTile) in _adjacentTiles)
{
// If the tile is null or has no air, we don't do anything
if(enemyTile?.Air == null) continue;
@@ -675,11 +676,11 @@ namespace Content.Server.Atmos
// Space wind!
if (difference > 0)
{
- ConsiderPressureDifference(enemyTile, difference);
+ ConsiderPressureDifference(direction, difference);
}
else
{
- enemyTile.ConsiderPressureDifference(this, -difference);
+ enemyTile.ConsiderPressureDifference(direction.GetOpposite(), -difference);
}
LastShareCheck();
@@ -929,11 +930,10 @@ namespace Content.Server.Atmos
var tiles = new List();
var spaceTiles = new List();
tiles.Add(this);
- _tileAtmosInfo = new TileAtmosInfo
- {
- LastQueueCycle = queueCycle,
- CurrentTransferDirection = Direction.Invalid
- };
+
+ ResetTileAtmosInfo();
+ _tileAtmosInfo.LastQueueCycle = queueCycle;
+
var tileCount = 1;
for (var i = 0; i < tileCount; i++)
{
@@ -947,16 +947,17 @@ namespace Content.Server.Atmos
}
else
{
- if (i > Atmospherics.ZumosHardTileLimit) continue;
+ if (i > Atmospherics.ZumosTileLimit) continue;
foreach (var direction in Cardinal)
{
- if (!_adjacentTiles.TryGetValue(direction, out var tile2)) continue;
+ if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
if (tile2?.Air == null) continue;
if (tile2._tileAtmosInfo.LastQueueCycle == queueCycle) continue;
tile.ConsiderFirelocks(tile2);
if (tile._adjacentTiles[direction]?.Air != null)
{
- tile2._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle};
+ tile2.ResetTileAtmosInfo();
+ tile2._tileAtmosInfo.LastQueueCycle = queueCycle;
tiles.Add(tile2);
tileCount++;
}
@@ -979,7 +980,7 @@ namespace Content.Server.Atmos
var tile = progressionOrder[i];
foreach (var direction in Cardinal)
{
- if (!_adjacentTiles.TryGetValue(direction, out var tile2)) continue;
+ if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
if (tile2?._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
if(tile2.Air.Immutable) continue;
@@ -992,14 +993,12 @@ namespace Content.Server.Atmos
}
}
- for (int i = 0; i < progressionCount; i++)
+ for (var i = progressionCount - 1; i >= 0; i--)
{
var tile = progressionOrder[i];
if (tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) continue;
- var hpdLength = _gridAtmosphereComponent.HighPressureDeltaCount;
- var inHdp = _gridAtmosphereComponent.HasHighPressureDelta(tile);
- if(!inHdp)
- _gridAtmosphereComponent.AddHighPressureDelta(tile);
+ _gridAtmosphereComponent.AddHighPressureDelta(tile);
+ _gridAtmosphereComponent.AddActiveTile(tile);
if (!tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var tile2) || tile2.Air == null) continue;
var sum = tile2.Air.TotalMoles;
totalGasesRemoved += sum;
@@ -1007,11 +1006,13 @@ namespace Content.Server.Atmos
tile2._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
tile.PressureDifference = tile._tileAtmosInfo.CurrentTransferAmount;
tile._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection;
+
if (tile2._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid)
{
tile2.PressureDifference = tile2._tileAtmosInfo.CurrentTransferAmount;
tile2._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection;
}
+
tile.Air.Clear();
tile.UpdateVisuals();
tile.HandleDecompressionFloorRip(sum);
@@ -1020,7 +1021,8 @@ namespace Content.Server.Atmos
private void HandleDecompressionFloorRip(float sum)
{
- if (sum > 20 && _robustRandom.Prob(FloatMath.Clamp(sum / 100, 0.005f, 0.5f)))
+ var chance = FloatMath.Clamp(sum / 500, 0.005f, 0.5f);
+ if (sum > 20 && _robustRandom.Prob(chance))
_gridAtmosphereComponent.PryTile(GridIndices);
}
@@ -1061,14 +1063,19 @@ namespace Content.Server.Atmos
{
foreach (var direction in Cardinal)
{
- if(!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction)))
- _adjacentTiles[direction] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction));
+ if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction)))
+ {
+ var adjacent = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction));
+ _adjacentTiles[direction] = adjacent;
+ adjacent.UpdateAdjacent(direction.GetOpposite());
+ }
}
}
public void UpdateAdjacent(Direction direction)
{
- _adjacentTiles[direction] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction));
+ if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction)))
+ _adjacentTiles[direction] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction));
}
private void LastShareCheck()
diff --git a/Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs b/Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs
index 478dd540cb..07c6ed66e9 100644
--- a/Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs
+++ b/Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs
@@ -40,7 +40,7 @@ namespace Content.Server.GameObjects.Components.Atmos
base.ExposeData(serializer);
serializer.DataField(ref _airBlocked, "airBlocked", true);
- serializer.DataField(ref _fixVacuum, "fixVacuum", false);
+ serializer.DataField(ref _fixVacuum, "fixVacuum", true);
}
public override void Initialize()
@@ -57,15 +57,6 @@ namespace Content.Server.GameObjects.Components.Atmos
UpdatePosition();
}
- public override void OnRemove()
- {
- base.OnRemove();
-
- _airBlocked = false;
-
- UpdatePosition();
- }
-
public void MapInit()
{
_snapGrid.OnPositionChanged += OnTransformMove;
@@ -80,6 +71,11 @@ namespace Content.Server.GameObjects.Components.Atmos
_airBlocked = false;
_snapGrid.OnPositionChanged -= OnTransformMove;
+
+ if(_fixVacuum)
+ EntitySystem.Get().GetGridAtmosphere(Owner.Transform.GridID)?
+ .FixVacuum(_snapGrid.Position);
+
UpdatePosition();
}
diff --git a/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs b/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs
index c7207ffa4e..05ba999b0b 100644
--- a/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs
+++ b/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs
@@ -28,7 +28,6 @@ namespace Content.Server.GameObjects.Components.Atmos
[RegisterComponent, Serializable]
public class GridAtmosphereComponent : Component, IGridAtmosphereComponent
{
- [Robust.Shared.IoC.Dependency] private IGameTiming _gameTiming = default!;
[Robust.Shared.IoC.Dependency] private IMapManager _mapManager = default!;
///
@@ -154,13 +153,14 @@ namespace Content.Server.GameObjects.Components.Atmos
if (tile == null)
{
tile = new TileAtmosphere(this, _grid.Index, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C});
- _tiles.Add(indices, tile);
+ _tiles[indices] = tile;
}
if (IsSpace(indices))
{
tile.Air = new GasMixture(GetVolumeForCells(1));
tile.Air.MarkImmutable();
+ _tiles[indices] = tile;
} else if (IsAirBlocked(indices))
{
@@ -174,17 +174,7 @@ namespace Content.Server.GameObjects.Components.Atmos
{
if (tile.Air == null && obs.FixVacuum)
{
- var adjacent = GetAdjacentTiles(indices);
- tile.Air = new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C};
-
- var ratio = 1f / adjacent.Count;
-
- foreach (var (direction, adj) in adjacent)
- {
- var mix = adj.Air.RemoveRatio(ratio);
- tile.Air.Merge(mix);
- adj.Air.Merge(mix);
- }
+ FixVacuum(tile.GridIndices);
}
}
@@ -206,6 +196,25 @@ namespace Content.Server.GameObjects.Components.Atmos
_invalidatedCoords.Clear();
}
+ ///
+ public void FixVacuum(MapIndices indices)
+ {
+ var tile = GetTile(indices);
+ if (tile?.GridIndex != _grid.Index) return;
+ var adjacent = GetAdjacentTiles(indices);
+ tile.Air = new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C};
+ _tiles[indices] = tile;
+
+ var ratio = 1f / adjacent.Count;
+
+ foreach (var (direction, adj) in adjacent)
+ {
+ var mix = adj.Air.RemoveRatio(ratio);
+ tile.Air.Merge(mix);
+ adj.Air.Merge(mix);
+ }
+ }
+
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddActiveTile(TileAtmosphere tile)
diff --git a/Content.Server/GameObjects/Components/Chemistry/VaporComponent.cs b/Content.Server/GameObjects/Components/Chemistry/VaporComponent.cs
index cc477cd8e9..066cf21836 100644
--- a/Content.Server/GameObjects/Components/Chemistry/VaporComponent.cs
+++ b/Content.Server/GameObjects/Components/Chemistry/VaporComponent.cs
@@ -16,9 +16,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
[RegisterComponent]
class VaporComponent : Component, ICollideBehavior
{
-#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager = default!;
-#pragma warning enable 649
public override string Name => "Vapor";
[ViewVariables]
@@ -66,7 +64,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
{
var worldBounds = collidable.WorldAABB;
var mapGrid = _mapManager.GetGrid(Owner.Transform.GridID);
-
+
var tiles = mapGrid.GetTilesIntersecting(worldBounds);
var amount = _transferAmount / ReagentUnit.New(tiles.Count());
foreach (var tile in tiles)
diff --git a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
index 8e5ca778a1..d29c7d93c9 100644
--- a/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
+++ b/Content.Server/GameObjects/Components/Construction/ConstructionComponent.cs
@@ -2,8 +2,6 @@
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -16,9 +14,6 @@ namespace Content.Server.GameObjects.Components.Construction
[RegisterComponent]
public class ConstructionComponent : Component, IExamine
{
-#pragma warning disable 649
- [Dependency] private readonly ILocalizationManager _loc;
-#pragma warning restore 649
///
public override string Name => "Construction";
diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalTubeComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalTubeComponent.cs
index 9f9d0efe21..29d5132925 100644
--- a/Content.Server/GameObjects/Components/Disposal/DisposalTubeComponent.cs
+++ b/Content.Server/GameObjects/Components/Disposal/DisposalTubeComponent.cs
@@ -217,7 +217,7 @@ namespace Content.Server.GameObjects.Components.Disposal
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
- serializer.DataField(ref _clangSound, "clangSound", "/Audio/effects/clang.ogg");
+ serializer.DataField(ref _clangSound, "clangSound", "/Audio/Effects/clang.ogg");
}
public override void Initialize()
diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs
index bc4d887457..89a6049b66 100644
--- a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs
+++ b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs
@@ -94,7 +94,7 @@ namespace Content.Server.GameObjects.Components.Disposal
collidable.Anchored;
[ViewVariables]
- private State State => _pressure >= 1 ? State.Ready : State.Pressurizing;
+ private PressureState State => _pressure >= 1 ? PressureState.Ready : PressureState.Pressurizing;
[ViewVariables]
private bool Engaged
diff --git a/Content.Server/GameObjects/Components/GUI/HandsComponent.cs b/Content.Server/GameObjects/Components/GUI/HandsComponent.cs
index 0ea707e19c..ea75c7f382 100644
--- a/Content.Server/GameObjects/Components/GUI/HandsComponent.cs
+++ b/Content.Server/GameObjects/Components/GUI/HandsComponent.cs
@@ -42,6 +42,8 @@ namespace Content.Server.GameObjects.Components.GUI
private string? _activeHand;
private uint _nextHand;
+ public event Action? OnItemChanged;
+
[ViewVariables(VVAccess.ReadWrite)]
public string? ActiveHand
{
@@ -60,6 +62,8 @@ namespace Content.Server.GameObjects.Components.GUI
[ViewVariables] private readonly List _hands = new List();
+ public IEnumerable Hands => _hands.Select(h => h.Name);
+
// Mostly arbitrary.
public const float PickupRange = 2;
@@ -105,6 +109,12 @@ namespace Content.Server.GameObjects.Components.GUI
return GetHand(handName)?.Entity?.GetComponent();
}
+ public bool TryGetItem(string handName, [MaybeNullWhen(false)] out ItemComponent item)
+ {
+ item = GetItem(handName);
+ return item != null;
+ }
+
public ItemComponent? GetActiveHand => ActiveHand == null
? null
: GetItem(ActiveHand);
@@ -136,6 +146,8 @@ namespace Content.Server.GameObjects.Components.GUI
{
if (PutInHand(item, hand, false))
{
+ OnItemChanged?.Invoke();
+
return true;
}
}
@@ -156,6 +168,7 @@ namespace Content.Server.GameObjects.Components.GUI
if (success)
{
item.Owner.Transform.LocalPosition = Vector2.Zero;
+ OnItemChanged?.Invoke();
}
_entitySystemManager.GetEntitySystem().HandSelectedInteraction(Owner, item.Owner);
@@ -250,6 +263,8 @@ namespace Content.Server.GameObjects.Components.GUI
container.Insert(item.Owner);
}
+ OnItemChanged?.Invoke();
+
Dirty();
return true;
}
@@ -300,6 +315,8 @@ namespace Content.Server.GameObjects.Components.GUI
container.Insert(item.Owner);
}
+ OnItemChanged?.Invoke();
+
Dirty();
return true;
}
@@ -364,6 +381,8 @@ namespace Content.Server.GameObjects.Components.GUI
throw new InvalidOperationException();
}
+ OnItemChanged?.Invoke();
+
Dirty();
return true;
}
@@ -415,6 +434,8 @@ namespace Content.Server.GameObjects.Components.GUI
ActiveHand ??= name;
+ OnItemChanged?.Invoke();
+
Dirty();
}
@@ -435,6 +456,8 @@ namespace Content.Server.GameObjects.Components.GUI
_activeHand = _hands.FirstOrDefault()?.Name;
}
+ OnItemChanged?.Invoke();
+
Dirty();
}
@@ -645,13 +668,13 @@ namespace Content.Server.GameObjects.Components.GUI
Dirty();
- if (!message.Entity.TryGetComponent(out IPhysicsComponent physics))
+ if (!message.Entity.TryGetComponent(out ICollidableComponent collidable))
{
return;
}
// set velocity to zero
- physics.Stop();
+ collidable.Stop();
return;
}
}
diff --git a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs
index aa9f03ac10..1d1e52b6a2 100644
--- a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs
+++ b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs
@@ -33,9 +33,13 @@ namespace Content.Server.GameObjects.Components.GUI
#pragma warning restore 649
[ViewVariables]
- private readonly Dictionary SlotContainers = new Dictionary();
+ private readonly Dictionary _slotContainers = new Dictionary();
- private KeyValuePair? HoverEntity;
+ private KeyValuePair? _hoverEntity;
+
+ public IEnumerable Slots => _slotContainers.Keys;
+
+ public event Action OnItemChanged;
public override void Initialize()
{
@@ -43,7 +47,7 @@ namespace Content.Server.GameObjects.Components.GUI
foreach (var slotName in InventoryInstance.SlotMasks)
{
- if (slotName != Slots.NONE)
+ if (slotName != EquipmentSlotDefines.Slots.NONE)
{
AddSlot(slotName);
}
@@ -58,7 +62,7 @@ namespace Content.Server.GameObjects.Components.GUI
{
var multiplier = 1f;
- foreach (var (slot, containerSlot) in SlotContainers)
+ foreach (var (slot, containerSlot) in _slotContainers)
{
foreach (var entity in containerSlot.ContainedEntities)
{
@@ -81,7 +85,7 @@ namespace Content.Server.GameObjects.Components.GUI
{
var multiplier = 1f;
- foreach (var (slot, containerSlot) in SlotContainers)
+ foreach (var (slot, containerSlot) in _slotContainers)
{
foreach (var entity in containerSlot.ContainedEntities)
{
@@ -99,7 +103,7 @@ namespace Content.Server.GameObjects.Components.GUI
bool IEffectBlocker.CanSlip()
{
if(Owner.TryGetComponent(out InventoryComponent inventoryComponent) &&
- inventoryComponent.TryGetSlotItem(Slots.SHOES, out ItemComponent shoes)
+ inventoryComponent.TryGetSlotItem(EquipmentSlotDefines.Slots.SHOES, out ItemComponent shoes)
)
{
return EffectBlockerSystem.CanSlip(shoes.Owner);
@@ -110,7 +114,7 @@ namespace Content.Server.GameObjects.Components.GUI
public override void OnRemove()
{
- var slots = SlotContainers.Keys.ToList();
+ var slots = _slotContainers.Keys.ToList();
foreach (var slot in slots)
{
RemoveSlot(slot);
@@ -140,15 +144,15 @@ namespace Content.Server.GameObjects.Components.GUI
}
public T GetSlotItem(Slots slot) where T : ItemComponent
{
- if (!SlotContainers.ContainsKey(slot))
+ if (!_slotContainers.ContainsKey(slot))
{
return null;
}
- var containedEntity = SlotContainers[slot].ContainedEntity;
+ var containedEntity = _slotContainers[slot].ContainedEntity;
if (containedEntity?.Deleted == true)
{
- SlotContainers[slot] = null;
+ _slotContainers[slot] = null;
containedEntity = null;
Dirty();
}
@@ -169,7 +173,7 @@ namespace Content.Server.GameObjects.Components.GUI
///
/// The slot to put the item in.
/// The item to insert into the slot.
- /// The translated reason why the item cannot be equiped, if this function returns false. Can be null.
+ /// The translated reason why the item cannot be equipped, if this function returns false. Can be null.
/// True if the item was successfully inserted, false otherwise.
public bool Equip(Slots slot, ItemComponent item, out string reason)
{
@@ -184,7 +188,7 @@ namespace Content.Server.GameObjects.Components.GUI
return false;
}
- var inventorySlot = SlotContainers[slot];
+ var inventorySlot = _slotContainers[slot];
if (!inventorySlot.Insert(item.Owner))
{
return false;
@@ -192,6 +196,8 @@ namespace Content.Server.GameObjects.Components.GUI
_entitySystemManager.GetEntitySystem().EquippedInteraction(Owner, item.Owner, slot);
+ OnItemChanged?.Invoke();
+
Dirty();
return true;
@@ -239,7 +245,7 @@ namespace Content.Server.GameObjects.Components.GUI
reason = Loc.GetString("You can't equip this!");
}
- return pass && SlotContainers[slot].CanInsert(item.Owner);
+ return pass && _slotContainers[slot].CanInsert(item.Owner);
}
public bool CanEquip(Slots slot, ItemComponent item) => CanEquip(slot, item, out var _);
@@ -258,7 +264,7 @@ namespace Content.Server.GameObjects.Components.GUI
return false;
}
- var inventorySlot = SlotContainers[slot];
+ var inventorySlot = _slotContainers[slot];
var item = inventorySlot.ContainedEntity.GetComponent();
if (!inventorySlot.Remove(inventorySlot.ContainedEntity))
{
@@ -271,6 +277,8 @@ namespace Content.Server.GameObjects.Components.GUI
_entitySystemManager.GetEntitySystem().UnequippedInteraction(Owner, item.Owner, slot);
+ OnItemChanged?.Invoke();
+
Dirty();
return true;
@@ -288,7 +296,7 @@ namespace Content.Server.GameObjects.Components.GUI
if (!ActionBlockerSystem.CanUnequip(Owner))
return false;
- var InventorySlot = SlotContainers[slot];
+ var InventorySlot = _slotContainers[slot];
return InventorySlot.ContainedEntity != null && InventorySlot.CanRemove(InventorySlot.ContainedEntity);
}
@@ -307,7 +315,12 @@ namespace Content.Server.GameObjects.Components.GUI
}
Dirty();
- return SlotContainers[slot] = ContainerManagerComponent.Create(GetSlotString(slot), Owner);
+
+ _slotContainers[slot] = ContainerManagerComponent.Create(GetSlotString(slot), Owner);
+
+ OnItemChanged?.Invoke();
+
+ return _slotContainers[slot];
}
///
@@ -331,7 +344,10 @@ namespace Content.Server.GameObjects.Components.GUI
"Unable to remove slot as the contained clothing could not be dropped");
}
- SlotContainers.Remove(slot);
+ _slotContainers.Remove(slot);
+
+ OnItemChanged?.Invoke();
+
Dirty();
}
@@ -342,7 +358,7 @@ namespace Content.Server.GameObjects.Components.GUI
/// True if the slot exists, false otherwise.
public bool HasSlot(Slots slot)
{
- return SlotContainers.ContainsKey(slot);
+ return _slotContainers.ContainsKey(slot);
}
///
@@ -354,7 +370,7 @@ namespace Content.Server.GameObjects.Components.GUI
// make sure this is one of our containers.
// Technically the correct way would be to enumerate the possible slot names
// comparing with this container, but I might as well put the dictionary to good use.
- if (!(container is ContainerSlot slot) || !SlotContainers.ContainsValue(slot))
+ if (!(container is ContainerSlot slot) || !_slotContainers.ContainsValue(slot))
return;
if (entity.TryGetComponent(out ItemComponent itemComp))
@@ -362,6 +378,8 @@ namespace Content.Server.GameObjects.Components.GUI
itemComp.RemovedFromSlot();
}
+ OnItemChanged?.Invoke();
+
Dirty();
}
@@ -417,7 +435,7 @@ namespace Content.Server.GameObjects.Components.GUI
if (activeHand != null && GetSlotItem(msg.Inventoryslot) == null)
{
var canEquip = CanEquip(msg.Inventoryslot, activeHand, out var reason);
- HoverEntity = new KeyValuePair(msg.Inventoryslot, (activeHand.Owner.Uid, canEquip));
+ _hoverEntity = new KeyValuePair(msg.Inventoryslot, (activeHand.Owner.Uid, canEquip));
Dirty();
}
@@ -476,7 +494,7 @@ namespace Content.Server.GameObjects.Components.GUI
public override ComponentState GetComponentState()
{
var list = new List>();
- foreach (var (slot, container) in SlotContainers)
+ foreach (var (slot, container) in _slotContainers)
{
if (container.ContainedEntity != null)
{
@@ -484,8 +502,8 @@ namespace Content.Server.GameObjects.Components.GUI
}
}
- var hover = HoverEntity;
- HoverEntity = null;
+ var hover = _hoverEntity;
+ _hoverEntity = null;
return new InventoryComponentState(list, hover);
}
@@ -497,7 +515,7 @@ namespace Content.Server.GameObjects.Components.GUI
return;
}
- foreach (var slot in SlotContainers.Values.ToList())
+ foreach (var slot in _slotContainers.Values.ToList())
{
foreach (var entity in slot.ContainedEntities)
{
diff --git a/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs b/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs
new file mode 100644
index 0000000000..e045fd2b5a
--- /dev/null
+++ b/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs
@@ -0,0 +1,372 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Content.Server.GameObjects.Components.Items.Storage;
+using Content.Server.GameObjects.EntitySystems.DoAfter;
+using Content.Server.Interfaces;
+using Content.Shared.GameObjects.Components.GUI;
+using Content.Shared.GameObjects.Components.Inventory;
+using Content.Shared.GameObjects.EntitySystems;
+using Content.Shared.Interfaces.GameObjects.Components;
+using Robust.Server.GameObjects.Components.UserInterface;
+using Robust.Server.Interfaces.GameObjects;
+using Robust.Server.Interfaces.Player;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects.Systems;
+using Robust.Shared.Interfaces.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+using Robust.Shared.Log;
+using Robust.Shared.ViewVariables;
+using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
+
+namespace Content.Server.GameObjects.Components.GUI
+{
+ [RegisterComponent]
+ public sealed class StrippableComponent : SharedStrippableComponent, IDragDrop
+ {
+ [Dependency] private IServerNotifyManager _notifyManager = default!;
+
+ public const float StripDelay = 2f;
+
+ [ViewVariables]
+ private BoundUserInterface _userInterface;
+
+ private InventoryComponent _inventoryComponent;
+ private HandsComponent _handsComponent;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _userInterface = Owner.GetComponent().GetBoundUserInterface(StrippingUiKey.Key);
+ _userInterface.OnReceiveMessage += HandleUserInterfaceMessage;
+
+ _inventoryComponent = Owner.GetComponent();
+ _handsComponent = Owner.GetComponent();
+
+ _inventoryComponent.OnItemChanged += UpdateSubscribed;
+
+ // Initial update.
+ UpdateSubscribed();
+ }
+
+ private void UpdateSubscribed()
+ {
+ var inventory = GetInventorySlots();
+ var hands = GetHandSlots();
+
+ _userInterface.SetState(new StrippingBoundUserInterfaceState(inventory, hands));
+ }
+
+ public bool CanDragDrop(DragDropEventArgs eventArgs)
+ {
+ return eventArgs.User.HasComponent()
+ && eventArgs.Target != eventArgs.Dropped && eventArgs.Target == eventArgs.User;
+ }
+
+ public bool DragDrop(DragDropEventArgs eventArgs)
+ {
+ if (!eventArgs.User.TryGetComponent(out IActorComponent actor)) return false;
+
+ OpenUserInterface(actor.playerSession);
+ return true;
+ }
+
+ private Dictionary GetInventorySlots()
+ {
+ var dictionary = new Dictionary();
+
+ foreach (var slot in _inventoryComponent.Slots)
+ {
+ dictionary[slot] = _inventoryComponent.GetSlotItem(slot)?.Owner.Name ?? "None";
+ }
+
+ return dictionary;
+ }
+
+ private Dictionary GetHandSlots()
+ {
+ var dictionary = new Dictionary();
+
+ foreach (var hand in _handsComponent.Hands)
+ {
+ dictionary[hand] = _handsComponent.GetItem(hand)?.Owner.Name ?? "None";
+ }
+
+ return dictionary;
+ }
+
+ private void OpenUserInterface(IPlayerSession session)
+ {
+ _userInterface.Open(session);
+ }
+
+ ///
+ /// Places item in user's active hand to an inventory slot.
+ ///
+ private async void PlaceActiveHandItemInInventory(IEntity user, Slots slot)
+ {
+ var inventory = Owner.GetComponent();
+ var userHands = user.GetComponent();
+ var item = userHands.GetActiveHand;
+
+ bool Check()
+ {
+ if (!ActionBlockerSystem.CanInteract(user))
+ return false;
+
+ if (item == null)
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("You aren't holding anything!"));
+ return false;
+ }
+
+ if (!userHands.CanDrop(userHands.ActiveHand!))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("You can't drop that!"));
+ return false;
+ }
+
+ if (!inventory.HasSlot(slot))
+ return false;
+
+ if (inventory.TryGetSlotItem(slot, out ItemComponent _))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} already {0:have} something there!", Owner));
+ return false;
+ }
+
+ if (!inventory.CanEquip(slot, item))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} cannot equip that there!", Owner));
+ return false;
+ }
+
+ return true;
+ }
+
+ var doAfterSystem = EntitySystem.Get();
+
+ var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
+ {
+ ExtraCheck = Check,
+ BreakOnStun = true,
+ BreakOnDamage = true,
+ BreakOnTargetMove = true,
+ BreakOnUserMove = true,
+ NeedHand = true,
+ };
+
+ var result = await doAfterSystem.DoAfter(doAfterArgs);
+ if (result != DoAfterStatus.Finished) return;
+
+ userHands.Drop(item!.Owner, false);
+ inventory.Equip(slot, item!.Owner);
+
+ UpdateSubscribed();
+ }
+
+ ///
+ /// Places item in user's active hand in one of the entity's hands.
+ ///
+ private async void PlaceActiveHandItemInHands(IEntity user, string hand)
+ {
+ var hands = Owner.GetComponent();
+ var userHands = user.GetComponent();
+ var item = userHands.GetActiveHand;
+
+ bool Check()
+ {
+ if (!ActionBlockerSystem.CanInteract(user))
+ return false;
+
+ if (item == null)
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("You aren't holding anything!"));
+ return false;
+ }
+
+ if (!userHands.CanDrop(userHands.ActiveHand!))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("You can't drop that!"));
+ return false;
+ }
+
+ if (!hands.HasHand(hand))
+ return false;
+
+ if (hands.TryGetItem(hand, out var _))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} already {0:have} something there!", Owner));
+ return false;
+ }
+
+ if (!hands.CanPutInHand(item, hand))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} cannot put that there!", Owner));
+ return false;
+ }
+
+ return true;
+ }
+
+ var doAfterSystem = EntitySystem.Get();
+
+ var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
+ {
+ ExtraCheck = Check,
+ BreakOnStun = true,
+ BreakOnDamage = true,
+ BreakOnTargetMove = true,
+ BreakOnUserMove = true,
+ NeedHand = true,
+ };
+
+ var result = await doAfterSystem.DoAfter(doAfterArgs);
+ if (result != DoAfterStatus.Finished) return;
+
+ userHands.Drop(hand, false);
+ hands.PutInHand(item, hand, false);
+ UpdateSubscribed();
+ }
+
+ ///
+ /// Takes an item from the inventory and places it in the user's active hand.
+ ///
+ private async void TakeItemFromInventory(IEntity user, Slots slot)
+ {
+ var inventory = Owner.GetComponent();
+ var userHands = user.GetComponent();
+
+ bool Check()
+ {
+ if (!ActionBlockerSystem.CanInteract(user))
+ return false;
+
+ if (!inventory.HasSlot(slot))
+ return false;
+
+ if (!inventory.TryGetSlotItem(slot, out ItemComponent itemToTake))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} {0:have} nothing there!", Owner));
+ return false;
+ }
+
+ if (!inventory.CanUnequip(slot))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} cannot unequip that!", Owner));
+ return false;
+ }
+
+ return true;
+ }
+
+ var doAfterSystem = EntitySystem.Get();
+
+ var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
+ {
+ ExtraCheck = Check,
+ BreakOnStun = true,
+ BreakOnDamage = true,
+ BreakOnTargetMove = true,
+ BreakOnUserMove = true,
+ };
+
+ var result = await doAfterSystem.DoAfter(doAfterArgs);
+ if (result != DoAfterStatus.Finished) return;
+
+ var item = inventory.GetSlotItem(slot);
+ inventory.Unequip(slot);
+ userHands.PutInHandOrDrop(item);
+ UpdateSubscribed();
+ }
+
+ ///
+ /// Takes an item from a hand and places it in the user's active hand.
+ ///
+ private async void TakeItemFromHands(IEntity user, string hand)
+ {
+ var hands = Owner.GetComponent();
+ var userHands = user.GetComponent();
+
+ bool Check()
+ {
+ if (!ActionBlockerSystem.CanInteract(user))
+ return false;
+
+ if (!hands.HasHand(hand))
+ return false;
+
+ if (!hands.TryGetItem(hand, out var heldItem))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} {0:have} nothing there!", Owner));
+ return false;
+ }
+
+ if (!hands.CanDrop(hand))
+ {
+ _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} cannot drop that!", Owner));
+ return false;
+ }
+
+ return true;
+ }
+
+ var doAfterSystem = EntitySystem.Get();
+
+ var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
+ {
+ ExtraCheck = Check,
+ BreakOnStun = true,
+ BreakOnDamage = true,
+ BreakOnTargetMove = true,
+ BreakOnUserMove = true,
+ };
+
+ var result = await doAfterSystem.DoAfter(doAfterArgs);
+ if (result != DoAfterStatus.Finished) return;
+
+ var item = hands.GetItem(hand);
+ hands.Drop(hand, false);
+ userHands.PutInHandOrDrop(item);
+ UpdateSubscribed();
+ }
+
+ private void HandleUserInterfaceMessage(ServerBoundUserInterfaceMessage obj)
+ {
+ var user = obj.Session.AttachedEntity;
+ if (user == null || !(user.TryGetComponent(out HandsComponent userHands))) return;
+
+ var placingItem = userHands.GetActiveHand != null;
+
+ switch (obj.Message)
+ {
+ case StrippingInventoryButtonPressed inventoryMessage:
+ var inventory = Owner.GetComponent();
+
+ if (inventory.TryGetSlotItem(inventoryMessage.Slot, out ItemComponent _))
+ placingItem = false;
+
+ if(placingItem)
+ PlaceActiveHandItemInInventory(user, inventoryMessage.Slot);
+ else
+ TakeItemFromInventory(user, inventoryMessage.Slot);
+ break;
+ case StrippingHandButtonPressed handMessage:
+ var hands = Owner.GetComponent();
+
+ if (hands.TryGetItem(handMessage.Hand, out _))
+ placingItem = false;
+
+ if(placingItem)
+ PlaceActiveHandItemInHands(user, handMessage.Hand);
+ else
+ TakeItemFromHands(user, handMessage.Hand);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs
index 79bd216244..73d1fd09ca 100644
--- a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs
+++ b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs
@@ -10,7 +10,6 @@ using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
-using Content.Shared.Physics;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.EntitySystems;
@@ -50,8 +49,6 @@ namespace Content.Server.GameObjects.Components.Items.Storage
private bool _occludesLight;
private bool _open;
private bool _isWeldedShut;
- private int _collisionMaskStorage;
- private int _collisionLayerStorage;
[ViewVariables]
protected Container Contents;
@@ -202,18 +199,13 @@ namespace Content.Server.GameObjects.Components.Items.Storage
{
if (!_isCollidableWhenOpen && Owner.TryGetComponent(out var collidableComponent))
{
- var physShape = collidableComponent.PhysicsShapes[0];
if (Open)
{
- _collisionMaskStorage = physShape.CollisionMask;
- physShape.CollisionMask = (int)CollisionGroup.Impassable;
- _collisionLayerStorage = physShape.CollisionLayer;
- physShape.CollisionLayer = (int)CollisionGroup.None;
+ collidableComponent.Hard = false;
}
else
{
- physShape.CollisionMask = _collisionMaskStorage;
- physShape.CollisionLayer = _collisionLayerStorage;
+ collidableComponent.Hard = true;
}
}
diff --git a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs
index daf1f94d07..67eca347f2 100644
--- a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs
+++ b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs
@@ -14,7 +14,6 @@ using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
-using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
@@ -29,7 +28,6 @@ namespace Content.Server.GameObjects.Components.Items.Storage
public override uint? NetID => ContentNetIDs.ITEM;
#pragma warning disable 649
- [Dependency] private readonly IRobustRandom _robustRandom;
[Dependency] private readonly IMapManager _mapManager;
#pragma warning restore 649
@@ -93,7 +91,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
return false;
}
- if (Owner.TryGetComponent(out PhysicsComponent physics) &&
+ if (Owner.TryGetComponent(out CollidableComponent physics) &&
physics.Anchored)
{
return false;
diff --git a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs
index 20271f0b23..d148490bd9 100644
--- a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs
+++ b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs
@@ -132,12 +132,12 @@ namespace Content.Server.GameObjects.Components.Mobs
if (!HasMind)
{
message.AddMarkup(!dead
- ? $"[color=red]" + Loc.GetString("{0:They} are totally catatonic. The stresses of life in deep-space must have been too much for {0:them}. Any recovery is unlikely.", Owner) + "[/color]"
+ ? $"[color=red]" + Loc.GetString("{0:They} {0:are} totally catatonic. The stresses of life in deep-space must have been too much for {0:them}. Any recovery is unlikely.", Owner) + "[/color]"
: $"[color=purple]" + Loc.GetString("{0:Their} soul has departed.", Owner) + "[/color]");
}
else if (Mind?.Session == null)
{
- message.AddMarkup("[color=yellow]" + Loc.GetString("{0:They} have a blank, absent-minded stare and appears completely unresponsive to anything. {0:They} may snap out of it soon.", Owner) + "[/color]");
+ message.AddMarkup("[color=yellow]" + Loc.GetString("{0:They} {0:have} a blank, absent-minded stare and appears completely unresponsive to anything. {0:They} may snap out of it soon.", Owner) + "[/color]");
}
}
}
diff --git a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs
index 0c89727f0c..26b450ca47 100644
--- a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs
+++ b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs
@@ -42,9 +42,9 @@ namespace Content.Server.GameObjects.Components.Movement
{
base.Initialize();
- // This component requires a physics component.
- if (!Owner.HasComponent())
- Owner.AddComponent();
+ // This component requires a collidable component.
+ if (!Owner.HasComponent())
+ Owner.AddComponent();
}
///
diff --git a/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs
index 62063854f2..85bb04b84b 100644
--- a/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs
+++ b/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs
@@ -71,22 +71,15 @@ namespace Content.Server.GameObjects.Components.Movement
_entityManager.TryGetEntity(grid.GridEntityId, out var gridEntity))
{
//TODO: Switch to shuttle component
- if (!gridEntity.TryGetComponent(out IPhysicsComponent physComp))
+ if (!gridEntity.TryGetComponent(out ICollidableComponent collidable))
{
- physComp = gridEntity.AddComponent();
- physComp.Mass = 1;
+ collidable = gridEntity.AddComponent();
+ collidable.Mass = 1;
+ collidable.CanCollide = true;
+ collidable.PhysicsShapes.Add(new PhysShapeGrid(grid));
}
- //TODO: Is this always true?
- if (!gridEntity.HasComponent())
- {
- var collideComp = gridEntity.AddComponent();
- collideComp.CanCollide = true;
- //collideComp.IsHardCollidable = true;
- collideComp.PhysicsShapes.Add(new PhysShapeGrid(grid));
- }
-
- var controller = physComp.EnsureController();
+ var controller = collidable.EnsureController();
controller.Push(CalcNewVelocity(direction, enabled), CurrentWalkSpeed);
}
}
diff --git a/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs b/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs
index 00613c2b96..9b633dc688 100644
--- a/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs
+++ b/Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs
@@ -92,9 +92,9 @@ namespace Content.Server.GameObjects.Components.Projectiles
}
if (!entity.Deleted && entity.TryGetComponent(out CameraRecoilComponent recoilComponent)
- && Owner.TryGetComponent(out IPhysicsComponent physicsComponent))
+ && Owner.TryGetComponent(out ICollidableComponent collidableComponent))
{
- var direction = physicsComponent.LinearVelocity.Normalized;
+ var direction = collidableComponent.LinearVelocity.Normalized;
recoilComponent.Kick(direction);
}
}
diff --git a/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs b/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs
index 2ae7c7f5c9..b072072bc5 100644
--- a/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs
+++ b/Content.Server/GameObjects/Components/Projectiles/ThrownItemComponent.cs
@@ -88,7 +88,7 @@ namespace Content.Server.GameObjects.Components.Projectiles
public void StartThrow(Vector2 direction, float speed)
{
- var comp = Owner.GetComponent();
+ var comp = Owner.GetComponent();
comp.Status = BodyStatus.InAir;
var controller = comp.EnsureController();
diff --git a/Content.Server/GameObjects/Components/Rotatable/RotatableComponent.cs b/Content.Server/GameObjects/Components/Rotatable/RotatableComponent.cs
index 73194471c8..ffbcfbe4cc 100644
--- a/Content.Server/GameObjects/Components/Rotatable/RotatableComponent.cs
+++ b/Content.Server/GameObjects/Components/Rotatable/RotatableComponent.cs
@@ -21,9 +21,9 @@ namespace Content.Server.GameObjects.Components.Rotatable
private void TryRotate(IEntity user, Angle angle)
{
- if (Owner.TryGetComponent(out IPhysicsComponent physics))
+ if (Owner.TryGetComponent(out ICollidableComponent collidable))
{
- if (physics.Anchored)
+ if (collidable.Anchored)
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user, _localizationManager.GetString("It's stuck."));
return;
diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs
index b3d6b8e924..4b0553fe2b 100644
--- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs
+++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs
@@ -5,7 +5,6 @@ using Content.Server.GameObjects.Components.Damage;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Projectiles;
using Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition;
-using Content.Server.Interfaces;
using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Content.Shared.GameObjects.EntitySystems;
@@ -43,7 +42,6 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
#pragma warning disable 649
[Dependency] private IGameTiming _gameTiming;
[Dependency] private IRobustRandom _robustRandom;
- [Dependency] private readonly IServerNotifyManager _notifyManager;
#pragma warning restore 649
public override FireRateSelector FireRateSelector => _fireRateSelector;
@@ -385,15 +383,15 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
projectileAngle = angle;
}
- var physicsComponent = projectile.GetComponent();
- physicsComponent.Status = BodyStatus.InAir;
+ var collidableComponent = projectile.GetComponent();
+ collidableComponent.Status = BodyStatus.InAir;
projectile.Transform.GridPosition = Owner.Transform.GridPosition;
var projectileComponent = projectile.GetComponent();
projectileComponent.IgnoreEntity(shooter);
projectile
- .GetComponent()
+ .GetComponent()
.EnsureController()
.LinearVelocity = projectileAngle.ToVec() * velocity;
diff --git a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs
index ea9ca44ec1..a9a7899300 100644
--- a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs
@@ -27,7 +27,6 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
#pragma warning disable 649
[Dependency] private IMapManager _mapManager;
- [Dependency] private IEntityManager _entityManager;
[Dependency] private IPauseManager _pauseManager;
#pragma warning restore 649
private PathfindingSystem _pathfindingSystem;
diff --git a/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs b/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs
index 71acf54c52..327104a148 100644
--- a/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs
@@ -17,18 +17,14 @@ namespace Content.Server.GameObjects.EntitySystems
[UsedImplicitly]
public class AtmosphereSystem : EntitySystem
{
-#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPauseManager _pauseManager = default!;
-#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
_mapManager.TileChanged += OnTileChanged;
- EntityQuery = new MultipleTypeEntityQuery(new List(){typeof(IGridAtmosphereComponent)});
}
public IGridAtmosphereComponent? GetGridAtmosphere(GridId gridId)
@@ -36,7 +32,7 @@ namespace Content.Server.GameObjects.EntitySystems
// TODO Return space grid atmosphere for invalid grids or grids with no atmos
var grid = _mapManager.GetGrid(gridId);
- if (!_entityManager.TryGetEntity(grid.GridEntityId, out var gridEnt)) return null;
+ if (!EntityManager.TryGetEntity(grid.GridEntityId, out var gridEnt)) return null;
return gridEnt.TryGetComponent(out IGridAtmosphereComponent atmos) ? atmos : null;
}
@@ -45,13 +41,12 @@ namespace Content.Server.GameObjects.EntitySystems
{
base.Update(frameTime);
- foreach (var gridEnt in RelevantEntities)
+ foreach (var (mapGridComponent, gridAtmosphereComponent) in EntityManager.ComponentManager.EntityQuery())
{
- var grid = gridEnt.GetComponent();
- if (_pauseManager.IsGridPaused(grid.GridIndex))
+ if (_pauseManager.IsGridPaused(mapGridComponent.GridIndex))
continue;
- gridEnt.GetComponent().Update(frameTime);
+ gridAtmosphereComponent.Update(frameTime);
}
}
diff --git a/Content.Server/GameObjects/EntitySystems/Click/ExamineSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/ExamineSystem.cs
index 53fa77c516..1b83b0b3fe 100644
--- a/Content.Server/GameObjects/EntitySystems/Click/ExamineSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/Click/ExamineSystem.cs
@@ -4,6 +4,7 @@ using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
+using Robust.Shared.Localization;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems.Click
@@ -19,7 +20,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
static ExamineSystem()
{
_entityNotFoundMessage = new FormattedMessage();
- _entityNotFoundMessage.AddText("That entity doesn't exist");
+ _entityNotFoundMessage.AddText(Loc.GetString("That entity doesn't exist"));
}
public override void Initialize()
diff --git a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs
index 35c99acd98..84250d4e91 100644
--- a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs
@@ -58,23 +58,13 @@ namespace Content.Server.GameObjects.EntitySystems
public override void Update(float frameTime)
{
- foreach (var entity in RelevantEntities)
+ foreach (var (moverComponent, collidableComponent) in EntityManager.ComponentManager.EntityQuery())
{
+ var entity = moverComponent.Owner;
if (_pauseManager.IsEntityPaused(entity))
- {
continue;
- }
- var mover = entity.GetComponent();
- var physics = entity.GetComponent();
- if (entity.TryGetComponent(out var collider))
- {
- UpdateKinematics(entity.Transform, mover, physics, collider);
- }
- else
- {
- UpdateKinematics(entity.Transform, mover, physics);
- }
+ UpdateKinematics(entity.Transform, moverComponent, collidableComponent);
}
}
@@ -93,7 +83,7 @@ namespace Content.Server.GameObjects.EntitySystems
ev.Entity.RemoveComponent();
}
- if (ev.Entity.TryGetComponent(out IPhysicsComponent physics) &&
+ if (ev.Entity.TryGetComponent(out ICollidableComponent physics) &&
physics.TryGetController(out MoverController controller))
{
controller.StopMoving();
diff --git a/Content.Server/GameObjects/EntitySystems/VerbSystem.cs b/Content.Server/GameObjects/EntitySystems/VerbSystem.cs
index b0720ccfb3..6de086226a 100644
--- a/Content.Server/GameObjects/EntitySystems/VerbSystem.cs
+++ b/Content.Server/GameObjects/EntitySystems/VerbSystem.cs
@@ -2,6 +2,7 @@
using System.Reflection;
using Content.Shared.GameObjects.Verbs;
using Robust.Server.Interfaces.Player;
+using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
@@ -37,6 +38,12 @@ namespace Content.Server.GameObjects.EntitySystems
var session = eventArgs.SenderSession;
var userEntity = session.AttachedEntity;
+ if (userEntity == null)
+ {
+ Logger.Warning($"{nameof(UseVerb)} called by player {session} with no attached entity.");
+ return;
+ }
+
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
{
if ($"{component.GetType()}:{verb.GetType()}" != use.VerbKey)
@@ -44,14 +51,14 @@ namespace Content.Server.GameObjects.EntitySystems
continue;
}
- if (verb.RequireInteractionRange)
+ if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
{
- var distanceSquared = (userEntity.Transform.WorldPosition - entity.Transform.WorldPosition)
- .LengthSquared;
- if (distanceSquared > VerbUtility.InteractionRangeSquared)
- {
- break;
- }
+ break;
+ }
+
+ if (verb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity))
+ {
+ break;
}
verb.Activate(userEntity, component);
@@ -65,14 +72,15 @@ namespace Content.Server.GameObjects.EntitySystems
continue;
}
- if (globalVerb.RequireInteractionRange)
+ if (globalVerb.RequireInteractionRange &&
+ !VerbUtility.InVerbUseRange(userEntity, entity))
{
- var distanceSquared = (userEntity.Transform.WorldPosition - entity.Transform.WorldPosition)
- .LengthSquared;
- if (distanceSquared > VerbUtility.InteractionRangeSquared)
- {
- break;
- }
+ break;
+ }
+
+ if (globalVerb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity))
+ {
+ break;
}
globalVerb.Activate(userEntity, entity);
@@ -92,6 +100,12 @@ namespace Content.Server.GameObjects.EntitySystems
var userEntity = player.AttachedEntity;
+ if (userEntity == null)
+ {
+ Logger.Warning($"{nameof(UseVerb)} called by player {player} with no attached entity.");
+ return;
+ }
+
var data = new List();
//Get verbs, component dependent.
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
@@ -99,6 +113,9 @@ namespace Content.Server.GameObjects.EntitySystems
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
continue;
+ if (verb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity))
+ continue;
+
var verbData = verb.GetData(userEntity, component);
if (verbData.IsInvisible)
continue;
@@ -113,6 +130,9 @@ namespace Content.Server.GameObjects.EntitySystems
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
continue;
+ if (globalVerb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity))
+ continue;
+
var verbData = globalVerb.GetData(userEntity, entity);
if (verbData.IsInvisible)
continue;
diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs
index 50365c6569..bd2f43a13d 100644
--- a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs
+++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs
@@ -62,7 +62,7 @@ namespace Content.Server.GameTicking.GamePresets
for (var i = 0; i < numTraitors; i++)
{
IPlayerSession traitor;
- if(prefList.Count() == 0)
+ if(prefList.Count == 0)
{
traitor = _random.PickAndTake(list);
Logger.InfoS("preset", "Insufficient preferred traitors, picking at random.");
diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs
index 8c3ed03f1b..bef68e027d 100644
--- a/Content.Server/GameTicking/GameTicker.cs
+++ b/Content.Server/GameTicking/GameTicker.cs
@@ -264,7 +264,7 @@ namespace Content.Server.GameTicking
// Spawn everybody in!
foreach (var (player, job) in assignedJobs)
{
- SpawnPlayer(player, job, false);
+ SpawnPlayer(player, profiles[player.Name], job, false);
}
// Time to start the preset.
@@ -344,7 +344,7 @@ namespace Content.Server.GameTicking
if (LobbyEnabled)
_playerJoinLobby(targetPlayer);
else
- SpawnPlayer(targetPlayer);
+ SpawnPlayerAsync(targetPlayer);
}
public void MakeObserve(IPlayerSession player)
@@ -358,7 +358,7 @@ namespace Content.Server.GameTicking
{
if (!_playersInLobby.ContainsKey(player)) return;
- SpawnPlayer(player, jobId);
+ SpawnPlayerAsync(player, jobId);
}
public void ToggleReady(IPlayerSession player, bool ready)
@@ -620,7 +620,7 @@ namespace Content.Server.GameTicking
_playerJoinLobby(player);
}
-
+
EntitySystem.Get().ResettingCleanup();
EntitySystem.Get().ResettingCleanup();
EntitySystem.Get().ResetLayouts();
@@ -684,13 +684,13 @@ namespace Content.Server.GameTicking
return;
}
- SpawnPlayer(session);
+ SpawnPlayerAsync(session);
}
else
{
if (data.Mind.CurrentEntity == null)
{
- SpawnPlayer(session);
+ SpawnPlayerAsync(session);
}
else
{
@@ -744,14 +744,22 @@ namespace Content.Server.GameTicking
}, _updateShutdownCts.Token);
}
- private async void SpawnPlayer(IPlayerSession session, string jobId = null, bool lateJoin = true)
+ private async void SpawnPlayerAsync(IPlayerSession session, string jobId = null, bool lateJoin = true)
{
- _playerJoinGame(session);
-
var character = (HumanoidCharacterProfile) (await _prefsManager
.GetPreferencesAsync(session.SessionId.Username))
.SelectedCharacter;
+ SpawnPlayer(session, character, jobId, lateJoin);
+ }
+
+ private void SpawnPlayer(IPlayerSession session,
+ HumanoidCharacterProfile character,
+ string jobId = null,
+ bool lateJoin = true)
+ {
+ _playerJoinGame(session);
+
var data = session.ContentData();
data.WipeMind();
data.Mind = new Mind(session.SessionId)
diff --git a/Content.Server/GlobalVerbs/ControlMobVerb.cs b/Content.Server/GlobalVerbs/ControlMobVerb.cs
index 2b69c30c46..390094d763 100644
--- a/Content.Server/GlobalVerbs/ControlMobVerb.cs
+++ b/Content.Server/GlobalVerbs/ControlMobVerb.cs
@@ -13,6 +13,7 @@ namespace Content.Server.GlobalVerbs
public class ControlMobVerb : GlobalVerb
{
public override bool RequireInteractionRange => false;
+ public override bool BlockedByContainers => false;
public override void GetData(IEntity user, IEntity target, VerbData data)
{
diff --git a/Content.Server/GlobalVerbs/RejuvenateVerb.cs b/Content.Server/GlobalVerbs/RejuvenateVerb.cs
index 11b11e8c2a..5f074e085b 100644
--- a/Content.Server/GlobalVerbs/RejuvenateVerb.cs
+++ b/Content.Server/GlobalVerbs/RejuvenateVerb.cs
@@ -16,6 +16,7 @@ namespace Content.Server.GlobalVerbs
class RejuvenateVerb : GlobalVerb
{
public override bool RequireInteractionRange => false;
+ public override bool BlockedByContainers => false;
public override void GetData(IEntity user, IEntity target, VerbData data)
{
diff --git a/Content.Server/Interfaces/GameObjects/Components/Items/IHandsComponent.cs b/Content.Server/Interfaces/GameObjects/Components/Items/IHandsComponent.cs
index d631716cc9..e716533285 100644
--- a/Content.Server/Interfaces/GameObjects/Components/Items/IHandsComponent.cs
+++ b/Content.Server/Interfaces/GameObjects/Components/Items/IHandsComponent.cs
@@ -1,6 +1,11 @@
-using System;
+#nullable enable
+using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using Content.Server.GameObjects;
+using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
+using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Server.GameObjects.Components.Container;
@@ -12,10 +17,20 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items
{
public interface IHandsComponent : ISharedHandsComponent
{
+ ///
+ /// Invoked when the hand contents changes or when a hand is added/removed.
+ ///
+ event Action? OnItemChanged;
+
+ ///
+ /// The hands in this component.
+ ///
+ IEnumerable Hands { get; }
+
///
/// The hand name of the currently active hand.
///
- string ActiveHand { get; set; }
+ string? ActiveHand { get; set; }
///
/// Enumerates over every held item.
@@ -27,12 +42,20 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items
///
/// The name of the hand to get.
/// The item in the held, null if no item is held
- ItemComponent GetItem(string handName);
+ ItemComponent? GetItem(string handName);
+
+ ///
+ /// Attempts to get an item in a hand.
+ ///
+ /// The name of the hand to get.
+ /// The item in the held, null if no item is held
+ /// Whether it was holding an item
+ bool TryGetItem(string handName, [MaybeNullWhen(false)] out ItemComponent item);
///
/// Gets item held by the current active hand
///
- ItemComponent GetActiveHand { get; }
+ ItemComponent? GetActiveHand { get; }
///
/// Puts an item into any empty hand, preferring the active hand.
@@ -78,7 +101,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items
///
/// true if the entity is held, false otherwise
///
- bool TryHand(IEntity entity, out string handName);
+ bool TryHand(IEntity entity, [MaybeNullWhen(false)] out string handName);
///
/// Drops the item contained in the slot to the same position as our entity.
diff --git a/Content.Server/Throw/ThrowHelper.cs b/Content.Server/Throw/ThrowHelper.cs
index 6b62531a7f..ad8e76b4bf 100644
--- a/Content.Server/Throw/ThrowHelper.cs
+++ b/Content.Server/Throw/ThrowHelper.cs
@@ -85,7 +85,7 @@ namespace Content.Server.Throw
projComp.StartThrow(angle.ToVec(), spd);
if (throwSourceEnt != null &&
- throwSourceEnt.TryGetComponent(out var physics) &&
+ throwSourceEnt.TryGetComponent(out var physics) &&
physics.TryGetController(out MoverController mover))
{
var physicsMgr = IoCManager.Resolve();
@@ -136,7 +136,7 @@ namespace Content.Server.Throw
var distance = (targetLoc.ToMapPos(mapManager) - sourceLoc.ToMapPos(mapManager)).Length;
var throwDuration = ThrownItemComponent.DefaultThrowTime;
var mass = 1f;
- if (thrownEnt.TryGetComponent(out IPhysicsComponent physicsComponent))
+ if (thrownEnt.TryGetComponent(out ICollidableComponent physicsComponent))
{
mass = physicsComponent.Mass;
}
diff --git a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalUnitComponent.cs b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalUnitComponent.cs
index 1ce14ae17f..9bcd7edbf0 100644
--- a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalUnitComponent.cs
+++ b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalUnitComponent.cs
@@ -51,7 +51,7 @@ namespace Content.Shared.GameObjects.Components.Disposal
}
[Serializable, NetSerializable]
- public enum State
+ public enum PressureState
{
Ready,
Pressurizing
diff --git a/Content.Shared/GameObjects/Components/GUI/SharedStrippableComponent.cs b/Content.Shared/GameObjects/Components/GUI/SharedStrippableComponent.cs
new file mode 100644
index 0000000000..bcc9b2fc7c
--- /dev/null
+++ b/Content.Shared/GameObjects/Components/GUI/SharedStrippableComponent.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using Content.Shared.GameObjects.Components.Inventory;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects.Components.UserInterface;
+using Robust.Shared.Serialization;
+using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
+
+namespace Content.Shared.GameObjects.Components.GUI
+{
+ public class SharedStrippableComponent : Component
+ {
+ public override string Name => "Strippable";
+
+ [NetSerializable, Serializable]
+ public enum StrippingUiKey
+ {
+ Key,
+ }
+ }
+
+ [NetSerializable, Serializable]
+ public class StrippingInventoryButtonPressed : BoundUserInterfaceMessage
+ {
+ public Slots Slot { get; }
+
+ public StrippingInventoryButtonPressed(Slots slot)
+ {
+ Slot = slot;
+ }
+ }
+
+ [NetSerializable, Serializable]
+ public class StrippingHandButtonPressed : BoundUserInterfaceMessage
+ {
+ public string Hand { get; }
+
+ public StrippingHandButtonPressed(string hand)
+ {
+ Hand = hand;
+ }
+ }
+
+ [NetSerializable, Serializable]
+ public class StrippingBoundUserInterfaceState : BoundUserInterfaceState
+ {
+ public Dictionary Inventory { get; }
+ public Dictionary Hands { get; }
+
+ public StrippingBoundUserInterfaceState(Dictionary inventory, Dictionary hands)
+ {
+ Inventory = inventory;
+ Hands = hands;
+ }
+ }
+}
diff --git a/Content.Shared/GameObjects/Components/Movement/SharedPlayerInputMoverComponent.cs b/Content.Shared/GameObjects/Components/Movement/SharedPlayerInputMoverComponent.cs
index da88fdd468..9c74d666bc 100644
--- a/Content.Shared/GameObjects/Components/Movement/SharedPlayerInputMoverComponent.cs
+++ b/Content.Shared/GameObjects/Components/Movement/SharedPlayerInputMoverComponent.cs
@@ -142,11 +142,11 @@ namespace Content.Shared.GameObjects.Components.Movement
///
public override void OnAdd()
{
- // This component requires that the entity has a PhysicsComponent.
- if (!Owner.HasComponent())
+ // This component requires that the entity has a CollidableComponent.
+ if (!Owner.HasComponent())
Logger.Error(
$"[ECS] {Owner.Prototype?.Name} - {nameof(SharedPlayerInputMoverComponent)} requires" +
- $" {nameof(IPhysicsComponent)}. ");
+ $" {nameof(ICollidableComponent)}. ");
base.OnAdd();
}
diff --git a/Content.Shared/GameObjects/Components/Movement/SharedSlipperyComponent.cs b/Content.Shared/GameObjects/Components/Movement/SharedSlipperyComponent.cs
index 4181a2e36d..4d05195dba 100644
--- a/Content.Shared/GameObjects/Components/Movement/SharedSlipperyComponent.cs
+++ b/Content.Shared/GameObjects/Components/Movement/SharedSlipperyComponent.cs
@@ -50,13 +50,12 @@ namespace Content.Shared.GameObjects.Components.Movement
|| _slipped.Contains(entity.Uid)
|| !entity.TryGetComponent(out SharedStunnableComponent stun)
|| !entity.TryGetComponent(out ICollidableComponent otherBody)
- || !entity.TryGetComponent(out IPhysicsComponent otherPhysics)
|| !Owner.TryGetComponent(out ICollidableComponent body))
{
return false;
}
- if (otherPhysics.LinearVelocity.Length < RequiredSlipSpeed || stun.KnockedDown)
+ if (otherBody.LinearVelocity.Length < RequiredSlipSpeed || stun.KnockedDown)
{
return false;
}
diff --git a/Content.Shared/GameObjects/EntitySystems/ExamineSystemShared.cs b/Content.Shared/GameObjects/EntitySystems/ExamineSystemShared.cs
index 0e09c98af2..71e0190d8d 100644
--- a/Content.Shared/GameObjects/EntitySystems/ExamineSystemShared.cs
+++ b/Content.Shared/GameObjects/EntitySystems/ExamineSystemShared.cs
@@ -1,5 +1,7 @@
-using Content.Shared.GameObjects.Components.Mobs;
+using System;
+using Content.Shared.GameObjects.Components.Mobs;
using JetBrains.Annotations;
+using Robust.Shared.Containers;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
@@ -22,6 +24,15 @@ namespace Content.Shared.GameObjects.EntitySystems
public const float ExamineRangeSquared = ExamineRange * ExamineRange;
protected const float ExamineDetailsRange = 3f;
+ private static bool IsInDetailsRange(IEntity examiner, IEntity entity)
+ {
+ return Get()
+ .InRangeUnobstructed(examiner.Transform.MapPosition, entity.Transform.MapPosition,
+ ExamineDetailsRange, predicate: entity0 => entity0 == examiner || entity0 == entity,
+ ignoreInsideBlocker: true) &&
+ examiner.IsInSameOrNoContainer(entity);
+ }
+
[Pure]
protected static bool CanExamine(IEntity examiner, IEntity examined)
{
@@ -40,9 +51,16 @@ namespace Content.Shared.GameObjects.EntitySystems
return false;
}
+ Func predicate = entity => entity == examiner || entity == examined;
+
+ if (ContainerHelpers.TryGetContainer(examiner, out var container))
+ {
+ predicate += entity => entity == container.Owner;
+ }
+
return Get()
.InRangeUnobstructed(examiner.Transform.MapPosition, examined.Transform.MapPosition,
- ExamineRange, predicate: entity => entity == examiner || entity == examined, ignoreInsideBlocker:true);
+ ExamineRange, predicate: predicate, ignoreInsideBlocker:true);
}
public static FormattedMessage GetExamineText(IEntity entity, IEntity examiner)
@@ -60,15 +78,11 @@ namespace Content.Shared.GameObjects.EntitySystems
message.PushColor(Color.DarkGray);
- var inDetailsRange = Get()
- .InRangeUnobstructed(examiner.Transform.MapPosition, entity.Transform.MapPosition,
- ExamineDetailsRange, predicate: entity0 => entity0 == examiner || entity0 == entity, ignoreInsideBlocker: true);
-
//Add component statuses from components that report one
foreach (var examineComponent in entity.GetAllComponents())
{
var subMessage = new FormattedMessage();
- examineComponent.Examine(subMessage, inDetailsRange);
+ examineComponent.Examine(subMessage, IsInDetailsRange(examiner, entity));
if (subMessage.Tags.Count == 0)
continue;
diff --git a/Content.Shared/GameObjects/EntitySystems/SharedMoverSystem.cs b/Content.Shared/GameObjects/EntitySystems/SharedMoverSystem.cs
index 8c2cdcc9c3..7c9a4537a2 100644
--- a/Content.Shared/GameObjects/EntitySystems/SharedMoverSystem.cs
+++ b/Content.Shared/GameObjects/EntitySystems/SharedMoverSystem.cs
@@ -29,8 +29,6 @@ namespace Content.Shared.GameObjects.EntitySystems
{
base.Initialize();
- EntityQuery = new TypeEntityQuery(typeof(IMoverComponent));
-
var moveUpCmdHandler = new MoverDirInputCmdHandler(Direction.North);
var moveLeftCmdHandler = new MoverDirInputCmdHandler(Direction.West);
var moveRightCmdHandler = new MoverDirInputCmdHandler(Direction.East);
@@ -54,18 +52,17 @@ namespace Content.Shared.GameObjects.EntitySystems
base.Shutdown();
}
- protected void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, IPhysicsComponent physics,
- ICollidableComponent? collider = null)
+ protected void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, ICollidableComponent collidable)
{
- physics.EnsureController();
+ collidable.EnsureController();
var weightless = !transform.Owner.HasComponent() &&
_physicsManager.IsWeightless(transform.GridPosition);
- if (weightless && collider != null)
+ if (weightless)
{
// No gravity: is our entity touching anything?
- var touching = IsAroundCollider(transform, mover, collider);
+ var touching = IsAroundCollider(transform, mover, collidable);
if (!touching)
{
@@ -78,18 +75,16 @@ namespace Content.Shared.GameObjects.EntitySystems
var combined = walkDir + sprintDir;
if (combined.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner) && !weightless)
{
- if (physics.TryGetController(out MoverController controller))
+ if (collidable.TryGetController(out MoverController controller))
{
controller.StopMoving();
}
}
else
{
- //Console.WriteLine($"{IoCManager.Resolve().TickStamp}: {combined}");
-
if (weightless)
{
- if (physics.TryGetController(out MoverController controller))
+ if (collidable.TryGetController(out MoverController controller))
{
controller.Push(combined, mover.CurrentPushSpeed);
}
@@ -99,12 +94,13 @@ namespace Content.Shared.GameObjects.EntitySystems
}
var total = walkDir * mover.CurrentWalkSpeed + sprintDir * mover.CurrentSprintSpeed;
- //Console.WriteLine($"{walkDir} ({mover.CurrentWalkSpeed}) + {sprintDir} ({mover.CurrentSprintSpeed}): {total}");
- {if (physics.TryGetController(out MoverController controller))
{
- controller.Move(total, 1);
- }}
+ if (collidable.TryGetController(out MoverController controller))
+ {
+ controller.Move(total, 1);
+ }
+ }
transform.LocalRotation = total.GetDir().ToAngle();
diff --git a/Content.Shared/GameObjects/Verbs/GlobalVerb.cs b/Content.Shared/GameObjects/Verbs/GlobalVerb.cs
index ad5ebed628..e14830fab3 100644
--- a/Content.Shared/GameObjects/Verbs/GlobalVerb.cs
+++ b/Content.Shared/GameObjects/Verbs/GlobalVerb.cs
@@ -20,6 +20,12 @@ namespace Content.Shared.GameObjects.Verbs
///
public virtual bool RequireInteractionRange => true;
+ ///
+ /// If true, this verb requires both the user and the entity on which
+ /// this verb resides to be in the same container or no container.
+ ///
+ public virtual bool BlockedByContainers => true;
+
///
/// Gets the visible verb data for the user.
///
diff --git a/Content.Shared/GameObjects/Verbs/Verb.cs b/Content.Shared/GameObjects/Verbs/Verb.cs
index b4b1f27751..99cf50b24d 100644
--- a/Content.Shared/GameObjects/Verbs/Verb.cs
+++ b/Content.Shared/GameObjects/Verbs/Verb.cs
@@ -20,6 +20,12 @@ namespace Content.Shared.GameObjects.Verbs
///
public virtual bool RequireInteractionRange => true;
+ ///
+ /// If true, this verb requires both the user and the entity on which
+ /// this verb resides to be in the same container or no container.
+ ///
+ public virtual bool BlockedByContainers => true;
+
///
/// Gets the visible verb data for the user.
///
diff --git a/Content.Shared/Health/BodySystem/Mechanism/MechanismPrototype.cs b/Content.Shared/Health/BodySystem/Mechanism/MechanismPrototype.cs
index 3a37046b20..acf2711f28 100644
--- a/Content.Shared/Health/BodySystem/Mechanism/MechanismPrototype.cs
+++ b/Content.Shared/Health/BodySystem/Mechanism/MechanismPrototype.cs
@@ -18,7 +18,6 @@ namespace Content.Shared.Health.BodySystem.Mechanism
private string _name;
private string _description;
private string _examineMessage;
- private string _spritePath;
private string _rsiPath;
private string _rsiState;
private int _durability;
diff --git a/Content.Shared/Physics/CollisionGroup.cs b/Content.Shared/Physics/CollisionGroup.cs
index 8ef86b5a6f..0834d69787 100644
--- a/Content.Shared/Physics/CollisionGroup.cs
+++ b/Content.Shared/Physics/CollisionGroup.cs
@@ -20,6 +20,7 @@ namespace Content.Shared.Physics
VaultImpassable = 1 << 3, // 8 Things that cannot be jumped over, not half walls or tables
SmallImpassable = 1 << 4, // 16 Things a smaller object - a cat, a crab - can't go through - a wall, but not a computer terminal or a table
Clickable = 1 << 5, // 32 Temporary "dummy" layer to ensure that objects can still be clicked even if they don't collide with anything (you can't interact with objects that have no layer, including items)
+ GhostImpassable = 1 << 6, // 64 Things impassible by ghosts/observers, ie blessed tiles or forcefields
MapGrid = MapGridHelpers.CollisionGroup, // Map grids, like shuttles. This is the actual grid itself, not the walls or other entities connected to the grid.
diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml
index eba5ba58d4..4b6fea353d 100644
--- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml
+++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml
@@ -7,6 +7,12 @@
- type: Mind
- type: Physics
mass: 5
+ - type: Collidable
+ shapes:
+ - !type:PhysShapeAabb
+ bounds: "-0.35,-0.35,0.35,0.35"
+ mask:
+ - GhostImpassable
- type: Eye
zoom: 0.5, 0.5
drawFov: false
diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml
index b5a18678fd..9259c28043 100644
--- a/Resources/Prototypes/Entities/Mobs/Species/human.yml
+++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml
@@ -144,6 +144,12 @@
- type: Pullable
- type: CanSeeGases
- type: DoAfter
+ - type: Strippable
+ - type: UserInterface
+ interfaces:
+ - key: enum.StrippingUiKey.Key
+ type: StrippableBoundUserInterface
+
- type: entity
save: false
diff --git a/RobustToolbox b/RobustToolbox
index 5819e5ee92..1934428c95 160000
--- a/RobustToolbox
+++ b/RobustToolbox
@@ -1 +1 @@
-Subproject commit 5819e5ee92b287da6306807f8e05efc457c06a6c
+Subproject commit 1934428c95d44210cfc9c155c7d15405d4a374c4
diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings
index f9a53158a2..c93412bf8d 100644
--- a/SpaceStation14.sln.DotSettings
+++ b/SpaceStation14.sln.DotSettings
@@ -71,10 +71,12 @@
True
True
True
+ True
True
True
True
True
+ True
True
True
True