Merge pull request #2 from space-wizards/master

This commit is contained in:
Vince
2020-08-16 13:06:57 +02:00
committed by GitHub
67 changed files with 1038 additions and 335 deletions

View File

@@ -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;
}
}
/// <summary>
/// Remove a DoAfter without showing a cancellation graphic.
/// </summary>

View File

@@ -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<HandsComponent>()
&& eventArgs.Target != eventArgs.Dragged && eventArgs.Target == eventArgs.User;
}
public bool ClientCanDrag(CanDragEventArgs eventArgs)
{
return true;
}
}
}

View File

@@ -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<Slots, string> Inventory { get; private set; }
public Dictionary<string, string> 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();
}
}
}

View File

@@ -23,6 +23,7 @@ namespace Content.Client.GameObjects.Components.Items
[Dependency] private readonly IGameHud _gameHud = default!;
#pragma warning restore 649
/// <inheritdoc />
private readonly List<Hand> _hands = new List<Hand>();
[ViewVariables] public IReadOnlyList<Hand> Hands => _hands;

View File

@@ -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<CameraRecoilComponent>())
{
var recoil = entity.GetComponent<CameraRecoilComponent>();
recoil.FrameUpdate(frameTime);
}
}

View File

@@ -43,6 +43,25 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
}
/// <summary>
/// Called when the mind is detached from an entity
/// </summary>
/// 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();
}
/// <summary>
/// Add the necessary control for a DoAfter progress bar.
/// </summary>

View File

@@ -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)
{

View File

@@ -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<InstrumentComponent>())
{
entity.GetComponent<InstrumentComponent>().Update(frameTime);
instrumentComponent.Update(frameTime);
}
}
}

View File

@@ -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<MarkerComponent>();
}
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<MarkerComponent>())
{
entity.GetComponent<MarkerComponent>().UpdateVisibility();
markerComponent.UpdateVisibility();
}
}
}

View File

@@ -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<MeleeLungeComponent>();
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
foreach (var entity in RelevantEntities)
foreach (var meleeLungeComponent in EntityManager.ComponentManager.EntityQuery<MeleeLungeComponent>())
{
entity.GetComponent<MeleeLungeComponent>().Update(frameTime);
meleeLungeComponent.Update(frameTime);
}
}
}

View File

@@ -24,16 +24,15 @@ namespace Content.Client.GameObjects.EntitySystems
public override void Initialize()
{
SubscribeNetworkEvent<PlayMeleeWeaponAnimationMessage>(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<MeleeWeaponArcAnimationComponent>())
{
entity.GetComponent<MeleeWeaponArcAnimationComponent>().Update(frameTime);
arcAnimationComponent.Update(frameTime);
}
}

View File

@@ -30,11 +30,10 @@ namespace Content.Client.GameObjects.EntitySystems
return;
}
var physics = playerEnt.GetComponent<IPhysicsComponent>();
playerEnt.TryGetComponent(out ICollidableComponent? collidable);
physics.Predict = true;
var collidable = playerEnt.GetComponent<ICollidableComponent>();
collidable.Predict = true;
UpdateKinematics(playerEnt.Transform, mover, physics, collidable);
UpdateKinematics(playerEnt.Transform, mover, collidable);
}
public override void Update(float frameTime)

View File

@@ -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<ClientStatusEffectsComponent>())
{
entity.GetComponent<ClientStatusEffectsComponent>().FrameUpdate(frameTime);
clientStatusEffectsComponent.FrameUpdate(frameTime);
}
}
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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(() =>
{

View File

@@ -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;

View File

@@ -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<BaseButton.ButtonEventArgs> 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,
}
});
}
}
}

View File

@@ -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
{

View File

@@ -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();
}
}

View File

@@ -40,6 +40,11 @@ namespace Content.Server.Atmos
/// <param name="indices"></param>
void Invalidate(MapIndices indices);
/// <summary>
/// Attempts to fix a sudden vacuum by creating gas.
/// </summary>
void FixVacuum(MapIndices indices);
/// <summary>
/// Adds an active tile so it becomes processed every update until it becomes inactive.
/// Also makes the tile excited.

View File

@@ -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<HighPressureMovementController>();
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<Direction>();
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<Direction> 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<TileAtmosphere>();
var spaceTiles = new List<TileAtmosphere>();
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()

View File

@@ -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<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?
.FixVacuum(_snapGrid.Position);
UpdatePosition();
}

View File

@@ -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!;
/// <summary>
@@ -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();
}
/// <inheritdoc />
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);
}
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddActiveTile(TileAtmosphere tile)

View File

@@ -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)

View File

@@ -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
/// <inheritdoc />
public override string Name => "Construction";

View File

@@ -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()

View File

@@ -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

View File

