Predict dumping (#32394)
* Predict dumping - This got soaped really fucking hard. - Dumping is predicted, this required disposals to be predicte.d - Disposals required mailing (because it's tightly coupled), and a smidge of other content systems. - I also had to fix a compnetworkgenerator issue at the same time so it wouldn't mispredict. * Fix a bunch of stuff * nasty merge * Some reviews * Some more reviews while I stash * Fix merge * Fix merge * Half of review * Review * re(h)f * lizards * feexes * feex
This commit is contained in:
25
Content.Client/Configurable/ConfigurationSystem.cs
Normal file
25
Content.Client/Configurable/ConfigurationSystem.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Content.Client.Configurable.UI;
|
||||
using Content.Shared.Configurable;
|
||||
|
||||
namespace Content.Client.Configurable;
|
||||
|
||||
public sealed class ConfigurationSystem : SharedConfigurationSystem
|
||||
{
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ConfigurationComponent, AfterAutoHandleStateEvent>(OnConfigurationState);
|
||||
}
|
||||
|
||||
private void OnConfigurationState(Entity<ConfigurationComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (_uiSystem.TryGetOpenUi<ConfigurationBoundUserInterface>(ent.Owner,
|
||||
ConfigurationComponent.ConfigurationUiKey.Key,
|
||||
out var bui))
|
||||
{
|
||||
bui.Refresh(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Robust.Client.GameObjects;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Shared.Configurable;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using static Content.Shared.Configurable.ConfigurationComponent;
|
||||
|
||||
namespace Content.Client.Configurable.UI
|
||||
@@ -19,16 +21,53 @@ namespace Content.Client.Configurable.UI
|
||||
base.Open();
|
||||
_menu = this.CreateWindow<ConfigurationMenu>();
|
||||
_menu.OnConfiguration += SendConfiguration;
|
||||
if (EntMan.TryGetComponent(Owner, out ConfigurationComponent? component))
|
||||
Refresh((Owner, component));
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
public void Refresh(Entity<ConfigurationComponent> entity)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not ConfigurationBoundUserInterfaceState configurationState)
|
||||
if (_menu == null)
|
||||
return;
|
||||
|
||||
_menu?.Populate(configurationState);
|
||||
_menu.Column.Children.Clear();
|
||||
_menu.Inputs.Clear();
|
||||
|
||||
foreach (var field in entity.Comp.Config)
|
||||
{
|
||||
var label = new Label
|
||||
{
|
||||
Margin = new Thickness(0, 0, 8, 0),
|
||||
Name = field.Key,
|
||||
Text = field.Key + ":",
|
||||
VerticalAlignment = Control.VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = .2f,
|
||||
MinSize = new Vector2(60, 0)
|
||||
};
|
||||
|
||||
var input = new LineEdit
|
||||
{
|
||||
Name = field.Key + "-input",
|
||||
Text = field.Value ?? "",
|
||||
IsValid = _menu.Validate,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = .8f
|
||||
};
|
||||
|
||||
_menu.Inputs.Add((field.Key, input));
|
||||
|
||||
var row = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal
|
||||
};
|
||||
|
||||
ConfigurationMenu.CopyProperties(_menu.Row, row);
|
||||
|
||||
row.AddChild(label);
|
||||
row.AddChild(input);
|
||||
_menu.Column.AddChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
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.Configurable.ConfigurationComponent;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
@@ -14,10 +10,10 @@ namespace Content.Client.Configurable.UI
|
||||
{
|
||||
public sealed class ConfigurationMenu : DefaultWindow
|
||||
{
|
||||
private readonly BoxContainer _column;
|
||||
private readonly BoxContainer _row;
|
||||
public readonly BoxContainer Column;
|
||||
public readonly BoxContainer Row;
|
||||
|
||||
private readonly List<(string name, LineEdit input)> _inputs;
|
||||
public readonly List<(string name, LineEdit input)> Inputs;
|
||||
|
||||
[ViewVariables]
|
||||
public Regex? Validation { get; internal set; }
|
||||
@@ -28,7 +24,7 @@ namespace Content.Client.Configurable.UI
|
||||
{
|
||||
MinSize = SetSize = new Vector2(300, 250);
|
||||
|
||||
_inputs = new List<(string name, LineEdit input)>();
|
||||
Inputs = new List<(string name, LineEdit input)>();
|
||||
|
||||
Title = Loc.GetString("configuration-menu-device-title");
|
||||
|
||||
@@ -39,14 +35,14 @@ namespace Content.Client.Configurable.UI
|
||||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
_column = new BoxContainer
|
||||
Column = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8),
|
||||
SeparationOverride = 16,
|
||||
};
|
||||
|
||||
_row = new BoxContainer
|
||||
Row = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
SeparationOverride = 16,
|
||||
@@ -69,61 +65,20 @@ namespace Content.Client.Configurable.UI
|
||||
ModulateSelfOverride = Color.FromHex("#202025")
|
||||
};
|
||||
|
||||
outerColumn.AddChild(_column);
|
||||
outerColumn.AddChild(Column);
|
||||
baseContainer.AddChild(outerColumn);
|
||||
baseContainer.AddChild(confirmButton);
|
||||
Contents.AddChild(baseContainer);
|
||||
}
|
||||
|
||||
public void Populate(ConfigurationBoundUserInterfaceState state)
|
||||
{
|
||||
_column.Children.Clear();
|
||||
_inputs.Clear();
|
||||
|
||||
foreach (var field in state.Config)
|
||||
{
|
||||
var label = new Label
|
||||
{
|
||||
Margin = new Thickness(0, 0, 8, 0),
|
||||
Name = field.Key,
|
||||
Text = field.Key + ":",
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = .2f,
|
||||
MinSize = new Vector2(60, 0)
|
||||
};
|
||||
|
||||
var input = new LineEdit
|
||||
{
|
||||
Name = field.Key + "-input",
|
||||
Text = field.Value ?? "",
|
||||
IsValid = Validate,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = .8f
|
||||
};
|
||||
|
||||
_inputs.Add((field.Key, input));
|
||||
|
||||
var row = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
CopyProperties(_row, row);
|
||||
|
||||
row.AddChild(label);
|
||||
row.AddChild(input);
|
||||
_column.AddChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConfirm(ButtonEventArgs args)
|
||||
{
|
||||
var config = GenerateDictionary(_inputs, "Text");
|
||||
var config = GenerateDictionary(Inputs, "Text");
|
||||
OnConfiguration?.Invoke(config);
|
||||
Close();
|
||||
}
|
||||
|
||||
private bool Validate(string value)
|
||||
public bool Validate(string value)
|
||||
{
|
||||
return Validation?.IsMatch(value) != false;
|
||||
}
|
||||
@@ -140,7 +95,7 @@ namespace Content.Client.Configurable.UI
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
private static void CopyProperties<T>(T from, T to) where T : Control
|
||||
public static void CopyProperties<T>(T from, T to) where T : Control
|
||||
{
|
||||
foreach (var property in from.AllAttachedProperties)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Content.Client.Decals
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprites = default!;
|
||||
|
||||
private DecalOverlay _overlay = default!;
|
||||
private DecalOverlay? _overlay;
|
||||
|
||||
private HashSet<uint> _removedUids = new();
|
||||
private readonly List<Vector2i> _removedChunks = new();
|
||||
@@ -31,6 +31,9 @@ namespace Content.Client.Decals
|
||||
|
||||
public void ToggleOverlay()
|
||||
{
|
||||
if (_overlay == null)
|
||||
return;
|
||||
|
||||
if (_overlayManager.HasOverlay<DecalOverlay>())
|
||||
{
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
@@ -44,6 +47,10 @@ namespace Content.Client.Decals
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
if (_overlay == null)
|
||||
return;
|
||||
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.DeviceNetwork.Systems;
|
||||
|
||||
namespace Content.Client.DeviceNetwork.Systems;
|
||||
|
||||
public sealed class DeviceNetworkSystem : SharedDeviceNetworkSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using Content.Shared.Disposal.Components;
|
||||
|
||||
namespace Content.Client.Disposal;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class DisposalUnitComponent : SharedDisposalUnitComponent
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Content.Client.Disposal.Unit;
|
||||
using Content.Client.Power.EntitySystems;
|
||||
using Content.Shared.Disposal;
|
||||
using Content.Shared.Disposal.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Disposal.Mailing;
|
||||
|
||||
public sealed class MailingUnitBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
public MailingUnitWindow? MailingUnitWindow;
|
||||
|
||||
public MailingUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private void ButtonPressed(DisposalUnitComponent.UiButton button)
|
||||
{
|
||||
SendMessage(new DisposalUnitComponent.UiButtonPressedMessage(button));
|
||||
// If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs
|
||||
// the pressure lerp up.
|
||||
}
|
||||
|
||||
private void TargetSelected(ItemList.ItemListSelectedEventArgs args)
|
||||
{
|
||||
var item = args.ItemList[args.ItemIndex];
|
||||
SendMessage(new TargetSelectedMessage(item.Text));
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
MailingUnitWindow = this.CreateWindow<MailingUnitWindow>();
|
||||
MailingUnitWindow.OpenCenteredRight();
|
||||
|
||||
MailingUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Eject);
|
||||
MailingUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Engage);
|
||||
MailingUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Power);
|
||||
|
||||
MailingUnitWindow.TargetListContainer.OnItemSelected += TargetSelected;
|
||||
|
||||
if (EntMan.TryGetComponent(Owner, out MailingUnitComponent? component))
|
||||
Refresh((Owner, component));
|
||||
}
|
||||
|
||||
public void Refresh(Entity<MailingUnitComponent> entity)
|
||||
{
|
||||
if (MailingUnitWindow == null)
|
||||
return;
|
||||
|
||||
// TODO: This should be decoupled from disposals
|
||||
if (EntMan.TryGetComponent(entity.Owner, out DisposalUnitComponent? disposals))
|
||||
{
|
||||
var disposalSystem = EntMan.System<DisposalUnitSystem>();
|
||||
|
||||
var disposalState = disposalSystem.GetState(Owner, disposals);
|
||||
var fullPressure = disposalSystem.EstimatedFullPressure(Owner, disposals);
|
||||
|
||||
MailingUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{disposalState}");
|
||||
MailingUnitWindow.FullPressure = fullPressure;
|
||||
MailingUnitWindow.PressureBar.UpdatePressure(fullPressure);
|
||||
MailingUnitWindow.Power.Pressed = EntMan.System<PowerReceiverSystem>().IsPowered(Owner);
|
||||
MailingUnitWindow.Engage.Pressed = disposals.Engaged;
|
||||
}
|
||||
|
||||
MailingUnitWindow.Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", entity.Comp.Tag ?? " "));
|
||||
//UnitTag.Text = state.Tag;
|
||||
MailingUnitWindow.Target.Text = entity.Comp.Target;
|
||||
|
||||
MailingUnitWindow.TargetListContainer.Clear();
|
||||
foreach (var target in entity.Comp.TargetList)
|
||||
{
|
||||
MailingUnitWindow.TargetListContainer.AddItem(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Content.Client/Disposal/Mailing/MailingUnitSystem.cs
Normal file
22
Content.Client/Disposal/Mailing/MailingUnitSystem.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Content.Shared.Disposal;
|
||||
using Content.Shared.Disposal.Components;
|
||||
using Content.Shared.Disposal.Mailing;
|
||||
|
||||
namespace Content.Client.Disposal.Mailing;
|
||||
|
||||
public sealed class MailingUnitSystem : SharedMailingUnitSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<MailingUnitComponent, AfterAutoHandleStateEvent>(OnMailingState);
|
||||
}
|
||||
|
||||
private void OnMailingState(Entity<MailingUnitComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (UserInterfaceSystem.TryGetOpenUi<MailingUnitBoundUserInterface>(ent.Owner, MailingUnitUiKey.Key, out var bui))
|
||||
{
|
||||
bui.Refresh(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.Disposal.UI"
|
||||
MinSize="300 400"
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:disposal="clr-namespace:Content.Client.Disposal"
|
||||
MinSize="300 400"
|
||||
SetSize="300 400"
|
||||
Resizable="False">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" SeparationOverride="8">
|
||||
<Label Text="{Loc 'ui-mailing-unit-target-label'}" />
|
||||
<Label Name="Target"
|
||||
Access="Public"
|
||||
Text="" />
|
||||
</BoxContainer>
|
||||
<ItemList Name="TargetListContainer"
|
||||
@@ -18,14 +20,15 @@
|
||||
</ItemList>
|
||||
<BoxContainer Orientation="Horizontal" SeparationOverride="4">
|
||||
<Label Text="{Loc 'ui-disposal-unit-label-state'}" />
|
||||
<Label Name="UnitState"
|
||||
<Label Name="UnitState" Access="Public"
|
||||
Text="{Loc 'ui-disposal-unit-label-status'}" />
|
||||
</BoxContainer>
|
||||
<Control MinSize="0 5" />
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
SeparationOverride="4">
|
||||
<Label Text="{Loc 'ui-disposal-unit-label-pressure'}" />
|
||||
<ui:PressureBar Name="PressureBar"
|
||||
<disposal:PressureBar Name="PressureBar"
|
||||
Access="Public"
|
||||
MinSize="190 20"
|
||||
HorizontalAlignment="Right"
|
||||
MinValue="0"
|
||||
@@ -50,4 +53,4 @@
|
||||
StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
</controls:FancyWindow>
|
||||
27
Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs
Normal file
27
Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Disposal.Mailing
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="Shared.Disposal.Components.MailingUnitComponent"/>
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class MailingUnitWindow : FancyWindow
|
||||
{
|
||||
public TimeSpan FullPressure;
|
||||
|
||||
public MailingUnitWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
PressureBar.UpdatePressure(FullPressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using Content.Shared.Disposal;
|
||||
using Content.Shared.Disposal.Unit;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Disposal.UI;
|
||||
namespace Content.Client.Disposal;
|
||||
|
||||
public sealed class PressureBar : ProgressBar
|
||||
{
|
||||
@@ -1,187 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Disposal;
|
||||
using Content.Shared.Disposal.Components;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.Disposal.Systems;
|
||||
|
||||
public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
|
||||
{
|
||||
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
|
||||
private const string AnimationKey = "disposal_unit_animation";
|
||||
|
||||
private const string DefaultFlushState = "disposal-flush";
|
||||
private const string DefaultChargeState = "disposal-charging";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DisposalUnitComponent, ComponentHandleState>(OnHandleState);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, PreventCollideEvent>(OnPreventCollide);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, CanDropTargetEvent>(OnCanDragDropOn);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, GotEmaggedEvent>(OnEmagged);
|
||||
|
||||
SubscribeLocalEvent<DisposalUnitComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<DisposalUnitComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, DisposalUnitComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not DisposalUnitComponentState state)
|
||||
return;
|
||||
|
||||
component.FlushSound = state.FlushSound;
|
||||
component.State = state.State;
|
||||
component.NextPressurized = state.NextPressurized;
|
||||
component.AutomaticEngageTime = state.AutomaticEngageTime;
|
||||
component.NextFlush = state.NextFlush;
|
||||
component.Powered = state.Powered;
|
||||
component.Engaged = state.Engaged;
|
||||
component.RecentlyEjected.Clear();
|
||||
component.RecentlyEjected.AddRange(EnsureEntityList<DisposalUnitComponent>(state.RecentlyEjected, uid));
|
||||
}
|
||||
|
||||
public override bool HasDisposals(EntityUid? uid)
|
||||
{
|
||||
return HasComp<DisposalUnitComponent>(uid);
|
||||
}
|
||||
|
||||
public override bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component)
|
||||
{
|
||||
if (component != null)
|
||||
return true;
|
||||
|
||||
TryComp<DisposalUnitComponent>(uid, out var storage);
|
||||
component = storage;
|
||||
return component != null;
|
||||
}
|
||||
|
||||
public override void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, SharedDisposalUnitComponent sharedDisposalUnit, ComponentInit args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite) || !TryComp<AppearanceComponent>(uid, out var appearance))
|
||||
return;
|
||||
|
||||
UpdateState(uid, sharedDisposalUnit, sprite, appearance);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, SharedDisposalUnitComponent unit, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
UpdateState(uid, unit, args.Sprite, args.Component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visuals and tick animation
|
||||
/// </summary>
|
||||
private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, SpriteComponent sprite, AppearanceComponent appearance)
|
||||
{
|
||||
if (!_appearanceSystem.TryGetData<VisualState>(uid, Visuals.VisualState, out var state, appearance))
|
||||
return;
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == VisualState.UnAnchored);
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == VisualState.Anchored);
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is VisualState.OverlayFlushing or VisualState.OverlayCharging);
|
||||
|
||||
var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer)
|
||||
? sprite.LayerGetState(chargingLayer)
|
||||
: new RSI.StateId(DefaultChargeState);
|
||||
|
||||
// This is a transient state so not too worried about replaying in range.
|
||||
if (state == VisualState.OverlayFlushing)
|
||||
{
|
||||
if (!_animationSystem.HasRunningAnimation(uid, AnimationKey))
|
||||
{
|
||||
var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer)
|
||||
? sprite.LayerGetState(flushLayer)
|
||||
: new RSI.StateId(DefaultFlushState);
|
||||
|
||||
// Setup the flush animation to play
|
||||
var anim = new Animation
|
||||
{
|
||||
Length = unit.FlushDelay,
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick
|
||||
{
|
||||
LayerKey = DisposalUnitVisualLayers.OverlayFlush,
|
||||
KeyFrames =
|
||||
{
|
||||
// Play the flush animation
|
||||
new AnimationTrackSpriteFlick.KeyFrame(flushState, 0),
|
||||
// Return to base state (though, depending on how the unit is
|
||||
// configured we might get an appearance change event telling
|
||||
// us to go to charging state)
|
||||
new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) unit.FlushDelay.TotalSeconds)
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if (unit.FlushSound != null)
|
||||
{
|
||||
anim.AnimationTracks.Add(
|
||||
new AnimationTrackPlaySound
|
||||
{
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(unit.FlushSound), 0)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_animationSystem.Play(uid, anim, AnimationKey);
|
||||
}
|
||||
}
|
||||
else if (state == VisualState.OverlayCharging)
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState);
|
||||
else
|
||||
_animationSystem.Stop(uid, AnimationKey);
|
||||
|
||||
if (!_appearanceSystem.TryGetData<HandleState>(uid, Visuals.Handle, out var handleState, appearance))
|
||||
handleState = HandleState.Normal;
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != HandleState.Normal);
|
||||
|
||||
if (!_appearanceSystem.TryGetData<LightStates>(uid, Visuals.Light, out var lightState, appearance))
|
||||
lightState = LightStates.Off;
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging,
|
||||
(lightState & LightStates.Charging) != 0);
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady,
|
||||
(lightState & LightStates.Ready) != 0);
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull,
|
||||
(lightState & LightStates.Full) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DisposalUnitVisualLayers : byte
|
||||
{
|
||||
Unanchored,
|
||||
Base,
|
||||
BaseCharging,
|
||||
OverlayFlush,
|
||||
OverlayCharging,
|
||||
OverlayReady,
|
||||
OverlayFull,
|
||||
OverlayEngaged
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Client.Disposal.UI
|
||||
namespace Content.Client.Disposal.Tube
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalRouterWindow"/> and updates it when new server messages are received.
|
||||
@@ -1,11 +1,10 @@
|
||||
using Content.Shared.Disposal.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Client.Disposal.UI
|
||||
namespace Content.Client.Disposal.Tube
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalRouterComponent"/>
|
||||
@@ -1,9 +1,8 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent;
|
||||
|
||||
namespace Content.Client.Disposal.UI
|
||||
namespace Content.Client.Disposal.Tube
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalTaggerWindow"/> and updates it when new server messages are received.
|
||||
@@ -1,11 +1,10 @@
|
||||
using Content.Shared.Disposal.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent;
|
||||
|
||||
namespace Content.Client.Disposal.UI
|
||||
namespace Content.Client.Disposal.Tube
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalTaggerComponent"/>
|
||||
8
Content.Client/Disposal/Tube/DisposalTubeSystem.cs
Normal file
8
Content.Client/Disposal/Tube/DisposalTubeSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Disposal.Unit;
|
||||
|
||||
namespace Content.Client.Disposal.Tube;
|
||||
|
||||
public sealed class DisposalTubeSystem : SharedDisposalTubeSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
using Content.Client.Disposal.Systems;
|
||||
using Content.Shared.Disposal;
|
||||
using Content.Shared.Disposal.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.Disposal.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="MailingUnitWindow"/> or a <see cref="DisposalUnitWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class DisposalUnitBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
// What are you doing here
|
||||
[ViewVariables]
|
||||
public MailingUnitWindow? MailingUnitWindow;
|
||||
|
||||
[ViewVariables]
|
||||
public DisposalUnitWindow? DisposalUnitWindow;
|
||||
|
||||
public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiButton button)
|
||||
{
|
||||
SendMessage(new UiButtonPressedMessage(button));
|
||||
// If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs
|
||||
// the pressure lerp up.
|
||||
}
|
||||
|
||||
private void TargetSelected(ItemList.ItemListSelectedEventArgs args)
|
||||
{
|
||||
var item = args.ItemList[args.ItemIndex];
|
||||
SendMessage(new TargetSelectedMessage(item.Text));
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (UiKey is MailingUnitUiKey)
|
||||
{
|
||||
MailingUnitWindow = new MailingUnitWindow();
|
||||
|
||||
MailingUnitWindow.OpenCenteredRight();
|
||||
MailingUnitWindow.OnClose += Close;
|
||||
|
||||
MailingUnitWindow.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject);
|
||||
MailingUnitWindow.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage);
|
||||
MailingUnitWindow.Power.OnPressed += _ => ButtonPressed(UiButton.Power);
|
||||
|
||||
MailingUnitWindow.TargetListContainer.OnItemSelected += TargetSelected;
|
||||
}
|
||||
else if (UiKey is DisposalUnitUiKey)
|
||||
{
|
||||
DisposalUnitWindow = new DisposalUnitWindow();
|
||||
|
||||
DisposalUnitWindow.OpenCenteredRight();
|
||||
DisposalUnitWindow.OnClose += Close;
|
||||
|
||||
DisposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject);
|
||||
DisposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage);
|
||||
DisposalUnitWindow.Power.OnPressed += _ => ButtonPressed(UiButton.Power);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not MailingUnitBoundUserInterfaceState && state is not DisposalUnitBoundUserInterfaceState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case MailingUnitBoundUserInterfaceState mailingUnitState:
|
||||
MailingUnitWindow?.UpdateState(mailingUnitState);
|
||||
break;
|
||||
|
||||
case DisposalUnitBoundUserInterfaceState disposalUnitState:
|
||||
DisposalUnitWindow?.UpdateState(disposalUnitState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
MailingUnitWindow?.Dispose();
|
||||
DisposalUnitWindow?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using Content.Shared.Disposal.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.Disposal.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalUnitComponent"/>
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class DisposalUnitWindow : DefaultWindow
|
||||
{
|
||||
public TimeSpan FullPressure;
|
||||
|
||||
public DisposalUnitWindow()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the interface state for the disposals window.
|
||||
/// </summary>
|
||||
/// <returns>true if we should stop updating every frame.</returns>
|
||||
public void UpdateState(DisposalUnitBoundUserInterfaceState state)
|
||||
{
|
||||
Title = state.UnitName;
|
||||
UnitState.Text = state.UnitState;
|
||||
Power.Pressed = state.Powered;
|
||||
Engage.Pressed = state.Engaged;
|
||||
FullPressure = state.FullPressureTime;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
PressureBar.UpdatePressure(FullPressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using Content.Shared.Disposal;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Disposal.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="MailingUnitComponent"/>
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class MailingUnitWindow : DefaultWindow
|
||||
{
|
||||
public TimeSpan FullPressure;
|
||||
|
||||
public MailingUnitWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the interface state for the disposals window.
|
||||
/// </summary>
|
||||
/// <returns>true if we should stop updating every frame.</returns>
|
||||
public bool UpdateState(MailingUnitBoundUserInterfaceState state)
|
||||
{
|
||||
var disposalState = state.DisposalState;
|
||||
|
||||
Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", state.Tag ?? " "));
|
||||
UnitState.Text = disposalState.UnitState;
|
||||
FullPressure = disposalState.FullPressureTime;
|
||||
var pressureReached = PressureBar.UpdatePressure(disposalState.FullPressureTime);
|
||||
Power.Pressed = disposalState.Powered;
|
||||
Engage.Pressed = disposalState.Engaged;
|
||||
|
||||
//UnitTag.Text = state.Tag;
|
||||
Target.Text = state.Target;
|
||||
|
||||
TargetListContainer.Clear();
|
||||
foreach (var target in state.TargetList)
|
||||
{
|
||||
TargetListContainer.AddItem(target);
|
||||
}
|
||||
|
||||
return !disposalState.Powered || pressureReached;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
PressureBar.UpdatePressure(FullPressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using Content.Client.Disposal.Mailing;
|
||||
using Content.Client.Power.EntitySystems;
|
||||
using Content.Shared.Disposal.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Disposal.Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="MailingUnitWindow"/> or a <see cref="_disposalUnitWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class DisposalUnitBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables] private DisposalUnitWindow? _disposalUnitWindow;
|
||||
|
||||
public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private void ButtonPressed(DisposalUnitComponent.UiButton button)
|
||||
{
|
||||
SendPredictedMessage(new DisposalUnitComponent.UiButtonPressedMessage(button));
|
||||
// If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs
|
||||
// the pressure lerp up.
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_disposalUnitWindow = this.CreateWindow<DisposalUnitWindow>();
|
||||
|
||||
_disposalUnitWindow.OpenCenteredRight();
|
||||
|
||||
_disposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Eject);
|
||||
_disposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Engage);
|
||||
_disposalUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Power);
|
||||
|
||||
if (EntMan.TryGetComponent(Owner, out DisposalUnitComponent? component))
|
||||
{
|
||||
Refresh((Owner, component));
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh(Entity<DisposalUnitComponent> entity)
|
||||
{
|
||||
if (_disposalUnitWindow == null)
|
||||
return;
|
||||
|
||||
var disposalSystem = EntMan.System<DisposalUnitSystem>();
|
||||
|
||||
_disposalUnitWindow.Title = EntMan.GetComponent<MetaDataComponent>(entity.Owner).EntityName;
|
||||
|
||||
var state = disposalSystem.GetState(entity.Owner, entity.Comp);
|
||||
|
||||
_disposalUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{state}");
|
||||
_disposalUnitWindow.Power.Pressed = EntMan.System<PowerReceiverSystem>().IsPowered(Owner);
|
||||
_disposalUnitWindow.Engage.Pressed = entity.Comp.Engaged;
|
||||
_disposalUnitWindow.FullPressure = disposalSystem.EstimatedFullPressure(entity.Owner, entity.Comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
156
Content.Client/Disposal/Unit/DisposalUnitSystem.cs
Normal file
156
Content.Client/Disposal/Unit/DisposalUnitSystem.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using Content.Shared.Disposal.Components;
|
||||
using Content.Shared.Disposal.Unit;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Client.Disposal.Unit;
|
||||
|
||||
public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
|
||||
{
|
||||
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
private const string AnimationKey = "disposal_unit_animation";
|
||||
|
||||
private const string DefaultFlushState = "disposal-flush";
|
||||
private const string DefaultChargeState = "disposal-charging";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DisposalUnitComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
|
||||
SubscribeLocalEvent<DisposalUnitComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, DisposalUnitComponent component, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateUI((uid, component));
|
||||
}
|
||||
|
||||
protected override void UpdateUI(Entity<DisposalUnitComponent> entity)
|
||||
{
|
||||
if (_uiSystem.TryGetOpenUi<DisposalUnitBoundUserInterface>(entity.Owner, DisposalUnitComponent.DisposalUnitUiKey.Key, out var bui))
|
||||
{
|
||||
bui.Refresh(entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisposalInit(Entity<DisposalUnitComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
base.OnDisposalInit(ent, ref args);
|
||||
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite) || !TryComp<AppearanceComponent>(ent, out var appearance))
|
||||
return;
|
||||
|
||||
UpdateState(ent, sprite, appearance);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(Entity<DisposalUnitComponent> ent, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
UpdateState(ent, args.Sprite, args.Component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visuals and tick animation
|
||||
/// </summary>
|
||||
private void UpdateState(Entity<DisposalUnitComponent> ent, SpriteComponent sprite, AppearanceComponent appearance)
|
||||
{
|
||||
if (!_appearanceSystem.TryGetData<DisposalUnitComponent.VisualState>(ent, DisposalUnitComponent.Visuals.VisualState, out var state, appearance))
|
||||
return;
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == DisposalUnitComponent.VisualState.UnAnchored);
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == DisposalUnitComponent.VisualState.Anchored);
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is DisposalUnitComponent.VisualState.OverlayFlushing or DisposalUnitComponent.VisualState.OverlayCharging);
|
||||
|
||||
var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer)
|
||||
? sprite.LayerGetState(chargingLayer)
|
||||
: new RSI.StateId(DefaultChargeState);
|
||||
|
||||
// This is a transient state so not too worried about replaying in range.
|
||||
if (state == DisposalUnitComponent.VisualState.OverlayFlushing)
|
||||
{
|
||||
if (!_animationSystem.HasRunningAnimation(ent, AnimationKey))
|
||||
{
|
||||
var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer)
|
||||
? sprite.LayerGetState(flushLayer)
|
||||
: new RSI.StateId(DefaultFlushState);
|
||||
|
||||
// Setup the flush animation to play
|
||||
var anim = new Animation
|
||||
{
|
||||
Length = ent.Comp.FlushDelay,
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick
|
||||
{
|
||||
LayerKey = DisposalUnitVisualLayers.OverlayFlush,
|
||||
KeyFrames =
|
||||
{
|
||||
// Play the flush animation
|
||||
new AnimationTrackSpriteFlick.KeyFrame(flushState, 0),
|
||||
// Return to base state (though, depending on how the unit is
|
||||
// configured we might get an appearance change event telling
|
||||
// us to go to charging state)
|
||||
new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) ent.Comp.FlushDelay.TotalSeconds)
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if (ent.Comp.FlushSound != null)
|
||||
{
|
||||
anim.AnimationTracks.Add(
|
||||
new AnimationTrackPlaySound
|
||||
{
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(ent.Comp.FlushSound), 0)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_animationSystem.Play(ent, anim, AnimationKey);
|
||||
}
|
||||
}
|
||||
else if (state == DisposalUnitComponent.VisualState.OverlayCharging)
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState);
|
||||
else
|
||||
_animationSystem.Stop(ent.Owner, AnimationKey);
|
||||
|
||||
if (!_appearanceSystem.TryGetData<DisposalUnitComponent.HandleState>(ent, DisposalUnitComponent.Visuals.Handle, out var handleState, appearance))
|
||||
handleState = DisposalUnitComponent.HandleState.Normal;
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != DisposalUnitComponent.HandleState.Normal);
|
||||
|
||||
if (!_appearanceSystem.TryGetData<DisposalUnitComponent.LightStates>(ent, DisposalUnitComponent.Visuals.Light, out var lightState, appearance))
|
||||
lightState = DisposalUnitComponent.LightStates.Off;
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging,
|
||||
(lightState & DisposalUnitComponent.LightStates.Charging) != 0);
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady,
|
||||
(lightState & DisposalUnitComponent.LightStates.Ready) != 0);
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull,
|
||||
(lightState & DisposalUnitComponent.LightStates.Full) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DisposalUnitVisualLayers : byte
|
||||
{
|
||||
Unanchored,
|
||||
Base,
|
||||
BaseCharging,
|
||||
OverlayFlush,
|
||||
OverlayCharging,
|
||||
OverlayReady,
|
||||
OverlayFull,
|
||||
OverlayEngaged
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.Disposal.UI"
|
||||
MinSize="300 140"
|
||||
SetSize="300 140"
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:disposal="clr-namespace:Content.Client.Disposal"
|
||||
MinSize="300 140"
|
||||
SetSize="300 160"
|
||||
Resizable="False">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Vertical" Margin="10">
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
SeparationOverride="4">
|
||||
<Label Text="{Loc 'ui-disposal-unit-label-state'}" />
|
||||
<Label Name="UnitState"
|
||||
<Label Name="UnitState" Access="Public"
|
||||
Text="{Loc 'ui-disposal-unit-label-status'}" />
|
||||
</BoxContainer>
|
||||
<Control MinSize="0 5" />
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
SeparationOverride="4">
|
||||
<Label Text="{Loc 'ui-disposal-unit-label-pressure'}" />
|
||||
<ui:PressureBar Name="PressureBar"
|
||||
<disposal:PressureBar Name="PressureBar"
|
||||
MinSize="190 20"
|
||||
HorizontalAlignment="Right"
|
||||
MinValue="0"
|
||||
@@ -23,7 +24,7 @@
|
||||
Value="0.5" />
|
||||
</BoxContainer>
|
||||
<Control MinSize="0 10" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Button Name="Engage"
|
||||
Access="Public"
|
||||
Text="{Loc 'ui-disposal-unit-button-flush'}"
|
||||
@@ -39,4 +40,4 @@
|
||||
StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
</controls:FancyWindow>
|
||||
28
Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs
Normal file
28
Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Disposal.Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="Shared.Disposal.Components.DisposalUnitComponent"/>
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class DisposalUnitWindow : FancyWindow
|
||||
{
|
||||
public TimeSpan FullPressure;
|
||||
|
||||
public DisposalUnitWindow()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
PressureBar.UpdatePressure(FullPressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Shared.DeviceNetwork;
|
||||
using Content.Shared.DeviceNetwork.Systems;
|
||||
|
||||
namespace Content.Client.NetworkConfigurator.Systems;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Client.Power.Components;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Power;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Power.EntitySystems;
|
||||
@@ -26,9 +27,20 @@ public sealed class PowerReceiverSystem : SharedPowerReceiverSystem
|
||||
if (args.Current is not ApcPowerReceiverComponentState state)
|
||||
return;
|
||||
|
||||
var powerChanged = component.Powered != state.Powered;
|
||||
component.Powered = state.Powered;
|
||||
component.NeedsPower = state.NeedsPower;
|
||||
component.PowerDisabled = state.PowerDisabled;
|
||||
// SO client systems can handle it. The main reason for this is we can't guarantee compstate ordering.
|
||||
|
||||
if (powerChanged)
|
||||
RaisePower((uid, component));
|
||||
}
|
||||
|
||||
protected override void RaisePower(Entity<SharedApcPowerReceiverComponent> entity)
|
||||
{
|
||||
var ev = new PowerChangedEvent(entity.Comp.Powered, 0f);
|
||||
RaiseLocalEvent(entity.Owner, ref ev);
|
||||
}
|
||||
|
||||
public override bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component)
|
||||
|
||||
@@ -11,9 +11,6 @@ public sealed class StorageFillVisualizerSystem : VisualizerSystem<StorageFillVi
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!TryComp(uid, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
if (!AppearanceSystem.TryGetData<int>(uid, StorageFillVisuals.FillLevel, out var level, args.Component))
|
||||
return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user