Merge branch 'master' into 2020-08-19-firelocks
This commit is contained in:
@@ -247,13 +247,17 @@ namespace Content.Client.Chat
|
||||
case OOCAlias:
|
||||
{
|
||||
var conInput = text.Substring(1);
|
||||
if (string.IsNullOrWhiteSpace(conInput))
|
||||
return;
|
||||
_console.ProcessCommand($"ooc \"{CommandParsing.Escape(conInput)}\"");
|
||||
break;
|
||||
}
|
||||
case AdminChatAlias:
|
||||
{
|
||||
var conInput = text.Substring(1);
|
||||
if(_groupController.CanCommand("asay")){
|
||||
if (string.IsNullOrWhiteSpace(conInput))
|
||||
return;
|
||||
if (_groupController.CanCommand("asay")){
|
||||
_console.ProcessCommand($"asay \"{CommandParsing.Escape(conInput)}\"");
|
||||
}
|
||||
else
|
||||
@@ -265,6 +269,8 @@ namespace Content.Client.Chat
|
||||
case MeAlias:
|
||||
{
|
||||
var conInput = text.Substring(1);
|
||||
if (string.IsNullOrWhiteSpace(conInput))
|
||||
return;
|
||||
_console.ProcessCommand($"me \"{CommandParsing.Escape(conInput)}\"");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -106,13 +106,13 @@ namespace Content.Client.Chat
|
||||
}
|
||||
|
||||
// Lerp to our new vertical offset if it's been modified.
|
||||
if (FloatMath.CloseTo(_verticalOffsetAchieved - VerticalOffset, 0, 0.1))
|
||||
if (MathHelper.CloseTo(_verticalOffsetAchieved - VerticalOffset, 0, 0.1))
|
||||
{
|
||||
_verticalOffsetAchieved = VerticalOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
_verticalOffsetAchieved = FloatMath.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.DeltaSeconds);
|
||||
_verticalOffsetAchieved = MathHelper.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.DeltaSeconds);
|
||||
}
|
||||
|
||||
var worldPos = _senderEntity.Transform.WorldPosition;
|
||||
@@ -122,7 +122,7 @@ namespace Content.Client.Chat
|
||||
var screenPos = lowerCenter - (Width / 2, ContentHeight + _verticalOffsetAchieved);
|
||||
LayoutContainer.SetPosition(this, screenPos);
|
||||
|
||||
var height = FloatMath.Clamp(lowerCenter.Y - screenPos.Y, 0, ContentHeight);
|
||||
var height = MathHelper.Clamp(lowerCenter.Y - screenPos.Y, 0, ContentHeight);
|
||||
LayoutContainer.SetSize(this, (Size.X, height));
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Content.Client.GameObjects.Components.Body
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out ISpriteComponent sprite))
|
||||
if (!Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -50,7 +50,7 @@ namespace Content.Client.GameObjects.Components.Body
|
||||
|
||||
if (!partRemoved.Dropped.HasValue ||
|
||||
!_entityManager.TryGetEntity(partRemoved.Dropped.Value, out var entity) ||
|
||||
!entity.TryGetComponent(out ISpriteComponent droppedSprite))
|
||||
!entity.TryGetComponent(out ISpriteComponent? droppedSprite))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Content.Client.GameObjects.Components
|
||||
/// <returns>True if the click worked, false otherwise.</returns>
|
||||
public bool CheckClick(Vector2 worldPos, out int drawDepth, out uint renderOrder)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out ISpriteComponent sprite) || !sprite.Visible)
|
||||
if (!Owner.TryGetComponent(out ISpriteComponent? sprite) || !sprite.Visible)
|
||||
{
|
||||
drawDepth = default;
|
||||
renderOrder = default;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#nullable enable
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Localization;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalRouterWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class DisposalRouterBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private DisposalRouterWindow? _window;
|
||||
|
||||
public DisposalRouterBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new DisposalRouterWindow();
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text);
|
||||
_window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text);
|
||||
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiAction action, string tag)
|
||||
{
|
||||
SendMessage(new UiActionMessage(action, tag));
|
||||
_window?.Close();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (!(state is DisposalRouterUserInterfaceState cast))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalRouterComponent"/>
|
||||
/// </summary>
|
||||
public class DisposalRouterWindow : SS14Window
|
||||
{
|
||||
public readonly LineEdit TagInput;
|
||||
public readonly Button Confirm;
|
||||
|
||||
protected override Vector2? CustomSize => (400, 80);
|
||||
|
||||
public DisposalRouterWindow()
|
||||
{
|
||||
Title = Loc.GetString("Disposal Router");
|
||||
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Tags:")},
|
||||
new Control {CustomMinimumSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(TagInput = new LineEdit {SizeFlagsHorizontal = SizeFlags.Expand, CustomMinimumSize = (320, 0),
|
||||
ToolTip = Loc.GetString("A comma separated list of tags"), IsValid = tags => TagRegex.IsMatch(tags)}),
|
||||
new Control {CustomMinimumSize = (10, 0)},
|
||||
(Confirm = new Button {Text = Loc.GetString("Confirm")})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void UpdateState(DisposalRouterUserInterfaceState state)
|
||||
{
|
||||
TagInput.Text = state.Tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#nullable enable
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Localization;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalTaggerWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class DisposalTaggerBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private DisposalTaggerWindow? _window;
|
||||
|
||||
public DisposalTaggerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new DisposalTaggerWindow();
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text);
|
||||
_window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text);
|
||||
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiAction action, string tag)
|
||||
{
|
||||
SendMessage(new UiActionMessage(action, tag));
|
||||
_window?.Close();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (!(state is DisposalTaggerUserInterfaceState cast))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalTaggerComponent"/>
|
||||
/// </summary>
|
||||
public class DisposalTaggerWindow : SS14Window
|
||||
{
|
||||
public readonly LineEdit TagInput;
|
||||
public readonly Button Confirm;
|
||||
|
||||
protected override Vector2? CustomSize => (400, 80);
|
||||
|
||||
public DisposalTaggerWindow()
|
||||
{
|
||||
Title = Loc.GetString("Disposal Tagger");
|
||||
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Tag:")},
|
||||
new Control {CustomMinimumSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(TagInput = new LineEdit {SizeFlagsHorizontal = SizeFlags.Expand, CustomMinimumSize = (320, 0),
|
||||
IsValid = tag => TagRegex.IsMatch(tag)}),
|
||||
new Control {CustomMinimumSize = (10, 0)},
|
||||
(Confirm = new Button {Text = Loc.GetString("Confirm")})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void UpdateState(DisposalTaggerUserInterfaceState state)
|
||||
{
|
||||
TagInput.Text = state.Tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,12 +113,12 @@ namespace Content.Client.GameObjects.Components.Disposal
|
||||
if (normalized <= leftSideSize)
|
||||
{
|
||||
normalized /= leftSideSize; // Adjust range to 0.0 to 1.0
|
||||
finalHue = FloatMath.Lerp(leftHue, middleHue, normalized);
|
||||
finalHue = MathHelper.Lerp(leftHue, middleHue, normalized);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalized = (normalized - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0.
|
||||
finalHue = FloatMath.Lerp(middleHue, rightHue, normalized);
|
||||
finalHue = MathHelper.Lerp(middleHue, rightHue, normalized);
|
||||
}
|
||||
|
||||
// Check if null first to avoid repeatedly creating this.
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Content.Client.GameObjects.Components
|
||||
|
||||
int level;
|
||||
|
||||
if (FloatMath.CloseTo(charge, 0))
|
||||
if (MathHelper.CloseTo(charge, 0))
|
||||
{
|
||||
level = 0;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Content.Client.GameObjects.Components.Items
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out ItemComponent item)) return;
|
||||
if (!entity.TryGetComponent(out ItemComponent? item)) return;
|
||||
|
||||
var maybeInHands = item.GetInHandStateInfo(hand.Location);
|
||||
|
||||
|
||||
@@ -243,7 +243,7 @@ namespace Content.Client.GameObjects.Components
|
||||
|
||||
if (int.TryParse(ev.Text, out var result))
|
||||
{
|
||||
result = FloatMath.Clamp(result, 0, byte.MaxValue);
|
||||
result = MathHelper.Clamp(result, 0, byte.MaxValue);
|
||||
|
||||
_ignoreEvents = true;
|
||||
_colorValue = (byte) result;
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Content.Client.GameObjects.Components.MedicalScanner
|
||||
Title = Owner.Owner.Name,
|
||||
};
|
||||
_window.OnClose += Close;
|
||||
_window.ScanButton.OnPressed += _ => SendMessage(new UiButtonPressedMessage(UiButton.ScanDNA));
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,18 +12,38 @@ namespace Content.Client.GameObjects.Components.MedicalScanner
|
||||
{
|
||||
public class MedicalScannerWindow : SS14Window
|
||||
{
|
||||
public readonly Button ScanButton;
|
||||
private readonly Label _diagnostics;
|
||||
protected override Vector2? CustomSize => (485, 90);
|
||||
|
||||
public MedicalScannerWindow()
|
||||
{
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(ScanButton = new Button
|
||||
{
|
||||
Text = "Scan and Save DNA"
|
||||
}),
|
||||
(_diagnostics = new Label
|
||||
{
|
||||
Text = ""
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Populate(MedicalScannerBoundUserInterfaceState state)
|
||||
{
|
||||
Contents.RemoveAllChildren();
|
||||
var text = new StringBuilder();
|
||||
|
||||
if (!state.Entity.HasValue ||
|
||||
!state.HasDamage() ||
|
||||
!IoCManager.Resolve<IEntityManager>().TryGetEntity(state.Entity.Value, out var entity))
|
||||
{
|
||||
text.Append(Loc.GetString("No patient data."));
|
||||
_diagnostics.Text = Loc.GetString("No patient data.");
|
||||
ScanButton.Disabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -45,9 +65,10 @@ namespace Content.Client.GameObjects.Components.MedicalScanner
|
||||
|
||||
text.Append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
Contents.AddChild(new Label() {Text = text.ToString()});
|
||||
_diagnostics.Text = text.ToString();
|
||||
ScanButton.Disabled = state.IsScanned;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Content.Client.GameObjects.Components.Mobs
|
||||
private const float RestoreRateRamp = 0.1f;
|
||||
|
||||
// The maximum magnitude of the kick applied to the camera at any point.
|
||||
private const float KickMagnitudeMax = 5f;
|
||||
private const float KickMagnitudeMax = 2f;
|
||||
|
||||
private Vector2 _currentKick;
|
||||
private float _lastKickTime;
|
||||
@@ -87,7 +87,7 @@ namespace Content.Client.GameObjects.Components.Mobs
|
||||
// Continually restore camera to 0.
|
||||
var normalized = _currentKick.Normalized;
|
||||
_lastKickTime += frameTime;
|
||||
var restoreRate = FloatMath.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp));
|
||||
var restoreRate = MathHelper.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp));
|
||||
var restore = normalized * restoreRate * frameTime;
|
||||
var (x, y) = _currentKick - restore;
|
||||
if (Math.Sign(x) != Math.Sign(_currentKick.X))
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Content.Client.GameObjects.Components.Mobs
|
||||
var progress = (_gameTiming.CurTime - start).TotalSeconds / length;
|
||||
var ratio = (progress <= 1 ? (1 - progress) : (_gameTiming.CurTime - end).TotalSeconds * -5);
|
||||
|
||||
cooldownGraphic.Progress = FloatMath.Clamp((float)ratio, -1, 1);
|
||||
cooldownGraphic.Progress = MathHelper.Clamp((float)ratio, -1, 1);
|
||||
cooldownGraphic.Visible = ratio > -1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Content.Client.GameObjects.Components.Mobs
|
||||
WalkModifierOverride = state.WalkModifierOverride;
|
||||
RunModifierOverride = state.RunModifierOverride;
|
||||
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent movement))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movement))
|
||||
{
|
||||
movement.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Movement
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IClimbable))]
|
||||
public class ClimbableComponent : SharedClimbableComponent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Client.Interfaces.GameObjects.Components.Interaction;
|
||||
using Content.Shared.Physics;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Movement
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ClimbingComponent : SharedClimbingComponent, IClientDraggable
|
||||
{
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
if (!(curState is ClimbModeComponentState climbModeState) || Body == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsClimbing = climbModeState.Climbing;
|
||||
}
|
||||
|
||||
public override bool IsClimbing { get; set; }
|
||||
|
||||
bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs)
|
||||
{
|
||||
return eventArgs.Target.HasComponent<IClimbable>();
|
||||
}
|
||||
|
||||
bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace Content.Client.GameObjects.Components.Nutrition
|
||||
|
||||
_currentHungerThreshold = hunger.CurrentThreshold;
|
||||
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent movement))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movement))
|
||||
{
|
||||
movement.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Content.Client.GameObjects.Components.Nutrition
|
||||
|
||||
_currentThirstThreshold = thirst.CurrentThreshold;
|
||||
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent movement))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movement))
|
||||
{
|
||||
movement.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.GameObjects.Components.PDA;
|
||||
using Content.Shared.GameObjects.Components.PDA;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Content.Client.GameObjects.Components.PDA
|
||||
private enum PDAVisualLayers
|
||||
{
|
||||
Base,
|
||||
Unlit
|
||||
Flashlight
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace Content.Client.GameObjects.Components.PDA
|
||||
return;
|
||||
}
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
sprite.LayerSetVisible(PDAVisualLayers.Unlit, false);
|
||||
if(!component.TryGetData<bool>(PDAVisuals.ScreenLit, out var isScreenLit))
|
||||
sprite.LayerSetVisible(PDAVisualLayers.Flashlight, false);
|
||||
if(!component.TryGetData<bool>(PDAVisuals.FlashlightLit, out var isScreenLit))
|
||||
{
|
||||
return;
|
||||
}
|
||||
sprite.LayerSetState(PDAVisualLayers.Unlit, "unlit_pda_screen");
|
||||
sprite.LayerSetVisible(PDAVisualLayers.Unlit, isScreenLit);
|
||||
sprite.LayerSetState(PDAVisualLayers.Flashlight, "light_overlay");
|
||||
sprite.LayerSetVisible(PDAVisualLayers.Flashlight, isScreenLit);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ namespace Content.Client.GameObjects.Components.Power
|
||||
if (normalizedCharge <= leftSideSize)
|
||||
{
|
||||
normalizedCharge /= leftSideSize; // Adjust range to 0.0 to 1.0
|
||||
finalHue = FloatMath.Lerp(leftHue, middleHue, normalizedCharge);
|
||||
finalHue = MathHelper.Lerp(leftHue, middleHue, normalizedCharge);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedCharge = (normalizedCharge - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0.
|
||||
finalHue = FloatMath.Lerp(middleHue, rightHue, normalizedCharge);
|
||||
finalHue = MathHelper.Lerp(middleHue, rightHue, normalizedCharge);
|
||||
}
|
||||
|
||||
// Check if null first to avoid repeatedly creating this.
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace Content.Client.GameObjects.Components.Weapons
|
||||
const float xOffset = 0.0f;
|
||||
|
||||
// Overkill but easy to adjust if you want to mess around with the design
|
||||
var result = (float) FloatMath.Clamp(slope * (float) Math.Pow(ratio - xOffset, exponent) + yOffset, 0.0, 1.0);
|
||||
var result = (float) MathHelper.Clamp(slope * (float) Math.Pow(ratio - xOffset, exponent) + yOffset, 0.0, 1.0);
|
||||
DebugTools.Assert(!float.IsNaN(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (AttachedEntity?.IsValid() != true || !AttachedEntity.TryGetComponent(out DoAfterComponent doAfterComponent))
|
||||
if (AttachedEntity?.IsValid() != true || !AttachedEntity.TryGetComponent(out DoAfterComponent? doAfterComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter
|
||||
Gui ??= new DoAfterGui();
|
||||
Gui.AttachedEntity = entity;
|
||||
|
||||
if (entity.TryGetComponent(out DoAfterComponent doAfterComponent))
|
||||
if (entity.TryGetComponent(out DoAfterComponent? doAfterComponent))
|
||||
{
|
||||
foreach (var (_, doAfter) in doAfterComponent.DoAfters)
|
||||
{
|
||||
@@ -87,7 +87,7 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_player.TryGetComponent(out DoAfterComponent doAfterComponent))
|
||||
if (!_player.TryGetComponent(out DoAfterComponent? doAfterComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
var playerEnt = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
|
||||
if (playerEnt == null || !playerEnt.TryGetComponent(out IMoverComponent mover))
|
||||
if (playerEnt == null || !playerEnt.TryGetComponent(out IMoverComponent? mover))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,11 +207,10 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
//Get verbs, component dependent.
|
||||
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
|
||||
{
|
||||
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
|
||||
continue;
|
||||
|
||||
if (verb.BlockedByContainers && !user.IsInSameOrNoContainer(entity))
|
||||
if (!VerbUtility.VerbAccessChecks(user, entity, verb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var verbData = verb.GetData(user, component);
|
||||
|
||||
@@ -232,11 +231,10 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
//Get global verbs. Visible for all entities regardless of their components.
|
||||
foreach (var globalVerb in VerbUtility.GetGlobalVerbs(Assembly.GetExecutingAssembly()))
|
||||
{
|
||||
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
|
||||
continue;
|
||||
|
||||
if (globalVerb.BlockedByContainers && !user.IsInSameOrNoContainer(entity))
|
||||
if (!VerbUtility.VerbAccessChecks(user, entity, globalVerb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var verbData = globalVerb.GetData(user, entity);
|
||||
|
||||
|
||||
@@ -26,14 +26,16 @@ namespace Content.Client.GameTicking
|
||||
|
||||
[ViewVariables] public bool AreWeReady { get; private set; }
|
||||
[ViewVariables] public bool IsGameStarted { get; private set; }
|
||||
[ViewVariables] public bool DisallowedLateJoin { get; private set; }
|
||||
[ViewVariables] public string ServerInfoBlob { get; private set; }
|
||||
[ViewVariables] public DateTime StartTime { get; private set; }
|
||||
[ViewVariables] public bool Paused { get; private set; }
|
||||
[ViewVariables] public Dictionary<NetSessionId, bool> Ready { get; private set; }
|
||||
[ViewVariables] public Dictionary<NetSessionId, PlayerStatus> Status { get; private set; }
|
||||
|
||||
public event Action InfoBlobUpdated;
|
||||
public event Action LobbyStatusUpdated;
|
||||
public event Action LobbyReadyUpdated;
|
||||
public event Action LobbyLateJoinStatusUpdated;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -50,11 +52,17 @@ namespace Content.Client.GameTicking
|
||||
{
|
||||
IoCManager.Resolve<IClyde>().RequestWindowAttention();
|
||||
});
|
||||
_netManager.RegisterNetMessage<MsgTickerLateJoinStatus>(nameof(MsgTickerLateJoinStatus), LateJoinStatus);
|
||||
|
||||
Ready = new Dictionary<NetSessionId, bool>();
|
||||
Status = new Dictionary<NetSessionId, PlayerStatus>();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void LateJoinStatus(MsgTickerLateJoinStatus message)
|
||||
{
|
||||
DisallowedLateJoin = message.Disallowed;
|
||||
LobbyLateJoinStatusUpdated?.Invoke();
|
||||
}
|
||||
|
||||
|
||||
private void JoinLobby(MsgTickerJoinLobby message)
|
||||
@@ -69,7 +77,7 @@ namespace Content.Client.GameTicking
|
||||
AreWeReady = message.YouAreReady;
|
||||
Paused = message.Paused;
|
||||
if (IsGameStarted)
|
||||
Ready.Clear();
|
||||
Status.Clear();
|
||||
|
||||
LobbyStatusUpdated?.Invoke();
|
||||
}
|
||||
@@ -95,9 +103,9 @@ namespace Content.Client.GameTicking
|
||||
private void LobbyReady(MsgTickerLobbyReady message)
|
||||
{
|
||||
// Merge the Dictionaries
|
||||
foreach (var p in message.PlayerReady)
|
||||
foreach (var p in message.PlayerStatus)
|
||||
{
|
||||
Ready[p.Key] = p.Value;
|
||||
Status[p.Key] = p.Value;
|
||||
}
|
||||
LobbyReadyUpdated?.Invoke();
|
||||
}
|
||||
@@ -105,7 +113,7 @@ namespace Content.Client.GameTicking
|
||||
private void RoundEnd(MsgRoundEndMessage message)
|
||||
{
|
||||
//This is not ideal at all, but I don't see an immediately better fit anywhere else.
|
||||
var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundDuration, message.AllPlayersEndInfo);
|
||||
var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText, message.RoundDuration, message.AllPlayersEndInfo);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,8 @@
|
||||
"Listening",
|
||||
"Radio",
|
||||
"DisposalHolder",
|
||||
"DisposalTagger",
|
||||
"DisposalRouter",
|
||||
"DisposalTransit",
|
||||
"DisposalEntry",
|
||||
"DisposalJunction",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Robust.Shared.Network;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static Content.Shared.SharedGameTicker;
|
||||
|
||||
namespace Content.Client.Interfaces
|
||||
{
|
||||
@@ -9,13 +10,15 @@ namespace Content.Client.Interfaces
|
||||
bool IsGameStarted { get; }
|
||||
string ServerInfoBlob { get; }
|
||||
bool AreWeReady { get; }
|
||||
bool DisallowedLateJoin { get; }
|
||||
DateTime StartTime { get; }
|
||||
bool Paused { get; }
|
||||
Dictionary<NetSessionId, bool> Ready { get; }
|
||||
Dictionary<NetSessionId, PlayerStatus> Status { get; }
|
||||
|
||||
void Initialize();
|
||||
event Action InfoBlobUpdated;
|
||||
event Action LobbyStatusUpdated;
|
||||
event Action LobbyReadyUpdated;
|
||||
event Action LobbyLateJoinStatusUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.SharedGameTicker;
|
||||
|
||||
namespace Content.Client.State
|
||||
{
|
||||
@@ -101,13 +102,18 @@ namespace Content.Client.State
|
||||
_clientGameTicker.InfoBlobUpdated += UpdateLobbyUi;
|
||||
_clientGameTicker.LobbyStatusUpdated += LobbyStatusUpdated;
|
||||
_clientGameTicker.LobbyReadyUpdated += LobbyReadyUpdated;
|
||||
_clientGameTicker.LobbyLateJoinStatusUpdated += LobbyLateJoinStatusUpdated;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_playerManager.PlayerListUpdated -= PlayerManagerOnPlayerListUpdated;
|
||||
_clientGameTicker.InfoBlobUpdated -= UpdateLobbyUi;
|
||||
_clientGameTicker.LobbyStatusUpdated -= UpdateLobbyUi;
|
||||
_clientGameTicker.LobbyStatusUpdated -= LobbyStatusUpdated;
|
||||
_clientGameTicker.LobbyReadyUpdated -= LobbyReadyUpdated;
|
||||
_clientGameTicker.LobbyLateJoinStatusUpdated -= LobbyLateJoinStatusUpdated;
|
||||
|
||||
_clientGameTicker.Status.Clear();
|
||||
|
||||
_lobby.Dispose();
|
||||
_characterSetup.Dispose();
|
||||
@@ -153,11 +159,14 @@ namespace Content.Client.State
|
||||
private void PlayerManagerOnPlayerListUpdated(object sender, EventArgs e)
|
||||
{
|
||||
// Remove disconnected sessions from the Ready Dict
|
||||
foreach (var p in _clientGameTicker.Ready)
|
||||
foreach (var p in _clientGameTicker.Status)
|
||||
{
|
||||
if (!_playerManager.SessionsDict.TryGetValue(p.Key, out _))
|
||||
{
|
||||
_clientGameTicker.Ready.Remove(p.Key);
|
||||
// This is a shitty fix. Observers can rejoin because they are already in the game.
|
||||
// So we don't delete them, but keep them if they decide to rejoin
|
||||
if (p.Value != PlayerStatus.Observer)
|
||||
_clientGameTicker.Status.Remove(p.Key);
|
||||
}
|
||||
}
|
||||
UpdatePlayerList();
|
||||
@@ -170,6 +179,11 @@ namespace Content.Client.State
|
||||
UpdateLobbyUi();
|
||||
}
|
||||
|
||||
private void LobbyLateJoinStatusUpdated()
|
||||
{
|
||||
_lobby.ReadyButton.Disabled = _clientGameTicker.DisallowedLateJoin;
|
||||
}
|
||||
|
||||
private void UpdateLobbyUi()
|
||||
{
|
||||
if (_lobby == null)
|
||||
@@ -188,6 +202,7 @@ namespace Content.Client.State
|
||||
_lobby.StartTime.Text = "";
|
||||
_lobby.ReadyButton.Text = Loc.GetString("Ready Up");
|
||||
_lobby.ReadyButton.ToggleMode = true;
|
||||
_lobby.ReadyButton.Disabled = false;
|
||||
_lobby.ReadyButton.Pressed = _clientGameTicker.AreWeReady;
|
||||
}
|
||||
|
||||
@@ -207,12 +222,19 @@ namespace Content.Client.State
|
||||
// Don't show ready state if we're ingame
|
||||
if (!_clientGameTicker.IsGameStarted)
|
||||
{
|
||||
var ready = false;
|
||||
var status = PlayerStatus.NotReady;
|
||||
if (session.SessionId == _playerManager.LocalPlayer.SessionId)
|
||||
ready = _clientGameTicker.AreWeReady;
|
||||
status = _clientGameTicker.AreWeReady ? PlayerStatus.Ready : PlayerStatus.NotReady;
|
||||
else
|
||||
_clientGameTicker.Ready.TryGetValue(session.SessionId, out ready);
|
||||
readyState = ready ? Loc.GetString("Ready") : Loc.GetString("Not Ready");
|
||||
_clientGameTicker.Status.TryGetValue(session.SessionId, out status);
|
||||
|
||||
readyState = status switch
|
||||
{
|
||||
PlayerStatus.NotReady => Loc.GetString("Not Ready"),
|
||||
PlayerStatus.Ready => Loc.GetString("Ready"),
|
||||
PlayerStatus.Observer => Loc.GetString("Observer"),
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
_lobby.PlayerReadyList.AddItem(readyState, null, false);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Content.Client.UserInterface
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
Span<float> x = stackalloc float[10];
|
||||
Color color;
|
||||
|
||||
var lerp = 1f - MathF.Abs(Progress); // for future bikeshedding purposes
|
||||
@@ -41,7 +42,7 @@ namespace Content.Client.UserInterface
|
||||
}
|
||||
else
|
||||
{
|
||||
var alpha = FloatMath.Clamp(0.5f * lerp, 0f, 0.5f);
|
||||
var alpha = MathHelper.Clamp(0.5f * lerp, 0f, 0.5f);
|
||||
color = new Color(1f, 1f, 1f, alpha);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Content.Client.UserInterface
|
||||
var progress = (_gameTiming.CurTime - start).TotalSeconds / length;
|
||||
var ratio = (progress <= 1 ? (1 - progress) : (_gameTiming.CurTime - end).TotalSeconds * -5);
|
||||
|
||||
cooldownDisplay.Progress = FloatMath.Clamp((float)ratio, -1, 1);
|
||||
cooldownDisplay.Progress = MathHelper.Clamp((float)ratio, -1, 1);
|
||||
|
||||
if (ratio > -1f)
|
||||
{
|
||||
|
||||
@@ -82,6 +82,8 @@ namespace Content.Client.UserInterface
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
_preferencesManager.OnServerDataLoaded -= UpdateUI;
|
||||
|
||||
if (!disposing) return;
|
||||
_previewDummy.Delete();
|
||||
_previewDummy = null;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Utility;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
@@ -17,7 +18,7 @@ namespace Content.Client.UserInterface
|
||||
private TabContainer RoundEndWindowTabs { get; }
|
||||
protected override Vector2? CustomSize => (520, 580);
|
||||
|
||||
public RoundEndSummaryWindow(string gm, TimeSpan roundTimeSpan, List<RoundEndPlayerInfo> info )
|
||||
public RoundEndSummaryWindow(string gm, string roundEnd, TimeSpan roundTimeSpan, List<RoundEndPlayerInfo> info)
|
||||
{
|
||||
|
||||
Title = Loc.GetString("Round End Summary");
|
||||
@@ -49,6 +50,14 @@ namespace Content.Client.UserInterface
|
||||
gamemodeLabel.SetMarkup(Loc.GetString("Round of [color=white]{0}[/color] has ended.", gm));
|
||||
RoundEndSummaryTab.AddChild(gamemodeLabel);
|
||||
|
||||
//Round end text
|
||||
if (!string.IsNullOrEmpty(roundEnd))
|
||||
{
|
||||
var roundendLabel = new RichTextLabel();
|
||||
roundendLabel.SetMarkup(Loc.GetString(roundEnd));
|
||||
RoundEndSummaryTab.AddChild(roundendLabel);
|
||||
}
|
||||
|
||||
//Duration
|
||||
var roundTimeLabel = new RichTextLabel();
|
||||
roundTimeLabel.SetMarkup(Loc.GetString("It lasted for [color=yellow]{0} hours, {1} minutes, and {2} seconds.",
|
||||
@@ -65,30 +74,40 @@ namespace Content.Client.UserInterface
|
||||
//Create labels for each player info.
|
||||
foreach (var plyinfo in manifestSortedList)
|
||||
{
|
||||
|
||||
var playerInfoText = new RichTextLabel()
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.Fill
|
||||
SizeFlagsVertical = SizeFlags.Fill,
|
||||
};
|
||||
|
||||
//TODO: On Hover display a popup detailing more play info.
|
||||
//For example: their antag goals and if they completed them sucessfully.
|
||||
var icNameColor = plyinfo.Antag ? "red" : "white";
|
||||
playerInfoText.SetMarkup(
|
||||
Loc.GetString($"[color=gray]{plyinfo.PlayerOOCName}[/color] was [color={icNameColor}]{plyinfo.PlayerICName}[/color] playing role of [color=orange]{plyinfo.Role}[/color]."));
|
||||
Loc.GetString("[color=gray]{0}[/color] was [color={1}]{2}[/color] playing role of [color=orange]{3}[/color].",
|
||||
plyinfo.PlayerOOCName, icNameColor, plyinfo.PlayerICName, Loc.GetString(plyinfo.Role)));
|
||||
innerScrollContainer.AddChild(playerInfoText);
|
||||
}
|
||||
|
||||
scrollContainer.AddChild(innerScrollContainer);
|
||||
//Attach the entire ScrollContainer that holds all the playerinfo.
|
||||
PlayerManifestoTab.AddChild(scrollContainer);
|
||||
// TODO: 1240 Overlap, remove once it's fixed. Temp Hack to make the lines not overlap
|
||||
PlayerManifestoTab.OnVisibilityChanged += PlayerManifestoTab_OnVisibilityChanged;
|
||||
|
||||
//Finally, display the window.
|
||||
OpenCentered();
|
||||
MoveToFront();
|
||||
|
||||
}
|
||||
|
||||
private void PlayerManifestoTab_OnVisibilityChanged(Control obj)
|
||||
{
|
||||
if (obj.Visible)
|
||||
{
|
||||
// For some reason the lines get not properly drawn with the right height
|
||||
// so we just force a update
|
||||
ForceRunLayoutUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Client;
|
||||
using Content.Client.Interfaces.Parallax;
|
||||
using Content.Server;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -12,6 +13,7 @@ using EntryPoint = Content.Client.EntryPoint;
|
||||
|
||||
namespace Content.IntegrationTests
|
||||
{
|
||||
[Parallelizable(ParallelScope.All)]
|
||||
public abstract class ContentIntegrationTest : RobustIntegrationTest
|
||||
{
|
||||
protected sealed override ClientIntegrationInstance StartClient(ClientIntegrationOptions options = null)
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Content.IntegrationTests
|
||||
{
|
||||
}
|
||||
|
||||
public void EndRound()
|
||||
public void EndRound(string roundEnd)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
51
Content.IntegrationTests/Tests/DeleteInventoryTest.cs
Normal file
51
Content.IntegrationTests/Tests/DeleteInventoryTest.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Clothing;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
|
||||
|
||||
namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class DeleteInventoryTest : ContentIntegrationTest
|
||||
{
|
||||
// Test that when deleting an entity with an InventoryComponent,
|
||||
// any equipped items also get deleted.
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
var server = StartServerDummyTicker();
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
// Spawn everything.
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
mapMan.CreateNewMapEntity(MapId.Nullspace);
|
||||
|
||||
var entMgr = IoCManager.Resolve<IEntityManager>();
|
||||
var container = entMgr.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
var inv = container.AddComponent<InventoryComponent>();
|
||||
|
||||
var child = entMgr.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
var item = child.AddComponent<ClothingComponent>();
|
||||
item.SlotFlags = SlotFlags.HEAD;
|
||||
|
||||
// Equip item.
|
||||
Assert.That(inv.Equip(Slots.HEAD, item, false), Is.True);
|
||||
|
||||
// Delete parent.
|
||||
container.Delete();
|
||||
|
||||
// Assert that child item was also deleted.
|
||||
Assert.That(item.Deleted, Is.True);
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,8 +81,8 @@ namespace Content.IntegrationTests.Tests.Disposal
|
||||
var disposalTrunk = entityManager.SpawnEntity("DisposalTrunk", disposalUnit.Transform.MapPosition);
|
||||
|
||||
// Test for components existing
|
||||
Assert.True(disposalUnit.TryGetComponent(out unit));
|
||||
Assert.True(disposalTrunk.TryGetComponent(out entry));
|
||||
Assert.True(disposalUnit.TryGetComponent(out unit!));
|
||||
Assert.True(disposalTrunk.TryGetComponent(out entry!));
|
||||
|
||||
// Can't insert, unanchored and unpowered
|
||||
var disposalUnitAnchorable = disposalUnit.GetComponent<AnchorableComponent>();
|
||||
@@ -92,8 +92,8 @@ namespace Content.IntegrationTests.Tests.Disposal
|
||||
|
||||
// Anchor the disposal unit
|
||||
await disposalUnitAnchorable.TryAnchor(human, null, true);
|
||||
Assert.True(disposalUnit.TryGetComponent(out AnchorableComponent anchorableUnit));
|
||||
Assert.True(await anchorableUnit.TryAnchor(human, wrench));
|
||||
Assert.True(disposalUnit.TryGetComponent(out AnchorableComponent? anchorableUnit));
|
||||
Assert.True(await anchorableUnit!.TryAnchor(human, wrench));
|
||||
Assert.True(unit.Anchored);
|
||||
|
||||
// No power
|
||||
@@ -118,8 +118,8 @@ namespace Content.IntegrationTests.Tests.Disposal
|
||||
Flush(unit, false, entry, human, wrench);
|
||||
|
||||
// Remove power need
|
||||
Assert.True(disposalUnit.TryGetComponent(out PowerReceiverComponent power));
|
||||
power.NeedsPower = false;
|
||||
Assert.True(disposalUnit.TryGetComponent(out PowerReceiverComponent? power));
|
||||
power!.NeedsPower = false;
|
||||
Assert.True(unit.Powered);
|
||||
|
||||
// Flush with a mob and an item
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.GameObjects.Components.Movement
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ClimbableComponent))]
|
||||
[TestOf(typeof(ClimbingComponent))]
|
||||
public class ClimbUnitTest : ContentIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
var server = StartServerDummyTicker();
|
||||
|
||||
IEntity human;
|
||||
IEntity table;
|
||||
IEntity carpet;
|
||||
ClimbableComponent climbable;
|
||||
ClimbingComponent climbing;
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
// Spawn the entities
|
||||
human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace);
|
||||
table = entityManager.SpawnEntity("Table", MapCoordinates.Nullspace);
|
||||
|
||||
// Test for climb components existing
|
||||
// Players and tables should have these in their prototypes.
|
||||
Assert.True(human.TryGetComponent(out climbing!), "Human has no climbing");
|
||||
Assert.True(table.TryGetComponent(out climbable!), "Table has no climbable");
|
||||
|
||||
// Now let's make the player enter a climbing transitioning state.
|
||||
climbing.IsClimbing = true;
|
||||
climbing.TryMoveTo(human.Transform.WorldPosition, table.Transform.WorldPosition);
|
||||
var body = human.GetComponent<ICollidableComponent>();
|
||||
|
||||
Assert.True(body.HasController<ClimbController>(), "Player has no ClimbController");
|
||||
|
||||
// Force the player out of climb state. It should immediately remove the ClimbController.
|
||||
climbing.IsClimbing = false;
|
||||
|
||||
Assert.True(!body.HasController<ClimbController>(), "Player wrongly has a ClimbController");
|
||||
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Content.IntegrationTests/Tests/PowerTest.cs
Normal file
150
Content.IntegrationTests/Tests/PowerTest.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.Components.Power.PowerNetComponents;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class PowerTest : ContentIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task PowerNetTest()
|
||||
{
|
||||
var server = StartServerDummyTicker();
|
||||
|
||||
PowerSupplierComponent supplier = null;
|
||||
PowerConsumerComponent consumer1 = null;
|
||||
PowerConsumerComponent consumer2 = null;
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
var entityMan = IoCManager.Resolve<IEntityManager>();
|
||||
mapMan.CreateMap(new MapId(1));
|
||||
var grid = mapMan.CreateGrid(new MapId(1));
|
||||
|
||||
var generatorEnt = entityMan.SpawnEntity("DebugGenerator", new GridCoordinates(new Vector2(0, 0), grid.Index));
|
||||
var consumerEnt1 = entityMan.SpawnEntity("DebugConsumer", new GridCoordinates(new Vector2(0, 1), grid.Index));
|
||||
var consumerEnt2 = entityMan.SpawnEntity("DebugConsumer", new GridCoordinates(new Vector2(0, 2), grid.Index));
|
||||
|
||||
Assert.That(generatorEnt.TryGetComponent(out supplier));
|
||||
Assert.That(consumerEnt1.TryGetComponent(out consumer1));
|
||||
Assert.That(consumerEnt2.TryGetComponent(out consumer2));
|
||||
|
||||
var supplyRate = 1000; //arbitrary amount of power supply
|
||||
|
||||
supplier.SupplyRate = supplyRate;
|
||||
consumer1.DrawRate = supplyRate / 2; //arbitrary draw less than supply
|
||||
consumer2.DrawRate = supplyRate * 2; //arbitrary draw greater than supply
|
||||
|
||||
consumer1.Priority = Priority.First; //power goes to this consumer first
|
||||
consumer2.Priority = Priority.Last; //any excess power should go to low priority consumer
|
||||
});
|
||||
|
||||
server.RunTicks(1); //let run a tick for PowerNet to process power
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
Assert.That(consumer1.DrawRate, Is.EqualTo(consumer1.ReceivedPower)); //first should be fully powered
|
||||
Assert.That(consumer2.ReceivedPower, Is.EqualTo(supplier.SupplyRate - consumer1.ReceivedPower)); //second should get remaining power
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ApcChargingTest()
|
||||
{
|
||||
var server = StartServerDummyTicker();
|
||||
|
||||
BatteryComponent apcBattery = null;
|
||||
PowerSupplierComponent substationSupplier = null;
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
var entityMan = IoCManager.Resolve<IEntityManager>();
|
||||
mapMan.CreateMap(new MapId(1));
|
||||
var grid = mapMan.CreateGrid(new MapId(1));
|
||||
|
||||
var generatorEnt = entityMan.SpawnEntity("DebugGenerator", new GridCoordinates(new Vector2(0, 0), grid.Index));
|
||||
var substationEnt = entityMan.SpawnEntity("DebugSubstation", new GridCoordinates(new Vector2(0, 1), grid.Index));
|
||||
var apcEnt = entityMan.SpawnEntity("DebugApc", new GridCoordinates(new Vector2(0, 2), grid.Index));
|
||||
|
||||
Assert.That(generatorEnt.TryGetComponent<PowerSupplierComponent>(out var generatorSupplier));
|
||||
|
||||
Assert.That(substationEnt.TryGetComponent(out substationSupplier));
|
||||
Assert.That(substationEnt.TryGetComponent<BatteryStorageComponent>(out var substationStorage));
|
||||
Assert.That(substationEnt.TryGetComponent<BatteryDischargerComponent>(out var substationDischarger));
|
||||
|
||||
Assert.That(apcEnt.TryGetComponent(out apcBattery));
|
||||
Assert.That(apcEnt.TryGetComponent<BatteryStorageComponent>(out var apcStorage));
|
||||
|
||||
generatorSupplier.SupplyRate = 1000; //arbitrary nonzero amount of power
|
||||
substationStorage.ActiveDrawRate = 1000; //arbitrary nonzero power draw
|
||||
substationDischarger.ActiveSupplyRate = 500; //arbitirary nonzero power supply less than substation storage draw
|
||||
apcStorage.ActiveDrawRate = 500; //arbitrary nonzero power draw
|
||||
apcBattery.MaxCharge = 100; //abbitrary nonzero amount of charge
|
||||
apcBattery.CurrentCharge = 0; //no charge
|
||||
});
|
||||
|
||||
server.RunTicks(5); //let run a few ticks for PowerNets to reevaluate and start charging apc
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
Assert.That(substationSupplier.SupplyRate, Is.Not.EqualTo(0)); //substation should be providing power
|
||||
Assert.That(apcBattery.CurrentCharge, Is.Not.EqualTo(0)); //apc battery should have gained charge
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ApcNetTest()
|
||||
{
|
||||
var server = StartServerDummyTicker();
|
||||
|
||||
PowerReceiverComponent receiver = null;
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
var entityMan = IoCManager.Resolve<IEntityManager>();
|
||||
mapMan.CreateMap(new MapId(1));
|
||||
var grid = mapMan.CreateGrid(new MapId(1));
|
||||
|
||||
var apcEnt = entityMan.SpawnEntity("DebugApc", new GridCoordinates(new Vector2(0, 0), grid.Index));
|
||||
var apcExtensionEnt = entityMan.SpawnEntity("ApcExtensionCable", new GridCoordinates(new Vector2(0, 1), grid.Index));
|
||||
var powerReceiverEnt = entityMan.SpawnEntity("DebugPowerReceiver", new GridCoordinates(new Vector2(0, 2), grid.Index));
|
||||
|
||||
Assert.That(apcEnt.TryGetComponent<ApcComponent>(out var apc));
|
||||
Assert.That(apcExtensionEnt.TryGetComponent<PowerProviderComponent>(out var provider));
|
||||
Assert.That(powerReceiverEnt.TryGetComponent(out receiver));
|
||||
|
||||
provider.PowerTransferRange = 5; //arbitrary range to reach receiver
|
||||
receiver.PowerReceptionRange = 5; //arbitrary range to reach provider
|
||||
|
||||
apc.Battery.MaxCharge = 10000; //arbitrary nonzero amount of charge
|
||||
apc.Battery.CurrentCharge = apc.Battery.MaxCharge; //fill battery
|
||||
|
||||
receiver.Load = 1; //arbitrary small amount of power
|
||||
});
|
||||
|
||||
server.RunTicks(1); //let run a tick for ApcNet to process power
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
Assert.That(receiver.Powered);
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,11 @@ using NUnit.Framework;
|
||||
using Robust.Server.Interfaces.Maps;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Resources;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
@@ -14,7 +17,7 @@ namespace Content.IntegrationTests.Tests
|
||||
[Test]
|
||||
public async Task SaveLoadMultiGridMap()
|
||||
{
|
||||
const string mapPath = @"Maps/Test/TestMap.yml";
|
||||
const string mapPath = @"/Maps/Test/TestMap.yml";
|
||||
|
||||
var server = StartServer();
|
||||
await server.WaitIdleAsync();
|
||||
@@ -24,6 +27,10 @@ namespace Content.IntegrationTests.Tests
|
||||
|
||||
server.Post(() =>
|
||||
{
|
||||
var dir = new ResourcePath(mapPath).Directory;
|
||||
IoCManager.Resolve<IResourceManager>()
|
||||
.UserData.CreateDir(dir);
|
||||
|
||||
var mapId = mapManager.CreateMap(new MapId(5));
|
||||
|
||||
{
|
||||
|
||||
@@ -38,14 +38,14 @@ namespace Content.IntegrationTests.Tests
|
||||
string one;
|
||||
string two;
|
||||
|
||||
var rp1 = new ResourcePath("save load save 1.yml");
|
||||
var rp1 = new ResourcePath("/save load save 1.yml");
|
||||
using (var stream = userData.Open(rp1, FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
one = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
var rp2 = new ResourcePath("save load save 2.yml");
|
||||
var rp2 = new ResourcePath("/save load save 2.yml");
|
||||
using (var stream = userData.Open(rp2, FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
@@ -96,7 +96,7 @@ namespace Content.IntegrationTests.Tests
|
||||
|
||||
server.Post(() =>
|
||||
{
|
||||
mapLoader.SaveBlueprint(grid.Index, "load save ticks save 2.yml");
|
||||
mapLoader.SaveBlueprint(grid.Index, "/load save ticks save 2.yml");
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
@@ -105,13 +105,13 @@ namespace Content.IntegrationTests.Tests
|
||||
string one;
|
||||
string two;
|
||||
|
||||
using (var stream = userData.Open(new ResourcePath("load save ticks save 1.yml"), FileMode.Open))
|
||||
using (var stream = userData.Open(new ResourcePath("/load save ticks save 1.yml"), FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
one = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
using (var stream = userData.Open(new ResourcePath("load save ticks save 2.yml"), FileMode.Open))
|
||||
using (var stream = userData.Open(new ResourcePath("/load save ticks save 2.yml"), FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
two = reader.ReadToEnd();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
|
||||
namespace Content.Server.Database
|
||||
@@ -50,9 +51,10 @@ namespace Content.Server.Database
|
||||
|
||||
public class SqliteConfiguration : IDatabaseConfiguration
|
||||
{
|
||||
private readonly string _databaseFilePath;
|
||||
private readonly string? _databaseFilePath;
|
||||
|
||||
public SqliteConfiguration(string databaseFilePath)
|
||||
/// <param name="databaseFilePath">If null, an in-memory database is used.</param>
|
||||
public SqliteConfiguration(string? databaseFilePath)
|
||||
{
|
||||
_databaseFilePath = databaseFilePath;
|
||||
}
|
||||
@@ -62,7 +64,20 @@ namespace Content.Server.Database
|
||||
get
|
||||
{
|
||||
var optionsBuilder = new DbContextOptionsBuilder<PreferencesDbContext>();
|
||||
optionsBuilder.UseSqlite($"Data Source={_databaseFilePath}");
|
||||
SqliteConnection connection;
|
||||
if (_databaseFilePath != null)
|
||||
{
|
||||
connection = new SqliteConnection($"Data Source={_databaseFilePath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
connection = new SqliteConnection("Data Source=:memory:");
|
||||
// When using an in-memory DB we have to open it manually
|
||||
// so EFCore doesn't open, close and wipe it.
|
||||
connection.Open();
|
||||
}
|
||||
|
||||
optionsBuilder.UseSqlite(connection);
|
||||
return optionsBuilder.Options;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,19 +112,14 @@ namespace Content.Server.AI.Utility.Actions
|
||||
UpdateBlackboard(context);
|
||||
var considerations = GetConsiderations(context);
|
||||
DebugTools.Assert(considerations.Count > 0);
|
||||
// I used the IAUS video although I did have some confusion on how to structure it overall
|
||||
// as some of the slides seemed contradictory
|
||||
|
||||
// Ideally we should early-out each action as cheaply as possible if it's not valid
|
||||
|
||||
// We also need some way to tell if the action isn't going to
|
||||
// have a better score than the current action (if applicable) and early-out that way as well.
|
||||
|
||||
// 23:00 Building a better centaur
|
||||
// Overall structure is based on Building a better centaur
|
||||
// Ideally we should early-out each action as cheaply as possible if it's not valid, thus
|
||||
// the finalScore can only go down over time.
|
||||
|
||||
var finalScore = 1.0f;
|
||||
var minThreshold = min / Bonus;
|
||||
context.GetState<ConsiderationState>().SetValue(considerations.Count);
|
||||
// See 10:09 for this and the adjustments
|
||||
|
||||
foreach (var consideration in considerations)
|
||||
{
|
||||
|
||||
@@ -13,18 +13,27 @@ namespace Content.Server.AI.Utility.Considerations
|
||||
private float GetAdjustedScore(Blackboard context)
|
||||
{
|
||||
var score = GetScore(context);
|
||||
/*
|
||||
* Now using the geometric mean
|
||||
* for n scores you take the n-th root of the scores multiplied
|
||||
* e.g. a, b, c scores you take Math.Pow(a * b * c, 1/3)
|
||||
* To get the ACTUAL geometric mean at any one stage you'd need to divide by the running consideration count
|
||||
* however, the downside to this is it will fluctuate up and down over time.
|
||||
* For our purposes if we go below the minimum threshold we want to cut it off, thus we take a
|
||||
* "running geometric mean" which can only ever go down (and by the final value will equal the actual geometric mean).
|
||||
*/
|
||||
|
||||
// Previously we used a makeupvalue method although the geometric mean is less punishing for more considerations
|
||||
var considerationsCount = context.GetState<ConsiderationState>().GetValue();
|
||||
var modificationFactor = 1.0f - 1.0f / considerationsCount;
|
||||
var makeUpValue = (1.0f - score) * modificationFactor;
|
||||
var adjustedScore = score + makeUpValue * score;
|
||||
return FloatMath.Clamp(adjustedScore, 0.0f, 1.0f);
|
||||
var adjustedScore = MathF.Pow(score, 1 / (float) considerationsCount);
|
||||
return MathHelper.Clamp(adjustedScore, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
private static float BoolCurve(float x)
|
||||
{
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
return x == 1.0f ? 1.0f : 0.0f;
|
||||
return x > 0.0f ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
public Func<float> BoolCurve(Blackboard context)
|
||||
@@ -42,7 +51,7 @@ namespace Content.Server.AI.Utility.Considerations
|
||||
private static float InverseBoolCurve(float x)
|
||||
{
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
return x == 1.0f ? 0.0f : 1.0f;
|
||||
return x == 0.0f ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
public Func<float> InverseBoolCurve(Blackboard context)
|
||||
@@ -59,7 +68,7 @@ namespace Content.Server.AI.Utility.Considerations
|
||||
[Pure]
|
||||
private static float LogisticCurve(float x, float slope, float exponent, float yOffset, float xOffset)
|
||||
{
|
||||
return FloatMath.Clamp(
|
||||
return MathHelper.Clamp(
|
||||
exponent * (1 / (1 + (float) Math.Pow(Math.Log(1000) * slope, -1 * x + xOffset))) + yOffset, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
@@ -77,7 +86,7 @@ namespace Content.Server.AI.Utility.Considerations
|
||||
[Pure]
|
||||
private static float QuadraticCurve(float x, float slope, float exponent, float yOffset, float xOffset)
|
||||
{
|
||||
return FloatMath.Clamp(slope * (float) Math.Pow(x - xOffset, exponent) + yOffset, 0.0f, 1.0f);
|
||||
return MathHelper.Clamp(slope * (float) Math.Pow(x - xOffset, exponent) + yOffset, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public Func<float> QuadraticCurve(Blackboard context, float slope, float exponent, float yOffset, float xOffset)
|
||||
|
||||
@@ -16,6 +16,9 @@ namespace Content.Server.AI.WorldState.States.Inventory
|
||||
{
|
||||
foreach (var item in handsComponent.GetAllHeldItems())
|
||||
{
|
||||
if (item.Owner.Deleted)
|
||||
continue;
|
||||
|
||||
yield return item.Owner;
|
||||
}
|
||||
}
|
||||
|
||||
40
Content.Server/Administration/ReadyAll.cs
Normal file
40
Content.Server/Administration/ReadyAll.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Administration
|
||||
{
|
||||
public class ReadyAll : IClientCommand
|
||||
{
|
||||
public string Command => "readyall";
|
||||
public string Description => "Readies up all players in the lobby.";
|
||||
public string Help => $"{Command} | ̣{Command} <ready>";
|
||||
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
|
||||
{
|
||||
var ready = true;
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
ready = bool.Parse(args[0]);
|
||||
}
|
||||
|
||||
var gameTicker = IoCManager.Resolve<IGameTicker>();
|
||||
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
|
||||
|
||||
if (gameTicker.RunLevel != GameRunLevel.PreRoundLobby)
|
||||
{
|
||||
shell.SendText(player, "This command can only be ran while in the lobby!");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var p in playerManager.GetAllPlayers())
|
||||
{
|
||||
gameTicker.ToggleReady(p, ready);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,14 +53,14 @@ namespace Content.Server.Atmos
|
||||
{
|
||||
if (throwTarget != GridCoordinates.InvalidGrid)
|
||||
{
|
||||
var moveForce = maxForce * FloatMath.Clamp(moveProb, 0, 100) / 150f;
|
||||
var moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 150f;
|
||||
var pos = ((throwTarget.Position - transform.GridPosition.Position).Normalized + direction.ToVec()).Normalized;
|
||||
LinearVelocity = pos * moveForce;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var moveForce = MathF.Min(maxForce * FloatMath.Clamp(moveProb, 0, 100) / 2500f, 20f);
|
||||
var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f);
|
||||
LinearVelocity = direction.ToVec() * moveForce;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Content.Server.Atmos
|
||||
{
|
||||
if(_soundCooldown == 0)
|
||||
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/Effects/space_wind.ogg",
|
||||
GridIndices.ToGridCoordinates(_mapManager, GridIndex), AudioHelpers.WithVariation(0.125f).WithVolume(FloatMath.Clamp(PressureDifference / 10, 10, 100)));
|
||||
GridIndices.ToGridCoordinates(_mapManager, GridIndex), AudioHelpers.WithVariation(0.125f).WithVolume(MathHelper.Clamp(PressureDifference / 10, 10, 100)));
|
||||
}
|
||||
|
||||
|
||||
@@ -1043,7 +1043,7 @@ namespace Content.Server.Atmos
|
||||
|
||||
private void HandleDecompressionFloorRip(float sum)
|
||||
{
|
||||
var chance = FloatMath.Clamp(sum / 500, 0.005f, 0.5f);
|
||||
var chance = MathHelper.Clamp(sum / 500, 0.005f, 0.5f);
|
||||
if (sum > 20 && _robustRandom.Prob(chance))
|
||||
_gridAtmosphereComponent.PryTile(GridIndices);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Content.Server.Body
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent body))
|
||||
if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent? body))
|
||||
{
|
||||
var random = IoCManager.Resolve<IRobustRandom>();
|
||||
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
|
||||
@@ -72,7 +72,7 @@ namespace Content.Server.Body
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent body))
|
||||
if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent? body))
|
||||
{
|
||||
var random = IoCManager.Resolve<IRobustRandom>();
|
||||
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
|
||||
@@ -119,7 +119,7 @@ namespace Content.Server.Body
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent body))
|
||||
if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent? body))
|
||||
{
|
||||
var random = IoCManager.Resolve<IRobustRandom>();
|
||||
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Content.Server.Body.Mechanisms.Behaviors
|
||||
base.PreMetabolism(frameTime);
|
||||
|
||||
if (Mechanism.Body == null ||
|
||||
!Mechanism.Body.Owner.TryGetComponent(out BloodstreamComponent bloodstream))
|
||||
!Mechanism.Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Content.Server.Body.Mechanisms.Behaviors
|
||||
base.PreMetabolism(frameTime);
|
||||
|
||||
if (Mechanism.Body == null ||
|
||||
!Mechanism.Body.Owner.TryGetComponent(out LungComponent lung))
|
||||
!Mechanism.Body.Owner.TryGetComponent(out LungComponent? lung))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Content.Server.Body.Mechanisms.Behaviors
|
||||
base.PreMetabolism(frameTime);
|
||||
|
||||
if (Mechanism.Body == null ||
|
||||
!Mechanism.Body.Owner.TryGetComponent(out StomachComponent stomach))
|
||||
!Mechanism.Body.Owner.TryGetComponent(out StomachComponent? stomach))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
@@ -32,9 +32,11 @@ namespace Content.Server.Chat
|
||||
if (args.Length < 1)
|
||||
return;
|
||||
|
||||
var chat = IoCManager.Resolve<IChatManager>();
|
||||
var message = string.Join(" ", args).Trim();
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
var message = string.Join(" ", args);
|
||||
var chat = IoCManager.Resolve<IChatManager>();
|
||||
|
||||
if (player.AttachedEntity.HasComponent<GhostComponent>())
|
||||
chat.SendDeadChat(player, message);
|
||||
@@ -61,9 +63,11 @@ namespace Content.Server.Chat
|
||||
if (args.Length < 1)
|
||||
return;
|
||||
|
||||
var chat = IoCManager.Resolve<IChatManager>();
|
||||
var action = string.Join(" ", args).Trim();
|
||||
if (string.IsNullOrEmpty(action))
|
||||
return;
|
||||
|
||||
var action = string.Join(" ", args);
|
||||
var chat = IoCManager.Resolve<IChatManager>();
|
||||
|
||||
var mindComponent = player.ContentData().Mind;
|
||||
chat.EntityMe(mindComponent.OwnedEntity, action);
|
||||
@@ -78,8 +82,15 @@ namespace Content.Server.Chat
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return;
|
||||
|
||||
var message = string.Join(" ", args).Trim();
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
var chat = IoCManager.Resolve<IChatManager>();
|
||||
chat.SendOOC(player, string.Join(" ", args));
|
||||
chat.SendOOC(player, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +102,15 @@ namespace Content.Server.Chat
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
return;
|
||||
|
||||
var message = string.Join(" ", args).Trim();
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
var chat = IoCManager.Resolve<IChatManager>();
|
||||
chat.SendAdminChat(player, string.Join(" ", args));
|
||||
chat.SendAdminChat(player, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,16 +71,16 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
|
||||
public static ICollection<string> FindAccessTags(IEntity entity)
|
||||
{
|
||||
if (entity.TryGetComponent(out IAccess accessComponent))
|
||||
if (entity.TryGetComponent(out IAccess? accessComponent))
|
||||
{
|
||||
return accessComponent.Tags;
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out IHandsComponent handsComponent))
|
||||
if (entity.TryGetComponent(out IHandsComponent? handsComponent))
|
||||
{
|
||||
var activeHandEntity = handsComponent.GetActiveHand?.Owner;
|
||||
if (activeHandEntity != null &&
|
||||
activeHandEntity.TryGetComponent(out IAccess handAccessComponent))
|
||||
activeHandEntity.TryGetComponent(out IAccess? handAccessComponent))
|
||||
{
|
||||
return handAccessComponent.Tags;
|
||||
}
|
||||
@@ -90,11 +90,11 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out InventoryComponent inventoryComponent))
|
||||
if (entity.TryGetComponent(out InventoryComponent? inventoryComponent))
|
||||
{
|
||||
if (inventoryComponent.HasSlot(EquipmentSlotDefines.Slots.IDCARD) &&
|
||||
inventoryComponent.TryGetSlotItem(EquipmentSlotDefines.Slots.IDCARD, out ItemComponent item) &&
|
||||
item.Owner.TryGetComponent(out IAccess idAccessComponent)
|
||||
item.Owner.TryGetComponent(out IAccess? idAccessComponent)
|
||||
)
|
||||
{
|
||||
return idAccessComponent.Tags;
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Content.Server.GameObjects.Components
|
||||
if (!force)
|
||||
{
|
||||
if (utilizing == null ||
|
||||
!utilizing.TryGetComponent(out ToolComponent tool) ||
|
||||
!utilizing.TryGetComponent(out ToolComponent? tool) ||
|
||||
!(await tool.UseTool(user, Owner, 0.5f, ToolQuality.Anchoring)))
|
||||
{
|
||||
return false;
|
||||
@@ -93,7 +93,7 @@ namespace Content.Server.GameObjects.Components
|
||||
/// <returns>true if toggled, false otherwise</returns>
|
||||
private async Task<bool> TryToggleAnchor(IEntity user, IEntity? utilizing = null, bool force = false)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out ICollidableComponent collidable))
|
||||
if (!Owner.TryGetComponent(out ICollidableComponent? collidable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
{
|
||||
_pressureDanger = GasAnalyzerDanger.Nominal;
|
||||
}
|
||||
|
||||
|
||||
Dirty();
|
||||
_timeSinceSync = 0f;
|
||||
}
|
||||
@@ -131,11 +131,11 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
if (session.AttachedEntity == null)
|
||||
return;
|
||||
|
||||
if (!session.AttachedEntity.TryGetComponent(out IHandsComponent handsComponent))
|
||||
if (!session.AttachedEntity.TryGetComponent(out IHandsComponent? handsComponent))
|
||||
return;
|
||||
|
||||
var activeHandEntity = handsComponent?.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent gasAnalyzer))
|
||||
if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent? gasAnalyzer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -147,7 +147,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
// Check if position is out of range => don't update
|
||||
if (!_position.Value.InRange(_mapManager, pos, SharedInteractionSystem.InteractionRange))
|
||||
return;
|
||||
|
||||
|
||||
pos = _position.Value;
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.TryGetComponent(out IHandsComponent handsComponent))
|
||||
if (!player.TryGetComponent(out IHandsComponent? handsComponent))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, player,
|
||||
Loc.GetString("You have no hands."));
|
||||
@@ -203,7 +203,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
}
|
||||
|
||||
var activeHandEntity = handsComponent.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent gasAnalyzer))
|
||||
if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent? gasAnalyzer))
|
||||
{
|
||||
_notifyManager.PopupMessage(serverMsg.Session.AttachedEntity,
|
||||
serverMsg.Session.AttachedEntity,
|
||||
@@ -225,7 +225,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
OpenInterface(actor.playerSession, eventArgs.ClickLocation);
|
||||
//TODO: show other sprite when ui open?
|
||||
@@ -236,7 +236,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
void IDropped.Dropped(DroppedEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
CloseInterface(actor.playerSession);
|
||||
//TODO: if other sprite is shown, change again
|
||||
@@ -245,7 +245,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
OpenInterface(actor.playerSession);
|
||||
//TODO: show other sprite when ui open?
|
||||
|
||||
@@ -272,7 +272,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
|
||||
private void CalculateSpeed()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out MovementSpeedModifierComponent playerMover))
|
||||
if (!Owner.TryGetComponent(out MovementSpeedModifierComponent? playerMover))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
/// </summary>
|
||||
private void BuckleStatus()
|
||||
{
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent? status))
|
||||
{
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Buckled,
|
||||
Buckled
|
||||
@@ -291,7 +291,7 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(BuckleVisuals.Buckled, true);
|
||||
}
|
||||
@@ -359,12 +359,12 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
Owner.Transform.WorldRotation = oldBuckledTo.Owner.Transform.WorldRotation;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(BuckleVisuals.Buckled, false);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out StunnableComponent stunnable) && stunnable.KnockedDown)
|
||||
if (Owner.TryGetComponent(out StunnableComponent? stunnable) && stunnable.KnockedDown)
|
||||
{
|
||||
StandingStateHelper.Down(Owner);
|
||||
}
|
||||
@@ -373,14 +373,14 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
StandingStateHelper.Standing(Owner);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out MobStateManagerComponent stateManager))
|
||||
if (Owner.TryGetComponent(out MobStateManagerComponent? stateManager))
|
||||
{
|
||||
stateManager.CurrentMobState.EnterState(Owner);
|
||||
}
|
||||
|
||||
BuckleStatus();
|
||||
|
||||
if (oldBuckledTo.Owner.TryGetComponent(out StrapComponent strap))
|
||||
if (oldBuckledTo.Owner.TryGetComponent(out StrapComponent? strap))
|
||||
{
|
||||
strap.Remove(this);
|
||||
_entitySystem.GetEntitySystem<AudioSystem>()
|
||||
@@ -535,7 +535,7 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
_entityManager.EventBus.UnsubscribeEvents(this);
|
||||
|
||||
if (BuckledTo != null &&
|
||||
BuckledTo.Owner.TryGetComponent(out StrapComponent strap))
|
||||
BuckledTo.Owner.TryGetComponent(out StrapComponent? strap))
|
||||
{
|
||||
strap.Remove(this);
|
||||
}
|
||||
@@ -552,7 +552,7 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
|
||||
if (BuckledTo != null &&
|
||||
Owner.Transform.WorldRotation.GetCardinalDir() == Direction.North &&
|
||||
BuckledTo.Owner.TryGetComponent(out SpriteComponent strapSprite))
|
||||
BuckledTo.Owner.TryGetComponent(out SpriteComponent? strapSprite))
|
||||
{
|
||||
drawDepth = strapSprite.DrawDepth - 1;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
{
|
||||
_state = value;
|
||||
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -93,7 +93,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent receiver) &&
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver) &&
|
||||
!receiver.Powered)
|
||||
{
|
||||
return false;
|
||||
@@ -114,7 +114,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out ICollidableComponent collidable) ||
|
||||
if (!entity.TryGetComponent(out ICollidableComponent? collidable) ||
|
||||
collidable.Anchored)
|
||||
{
|
||||
return false;
|
||||
@@ -155,7 +155,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ICollidableComponent collidable))
|
||||
if (entity.TryGetComponent(out ICollidableComponent? collidable))
|
||||
{
|
||||
var controller = collidable.EnsureController<ConveyedController>();
|
||||
controller.Move(direction, _speed * frameTime);
|
||||
@@ -225,7 +225,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!@switch.TryGetComponent(out ConveyorSwitchComponent component))
|
||||
if (!@switch.TryGetComponent(out ConveyorSwitchComponent? component))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -247,13 +247,13 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent conveyorSwitch))
|
||||
if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent? conveyorSwitch))
|
||||
{
|
||||
conveyorSwitch.Connect(this, eventArgs.User);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eventArgs.Using.TryGetComponent(out ToolComponent tool))
|
||||
if (eventArgs.Using.TryGetComponent(out ToolComponent? tool))
|
||||
{
|
||||
return await ToolUsed(eventArgs.User, tool);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
{
|
||||
_state = value;
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(ConveyorVisuals.State, value);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!conveyor.TryGetComponent(out ConveyorComponent component))
|
||||
if (!conveyor.TryGetComponent(out ConveyorComponent? component))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -172,7 +172,7 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!@switch.TryGetComponent(out ConveyorSwitchComponent component))
|
||||
if (!@switch.TryGetComponent(out ConveyorSwitchComponent? component))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -196,13 +196,13 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Using.TryGetComponent(out ConveyorComponent conveyor))
|
||||
if (eventArgs.Using.TryGetComponent(out ConveyorComponent? conveyor))
|
||||
{
|
||||
Connect(conveyor, eventArgs.User);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent otherSwitch))
|
||||
if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent? otherSwitch))
|
||||
{
|
||||
SyncWith(otherSwitch, eventArgs.User);
|
||||
return true;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out IDisposalTubeComponent tube))
|
||||
if (!entity.TryGetComponent(out IDisposalTubeComponent? tube))
|
||||
{
|
||||
shell.SendText(player, Loc.GetString("Entity with uid {0} doesn't have a {1} component", id, nameof(IDisposalTubeComponent)));
|
||||
return;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -41,6 +43,12 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
[ViewVariables]
|
||||
public IDisposalTubeComponent? NextTube { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of tags attached to the content, used for sorting
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public HashSet<string> Tags { get; set; } = new HashSet<string>();
|
||||
|
||||
private bool CanInsert(IEntity entity)
|
||||
{
|
||||
if (!_contents.CanInsert(entity))
|
||||
@@ -48,6 +56,12 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out ICollidableComponent? collidable) ||
|
||||
!collidable.CanCollide)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return entity.HasComponent<ItemComponent>() ||
|
||||
entity.HasComponent<IBodyManagerComponent>();
|
||||
}
|
||||
@@ -59,6 +73,11 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ICollidableComponent? collidable))
|
||||
{
|
||||
collidable.CanCollide = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -86,6 +105,11 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
foreach (var entity in _contents.ContainedEntities.ToArray())
|
||||
{
|
||||
if (entity.TryGetComponent(out ICollidableComponent? collidable))
|
||||
{
|
||||
collidable.CanCollide = true;
|
||||
}
|
||||
|
||||
_contents.ForceRemove(entity);
|
||||
|
||||
if (entity.Transform.Parent == Owner.Transform)
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalRouterComponent : DisposalJunctionComponent, IActivate
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "DisposalRouter";
|
||||
|
||||
[ViewVariables]
|
||||
private BoundUserInterface _userInterface;
|
||||
|
||||
[ViewVariables]
|
||||
private HashSet<string> _tags;
|
||||
|
||||
[ViewVariables]
|
||||
public bool Anchored =>
|
||||
!Owner.TryGetComponent(out CollidableComponent collidable) ||
|
||||
collidable.Anchored;
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
var directions = ConnectableDirections();
|
||||
|
||||
if (holder.Tags.Overlaps(_tags))
|
||||
{
|
||||
return directions[1];
|
||||
}
|
||||
|
||||
return Owner.Transform.LocalRotation.GetDir();
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(DisposalRouterUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
|
||||
_tags = new HashSet<string>();
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles ui messages from the client. For things such as button presses
|
||||
/// which interact with the world and require server action.
|
||||
/// </summary>
|
||||
/// <param name="obj">A user interface message from the client.</param>
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
var msg = (UiActionMessage) obj.Message;
|
||||
|
||||
if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity))
|
||||
return;
|
||||
|
||||
//Check for correct message and ignore maleformed strings
|
||||
if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tags))
|
||||
{
|
||||
_tags.Clear();
|
||||
foreach (var tag in msg.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
_tags.Add(tag.Trim());
|
||||
ClickSound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the player entity is able to use the configuration interface of the pipe tagger.
|
||||
/// </summary>
|
||||
/// <param name="playerEntity">The player entity.</param>
|
||||
/// <returns>Returns true if the entity can use the configuration interface, and false if it cannot.</returns>
|
||||
private bool PlayerCanUseDisposalTagger(IEntity playerEntity)
|
||||
{
|
||||
//Need player entity to check if they are still able to use the configuration interface
|
||||
if (playerEntity == null)
|
||||
return false;
|
||||
if (!Anchored)
|
||||
return false;
|
||||
//Check if player can interact in their current state
|
||||
if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets component data to be used to update the user interface client-side.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="SharedDisposalRouterComponent.DisposalRouterBoundUserInterfaceState"/></returns>
|
||||
private DisposalRouterUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
if(_tags == null || _tags.Count <= 0)
|
||||
{
|
||||
return new DisposalRouterUserInterfaceState("");
|
||||
}
|
||||
|
||||
var taglist = new System.Text.StringBuilder();
|
||||
|
||||
foreach (var tag in _tags)
|
||||
{
|
||||
taglist.Append(tag);
|
||||
taglist.Append(", ");
|
||||
}
|
||||
|
||||
taglist.Remove(taglist.Length - 2, 2);
|
||||
|
||||
return new DisposalRouterUserInterfaceState(taglist.ToString());
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
_userInterface.SetState(state);
|
||||
}
|
||||
|
||||
private void ClickSound()
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible.
|
||||
/// </summary>
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IActorComponent actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
Loc.GetString("You have no hands."));
|
||||
return;
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
_userInterface.Open(actor.playerSession);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
_userInterface.CloseAll();
|
||||
base.OnRemove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalTaggerComponent : DisposalTransitComponent, IActivate
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "DisposalTagger";
|
||||
|
||||
[ViewVariables]
|
||||
private BoundUserInterface _userInterface;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private string _tag = "";
|
||||
|
||||
[ViewVariables]
|
||||
public bool Anchored =>
|
||||
!Owner.TryGetComponent(out CollidableComponent collidable) ||
|
||||
collidable.Anchored;
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
holder.Tags.Add(_tag);
|
||||
return base.NextDirection(holder);
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(DisposalTaggerUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles ui messages from the client. For things such as button presses
|
||||
/// which interact with the world and require server action.
|
||||
/// </summary>
|
||||
/// <param name="obj">A user interface message from the client.</param>
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
var msg = (UiActionMessage) obj.Message;
|
||||
|
||||
if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity))
|
||||
return;
|
||||
|
||||
//Check for correct message and ignore maleformed strings
|
||||
if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tag))
|
||||
{
|
||||
_tag = msg.Tag;
|
||||
ClickSound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the player entity is able to use the configuration interface of the pipe tagger.
|
||||
/// </summary>
|
||||
/// <param name="playerEntity">The player entity.</param>
|
||||
/// <returns>Returns true if the entity can use the configuration interface, and false if it cannot.</returns>
|
||||
private bool PlayerCanUseDisposalTagger(IEntity playerEntity)
|
||||
{
|
||||
//Need player entity to check if they are still able to use the configuration interface
|
||||
if (playerEntity == null)
|
||||
return false;
|
||||
if (!Anchored)
|
||||
return false;
|
||||
//Check if player can interact in their current state
|
||||
if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets component data to be used to update the user interface client-side.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="SharedDisposalTaggerComponent.DisposalTaggerBoundUserInterfaceState"/></returns>
|
||||
private DisposalTaggerUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
return new DisposalTaggerUserInterfaceState(_tag);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
_userInterface.SetState(state);
|
||||
}
|
||||
|
||||
private void ClickSound()
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible.
|
||||
/// </summary>
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IActorComponent actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
Loc.GetString("You have no hands."));
|
||||
return;
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
_userInterface.Open(actor.playerSession);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
_userInterface.CloseAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
[ViewVariables]
|
||||
private bool Anchored =>
|
||||
!Owner.TryGetComponent(out CollidableComponent collidable) ||
|
||||
!Owner.TryGetComponent(out CollidableComponent? collidable) ||
|
||||
collidable.Anchored;
|
||||
|
||||
/// <summary>
|
||||
@@ -71,7 +71,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
var snapGrid = Owner.GetComponent<SnapGridComponent>();
|
||||
var tube = snapGrid
|
||||
.GetInDir(nextDirection)
|
||||
.Select(x => x.TryGetComponent(out IDisposalTubeComponent c) ? c : null)
|
||||
.Select(x => x.TryGetComponent(out IDisposalTubeComponent? c) ? c : null)
|
||||
.FirstOrDefault(x => x != null && x != this);
|
||||
|
||||
if (tube == null)
|
||||
@@ -153,7 +153,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
foreach (var entity in Contents.ContainedEntities.ToArray())
|
||||
{
|
||||
if (!entity.TryGetComponent(out DisposalHolderComponent holder))
|
||||
if (!entity.TryGetComponent(out DisposalHolderComponent? holder))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -171,7 +171,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
private void UpdateVisualState()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -187,7 +187,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
private void AnchoredChanged()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out CollidableComponent collidable))
|
||||
if (!Owner.TryGetComponent(out CollidableComponent? collidable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
[ViewVariables]
|
||||
public bool Powered =>
|
||||
!Owner.TryGetComponent(out PowerReceiverComponent receiver) ||
|
||||
!Owner.TryGetComponent(out PowerReceiverComponent? receiver) ||
|
||||
receiver.Powered;
|
||||
|
||||
[ViewVariables]
|
||||
public bool Anchored =>
|
||||
!Owner.TryGetComponent(out CollidableComponent collidable) ||
|
||||
!Owner.TryGetComponent(out CollidableComponent? collidable) ||
|
||||
collidable.Anchored;
|
||||
|
||||
[ViewVariables]
|
||||
@@ -122,6 +122,12 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out ICollidableComponent? collidable) ||
|
||||
!collidable.CanCollide)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entity.HasComponent<ItemComponent>() &&
|
||||
!entity.HasComponent<IBodyManagerComponent>())
|
||||
{
|
||||
@@ -153,7 +159,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
TryQueueEngage();
|
||||
|
||||
if (entity.TryGetComponent(out IActorComponent actor))
|
||||
if (entity.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
_userInterface.Close(actor.playerSession);
|
||||
}
|
||||
@@ -175,7 +181,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
private bool TryDrop(IEntity user, IEntity entity)
|
||||
{
|
||||
if (!user.TryGetComponent(out HandsComponent hands))
|
||||
if (!user.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -267,7 +273,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
private void TogglePower()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out PowerReceiverComponent receiver))
|
||||
if (!Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -346,7 +352,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
private void UpdateVisualState(bool flush)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -482,7 +488,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
var collidable = Owner.EnsureComponent<CollidableComponent>();
|
||||
collidable.AnchoredChanged += UpdateVisualState;
|
||||
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent receiver))
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
receiver.OnPowerStateChanged += PowerStateChanged;
|
||||
}
|
||||
@@ -492,12 +498,12 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
if (Owner.TryGetComponent(out ICollidableComponent collidable))
|
||||
if (Owner.TryGetComponent(out ICollidableComponent? collidable))
|
||||
{
|
||||
collidable.AnchoredChanged -= UpdateVisualState;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent receiver))
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
receiver.OnPowerStateChanged -= PowerStateChanged;
|
||||
}
|
||||
@@ -524,7 +530,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
switch (message)
|
||||
{
|
||||
case RelayMovementEntityMessage msg:
|
||||
if (!msg.Entity.TryGetComponent(out HandsComponent hands) ||
|
||||
if (!msg.Entity.TryGetComponent(out HandsComponent? hands) ||
|
||||
hands.Count == 0 ||
|
||||
_gameTiming.CurTime < _lastExitAttempt + ExitAttemptDelay)
|
||||
{
|
||||
@@ -553,7 +559,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Content.Server.GameObjects.Components
|
||||
{
|
||||
connectedClient = null;
|
||||
|
||||
if (!Owner.TryGetComponent(out IActorComponent actorComponent))
|
||||
if (!Owner.TryGetComponent(out IActorComponent? actorComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!await tool.UseTool(eventArgs.User, Owner, 3f, ToolQuality.Prying, AirlockCheck)) return false;
|
||||
if (!await tool.UseTool(eventArgs.User, Owner, 0.2f, ToolQuality.Prying, AirlockCheck)) return false;
|
||||
|
||||
if (State == DoorState.Closed)
|
||||
Open();
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
|
||||
foreach (var spillEntity in entityManager.GetEntitiesAt(spillTileMapGrid.ParentMapId, spillGridCoords.Position))
|
||||
{
|
||||
if (!spillEntity.TryGetComponent(out PuddleComponent puddleComponent))
|
||||
if (!spillEntity.TryGetComponent(out PuddleComponent? puddleComponent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -711,7 +711,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
|
||||
Dirty();
|
||||
|
||||
if (!message.Entity.TryGetComponent(out ICollidableComponent collidable))
|
||||
if (!message.Entity.TryGetComponent(out ICollidableComponent? collidable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -724,13 +724,13 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
|
||||
private void AddPullingStatuses(IEntity pulled)
|
||||
{
|
||||
if (pulled.TryGetComponent(out ServerStatusEffectsComponent pulledStatus))
|
||||
if (pulled.TryGetComponent(out ServerStatusEffectsComponent? pulledStatus))
|
||||
{
|
||||
pulledStatus.ChangeStatusEffectIcon(StatusEffect.Pulled,
|
||||
"/Textures/Interface/StatusEffects/Pull/pulled.png");
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent ownerStatus))
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent? ownerStatus))
|
||||
{
|
||||
ownerStatus.ChangeStatusEffectIcon(StatusEffect.Pulling,
|
||||
"/Textures/Interface/StatusEffects/Pull/pulling.png");
|
||||
@@ -739,12 +739,12 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
|
||||
private void RemovePullingStatuses(IEntity pulled)
|
||||
{
|
||||
if (pulled.TryGetComponent(out ServerStatusEffectsComponent pulledStatus))
|
||||
if (pulled.TryGetComponent(out ServerStatusEffectsComponent? pulledStatus))
|
||||
{
|
||||
pulledStatus.RemoveStatusEffect(StatusEffect.Pulled);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent ownerStatus))
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent? ownerStatus))
|
||||
{
|
||||
ownerStatus.RemoveStatusEffect(StatusEffect.Pulling);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
@@ -115,8 +116,14 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
public override void OnRemove()
|
||||
{
|
||||
var slots = _slotContainers.Keys.ToList();
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
if (TryGetSlotItem(slot, out ItemComponent item))
|
||||
{
|
||||
item.Owner.Delete();
|
||||
}
|
||||
|
||||
RemoveSlot(slot);
|
||||
}
|
||||
|
||||
@@ -267,17 +274,17 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
}
|
||||
|
||||
var inventorySlot = _slotContainers[slot];
|
||||
var item = inventorySlot.ContainedEntity.GetComponent<ItemComponent>();
|
||||
if (!inventorySlot.Remove(inventorySlot.ContainedEntity))
|
||||
var entity = inventorySlot.ContainedEntity;
|
||||
var item = entity.GetComponent<ItemComponent>();
|
||||
if (!inventorySlot.Remove(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: The item should be dropped to the container our owner is in, if any.
|
||||
var itemTransform = item.Owner.GetComponent<ITransformComponent>();
|
||||
itemTransform.GridPosition = Owner.GetComponent<ITransformComponent>().GridPosition;
|
||||
ContainerHelpers.AttachParentToContainerOrGrid(entity.Transform);
|
||||
|
||||
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedInteraction(Owner, item.Owner, slot);
|
||||
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedInteraction(Owner, entity, slot);
|
||||
|
||||
OnItemChanged?.Invoke();
|
||||
|
||||
@@ -286,6 +293,29 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ForceUnequip(Slots slot)
|
||||
{
|
||||
var inventorySlot = _slotContainers[slot];
|
||||
var entity = inventorySlot.ContainedEntity;
|
||||
if (entity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = entity.GetComponent<ItemComponent>();
|
||||
inventorySlot.ForceRemove(entity);
|
||||
|
||||
var itemTransform = entity.Transform;
|
||||
|
||||
ContainerHelpers.AttachParentToContainerOrGrid(itemTransform);
|
||||
|
||||
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedInteraction(Owner, item.Owner, slot);
|
||||
|
||||
OnItemChanged?.Invoke();
|
||||
|
||||
Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an item can be dropped from the specified slot.
|
||||
/// </summary>
|
||||
@@ -340,13 +370,11 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
throw new InvalidOperationException($"Slow '{slot}' does not exist.");
|
||||
}
|
||||
|
||||
if (GetSlotItem(slot) != null && !Unequip(slot))
|
||||
{
|
||||
// TODO: Handle this potential failiure better.
|
||||
throw new InvalidOperationException(
|
||||
"Unable to remove slot as the contained clothing could not be dropped");
|
||||
}
|
||||
ForceUnequip(slot);
|
||||
|
||||
var container = _slotContainers[slot];
|
||||
|
||||
container.Shutdown();
|
||||
_slotContainers.Remove(slot);
|
||||
|
||||
OnItemChanged?.Invoke();
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
if (entity.TryGetComponent(out AnchorableComponent anchorable))
|
||||
if (entity.TryGetComponent(out AnchorableComponent? anchorable))
|
||||
{
|
||||
anchorable.TryAnchor(player.AttachedEntity, force: true);
|
||||
}
|
||||
@@ -151,7 +151,7 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
if (entity.TryGetComponent(out AnchorableComponent anchorable))
|
||||
if (entity.TryGetComponent(out AnchorableComponent? anchorable))
|
||||
{
|
||||
anchorable.TryUnAnchor(player.AttachedEntity, force: true);
|
||||
}
|
||||
|
||||
@@ -101,13 +101,13 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
{
|
||||
EnsureInitialCalculated();
|
||||
|
||||
if (entity.TryGetComponent(out ServerStorageComponent storage) &&
|
||||
if (entity.TryGetComponent(out ServerStorageComponent? storage) &&
|
||||
storage._storageCapacityMax >= _storageCapacityMax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out StorableComponent store) &&
|
||||
if (entity.TryGetComponent(out StorableComponent? store) &&
|
||||
store.ObjectSize > _storageCapacityMax - _storageUsed)
|
||||
{
|
||||
return false;
|
||||
@@ -164,7 +164,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
|
||||
Logger.DebugS(LoggerName, $"Storage (UID {Owner.Uid}) had entity (UID {message.Entity.Uid}) removed from it.");
|
||||
|
||||
if (!message.Entity.TryGetComponent(out StorableComponent storable))
|
||||
if (!message.Entity.TryGetComponent(out StorableComponent? storable))
|
||||
{
|
||||
Logger.WarningS(LoggerName, $"Removed entity {message.Entity.Uid} without a StorableComponent from storage {Owner.Uid} at {Owner.Transform.MapPosition}");
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
{
|
||||
EnsureInitialCalculated();
|
||||
|
||||
if (!player.TryGetComponent(out IHandsComponent hands) ||
|
||||
if (!player.TryGetComponent(out IHandsComponent? hands) ||
|
||||
hands.GetActiveHand == null)
|
||||
{
|
||||
return false;
|
||||
@@ -317,7 +317,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
|
||||
private void UpdateDoorState()
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(StorageVisuals.Open, SubscribedSessions.Count != 0);
|
||||
}
|
||||
@@ -382,7 +382,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
|
||||
var item = entity.GetComponent<ItemComponent>();
|
||||
if (item == null ||
|
||||
!player.TryGetComponent(out HandsComponent hands))
|
||||
!player.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -496,7 +496,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
|
||||
foreach (var entity in storedEntities)
|
||||
{
|
||||
var exActs = entity.GetAllComponents<IExAct>();
|
||||
var exActs = entity.GetAllComponents<IExAct>().ToArray();
|
||||
foreach (var exAct in exActs)
|
||||
{
|
||||
exAct.OnExplosion(eventArgs);
|
||||
@@ -506,7 +506,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
|
||||
bool IDragDrop.CanDragDrop(DragDropEventArgs eventArgs)
|
||||
{
|
||||
return eventArgs.Target.TryGetComponent(out PlaceableSurfaceComponent placeable) &&
|
||||
return eventArgs.Target.TryGetComponent(out PlaceableSurfaceComponent? placeable) &&
|
||||
placeable.IsPlaceable;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Medical;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -38,9 +39,14 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
_appearance = Owner.GetComponent<AppearanceComponent>();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(MedicalScannerUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
_bodyContainer = ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-bodyContainer", Owner);
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
|
||||
//TODO: write this so that it checks for a change in power events and acts accordingly.
|
||||
var newState = GetUserInterfaceState();
|
||||
_userInterface.SetState(newState);
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
@@ -48,7 +54,8 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
new MedicalScannerBoundUserInterfaceState(
|
||||
null,
|
||||
new Dictionary<DamageClass, int>(),
|
||||
new Dictionary<DamageType, int>());
|
||||
new Dictionary<DamageType, int>(),
|
||||
false);
|
||||
|
||||
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
@@ -68,7 +75,7 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
var classes = new Dictionary<DamageClass, int>(damageable.DamageClasses);
|
||||
var types = new Dictionary<DamageType, int>(damageable.DamageTypes);
|
||||
|
||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types);
|
||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, CloningSystem.HasUid(body.Uid));
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
@@ -92,12 +99,18 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
default: throw new ArgumentException(nameof(damageState));
|
||||
}
|
||||
}
|
||||
|
||||
private MedicalScannerStatus GetStatus()
|
||||
{
|
||||
var body = _bodyContainer.ContainedEntity;
|
||||
return body == null
|
||||
? MedicalScannerStatus.Open
|
||||
: GetStatusFromDamageState(body.GetComponent<IDamageableComponent>().CurrentDamageState);
|
||||
if (Powered)
|
||||
{
|
||||
var body = _bodyContainer.ContainedEntity;
|
||||
return body == null
|
||||
? MedicalScannerStatus.Open
|
||||
: GetStatusFromDamageState(body.GetComponent<IDamageableComponent>().CurrentDamageState);
|
||||
}
|
||||
|
||||
return MedicalScannerStatus.Off;
|
||||
}
|
||||
|
||||
private void UpdateAppearance()
|
||||
@@ -178,14 +191,28 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
if (_bodyContainer.ContainedEntity == null)
|
||||
{
|
||||
// There's no need to update if there's no one inside
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateUserInterface();
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (!(obj.Message is UiButtonPressedMessage message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.Button)
|
||||
{
|
||||
case UiButton.ScanDNA:
|
||||
if (_bodyContainer.ContainedEntity != null)
|
||||
{
|
||||
CloningSystem.AddToScannedUids(_bodyContainer.ContainedEntity.Uid);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace Content.Server.GameObjects.Components.Metabolism
|
||||
if (Suffocating &&
|
||||
Owner.TryGetComponent(out IDamageableComponent damageable))
|
||||
{
|
||||
damageable.ChangeDamage(DamageClass.Airloss, _suffocationDamage, false);
|
||||
// damageable.ChangeDamage(DamageClass.Airloss, _suffocationDamage, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
var visiting = Mind?.VisitingEntity;
|
||||
if (visiting != null)
|
||||
{
|
||||
if (visiting.TryGetComponent(out GhostComponent ghost))
|
||||
if (visiting.TryGetComponent(out GhostComponent? ghost))
|
||||
{
|
||||
ghost.CanReturnToBody = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Server.GameObjects.Components.Body;
|
||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Robust.Shared.Maths;
|
||||
using System;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IClimbable))]
|
||||
public class ClimbableComponent : SharedClimbableComponent, IDragDropOn
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
/// <summary>
|
||||
/// The range from which this entity can be climbed.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private float _range;
|
||||
|
||||
/// <summary>
|
||||
/// The time it takes to climb onto the entity.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private float _climbDelay;
|
||||
|
||||
private ICollidableComponent _collidableComponent;
|
||||
private DoAfterSystem _doAfterSystem;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_collidableComponent = Owner.GetComponent<ICollidableComponent>();
|
||||
_doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _range, "range", SharedInteractionSystem.InteractionRange / 1.4f);
|
||||
serializer.DataField(ref _climbDelay, "delay", 0.8f);
|
||||
}
|
||||
|
||||
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(eventArgs.User))
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eventArgs.User == eventArgs.Dropped) // user is dragging themselves onto a climbable
|
||||
{
|
||||
if (!eventArgs.User.HasComponent<ClimbingComponent>())
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are incapable of climbing!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var bodyManager = eventArgs.User.GetComponent<BodyManagerComponent>();
|
||||
|
||||
if (bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Leg).Count == 0 ||
|
||||
bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Foot).Count == 0)
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are unable to climb!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var userPosition = eventArgs.User.Transform.MapPosition;
|
||||
var climbablePosition = eventArgs.Target.Transform.MapPosition;
|
||||
var interaction = EntitySystem.Get<SharedInteractionSystem>();
|
||||
bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User);
|
||||
|
||||
if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored))
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // user is dragging some other entity onto a climbable
|
||||
{
|
||||
if (eventArgs.Target == null || !eventArgs.Dropped.HasComponent<ClimbingComponent>())
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var userPosition = eventArgs.User.Transform.MapPosition;
|
||||
var otherUserPosition = eventArgs.Dropped.Transform.MapPosition;
|
||||
var climbablePosition = eventArgs.Target.Transform.MapPosition;
|
||||
var interaction = EntitySystem.Get<SharedInteractionSystem>();
|
||||
bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User || entity == eventArgs.Dropped);
|
||||
|
||||
if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored) ||
|
||||
!interaction.InRangeUnobstructed(userPosition, otherUserPosition, _range, predicate: Ignored))
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.User == eventArgs.Dropped)
|
||||
{
|
||||
TryClimb(eventArgs.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
TryMoveEntity(eventArgs.User, eventArgs.Dropped);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void TryMoveEntity(IEntity user, IEntity entityToMove)
|
||||
{
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, _climbDelay, default, entityToMove)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
BreakOnStun = true
|
||||
};
|
||||
|
||||
var result = await _doAfterSystem.DoAfter(doAfterEventArgs);
|
||||
|
||||
if (result != DoAfterStatus.Cancelled && entityToMove.TryGetComponent(out ICollidableComponent body) && body.PhysicsShapes.Count >= 1)
|
||||
{
|
||||
var direction = (Owner.Transform.WorldPosition - entityToMove.Transform.WorldPosition).Normalized;
|
||||
var endPoint = Owner.Transform.WorldPosition;
|
||||
|
||||
var climbMode = entityToMove.GetComponent<ClimbingComponent>();
|
||||
climbMode.IsClimbing = true;
|
||||
|
||||
if (MathF.Abs(direction.X) < 0.6f) // user climbed mostly vertically so lets make it a clean straight line
|
||||
{
|
||||
endPoint = new Vector2(entityToMove.Transform.WorldPosition.X, endPoint.Y);
|
||||
}
|
||||
else if (MathF.Abs(direction.Y) < 0.6f) // user climbed mostly horizontally so lets make it a clean straight line
|
||||
{
|
||||
endPoint = new Vector2(endPoint.X, entityToMove.Transform.WorldPosition.Y);
|
||||
}
|
||||
|
||||
climbMode.TryMoveTo(entityToMove.Transform.WorldPosition, endPoint);
|
||||
// we may potentially need additional logic since we're forcing a player onto a climbable
|
||||
// there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for
|
||||
|
||||
PopupMessageOtherClientsInRange(user, Loc.GetString("{0:theName} forces {1:theName} onto {2:theName}!", user, entityToMove, Owner), 15);
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You force {0:theName} onto {1:theName}!", entityToMove, Owner));
|
||||
}
|
||||
}
|
||||
|
||||
private async void TryClimb(IEntity user)
|
||||
{
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, _climbDelay, default, Owner)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
BreakOnStun = true
|
||||
};
|
||||
|
||||
var result = await _doAfterSystem.DoAfter(doAfterEventArgs);
|
||||
|
||||
if (result != DoAfterStatus.Cancelled && user.TryGetComponent(out ICollidableComponent body) && body.PhysicsShapes.Count >= 1)
|
||||
{
|
||||
var direction = (Owner.Transform.WorldPosition - user.Transform.WorldPosition).Normalized;
|
||||
var endPoint = Owner.Transform.WorldPosition;
|
||||
|
||||
var climbMode = user.GetComponent<ClimbingComponent>();
|
||||
climbMode.IsClimbing = true;
|
||||
|
||||
if (MathF.Abs(direction.X) < 0.6f) // user climbed mostly vertically so lets make it a clean straight line
|
||||
{
|
||||
endPoint = new Vector2(user.Transform.WorldPosition.X, endPoint.Y);
|
||||
}
|
||||
else if (MathF.Abs(direction.Y) < 0.6f) // user climbed mostly horizontally so lets make it a clean straight line
|
||||
{
|
||||
endPoint = new Vector2(endPoint.X, user.Transform.WorldPosition.Y);
|
||||
}
|
||||
|
||||
climbMode.TryMoveTo(user.Transform.WorldPosition, endPoint);
|
||||
|
||||
PopupMessageOtherClientsInRange(user, Loc.GetString("{0:theName} jumps onto {1:theName}!", user, Owner), 15);
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You jump onto {0:theName}!", Owner));
|
||||
}
|
||||
}
|
||||
|
||||
private void PopupMessageOtherClientsInRange(IEntity source, string message, int maxReceiveDistance)
|
||||
{
|
||||
var viewers = _playerManager.GetPlayersInRange(source.Transform.GridPosition, maxReceiveDistance);
|
||||
|
||||
foreach (var viewer in viewers)
|
||||
{
|
||||
var viewerEntity = viewer.AttachedEntity;
|
||||
|
||||
if (viewerEntity == null || source == viewerEntity)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
source.PopupMessage(viewer.AttachedEntity, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ClimbingComponent : SharedClimbingComponent, IActionBlocker
|
||||
{
|
||||
private bool _isClimbing = false;
|
||||
private ClimbController _climbController = default;
|
||||
|
||||
public override bool IsClimbing
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isClimbing;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!value && Body != null)
|
||||
{
|
||||
Body.TryRemoveController<ClimbController>();
|
||||
}
|
||||
|
||||
_isClimbing = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the owner climb from one point to another
|
||||
/// </summary>
|
||||
public void TryMoveTo(Vector2 from, Vector2 to)
|
||||
{
|
||||
if (Body != null)
|
||||
{
|
||||
_climbController = Body.EnsureController<ClimbController>();
|
||||
_climbController.TryMoveTo(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
if (Body != null && IsClimbing)
|
||||
{
|
||||
if (_climbController != null && (_climbController.IsBlocked || !_climbController.IsActive))
|
||||
{
|
||||
if (Body.TryRemoveController<ClimbController>())
|
||||
{
|
||||
_climbController = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClimbing)
|
||||
{
|
||||
Body.WakeBody();
|
||||
}
|
||||
|
||||
if (!IsOnClimbableThisFrame && IsClimbing && _climbController == null)
|
||||
{
|
||||
IsClimbing = false;
|
||||
}
|
||||
|
||||
IsOnClimbableThisFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new ClimbModeComponentState(_isClimbing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
_entityManager.TryGetEntity(grid.GridEntityId, out var gridEntity))
|
||||
{
|
||||
//TODO: Switch to shuttle component
|
||||
if (!gridEntity.TryGetComponent(out ICollidableComponent collidable))
|
||||
if (!gridEntity.TryGetComponent(out ICollidableComponent? collidable))
|
||||
{
|
||||
collidable = gridEntity.AddComponent<CollidableComponent>();
|
||||
collidable.Mass = 1;
|
||||
@@ -137,9 +137,9 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
private void SetController(IEntity entity)
|
||||
{
|
||||
if (_controller != null ||
|
||||
!entity.TryGetComponent(out MindComponent mind) ||
|
||||
!entity.TryGetComponent(out MindComponent? mind) ||
|
||||
mind.Mind == null ||
|
||||
!Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
!Owner.TryGetComponent(out ServerStatusEffectsComponent? status))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -179,17 +179,17 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
/// <param name="entity">The entity to update</param>
|
||||
private void UpdateRemovedEntity(IEntity entity)
|
||||
{
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent? status))
|
||||
{
|
||||
status.RemoveStatusEffect(StatusEffect.Piloting);
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out MindComponent mind))
|
||||
if (entity.TryGetComponent(out MindComponent? mind))
|
||||
{
|
||||
mind.Mind?.UnVisit();
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out BuckleComponent buckle))
|
||||
if (entity.TryGetComponent(out BuckleComponent? buckle))
|
||||
{
|
||||
buckle.TryUnbuckle(entity, true);
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
|
||||
private void UpdatePDAAppearance()
|
||||
{
|
||||
_appearance?.SetData(PDAVisuals.ScreenLit, _lightOn);
|
||||
_appearance?.SetData(PDAVisuals.FlashlightLit, _lightOn);
|
||||
}
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
@@ -164,7 +164,7 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -175,7 +175,7 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
|
||||
public bool UseEntity(UseEntityEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Content.Server.GameObjects.Components.Pointing
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent sprite))
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.DrawDepth = (int) DrawDepth.Overlays;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Content.Server.GameObjects.Components.Pointing
|
||||
private void UpdateAppearance()
|
||||
{
|
||||
if (_chasing == null ||
|
||||
!Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
!Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Pointing
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent sprite))
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.DrawDepth = (int) DrawDepth.Overlays;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Content.Server.GameObjects.Components.Power
|
||||
|
||||
private void SetCurrentCharge(float newChargeAmount)
|
||||
{
|
||||
_currentCharge = FloatMath.Clamp(newChargeAmount, 0, MaxCharge);
|
||||
_currentCharge = MathHelper.Clamp(newChargeAmount, 0, MaxCharge);
|
||||
UpdateStorageState();
|
||||
OnChargeChanged();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
|
||||
|
||||
private void TryFlip(IEntity user)
|
||||
{
|
||||
if (Owner.TryGetComponent(out ICollidableComponent collidable) &&
|
||||
if (Owner.TryGetComponent(out ICollidableComponent? collidable) &&
|
||||
collidable.Anchored)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user, Loc.GetString("It's stuck."));
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
|
||||
{
|
||||
var currentTime = _gameTiming.CurTime;
|
||||
var timeSinceLastFire = (currentTime - _lastFire).TotalSeconds;
|
||||
var newTheta = FloatMath.Clamp(_currentAngle.Theta + _angleIncrease - _angleDecay * timeSinceLastFire, _minAngle.Theta, _maxAngle.Theta);
|
||||
var newTheta = MathHelper.Clamp(_currentAngle.Theta + _angleIncrease - _angleDecay * timeSinceLastFire, _minAngle.Theta, _maxAngle.Theta);
|
||||
_currentAngle = new Angle(newTheta);
|
||||
|
||||
var random = (_robustRandom.NextDouble() - 0.5) * 2;
|
||||
|
||||
@@ -373,7 +373,7 @@ namespace Content.Server.GameObjects.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.TryGetComponent(out IHandsComponent handsComponent))
|
||||
if (!player.TryGetComponent(out IHandsComponent? handsComponent))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, player,
|
||||
Loc.GetString("You have no hands."));
|
||||
|
||||
@@ -647,7 +647,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
|
||||
var additionalVector = (centerGrid.Position - entityGridCoords.Position);
|
||||
var distance = additionalVector.Length;
|
||||
// If we're too far no point, if we're close then cap it at the normalized vector
|
||||
distance = FloatMath.Clamp(2.5f - distance, 0.0f, 1.0f);
|
||||
distance = MathHelper.Clamp(2.5f - distance, 0.0f, 1.0f);
|
||||
additionalVector = new Angle(90 * distance).RotateVec(additionalVector);
|
||||
avoidanceVector += additionalVector;
|
||||
// if we do need to avoid that means we'll have to lookahead for the next tile
|
||||
|
||||
@@ -30,19 +30,19 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
[Robust.Shared.IoC.Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Robust.Shared.IoC.Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Robust.Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The tiles that have had their atmos data updated since last tick
|
||||
/// </summary>
|
||||
private Dictionary<GridId, HashSet<MapIndices>> _invalidTiles = new Dictionary<GridId, HashSet<MapIndices>>();
|
||||
|
||||
private Dictionary<IPlayerSession, PlayerGasOverlay> _knownPlayerChunks =
|
||||
|
||||
private Dictionary<IPlayerSession, PlayerGasOverlay> _knownPlayerChunks =
|
||||
new Dictionary<IPlayerSession, PlayerGasOverlay>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gas data stored in chunks to make PVS / bubbling easier.
|
||||
/// </summary>
|
||||
private Dictionary<GridId, Dictionary<MapIndices, GasOverlayChunk>> _overlay =
|
||||
private Dictionary<GridId, Dictionary<MapIndices, GasOverlayChunk>> _overlay =
|
||||
new Dictionary<GridId, Dictionary<MapIndices, GasOverlayChunk>>();
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +52,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
// Because the gas overlay updates aren't run every tick we need to avoid the pop-in that might occur with
|
||||
// the regular PVS range.
|
||||
private const float RangeOffset = 6.0f;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Overlay update ticks per second.
|
||||
/// </summary>
|
||||
@@ -164,8 +164,8 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
var moles = tile.Air.Gases[i];
|
||||
|
||||
if (moles < gas.GasMolesVisible) continue;
|
||||
|
||||
var data = new GasData(i, (byte) (FloatMath.Clamp01(moles / gas.GasMolesVisibleMax) * 255));
|
||||
|
||||
var data = new GasData(i, (byte) (MathHelper.Clamp01(moles / gas.GasMolesVisibleMax) * 255));
|
||||
tileData.Add(data);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -187,10 +187,10 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
private List<GasOverlayChunk> GetChunksInRange(IEntity entity)
|
||||
{
|
||||
var inRange = new List<GasOverlayChunk>();
|
||||
|
||||
|
||||
// This is the max in any direction that we can get a chunk (e.g. max 2 chunks away of data).
|
||||
var (maxXDiff, maxYDiff) = ((int) (_updateRange / ChunkSize) + 1, (int) (_updateRange / ChunkSize) + 1);
|
||||
|
||||
|
||||
var worldBounds = Box2.CenteredAround(entity.Transform.WorldPosition,
|
||||
new Vector2(_updateRange, _updateRange));
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
}
|
||||
|
||||
var entityTile = grid.GetTileRef(entity.Transform.GridPosition).GridIndices;
|
||||
|
||||
|
||||
for (var x = -maxXDiff; x <= maxXDiff; x++)
|
||||
{
|
||||
for (var y = -maxYDiff; y <= maxYDiff; y++)
|
||||
@@ -210,7 +210,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
var chunkIndices = GetGasChunkIndices(new MapIndices(entityTile.X + x * ChunkSize, entityTile.Y + y * ChunkSize));
|
||||
|
||||
if (!chunks.TryGetValue(chunkIndices, out var chunk)) continue;
|
||||
|
||||
|
||||
// Now we'll check if it's in range and relevant for us
|
||||
// (e.g. if we're on the very edge of a chunk we may need more chunks).
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
yDiff > 0 && yDiff > _updateRange ||
|
||||
xDiff < 0 && Math.Abs(xDiff + ChunkSize) > _updateRange ||
|
||||
yDiff < 0 && Math.Abs(yDiff + ChunkSize) > _updateRange) continue;
|
||||
|
||||
|
||||
inRange.Add(chunk);
|
||||
}
|
||||
}
|
||||
@@ -237,25 +237,25 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_updateRange = _configManager.GetCVar<float>("net.maxupdaterange") + RangeOffset;
|
||||
|
||||
|
||||
// TODO: So in the worst case scenario we still have to send a LOT of tile data per tick if there's a fire.
|
||||
// If we go with say 15 tile radius then we have up to 900 tiles to update per tick.
|
||||
// In a saltern fire the worst you'll normally see is around 650 at the moment.
|
||||
// Need a way to fake this more because sending almost 2,000 tile updates per second to even 50 players is... yikes
|
||||
// I mean that's as big as it gets so larger maps will have the same but still, that's a lot of data.
|
||||
|
||||
|
||||
// Some ways to do this are potentially: splitting fire and gas update data so they don't update at the same time
|
||||
// (gives the illusion of more updates happening), e.g. if gas updates are 3 times a second and fires are 1.6 times a second or something.
|
||||
// Could also look at updating tiles close to us more frequently (e.g. within 1 chunk every tick).
|
||||
// Stuff just out of our viewport we need so when we move it doesn't pop in but it doesn't mean we need to update it every tick.
|
||||
|
||||
|
||||
AccumulatedFrameTime -= _updateCooldown;
|
||||
|
||||
var gridAtmosComponents = new Dictionary<GridId, GridAtmosphereComponent>();
|
||||
var updatedTiles = new Dictionary<GasOverlayChunk, HashSet<MapIndices>>();
|
||||
|
||||
|
||||
// So up to this point we've been caching the updated tiles for multiple ticks.
|
||||
// Now we'll go through and check whether the update actually matters for the overlay or not,
|
||||
// and if not then we won't bother sending the data.
|
||||
@@ -263,7 +263,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
{
|
||||
var gridEntityId = _mapManager.GetGrid(gridId).GridEntityId;
|
||||
|
||||
if (!EntityManager.GetEntity(gridEntityId).TryGetComponent(out GridAtmosphereComponent gam))
|
||||
if (!EntityManager.GetEntity(gridEntityId).TryGetComponent(out GridAtmosphereComponent? gam))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -286,7 +286,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
tiles = new HashSet<MapIndices>();
|
||||
updatedTiles[chunk] = tiles;
|
||||
}
|
||||
|
||||
|
||||
updatedTiles[chunk].Add(invalid);
|
||||
chunk.Update(data, invalid);
|
||||
}
|
||||
@@ -306,13 +306,13 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
foreach (var (session, overlay) in _knownPlayerChunks)
|
||||
{
|
||||
if (session.AttachedEntity == null) continue;
|
||||
|
||||
|
||||
// Get chunks in range and update if we've moved around or the chunks have new overlay data
|
||||
var chunksInRange = GetChunksInRange(session.AttachedEntity);
|
||||
var knownChunks = overlay.GetKnownChunks();
|
||||
var chunksToRemove = new List<GasOverlayChunk>();
|
||||
var chunksToAdd = new List<GasOverlayChunk>();
|
||||
|
||||
|
||||
foreach (var chunk in chunksInRange)
|
||||
{
|
||||
if (!knownChunks.Contains(chunk))
|
||||
@@ -328,7 +328,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
chunksToRemove.Add(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (var chunk in chunksToAdd)
|
||||
{
|
||||
var message = overlay.AddChunk(currentTick, chunk);
|
||||
@@ -342,7 +342,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
{
|
||||
overlay.RemoveChunk(chunk);
|
||||
}
|
||||
|
||||
|
||||
var clientInvalids = new Dictionary<GridId, List<(MapIndices, GasOverlayData)>>();
|
||||
|
||||
// Check for any dirty chunks in range and bundle the data to send to the client.
|
||||
@@ -355,7 +355,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
existingData = new List<(MapIndices, GasOverlayData)>();
|
||||
clientInvalids[chunk.GridIndices] = existingData;
|
||||
}
|
||||
|
||||
|
||||
chunk.GetData(existingData, invalids);
|
||||
}
|
||||
|
||||
@@ -370,10 +370,10 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
}
|
||||
private sealed class PlayerGasOverlay
|
||||
{
|
||||
private readonly Dictionary<GridId, Dictionary<MapIndices, GasOverlayChunk>> _data =
|
||||
private readonly Dictionary<GridId, Dictionary<MapIndices, GasOverlayChunk>> _data =
|
||||
new Dictionary<GridId, Dictionary<MapIndices, GasOverlayChunk>>();
|
||||
|
||||
private readonly Dictionary<GasOverlayChunk, GameTick> _lastSent =
|
||||
|
||||
private readonly Dictionary<GasOverlayChunk, GameTick> _lastSent =
|
||||
new Dictionary<GasOverlayChunk, GameTick>();
|
||||
|
||||
public GasOverlayMessage UpdateClient(GridId grid, List<(MapIndices, GasOverlayData)> data)
|
||||
@@ -386,11 +386,11 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
_data.Clear();
|
||||
_lastSent.Clear();
|
||||
}
|
||||
|
||||
|
||||
public List<GasOverlayChunk> GetKnownChunks()
|
||||
{
|
||||
var known = new List<GasOverlayChunk>();
|
||||
|
||||
|
||||
foreach (var (_, chunks) in _data)
|
||||
{
|
||||
foreach (var (_, chunk) in chunks)
|
||||
@@ -414,7 +414,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
_lastSent[chunk] = currentTick;
|
||||
var message = ChunkToMessage(chunk);
|
||||
|
||||
@@ -444,7 +444,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
{
|
||||
// Chunk data should already be up to date.
|
||||
// Only send relevant tiles to client.
|
||||
|
||||
|
||||
var tileData = new List<(MapIndices, GasOverlayData)>();
|
||||
|
||||
for (var x = 0; x < ChunkSize; x++)
|
||||
@@ -467,7 +467,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return new GasOverlayMessage(chunk.GridIndices, tileData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
|
||||
if (!EntityManager.TryGetEntity(grid.GridEntityId, out var gridEnt)) return null;
|
||||
|
||||
return gridEnt.TryGetComponent(out IGridAtmosphereComponent atmos) ? atmos : null;
|
||||
return gridEnt.TryGetComponent(out IGridAtmosphereComponent? atmos) ? atmos : null;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
|
||||
18
Content.Server/GameObjects/EntitySystems/ClimbSystem.cs
Normal file
18
Content.Server/GameObjects/EntitySystems/ClimbSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class ClimbSystem : EntitySystem
|
||||
{
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
foreach (var comp in ComponentManager.EntityQuery<ClimbingComponent>())
|
||||
{
|
||||
comp.Update(frameTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Content.Server/GameObjects/EntitySystems/CloningSystem.cs
Normal file
24
Content.Server/GameObjects/EntitySystems/CloningSystem.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
internal sealed class CloningSystem : EntitySystem
|
||||
{
|
||||
public static List<EntityUid> scannedUids = new List<EntityUid>();
|
||||
|
||||
public static void AddToScannedUids(EntityUid uid)
|
||||
{
|
||||
if (!scannedUids.Contains(uid))
|
||||
{
|
||||
scannedUids.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasUid(EntityUid uid)
|
||||
{
|
||||
return scannedUids.Contains(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter
|
||||
|
||||
// For this we need to stay on the same hand slot and need the same item in that hand slot
|
||||
// (or if there is no item there we need to keep it free).
|
||||
if (eventArgs.NeedHand && eventArgs.User.TryGetComponent(out HandsComponent handsComponent))
|
||||
if (eventArgs.NeedHand && eventArgs.User.TryGetComponent(out HandsComponent? handsComponent))
|
||||
{
|
||||
_activeHand = handsComponent.ActiveHand;
|
||||
_activeItem = handsComponent.GetActiveHand;
|
||||
@@ -126,7 +126,7 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter
|
||||
}
|
||||
|
||||
if (EventArgs.BreakOnStun &&
|
||||
EventArgs.User.TryGetComponent(out StunnableComponent stunnableComponent) &&
|
||||
EventArgs.User.TryGetComponent(out StunnableComponent? stunnableComponent) &&
|
||||
stunnableComponent.Stunned)
|
||||
{
|
||||
return true;
|
||||
@@ -134,7 +134,7 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter
|
||||
|
||||
if (EventArgs.NeedHand)
|
||||
{
|
||||
if (!EventArgs.User.TryGetComponent(out HandsComponent handsComponent))
|
||||
if (!EventArgs.User.TryGetComponent(out HandsComponent? handsComponent))
|
||||
{
|
||||
// If we had a hand but no longer have it that's still a paddlin'
|
||||
if (_activeHand != null)
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
[UsedImplicitly]
|
||||
internal sealed class MedicalScannerSystem : EntitySystem
|
||||
{
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
foreach (var comp in ComponentManager.EntityQuery<MedicalScannerComponent>())
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
ev.Entity.RemoveComponent<PlayerInputMoverComponent>();
|
||||
}
|
||||
|
||||
if (ev.Entity.TryGetComponent(out ICollidableComponent physics) &&
|
||||
if (ev.Entity.TryGetComponent(out ICollidableComponent? physics) &&
|
||||
physics.TryGetController(out MoverController controller))
|
||||
{
|
||||
controller.StopMoving();
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Pointing;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Interfaces;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Robust.Server.GameObjects.Components;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Server.Player;
|
||||
@@ -40,6 +43,8 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
/// </summary>
|
||||
private readonly Dictionary<ICommonSession, TimeSpan> _pointers = new Dictionary<ICommonSession, TimeSpan>();
|
||||
|
||||
private const float PointingRange = 15f;
|
||||
|
||||
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus != SessionStatus.Disconnected)
|
||||
@@ -79,7 +84,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
|
||||
public bool TryPoint(ICommonSession? session, GridCoordinates coords, EntityUid uid)
|
||||
{
|
||||
var player = session?.AttachedEntity;
|
||||
var player = (session as IPlayerSession)?.ContentData().Mind.CurrentEntity;
|
||||
if (player == null)
|
||||
{
|
||||
return false;
|
||||
@@ -112,16 +117,27 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
}
|
||||
}
|
||||
|
||||
var viewers = _playerManager.GetPlayersInRange(player.Transform.GridPosition, 15);
|
||||
|
||||
var arrow = EntityManager.SpawnEntity("pointingarrow", coords);
|
||||
|
||||
if (player.TryGetComponent(out VisibilityComponent playerVisibility))
|
||||
var layer = (int)VisibilityFlags.Normal;
|
||||
if (player.TryGetComponent(out VisibilityComponent? playerVisibility))
|
||||
{
|
||||
var arrowVisibility = arrow.EnsureComponent<VisibilityComponent>();
|
||||
arrowVisibility.Layer = playerVisibility.Layer;
|
||||
layer = arrowVisibility.Layer = playerVisibility.Layer;
|
||||
}
|
||||
|
||||
// Get players that are in range and whose visibility layer matches the arrow's.
|
||||
var viewers = _playerManager.GetPlayersBy((playerSession) =>
|
||||
{
|
||||
if ((playerSession.VisibilityMask & layer) == 0)
|
||||
return false;
|
||||
|
||||
var ent = playerSession.ContentData().Mind.CurrentEntity;
|
||||
|
||||
return ent != null
|
||||
&& ent.Transform.MapPosition.InRange(player.Transform.MapPosition, PointingRange);
|
||||
});
|
||||
|
||||
string selfMessage;
|
||||
string viewerMessage;
|
||||
string? viewerPointedAtMessage = null;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using Content.Server.StationEvents;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Content.Server.StationEvents;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
@@ -9,25 +13,23 @@ using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using static Content.Shared.StationEvents.SharedStationEvent;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems.StationEvents
|
||||
{
|
||||
[UsedImplicitly]
|
||||
// Somewhat based off of TG's implementation of events
|
||||
public sealed class StationEventSystem : EntitySystem
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNetManager _netManager;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly IGameTicker _gameTicker;
|
||||
#pragma warning restore 649
|
||||
// Somewhat based off of TG's implementation of events
|
||||
|
||||
public StationEvent CurrentEvent { get; private set; }
|
||||
|
||||
public IReadOnlyCollection<StationEvent> StationEvents => _stationEvents;
|
||||
|
||||
private List<StationEvent> _stationEvents = new List<StationEvent>();
|
||||
|
||||
private const float MinimumTimeUntilFirstEvent = 600;
|
||||
@@ -194,7 +196,18 @@ namespace Content.Server.GameObjects.EntitySystems.StationEvents
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Stop events from happening in lobby and force active event to end if the round ends
|
||||
if (_gameTicker.RunLevel != GameTicking.GameRunLevel.InRound)
|
||||
{
|
||||
if (CurrentEvent != null)
|
||||
{
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep running the current event
|
||||
if (CurrentEvent != null)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user