Merge branch 'master' into 2021-07-16/windoors
# Conflicts: # Content.Server/Doors/Components/AirlockComponent.cs
This commit is contained in:
@@ -19,14 +19,9 @@ namespace Content.Client.AME.UI
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new AMEWindow();
|
||||
_window = new AMEWindow(this);
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
|
||||
_window.EjectButton.OnPressed += _ => ButtonPressed(UiButton.Eject);
|
||||
_window.ToggleInjection.OnPressed += _ => ButtonPressed(UiButton.ToggleInjection);
|
||||
_window.IncreaseFuelButton.OnPressed += _ => ButtonPressed(UiButton.IncreaseFuel);
|
||||
_window.DecreaseFuelButton.OnPressed += _ => ButtonPressed(UiButton.DecreaseFuel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,7 +39,7 @@ namespace Content.Client.AME.UI
|
||||
_window?.UpdateState(castState); //Update window state
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiButton button, int dispenseIndex = -1)
|
||||
public void ButtonPressed(UiButton button, int dispenseIndex = -1)
|
||||
{
|
||||
SendMessage(new UiButtonPressedMessage(button));
|
||||
}
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using static Content.Shared.AME.SharedAMEControllerComponent;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.AME.UI
|
||||
{
|
||||
public class AMEWindow : SS14Window
|
||||
{
|
||||
public Label InjectionStatus { get; set; }
|
||||
public Button EjectButton { get; set; }
|
||||
public Button ToggleInjection { get; set; }
|
||||
public Button IncreaseFuelButton { get; set; }
|
||||
public Button DecreaseFuelButton { get; set; }
|
||||
public ProgressBar? FuelMeter { get; set; }
|
||||
public Label FuelAmount { get; set; }
|
||||
public Label InjectionAmount { get; set; }
|
||||
public Label CoreCount { get; set; }
|
||||
|
||||
|
||||
public AMEWindow()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Title = Loc.GetString("ame-window-title");
|
||||
|
||||
MinSize = SetSize = (250, 250);
|
||||
|
||||
Contents.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("ame-window-engine-status-label") + " "},
|
||||
(InjectionStatus = new Label {Text = Loc.GetString("ame-window-engine-injection-status-not-injecting-label")})
|
||||
}
|
||||
},
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(ToggleInjection = new Button {Text = Loc.GetString("ame-window-toggle-injection-button"), StyleClasses = {StyleBase.ButtonOpenBoth}, Disabled = true}),
|
||||
}
|
||||
},
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("ame-window-fuel-status-label") + " "},
|
||||
(FuelAmount = new Label {Text = Loc.GetString("ame-window-fuel-not-inserted-text")})
|
||||
}
|
||||
},
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(EjectButton = new Button {Text = Loc.GetString("ame-window-eject-button"), StyleClasses = {StyleBase.ButtonOpenBoth}, Disabled = true}),
|
||||
}
|
||||
},
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("ame-window-injection-amount-label") + " "},
|
||||
(InjectionAmount = new Label {Text = "0"})
|
||||
}
|
||||
},
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
(IncreaseFuelButton = new Button {Text = Loc.GetString("ame-window-increase-fuel-button"), StyleClasses = {StyleBase.ButtonOpenRight}}),
|
||||
(DecreaseFuelButton = new Button {Text = Loc.GetString("ame-window-decrease-fuel-button"), StyleClasses = {StyleBase.ButtonOpenLeft}}),
|
||||
}
|
||||
},
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = Loc.GetString("ame-window-core-count-label") + " "},
|
||||
(CoreCount = new Label { Text = "0"}),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This searches recursively through all the children of "parent"
|
||||
/// and sets the Disabled value of any buttons found to "val"
|
||||
/// </summary>
|
||||
/// <param name="parent">The control which childrens get searched</param>
|
||||
/// <param name="val">The value to which disabled gets set</param>
|
||||
private void SetButtonDisabledRecursive(Control parent, bool val)
|
||||
{
|
||||
foreach (var child in parent.Children)
|
||||
{
|
||||
if (child is Button but)
|
||||
{
|
||||
but.Disabled = val;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(child, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI state when new state data is received from the server.
|
||||
/// </summary>
|
||||
/// <param name="state">State data sent by the server.</param>
|
||||
public void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
var castState = (AMEControllerBoundUserInterfaceState) state;
|
||||
|
||||
// Disable all buttons if not powered
|
||||
if (Contents.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
EjectButton.Disabled = false;
|
||||
}
|
||||
|
||||
if (!castState.HasFuelJar)
|
||||
{
|
||||
EjectButton.Disabled = true;
|
||||
ToggleInjection.Disabled = true;
|
||||
FuelAmount.Text = Loc.GetString("ame-window-fuel-not-inserted-text");
|
||||
}
|
||||
else
|
||||
{
|
||||
EjectButton.Disabled = false;
|
||||
ToggleInjection.Disabled = false;
|
||||
FuelAmount.Text = $"{castState.FuelAmount}";
|
||||
}
|
||||
|
||||
if (!castState.IsMaster)
|
||||
{
|
||||
ToggleInjection.Disabled = true;
|
||||
}
|
||||
|
||||
if (!castState.Injecting)
|
||||
{
|
||||
InjectionStatus.Text = Loc.GetString("ame-window-engine-injection-status-not-injecting-label") + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
InjectionStatus.Text = Loc.GetString("ame-window-engine-injection-status-injecting-label") + " ";
|
||||
}
|
||||
|
||||
CoreCount.Text = $"{castState.CoreCount}";
|
||||
InjectionAmount.Text = $"{castState.InjectionAmount}";
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Content.Client/AME/UI/AMEWindow.xaml
Normal file
46
Content.Client/AME/UI/AMEWindow.xaml
Normal file
@@ -0,0 +1,46 @@
|
||||
<SS14Window xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'ame-window-title'}"
|
||||
MinSize="250 250">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'ame-window-engine-status-label'}" />
|
||||
<Label Text=" " />
|
||||
<Label Name="InjectionStatus" Text="{Loc 'ame-window-engine-injection-status-not-injecting-label'}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="ToggleInjection"
|
||||
Text="{Loc 'ame-window-toggle-injection-button'}"
|
||||
StyleClasses="OpenBoth"
|
||||
Disabled="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'ame-window-fuel-status-label'}" />
|
||||
<Label Text=" " />
|
||||
<Label Name="FuelAmount" Text="{Loc 'ame-window-fuel-not-inserted-text'}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="EjectButton"
|
||||
Text="{Loc 'ame-window-eject-button'}"
|
||||
StyleClasses="OpenBoth"
|
||||
Disabled="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'ame-window-injection-amount-label'}" />
|
||||
<Label Text=" " />
|
||||
<Label Name="InjectionAmount" Text="0" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="IncreaseFuelButton"
|
||||
Text="{Loc 'ame-window-increase-fuel-button'}"
|
||||
StyleClasses="OpenRight" />
|
||||
<Button Name="DecreaseFuelButton"
|
||||
Text="{Loc 'ame-window-decrease-fuel-button'}"
|
||||
StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'ame-window-core-count-label'}" />
|
||||
<Label Text=" " />
|
||||
<Label Name="CoreCount" Text="0" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</SS14Window>
|
||||
74
Content.Client/AME/UI/AMEWindow.xaml.cs
Normal file
74
Content.Client/AME/UI/AMEWindow.xaml.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Content.Client.UserInterface;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using static Content.Shared.AME.SharedAMEControllerComponent;
|
||||
|
||||
namespace Content.Client.AME.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class AMEWindow : SS14Window
|
||||
{
|
||||
public AMEWindow(AMEControllerBoundUserInterface ui)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
EjectButton.OnPressed += _ => ui.ButtonPressed(UiButton.Eject);
|
||||
ToggleInjection.OnPressed += _ => ui.ButtonPressed(UiButton.ToggleInjection);
|
||||
IncreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.IncreaseFuel);
|
||||
DecreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.DecreaseFuel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI state when new state data is received from the server.
|
||||
/// </summary>
|
||||
/// <param name="state">State data sent by the server.</param>
|
||||
public void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
var castState = (AMEControllerBoundUserInterfaceState) state;
|
||||
|
||||
// Disable all buttons if not powered
|
||||
if (Contents.Children != null)
|
||||
{
|
||||
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
EjectButton.Disabled = false;
|
||||
}
|
||||
|
||||
if (!castState.HasFuelJar)
|
||||
{
|
||||
EjectButton.Disabled = true;
|
||||
ToggleInjection.Disabled = true;
|
||||
FuelAmount.Text = Loc.GetString("ame-window-fuel-not-inserted-text");
|
||||
}
|
||||
else
|
||||
{
|
||||
EjectButton.Disabled = false;
|
||||
ToggleInjection.Disabled = false;
|
||||
FuelAmount.Text = $"{castState.FuelAmount}";
|
||||
}
|
||||
|
||||
if (!castState.IsMaster)
|
||||
{
|
||||
ToggleInjection.Disabled = true;
|
||||
}
|
||||
|
||||
if (!castState.Injecting)
|
||||
{
|
||||
InjectionStatus.Text = Loc.GetString("ame-window-engine-injection-status-not-injecting-label") + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
InjectionStatus.Text = Loc.GetString("ame-window-engine-injection-status-injecting-label") + " ";
|
||||
}
|
||||
|
||||
CoreCount.Text = $"{castState.CoreCount}";
|
||||
InjectionAmount.Text = $"{castState.InjectionAmount}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Access;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.Access.SharedIdCardConsoleComponent;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
{
|
||||
public class IdCardConsoleWindow : SS14Window
|
||||
{
|
||||
private readonly Button _privilegedIdButton;
|
||||
private readonly Button _targetIdButton;
|
||||
|
||||
private readonly Label _privilegedIdLabel;
|
||||
private readonly Label _targetIdLabel;
|
||||
|
||||
private readonly Label _fullNameLabel;
|
||||
private readonly LineEdit _fullNameLineEdit;
|
||||
private readonly Label _jobTitleLabel;
|
||||
private readonly LineEdit _jobTitleLineEdit;
|
||||
|
||||
private readonly Button _fullNameSaveButton;
|
||||
private readonly Button _jobTitleSaveButton;
|
||||
|
||||
private readonly IdCardConsoleBoundUserInterface _owner;
|
||||
|
||||
private readonly Dictionary<string, Button> _accessButtons = new();
|
||||
|
||||
private string? _lastFullName;
|
||||
private string? _lastJobTitle;
|
||||
|
||||
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager)
|
||||
{
|
||||
MinSize = SetSize = (650, 290);
|
||||
_owner = owner;
|
||||
var vBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
|
||||
vBox.AddChild(new GridContainer
|
||||
{
|
||||
Columns = 3,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("id-card-console-window-privileged-id")},
|
||||
(_privilegedIdButton = new Button()),
|
||||
(_privilegedIdLabel = new Label()),
|
||||
|
||||
new Label {Text = Loc.GetString("id-card-console-window-target-id")},
|
||||
(_targetIdButton = new Button()),
|
||||
(_targetIdLabel = new Label())
|
||||
}
|
||||
});
|
||||
|
||||
_privilegedIdButton.OnPressed += _ => _owner.ButtonPressed(UiButton.PrivilegedId);
|
||||
_targetIdButton.OnPressed += _ => _owner.ButtonPressed(UiButton.TargetId);
|
||||
|
||||
// Separator
|
||||
vBox.AddChild(new Control {MinSize = (0, 8)});
|
||||
|
||||
// Name and job title line edits.
|
||||
vBox.AddChild(new GridContainer
|
||||
{
|
||||
Columns = 3,
|
||||
HSeparationOverride = 4,
|
||||
Children =
|
||||
{
|
||||
// Name
|
||||
(_fullNameLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("id-card-console-window-full-name-label")
|
||||
}),
|
||||
(_fullNameLineEdit = new LineEdit
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
}),
|
||||
(_fullNameSaveButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("id-card-console-window-save-button"),
|
||||
Disabled = true
|
||||
}),
|
||||
|
||||
// Title
|
||||
(_jobTitleLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("id-card-console-window-job-title-label")
|
||||
}),
|
||||
(_jobTitleLineEdit = new LineEdit
|
||||
{
|
||||
HorizontalExpand = true
|
||||
}),
|
||||
(_jobTitleSaveButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("id-card-console-window-save-button"),
|
||||
Disabled = true
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
_fullNameLineEdit.OnTextEntered += _ => SubmitData();
|
||||
_fullNameLineEdit.OnTextChanged += _ =>
|
||||
{
|
||||
_fullNameSaveButton.Disabled = _fullNameSaveButton.Text == _lastFullName;
|
||||
};
|
||||
_fullNameSaveButton.OnPressed += _ => SubmitData();
|
||||
|
||||
_jobTitleLineEdit.OnTextEntered += _ => SubmitData();
|
||||
_jobTitleLineEdit.OnTextChanged += _ =>
|
||||
{
|
||||
_jobTitleSaveButton.Disabled = _jobTitleLineEdit.Text == _lastJobTitle;
|
||||
};
|
||||
_jobTitleSaveButton.OnPressed += _ => SubmitData();
|
||||
|
||||
// Separator
|
||||
vBox.AddChild(new Control {MinSize = (0, 8)});
|
||||
|
||||
{
|
||||
var grid = new GridContainer
|
||||
{
|
||||
Columns = 5,
|
||||
HorizontalAlignment = HAlignment.Center
|
||||
};
|
||||
vBox.AddChild(grid);
|
||||
|
||||
foreach (var accessLevel in prototypeManager.EnumeratePrototypes<AccessLevelPrototype>())
|
||||
{
|
||||
var newButton = new Button
|
||||
{
|
||||
Text = accessLevel.Name,
|
||||
ToggleMode = true,
|
||||
};
|
||||
grid.AddChild(newButton);
|
||||
_accessButtons.Add(accessLevel.ID, newButton);
|
||||
newButton.OnPressed += _ => SubmitData();
|
||||
}
|
||||
}
|
||||
|
||||
Contents.AddChild(vBox);
|
||||
}
|
||||
|
||||
public void UpdateState(IdCardConsoleBoundUserInterfaceState state)
|
||||
{
|
||||
_privilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||
? Loc.GetString("id-card-console-window-eject-button")
|
||||
: Loc.GetString("id-card-console-window-insert-button");
|
||||
|
||||
_privilegedIdLabel.Text = state.PrivilegedIdName;
|
||||
|
||||
_targetIdButton.Text = state.IsTargetIdPresent
|
||||
? Loc.GetString("id-card-console-window-eject-button")
|
||||
: Loc.GetString("id-card-console-window-insert-button");
|
||||
|
||||
_targetIdLabel.Text = state.TargetIdName;
|
||||
|
||||
var interfaceEnabled =
|
||||
state.IsPrivilegedIdPresent && state.IsPrivilegedIdAuthorized && state.IsTargetIdPresent;
|
||||
|
||||
var fullNameDirty = _lastFullName != null && _fullNameLineEdit.Text != state.TargetIdFullName;
|
||||
var jobTitleDirty = _lastJobTitle != null && _jobTitleLineEdit.Text != state.TargetIdJobTitle;
|
||||
|
||||
_fullNameLabel.Modulate = interfaceEnabled ? Color.White : Color.Gray;
|
||||
_fullNameLineEdit.Editable = interfaceEnabled;
|
||||
if (!fullNameDirty)
|
||||
{
|
||||
_fullNameLineEdit.Text = state.TargetIdFullName ?? string.Empty;
|
||||
}
|
||||
|
||||
_fullNameSaveButton.Disabled = !interfaceEnabled || !fullNameDirty;
|
||||
|
||||
_jobTitleLabel.Modulate = interfaceEnabled ? Color.White : Color.Gray;
|
||||
_jobTitleLineEdit.Editable = interfaceEnabled;
|
||||
if (!jobTitleDirty)
|
||||
{
|
||||
_jobTitleLineEdit.Text = state.TargetIdJobTitle ?? string.Empty;
|
||||
}
|
||||
|
||||
_jobTitleSaveButton.Disabled = !interfaceEnabled || !jobTitleDirty;
|
||||
|
||||
foreach (var (accessName, button) in _accessButtons)
|
||||
{
|
||||
button.Disabled = !interfaceEnabled;
|
||||
if (interfaceEnabled)
|
||||
{
|
||||
button.Pressed = state.TargetIdAccessList?.Contains(accessName) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
_lastFullName = state.TargetIdFullName;
|
||||
_lastJobTitle = state.TargetIdJobTitle;
|
||||
}
|
||||
|
||||
private void SubmitData()
|
||||
{
|
||||
_owner.SubmitData(
|
||||
_fullNameLineEdit.Text,
|
||||
_jobTitleLineEdit.Text,
|
||||
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
|
||||
_accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Content.Client/Access/UI/IdCardConsoleWindow.xaml
Normal file
30
Content.Client/Access/UI/IdCardConsoleWindow.xaml
Normal file
@@ -0,0 +1,30 @@
|
||||
<SS14Window xmlns="https://spacestation14.io"
|
||||
MinSize="650 290">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<GridContainer Columns="3">
|
||||
<Label Text="{Loc 'id-card-console-window-privileged-id'}" />
|
||||
<Button Name="PrivilegedIdButton" />
|
||||
<Label Name="PrivilegedIdLabel" />
|
||||
|
||||
<Label Text="{Loc 'id-card-console-window-target-id'}" />
|
||||
<Button Name="TargetIdButton" />
|
||||
<Label Name="TargetIdLabel" />
|
||||
</GridContainer>
|
||||
<Control MinSize="0 8" />
|
||||
<GridContainer Columns="3" HSeparationOverride="4">
|
||||
<Label Name="FullNameLabel" Text="{Loc 'id-card-console-window-full-name-label'}" />
|
||||
<LineEdit Name="FullNameLineEdit" HorizontalExpand="True" />
|
||||
<Button Name="FullNameSaveButton" Text="{Loc 'id-card-console-window-save-button'}" Disabled="True" />
|
||||
|
||||
<Label Name="JobTitleLabel" Text="{Loc 'id-card-console-window-job-title-label'}" />
|
||||
<LineEdit Name="JobTitleLineEdit" HorizontalExpand="True" />
|
||||
<Button Name="JobTitleSaveButton" Text="{Loc 'id-card-console-window-save-button'}" Disabled="True" />
|
||||
</GridContainer>
|
||||
<Control MinSize="0 8" />
|
||||
<GridContainer Name="AccessLevelGrid" Columns="5" HorizontalAlignment="Center">
|
||||
|
||||
<!-- Access level buttons are added here by the C# code -->
|
||||
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</SS14Window>
|
||||
121
Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
Normal file
121
Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Access;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.Access.SharedIdCardConsoleComponent;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class IdCardConsoleWindow : SS14Window
|
||||
{
|
||||
private readonly IdCardConsoleBoundUserInterface _owner;
|
||||
|
||||
private readonly Dictionary<string, Button> _accessButtons = new();
|
||||
|
||||
private string? _lastFullName;
|
||||
private string? _lastJobTitle;
|
||||
|
||||
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_owner = owner;
|
||||
|
||||
PrivilegedIdButton.OnPressed += _ => _owner.ButtonPressed(UiButton.PrivilegedId);
|
||||
TargetIdButton.OnPressed += _ => _owner.ButtonPressed(UiButton.TargetId);
|
||||
|
||||
FullNameLineEdit.OnTextEntered += _ => SubmitData();
|
||||
FullNameLineEdit.OnTextChanged += _ =>
|
||||
{
|
||||
FullNameSaveButton.Disabled = FullNameSaveButton.Text == _lastFullName;
|
||||
};
|
||||
FullNameSaveButton.OnPressed += _ => SubmitData();
|
||||
|
||||
JobTitleLineEdit.OnTextEntered += _ => SubmitData();
|
||||
JobTitleLineEdit.OnTextChanged += _ =>
|
||||
{
|
||||
JobTitleSaveButton.Disabled = JobTitleLineEdit.Text == _lastJobTitle;
|
||||
};
|
||||
JobTitleSaveButton.OnPressed += _ => SubmitData();
|
||||
|
||||
foreach (var accessLevel in prototypeManager.EnumeratePrototypes<AccessLevelPrototype>())
|
||||
{
|
||||
var newButton = new Button
|
||||
{
|
||||
Text = accessLevel.Name,
|
||||
ToggleMode = true,
|
||||
};
|
||||
AccessLevelGrid.AddChild(newButton);
|
||||
_accessButtons.Add(accessLevel.ID, newButton);
|
||||
newButton.OnPressed += _ => SubmitData();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateState(IdCardConsoleBoundUserInterfaceState state)
|
||||
{
|
||||
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||
? Loc.GetString("id-card-console-window-eject-button")
|
||||
: Loc.GetString("id-card-console-window-insert-button");
|
||||
|
||||
PrivilegedIdLabel.Text = state.PrivilegedIdName;
|
||||
|
||||
TargetIdButton.Text = state.IsTargetIdPresent
|
||||
? Loc.GetString("id-card-console-window-eject-button")
|
||||
: Loc.GetString("id-card-console-window-insert-button");
|
||||
|
||||
TargetIdLabel.Text = state.TargetIdName;
|
||||
|
||||
var interfaceEnabled =
|
||||
state.IsPrivilegedIdPresent && state.IsPrivilegedIdAuthorized && state.IsTargetIdPresent;
|
||||
|
||||
var fullNameDirty = _lastFullName != null && FullNameLineEdit.Text != state.TargetIdFullName;
|
||||
var jobTitleDirty = _lastJobTitle != null && JobTitleLineEdit.Text != state.TargetIdJobTitle;
|
||||
|
||||
FullNameLabel.Modulate = interfaceEnabled ? Color.White : Color.Gray;
|
||||
FullNameLineEdit.Editable = interfaceEnabled;
|
||||
if (!fullNameDirty)
|
||||
{
|
||||
FullNameLineEdit.Text = state.TargetIdFullName ?? string.Empty;
|
||||
}
|
||||
|
||||
FullNameSaveButton.Disabled = !interfaceEnabled || !fullNameDirty;
|
||||
|
||||
JobTitleLabel.Modulate = interfaceEnabled ? Color.White : Color.Gray;
|
||||
JobTitleLineEdit.Editable = interfaceEnabled;
|
||||
if (!jobTitleDirty)
|
||||
{
|
||||
JobTitleLineEdit.Text = state.TargetIdJobTitle ?? string.Empty;
|
||||
}
|
||||
|
||||
JobTitleSaveButton.Disabled = !interfaceEnabled || !jobTitleDirty;
|
||||
|
||||
foreach (var (accessName, button) in _accessButtons)
|
||||
{
|
||||
button.Disabled = !interfaceEnabled;
|
||||
if (interfaceEnabled)
|
||||
{
|
||||
button.Pressed = state.TargetIdAccessList?.Contains(accessName) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
_lastFullName = state.TargetIdFullName;
|
||||
_lastJobTitle = state.TargetIdJobTitle;
|
||||
}
|
||||
|
||||
private void SubmitData()
|
||||
{
|
||||
_owner.SubmitData(
|
||||
FullNameLineEdit.Text,
|
||||
JobTitleLineEdit.Text,
|
||||
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
|
||||
_accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Actions.Assignments;
|
||||
using Content.Client.Actions.UI;
|
||||
using Content.Client.Hands;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Items.Managers;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Actions.Prototypes;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -26,12 +25,13 @@ namespace Content.Client.Actions
|
||||
public const byte Slots = 10;
|
||||
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||
|
||||
[ComponentDependency] private readonly HandsComponent? _handsComponent = null;
|
||||
[ComponentDependency] private readonly ClientInventoryComponent? _inventoryComponent = null;
|
||||
|
||||
private ActionsUI? _ui;
|
||||
private readonly List<ItemSlotButton> _highlightingItemSlots = new();
|
||||
private EntityUid _highlightedEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Current assignments for all hotbars / slots for this entity.
|
||||
@@ -225,26 +225,8 @@ namespace Content.Client.Actions
|
||||
{
|
||||
StopHighlightingItemSlots();
|
||||
|
||||
// figure out if it's in hand or inventory and highlight it
|
||||
foreach (var hand in _handsComponent!.Gui!.Hands)
|
||||
{
|
||||
if (hand.HeldItem != item || hand.HandButton == null) continue;
|
||||
_highlightingItemSlots.Add(hand.HandButton);
|
||||
hand.HandButton.Highlight(true);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (slot, slotItem) in _inventoryComponent!.AllSlots)
|
||||
{
|
||||
if (slotItem != item) continue;
|
||||
foreach (var itemSlotButton in
|
||||
_inventoryComponent.InterfaceController.GetItemSlotButtons(slot))
|
||||
{
|
||||
_highlightingItemSlots.Add(itemSlotButton);
|
||||
itemSlotButton.Highlight(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_highlightedEntity = item.Uid;
|
||||
_itemSlotManager.HighlightEntity(item.Uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -252,11 +234,11 @@ namespace Content.Client.Actions
|
||||
/// </summary>
|
||||
public void StopHighlightingItemSlots()
|
||||
{
|
||||
foreach (var itemSlot in _highlightingItemSlots)
|
||||
{
|
||||
itemSlot.Highlight(false);
|
||||
}
|
||||
_highlightingItemSlots.Clear();
|
||||
if (_highlightedEntity == default)
|
||||
return;
|
||||
|
||||
_itemSlotManager.UnHighlightEntity(_highlightedEntity);
|
||||
_highlightedEntity = default;
|
||||
}
|
||||
|
||||
public void ToggleActionsMenu()
|
||||
|
||||
10
Content.Client/Alerts/UI/AlertsUI.xaml
Normal file
10
Content.Client/Alerts/UI/AlertsUI.xaml
Normal file
@@ -0,0 +1,10 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
MinSize="64 64">
|
||||
<PanelContainer StyleClasses="TransparentBorderedWindowPanel"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top">
|
||||
<GridContainer Name="AlertContainer" MaxGridHeight="64" ExpandBackwards="True">
|
||||
|
||||
</GridContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
@@ -1,8 +1,9 @@
|
||||
using Content.Client.Chat.Managers;
|
||||
using Content.Client.Chat.UI;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.Alerts.UI
|
||||
@@ -10,13 +11,20 @@ namespace Content.Client.Alerts.UI
|
||||
/// <summary>
|
||||
/// The status effects display on the right side of the screen.
|
||||
/// </summary>
|
||||
public sealed class AlertsUI : Control
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AlertsUI : Control
|
||||
{
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
|
||||
public const float ChatSeparation = 38f;
|
||||
public GridContainer Grid { get; }
|
||||
|
||||
public GridContainer Grid => AlertContainer;
|
||||
|
||||
public AlertsUI()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
|
||||
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.End);
|
||||
LayoutContainer.SetAnchorTop(this, 0f);
|
||||
@@ -25,28 +33,11 @@ namespace Content.Client.Alerts.UI
|
||||
LayoutContainer.SetMarginBottom(this, -180);
|
||||
LayoutContainer.SetMarginTop(this, 250);
|
||||
LayoutContainer.SetMarginRight(this, -10);
|
||||
var panelContainer = new PanelContainer
|
||||
{
|
||||
StyleClasses = {StyleNano.StyleClassTransparentBorderedWindowPanel},
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
VerticalAlignment = VAlignment.Top
|
||||
};
|
||||
AddChild(panelContainer);
|
||||
|
||||
Grid = new GridContainer
|
||||
{
|
||||
MaxGridHeight = 64,
|
||||
ExpandBackwards = true
|
||||
};
|
||||
panelContainer.AddChild(Grid);
|
||||
|
||||
MinSize = (64, 64);
|
||||
}
|
||||
|
||||
protected override void EnteredTree()
|
||||
{
|
||||
base.EnteredTree();
|
||||
var _chatManager = IoCManager.Resolve<IChatManager>();
|
||||
_chatManager.OnChatBoxResized += OnChatResized;
|
||||
OnChatResized(new ChatResizedEventArgs(HudChatBox.InitialChatBottom));
|
||||
}
|
||||
@@ -54,15 +45,12 @@ namespace Content.Client.Alerts.UI
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
var _chatManager = IoCManager.Resolve<IChatManager>();
|
||||
_chatManager.OnChatBoxResized -= OnChatResized;
|
||||
}
|
||||
|
||||
|
||||
private void OnChatResized(ChatResizedEventArgs chatResizedEventArgs)
|
||||
{
|
||||
// resize us to fit just below the chatbox
|
||||
var _chatManager = IoCManager.Resolve<IChatManager>();
|
||||
if (_chatManager.CurrentChatBox != null)
|
||||
{
|
||||
LayoutContainer.SetMarginTop(this, chatResizedEventArgs.NewBottom + ChatSeparation);
|
||||
@@ -81,12 +69,12 @@ namespace Content.Client.Alerts.UI
|
||||
// this is here because there isn't currently a good way to allow the grid to adjust its height based
|
||||
// on constraints, otherwise we would use anchors to lay it out
|
||||
base.Resized();
|
||||
Grid.MaxGridHeight = Height;
|
||||
AlertContainer.MaxGridHeight = Height;
|
||||
}
|
||||
|
||||
protected override void UIScaleChanged()
|
||||
{
|
||||
Grid.MaxGridHeight = Height;
|
||||
AlertContainer.MaxGridHeight = Height;
|
||||
base.UIScaleChanged();
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ namespace Content.Client.Atmos.EntitySystems
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
// Gas overlays
|
||||
private readonly float[] _timer = new float[Atmospherics.TotalNumberOfGases];
|
||||
@@ -38,16 +39,12 @@ namespace Content.Client.Atmos.EntitySystems
|
||||
private readonly Dictionary<GridId, Dictionary<Vector2i, GasOverlayChunk>> _tileData =
|
||||
new();
|
||||
|
||||
private AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeNetworkEvent<GasOverlayMessage>(HandleGasOverlayMessage);
|
||||
_mapManager.OnGridRemoved += OnGridRemoved;
|
||||
|
||||
_atmosphereSystem = Get<AtmosphereSystem>();
|
||||
|
||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
var overlay = _atmosphereSystem.GetOverlay(i);
|
||||
|
||||
@@ -9,22 +9,13 @@ namespace Content.Client.Atmos.Visualizers
|
||||
[UsedImplicitly]
|
||||
public abstract class EnabledAtmosDeviceVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("disabledState")]
|
||||
private string _disabledState = string.Empty;
|
||||
[DataField("enabledState")]
|
||||
private string _enabledState = string.Empty;
|
||||
protected abstract object LayerMap { get; }
|
||||
protected abstract Enum DataKey { get; }
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
if (!entity.TryGetComponent(out ISpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
sprite.LayerMapSet(LayerMap, sprite.AddLayerState(_enabledState));
|
||||
sprite.LayerSetVisible(LayerMap, false);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
@@ -32,8 +23,8 @@ namespace Content.Client.Atmos.Visualizers
|
||||
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
if(component.TryGetData(DataKey, out bool enabled))
|
||||
sprite.LayerSetVisible(LayerMap, enabled);
|
||||
if(component.TryGetData(DataKey, out bool enabled) && sprite.LayerMapTryGet(LayerMap, out var layer))
|
||||
sprite.LayerSetState(layer, enabled ? _enabledState : _disabledState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
Content.Client/Atmos/Visualizers/GasFilterVisualizer.cs
Normal file
18
Content.Client/Atmos/Visualizers/GasFilterVisualizer.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Content.Shared.Atmos.Piping;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client.Atmos.Visualizers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasFilterVisualizer : EnabledAtmosDeviceVisualizer
|
||||
{
|
||||
protected override object LayerMap => Layers.Enabled;
|
||||
protected override Enum DataKey => FilterVisuals.Enabled;
|
||||
|
||||
enum Layers : byte
|
||||
{
|
||||
Enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ namespace Content.Client.Atmos.Visualizers
|
||||
}
|
||||
}
|
||||
|
||||
private enum Layers
|
||||
private enum Layers : byte
|
||||
{
|
||||
ConnectedToPort,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers
|
||||
protected override object LayerMap => Layers.Enabled;
|
||||
protected override Enum DataKey => OutletInjectorVisuals.Enabled;
|
||||
|
||||
enum Layers
|
||||
enum Layers : byte
|
||||
{
|
||||
Enabled,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers
|
||||
protected override object LayerMap => Layers.Enabled;
|
||||
protected override Enum DataKey => PassiveVentVisuals.Enabled;
|
||||
|
||||
enum Layers
|
||||
enum Layers : byte
|
||||
{
|
||||
Enabled,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping;
|
||||
using Content.Shared.SubFloor;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -69,13 +70,16 @@ namespace Content.Client.Atmos.Visualizers
|
||||
if (!component.TryGetData(PipeVisuals.VisualState, out PipeVisualState state))
|
||||
return;
|
||||
|
||||
if(!component.TryGetData(SubFloorVisuals.SubFloor, out bool subfloor))
|
||||
subfloor = true;
|
||||
|
||||
foreach (Layer layerKey in Enum.GetValues(typeof(Layer)))
|
||||
{
|
||||
var dir = (PipeDirection) layerKey;
|
||||
var layerVisible = state.ConnectedDirections.HasDirection(dir);
|
||||
|
||||
var layer = sprite.LayerMapGet(layerKey);
|
||||
sprite.LayerSetVisible(layer, layerVisible);
|
||||
sprite.LayerSetVisible(layer, layerVisible && subfloor);
|
||||
sprite.LayerSetColor(layer, color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers
|
||||
protected override object LayerMap => Layers.Enabled;
|
||||
protected override Enum DataKey => PressurePumpVisuals.Enabled;
|
||||
|
||||
enum Layers
|
||||
enum Layers : byte
|
||||
{
|
||||
Enabled,
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Content.Client.Atmos.Visualizers
|
||||
}
|
||||
}
|
||||
|
||||
public enum ScrubberVisualLayers
|
||||
public enum ScrubberVisualLayers : byte
|
||||
{
|
||||
Scrubber,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers
|
||||
protected override object LayerMap => Layers.Enabled;
|
||||
protected override Enum DataKey => ThermoMachineVisuals.Enabled;
|
||||
|
||||
enum Layers
|
||||
enum Layers : byte
|
||||
{
|
||||
Enabled,
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Content.Client.Atmos.Visualizers
|
||||
}
|
||||
}
|
||||
|
||||
public enum VentVisualLayers
|
||||
public enum VentVisualLayers : byte
|
||||
{
|
||||
Vent,
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Content.Client.Audio
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly ClientGameTicker _gameTicker = default!;
|
||||
|
||||
private SoundCollectionPrototype _ambientCollection = default!;
|
||||
|
||||
@@ -48,7 +49,7 @@ namespace Content.Client.Audio
|
||||
_client.PlayerJoinedServer += OnJoin;
|
||||
_client.PlayerLeaveServer += OnLeave;
|
||||
|
||||
Get<ClientGameTicker>().LobbyStatusUpdated += LobbySongReceived;
|
||||
_gameTicker.LobbyStatusUpdated += LobbySongReceived;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -60,7 +61,7 @@ namespace Content.Client.Audio
|
||||
_client.PlayerJoinedServer -= OnJoin;
|
||||
_client.PlayerLeaveServer -= OnLeave;
|
||||
|
||||
Get<ClientGameTicker>().LobbyStatusUpdated -= LobbySongReceived;
|
||||
_gameTicker.LobbyStatusUpdated -= LobbySongReceived;
|
||||
|
||||
EndAmbience();
|
||||
EndLobbyMusic();
|
||||
@@ -165,7 +166,7 @@ namespace Content.Client.Audio
|
||||
private void StartLobbyMusic()
|
||||
{
|
||||
EndLobbyMusic();
|
||||
var file = Get<ClientGameTicker>().LobbySong;
|
||||
var file = _gameTicker.LobbySong;
|
||||
if (file == null) // We have not received the lobby song yet.
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -279,34 +280,11 @@ namespace Content.Client.Chemistry.UI
|
||||
UpdatePanelInfo(castState);
|
||||
if (Contents.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
EjectButton.Disabled = !castState.HasBeaker;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This searches recursively through all the children of "parent"
|
||||
/// and sets the Disabled value of any buttons found to "val"
|
||||
/// </summary>
|
||||
/// <param name="parent">The control which childrens get searched</param>
|
||||
/// <param name="val">The value to which disabled gets set</param>
|
||||
private void SetButtonDisabledRecursive(Control parent, bool val)
|
||||
{
|
||||
foreach (var child in parent.Children)
|
||||
{
|
||||
if (child is Button but)
|
||||
{
|
||||
but.Disabled = val;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(child, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the container, buffer, and packaging panels.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Chemistry.Dispenser;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -173,29 +174,6 @@ namespace Content.Client.Chemistry.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This searches recursively through all the children of "parent"
|
||||
/// and sets the Disabled value of any buttons found to "val"
|
||||
/// </summary>
|
||||
/// <param name="parent">The control which childrens get searched</param>
|
||||
/// <param name="val">The value to which disabled gets set</param>
|
||||
private void SetButtonDisabledRecursive(Control parent, bool val)
|
||||
{
|
||||
foreach (var child in parent.Children)
|
||||
{
|
||||
if (child is Button but)
|
||||
{
|
||||
but.Disabled = val;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(child, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI state when new state data is received from the server.
|
||||
/// </summary>
|
||||
@@ -209,7 +187,7 @@ namespace Content.Client.Chemistry.UI
|
||||
// Disable all buttons if not powered
|
||||
if (Contents.Children != null)
|
||||
{
|
||||
SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
EjectButton.Disabled = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Clothing;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Cloning
|
||||
namespace Content.Client.Clothing
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class MagbootsComponent : SharedMagbootsComponent
|
||||
@@ -17,15 +17,6 @@ namespace Content.Client.Doors
|
||||
{
|
||||
private const string AnimationKey = "airlock_animation";
|
||||
|
||||
[DataField("open_sound", required: true)]
|
||||
private string _openSound = default!;
|
||||
|
||||
[DataField("close_sound", required: true)]
|
||||
private string _closeSound = default!;
|
||||
|
||||
[DataField("deny_sound", required: true)]
|
||||
private string _denySound = default!;
|
||||
|
||||
[DataField("animation_time")]
|
||||
private float _delay = 0.8f;
|
||||
|
||||
@@ -51,14 +42,6 @@ namespace Content.Client.Doors
|
||||
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel);
|
||||
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
|
||||
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
CloseAnimation.AnimationTracks.Add(sound);
|
||||
|
||||
if (_closeSound != null)
|
||||
{
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_closeSound, 0));
|
||||
}
|
||||
}
|
||||
|
||||
OpenAnimation = new Animation {Length = TimeSpan.FromSeconds(_delay)};
|
||||
@@ -80,11 +63,6 @@ namespace Content.Client.Doors
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
OpenAnimation.AnimationTracks.Add(sound);
|
||||
|
||||
if (_openSound != null)
|
||||
{
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_openSound, 0));
|
||||
}
|
||||
}
|
||||
|
||||
DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(0.3f)};
|
||||
@@ -96,11 +74,6 @@ namespace Content.Client.Doors
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
DenyAnimation.AnimationTracks.Add(sound);
|
||||
|
||||
if (_denySound != null)
|
||||
{
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_denySound, 0, () => AudioHelpers.WithVariation(0.05f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace Content.Client.DragDrop
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||
|
||||
// how often to recheck possible targets (prevents calling expensive
|
||||
// check logic each update)
|
||||
@@ -69,8 +71,6 @@ namespace Content.Client.DragDrop
|
||||
|
||||
private ShaderInstance? _dropTargetInRangeShader;
|
||||
private ShaderInstance? _dropTargetOutOfRangeShader;
|
||||
private SharedInteractionSystem _interactionSystem = default!;
|
||||
private InputSystem _inputSystem = default!;
|
||||
|
||||
private readonly List<ISpriteComponent> _highlightedSprites = new();
|
||||
|
||||
@@ -80,8 +80,6 @@ namespace Content.Client.DragDrop
|
||||
|
||||
_dropTargetInRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetInRange).Instance();
|
||||
_dropTargetOutOfRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetOutOfRange).Instance();
|
||||
_interactionSystem = Get<SharedInteractionSystem>();
|
||||
_inputSystem = Get<InputSystem>();
|
||||
// needs to fire on mouseup and mousedown so we can detect a drag / drop
|
||||
CommandBinds.Builder
|
||||
.Bind(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false))
|
||||
|
||||
@@ -81,7 +81,6 @@ namespace Content.Client.Entry
|
||||
factory.RegisterClass<SharedCargoConsoleComponent>();
|
||||
factory.RegisterClass<SharedReagentDispenserComponent>();
|
||||
factory.RegisterClass<SharedChemMasterComponent>();
|
||||
factory.RegisterClass<SharedMicrowaveComponent>();
|
||||
factory.RegisterClass<SharedGravityGeneratorComponent>();
|
||||
factory.RegisterClass<SharedAMEControllerComponent>();
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ namespace Content.Client.Entry
|
||||
"ExaminableBattery",
|
||||
"PottedPlantHide",
|
||||
"SecureEntityStorage",
|
||||
"Lock",
|
||||
"PresetIdCard",
|
||||
"SolarControlConsole",
|
||||
"FlashOnTrigger",
|
||||
@@ -153,7 +154,8 @@ namespace Content.Client.Entry
|
||||
"GasPassiveGate",
|
||||
"GasValve",
|
||||
"GasThermoMachine",
|
||||
"Metabolism",
|
||||
"Respirator",
|
||||
"Metabolizer",
|
||||
"AiFactionTag",
|
||||
"PressureProtection",
|
||||
"AMEPart",
|
||||
|
||||
3
Content.Client/Hands/HandVirtualPullItemStatus.xaml
Normal file
3
Content.Client/Hands/HandVirtualPullItemStatus.xaml
Normal file
@@ -0,0 +1,3 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<Label StyleClasses="ItemStatus" Text="Pulling" />
|
||||
</Control>
|
||||
13
Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs
Normal file
13
Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
public sealed class HandVirtualPullItemStatus : Control
|
||||
{
|
||||
public HandVirtualPullItemStatus()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Animations;
|
||||
using Content.Client.HUD;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
@@ -18,16 +11,7 @@ namespace Content.Client.Hands
|
||||
[ComponentReference(typeof(SharedHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent
|
||||
{
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public HandsGui? Gui { get; private set; }
|
||||
|
||||
protected override void OnRemove()
|
||||
{
|
||||
ClearGui();
|
||||
base.OnRemove();
|
||||
}
|
||||
public HandsGui? Gui { get; set; }
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
@@ -38,94 +22,23 @@ namespace Content.Client.Hands
|
||||
|
||||
foreach (var handState in state.Hands)
|
||||
{
|
||||
var newHand = new Hand(handState.Name, handState.Enabled, handState.Location);
|
||||
var newHand = new Hand(handState.Name, handState.Location);
|
||||
Hands.Add(newHand);
|
||||
}
|
||||
|
||||
ActiveHand = state.ActiveHand;
|
||||
|
||||
UpdateHandContainers();
|
||||
UpdateHandVisualizer();
|
||||
UpdateHandsGuiState();
|
||||
}
|
||||
|
||||
public void SettupGui()
|
||||
{
|
||||
if (Gui == null)
|
||||
{
|
||||
Gui = new HandsGui();
|
||||
_gameHud.HandsContainer.AddChild(Gui);
|
||||
Gui.HandClick += args => OnHandClick(args.HandClicked);
|
||||
Gui.HandActivate += args => OnActivateInHand(args.HandUsed);
|
||||
UpdateHandsGuiState();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearGui()
|
||||
{
|
||||
Gui?.Dispose();
|
||||
Gui = null;
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PickupAnimationMessage msg:
|
||||
RunPickupAnimation(msg);
|
||||
break;
|
||||
}
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandsModifiedMessage { Hands = this });
|
||||
}
|
||||
|
||||
public override void HandsModified()
|
||||
{
|
||||
base.HandsModified();
|
||||
|
||||
UpdateHandContainers();
|
||||
UpdateHandVisualizer();
|
||||
UpdateHandsGuiState();
|
||||
}
|
||||
|
||||
private void OnHandClick(string handClicked)
|
||||
{
|
||||
if (!TryGetHand(handClicked, out var pressedHand))
|
||||
return;
|
||||
|
||||
if (!TryGetActiveHand(out var activeHand))
|
||||
return;
|
||||
|
||||
var pressedEntity = pressedHand.HeldEntity;
|
||||
var activeEntity = activeHand.HeldEntity;
|
||||
|
||||
if (pressedHand == activeHand && activeEntity != null)
|
||||
{
|
||||
SendNetworkMessage(new UseInHandMsg()); //use item in hand
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity == null)
|
||||
{
|
||||
SendNetworkMessage(new ClientChangedHandMsg(pressedHand.Name)); //swap hand
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity != null)
|
||||
{
|
||||
SendNetworkMessage(new ClientAttackByInHandMsg(pressedHand.Name)); //use active item on held item
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity == null)
|
||||
{
|
||||
SendNetworkMessage(new MoveItemFromHandMsg(pressedHand.Name)); //move item in hand to active hand
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnActivateInHand(string handActivated)
|
||||
{
|
||||
SendNetworkMessage(new ActivateInHandMsg(handActivated));
|
||||
base.HandsModified();
|
||||
}
|
||||
|
||||
public void UpdateHandContainers()
|
||||
@@ -149,27 +62,10 @@ namespace Content.Client.Hands
|
||||
appearance.SetData(HandsVisuals.VisualState, GetHandsVisualState());
|
||||
}
|
||||
|
||||
public void UpdateHandsGuiState()
|
||||
{
|
||||
Gui?.SetState(GetHandsGuiState());
|
||||
}
|
||||
|
||||
private HandsGuiState GetHandsGuiState()
|
||||
{
|
||||
var handStates = new List<GuiHand>();
|
||||
|
||||
foreach (var hand in ReadOnlyHands)
|
||||
{
|
||||
var handState = new GuiHand(hand.Name, hand.Location, hand.HeldEntity, hand.Enabled);
|
||||
handStates.Add(handState);
|
||||
}
|
||||
return new HandsGuiState(handStates, ActiveHand);
|
||||
}
|
||||
|
||||
private HandsVisualState GetHandsVisualState()
|
||||
{
|
||||
var hands = new List<HandVisualState>();
|
||||
foreach (var hand in ReadOnlyHands)
|
||||
foreach (var hand in Hands)
|
||||
{
|
||||
if (hand.HeldEntity == null)
|
||||
continue;
|
||||
@@ -182,16 +78,5 @@ namespace Content.Client.Hands
|
||||
}
|
||||
return new(hands);
|
||||
}
|
||||
|
||||
private void RunPickupAnimation(PickupAnimationMessage msg)
|
||||
{
|
||||
if (!Owner.EntityManager.TryGetEntity(msg.EntityUid, out var entity))
|
||||
return;
|
||||
|
||||
if (!IoCManager.Resolve<IGameTiming>().IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
ReusableAnimations.AnimateEntityPickup(entity, msg.InitialPosition, msg.PickupDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
Content.Client/Hands/HandsGui.xaml
Normal file
6
Content.Client/Hands/HandsGui.xaml
Normal file
@@ -0,0 +1,6 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Control Name="StatusContainer" />
|
||||
<BoxContainer Name="HandsContainer" Orientation="Horizontal" HorizontalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -1,87 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Client.HUD;
|
||||
using Content.Client.Items.Managers;
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
public class HandsGui : Control
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HandsGui : Control
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
||||
|
||||
private readonly HandsSystem _handsSystem;
|
||||
private readonly HandsComponent _handsComponent;
|
||||
|
||||
private Texture StorageTexture => _gameHud.GetHudTexture("back.png");
|
||||
private Texture BlockedTexture => _resourceCache.GetTexture("/Textures/Interface/Inventory/blocked.png");
|
||||
|
||||
private ItemStatusPanel StatusPanel { get; }
|
||||
|
||||
private BoxContainer HandsContainer { get; }
|
||||
|
||||
[ViewVariables]
|
||||
public IReadOnlyList<GuiHand> Hands => _hands;
|
||||
private List<GuiHand> _hands = new();
|
||||
[ViewVariables] private GuiHand[] _hands = Array.Empty<GuiHand>();
|
||||
|
||||
private string? ActiveHand { get; set; }
|
||||
|
||||
public Action<HandClickEventArgs>? HandClick; //TODO: Move to Eventbus
|
||||
|
||||
public Action<HandActivateEventArgs>? HandActivate; //TODO: Move to Eventbus
|
||||
|
||||
public HandsGui()
|
||||
public HandsGui(HandsComponent hands, HandsSystem handsSystem)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
||||
_handsComponent = hands;
|
||||
_handsSystem = handsSystem;
|
||||
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
SeparationOverride = 0,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Children =
|
||||
{
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
(StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle)),
|
||||
(HandsContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalAlignment = HAlignment.Center
|
||||
}),
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle);
|
||||
StatusContainer.AddChild(StatusPanel);
|
||||
StatusPanel.SetPositionFirst();
|
||||
}
|
||||
|
||||
public void SetState(HandsGuiState state)
|
||||
protected override void EnteredTree()
|
||||
{
|
||||
base.EnteredTree();
|
||||
|
||||
_handsSystem.GuiStateUpdated += HandsSystemOnGuiStateUpdated;
|
||||
_configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
||||
|
||||
HandsSystemOnGuiStateUpdated();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
_handsSystem.GuiStateUpdated -= HandsSystemOnGuiStateUpdated;
|
||||
_configManager.UnsubValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
||||
}
|
||||
|
||||
private void HandsSystemOnGuiStateUpdated()
|
||||
{
|
||||
var state = _handsSystem.GetGuiState();
|
||||
|
||||
ActiveHand = state.ActiveHand;
|
||||
_hands = state.GuiHands;
|
||||
Array.Sort(_hands, HandOrderComparer.Instance);
|
||||
UpdateGui();
|
||||
}
|
||||
|
||||
@@ -97,12 +94,15 @@ namespace Content.Client.Hands
|
||||
|
||||
var handName = hand.Name;
|
||||
newButton.OnPressed += args => OnHandPressed(args, handName);
|
||||
newButton.OnStoragePressed += args => OnStoragePressed(handName);
|
||||
|
||||
newButton.Blocked.Visible = !hand.Enabled;
|
||||
newButton.OnStoragePressed += _ => OnStoragePressed(handName);
|
||||
|
||||
_itemSlotManager.SetItemSlot(newButton, hand.HeldItem);
|
||||
|
||||
// Show blocked overlay if hand is pulling.
|
||||
newButton.Blocked.Visible =
|
||||
hand.HeldItem != null && hand.HeldItem.HasComponent<HandVirtualPullComponent>();
|
||||
}
|
||||
|
||||
if (TryGetActiveHand(out var activeHand))
|
||||
{
|
||||
activeHand.HandButton.SetActiveHand(true);
|
||||
@@ -114,7 +114,7 @@ namespace Content.Client.Hands
|
||||
{
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
{
|
||||
HandClick?.Invoke(new HandClickEventArgs(handName));
|
||||
_handsSystem.UIHandClick(_handsComponent, handName);
|
||||
}
|
||||
else if (TryGetHand(handName, out var hand))
|
||||
{
|
||||
@@ -124,7 +124,7 @@ namespace Content.Client.Hands
|
||||
|
||||
private void OnStoragePressed(string handName)
|
||||
{
|
||||
HandActivate?.Invoke(new HandActivateEventArgs(handName));
|
||||
_handsSystem.UIHandActivate(handName);
|
||||
}
|
||||
|
||||
private bool TryGetActiveHand([NotNullWhen(true)] out GuiHand? activeHand)
|
||||
@@ -145,6 +145,7 @@ namespace Content.Client.Hands
|
||||
if (hand.Name == handName)
|
||||
foundHand = hand;
|
||||
}
|
||||
|
||||
return foundHand != null;
|
||||
}
|
||||
|
||||
@@ -153,7 +154,9 @@ namespace Content.Client.Hands
|
||||
base.FrameUpdate(args);
|
||||
|
||||
foreach (var hand in _hands)
|
||||
{
|
||||
_itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem);
|
||||
}
|
||||
}
|
||||
|
||||
private HandButton MakeHandButton(HandLocation buttonLocation)
|
||||
@@ -173,23 +176,31 @@ namespace Content.Client.Hands
|
||||
UpdateGui();
|
||||
}
|
||||
|
||||
public class HandClickEventArgs
|
||||
private sealed class HandOrderComparer : IComparer<GuiHand>
|
||||
{
|
||||
public string HandClicked { get; }
|
||||
public static readonly HandOrderComparer Instance = new();
|
||||
|
||||
public HandClickEventArgs(string handClicked)
|
||||
public int Compare(GuiHand? x, GuiHand? y)
|
||||
{
|
||||
HandClicked = handClicked;
|
||||
}
|
||||
}
|
||||
if (ReferenceEquals(x, y)) return 0;
|
||||
if (ReferenceEquals(null, y)) return 1;
|
||||
if (ReferenceEquals(null, x)) return -1;
|
||||
|
||||
public class HandActivateEventArgs
|
||||
{
|
||||
public string HandUsed { get; }
|
||||
var orderX = Map(x.HandLocation);
|
||||
var orderY = Map(y.HandLocation);
|
||||
|
||||
public HandActivateEventArgs(string handUsed)
|
||||
{
|
||||
HandUsed = handUsed;
|
||||
return orderX.CompareTo(orderY);
|
||||
|
||||
static int Map(HandLocation loc)
|
||||
{
|
||||
return loc switch
|
||||
{
|
||||
HandLocation.Left => 3,
|
||||
HandLocation.Middle => 2,
|
||||
HandLocation.Right => 1,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(loc), loc, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,7 +214,7 @@ namespace Content.Client.Hands
|
||||
/// The set of hands to be displayed.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public List<GuiHand> GuiHands { get; } = new();
|
||||
public GuiHand[] GuiHands { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the currently active hand.
|
||||
@@ -211,7 +222,7 @@ namespace Content.Client.Hands
|
||||
[ViewVariables]
|
||||
public string? ActiveHand { get; }
|
||||
|
||||
public HandsGuiState(List<GuiHand> guiHands, string? activeHand = null)
|
||||
public HandsGuiState(GuiHand[] guiHands, string? activeHand = null)
|
||||
{
|
||||
GuiHands = guiHands;
|
||||
ActiveHand = activeHand;
|
||||
@@ -247,18 +258,11 @@ namespace Content.Client.Hands
|
||||
[ViewVariables]
|
||||
public HandButton HandButton { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// If this hand can be used by the player.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool Enabled { get; }
|
||||
|
||||
public GuiHand(string name, HandLocation handLocation, IEntity? heldItem, bool enabled)
|
||||
public GuiHand(string name, HandLocation handLocation, IEntity? heldItem)
|
||||
{
|
||||
Name = name;
|
||||
HandLocation = handLocation;
|
||||
HeldItem = heldItem;
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
internal sealed class HandsSystem : SharedHandsSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, PlayerAttachedEvent>((_, component, _) => component.SettupGui());
|
||||
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>((_, component, _) => component.ClearGui());
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.SwapHands, InputCmdHandler.FromDelegate(SwapHandsPressed))
|
||||
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
|
||||
.Register<HandsSystem>();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
CommandBinds.Unregister<HandsSystem>();
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void SwapHandsPressed(ICommonSession? session)
|
||||
{
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
var player = session.AttachedEntity;
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
||||
return;
|
||||
|
||||
if (!hands.TryGetSwapHandsResult(out var nextHand))
|
||||
return;
|
||||
|
||||
EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(nextHand));
|
||||
}
|
||||
|
||||
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
if (session == null)
|
||||
return false;
|
||||
|
||||
var player = session.AttachedEntity;
|
||||
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
||||
return false;
|
||||
|
||||
var activeHand = hands.ActiveHand;
|
||||
|
||||
if (activeHand == null)
|
||||
return false;
|
||||
|
||||
EntityManager.RaisePredictiveEvent(new RequestDropHeldEntityEvent(activeHand, coords));
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
component.HandsModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Content.Client/Hands/Systems/HandVirtualPullSystem.cs
Normal file
18
Content.Client/Hands/Systems/HandVirtualPullSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Hands.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class HandVirtualPullSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Subs.ItemStatus<HandVirtualPullComponent>(_ => new HandVirtualPullItemStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Content.Client/Hands/Systems/HandsSystem.cs
Normal file
149
Content.Client/Hands/Systems/HandsSystem.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Client.Animations;
|
||||
using Content.Client.HUD;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Hands
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class HandsSystem : SharedHandsSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public event Action? GuiStateUpdated;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, PlayerAttachedEvent>(HandlePlayerAttached);
|
||||
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>(HandlePlayerDetached);
|
||||
SubscribeLocalEvent<HandsComponent, ComponentRemove>(HandleCompRemove);
|
||||
SubscribeLocalEvent<HandsModifiedMessage>(HandleHandsModified);
|
||||
|
||||
SubscribeNetworkEvent<PickupAnimationMessage>(HandlePickupAnimation);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
CommandBinds.Unregister<HandsSystem>();
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void HandleHandsModified(HandsModifiedMessage ev)
|
||||
{
|
||||
if (ev.Hands.Owner == _playerManager.LocalPlayer?.ControlledEntity)
|
||||
GuiStateUpdated?.Invoke();
|
||||
}
|
||||
|
||||
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
if (uid == _playerManager.LocalPlayer?.ControlledEntity?.Uid)
|
||||
GuiStateUpdated?.Invoke();
|
||||
}
|
||||
|
||||
private void HandlePickupAnimation(PickupAnimationMessage msg)
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(msg.EntityUid, out var entity))
|
||||
return;
|
||||
|
||||
if (!_gameTiming.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
ReusableAnimations.AnimateEntityPickup(entity, msg.InitialPosition, msg.PickupDirection);
|
||||
}
|
||||
|
||||
public HandsGuiState GetGuiState()
|
||||
{
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
|
||||
if (player == null || !player.TryGetComponent(out HandsComponent? hands))
|
||||
return new HandsGuiState(Array.Empty<GuiHand>());
|
||||
|
||||
var states = hands.Hands
|
||||
.Select(hand => new GuiHand(hand.Name, hand.Location, hand.HeldEntity))
|
||||
.ToArray();
|
||||
|
||||
return new HandsGuiState(states, hands.ActiveHand);
|
||||
}
|
||||
|
||||
public void UIHandClick(HandsComponent hands, string handName)
|
||||
{
|
||||
if (!hands.TryGetHand(handName, out var pressedHand))
|
||||
return;
|
||||
|
||||
if (!hands.TryGetActiveHand(out var activeHand))
|
||||
return;
|
||||
|
||||
var pressedEntity = pressedHand.HeldEntity;
|
||||
var activeEntity = activeHand.HeldEntity;
|
||||
|
||||
if (pressedHand == activeHand && activeEntity != null)
|
||||
{
|
||||
// use item in hand
|
||||
// it will always be attack_self() in my heart.
|
||||
RaiseNetworkEvent(new UseInHandMsg());
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity == null)
|
||||
{
|
||||
// change active hand
|
||||
RaiseNetworkEvent(new RequestSetHandEvent(handName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity != null)
|
||||
{
|
||||
// use active item on held item
|
||||
RaiseNetworkEvent(new ClientInteractUsingInHandMsg(pressedHand.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity == null)
|
||||
{
|
||||
// use active item on held item
|
||||
RaiseNetworkEvent(new MoveItemFromHandMsg(pressedHand.Name));
|
||||
}
|
||||
}
|
||||
|
||||
public void UIHandActivate(string handName)
|
||||
{
|
||||
RaiseNetworkEvent (new ActivateInHandMsg(handName));
|
||||
}
|
||||
|
||||
private void HandlePlayerAttached(EntityUid uid, HandsComponent component, PlayerAttachedEvent args)
|
||||
{
|
||||
component.Gui = new HandsGui(component, this);
|
||||
_gameHud.HandsContainer.AddChild(component.Gui);
|
||||
}
|
||||
|
||||
private static void HandlePlayerDetached(EntityUid uid, HandsComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
ClearGui(component);
|
||||
}
|
||||
|
||||
private static void HandleCompRemove(EntityUid uid, HandsComponent component, ComponentRemove args)
|
||||
{
|
||||
ClearGui(component);
|
||||
}
|
||||
|
||||
private static void ClearGui(HandsComponent comp)
|
||||
{
|
||||
comp.Gui?.Orphan();
|
||||
comp.Gui = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Content.Client/Items/ItemStatusMessages.cs
Normal file
32
Content.Client/Items/ItemStatusMessages.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Items
|
||||
{
|
||||
public sealed class ItemStatusCollectMessage : EntityEventArgs
|
||||
{
|
||||
public List<Control> Controls = new();
|
||||
}
|
||||
|
||||
public static class ItemStatusRegisterExt
|
||||
{
|
||||
/// <summary>
|
||||
/// Register an item status control for a component.
|
||||
/// </summary>
|
||||
/// <param name="subs">The <see cref="EntitySystem.Subs"/> handle from within entity system initialize.</param>
|
||||
/// <param name="createControl">A delegate to create the actual control.</param>
|
||||
/// <typeparam name="TComp">The type of component for which this control should be made.</typeparam>
|
||||
public static void ItemStatus<TComp>(
|
||||
this EntitySystem.Subscriptions subs,
|
||||
Func<EntityUid, Control> createControl)
|
||||
where TComp : IComponent
|
||||
{
|
||||
subs.SubscribeLocalEvent<TComp, ItemStatusCollectMessage>((uid, _, args) =>
|
||||
{
|
||||
args.Controls.Add(createControl(uid));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Client.Items.UI;
|
||||
using System;
|
||||
using Content.Client.Items.UI;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -10,5 +11,21 @@ namespace Content.Client.Items.Managers
|
||||
void UpdateCooldown(ItemSlotButton? cooldownTexture, IEntity? entity);
|
||||
bool SetItemSlot(ItemSlotButton button, IEntity? entity);
|
||||
void HoverInSlot(ItemSlotButton button, IEntity? entity, bool fits);
|
||||
event Action<EntitySlotHighlightedEventArgs>? EntityHighlightedUpdated;
|
||||
bool IsHighlighted(EntityUid uid);
|
||||
|
||||
/// <summary>
|
||||
/// Highlight all slot controls that contain the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="uid">The UID of the entity to highlight.</param>
|
||||
/// <seealso cref="UnHighlightEntity"/>
|
||||
void HighlightEntity(EntityUid uid);
|
||||
|
||||
/// <summary>
|
||||
/// Remove highlighting for the specified entity.
|
||||
/// </summary>
|
||||
/// <param name="uid">The UID of the entity to unhighlight.</param>
|
||||
/// <seealso cref="HighlightEntity"/>
|
||||
void UnHighlightEntity(EntityUid uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Examine;
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Storage;
|
||||
using Content.Client.Verbs;
|
||||
using Content.Shared.Cooldown;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -28,6 +31,11 @@ namespace Content.Client.Items.Managers
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IComponentManager _componentManager = default!;
|
||||
|
||||
private readonly HashSet<EntityUid> _highlightEntities = new();
|
||||
|
||||
public event Action<EntitySlotHighlightedEventArgs>? EntityHighlightedUpdated;
|
||||
|
||||
public bool SetItemSlot(ItemSlotButton button, IEntity? entity)
|
||||
{
|
||||
@@ -38,13 +46,26 @@ namespace Content.Client.Items.Managers
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!entity.TryGetComponent(out ISpriteComponent? sprite))
|
||||
ISpriteComponent? sprite;
|
||||
if (entity.TryGetComponent(out HandVirtualPullComponent? virtPull)
|
||||
&& _componentManager.TryGetComponent(virtPull.PulledEntity, out ISpriteComponent pulledSprite))
|
||||
{
|
||||
sprite = pulledSprite;
|
||||
}
|
||||
else if (!entity.TryGetComponent(out sprite))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
button.ClearHover();
|
||||
button.SpriteView.Sprite = sprite;
|
||||
button.StorageButton.Visible = entity.HasComponent<ClientStorageComponent>();
|
||||
}
|
||||
|
||||
button.Entity = entity?.Uid ?? default;
|
||||
|
||||
// im lazy
|
||||
button.UpdateSlotHighlighted();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,5 +166,38 @@ namespace Content.Client.Items.Managers
|
||||
|
||||
button.HoverSpriteView.Sprite = hoverSprite;
|
||||
}
|
||||
|
||||
public bool IsHighlighted(EntityUid uid)
|
||||
{
|
||||
return _highlightEntities.Contains(uid);
|
||||
}
|
||||
|
||||
public void HighlightEntity(EntityUid uid)
|
||||
{
|
||||
if (!_highlightEntities.Add(uid))
|
||||
return;
|
||||
|
||||
EntityHighlightedUpdated?.Invoke(new EntitySlotHighlightedEventArgs(uid, true));
|
||||
}
|
||||
|
||||
public void UnHighlightEntity(EntityUid uid)
|
||||
{
|
||||
if (!_highlightEntities.Remove(uid))
|
||||
return;
|
||||
|
||||
EntityHighlightedUpdated?.Invoke(new EntitySlotHighlightedEventArgs(uid, false));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct EntitySlotHighlightedEventArgs
|
||||
{
|
||||
public EntitySlotHighlightedEventArgs(EntityUid entity, bool newHighlighted)
|
||||
{
|
||||
Entity = entity;
|
||||
NewHighlighted = newHighlighted;
|
||||
}
|
||||
|
||||
public EntityUid Entity { get; }
|
||||
public bool NewHighlighted { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
using System;
|
||||
using Content.Client.Cooldown;
|
||||
using Content.Client.Items.Managers;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Items.UI
|
||||
{
|
||||
public class ItemSlotButton : Control
|
||||
public class ItemSlotButton : Control, IEntityEventSubscriber
|
||||
{
|
||||
private const string HighlightShader = "SelectionOutlineInrange";
|
||||
|
||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||
|
||||
public EntityUid Entity { get; set; }
|
||||
public TextureRect Button { get; }
|
||||
public SpriteView SpriteView { get; }
|
||||
public SpriteView HoverSpriteView { get; }
|
||||
@@ -32,6 +38,8 @@ namespace Content.Client.Items.UI
|
||||
|
||||
public ItemSlotButton(Texture texture, Texture storageTexture, string textureName)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
MinSize = (64, 64);
|
||||
|
||||
TextureName = textureName;
|
||||
@@ -101,6 +109,31 @@ namespace Content.Client.Items.UI
|
||||
});
|
||||
}
|
||||
|
||||
protected override void EnteredTree()
|
||||
{
|
||||
base.EnteredTree();
|
||||
|
||||
_itemSlotManager.EntityHighlightedUpdated += HandleEntitySlotHighlighted;
|
||||
UpdateSlotHighlighted();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
_itemSlotManager.EntityHighlightedUpdated -= HandleEntitySlotHighlighted;
|
||||
}
|
||||
|
||||
private void HandleEntitySlotHighlighted(EntitySlotHighlightedEventArgs entitySlotHighlightedEventArgs)
|
||||
{
|
||||
UpdateSlotHighlighted();
|
||||
}
|
||||
|
||||
public void UpdateSlotHighlighted()
|
||||
{
|
||||
Highlight(_itemSlotManager.IsHighlighted(Entity));
|
||||
}
|
||||
|
||||
public void ClearHover()
|
||||
{
|
||||
if (EntityHover)
|
||||
|
||||
@@ -8,7 +8,9 @@ using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Client.IoC.StaticIoC;
|
||||
@@ -18,6 +20,8 @@ namespace Content.Client.Items.UI
|
||||
{
|
||||
public class ItemStatusPanel : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly List<(IItemStatus, Control)> _activeStatusComponents = new();
|
||||
|
||||
@@ -33,6 +37,8 @@ namespace Content.Client.Items.UI
|
||||
|
||||
public ItemStatusPanel(Texture texture, StyleBox.Margin cutout, StyleBox.Margin flat, Label.AlignMode textAlign)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var panel = new StyleBoxTexture
|
||||
{
|
||||
Texture = texture
|
||||
@@ -117,6 +123,13 @@ namespace Content.Client.Items.UI
|
||||
return new ItemStatusPanel(ResC.GetTexture(texture), cutOut, flat, textAlign);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
UpdateItemName();
|
||||
}
|
||||
|
||||
public void Update(IEntity? entity)
|
||||
{
|
||||
if (entity == null)
|
||||
@@ -131,12 +144,29 @@ namespace Content.Client.Items.UI
|
||||
{
|
||||
_entity = entity;
|
||||
BuildNewEntityStatus();
|
||||
_itemNameLabel.Text = entity.Name;
|
||||
|
||||
UpdateItemName();
|
||||
}
|
||||
|
||||
_panel.Visible = true;
|
||||
}
|
||||
|
||||
private void UpdateItemName()
|
||||
{
|
||||
if (_entity == null)
|
||||
return;
|
||||
|
||||
if (_entity.TryGetComponent(out HandVirtualPullComponent? virtualPull)
|
||||
&& _entityManager.TryGetEntity(virtualPull.PulledEntity, out var pulledEnt))
|
||||
{
|
||||
_itemNameLabel.Text = pulledEnt.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_itemNameLabel.Text = _entity.Name;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearOldStatus()
|
||||
{
|
||||
_statusContents.RemoveAllChildren();
|
||||
@@ -162,6 +192,14 @@ namespace Content.Client.Items.UI
|
||||
|
||||
_activeStatusComponents.Add((statusComponent, control));
|
||||
}
|
||||
|
||||
var collectMsg = new ItemStatusCollectMessage();
|
||||
_entity.EntityManager.EventBus.RaiseLocalEvent(_entity.Uid, collectMsg);
|
||||
|
||||
foreach (var control in collectMsg.Controls)
|
||||
{
|
||||
_statusContents.AddChild(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ using Content.Shared.DragDrop;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Kitchen
|
||||
namespace Content.Client.Kitchen.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
internal sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
|
||||
17
Content.Client/Kitchen/Components/MicrowaveComponent.cs
Normal file
17
Content.Client/Kitchen/Components/MicrowaveComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.Kitchen.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MicrowaveComponent : SharedMicrowaveComponent
|
||||
{
|
||||
public IPlayingAudioStream? PlayingStream { get; set; }
|
||||
|
||||
[DataField("loopingSound")]
|
||||
public SoundSpecifier LoopingSound = new SoundPathSpecifier("/Audio/Machines/microwave_loop.ogg");
|
||||
}
|
||||
}
|
||||
31
Content.Client/Kitchen/EntitySystems/MicrowaveSystem.cs
Normal file
31
Content.Client/Kitchen/EntitySystems/MicrowaveSystem.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using Content.Client.Kitchen.Components;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Kitchen.EntitySystems
|
||||
{
|
||||
public class MicrowaveSystem : EntitySystem
|
||||
{
|
||||
public void StartSoundLoop(MicrowaveComponent microwave)
|
||||
{
|
||||
StopSoundLoop(microwave);
|
||||
|
||||
microwave.PlayingStream = SoundSystem.Play(Filter.Local(), microwave.LoopingSound.GetSound(), microwave.Owner,
|
||||
AudioParams.Default.WithAttenuation(1).WithMaxDistance(5).WithLoop(true));
|
||||
}
|
||||
|
||||
public void StopSoundLoop(MicrowaveComponent microwave)
|
||||
{
|
||||
try
|
||||
{
|
||||
microwave.PlayingStream?.Stop();
|
||||
}
|
||||
catch (Exception _)
|
||||
{
|
||||
// TODO: HOLY SHIT EXPOSE SOME DISPOSED PROPERTY ON PLAYING STREAM OR SOMETHING.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Content.Client/Kitchen/UI/GrinderMenu.xaml
Normal file
15
Content.Client/Kitchen/UI/GrinderMenu.xaml
Normal file
@@ -0,0 +1,15 @@
|
||||
<SS14Window xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.Kitchen.UI"
|
||||
Title="{Loc grinder-menu-title}" MinSize="512 256" SetSize="512 256">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer Orientation="Vertical" VerticalAlignment="Center">
|
||||
<Button Name="GrindButton" Text="{Loc grinder-menu-grind-button}" TextAlign="Center" MinSize="64 64"/>
|
||||
<Control MinSize="0 16"/>
|
||||
<Button Name="JuiceButton" Text="{Loc grinder-menu-juice-button}" TextAlign="Center" MinSize="64 64"/>
|
||||
</BoxContainer>
|
||||
<Control MinSize="16 0"/>
|
||||
<ui:LabelledContentBox Name="ChamberContentBox" LabelText="{Loc grinder-menu-chamber-content-box-label}" ButtonText="{Loc grinder-menu-chamber-content-box-button}" VerticalExpand="True" HorizontalExpand="True" SizeFlagsStretchRatio="2"/>
|
||||
<Control MinSize="8 0"/>
|
||||
<ui:LabelledContentBox Name="BeakerContentBox" LabelText="{Loc grinder-menu-beaker-content-box-label}" ButtonText="{Loc grinder-menu-beaker-content-box-button}" VerticalExpand="True" HorizontalExpand="True" SizeFlagsStretchRatio="2"/>
|
||||
</BoxContainer>
|
||||
</SS14Window>
|
||||
131
Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
Normal file
131
Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Kitchen.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class GrinderMenu : SS14Window
|
||||
{
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IPrototypeManager _prototypeManager ;
|
||||
private readonly ReagentGrinderBoundUserInterface _owner;
|
||||
|
||||
private readonly Dictionary<int, EntityUid> _chamberVisualContents = new();
|
||||
|
||||
public GrinderMenu(ReagentGrinderBoundUserInterface owner, IEntityManager entityManager, IPrototypeManager prototypeManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entityManager = entityManager;
|
||||
_prototypeManager = prototypeManager;
|
||||
_owner = owner;
|
||||
GrindButton.OnPressed += owner.StartGrinding;
|
||||
JuiceButton.OnPressed += owner.StartJuicing;
|
||||
ChamberContentBox.EjectButton.OnPressed += owner.EjectAll;
|
||||
BeakerContentBox.EjectButton.OnPressed += owner.EjectBeaker;
|
||||
ChamberContentBox.BoxContents.OnItemSelected += OnChamberBoxContentsItemSelected;
|
||||
BeakerContentBox.BoxContents.SelectMode = ItemList.ItemListSelectMode.None;
|
||||
}
|
||||
|
||||
private void OnChamberBoxContentsItemSelected(ItemList.ItemListSelectedEventArgs args)
|
||||
{
|
||||
_owner.EjectChamberContent(_chamberVisualContents[args.ItemIndex]);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
_chamberVisualContents.Clear();
|
||||
GrindButton.OnPressed -= _owner.StartGrinding;
|
||||
JuiceButton.OnPressed -= _owner.StartJuicing;
|
||||
ChamberContentBox.EjectButton.OnPressed -= _owner.EjectAll;
|
||||
BeakerContentBox.EjectButton.OnPressed -= _owner.EjectBeaker;
|
||||
ChamberContentBox.BoxContents.OnItemSelected -= OnChamberBoxContentsItemSelected;
|
||||
}
|
||||
|
||||
public void UpdateState(ReagentGrinderInterfaceState state)
|
||||
{
|
||||
BeakerContentBox.EjectButton.Disabled = !state.HasBeakerIn;
|
||||
ChamberContentBox.EjectButton.Disabled = state.ChamberContents.Length <= 0;
|
||||
GrindButton.Disabled = !state.CanGrind || !state.Powered;
|
||||
JuiceButton.Disabled = !state.CanJuice || !state.Powered;
|
||||
RefreshContentsDisplay(state.ReagentQuantities, state.ChamberContents, state.HasBeakerIn);
|
||||
}
|
||||
|
||||
public void HandleMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage workStarted:
|
||||
GrindButton.Disabled = true;
|
||||
GrindButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Grind ? Color.Green : Color.White;
|
||||
JuiceButton.Disabled = true;
|
||||
JuiceButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Juice ? Color.Green : Color.White;
|
||||
BeakerContentBox.EjectButton.Disabled = true;
|
||||
ChamberContentBox.EjectButton.Disabled = true;
|
||||
break;
|
||||
case SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage doneMessage:
|
||||
GrindButton.Disabled = false;
|
||||
JuiceButton.Disabled = false;
|
||||
GrindButton.Modulate = Color.White;
|
||||
JuiceButton.Modulate = Color.White;
|
||||
BeakerContentBox.EjectButton.Disabled = false;
|
||||
ChamberContentBox.EjectButton.Disabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshContentsDisplay(IList<Solution.ReagentQuantity>? reagents, IReadOnlyList<EntityUid> containedSolids, bool isBeakerAttached)
|
||||
{
|
||||
//Refresh chamber contents
|
||||
_chamberVisualContents.Clear();
|
||||
|
||||
ChamberContentBox.BoxContents.Clear();
|
||||
foreach (var uid in containedSolids)
|
||||
{
|
||||
if (!_entityManager.TryGetEntity(uid, out var entity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var texture = entity.GetComponent<SpriteComponent>().Icon?.Default;
|
||||
|
||||
var solidItem = ChamberContentBox.BoxContents.AddItem(entity.Name, texture);
|
||||
var solidIndex = ChamberContentBox.BoxContents.IndexOf(solidItem);
|
||||
_chamberVisualContents.Add(solidIndex, uid);
|
||||
}
|
||||
|
||||
//Refresh beaker contents
|
||||
BeakerContentBox.BoxContents.Clear();
|
||||
//if no beaker is attached use this guard to prevent hitting a null reference.
|
||||
if (!isBeakerAttached || reagents == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Looks like we have a beaker attached.
|
||||
if (reagents.Count <= 0)
|
||||
{
|
||||
BeakerContentBox.BoxContents.AddItem(Loc.GetString("grinder-menu-beaker-content-box-is-empty"));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var reagent in reagents)
|
||||
{
|
||||
var reagentName = _prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto) ? Loc.GetString($"{reagent.Quantity} {proto.Name}") : "???";
|
||||
BeakerContentBox.BoxContents.AddItem(reagentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Content.Client/Kitchen/UI/LabelledContentBox.xaml
Normal file
7
Content.Client/Kitchen/UI/LabelledContentBox.xaml
Normal file
@@ -0,0 +1,7 @@
|
||||
<customControls:BoxContainer Orientation="Vertical" xmlns:customControls="https://spacestation14.io">
|
||||
<customControls:SplitContainer Orientation="Horizontal">
|
||||
<customControls:Label Name="Label" Align="Center"/>
|
||||
<customControls:Button Name="Button" TextAlign="Center"/>
|
||||
</customControls:SplitContainer>
|
||||
<customControls:ItemList Name="ItemList" VerticalExpand="True" HorizontalExpand="True" SelectMode="Button" SizeFlagsStretchRatio="2" MinSize="100 128"/>
|
||||
</customControls:BoxContainer>
|
||||
15
Content.Client/Kitchen/UI/LabelledContentBox.xaml.cs
Normal file
15
Content.Client/Kitchen/UI/LabelledContentBox.xaml.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Kitchen.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class LabelledContentBox : BoxContainer
|
||||
{
|
||||
public string? LabelText { get => Label.Text; set => Label.Text = value; }
|
||||
public string? ButtonText { get => Button.Text; set => Button.Text = value; }
|
||||
|
||||
public ItemList BoxContents => ItemList;
|
||||
public Button EjectButton => Button;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.Chemistry.Solution.Solution;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Kitchen.UI
|
||||
{
|
||||
@@ -21,8 +14,6 @@ namespace Content.Client.Kitchen.UI
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private GrinderMenu? _menu;
|
||||
private readonly Dictionary<int, EntityUid> _chamberVisualContents = new();
|
||||
private readonly Dictionary<int, ReagentQuantity> _beakerVisualContents = new();
|
||||
|
||||
public ReagentGrinderBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) { }
|
||||
|
||||
@@ -30,22 +21,9 @@ namespace Content.Client.Kitchen.UI
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new GrinderMenu(this);
|
||||
_menu = new GrinderMenu(this, _entityManager, _prototypeManager);
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
_menu.GrindButton.OnPressed += args => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage());
|
||||
_menu.JuiceButton.OnPressed += args => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage());
|
||||
_menu.ChamberContentBox.EjectButton.OnPressed += args => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage());
|
||||
_menu.BeakerContentBox.EjectButton.OnPressed += args => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectBeakerMessage());
|
||||
_menu.ChamberContentBox.BoxContents.OnItemSelected += args =>
|
||||
{
|
||||
SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage(_chamberVisualContents[args.ItemIndex]));
|
||||
};
|
||||
|
||||
_menu.BeakerContentBox.BoxContents.OnItemSelected += args =>
|
||||
{
|
||||
SendMessage(new SharedReagentGrinderComponent.ReagentGrinderVaporizeReagentIndexedMessage(_beakerVisualContents[args.ItemIndex]));
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -56,8 +34,6 @@ namespace Content.Client.Kitchen.UI
|
||||
return;
|
||||
}
|
||||
|
||||
_chamberVisualContents?.Clear();
|
||||
_beakerVisualContents?.Clear();
|
||||
_menu?.Dispose();
|
||||
}
|
||||
|
||||
@@ -69,243 +45,19 @@ namespace Content.Client.Kitchen.UI
|
||||
return;
|
||||
}
|
||||
|
||||
if (_menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_menu.BeakerContentBox.EjectButton.Disabled = !cState.HasBeakerIn;
|
||||
_menu.ChamberContentBox.EjectButton.Disabled = cState.ChamberContents.Length <= 0;
|
||||
_menu.GrindButton.Disabled = !cState.CanGrind || !cState.Powered;
|
||||
_menu.JuiceButton.Disabled = !cState.CanJuice || !cState.Powered;
|
||||
RefreshContentsDisplay(cState.ReagentQuantities, cState.ChamberContents, cState.HasBeakerIn);
|
||||
_menu?.UpdateState(cState);
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
|
||||
if (_menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage workStarted:
|
||||
_menu.GrindButton.Disabled = true;
|
||||
_menu.GrindButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Grind ? Color.Green : Color.White;
|
||||
_menu.JuiceButton.Disabled = true;
|
||||
_menu.JuiceButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Juice ? Color.Green : Color.White;
|
||||
_menu.BeakerContentBox.EjectButton.Disabled = true;
|
||||
_menu.ChamberContentBox.EjectButton.Disabled = true;
|
||||
break;
|
||||
case SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage doneMessage:
|
||||
_menu.GrindButton.Disabled = false;
|
||||
_menu.JuiceButton.Disabled = false;
|
||||
_menu.GrindButton.Modulate = Color.White;
|
||||
_menu.JuiceButton.Modulate = Color.White;
|
||||
_menu.BeakerContentBox.EjectButton.Disabled = false;
|
||||
_menu.ChamberContentBox.EjectButton.Disabled = false;
|
||||
break;
|
||||
}
|
||||
_menu?.HandleMessage(message);
|
||||
}
|
||||
|
||||
private void RefreshContentsDisplay(IList<ReagentQuantity>? reagents, IReadOnlyList<EntityUid> containedSolids, bool isBeakerAttached)
|
||||
{
|
||||
//Refresh chamber contents
|
||||
_chamberVisualContents.Clear();
|
||||
|
||||
if (_menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_menu.ChamberContentBox.BoxContents.Clear();
|
||||
foreach (var uid in containedSolids)
|
||||
{
|
||||
if (!_entityManager.TryGetEntity(uid, out var entity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var texture = entity.GetComponent<SpriteComponent>().Icon?.Default;
|
||||
|
||||
var solidItem = _menu.ChamberContentBox.BoxContents.AddItem(entity.Name, texture);
|
||||
var solidIndex = _menu.ChamberContentBox.BoxContents.IndexOf(solidItem);
|
||||
_chamberVisualContents.Add(solidIndex, uid);
|
||||
}
|
||||
|
||||
//Refresh beaker contents
|
||||
_beakerVisualContents.Clear();
|
||||
_menu.BeakerContentBox.BoxContents.Clear();
|
||||
//if no beaker is attached use this guard to prevent hitting a null reference.
|
||||
if (!isBeakerAttached || reagents == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Looks like we have a beaker attached.
|
||||
if (reagents.Count <= 0)
|
||||
{
|
||||
_menu.BeakerContentBox.BoxContents.AddItem(Loc.GetString("grinder-menu-beaker-content-box-is-empty"));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < reagents.Count; i++)
|
||||
{
|
||||
var goodIndex = _prototypeManager.TryIndex(reagents[i].ReagentId, out ReagentPrototype? proto);
|
||||
var reagentName = goodIndex ? Loc.GetString($"{reagents[i].Quantity} {proto!.Name}") : "???";
|
||||
var reagentAdded = _menu.BeakerContentBox.BoxContents.AddItem(reagentName);
|
||||
var reagentIndex = _menu.BeakerContentBox.BoxContents.IndexOf(reagentAdded);
|
||||
_beakerVisualContents.Add(reagentIndex, reagents[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GrinderMenu : SS14Window
|
||||
{
|
||||
/*The contents of the chamber and beaker will both be vertical scroll rectangles.
|
||||
* Will have a vsplit to split the g/j buttons from the contents menu.
|
||||
* |--------------------------------\
|
||||
* | | Chamber [E] Beaker [E] |
|
||||
* | [G] | | | | | |
|
||||
* | | | | | | |
|
||||
* | | | | | | |
|
||||
* | [J] | |-----| |-----| |
|
||||
* | | |
|
||||
* \---------------------------------/
|
||||
*
|
||||
*/
|
||||
|
||||
private ReagentGrinderBoundUserInterface Owner { get; set; }
|
||||
|
||||
//We'll need 4 buttons, grind, juice, eject beaker, eject the chamber contents.
|
||||
//The other 2 are referenced in the Open function.
|
||||
public Button GrindButton { get; }
|
||||
public Button JuiceButton { get; }
|
||||
|
||||
public LabelledContentBox ChamberContentBox { get; }
|
||||
public LabelledContentBox BeakerContentBox { get; }
|
||||
|
||||
public sealed class LabelledContentBox : VBoxContainer
|
||||
{
|
||||
public string? LabelText { get; set; }
|
||||
|
||||
public ItemList BoxContents { get; set; }
|
||||
|
||||
public Button EjectButton { get; set; }
|
||||
|
||||
private Label _label;
|
||||
|
||||
public LabelledContentBox(string labelText, string buttonText)
|
||||
{
|
||||
_label = new Label
|
||||
{
|
||||
Text = labelText,
|
||||
Align = Label.AlignMode.Center,
|
||||
};
|
||||
|
||||
EjectButton = new Button
|
||||
{
|
||||
Text = buttonText,
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
};
|
||||
|
||||
var vSplit = new HSplitContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
_label,
|
||||
EjectButton
|
||||
}
|
||||
};
|
||||
|
||||
AddChild(vSplit);
|
||||
BoxContents = new ItemList
|
||||
{
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
SelectMode = ItemList.ItemListSelectMode.Button,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
MinSize = (100, 128)
|
||||
};
|
||||
AddChild(BoxContents);
|
||||
}
|
||||
}
|
||||
|
||||
public GrinderMenu(ReagentGrinderBoundUserInterface owner)
|
||||
{
|
||||
SetSize = MinSize = (512, 256);
|
||||
Owner = owner;
|
||||
Title = Loc.GetString("grinder-menu-title");
|
||||
|
||||
var hSplit = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal
|
||||
};
|
||||
|
||||
var vBoxGrindJuiceButtonPanel = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
GrindButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("grinder-menu-grind-button"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
MinSize = (64, 64)
|
||||
};
|
||||
|
||||
JuiceButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("grinder-menu-juice-button"),
|
||||
TextAlign = Label.AlignMode.Center,
|
||||
MinSize = (64, 64)
|
||||
};
|
||||
|
||||
vBoxGrindJuiceButtonPanel.AddChild(GrindButton);
|
||||
//inner button padding
|
||||
vBoxGrindJuiceButtonPanel.AddChild(new Control
|
||||
{
|
||||
MinSize = (0, 16),
|
||||
});
|
||||
vBoxGrindJuiceButtonPanel.AddChild(JuiceButton);
|
||||
|
||||
ChamberContentBox = new LabelledContentBox(Loc.GetString("grinder-menu-chamber-content-box-label"), Loc.GetString("grinder-menu-chamber-content-box-button"))
|
||||
{
|
||||
//Modulate = Color.Red,
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
|
||||
};
|
||||
|
||||
BeakerContentBox = new LabelledContentBox(Loc.GetString("grinder-menu-beaker-content-box-label"), Loc.GetString("grinder-menu-beaker-content-box-button"))
|
||||
{
|
||||
//Modulate = Color.Blue,
|
||||
VerticalExpand = true,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
};
|
||||
|
||||
hSplit.AddChild(vBoxGrindJuiceButtonPanel);
|
||||
|
||||
//Padding between the g/j buttons panel and the itemlist boxes panel.
|
||||
hSplit.AddChild(new Control
|
||||
{
|
||||
MinSize = (16, 0),
|
||||
});
|
||||
hSplit.AddChild(ChamberContentBox);
|
||||
|
||||
//Padding between the two itemlists.
|
||||
hSplit.AddChild(new Control
|
||||
{
|
||||
MinSize = (8, 0),
|
||||
});
|
||||
hSplit.AddChild(BeakerContentBox);
|
||||
Contents.AddChild(hSplit);
|
||||
}
|
||||
}
|
||||
public void StartGrinding(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage());
|
||||
public void StartJuicing(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage());
|
||||
public void EjectAll(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage());
|
||||
public void EjectBeaker(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectBeakerMessage());
|
||||
public void EjectChamberContent(EntityUid uid) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage(uid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Content.Client.Sound;
|
||||
using Content.Client.Kitchen.Components;
|
||||
using Content.Client.Kitchen.EntitySystems;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Sound;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Client.Kitchen.Visualizers
|
||||
@@ -17,35 +17,33 @@ namespace Content.Client.Kitchen.Visualizers
|
||||
base.OnChangeData(component);
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
var loopingSoundComponent = component.Owner.GetComponentOrNull<LoopingSoundComponent>();
|
||||
var microwaveComponent = component.Owner.GetComponentOrNull<MicrowaveComponent>();
|
||||
|
||||
if (!component.TryGetData(PowerDeviceVisuals.VisualState, out MicrowaveVisualState state))
|
||||
{
|
||||
state = MicrowaveVisualState.Idle;
|
||||
}
|
||||
// The only reason we get the entity system so late is so that tests don't fail... Amazing, huh?
|
||||
switch (state)
|
||||
{
|
||||
case MicrowaveVisualState.Broken:
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mwb");
|
||||
loopingSoundComponent?.StopAllSounds();
|
||||
if(microwaveComponent != null)
|
||||
EntitySystem.Get<MicrowaveSystem>().StopSoundLoop(microwaveComponent);
|
||||
break;
|
||||
|
||||
case MicrowaveVisualState.Idle:
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_unlit");
|
||||
loopingSoundComponent?.StopAllSounds();
|
||||
if(microwaveComponent != null)
|
||||
EntitySystem.Get<MicrowaveSystem>().StopSoundLoop(microwaveComponent);
|
||||
break;
|
||||
|
||||
case MicrowaveVisualState.Cooking:
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_running_unlit");
|
||||
var audioParams = AudioParams.Default;
|
||||
audioParams.Loop = true;
|
||||
var scheduledSound = new ScheduledSound();
|
||||
scheduledSound.Filename = "/Audio/Machines/microwave_loop.ogg";
|
||||
scheduledSound.AudioParams = audioParams;
|
||||
loopingSoundComponent?.StopAllSounds();
|
||||
loopingSoundComponent?.AddScheduledSound(scheduledSound);
|
||||
if(microwaveComponent != null)
|
||||
EntitySystem.Get<MicrowaveSystem>().StartSoundLoop(microwaveComponent);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Light.Component;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Light.Components
|
||||
@@ -9,6 +10,6 @@ namespace Content.Client.Light.Components
|
||||
[RegisterComponent]
|
||||
public class ExpendableLightComponent : SharedExpendableLightComponent
|
||||
{
|
||||
|
||||
public IPlayingAudioStream? PlayingStream { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using Content.Client.Light.Components;
|
||||
using System;
|
||||
using Content.Client.Light.Components;
|
||||
using Content.Shared.Light.Component;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Light.Visualizers
|
||||
{
|
||||
@@ -17,7 +20,7 @@ namespace Content.Client.Light.Visualizers
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.TryGetData(ExpendableLightVisuals.State, out string lightBehaviourID))
|
||||
if (component.TryGetData(ExpendableLightVisuals.Behavior, out string lightBehaviourID))
|
||||
{
|
||||
if (component.Owner.TryGetComponent<LightBehaviourComponent>(out var lightBehaviour))
|
||||
{
|
||||
@@ -33,6 +36,35 @@ namespace Content.Client.Light.Visualizers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TryStopStream(IPlayingAudioStream? stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream?.Stop();
|
||||
}
|
||||
catch (Exception _)
|
||||
{
|
||||
// TODO: HOLY SHIT EXPOSE SOME DISPOSED PROPERTY ON PLAYING STREAM OR SOMETHING.
|
||||
}
|
||||
}
|
||||
|
||||
if (component.TryGetData(ExpendableLightVisuals.State, out ExpendableLightState state)
|
||||
&& component.Owner.TryGetComponent<ExpendableLightComponent>(out var expendableLight))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ExpendableLightState.Lit:
|
||||
TryStopStream(expendableLight.PlayingStream);
|
||||
expendableLight.PlayingStream = SoundSystem.Play(Filter.Local(), expendableLight.LoopedSound,
|
||||
expendableLight.Owner, SharedExpendableLightComponent.LoopedSoundParams.WithLoop(true));
|
||||
break;
|
||||
|
||||
case ExpendableLightState.Dead:
|
||||
TryStopStream(expendableLight.PlayingStream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,18 @@ namespace Content.Client.Physics.Controllers
|
||||
!player.TryGetComponent(out IMoverComponent? mover) ||
|
||||
!player.TryGetComponent(out PhysicsComponent? body)) return;
|
||||
|
||||
body.Predict = true; // TODO: equal prediction instead of true?
|
||||
// Essentially we only want to set our mob to predicted so every other entity we just interpolate
|
||||
// (i.e. only see what the server has sent us).
|
||||
// The exception to this is joints.
|
||||
body.Predict = true;
|
||||
|
||||
// We set joints to predicted given these can affect how our mob moves.
|
||||
// I would only recommend disabling this if you make pulling not use joints anymore (someday maybe?)
|
||||
foreach (var joint in body.Joints)
|
||||
{
|
||||
joint.BodyA.Predict = true;
|
||||
joint.BodyB.Predict = true;
|
||||
}
|
||||
|
||||
// Server-side should just be handled on its own so we'll just do this shizznit
|
||||
if (player.TryGetComponent(out IMobMoverComponent? mobMover))
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.Sound
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class LoopingSoundComponent : SharedLoopingSoundComponent
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
private readonly Dictionary<ScheduledSound, IPlayingAudioStream> _audioStreams = new();
|
||||
|
||||
[DataField("schedules", true)]
|
||||
private List<ScheduledSound> _scheduledSounds
|
||||
{
|
||||
set => value.ForEach(AddScheduledSound);
|
||||
get => new();
|
||||
}
|
||||
|
||||
public override void StopAllSounds()
|
||||
{
|
||||
foreach (var kvp in _audioStreams)
|
||||
{
|
||||
kvp.Key.Play = false;
|
||||
kvp.Value.Stop();
|
||||
}
|
||||
_audioStreams.Clear();
|
||||
}
|
||||
|
||||
public override void StopScheduledSound(string filename)
|
||||
{
|
||||
foreach (var kvp in _audioStreams)
|
||||
{
|
||||
if (kvp.Key.Filename != filename) continue;
|
||||
kvp.Key.Play = false;
|
||||
kvp.Value.Stop();
|
||||
_audioStreams.Remove(kvp.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddScheduledSound(ScheduledSound schedule)
|
||||
{
|
||||
Play(schedule);
|
||||
}
|
||||
|
||||
public void Play(ScheduledSound schedule)
|
||||
{
|
||||
if (!schedule.Play) return;
|
||||
|
||||
Owner.SpawnTimer((int) schedule.Delay + (_random.Next((int) schedule.RandomDelay)),() =>
|
||||
{
|
||||
if (!schedule.Play) return; // We make sure this hasn't changed.
|
||||
|
||||
if (!_audioStreams.ContainsKey(schedule))
|
||||
{
|
||||
_audioStreams.Add(schedule, SoundSystem.Play(Filter.Local(), schedule.Filename, Owner, schedule.AudioParams)!);
|
||||
}
|
||||
else
|
||||
{
|
||||
_audioStreams[schedule] = SoundSystem.Play(Filter.Local(), schedule.Filename, Owner, schedule.AudioParams)!;
|
||||
}
|
||||
|
||||
if (schedule.Times == 0) return;
|
||||
|
||||
if (schedule.Times > 0) schedule.Times--;
|
||||
|
||||
Play(schedule);
|
||||
});
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, channel, session);
|
||||
switch (message)
|
||||
{
|
||||
case ScheduledSoundMessage msg:
|
||||
AddScheduledSound(msg.Schedule);
|
||||
break;
|
||||
|
||||
case StopSoundScheduleMessage msg:
|
||||
StopScheduledSound(msg.Filename);
|
||||
break;
|
||||
|
||||
case StopAllSoundsMessage _:
|
||||
StopAllSounds();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SoundSystem.OcclusionCollisionMask = (int) CollisionGroup.Impassable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Storage;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
40
Content.Client/SubFloor/SubFloorShowLayerVisualizer.cs
Normal file
40
Content.Client/SubFloor/SubFloorShowLayerVisualizer.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Content.Shared.SubFloor;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.SubFloor
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class SubFloorShowLayerVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (!component.Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
if (component.TryGetData(SubFloorVisuals.SubFloor, out bool subfloor))
|
||||
{
|
||||
sprite.Visible = true;
|
||||
|
||||
// Due to the way this visualizer works, you might want to specify it before any other
|
||||
// visualizer that hides/shows layers depending on certain conditions, such as PipeConnectorVisualizer.
|
||||
foreach (var layer in sprite.AllLayers)
|
||||
{
|
||||
layer.Visible = subfloor;
|
||||
}
|
||||
|
||||
if (sprite.LayerMapTryGet(Layers.FirstLayer, out var firstLayer))
|
||||
{
|
||||
sprite.LayerSetVisible(firstLayer, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Layers : byte
|
||||
{
|
||||
FirstLayer,
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Content.Client/UserInterface/ButtonHelpers.cs
Normal file
31
Content.Client/UserInterface/ButtonHelpers.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface
|
||||
{
|
||||
public static class ButtonHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// This searches recursively through all the children of "parent"
|
||||
/// and sets the Disabled value of any buttons found to "val"
|
||||
/// </summary>
|
||||
/// <param name="parent">The control which childrens get searched</param>
|
||||
/// <param name="val">The value to which disabled gets set</param>
|
||||
public static void SetButtonDisabledRecursive(Control parent, bool val)
|
||||
{
|
||||
foreach (var child in parent.Children)
|
||||
{
|
||||
if (child is Button but)
|
||||
{
|
||||
but.Disabled = val;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.ChildCount > 0)
|
||||
{
|
||||
SetButtonDisabledRecursive(child, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ namespace Content.Client.Weapons.Melee
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly EffectSystem _effectSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -67,7 +68,6 @@ namespace Content.Client.Weapons.Melee
|
||||
source.TryGetComponent(out ISpriteComponent? sourceSprite) &&
|
||||
sourceSprite.BaseRSI?.Path != null)
|
||||
{
|
||||
var sys = Get<EffectSystem>();
|
||||
var curTime = _gameTiming.CurTime;
|
||||
var effect = new EffectSystemMessage
|
||||
{
|
||||
@@ -81,7 +81,8 @@ namespace Content.Client.Weapons.Melee
|
||||
Born = curTime,
|
||||
DeathTime = curTime.Add(TimeSpan.FromMilliseconds(300f)),
|
||||
};
|
||||
sys.CreateEffect(effect);
|
||||
|
||||
_effectSystem.CreateEffect(effect);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,21 +24,12 @@ namespace Content.Client.Weapons.Ranged
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||
[Dependency] private readonly CombatModeSystem _combatModeSystem = default!;
|
||||
|
||||
private InputSystem _inputSystem = default!;
|
||||
private CombatModeSystem _combatModeSystem = default!;
|
||||
private bool _blocked;
|
||||
private int _shotCounter;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
_inputSystem = Get<InputSystem>();
|
||||
_combatModeSystem = Get<CombatModeSystem>();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Body.Behavior;
|
||||
using Content.Server.Body.Circulatory;
|
||||
using Content.Server.Metabolism;
|
||||
using Content.Server.Body.Respiratory;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Body.Components;
|
||||
using NUnit.Framework;
|
||||
@@ -32,7 +32,7 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
template: HumanoidTemplate
|
||||
preset: HumanPreset
|
||||
centerSlot: torso
|
||||
- type: Metabolism
|
||||
- type: Respirator
|
||||
metabolismHeat: 5000
|
||||
radiatedHeat: 400
|
||||
implicitHeatRegulation: 5000
|
||||
@@ -148,7 +148,7 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
|
||||
MapId mapId;
|
||||
IMapGrid grid = null;
|
||||
MetabolismComponent metabolism = null;
|
||||
RespiratorComponent respirator = null;
|
||||
IEntity human = null;
|
||||
|
||||
var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml";
|
||||
@@ -169,8 +169,8 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
|
||||
Assert.True(human.TryGetComponent(out SharedBodyComponent body));
|
||||
Assert.True(body.HasMechanismBehavior<LungBehavior>());
|
||||
Assert.True(human.TryGetComponent(out metabolism));
|
||||
Assert.False(metabolism.Suffocating);
|
||||
Assert.True(human.TryGetComponent(out respirator));
|
||||
Assert.False(respirator.Suffocating);
|
||||
});
|
||||
|
||||
var increment = 10;
|
||||
@@ -178,7 +178,7 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
for (var tick = 0; tick < 600; tick += increment)
|
||||
{
|
||||
await server.WaitRunTicks(increment);
|
||||
Assert.False(metabolism.Suffocating, $"Entity {human.Name} is suffocating on tick {tick}");
|
||||
Assert.False(respirator.Suffocating, $"Entity {human.Name} is suffocating on tick {tick}");
|
||||
}
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Coordinates;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Chemistry
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ReactionPrototype))]
|
||||
public class TryAllReactionsTest : ContentIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TryAllTest()
|
||||
{
|
||||
var server = StartServerDummyTicker();
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||
|
||||
foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes<ReactionPrototype>())
|
||||
{
|
||||
//since i have no clue how to isolate each loop assert-wise im just gonna throw this one in for good measure
|
||||
Console.WriteLine($"Testing {reactionPrototype.ID}");
|
||||
|
||||
IEntity beaker;
|
||||
SolutionContainerComponent component = null;
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||
|
||||
beaker = entityManager.SpawnEntity("BluespaceBeaker", MapCoordinates.Nullspace);
|
||||
Assert.That(beaker.TryGetComponent(out component));
|
||||
foreach (var (id, reactant) in reactionPrototype.Reactants)
|
||||
{
|
||||
Assert.That(component.TryAddReagent(id, reactant.Amount, out var quantity));
|
||||
Assert.That(reactant.Amount, Is.EqualTo(quantity));
|
||||
}
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
//you just got linq'd fool
|
||||
//(i'm sorry)
|
||||
var foundProductsMap = reactionPrototype.Products
|
||||
.Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount))
|
||||
.ToDictionary(x => x, x => false);
|
||||
foreach (var reagent in component.Solution.Contents)
|
||||
{
|
||||
Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.ReagentId && x.Key.Value == reagent.Quantity, out var foundProduct));
|
||||
foundProductsMap[foundProduct.Value.Key] = true;
|
||||
}
|
||||
|
||||
Assert.That(foundProductsMap.All(x => x.Value));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Client.Clickable;
|
||||
using Content.Server.GameTicking;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
@@ -57,12 +60,19 @@ namespace Content.IntegrationTests.Tests
|
||||
[TestCase("ClickTestRotatingCornerInvisibleNoRot", 0.25f, 0.25f, DirSouthEastJustShy, 1, ExpectedResult = true)]
|
||||
public async Task<bool> Test(string prototype, float clickPosX, float clickPosY, double angle, float scale)
|
||||
{
|
||||
Vector2? worldPos = null;
|
||||
EntityUid entity = default;
|
||||
var clientEntManager = _client.ResolveDependency<IEntityManager>();
|
||||
var serverEntManager = _server.ResolveDependency<IEntityManager>();
|
||||
var mapManager = _server.ResolveDependency<IMapManager>();
|
||||
var gameTicker = _server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<GameTicker>();
|
||||
|
||||
await _server.WaitPost(() =>
|
||||
{
|
||||
var entMgr = IoCManager.Resolve<IEntityManager>();
|
||||
var ent = entMgr.SpawnEntity(prototype, new MapCoordinates(0, 0, new MapId(1)));
|
||||
var gridEnt = mapManager.GetGrid(gameTicker.DefaultGridId).GridEntityId;
|
||||
worldPos = serverEntManager.GetEntity(gridEnt).Transform.WorldPosition;
|
||||
|
||||
var ent = serverEntManager.SpawnEntity(prototype, new EntityCoordinates(gridEnt, 0f, 0f));
|
||||
ent.Transform.LocalRotation = angle;
|
||||
ent.GetComponent<SpriteComponent>().Scale = (scale, scale);
|
||||
entity = ent.Uid;
|
||||
@@ -75,17 +85,15 @@ namespace Content.IntegrationTests.Tests
|
||||
|
||||
await _client.WaitPost(() =>
|
||||
{
|
||||
var entMgr = IoCManager.Resolve<IEntityManager>();
|
||||
var ent = entMgr.GetEntity(entity);
|
||||
var ent = clientEntManager.GetEntity(entity);
|
||||
var clickable = ent.GetComponent<ClickableComponent>();
|
||||
|
||||
hit = clickable.CheckClick((clickPosX, clickPosY), out _, out _);
|
||||
hit = clickable.CheckClick((clickPosX, clickPosY) + worldPos!.Value, out _, out _);
|
||||
});
|
||||
|
||||
await _server.WaitPost(() =>
|
||||
{
|
||||
var entMgr = IoCManager.Resolve<IEntityManager>();
|
||||
entMgr.DeleteEntity(entity);
|
||||
serverEntManager.DeleteEntity(entity);
|
||||
});
|
||||
|
||||
return hit;
|
||||
|
||||
@@ -52,7 +52,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
||||
- type: PointLight
|
||||
enabled: false
|
||||
radius: 3
|
||||
- type: LoopingSound
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: FlashLightVisualizer
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Shared.Spawning;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
|
||||
@@ -44,7 +45,11 @@ namespace Content.IntegrationTests.Tests.Utility
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var mapId = new MapId(1);
|
||||
var grid = sMapManager.GetGrid(new GridId(1));
|
||||
grid.SetTile(new Vector2i(0, 0), new Tile(1));
|
||||
var gridEnt = sEntityManager.GetEntity(grid.GridEntityId);
|
||||
var gridPos = gridEnt.Transform.WorldPosition;
|
||||
var entityCoordinates = new EntityCoordinates(grid.GridEntityId, 0, 0);
|
||||
|
||||
// Nothing blocking it, only entity is the grid
|
||||
@@ -52,8 +57,7 @@ namespace Content.IntegrationTests.Tests.Utility
|
||||
Assert.True(sEntityManager.TrySpawnIfUnobstructed(null, entityCoordinates, CollisionGroup.Impassable, out var entity));
|
||||
Assert.NotNull(entity);
|
||||
|
||||
var mapId = new MapId(1);
|
||||
var mapCoordinates = new MapCoordinates(0, 0, mapId);
|
||||
var mapCoordinates = new MapCoordinates(gridPos.X, gridPos.Y, mapId);
|
||||
|
||||
// Nothing blocking it, only entity is the grid
|
||||
Assert.NotNull(sEntityManager.SpawnIfUnobstructed(null, mapCoordinates, CollisionGroup.Impassable));
|
||||
|
||||
@@ -37,8 +37,7 @@ namespace Content.Server.AI.Pathfinding.Accessible
|
||||
*/
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private PathfindingSystem _pathfindingSystem = default!;
|
||||
[Dependency] private readonly PathfindingSystem _pathfindingSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Queued region updates
|
||||
@@ -80,7 +79,6 @@ namespace Content.Server.AI.Pathfinding.Accessible
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_pathfindingSystem = Get<PathfindingSystem>();
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||
SubscribeLocalEvent<PathfindingChunkUpdateMessage>(RecalculateNodeRegions);
|
||||
#if DEBUG
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace Content.Server.AI.Steering
|
||||
// http://www.red3d.com/cwr/papers/1999/gdc99steer.html for a steering overview
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IPauseManager _pauseManager = default!;
|
||||
|
||||
private PathfindingSystem _pathfindingSystem = default!;
|
||||
[Dependency] private readonly PathfindingSystem _pathfindingSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we try to avoid non-blocking physics objects
|
||||
@@ -87,7 +86,6 @@ namespace Content.Server.AI.Steering
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_pathfindingSystem = Get<PathfindingSystem>();
|
||||
|
||||
for (var i = 0; i < AgentListCount; i++)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Content.Server.Atmos.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (grid.HasComponent<IGridAtmosphereComponent>())
|
||||
if (grid.HasComponent<IAtmosphereComponent>())
|
||||
{
|
||||
shell.WriteLine("Grid already has an atmosphere.");
|
||||
return;
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Content.Server.Atmos.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (grid.HasComponent<IGridAtmosphereComponent>())
|
||||
if (grid.HasComponent<IAtmosphereComponent>())
|
||||
{
|
||||
shell.WriteLine("Grid already has an atmosphere.");
|
||||
return;
|
||||
|
||||
@@ -23,23 +23,20 @@ namespace Content.Server.Atmos.Components
|
||||
[ViewVariables]
|
||||
[ComponentDependency] private readonly FlammableComponent? _flammableComponent = null;
|
||||
|
||||
public void Update(TileAtmosphere tile, float frameDelta, AtmosphereSystem atmosphereSystem)
|
||||
public void Update(GasMixture air, float frameDelta, AtmosphereSystem atmosphereSystem)
|
||||
{
|
||||
if (_temperatureComponent != null)
|
||||
{
|
||||
if (tile.Air != null)
|
||||
{
|
||||
var temperatureDelta = tile.Air.Temperature - _temperatureComponent.CurrentTemperature;
|
||||
var tileHeatCapacity = atmosphereSystem.GetHeatCapacity(tile.Air);
|
||||
var heat = temperatureDelta * (tileHeatCapacity * _temperatureComponent.HeatCapacity / (tileHeatCapacity + _temperatureComponent.HeatCapacity));
|
||||
_temperatureComponent.ReceiveHeat(heat);
|
||||
}
|
||||
var temperatureDelta = air.Temperature - _temperatureComponent.CurrentTemperature;
|
||||
var tileHeatCapacity = atmosphereSystem.GetHeatCapacity(air);
|
||||
var heat = temperatureDelta * (tileHeatCapacity * _temperatureComponent.HeatCapacity / (tileHeatCapacity + _temperatureComponent.HeatCapacity));
|
||||
_temperatureComponent.ReceiveHeat(heat);
|
||||
_temperatureComponent.Update();
|
||||
}
|
||||
|
||||
_barotraumaComponent?.Update(tile.Air?.Pressure ?? 0);
|
||||
_barotraumaComponent?.Update(air.Pressure);
|
||||
|
||||
_flammableComponent?.Update(tile);
|
||||
_flammableComponent?.Update(air);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Content.Server.Atmos.Components
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
public void Update(TileAtmosphere tile)
|
||||
public void Update(GasMixture air)
|
||||
{
|
||||
// Slowly dry ourselves off if wet.
|
||||
if (FireStacks < 0)
|
||||
@@ -104,13 +104,13 @@ namespace Content.Server.Atmos.Components
|
||||
}
|
||||
|
||||
// If we're in an oxygenless environment, put the fire out.
|
||||
if (tile.Air?.GetMoles(Gas.Oxygen) < 1f)
|
||||
if (air.GetMoles(Gas.Oxygen) < 1f)
|
||||
{
|
||||
Extinguish();
|
||||
return;
|
||||
}
|
||||
|
||||
EntitySystem.Get<AtmosphereSystem>().HotspotExpose(tile.GridIndex, tile.GridIndices, 700f, 50f, true);
|
||||
EntitySystem.Get<AtmosphereSystem>().HotspotExpose(Owner.Transform.Coordinates, 700f, 50f, true);
|
||||
|
||||
var physics = Owner.GetComponent<IPhysBody>();
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#nullable disable warnings
|
||||
using System;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Respiratory;
|
||||
|
||||
@@ -14,13 +14,14 @@ using Dependency = Robust.Shared.IoC.DependencyAttribute;
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// This is our SSAir equivalent.
|
||||
/// Internal Atmos class. Use <see cref="AtmosphereSystem"/> to interact with atmos instead.
|
||||
/// </summary>
|
||||
[ComponentReference(typeof(IGridAtmosphereComponent))]
|
||||
[ComponentReference(typeof(IAtmosphereComponent))]
|
||||
[RegisterComponent, Serializable]
|
||||
public class GridAtmosphereComponent : Component, IGridAtmosphereComponent, ISerializationHooks
|
||||
public class GridAtmosphereComponent : Component, IAtmosphereComponent, ISerializationHooks
|
||||
{
|
||||
public override string Name => "GridAtmosphere";
|
||||
|
||||
public virtual bool Simulated => true;
|
||||
|
||||
[ViewVariables]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
public interface IGridAtmosphereComponent : IComponent
|
||||
public interface IAtmosphereComponent : IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this atmosphere is simulated or not.
|
||||
13
Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs
Normal file
13
Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IAtmosphereComponent))]
|
||||
public class SpaceAtmosphereComponent : Component, IAtmosphereComponent
|
||||
{
|
||||
public override string Name => "SpaceAtmosphere";
|
||||
|
||||
public bool Simulated => false;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IGridAtmosphereComponent))]
|
||||
public class SpaceGridAtmosphereComponent : UnsimulatedGridAtmosphereComponent
|
||||
{
|
||||
public override string Name => "SpaceGridAtmosphere";
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,9 @@ using Robust.Shared.Maths;
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IGridAtmosphereComponent))]
|
||||
[ComponentReference(typeof(GridAtmosphereComponent))]
|
||||
[ComponentReference(typeof(IAtmosphereComponent))]
|
||||
[Serializable]
|
||||
public class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent, IGridAtmosphereComponent
|
||||
public class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent
|
||||
{
|
||||
public override string Name => "UnsimulatedGridAtmosphere";
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
public class AirtightSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
if (airtight.FixVacuum)
|
||||
{
|
||||
Get<AtmosphereSystem>().FixVacuum(airtight.LastPosition.Item1, airtight.LastPosition.Item2);
|
||||
_atmosphereSystem.FixVacuum(airtight.LastPosition.Item1, airtight.LastPosition.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,9 +94,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (!gridId.IsValid())
|
||||
return;
|
||||
|
||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
||||
atmosphereSystem.UpdateAdjacent(gridId, pos);
|
||||
atmosphereSystem.InvalidateTile(gridId, pos);
|
||||
_atmosphereSystem.UpdateAdjacent(gridId, pos);
|
||||
_atmosphereSystem.InvalidateTile(gridId, pos);
|
||||
}
|
||||
|
||||
private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle)
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Players allowed to see the atmos debug overlay.
|
||||
@@ -127,7 +128,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
AccumulatedFrameTime -= _updateCooldown;
|
||||
|
||||
var currentTick = _gameTiming.CurTick;
|
||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
||||
|
||||
// Now we'll go through each player, then through each chunk in range of that player checking if the player is still in range
|
||||
// If they are, check if they need the new data to send (i.e. if there's an overlay for the gas).
|
||||
@@ -157,7 +157,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
for (var x = 0; x < LocalViewRange; x++)
|
||||
{
|
||||
var Vector2i = new Vector2i(baseTile.X + x, baseTile.Y + y);
|
||||
debugOverlayContent[index++] = ConvertTileToData(atmosphereSystem.GetTileAtmosphereOrCreateSpace(grid, gam, Vector2i));
|
||||
debugOverlayContent[index++] = ConvertTileToData(_atmosphereSystem.GetTileAtmosphereOrCreateSpace(grid, gam, Vector2i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
public bool MonstermosRipTiles { get; private set; }
|
||||
public bool GridImpulse { get; private set; }
|
||||
public bool Superconduction { get; private set; }
|
||||
public bool ExcitedGroups { get; private set; }
|
||||
public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; }
|
||||
public float AtmosMaxProcessTime { get; private set; }
|
||||
public float AtmosTickRate { get; private set; }
|
||||
@@ -31,6 +32,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
_cfg.OnValueChanged(CCVars.Superconduction, value => Superconduction = value, true);
|
||||
_cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true);
|
||||
_cfg.OnValueChanged(CCVars.AtmosTickRate, value => AtmosTickRate = value, true);
|
||||
_cfg.OnValueChanged(CCVars.ExcitedGroups, value => ExcitedGroups = value, true);
|
||||
_cfg.OnValueChanged(CCVars.ExcitedGroupsSpaceIsAllConsuming, value => ExcitedGroupsSpaceIsAllConsuming = value, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,13 +42,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity);
|
||||
}
|
||||
|
||||
public float GetHeatCapacityArchived(GasMixture mixture)
|
||||
{
|
||||
Span<float> tmp = stackalloc float[mixture.Moles.Length];
|
||||
NumericsHelpers.Multiply(mixture.MolesArchived, GasSpecificHeats, tmp);
|
||||
return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity);
|
||||
}
|
||||
|
||||
public float GetThermalEnergy(GasMixture mixture)
|
||||
{
|
||||
return mixture.Temperature * GetHeatCapacity(mixture);
|
||||
@@ -79,7 +72,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
public float Share(GasMixture receiver, GasMixture sharer, int atmosAdjacentTurfs)
|
||||
{
|
||||
var temperatureDelta = receiver.TemperatureArchived - sharer.TemperatureArchived;
|
||||
var temperatureDelta = receiver.Temperature - sharer.Temperature;
|
||||
var absTemperatureDelta = Math.Abs(temperatureDelta);
|
||||
var oldHeatCapacity = 0f;
|
||||
var oldSharerHeatCapacity = 0f;
|
||||
@@ -130,12 +123,12 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
// Transfer of thermal energy (via changed heat capacity) between self and sharer.
|
||||
if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * receiver.TemperatureArchived) + (heatCapacitySharerToThis * sharer.TemperatureArchived)) / newHeatCapacity;
|
||||
receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * receiver.Temperature) + (heatCapacitySharerToThis * sharer.Temperature)) / newHeatCapacity;
|
||||
}
|
||||
|
||||
if (!sharer.Immutable && newSharerHeatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * sharer.TemperatureArchived) + (heatCapacityToSharer*receiver.TemperatureArchived)) / newSharerHeatCapacity;
|
||||
sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * sharer.Temperature) + (heatCapacityToSharer*receiver.Temperature)) / newSharerHeatCapacity;
|
||||
}
|
||||
|
||||
// Thermal energy of the system (self and sharer) is unchanged.
|
||||
@@ -154,17 +147,17 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var moles = receiver.TotalMoles;
|
||||
var theirMoles = sharer.TotalMoles;
|
||||
|
||||
return (receiver.TemperatureArchived * (moles + movedMoles)) - (sharer.TemperatureArchived * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume;
|
||||
return (receiver.Temperature * (moles + movedMoles)) - (sharer.Temperature * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume;
|
||||
|
||||
}
|
||||
|
||||
public float TemperatureShare(GasMixture receiver, GasMixture sharer, float conductionCoefficient)
|
||||
{
|
||||
var temperatureDelta = receiver.TemperatureArchived - sharer.TemperatureArchived;
|
||||
var temperatureDelta = receiver.Temperature - sharer.Temperature;
|
||||
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var heatCapacity = GetHeatCapacityArchived(receiver);
|
||||
var sharerHeatCapacity = GetHeatCapacityArchived(sharer);
|
||||
var heatCapacity = GetHeatCapacity(receiver);
|
||||
var sharerHeatCapacity = GetHeatCapacity(sharer);
|
||||
|
||||
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
@@ -183,10 +176,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
public float TemperatureShare(GasMixture receiver, float conductionCoefficient, float sharerTemperature, float sharerHeatCapacity)
|
||||
{
|
||||
var temperatureDelta = receiver.TemperatureArchived - sharerTemperature;
|
||||
var temperatureDelta = receiver.Temperature - sharerTemperature;
|
||||
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var heatCapacity = GetHeatCapacityArchived(receiver);
|
||||
var heatCapacity = GetHeatCapacity(receiver);
|
||||
|
||||
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
|
||||
@@ -23,13 +23,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
|
||||
private GasTileOverlaySystem _gasTileOverlaySystem = default!;
|
||||
[Dependency] private readonly GasTileOverlaySystem _gasTileOverlaySystem = default!;
|
||||
|
||||
private void InitializeGrid()
|
||||
{
|
||||
_gasTileOverlaySystem = Get<GasTileOverlaySystem>();
|
||||
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, ComponentInit>(OnGridAtmosphereInit);
|
||||
}
|
||||
|
||||
@@ -120,6 +117,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
/// <returns>All tile mixtures in a grid.</returns>
|
||||
public IEnumerable<GasMixture> GetAllTileMixtures(GridId grid, bool invalidate = false)
|
||||
{
|
||||
// Return an array with a single space gas mixture for invalid grids.
|
||||
if (!grid.IsValid())
|
||||
return new []{ GasMixture.SpaceGas };
|
||||
|
||||
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
|
||||
return Enumerable.Empty<GasMixture>();
|
||||
|
||||
@@ -669,7 +670,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
public GasMixture? GetTileMixture(EntityCoordinates coordinates, bool invalidate = false)
|
||||
{
|
||||
return TryGetGridAndTile(coordinates, out var tuple)
|
||||
? GetTileMixture(tuple.Value.Grid, tuple.Value.Tile, invalidate) : null;
|
||||
? GetTileMixture(tuple.Value.Grid, tuple.Value.Tile, invalidate) : GasMixture.SpaceGas;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -681,6 +682,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
/// <returns>The tile mixture, or null</returns>
|
||||
public GasMixture? GetTileMixture(GridId grid, Vector2i tile, bool invalidate = false)
|
||||
{
|
||||
// Always return space gas mixtures for invalid grids (grid 0)
|
||||
if (!grid.IsValid())
|
||||
return GasMixture.SpaceGas;
|
||||
|
||||
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
|
||||
return null;
|
||||
|
||||
@@ -689,7 +694,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
return GetTileMixture(gridAtmosphere, tile, invalidate);
|
||||
}
|
||||
|
||||
if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out SpaceGridAtmosphereComponent? spaceAtmosphere))
|
||||
if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out SpaceAtmosphereComponent? _))
|
||||
{
|
||||
// Always return a new space gas mixture in this case.
|
||||
return GasMixture.SpaceGas;
|
||||
@@ -970,6 +975,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
/// <returns>All adjacent tile gas mixtures to the tile in question</returns>
|
||||
public IEnumerable<GasMixture> GetAdjacentTileMixtures(GridId grid, Vector2i tile, bool includeBlocked = false, bool invalidate = false)
|
||||
{
|
||||
// For invalid grids, return an array with a single space gas mixture in it.
|
||||
if (!grid.IsValid())
|
||||
return new []{ GasMixture.SpaceGas };
|
||||
|
||||
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
|
||||
return Enumerable.Empty<GasMixture>();
|
||||
|
||||
@@ -1377,7 +1386,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
return false;
|
||||
|
||||
if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)
|
||||
&& gridAtmosphere.AtmosDevices.Contains(atmosDevice))
|
||||
&& gridAtmosphere.AtmosDevices.Contains(atmosDevice))
|
||||
{
|
||||
atmosDevice.JoinedGrid = null;
|
||||
gridAtmosphere.AtmosDevices.Remove(atmosDevice);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#nullable disable warnings
|
||||
#nullable enable annotations
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
@@ -140,7 +137,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
Merge(tile.Air, affected);
|
||||
}
|
||||
|
||||
var tileRef = tile.GridIndices.GetTileRef(tile.GridIndex);
|
||||
var tileRef = tile.GridIndices.GetTileRef(tile.GridIndex, _mapManager);
|
||||
|
||||
foreach (var entity in tileRef.GetEntitiesInTileFast())
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable warnings
|
||||
#nullable enable annotations
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
@@ -16,9 +14,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
return;
|
||||
}
|
||||
|
||||
if (tile.ArchivedCycle < fireCount)
|
||||
Archive(tile, fireCount);
|
||||
|
||||
tile.CurrentCycle = fireCount;
|
||||
var adjacentTileLength = 0;
|
||||
|
||||
@@ -38,11 +33,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
// If the tile is null or has no air, we don't do anything for it.
|
||||
if(enemyTile?.Air == null) continue;
|
||||
if (fireCount <= enemyTile.CurrentCycle) continue;
|
||||
Archive(enemyTile, fireCount);
|
||||
|
||||
var shouldShareAir = false;
|
||||
|
||||
if (tile.ExcitedGroup != null && enemyTile.ExcitedGroup != null)
|
||||
if (ExcitedGroups && tile.ExcitedGroup != null && enemyTile.ExcitedGroup != null)
|
||||
{
|
||||
if (tile.ExcitedGroup != enemyTile.ExcitedGroup)
|
||||
{
|
||||
@@ -57,21 +51,24 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
AddActiveTile(gridAtmosphere, enemyTile);
|
||||
}
|
||||
|
||||
var excitedGroup = tile.ExcitedGroup;
|
||||
excitedGroup ??= enemyTile.ExcitedGroup;
|
||||
|
||||
if (excitedGroup == null)
|
||||
if (ExcitedGroups)
|
||||
{
|
||||
excitedGroup = new ExcitedGroup();
|
||||
gridAtmosphere.ExcitedGroups.Add(excitedGroup);
|
||||
var excitedGroup = tile.ExcitedGroup;
|
||||
excitedGroup ??= enemyTile.ExcitedGroup;
|
||||
|
||||
if (excitedGroup == null)
|
||||
{
|
||||
excitedGroup = new ExcitedGroup();
|
||||
gridAtmosphere.ExcitedGroups.Add(excitedGroup);
|
||||
}
|
||||
|
||||
if (tile.ExcitedGroup == null)
|
||||
ExcitedGroupAddTile(excitedGroup, tile);
|
||||
|
||||
if(enemyTile.ExcitedGroup == null)
|
||||
ExcitedGroupAddTile(excitedGroup, enemyTile);
|
||||
}
|
||||
|
||||
if (tile.ExcitedGroup == null)
|
||||
ExcitedGroupAddTile(excitedGroup, tile);
|
||||
|
||||
if(enemyTile.ExcitedGroup == null)
|
||||
ExcitedGroupAddTile(excitedGroup, enemyTile);
|
||||
|
||||
shouldShareAir = true;
|
||||
}
|
||||
|
||||
@@ -106,17 +103,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (ConsiderSuperconductivity(gridAtmosphere, tile, true))
|
||||
remove = false;
|
||||
|
||||
if(tile.ExcitedGroup == null && remove)
|
||||
if(ExcitedGroups && tile.ExcitedGroup == null && remove)
|
||||
RemoveActiveTile(gridAtmosphere, tile);
|
||||
}
|
||||
|
||||
private void Archive(TileAtmosphere tile, int fireCount)
|
||||
{
|
||||
tile.Air?.Archive();
|
||||
tile.ArchivedCycle = fireCount;
|
||||
tile.TemperatureArchived = tile.Temperature;
|
||||
}
|
||||
|
||||
private void LastShareCheck(TileAtmosphere tile)
|
||||
{
|
||||
if (tile.Air == null || tile.ExcitedGroup == null)
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#nullable disable warnings
|
||||
#nullable enable annotations
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Server.Doors.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
@@ -20,7 +18,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
private readonly TileAtmosphereComparer _monstermosComparer = new();
|
||||
|
||||
private readonly TileAtmosphere[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
|
||||
private readonly TileAtmosphere?[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
|
||||
private readonly TileAtmosphere[] _equalizeGiverTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
|
||||
private readonly TileAtmosphere[] _equalizeTakerTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
|
||||
private readonly TileAtmosphere[] _equalizeQueue = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
|
||||
@@ -28,7 +26,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
private readonly TileAtmosphere[] _depressurizeSpaceTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
|
||||
private readonly TileAtmosphere[] _depressurizeProgressionOrder = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit * 2];
|
||||
|
||||
public void EqualizePressureInZone(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
|
||||
private void EqualizePressureInZone(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
|
||||
{
|
||||
if (tile.Air == null || (tile.MonstermosInfo.LastCycle >= cycleNum))
|
||||
return; // Already done.
|
||||
@@ -65,11 +63,12 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
if (i > Atmospherics.MonstermosHardTileLimit) break;
|
||||
var exploring = _equalizeTiles[i];
|
||||
var exploring = _equalizeTiles[i]!;
|
||||
|
||||
if (i < Atmospherics.MonstermosTileLimit)
|
||||
{
|
||||
var tileMoles = exploring.Air.TotalMoles;
|
||||
// Tiles in the _equalizeTiles array cannot have null air.
|
||||
var tileMoles = exploring.Air!.TotalMoles;
|
||||
exploring.MonstermosInfo.MoleDelta = tileMoles;
|
||||
totalMoles += tileMoles;
|
||||
}
|
||||
@@ -106,7 +105,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (otherTile == null)
|
||||
continue;
|
||||
|
||||
_equalizeTiles[i].MonstermosInfo.LastQueueCycle = 0;
|
||||
otherTile.MonstermosInfo.LastQueueCycle = 0;
|
||||
}
|
||||
|
||||
tileCount = Atmospherics.MonstermosTileLimit;
|
||||
@@ -118,7 +117,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = _equalizeTiles[i];
|
||||
var otherTile = _equalizeTiles[i]!;
|
||||
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
||||
otherTile.MonstermosInfo.MoleDelta -= averageMoles;
|
||||
if (otherTile.MonstermosInfo.MoleDelta > 0)
|
||||
@@ -133,7 +132,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
var logN = MathF.Log2(tileCount);
|
||||
|
||||
// Optimization - try to spread gases using an O(nlogn) algorithm that has a chance of not working first to avoid O(n^2)
|
||||
// Optimization - try to spread gases using an O(n log n) algorithm that has a chance of not working first to avoid O(n^2)
|
||||
if (giverTilesLength > logN && takerTilesLength > logN)
|
||||
{
|
||||
// Even if it fails, it will speed up the next part.
|
||||
@@ -141,7 +140,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = _equalizeTiles[i];
|
||||
var otherTile = _equalizeTiles[i]!;
|
||||
otherTile.MonstermosInfo.FastDone = true;
|
||||
if (!(otherTile.MonstermosInfo.MoleDelta > 0)) continue;
|
||||
var eligibleDirections = AtmosDirection.Invalid;
|
||||
@@ -150,7 +149,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
var tile2 = otherTile.AdjacentTiles[j];
|
||||
var tile2 = otherTile.AdjacentTiles[j]!;
|
||||
|
||||
// skip anything that isn't part of our current processing block.
|
||||
if (tile2.MonstermosInfo.FastDone || tile2.MonstermosInfo.LastQueueCycle != queueCycle)
|
||||
@@ -171,7 +170,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
AdjustEqMovement(otherTile, direction, molesToMove);
|
||||
otherTile.MonstermosInfo.MoleDelta -= molesToMove;
|
||||
otherTile.AdjacentTiles[j].MonstermosInfo.MoleDelta += molesToMove;
|
||||
otherTile.AdjacentTiles[j]!.MonstermosInfo.MoleDelta += molesToMove;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +179,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = _equalizeTiles[i];
|
||||
var otherTile = _equalizeTiles[i]!;
|
||||
if (otherTile.MonstermosInfo.MoleDelta > 0)
|
||||
{
|
||||
_equalizeGiverTiles[giverTilesLength++] = otherTile;
|
||||
@@ -252,7 +251,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (otherTile.MonstermosInfo.CurrentTransferAmount != 0 && otherTile.MonstermosInfo.CurrentTransferDirection != AtmosDirection.Invalid)
|
||||
{
|
||||
AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount);
|
||||
otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()]
|
||||
otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()]!
|
||||
.MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount;
|
||||
otherTile.MonstermosInfo.CurrentTransferAmount = 0;
|
||||
}
|
||||
@@ -319,7 +318,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount);
|
||||
|
||||
otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()]
|
||||
otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()]!
|
||||
.MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount;
|
||||
otherTile.MonstermosInfo.CurrentTransferAmount = 0;
|
||||
}
|
||||
@@ -328,19 +327,19 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = _equalizeTiles[i];
|
||||
var otherTile = _equalizeTiles[i]!;
|
||||
FinalizeEq(gridAtmosphere, otherTile);
|
||||
}
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = _equalizeTiles[i];
|
||||
var otherTile = _equalizeTiles[i]!;
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
var otherTile2 = otherTile.AdjacentTiles[j];
|
||||
if (otherTile2?.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue;
|
||||
var otherTile2 = otherTile.AdjacentTiles[j]!;
|
||||
if (otherTile2.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue;
|
||||
AddActiveTile(gridAtmosphere, otherTile2);
|
||||
break;
|
||||
}
|
||||
@@ -353,7 +352,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
Array.Clear(_equalizeQueue, 0, Atmospherics.MonstermosTileLimit);
|
||||
}
|
||||
|
||||
public void ExplosivelyDepressurize(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
|
||||
private void ExplosivelyDepressurize(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
|
||||
{
|
||||
// Check if explosive depressurization is enabled and if the tile is valid.
|
||||
if (!MonstermosDepressurization || tile.Air == null)
|
||||
@@ -376,7 +375,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var otherTile = _depressurizeTiles[i];
|
||||
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
||||
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
if (otherTile.Air.Immutable)
|
||||
// Tiles in the _depressurizeTiles array cannot have null air.
|
||||
if (otherTile.Air!.Immutable)
|
||||
{
|
||||
_depressurizeSpaceTiles[spaceTileCount++] = otherTile;
|
||||
otherTile.PressureSpecificTarget = otherTile;
|
||||
@@ -388,7 +388,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
var otherTile2 = otherTile.AdjacentTiles[j];
|
||||
if (otherTile2.Air == null) continue;
|
||||
if (otherTile2?.Air == null) continue;
|
||||
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue;
|
||||
|
||||
ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2);
|
||||
@@ -421,8 +421,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
// TODO ATMOS This is a terrible hack that accounts for the mess that are space TileAtmospheres.
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air.Immutable) continue;
|
||||
// Tiles in _depressurizeProgressionOrder cannot have null air.
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air!.Immutable) continue;
|
||||
var tile2 = otherTile.AdjacentTiles[j];
|
||||
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
||||
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
@@ -509,7 +509,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
InvalidateVisuals(other.GridIndex, other.GridIndices);
|
||||
}
|
||||
|
||||
public void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
private void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
Span<float> transferDirections = stackalloc float[Atmospherics.Directions];
|
||||
var hasTransferDirs = false;
|
||||
@@ -533,7 +533,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (otherTile?.Air == null) continue;
|
||||
if (amount > 0)
|
||||
{
|
||||
if (tile.Air.TotalMoles < amount)
|
||||
// Everything that calls this method already ensures that Air will not be null.
|
||||
if (tile.Air!.TotalMoles < amount)
|
||||
FinalizeEqNeighbors(gridAtmosphere, tile, transferDirections);
|
||||
|
||||
otherTile.MonstermosInfo[direction.GetOpposite()] = 0;
|
||||
@@ -551,15 +552,19 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
var amount = transferDirs[i];
|
||||
// Since AdjacentBits is set, AdjacentTiles[i] wouldn't be null, and neither would its air.
|
||||
if(amount < 0 && tile.AdjacentBits.IsFlagSet(direction))
|
||||
FinalizeEq(gridAtmosphere, tile.AdjacentTiles[i]); // A bit of recursion if needed.
|
||||
FinalizeEq(gridAtmosphere, tile.AdjacentTiles[i]!); // A bit of recursion if needed.
|
||||
}
|
||||
}
|
||||
|
||||
private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, float amount)
|
||||
{
|
||||
DebugTools.Assert(tile.AdjacentBits.HasFlag(direction));
|
||||
DebugTools.Assert(tile.AdjacentTiles[direction.ToIndex()] != null);
|
||||
tile.MonstermosInfo[direction] += amount;
|
||||
tile.AdjacentTiles[direction.ToIndex()].MonstermosInfo[direction.GetOpposite()] -= amount;
|
||||
// Every call to this method already ensures that the adjacent tile won't be null.
|
||||
tile.AdjacentTiles[direction.ToIndex()]!.MonstermosInfo[direction.GetOpposite()] -= amount;
|
||||
}
|
||||
|
||||
private void HandleDecompressionFloorRip(IMapGrid mapGrid, TileAtmosphere tile, float sum)
|
||||
@@ -573,9 +578,9 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
PryTile(mapGrid, tile.GridIndices);
|
||||
}
|
||||
|
||||
private class TileAtmosphereComparer : IComparer<TileAtmosphere>
|
||||
private class TileAtmosphereComparer : IComparer<TileAtmosphere?>
|
||||
{
|
||||
public int Compare(TileAtmosphere a, TileAtmosphere b)
|
||||
public int Compare(TileAtmosphere? a, TileAtmosphere? b)
|
||||
{
|
||||
if (a == null && b == null)
|
||||
return 0;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private readonly AtmosDeviceUpdateEvent _updateEvent = new();
|
||||
private readonly Stopwatch _simulationStopwatch = new();
|
||||
|
||||
/// <summary>
|
||||
@@ -204,11 +205,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
atmosphere.CurrentRunAtmosDevices = new Queue<AtmosDeviceComponent>(atmosphere.AtmosDevices);
|
||||
|
||||
var time = _gameTiming.CurTime;
|
||||
var updateEvent = new AtmosDeviceUpdateEvent();
|
||||
var number = 0;
|
||||
while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device))
|
||||
{
|
||||
EntityManager.EventBus.RaiseLocalEvent(device.Owner.Uid, updateEvent, false);
|
||||
RaiseLocalEvent(device.Owner.Uid, _updateEvent, false);
|
||||
device.LastProcess = time;
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
@@ -241,7 +241,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
var atmosphere = _currentRunAtmosphere[_currentRunAtmosphereIndex];
|
||||
|
||||
if (atmosphere.Paused || atmosphere.LifeStage >= ComponentLifeStage.Stopping)
|
||||
if (atmosphere.Paused || !atmosphere.Simulated || atmosphere.LifeStage >= ComponentLifeStage.Stopping)
|
||||
continue;
|
||||
|
||||
atmosphere.Timer += frameTime;
|
||||
@@ -281,7 +281,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
}
|
||||
|
||||
atmosphere.ProcessingPaused = false;
|
||||
atmosphere.State = AtmosphereProcessingState.ExcitedGroups;
|
||||
// Next state depends on whether excited groups are enabled or not.
|
||||
atmosphere.State = ExcitedGroups ? AtmosphereProcessingState.ExcitedGroups : AtmosphereProcessingState.HighPressureDelta;
|
||||
continue;
|
||||
case AtmosphereProcessingState.ExcitedGroups:
|
||||
if (!ProcessExcitedGroups(atmosphere))
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (adjacent == null || adjacent.ThermalConductivity == 0f)
|
||||
continue;
|
||||
|
||||
if(adjacent.ArchivedCycle < gridAtmosphere.UpdateCounter)
|
||||
Archive(adjacent, gridAtmosphere.UpdateCounter);
|
||||
|
||||
NeighborConductWithSource(gridAtmosphere, adjacent, tile);
|
||||
|
||||
ConsiderSuperconductivity(gridAtmosphere, adjacent);
|
||||
@@ -37,8 +34,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
if(tile.Air == null)
|
||||
{
|
||||
if(tile.ArchivedCycle < gridAtmosphere.UpdateCounter)
|
||||
Archive(tile, gridAtmosphere.UpdateCounter);
|
||||
return AtmosDirection.All;
|
||||
}
|
||||
|
||||
@@ -128,7 +123,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient)
|
||||
{
|
||||
var deltaTemperature = (tile.TemperatureArchived - other.TemperatureArchived);
|
||||
var deltaTemperature = (tile.Temperature - other.Temperature);
|
||||
if (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider
|
||||
&& tile.HeatCapacity != 0f && other.HeatCapacity != 0f)
|
||||
{
|
||||
@@ -146,7 +141,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (tile.Temperature > Atmospherics.T0C)
|
||||
{
|
||||
// Hardcoded space temperature.
|
||||
var deltaTemperature = (tile.TemperatureArchived - Atmospherics.TCMB);
|
||||
var deltaTemperature = (tile.Temperature - Atmospherics.TCMB);
|
||||
if ((tile.HeatCapacity > 0) && (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider))
|
||||
{
|
||||
var heat = tile.ThermalConductivity * deltaTemperature * (tile.HeatCapacity *
|
||||
|
||||
@@ -8,6 +8,9 @@ using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// This is our SSAir equivalent, if you need to interact with or query atmos in any way, go through this.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public partial class AtmosphereSystem : SharedAtmosphereSystem
|
||||
{
|
||||
@@ -29,7 +32,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
#region Events
|
||||
|
||||
// Map events.
|
||||
_mapManager.MapCreated += OnMapCreated;
|
||||
_mapManager.TileChanged += OnTileChanged;
|
||||
|
||||
#endregion
|
||||
@@ -39,7 +41,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_mapManager.MapCreated -= OnMapCreated;
|
||||
_mapManager.TileChanged -= OnTileChanged;
|
||||
}
|
||||
|
||||
@@ -57,17 +58,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
InvalidateTile(eventArgs.NewTile.GridIndex, eventArgs.NewTile.GridIndices);
|
||||
}
|
||||
|
||||
private void OnMapCreated(object? sender, MapEventArgs e)
|
||||
{
|
||||
if (e.Map == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var map = _mapManager.GetMapEntity(e.Map);
|
||||
|
||||
if (!map.HasComponent<IGridAtmosphereComponent>())
|
||||
map.AddComponent<SpaceGridAtmosphereComponent>();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -81,7 +71,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
foreach (var exposed in EntityManager.ComponentManager.EntityQuery<AtmosExposedComponent>())
|
||||
{
|
||||
// TODO ATMOS: Kill this with fire.
|
||||
var tile = GetTileAtmosphereOrCreateSpace(exposed.Owner.Transform.Coordinates);
|
||||
var tile = GetTileMixture(exposed.Owner.Transform.Coordinates);
|
||||
if (tile == null) continue;
|
||||
exposed.Update(tile, _exposedTimer, this);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasTankSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
private const float TimerDelay = 0.5f;
|
||||
private float _timer = 0f;
|
||||
|
||||
@@ -19,11 +22,9 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (_timer < TimerDelay) return;
|
||||
_timer -= TimerDelay;
|
||||
|
||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
||||
|
||||
foreach (var gasTank in EntityManager.ComponentManager.EntityQuery<GasTankComponent>())
|
||||
{
|
||||
atmosphereSystem.React(gasTank.Air, gasTank);
|
||||
_atmosphereSystem.React(gasTank.Air, gasTank);
|
||||
gasTank.CheckStatus();
|
||||
gasTank.UpdateUserInterface();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The tiles that have had their atmos data updated since last tick
|
||||
@@ -58,8 +59,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
/// </summary>
|
||||
private float _updateCooldown;
|
||||
|
||||
private AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
private int _thresholds;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -68,7 +67,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||
|
||||
_atmosphereSystem = Get<AtmosphereSystem>();
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
_mapManager.OnGridRemoved += OnGridRemoved;
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
|
||||
@@ -26,9 +26,6 @@ namespace Content.Server.Atmos
|
||||
[DataField("moles")] [ViewVariables]
|
||||
public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
|
||||
[DataField("molesArchived")] [ViewVariables]
|
||||
public float[] MolesArchived = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
|
||||
[DataField("temperature")] [ViewVariables]
|
||||
private float _temperature = Atmospherics.TCMB;
|
||||
|
||||
@@ -73,9 +70,6 @@ namespace Content.Server.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
[DataField("temperatureArchived")] [ViewVariables]
|
||||
public float TemperatureArchived { get; private set; }
|
||||
|
||||
[DataField("volume")] [ViewVariables]
|
||||
public float Volume { get; set; }
|
||||
|
||||
@@ -96,13 +90,6 @@ namespace Content.Server.Atmos
|
||||
Immutable = true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Archive()
|
||||
{
|
||||
Moles.AsSpan().CopyTo(MolesArchived.AsSpan());
|
||||
TemperatureArchived = Temperature;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public float GetMoles(int gasId)
|
||||
{
|
||||
@@ -253,7 +240,6 @@ namespace Content.Server.Atmos
|
||||
{
|
||||
// The arrays MUST have a specific length.
|
||||
Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases);
|
||||
Array.Resize(ref MolesArchived, Atmospherics.AdjustedNumberOfGases);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@@ -268,12 +254,10 @@ namespace Content.Server.Atmos
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Moles.SequenceEqual(other.Moles)
|
||||
&& MolesArchived.SequenceEqual(other.MolesArchived)
|
||||
&& _temperature.Equals(other._temperature)
|
||||
&& ReactionResults.SequenceEqual(other.ReactionResults)
|
||||
&& Immutable == other.Immutable
|
||||
&& LastShare.Equals(other.LastShare)
|
||||
&& TemperatureArchived.Equals(other.TemperatureArchived)
|
||||
&& Volume.Equals(other.Volume);
|
||||
}
|
||||
|
||||
@@ -284,13 +268,10 @@ namespace Content.Server.Atmos
|
||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
var moles = Moles[i];
|
||||
var molesArchived = MolesArchived[i];
|
||||
hashCode.Add(moles);
|
||||
hashCode.Add(molesArchived);
|
||||
}
|
||||
|
||||
hashCode.Add(_temperature);
|
||||
hashCode.Add(TemperatureArchived);
|
||||
hashCode.Add(Immutable);
|
||||
hashCode.Add(LastShare);
|
||||
hashCode.Add(Volume);
|
||||
@@ -303,11 +284,9 @@ namespace Content.Server.Atmos
|
||||
var newMixture = new GasMixture()
|
||||
{
|
||||
Moles = (float[])Moles.Clone(),
|
||||
MolesArchived = (float[])MolesArchived.Clone(),
|
||||
_temperature = _temperature,
|
||||
Immutable = Immutable,
|
||||
LastShare = LastShare,
|
||||
TemperatureArchived = TemperatureArchived,
|
||||
Volume = Volume,
|
||||
};
|
||||
return newMixture;
|
||||
|
||||
@@ -6,20 +6,5 @@ namespace Content.Server.Atmos
|
||||
public interface IGasMixtureHolder
|
||||
{
|
||||
public GasMixture Air { get; set; }
|
||||
|
||||
public virtual void AssumeAir(GasMixture giver)
|
||||
{
|
||||
EntitySystem.Get<AtmosphereSystem>().Merge(Air, giver);
|
||||
}
|
||||
|
||||
public GasMixture RemoveAir(float amount)
|
||||
{
|
||||
return Air.Remove(amount);
|
||||
}
|
||||
|
||||
public GasMixture RemoveAirVolume(float ratio)
|
||||
{
|
||||
return Air.RemoveRatio(ratio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,15 @@ using Content.Shared.Atmos.Visuals;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasDualPortVentPumpSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -34,24 +37,23 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
return;
|
||||
}
|
||||
|
||||
appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off);
|
||||
|
||||
if (!vent.Enabled)
|
||||
return;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
||||
return;
|
||||
|
||||
if (!nodeContainer.TryGetNode(vent.InletName, out PipeNode? inlet)
|
||||
if (!vent.Enabled
|
||||
|| !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||
|| !nodeContainer.TryGetNode(vent.InletName, out PipeNode? inlet)
|
||||
|| !nodeContainer.TryGetNode(vent.OutletName, out PipeNode? outlet))
|
||||
{
|
||||
appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off);
|
||||
return;
|
||||
}
|
||||
|
||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
||||
var environment = atmosphereSystem.GetTileMixture(vent.Owner.Transform.Coordinates, true);
|
||||
var environment = _atmosphereSystem.GetTileMixture(vent.Owner.Transform.Coordinates, true);
|
||||
|
||||
// We're in an air-blocked tile... Do nothing.
|
||||
if (environment == null)
|
||||
{
|
||||
appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vent.PumpDirection == VentPumpDirection.Releasing)
|
||||
{
|
||||
@@ -68,7 +70,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
{
|
||||
var transferMoles = pressureDelta * environment.Volume / inlet.Air.Temperature * Atmospherics.R;
|
||||
var removed = inlet.Air.Remove(transferMoles);
|
||||
atmosphereSystem.Merge(environment, removed);
|
||||
_atmosphereSystem.Merge(environment, removed);
|
||||
}
|
||||
}
|
||||
else if (vent.PumpDirection == VentPumpDirection.Siphoning && environment.Pressure > 0f)
|
||||
@@ -89,7 +91,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
{
|
||||
var removed = environment.Remove(molesDelta);
|
||||
|
||||
Get<AtmosphereSystem>().Merge(outlet.Air, removed);
|
||||
_atmosphereSystem.Merge(outlet.Air, removed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,22 +25,23 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
var appearance = pump.Owner.GetComponentOrNull<AppearanceComponent>();
|
||||
appearance?.SetData(PressurePumpVisuals.Enabled, false);
|
||||
|
||||
if (!pump.Enabled)
|
||||
return;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
||||
return;
|
||||
|
||||
if (!nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|
||||
if (!pump.Enabled
|
||||
|| !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||
|| !nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|
||||
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
|
||||
{
|
||||
appearance?.SetData(PressurePumpVisuals.Enabled, false);
|
||||
return;
|
||||
}
|
||||
|
||||
var outputStartingPressure = outlet.Air.Pressure;
|
||||
|
||||
if (MathHelper.CloseTo(pump.TargetPressure, outputStartingPressure))
|
||||
{
|
||||
appearance?.SetData(PressurePumpVisuals.Enabled, false);
|
||||
return; // No need to pump gas if target has been reached.
|
||||
}
|
||||
|
||||
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
[UsedImplicitly]
|
||||
public class GasVolumePumpSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -56,13 +57,12 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
// Some of the gas from the mixture leaks when overclocked.
|
||||
if (pump.Overclocked)
|
||||
{
|
||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
||||
var tile = atmosphereSystem.GetTileMixture(pump.Owner.Transform.Coordinates, true);
|
||||
var tile = _atmosphereSystem.GetTileMixture(pump.Owner.Transform.Coordinates, true);
|
||||
|
||||
if (tile != null)
|
||||
{
|
||||
var leaked = removed.RemoveRatio(pump.LeakRatio);
|
||||
atmosphereSystem.Merge(tile, leaked);
|
||||
_atmosphereSystem.Merge(tile, leaked);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Content.Server.Atmos.Piping.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds itself to a <see cref="IGridAtmosphereComponent"/> to be updated by.
|
||||
/// Adds itself to a <see cref="IAtmosphereComponent"/> to be updated by.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class AtmosDeviceComponent : Component
|
||||
@@ -22,6 +22,19 @@ namespace Content.Server.Atmos.Piping.Components
|
||||
[DataField("requireAnchored")]
|
||||
public bool RequireAnchored { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this device will join an entity system to process when not in a grid.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("joinSystem")]
|
||||
public bool JoinSystem { get; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we have joined an entity system to process.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool JoinedSystem { get; set; } = false;
|
||||
|
||||
[ViewVariables]
|
||||
public TimeSpan LastProcess { get; set; } = TimeSpan.Zero;
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
@@ -12,7 +12,13 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
[UsedImplicitly]
|
||||
public class AtmosDeviceSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
private readonly AtmosDeviceUpdateEvent _updateEvent = new();
|
||||
|
||||
private float _timer = 0f;
|
||||
private readonly HashSet<AtmosDeviceComponent> _joinedDevices = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -32,11 +38,24 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
public void JoinAtmosphere(AtmosDeviceComponent component)
|
||||
{
|
||||
if (!CanJoinAtmosphere(component))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We try to add the device to a valid atmosphere, and if we can't, try to add it to the entity system.
|
||||
if (!_atmosphereSystem.AddAtmosDevice(component))
|
||||
{
|
||||
if (component.JoinSystem)
|
||||
{
|
||||
_joinedDevices.Add(component);
|
||||
component.JoinedSystem = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We try to add the device to a valid atmosphere.
|
||||
if (!Get<AtmosphereSystem>().AddAtmosDevice(component))
|
||||
return;
|
||||
|
||||
component.LastProcess = _gameTiming.CurTime;
|
||||
|
||||
@@ -45,8 +64,19 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
|
||||
public void LeaveAtmosphere(AtmosDeviceComponent component)
|
||||
{
|
||||
if (!Get<AtmosphereSystem>().RemoveAtmosDevice(component))
|
||||
// Try to remove the component from an atmosphere, and if not
|
||||
if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component))
|
||||
{
|
||||
// The grid might have been removed but not us... This usually shouldn't happen.
|
||||
component.JoinedGrid = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.JoinedSystem)
|
||||
{
|
||||
_joinedDevices.Remove(component);
|
||||
component.JoinedSystem = false;
|
||||
}
|
||||
|
||||
component.LastProcess = TimeSpan.Zero;
|
||||
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceDisabledEvent(), false);
|
||||
@@ -84,5 +114,22 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
{
|
||||
RejoinAtmosphere(component);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
_timer += frameTime;
|
||||
|
||||
if (_timer < _atmosphereSystem.AtmosTime)
|
||||
return;
|
||||
|
||||
_timer -= _atmosphereSystem.AtmosTime;
|
||||
|
||||
var time = _gameTiming.CurTime;
|
||||
foreach (var device in _joinedDevices)
|
||||
{
|
||||
RaiseLocalEvent(device.Owner.Uid, _updateEvent, false);
|
||||
device.LastProcess = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Shared.Atmos;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
@@ -15,6 +16,8 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
[UsedImplicitly]
|
||||
public class AtmosUnsafeUnanchorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<AtmosUnsafeUnanchorComponent, BeforeUnanchoredEvent>(OnBeforeUnanchored);
|
||||
@@ -26,7 +29,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
if (!component.Enabled || !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
|
||||
return;
|
||||
|
||||
if (Get<AtmosphereSystem>().GetTileMixture(component.Owner.Transform.Coordinates) is not {} environment)
|
||||
if (_atmosphereSystem.GetTileMixture(component.Owner.Transform.Coordinates) is not {} environment)
|
||||
return;
|
||||
|
||||
foreach (var node in nodes.Nodes.Values)
|
||||
@@ -47,9 +50,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
if (!component.Enabled || !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
|
||||
return;
|
||||
|
||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
||||
|
||||
if (atmosphereSystem.GetTileMixture(component.Owner.Transform.Coordinates, true) is not {} environment)
|
||||
if (_atmosphereSystem.GetTileMixture(component.Owner.Transform.Coordinates, true) is not {} environment)
|
||||
environment = GasMixture.SpaceGas;
|
||||
|
||||
var lost = 0f;
|
||||
@@ -71,10 +72,10 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
{
|
||||
if (node is not PipeNode pipe) continue;
|
||||
|
||||
atmosphereSystem.Merge(buffer, pipe.Air.Remove(sharedLoss));
|
||||
_atmosphereSystem.Merge(buffer, pipe.Air.Remove(sharedLoss));
|
||||
}
|
||||
|
||||
atmosphereSystem.Merge(environment, buffer);
|
||||
_atmosphereSystem.Merge(environment, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@ using Content.Server.Atmos.Piping.Other.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasMinerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -21,9 +24,7 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
||||
|
||||
private void OnMinerUpdated(EntityUid uid, GasMinerComponent miner, AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
||||
|
||||
if (!CheckMinerOperation(atmosphereSystem, miner, out var environment) || !miner.Enabled || !miner.SpawnGas.HasValue || miner.SpawnAmount <= 0f)
|
||||
if (!CheckMinerOperation(miner, out var environment) || !miner.Enabled || !miner.SpawnGas.HasValue || miner.SpawnAmount <= 0f)
|
||||
return;
|
||||
|
||||
// Time to mine some gas.
|
||||
@@ -31,15 +32,15 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
||||
var merger = new GasMixture(1) { Temperature = miner.SpawnTemperature };
|
||||
merger.SetMoles(miner.SpawnGas.Value, miner.SpawnAmount);
|
||||
|
||||
atmosphereSystem.Merge(environment, merger);
|
||||
_atmosphereSystem.Merge(environment, merger);
|
||||
}
|
||||
|
||||
private bool CheckMinerOperation(AtmosphereSystem atmosphereSystem, GasMinerComponent miner, [NotNullWhen(true)] out GasMixture? environment)
|
||||
private bool CheckMinerOperation(GasMinerComponent miner, [NotNullWhen(true)] out GasMixture? environment)
|
||||
{
|
||||
environment = atmosphereSystem.GetTileMixture(miner.Owner.Transform.Coordinates, true);
|
||||
environment = _atmosphereSystem.GetTileMixture(miner.Owner.Transform.Coordinates, true);
|
||||
|
||||
// Space.
|
||||
if (atmosphereSystem.IsTileSpace(miner.Owner.Transform.Coordinates))
|
||||
if (_atmosphereSystem.IsTileSpace(miner.Owner.Transform.Coordinates))
|
||||
{
|
||||
miner.Broken = true;
|
||||
return false;
|
||||
|
||||
@@ -3,7 +3,9 @@ using Content.Server.Atmos.Piping.Trinary.Components;
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Server.NodeContainer.Nodes;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -24,33 +26,35 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
|
||||
|
||||
private void OnFilterUpdated(EntityUid uid, GasFilterComponent filter, AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
if (!filter.Enabled)
|
||||
return;
|
||||
var appearance = filter.Owner.GetComponentOrNull<AppearanceComponent>();
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
||||
if (!filter.Enabled
|
||||
|| !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||
|| !ComponentManager.TryGetComponent(uid, out AtmosDeviceComponent? device)
|
||||
|| !nodeContainer.TryGetNode(filter.InletName, out PipeNode? inletNode)
|
||||
|| !nodeContainer.TryGetNode(filter.FilterName, out PipeNode? filterNode)
|
||||
|| !nodeContainer.TryGetNode(filter.OutletName, out PipeNode? outletNode)
|
||||
|| outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure) // No need to transfer if target is full.
|
||||
{
|
||||
appearance?.SetData(FilterVisuals.Enabled, false);
|
||||
return;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out AtmosDeviceComponent? device))
|
||||
return;
|
||||
|
||||
if (!nodeContainer.TryGetNode(filter.InletName, out PipeNode? inletNode)
|
||||
|| !nodeContainer.TryGetNode(filter.FilterName, out PipeNode? filterNode)
|
||||
|| !nodeContainer.TryGetNode(filter.OutletName, out PipeNode? outletNode))
|
||||
return;
|
||||
|
||||
if (outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure)
|
||||
return; // No need to transfer if target is full.
|
||||
}
|
||||
|
||||
// We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters.
|
||||
var transferRatio = (float)(filter.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds) / inletNode.Air.Volume;
|
||||
|
||||
if (transferRatio <= 0)
|
||||
{
|
||||
appearance?.SetData(FilterVisuals.Enabled, false);
|
||||
return;
|
||||
}
|
||||
|
||||
var removed = inletNode.Air.RemoveRatio(transferRatio);
|
||||
|
||||
if (filter.FilteredGas.HasValue)
|
||||
{
|
||||
appearance?.SetData(FilterVisuals.Enabled, true);
|
||||
|
||||
var filteredOut = new GasMixture() {Temperature = removed.Temperature};
|
||||
|
||||
filteredOut.SetMoles(filter.FilteredGas.Value, removed.GetMoles(filter.FilteredGas.Value));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user