@@ -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<Hand> _hands = new List<Hand>();
public IEnumerable<string> 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<ItemComponent>();
}
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<InteractionSystem>().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;
}
}

View File

@@ -33,9 +33,13 @@ namespace Content.Server.GameObjects.Components.GUI
#pragma warning restore 649
[ViewVariables]
private readonly Dictionary<Slots, ContainerSlot> SlotContainers = new Dictionary<Slots, ContainerSlot>();
private readonly Dictionary<Slots, ContainerSlot> _slotContainers = new Dictionary<Slots, ContainerSlot>();
private KeyValuePair<Slots, (EntityUid entity, bool fits)>? HoverEntity;
private KeyValuePair<Slots, (EntityUid entity, bool fits)>? _hoverEntity;
public IEnumerable<Slots> 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<T>(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
/// </remarks>
/// <param name="slot">The slot to put the item in.</param>
/// <param name="item">The item to insert into the slot.</param>
/// <param name="reason">The translated reason why the item cannot be equiped, if this function returns false. Can be null.</param>
/// <param name="reason">The translated reason why the item cannot be equipped, if this function returns false. Can be null.</param>
/// <returns>True if the item was successfully inserted, false otherwise.</returns>
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<InteractionSystem>().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<ItemComponent>();
if (!inventorySlot.Remove(inventorySlot.ContainedEntity))
{
@@ -271,6 +277,8 @@ namespace Content.Server.GameObjects.Components.GUI
_entitySystemManager.GetEntitySystem<InteractionSystem>().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<ContainerSlot>(GetSlotString(slot), Owner);
_slotContainers[slot] = ContainerManagerComponent.Create<ContainerSlot>(GetSlotString(slot), Owner);
OnItemChanged?.Invoke();
return _slotContainers[slot];
}
/// <summary>
@@ -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
/// <returns>True if the slot exists, false otherwise.</returns>
public bool HasSlot(Slots slot)
{
return SlotContainers.ContainsKey(slot);
return _slotContainers.ContainsKey(slot);
}
/// <summary>
@@ -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<Slots, (EntityUid entity, bool fits)>(msg.Inventoryslot, (activeHand.Owner.Uid, canEquip));
_hoverEntity = new KeyValuePair<Slots, (EntityUid entity, bool fits)>(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<KeyValuePair<Slots, EntityUid>>();
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)
{

View File

@@ -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<ServerUserInterfaceComponent>().GetBoundUserInterface(StrippingUiKey.Key);
_userInterface.OnReceiveMessage += HandleUserInterfaceMessage;
_inventoryComponent = Owner.GetComponent<InventoryComponent>();
_handsComponent = Owner.GetComponent<HandsComponent>();
_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<HandsComponent>()
&& 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<Slots, string> GetInventorySlots()
{
var dictionary = new Dictionary<Slots, string>();
foreach (var slot in _inventoryComponent.Slots)
{
dictionary[slot] = _inventoryComponent.GetSlotItem(slot)?.Owner.Name ?? "None";
}
return dictionary;
}
private Dictionary<string, string> GetHandSlots()
{
var dictionary = new Dictionary<string, string>();
foreach (var hand in _handsComponent.Hands)
{
dictionary[hand] = _handsComponent.GetItem(hand)?.Owner.Name ?? "None";
}
return dictionary;
}
private void OpenUserInterface(IPlayerSession session)
{
_userInterface.Open(session);
}
/// <summary>
/// Places item in user's active hand to an inventory slot.
/// </summary>
private async void PlaceActiveHandItemInInventory(IEntity user, Slots slot)
{
var inventory = Owner.GetComponent<InventoryComponent>();
var userHands = user.GetComponent<HandsComponent>();
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<DoAfterSystem>();
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();
}
/// <summary>
/// Places item in user's active hand in one of the entity's hands.
/// </summary>
private async void PlaceActiveHandItemInHands(IEntity user, string hand)
{
var hands = Owner.GetComponent<HandsComponent>();
var userHands = user.GetComponent<HandsComponent>();
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<DoAfterSystem>();
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();
}
/// <summary>
/// Takes an item from the inventory and places it in the user's active hand.
/// </summary>
private async void TakeItemFromInventory(IEntity user, Slots slot)
{
var inventory = Owner.GetComponent<InventoryComponent>();
var userHands = user.GetComponent<HandsComponent>();
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<DoAfterSystem>();
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();
}
/// <summary>
/// Takes an item from a hand and places it in the user's active hand.
/// </summary>
private async void TakeItemFromHands(IEntity user, string hand)
{
var hands = Owner.GetComponent<HandsComponent>();
var userHands = user.GetComponent<HandsComponent>();
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<DoAfterSystem>();
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<InventoryComponent>();
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<HandsComponent>();
if (hands.TryGetItem(handMessage.Hand, out _))
placingItem = false;
if(placingItem)
PlaceActiveHandItemInHands(user, handMessage.Hand);
else
TakeItemFromHands(user, handMessage.Hand);
break;
default:
break;
}
}
}
}

View File

@@ -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<ICollidableComponent>(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;
}
}

View File

@@ -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;

View File

@@ -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]");
}
}
}

View File

@@ -42,9 +42,9 @@ namespace Content.Server.GameObjects.Components.Movement
{
base.Initialize();
// This component requires a physics component.
if (!Owner.HasComponent<IPhysicsComponent>())
Owner.AddComponent<PhysicsComponent>();
// This component requires a collidable component.
if (!Owner.HasComponent<ICollidableComponent>())
Owner.AddComponent<CollidableComponent>();
}
/// <inheritdoc />

View File

@@ -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<PhysicsComponent>();
physComp.Mass = 1;
collidable = gridEntity.AddComponent<CollidableComponent>();
collidable.Mass = 1;
collidable.CanCollide = true;
collidable.PhysicsShapes.Add(new PhysShapeGrid(grid));
}
//TODO: Is this always true?
if (!gridEntity.HasComponent<ICollidableComponent>())
{
var collideComp = gridEntity.AddComponent<CollidableComponent>();
collideComp.CanCollide = true;
//collideComp.IsHardCollidable = true;
collideComp.PhysicsShapes.Add(new PhysShapeGrid(grid));
}
var controller = physComp.EnsureController<ShuttleController>();
var controller = collidable.EnsureController<ShuttleController>();
controller.Push(CalcNewVelocity(direction, enabled), CurrentWalkSpeed);
}
}

View File

@@ -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);
}
}

View File

@@ -88,7 +88,7 @@ namespace Content.Server.GameObjects.Components.Projectiles
public void StartThrow(Vector2 direction, float speed)
{
var comp = Owner.GetComponent<IPhysicsComponent>();
var comp = Owner.GetComponent<ICollidableComponent>();
comp.Status = BodyStatus.InAir;
var controller = comp.EnsureController<ThrownController>();

View File

@@ -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;

View File

@@ -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<IPhysicsComponent>();
physicsComponent.Status = BodyStatus.InAir;
var collidableComponent = projectile.GetComponent<ICollidableComponent>();
collidableComponent.Status = BodyStatus.InAir;
projectile.Transform.GridPosition = Owner.Transform.GridPosition;
var projectileComponent = projectile.GetComponent<ProjectileComponent>();
projectileComponent.IgnoreEntity(shooter);
projectile
.GetComponent<IPhysicsComponent>()
.GetComponent<ICollidableComponent>()
.EnsureController<BulletController>()
.LinearVelocity = projectileAngle.ToVec() * velocity;

View File

@@ -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;

View File

@@ -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<Type>(){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<IMapGridComponent, IGridAtmosphereComponent>())
{
var grid = gridEnt.GetComponent<IMapGridComponent>();
if (_pauseManager.IsGridPaused(grid.GridIndex))
if (_pauseManager.IsGridPaused(mapGridComponent.GridIndex))
continue;
gridEnt.GetComponent<IGridAtmosphereComponent>().Update(frameTime);
gridAtmosphereComponent.Update(frameTime);
}
}

View File

@@ -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()

View File

@@ -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<IMoverComponent, ICollidableComponent>())
{
var entity = moverComponent.Owner;
if (_pauseManager.IsEntityPaused(entity))
{
continue;
}
var mover = entity.GetComponent<IMoverComponent>();
var physics = entity.GetComponent<IPhysicsComponent>();
if (entity.TryGetComponent<ICollidableComponent>(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<PlayerInputMoverComponent>();
}
if (ev.Entity.TryGetComponent(out IPhysicsComponent physics) &&
if (ev.Entity.TryGetComponent(out ICollidableComponent physics) &&
physics.TryGetController(out MoverController controller))
{
controller.StopMoving();

View File

@@ -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<VerbsResponseMessage.NetVerbData>();
//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;

View File

@@ -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.");

View File

@@ -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<PathfindingSystem>().ResettingCleanup();
EntitySystem.Get<AiReachableSystem>().ResettingCleanup();
EntitySystem.Get<WireHackingSystem>().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)

View File

@@ -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)
{

View File

@@ -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)
{

View File

@@ -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
{
/// <summary>
/// Invoked when the hand contents changes or when a hand is added/removed.
/// </summary>
event Action? OnItemChanged;
/// <summary>
/// The hands in this component.
/// </summary>
IEnumerable<string> Hands { get; }
/// <summary>
/// The hand name of the currently active hand.
/// </summary>
string ActiveHand { get; set; }
string? ActiveHand { get; set; }
/// <summary>
/// Enumerates over every held item.
@@ -27,12 +42,20 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items
/// </summary>
/// <param name="handName">The name of the hand to get.</param>
/// <returns>The item in the held, null if no item is held</returns>
ItemComponent GetItem(string handName);
ItemComponent? GetItem(string handName);
/// <summary>
/// Attempts to get an item in a hand.
/// </summary>
/// <param name="handName">The name of the hand to get.</param>
/// <param name="item">The item in the held, null if no item is held</param>
/// <returns>Whether it was holding an item</returns>
bool TryGetItem(string handName, [MaybeNullWhen(false)] out ItemComponent item);
/// <summary>
/// Gets item held by the current active hand
/// </summary>
ItemComponent GetActiveHand { get; }
ItemComponent? GetActiveHand { get; }
/// <summary>
/// Puts an item into any empty hand, preferring the active hand.
@@ -78,7 +101,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items
/// <returns>
/// true if the entity is held, false otherwise
/// </returns>
bool TryHand(IEntity entity, out string handName);
bool TryHand(IEntity entity, [MaybeNullWhen(false)] out string handName);
/// <summary>
/// Drops the item contained in the slot to the same position as our entity.

View File

@@ -85,7 +85,7 @@ namespace Content.Server.Throw
projComp.StartThrow(angle.ToVec(), spd);
if (throwSourceEnt != null &&
throwSourceEnt.TryGetComponent<IPhysicsComponent>(out var physics) &&
throwSourceEnt.TryGetComponent<ICollidableComponent>(out var physics) &&
physics.TryGetController(out MoverController mover))
{
var physicsMgr = IoCManager.Resolve<IPhysicsManager>();
@@ -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;
}

View File

@@ -51,7 +51,7 @@ namespace Content.Shared.GameObjects.Components.Disposal
}
[Serializable, NetSerializable]
public enum State
public enum PressureState
{
Ready,
Pressurizing

View File

@@ -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<Slots, string> Inventory { get; }
public Dictionary<string, string> Hands { get; }
public StrippingBoundUserInterfaceState(Dictionary<Slots, string> inventory, Dictionary<string, string> hands)
{
Inventory = inventory;
Hands = hands;
}
}
}

View File

@@ -142,11 +142,11 @@ namespace Content.Shared.GameObjects.Components.Movement
/// <inheritdoc />
public override void OnAdd()
{
// This component requires that the entity has a PhysicsComponent.
if (!Owner.HasComponent<IPhysicsComponent>())
// This component requires that the entity has a CollidableComponent.
if (!Owner.HasComponent<ICollidableComponent>())
Logger.Error(
$"[ECS] {Owner.Prototype?.Name} - {nameof(SharedPlayerInputMoverComponent)} requires" +
$" {nameof(IPhysicsComponent)}. ");
$" {nameof(ICollidableComponent)}. ");
base.OnAdd();
}

View File

@@ -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;
}

View File

@@ -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<SharedInteractionSystem>()
.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<IEntity, bool> predicate = entity => entity == examiner || entity == examined;
if (ContainerHelpers.TryGetContainer(examiner, out var container))
{
predicate += entity => entity == container.Owner;
}
return Get<SharedInteractionSystem>()
.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<SharedInteractionSystem>()
.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<IExamine>())
{
var subMessage = new FormattedMessage();
examineComponent.Examine(subMessage, inDetailsRange);
examineComponent.Examine(subMessage, IsInDetailsRange(examiner, entity));
if (subMessage.Tags.Count == 0)
continue;

View File

@@ -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<MoverController>();
collidable.EnsureController<MoverController>();
var weightless = !transform.Owner.HasComponent<MovementIgnoreGravityComponent>() &&
_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<IGameTiming>().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();

View File

@@ -20,6 +20,12 @@ namespace Content.Shared.GameObjects.Verbs
/// </summary>
public virtual bool RequireInteractionRange => true;
/// <summary>
/// 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.
/// </summary>
public virtual bool BlockedByContainers => true;
/// <summary>
/// Gets the visible verb data for the user.
/// </summary>

View File

@@ -20,6 +20,12 @@ namespace Content.Shared.GameObjects.Verbs
/// </summary>
public virtual bool RequireInteractionRange => true;
/// <summary>
/// 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.
/// </summary>
public virtual bool BlockedByContainers => true;
/// <summary>
/// Gets the visible verb data for the user.
/// </summary>

View File

@@ -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;

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -71,10 +71,12 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Soundfont/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=soundfonts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Spawner/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Strippable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=stunnable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=superconduction/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=swsl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=underplating/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unequip/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unexcite/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=uplink/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Wirecutter/@EntryIndexedValue">True</s:Boolean>