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();
|
base.Open();
|
||||||
|
|
||||||
_window = new AMEWindow();
|
_window = new AMEWindow(this);
|
||||||
_window.OnClose += Close;
|
_window.OnClose += Close;
|
||||||
_window.OpenCentered();
|
_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>
|
/// <summary>
|
||||||
@@ -44,7 +39,7 @@ namespace Content.Client.AME.UI
|
|||||||
_window?.UpdateState(castState); //Update window state
|
_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));
|
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.Assignments;
|
||||||
using Content.Client.Actions.UI;
|
using Content.Client.Actions.UI;
|
||||||
using Content.Client.Hands;
|
using Content.Client.Hands;
|
||||||
using Content.Client.Inventory;
|
using Content.Client.Inventory;
|
||||||
using Content.Client.Items.UI;
|
using Content.Client.Items.Managers;
|
||||||
using Content.Shared.Actions.Components;
|
using Content.Shared.Actions.Components;
|
||||||
using Content.Shared.Actions.Prototypes;
|
using Content.Shared.Actions.Prototypes;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
@@ -26,12 +25,13 @@ namespace Content.Client.Actions
|
|||||||
public const byte Slots = 10;
|
public const byte Slots = 10;
|
||||||
|
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
|
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||||
|
|
||||||
[ComponentDependency] private readonly HandsComponent? _handsComponent = null;
|
[ComponentDependency] private readonly HandsComponent? _handsComponent = null;
|
||||||
[ComponentDependency] private readonly ClientInventoryComponent? _inventoryComponent = null;
|
[ComponentDependency] private readonly ClientInventoryComponent? _inventoryComponent = null;
|
||||||
|
|
||||||
private ActionsUI? _ui;
|
private ActionsUI? _ui;
|
||||||
private readonly List<ItemSlotButton> _highlightingItemSlots = new();
|
private EntityUid _highlightedEntity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current assignments for all hotbars / slots for this entity.
|
/// Current assignments for all hotbars / slots for this entity.
|
||||||
@@ -225,26 +225,8 @@ namespace Content.Client.Actions
|
|||||||
{
|
{
|
||||||
StopHighlightingItemSlots();
|
StopHighlightingItemSlots();
|
||||||
|
|
||||||
// figure out if it's in hand or inventory and highlight it
|
_highlightedEntity = item.Uid;
|
||||||
foreach (var hand in _handsComponent!.Gui!.Hands)
|
_itemSlotManager.HighlightEntity(item.Uid);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -252,11 +234,11 @@ namespace Content.Client.Actions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void StopHighlightingItemSlots()
|
public void StopHighlightingItemSlots()
|
||||||
{
|
{
|
||||||
foreach (var itemSlot in _highlightingItemSlots)
|
if (_highlightedEntity == default)
|
||||||
{
|
return;
|
||||||
itemSlot.Highlight(false);
|
|
||||||
}
|
_itemSlotManager.UnHighlightEntity(_highlightedEntity);
|
||||||
_highlightingItemSlots.Clear();
|
_highlightedEntity = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleActionsMenu()
|
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.Managers;
|
||||||
using Content.Client.Chat.UI;
|
using Content.Client.Chat.UI;
|
||||||
using Content.Client.Stylesheets;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Client.Alerts.UI
|
namespace Content.Client.Alerts.UI
|
||||||
@@ -10,13 +11,20 @@ namespace Content.Client.Alerts.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The status effects display on the right side of the screen.
|
/// The status effects display on the right side of the screen.
|
||||||
/// </summary>
|
/// </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 const float ChatSeparation = 38f;
|
||||||
public GridContainer Grid { get; }
|
|
||||||
|
public GridContainer Grid => AlertContainer;
|
||||||
|
|
||||||
public AlertsUI()
|
public AlertsUI()
|
||||||
{
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
|
LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
|
||||||
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.End);
|
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.End);
|
||||||
LayoutContainer.SetAnchorTop(this, 0f);
|
LayoutContainer.SetAnchorTop(this, 0f);
|
||||||
@@ -25,28 +33,11 @@ namespace Content.Client.Alerts.UI
|
|||||||
LayoutContainer.SetMarginBottom(this, -180);
|
LayoutContainer.SetMarginBottom(this, -180);
|
||||||
LayoutContainer.SetMarginTop(this, 250);
|
LayoutContainer.SetMarginTop(this, 250);
|
||||||
LayoutContainer.SetMarginRight(this, -10);
|
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()
|
protected override void EnteredTree()
|
||||||
{
|
{
|
||||||
base.EnteredTree();
|
base.EnteredTree();
|
||||||
var _chatManager = IoCManager.Resolve<IChatManager>();
|
|
||||||
_chatManager.OnChatBoxResized += OnChatResized;
|
_chatManager.OnChatBoxResized += OnChatResized;
|
||||||
OnChatResized(new ChatResizedEventArgs(HudChatBox.InitialChatBottom));
|
OnChatResized(new ChatResizedEventArgs(HudChatBox.InitialChatBottom));
|
||||||
}
|
}
|
||||||
@@ -54,15 +45,12 @@ namespace Content.Client.Alerts.UI
|
|||||||
protected override void ExitedTree()
|
protected override void ExitedTree()
|
||||||
{
|
{
|
||||||
base.ExitedTree();
|
base.ExitedTree();
|
||||||
var _chatManager = IoCManager.Resolve<IChatManager>();
|
|
||||||
_chatManager.OnChatBoxResized -= OnChatResized;
|
_chatManager.OnChatBoxResized -= OnChatResized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void OnChatResized(ChatResizedEventArgs chatResizedEventArgs)
|
private void OnChatResized(ChatResizedEventArgs chatResizedEventArgs)
|
||||||
{
|
{
|
||||||
// resize us to fit just below the chatbox
|
// resize us to fit just below the chatbox
|
||||||
var _chatManager = IoCManager.Resolve<IChatManager>();
|
|
||||||
if (_chatManager.CurrentChatBox != null)
|
if (_chatManager.CurrentChatBox != null)
|
||||||
{
|
{
|
||||||
LayoutContainer.SetMarginTop(this, chatResizedEventArgs.NewBottom + ChatSeparation);
|
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
|
// 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
|
// on constraints, otherwise we would use anchors to lay it out
|
||||||
base.Resized();
|
base.Resized();
|
||||||
Grid.MaxGridHeight = Height;
|
AlertContainer.MaxGridHeight = Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UIScaleChanged()
|
protected override void UIScaleChanged()
|
||||||
{
|
{
|
||||||
Grid.MaxGridHeight = Height;
|
AlertContainer.MaxGridHeight = Height;
|
||||||
base.UIScaleChanged();
|
base.UIScaleChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@ namespace Content.Client.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
|
||||||
// Gas overlays
|
// Gas overlays
|
||||||
private readonly float[] _timer = new float[Atmospherics.TotalNumberOfGases];
|
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 =
|
private readonly Dictionary<GridId, Dictionary<Vector2i, GasOverlayChunk>> _tileData =
|
||||||
new();
|
new();
|
||||||
|
|
||||||
private AtmosphereSystem _atmosphereSystem = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeNetworkEvent<GasOverlayMessage>(HandleGasOverlayMessage);
|
SubscribeNetworkEvent<GasOverlayMessage>(HandleGasOverlayMessage);
|
||||||
_mapManager.OnGridRemoved += OnGridRemoved;
|
_mapManager.OnGridRemoved += OnGridRemoved;
|
||||||
|
|
||||||
_atmosphereSystem = Get<AtmosphereSystem>();
|
|
||||||
|
|
||||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||||
{
|
{
|
||||||
var overlay = _atmosphereSystem.GetOverlay(i);
|
var overlay = _atmosphereSystem.GetOverlay(i);
|
||||||
|
|||||||
@@ -9,22 +9,13 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public abstract class EnabledAtmosDeviceVisualizer : AppearanceVisualizer
|
public abstract class EnabledAtmosDeviceVisualizer : AppearanceVisualizer
|
||||||
{
|
{
|
||||||
|
[DataField("disabledState")]
|
||||||
|
private string _disabledState = string.Empty;
|
||||||
[DataField("enabledState")]
|
[DataField("enabledState")]
|
||||||
private string _enabledState = string.Empty;
|
private string _enabledState = string.Empty;
|
||||||
protected abstract object LayerMap { get; }
|
protected abstract object LayerMap { get; }
|
||||||
protected abstract Enum DataKey { 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)
|
public override void OnChangeData(AppearanceComponent component)
|
||||||
{
|
{
|
||||||
base.OnChangeData(component);
|
base.OnChangeData(component);
|
||||||
@@ -32,8 +23,8 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(component.TryGetData(DataKey, out bool enabled))
|
if(component.TryGetData(DataKey, out bool enabled) && sprite.LayerMapTryGet(LayerMap, out var layer))
|
||||||
sprite.LayerSetVisible(LayerMap, enabled);
|
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,
|
ConnectedToPort,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
protected override object LayerMap => Layers.Enabled;
|
protected override object LayerMap => Layers.Enabled;
|
||||||
protected override Enum DataKey => OutletInjectorVisuals.Enabled;
|
protected override Enum DataKey => OutletInjectorVisuals.Enabled;
|
||||||
|
|
||||||
enum Layers
|
enum Layers : byte
|
||||||
{
|
{
|
||||||
Enabled,
|
Enabled,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
protected override object LayerMap => Layers.Enabled;
|
protected override object LayerMap => Layers.Enabled;
|
||||||
protected override Enum DataKey => PassiveVentVisuals.Enabled;
|
protected override Enum DataKey => PassiveVentVisuals.Enabled;
|
||||||
|
|
||||||
enum Layers
|
enum Layers : byte
|
||||||
{
|
{
|
||||||
Enabled,
|
Enabled,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Piping;
|
using Content.Shared.Atmos.Piping;
|
||||||
|
using Content.Shared.SubFloor;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
@@ -69,13 +70,16 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
if (!component.TryGetData(PipeVisuals.VisualState, out PipeVisualState state))
|
if (!component.TryGetData(PipeVisuals.VisualState, out PipeVisualState state))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(!component.TryGetData(SubFloorVisuals.SubFloor, out bool subfloor))
|
||||||
|
subfloor = true;
|
||||||
|
|
||||||
foreach (Layer layerKey in Enum.GetValues(typeof(Layer)))
|
foreach (Layer layerKey in Enum.GetValues(typeof(Layer)))
|
||||||
{
|
{
|
||||||
var dir = (PipeDirection) layerKey;
|
var dir = (PipeDirection) layerKey;
|
||||||
var layerVisible = state.ConnectedDirections.HasDirection(dir);
|
var layerVisible = state.ConnectedDirections.HasDirection(dir);
|
||||||
|
|
||||||
var layer = sprite.LayerMapGet(layerKey);
|
var layer = sprite.LayerMapGet(layerKey);
|
||||||
sprite.LayerSetVisible(layer, layerVisible);
|
sprite.LayerSetVisible(layer, layerVisible && subfloor);
|
||||||
sprite.LayerSetColor(layer, color);
|
sprite.LayerSetColor(layer, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
protected override object LayerMap => Layers.Enabled;
|
protected override object LayerMap => Layers.Enabled;
|
||||||
protected override Enum DataKey => PressurePumpVisuals.Enabled;
|
protected override Enum DataKey => PressurePumpVisuals.Enabled;
|
||||||
|
|
||||||
enum Layers
|
enum Layers : byte
|
||||||
{
|
{
|
||||||
Enabled,
|
Enabled,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ScrubberVisualLayers
|
public enum ScrubberVisualLayers : byte
|
||||||
{
|
{
|
||||||
Scrubber,
|
Scrubber,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
protected override object LayerMap => Layers.Enabled;
|
protected override object LayerMap => Layers.Enabled;
|
||||||
protected override Enum DataKey => ThermoMachineVisuals.Enabled;
|
protected override Enum DataKey => ThermoMachineVisuals.Enabled;
|
||||||
|
|
||||||
enum Layers
|
enum Layers : byte
|
||||||
{
|
{
|
||||||
Enabled,
|
Enabled,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace Content.Client.Atmos.Visualizers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum VentVisualLayers
|
public enum VentVisualLayers : byte
|
||||||
{
|
{
|
||||||
Vent,
|
Vent,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ namespace Content.Client.Audio
|
|||||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||||
[Dependency] private readonly IBaseClient _client = default!;
|
[Dependency] private readonly IBaseClient _client = default!;
|
||||||
|
[Dependency] private readonly ClientGameTicker _gameTicker = default!;
|
||||||
|
|
||||||
private SoundCollectionPrototype _ambientCollection = default!;
|
private SoundCollectionPrototype _ambientCollection = default!;
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ namespace Content.Client.Audio
|
|||||||
_client.PlayerJoinedServer += OnJoin;
|
_client.PlayerJoinedServer += OnJoin;
|
||||||
_client.PlayerLeaveServer += OnLeave;
|
_client.PlayerLeaveServer += OnLeave;
|
||||||
|
|
||||||
Get<ClientGameTicker>().LobbyStatusUpdated += LobbySongReceived;
|
_gameTicker.LobbyStatusUpdated += LobbySongReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
@@ -60,7 +61,7 @@ namespace Content.Client.Audio
|
|||||||
_client.PlayerJoinedServer -= OnJoin;
|
_client.PlayerJoinedServer -= OnJoin;
|
||||||
_client.PlayerLeaveServer -= OnLeave;
|
_client.PlayerLeaveServer -= OnLeave;
|
||||||
|
|
||||||
Get<ClientGameTicker>().LobbyStatusUpdated -= LobbySongReceived;
|
_gameTicker.LobbyStatusUpdated -= LobbySongReceived;
|
||||||
|
|
||||||
EndAmbience();
|
EndAmbience();
|
||||||
EndLobbyMusic();
|
EndLobbyMusic();
|
||||||
@@ -165,7 +166,7 @@ namespace Content.Client.Audio
|
|||||||
private void StartLobbyMusic()
|
private void StartLobbyMusic()
|
||||||
{
|
{
|
||||||
EndLobbyMusic();
|
EndLobbyMusic();
|
||||||
var file = Get<ClientGameTicker>().LobbySong;
|
var file = _gameTicker.LobbySong;
|
||||||
if (file == null) // We have not received the lobby song yet.
|
if (file == null) // We have not received the lobby song yet.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Client.UserInterface;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
@@ -279,34 +280,11 @@ namespace Content.Client.Chemistry.UI
|
|||||||
UpdatePanelInfo(castState);
|
UpdatePanelInfo(castState);
|
||||||
if (Contents.Children != null)
|
if (Contents.Children != null)
|
||||||
{
|
{
|
||||||
SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||||
EjectButton.Disabled = !castState.HasBeaker;
|
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>
|
/// <summary>
|
||||||
/// Update the container, buffer, and packaging panels.
|
/// Update the container, buffer, and packaging panels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Client.UserInterface;
|
||||||
using Content.Shared.Chemistry.Dispenser;
|
using Content.Shared.Chemistry.Dispenser;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Robust.Client.Graphics;
|
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>
|
/// <summary>
|
||||||
/// Update the UI state when new state data is received from the server.
|
/// Update the UI state when new state data is received from the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -209,7 +187,7 @@ namespace Content.Client.Chemistry.UI
|
|||||||
// Disable all buttons if not powered
|
// Disable all buttons if not powered
|
||||||
if (Contents.Children != null)
|
if (Contents.Children != null)
|
||||||
{
|
{
|
||||||
SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||||
EjectButton.Disabled = false;
|
EjectButton.Disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Shared.Clothing;
|
using Content.Shared.Clothing;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.Cloning
|
namespace Content.Client.Clothing
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class MagbootsComponent : SharedMagbootsComponent
|
public sealed class MagbootsComponent : SharedMagbootsComponent
|
||||||
@@ -17,15 +17,6 @@ namespace Content.Client.Doors
|
|||||||
{
|
{
|
||||||
private const string AnimationKey = "airlock_animation";
|
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")]
|
[DataField("animation_time")]
|
||||||
private float _delay = 0.8f;
|
private float _delay = 0.8f;
|
||||||
|
|
||||||
@@ -51,14 +42,6 @@ namespace Content.Client.Doors
|
|||||||
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel);
|
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel);
|
||||||
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
|
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
|
||||||
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f));
|
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)};
|
OpenAnimation = new Animation {Length = TimeSpan.FromSeconds(_delay)};
|
||||||
@@ -80,11 +63,6 @@ namespace Content.Client.Doors
|
|||||||
|
|
||||||
var sound = new AnimationTrackPlaySound();
|
var sound = new AnimationTrackPlaySound();
|
||||||
OpenAnimation.AnimationTracks.Add(sound);
|
OpenAnimation.AnimationTracks.Add(sound);
|
||||||
|
|
||||||
if (_openSound != null)
|
|
||||||
{
|
|
||||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_openSound, 0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(0.3f)};
|
DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(0.3f)};
|
||||||
@@ -96,11 +74,6 @@ namespace Content.Client.Doors
|
|||||||
|
|
||||||
var sound = new AnimationTrackPlaySound();
|
var sound = new AnimationTrackPlaySound();
|
||||||
DenyAnimation.AnimationTracks.Add(sound);
|
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 IEyeManager _eyeManager = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = 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
|
// how often to recheck possible targets (prevents calling expensive
|
||||||
// check logic each update)
|
// check logic each update)
|
||||||
@@ -69,8 +71,6 @@ namespace Content.Client.DragDrop
|
|||||||
|
|
||||||
private ShaderInstance? _dropTargetInRangeShader;
|
private ShaderInstance? _dropTargetInRangeShader;
|
||||||
private ShaderInstance? _dropTargetOutOfRangeShader;
|
private ShaderInstance? _dropTargetOutOfRangeShader;
|
||||||
private SharedInteractionSystem _interactionSystem = default!;
|
|
||||||
private InputSystem _inputSystem = default!;
|
|
||||||
|
|
||||||
private readonly List<ISpriteComponent> _highlightedSprites = new();
|
private readonly List<ISpriteComponent> _highlightedSprites = new();
|
||||||
|
|
||||||
@@ -80,8 +80,6 @@ namespace Content.Client.DragDrop
|
|||||||
|
|
||||||
_dropTargetInRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetInRange).Instance();
|
_dropTargetInRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetInRange).Instance();
|
||||||
_dropTargetOutOfRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetOutOfRange).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
|
// needs to fire on mouseup and mousedown so we can detect a drag / drop
|
||||||
CommandBinds.Builder
|
CommandBinds.Builder
|
||||||
.Bind(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false))
|
.Bind(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false))
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ namespace Content.Client.Entry
|
|||||||
factory.RegisterClass<SharedCargoConsoleComponent>();
|
factory.RegisterClass<SharedCargoConsoleComponent>();
|
||||||
factory.RegisterClass<SharedReagentDispenserComponent>();
|
factory.RegisterClass<SharedReagentDispenserComponent>();
|
||||||
factory.RegisterClass<SharedChemMasterComponent>();
|
factory.RegisterClass<SharedChemMasterComponent>();
|
||||||
factory.RegisterClass<SharedMicrowaveComponent>();
|
|
||||||
factory.RegisterClass<SharedGravityGeneratorComponent>();
|
factory.RegisterClass<SharedGravityGeneratorComponent>();
|
||||||
factory.RegisterClass<SharedAMEControllerComponent>();
|
factory.RegisterClass<SharedAMEControllerComponent>();
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ namespace Content.Client.Entry
|
|||||||
"ExaminableBattery",
|
"ExaminableBattery",
|
||||||
"PottedPlantHide",
|
"PottedPlantHide",
|
||||||
"SecureEntityStorage",
|
"SecureEntityStorage",
|
||||||
|
"Lock",
|
||||||
"PresetIdCard",
|
"PresetIdCard",
|
||||||
"SolarControlConsole",
|
"SolarControlConsole",
|
||||||
"FlashOnTrigger",
|
"FlashOnTrigger",
|
||||||
@@ -153,7 +154,8 @@ namespace Content.Client.Entry
|
|||||||
"GasPassiveGate",
|
"GasPassiveGate",
|
||||||
"GasValve",
|
"GasValve",
|
||||||
"GasThermoMachine",
|
"GasThermoMachine",
|
||||||
"Metabolism",
|
"Respirator",
|
||||||
|
"Metabolizer",
|
||||||
"AiFactionTag",
|
"AiFactionTag",
|
||||||
"PressureProtection",
|
"PressureProtection",
|
||||||
"AMEPart",
|
"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 System.Collections.Generic;
|
||||||
using Content.Client.Animations;
|
|
||||||
using Content.Client.HUD;
|
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
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
|
namespace Content.Client.Hands
|
||||||
{
|
{
|
||||||
@@ -18,16 +11,7 @@ namespace Content.Client.Hands
|
|||||||
[ComponentReference(typeof(SharedHandsComponent))]
|
[ComponentReference(typeof(SharedHandsComponent))]
|
||||||
public class HandsComponent : SharedHandsComponent
|
public class HandsComponent : SharedHandsComponent
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
public HandsGui? Gui { get; set; }
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public HandsGui? Gui { get; private set; }
|
|
||||||
|
|
||||||
protected override void OnRemove()
|
|
||||||
{
|
|
||||||
ClearGui();
|
|
||||||
base.OnRemove();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||||
{
|
{
|
||||||
@@ -38,94 +22,23 @@ namespace Content.Client.Hands
|
|||||||
|
|
||||||
foreach (var handState in state.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);
|
Hands.Add(newHand);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveHand = state.ActiveHand;
|
ActiveHand = state.ActiveHand;
|
||||||
|
|
||||||
UpdateHandContainers();
|
UpdateHandContainers();
|
||||||
UpdateHandVisualizer();
|
UpdateHandVisualizer();
|
||||||
UpdateHandsGuiState();
|
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandsModifiedMessage { Hands = this });
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandsModified()
|
public override void HandsModified()
|
||||||
{
|
{
|
||||||
base.HandsModified();
|
|
||||||
|
|
||||||
UpdateHandContainers();
|
UpdateHandContainers();
|
||||||
UpdateHandVisualizer();
|
UpdateHandVisualizer();
|
||||||
UpdateHandsGuiState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHandClick(string handClicked)
|
base.HandsModified();
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateHandContainers()
|
public void UpdateHandContainers()
|
||||||
@@ -149,27 +62,10 @@ namespace Content.Client.Hands
|
|||||||
appearance.SetData(HandsVisuals.VisualState, GetHandsVisualState());
|
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()
|
private HandsVisualState GetHandsVisualState()
|
||||||
{
|
{
|
||||||
var hands = new List<HandVisualState>();
|
var hands = new List<HandVisualState>();
|
||||||
foreach (var hand in ReadOnlyHands)
|
foreach (var hand in Hands)
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity == null)
|
if (hand.HeldEntity == null)
|
||||||
continue;
|
continue;
|
||||||
@@ -182,16 +78,5 @@ namespace Content.Client.Hands
|
|||||||
}
|
}
|
||||||
return new(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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
|
||||||
using Content.Client.HUD;
|
using Content.Client.HUD;
|
||||||
using Content.Client.Items.Managers;
|
using Content.Client.Items.Managers;
|
||||||
using Content.Client.Items.UI;
|
using Content.Client.Items.UI;
|
||||||
using Content.Client.Resources;
|
using Content.Client.Resources;
|
||||||
using Content.Shared;
|
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Input;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Player;
|
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
|
||||||
|
|
||||||
namespace Content.Client.Hands
|
namespace Content.Client.Hands
|
||||||
{
|
{
|
||||||
public class HandsGui : Control
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class HandsGui : Control
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||||
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
||||||
|
|
||||||
|
private readonly HandsSystem _handsSystem;
|
||||||
|
private readonly HandsComponent _handsComponent;
|
||||||
|
|
||||||
private Texture StorageTexture => _gameHud.GetHudTexture("back.png");
|
private Texture StorageTexture => _gameHud.GetHudTexture("back.png");
|
||||||
private Texture BlockedTexture => _resourceCache.GetTexture("/Textures/Interface/Inventory/blocked.png");
|
private Texture BlockedTexture => _resourceCache.GetTexture("/Textures/Interface/Inventory/blocked.png");
|
||||||
|
|
||||||
private ItemStatusPanel StatusPanel { get; }
|
private ItemStatusPanel StatusPanel { get; }
|
||||||
|
|
||||||
private BoxContainer HandsContainer { get; }
|
[ViewVariables] private GuiHand[] _hands = Array.Empty<GuiHand>();
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public IReadOnlyList<GuiHand> Hands => _hands;
|
|
||||||
private List<GuiHand> _hands = new();
|
|
||||||
|
|
||||||
private string? ActiveHand { get; set; }
|
private string? ActiveHand { get; set; }
|
||||||
|
|
||||||
public Action<HandClickEventArgs>? HandClick; //TODO: Move to Eventbus
|
public HandsGui(HandsComponent hands, HandsSystem handsSystem)
|
||||||
|
|
||||||
public Action<HandActivateEventArgs>? HandActivate; //TODO: Move to Eventbus
|
|
||||||
|
|
||||||
public HandsGui()
|
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
_handsComponent = hands;
|
||||||
_configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
_handsSystem = handsSystem;
|
||||||
|
|
||||||
AddChild(new BoxContainer
|
RobustXamlLoader.Load(this);
|
||||||
{
|
IoCManager.InjectDependencies(this);
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
SeparationOverride = 0,
|
StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle);
|
||||||
HorizontalAlignment = HAlignment.Center,
|
StatusContainer.AddChild(StatusPanel);
|
||||||
Children =
|
StatusPanel.SetPositionFirst();
|
||||||
{
|
|
||||||
new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Vertical,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
(StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle)),
|
|
||||||
(HandsContainer = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
HorizontalAlignment = HAlignment.Center
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
ActiveHand = state.ActiveHand;
|
||||||
_hands = state.GuiHands;
|
_hands = state.GuiHands;
|
||||||
|
Array.Sort(_hands, HandOrderComparer.Instance);
|
||||||
UpdateGui();
|
UpdateGui();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,12 +94,15 @@ namespace Content.Client.Hands
|
|||||||
|
|
||||||
var handName = hand.Name;
|
var handName = hand.Name;
|
||||||
newButton.OnPressed += args => OnHandPressed(args, handName);
|
newButton.OnPressed += args => OnHandPressed(args, handName);
|
||||||
newButton.OnStoragePressed += args => OnStoragePressed(handName);
|
newButton.OnStoragePressed += _ => OnStoragePressed(handName);
|
||||||
|
|
||||||
newButton.Blocked.Visible = !hand.Enabled;
|
|
||||||
|
|
||||||
_itemSlotManager.SetItemSlot(newButton, hand.HeldItem);
|
_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))
|
if (TryGetActiveHand(out var activeHand))
|
||||||
{
|
{
|
||||||
activeHand.HandButton.SetActiveHand(true);
|
activeHand.HandButton.SetActiveHand(true);
|
||||||
@@ -114,7 +114,7 @@ namespace Content.Client.Hands
|
|||||||
{
|
{
|
||||||
if (args.Function == EngineKeyFunctions.UIClick)
|
if (args.Function == EngineKeyFunctions.UIClick)
|
||||||
{
|
{
|
||||||
HandClick?.Invoke(new HandClickEventArgs(handName));
|
_handsSystem.UIHandClick(_handsComponent, handName);
|
||||||
}
|
}
|
||||||
else if (TryGetHand(handName, out var hand))
|
else if (TryGetHand(handName, out var hand))
|
||||||
{
|
{
|
||||||
@@ -124,7 +124,7 @@ namespace Content.Client.Hands
|
|||||||
|
|
||||||
private void OnStoragePressed(string handName)
|
private void OnStoragePressed(string handName)
|
||||||
{
|
{
|
||||||
HandActivate?.Invoke(new HandActivateEventArgs(handName));
|
_handsSystem.UIHandActivate(handName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetActiveHand([NotNullWhen(true)] out GuiHand? activeHand)
|
private bool TryGetActiveHand([NotNullWhen(true)] out GuiHand? activeHand)
|
||||||
@@ -145,6 +145,7 @@ namespace Content.Client.Hands
|
|||||||
if (hand.Name == handName)
|
if (hand.Name == handName)
|
||||||
foundHand = hand;
|
foundHand = hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundHand != null;
|
return foundHand != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +154,9 @@ namespace Content.Client.Hands
|
|||||||
base.FrameUpdate(args);
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
foreach (var hand in _hands)
|
foreach (var hand in _hands)
|
||||||
|
{
|
||||||
_itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem);
|
_itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HandButton MakeHandButton(HandLocation buttonLocation)
|
private HandButton MakeHandButton(HandLocation buttonLocation)
|
||||||
@@ -173,23 +176,31 @@ namespace Content.Client.Hands
|
|||||||
UpdateGui();
|
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
|
var orderX = Map(x.HandLocation);
|
||||||
{
|
var orderY = Map(y.HandLocation);
|
||||||
public string HandUsed { get; }
|
|
||||||
|
|
||||||
public HandActivateEventArgs(string handUsed)
|
return orderX.CompareTo(orderY);
|
||||||
{
|
|
||||||
HandUsed = handUsed;
|
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.
|
/// The set of hands to be displayed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public List<GuiHand> GuiHands { get; } = new();
|
public GuiHand[] GuiHands { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the currently active hand.
|
/// The name of the currently active hand.
|
||||||
@@ -211,7 +222,7 @@ namespace Content.Client.Hands
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public string? ActiveHand { get; }
|
public string? ActiveHand { get; }
|
||||||
|
|
||||||
public HandsGuiState(List<GuiHand> guiHands, string? activeHand = null)
|
public HandsGuiState(GuiHand[] guiHands, string? activeHand = null)
|
||||||
{
|
{
|
||||||
GuiHands = guiHands;
|
GuiHands = guiHands;
|
||||||
ActiveHand = activeHand;
|
ActiveHand = activeHand;
|
||||||
@@ -247,18 +258,11 @@ namespace Content.Client.Hands
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public HandButton HandButton { get; set; } = default!;
|
public HandButton HandButton { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
public GuiHand(string name, HandLocation handLocation, IEntity? heldItem)
|
||||||
/// 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)
|
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
HandLocation = handLocation;
|
HandLocation = handLocation;
|
||||||
HeldItem = heldItem;
|
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.Client.UserInterface;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
@@ -10,5 +11,21 @@ namespace Content.Client.Items.Managers
|
|||||||
void UpdateCooldown(ItemSlotButton? cooldownTexture, IEntity? entity);
|
void UpdateCooldown(ItemSlotButton? cooldownTexture, IEntity? entity);
|
||||||
bool SetItemSlot(ItemSlotButton button, IEntity? entity);
|
bool SetItemSlot(ItemSlotButton button, IEntity? entity);
|
||||||
void HoverInSlot(ItemSlotButton button, IEntity? entity, bool fits);
|
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.Examine;
|
||||||
using Content.Client.Items.UI;
|
using Content.Client.Items.UI;
|
||||||
using Content.Client.Storage;
|
using Content.Client.Storage;
|
||||||
using Content.Client.Verbs;
|
using Content.Client.Verbs;
|
||||||
using Content.Shared.Cooldown;
|
using Content.Shared.Cooldown;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
@@ -28,6 +31,11 @@ namespace Content.Client.Items.Managers
|
|||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = 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)
|
public bool SetItemSlot(ItemSlotButton button, IEntity? entity)
|
||||||
{
|
{
|
||||||
@@ -38,13 +46,26 @@ namespace Content.Client.Items.Managers
|
|||||||
}
|
}
|
||||||
else
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
button.ClearHover();
|
button.ClearHover();
|
||||||
button.SpriteView.Sprite = sprite;
|
button.SpriteView.Sprite = sprite;
|
||||||
button.StorageButton.Visible = entity.HasComponent<ClientStorageComponent>();
|
button.StorageButton.Visible = entity.HasComponent<ClientStorageComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.Entity = entity?.Uid ?? default;
|
||||||
|
|
||||||
|
// im lazy
|
||||||
|
button.UpdateSlotHighlighted();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,5 +166,38 @@ namespace Content.Client.Items.Managers
|
|||||||
|
|
||||||
button.HoverSpriteView.Sprite = hoverSprite;
|
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 System;
|
||||||
using Content.Client.Cooldown;
|
using Content.Client.Cooldown;
|
||||||
|
using Content.Client.Items.Managers;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
namespace Content.Client.Items.UI
|
namespace Content.Client.Items.UI
|
||||||
{
|
{
|
||||||
public class ItemSlotButton : Control
|
public class ItemSlotButton : Control, IEntityEventSubscriber
|
||||||
{
|
{
|
||||||
private const string HighlightShader = "SelectionOutlineInrange";
|
private const string HighlightShader = "SelectionOutlineInrange";
|
||||||
|
|
||||||
|
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||||
|
|
||||||
|
public EntityUid Entity { get; set; }
|
||||||
public TextureRect Button { get; }
|
public TextureRect Button { get; }
|
||||||
public SpriteView SpriteView { get; }
|
public SpriteView SpriteView { get; }
|
||||||
public SpriteView HoverSpriteView { get; }
|
public SpriteView HoverSpriteView { get; }
|
||||||
@@ -32,6 +38,8 @@ namespace Content.Client.Items.UI
|
|||||||
|
|
||||||
public ItemSlotButton(Texture texture, Texture storageTexture, string textureName)
|
public ItemSlotButton(Texture texture, Texture storageTexture, string textureName)
|
||||||
{
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
MinSize = (64, 64);
|
MinSize = (64, 64);
|
||||||
|
|
||||||
TextureName = textureName;
|
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()
|
public void ClearHover()
|
||||||
{
|
{
|
||||||
if (EntityHover)
|
if (EntityHover)
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ using Robust.Client.Graphics;
|
|||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using static Content.Client.IoC.StaticIoC;
|
using static Content.Client.IoC.StaticIoC;
|
||||||
@@ -18,6 +20,8 @@ namespace Content.Client.Items.UI
|
|||||||
{
|
{
|
||||||
public class ItemStatusPanel : Control
|
public class ItemStatusPanel : Control
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private readonly List<(IItemStatus, Control)> _activeStatusComponents = new();
|
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)
|
public ItemStatusPanel(Texture texture, StyleBox.Margin cutout, StyleBox.Margin flat, Label.AlignMode textAlign)
|
||||||
{
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
var panel = new StyleBoxTexture
|
var panel = new StyleBoxTexture
|
||||||
{
|
{
|
||||||
Texture = texture
|
Texture = texture
|
||||||
@@ -117,6 +123,13 @@ namespace Content.Client.Items.UI
|
|||||||
return new ItemStatusPanel(ResC.GetTexture(texture), cutOut, flat, textAlign);
|
return new ItemStatusPanel(ResC.GetTexture(texture), cutOut, flat, textAlign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
UpdateItemName();
|
||||||
|
}
|
||||||
|
|
||||||
public void Update(IEntity? entity)
|
public void Update(IEntity? entity)
|
||||||
{
|
{
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
@@ -131,12 +144,29 @@ namespace Content.Client.Items.UI
|
|||||||
{
|
{
|
||||||
_entity = entity;
|
_entity = entity;
|
||||||
BuildNewEntityStatus();
|
BuildNewEntityStatus();
|
||||||
_itemNameLabel.Text = entity.Name;
|
|
||||||
|
UpdateItemName();
|
||||||
}
|
}
|
||||||
|
|
||||||
_panel.Visible = true;
|
_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()
|
private void ClearOldStatus()
|
||||||
{
|
{
|
||||||
_statusContents.RemoveAllChildren();
|
_statusContents.RemoveAllChildren();
|
||||||
@@ -162,6 +192,14 @@ namespace Content.Client.Items.UI
|
|||||||
|
|
||||||
_activeStatusComponents.Add((statusComponent, control));
|
_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 Content.Shared.Kitchen.Components;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.Kitchen
|
namespace Content.Client.Kitchen.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
internal sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
|
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 Content.Shared.Kitchen.Components;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.Shared.Chemistry.Solution.Solution;
|
using static Content.Shared.Chemistry.Solution.Solution;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
|
||||||
|
|
||||||
namespace Content.Client.Kitchen.UI
|
namespace Content.Client.Kitchen.UI
|
||||||
{
|
{
|
||||||
@@ -21,8 +14,6 @@ namespace Content.Client.Kitchen.UI
|
|||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
private GrinderMenu? _menu;
|
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) { }
|
public ReagentGrinderBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) { }
|
||||||
|
|
||||||
@@ -30,22 +21,9 @@ namespace Content.Client.Kitchen.UI
|
|||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
_menu = new GrinderMenu(this);
|
_menu = new GrinderMenu(this, _entityManager, _prototypeManager);
|
||||||
_menu.OpenCentered();
|
_menu.OpenCentered();
|
||||||
_menu.OnClose += Close;
|
_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)
|
protected override void Dispose(bool disposing)
|
||||||
@@ -56,8 +34,6 @@ namespace Content.Client.Kitchen.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_chamberVisualContents?.Clear();
|
|
||||||
_beakerVisualContents?.Clear();
|
|
||||||
_menu?.Dispose();
|
_menu?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,243 +45,19 @@ namespace Content.Client.Kitchen.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_menu == null)
|
_menu?.UpdateState(cState);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||||
{
|
{
|
||||||
base.ReceiveMessage(message);
|
base.ReceiveMessage(message);
|
||||||
|
_menu?.HandleMessage(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshContentsDisplay(IList<ReagentQuantity>? reagents, IReadOnlyList<EntityUid> containedSolids, bool isBeakerAttached)
|
public void StartGrinding(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage());
|
||||||
{
|
public void StartJuicing(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage());
|
||||||
//Refresh chamber contents
|
public void EjectAll(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage());
|
||||||
_chamberVisualContents.Clear();
|
public void EjectBeaker(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectBeakerMessage());
|
||||||
|
public void EjectChamberContent(EntityUid uid) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage(uid));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.Kitchen.Components;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Sound;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Log;
|
using Robust.Shared.Log;
|
||||||
|
|
||||||
namespace Content.Client.Kitchen.Visualizers
|
namespace Content.Client.Kitchen.Visualizers
|
||||||
@@ -17,35 +17,33 @@ namespace Content.Client.Kitchen.Visualizers
|
|||||||
base.OnChangeData(component);
|
base.OnChangeData(component);
|
||||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
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))
|
if (!component.TryGetData(PowerDeviceVisuals.VisualState, out MicrowaveVisualState state))
|
||||||
{
|
{
|
||||||
state = MicrowaveVisualState.Idle;
|
state = MicrowaveVisualState.Idle;
|
||||||
}
|
}
|
||||||
|
// The only reason we get the entity system so late is so that tests don't fail... Amazing, huh?
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case MicrowaveVisualState.Broken:
|
case MicrowaveVisualState.Broken:
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mwb");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mwb");
|
||||||
loopingSoundComponent?.StopAllSounds();
|
if(microwaveComponent != null)
|
||||||
|
EntitySystem.Get<MicrowaveSystem>().StopSoundLoop(microwaveComponent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MicrowaveVisualState.Idle:
|
case MicrowaveVisualState.Idle:
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_unlit");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_unlit");
|
||||||
loopingSoundComponent?.StopAllSounds();
|
if(microwaveComponent != null)
|
||||||
|
EntitySystem.Get<MicrowaveSystem>().StopSoundLoop(microwaveComponent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MicrowaveVisualState.Cooking:
|
case MicrowaveVisualState.Cooking:
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_running_unlit");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_running_unlit");
|
||||||
var audioParams = AudioParams.Default;
|
if(microwaveComponent != null)
|
||||||
audioParams.Loop = true;
|
EntitySystem.Get<MicrowaveSystem>().StartSoundLoop(microwaveComponent);
|
||||||
var scheduledSound = new ScheduledSound();
|
|
||||||
scheduledSound.Filename = "/Audio/Machines/microwave_loop.ogg";
|
|
||||||
scheduledSound.AudioParams = audioParams;
|
|
||||||
loopingSoundComponent?.StopAllSounds();
|
|
||||||
loopingSoundComponent?.AddScheduledSound(scheduledSound);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Light.Component;
|
using Content.Shared.Light.Component;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.Light.Components
|
namespace Content.Client.Light.Components
|
||||||
@@ -9,6 +10,6 @@ namespace Content.Client.Light.Components
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class ExpendableLightComponent : SharedExpendableLightComponent
|
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 Content.Shared.Light.Component;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
namespace Content.Client.Light.Visualizers
|
namespace Content.Client.Light.Visualizers
|
||||||
{
|
{
|
||||||
@@ -17,7 +20,7 @@ namespace Content.Client.Light.Visualizers
|
|||||||
return;
|
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))
|
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 IMoverComponent? mover) ||
|
||||||
!player.TryGetComponent(out PhysicsComponent? body)) return;
|
!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
|
// Server-side should just be handled on its own so we'll just do this shizznit
|
||||||
if (player.TryGetComponent(out IMobMoverComponent? mobMover))
|
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 JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.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 IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly EffectSystem _effectSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -67,7 +68,6 @@ namespace Content.Client.Weapons.Melee
|
|||||||
source.TryGetComponent(out ISpriteComponent? sourceSprite) &&
|
source.TryGetComponent(out ISpriteComponent? sourceSprite) &&
|
||||||
sourceSprite.BaseRSI?.Path != null)
|
sourceSprite.BaseRSI?.Path != null)
|
||||||
{
|
{
|
||||||
var sys = Get<EffectSystem>();
|
|
||||||
var curTime = _gameTiming.CurTime;
|
var curTime = _gameTiming.CurTime;
|
||||||
var effect = new EffectSystemMessage
|
var effect = new EffectSystemMessage
|
||||||
{
|
{
|
||||||
@@ -81,7 +81,8 @@ namespace Content.Client.Weapons.Melee
|
|||||||
Born = curTime,
|
Born = curTime,
|
||||||
DeathTime = curTime.Add(TimeSpan.FromMilliseconds(300f)),
|
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 IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = 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 bool _blocked;
|
||||||
private int _shotCounter;
|
private int _shotCounter;
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
IoCManager.InjectDependencies(this);
|
|
||||||
_inputSystem = Get<InputSystem>();
|
|
||||||
_combatModeSystem = Get<CombatModeSystem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using Content.Server.Atmos;
|
using Content.Server.Atmos;
|
||||||
using Content.Server.Body.Behavior;
|
using Content.Server.Body.Behavior;
|
||||||
using Content.Server.Body.Circulatory;
|
using Content.Server.Body.Circulatory;
|
||||||
using Content.Server.Metabolism;
|
using Content.Server.Body.Respiratory;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@@ -32,7 +32,7 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
template: HumanoidTemplate
|
template: HumanoidTemplate
|
||||||
preset: HumanPreset
|
preset: HumanPreset
|
||||||
centerSlot: torso
|
centerSlot: torso
|
||||||
- type: Metabolism
|
- type: Respirator
|
||||||
metabolismHeat: 5000
|
metabolismHeat: 5000
|
||||||
radiatedHeat: 400
|
radiatedHeat: 400
|
||||||
implicitHeatRegulation: 5000
|
implicitHeatRegulation: 5000
|
||||||
@@ -148,7 +148,7 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
|
|
||||||
MapId mapId;
|
MapId mapId;
|
||||||
IMapGrid grid = null;
|
IMapGrid grid = null;
|
||||||
MetabolismComponent metabolism = null;
|
RespiratorComponent respirator = null;
|
||||||
IEntity human = null;
|
IEntity human = null;
|
||||||
|
|
||||||
var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml";
|
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(human.TryGetComponent(out SharedBodyComponent body));
|
||||||
Assert.True(body.HasMechanismBehavior<LungBehavior>());
|
Assert.True(body.HasMechanismBehavior<LungBehavior>());
|
||||||
Assert.True(human.TryGetComponent(out metabolism));
|
Assert.True(human.TryGetComponent(out respirator));
|
||||||
Assert.False(metabolism.Suffocating);
|
Assert.False(respirator.Suffocating);
|
||||||
});
|
});
|
||||||
|
|
||||||
var increment = 10;
|
var increment = 10;
|
||||||
@@ -178,7 +178,7 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
for (var tick = 0; tick < 600; tick += increment)
|
for (var tick = 0; tick < 600; tick += increment)
|
||||||
{
|
{
|
||||||
await server.WaitRunTicks(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();
|
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;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Client.Clickable;
|
using Content.Client.Clickable;
|
||||||
|
using Content.Server.GameTicking;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests
|
namespace Content.IntegrationTests.Tests
|
||||||
{
|
{
|
||||||
@@ -57,12 +60,19 @@ namespace Content.IntegrationTests.Tests
|
|||||||
[TestCase("ClickTestRotatingCornerInvisibleNoRot", 0.25f, 0.25f, DirSouthEastJustShy, 1, ExpectedResult = true)]
|
[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)
|
public async Task<bool> Test(string prototype, float clickPosX, float clickPosY, double angle, float scale)
|
||||||
{
|
{
|
||||||
|
Vector2? worldPos = null;
|
||||||
EntityUid entity = default;
|
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(() =>
|
await _server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var entMgr = IoCManager.Resolve<IEntityManager>();
|
var gridEnt = mapManager.GetGrid(gameTicker.DefaultGridId).GridEntityId;
|
||||||
var ent = entMgr.SpawnEntity(prototype, new MapCoordinates(0, 0, new MapId(1)));
|
worldPos = serverEntManager.GetEntity(gridEnt).Transform.WorldPosition;
|
||||||
|
|
||||||
|
var ent = serverEntManager.SpawnEntity(prototype, new EntityCoordinates(gridEnt, 0f, 0f));
|
||||||
ent.Transform.LocalRotation = angle;
|
ent.Transform.LocalRotation = angle;
|
||||||
ent.GetComponent<SpriteComponent>().Scale = (scale, scale);
|
ent.GetComponent<SpriteComponent>().Scale = (scale, scale);
|
||||||
entity = ent.Uid;
|
entity = ent.Uid;
|
||||||
@@ -75,17 +85,15 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
await _client.WaitPost(() =>
|
await _client.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var entMgr = IoCManager.Resolve<IEntityManager>();
|
var ent = clientEntManager.GetEntity(entity);
|
||||||
var ent = entMgr.GetEntity(entity);
|
|
||||||
var clickable = ent.GetComponent<ClickableComponent>();
|
var clickable = ent.GetComponent<ClickableComponent>();
|
||||||
|
|
||||||
hit = clickable.CheckClick((clickPosX, clickPosY), out _, out _);
|
hit = clickable.CheckClick((clickPosX, clickPosY) + worldPos!.Value, out _, out _);
|
||||||
});
|
});
|
||||||
|
|
||||||
await _server.WaitPost(() =>
|
await _server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var entMgr = IoCManager.Resolve<IEntityManager>();
|
serverEntManager.DeleteEntity(entity);
|
||||||
entMgr.DeleteEntity(entity);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return hit;
|
return hit;
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
|||||||
- type: PointLight
|
- type: PointLight
|
||||||
enabled: false
|
enabled: false
|
||||||
radius: 3
|
radius: 3
|
||||||
- type: LoopingSound
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: FlashLightVisualizer
|
- type: FlashLightVisualizer
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Content.Shared.Spawning;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Broadphase;
|
using Robust.Shared.Physics.Broadphase;
|
||||||
|
|
||||||
@@ -44,7 +45,11 @@ namespace Content.IntegrationTests.Tests.Utility
|
|||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
|
var mapId = new MapId(1);
|
||||||
var grid = sMapManager.GetGrid(new GridId(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);
|
var entityCoordinates = new EntityCoordinates(grid.GridEntityId, 0, 0);
|
||||||
|
|
||||||
// Nothing blocking it, only entity is the grid
|
// 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.True(sEntityManager.TrySpawnIfUnobstructed(null, entityCoordinates, CollisionGroup.Impassable, out var entity));
|
||||||
Assert.NotNull(entity);
|
Assert.NotNull(entity);
|
||||||
|
|
||||||
var mapId = new MapId(1);
|
var mapCoordinates = new MapCoordinates(gridPos.X, gridPos.Y, mapId);
|
||||||
var mapCoordinates = new MapCoordinates(0, 0, mapId);
|
|
||||||
|
|
||||||
// Nothing blocking it, only entity is the grid
|
// Nothing blocking it, only entity is the grid
|
||||||
Assert.NotNull(sEntityManager.SpawnIfUnobstructed(null, mapCoordinates, CollisionGroup.Impassable));
|
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 IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly PathfindingSystem _pathfindingSystem = default!;
|
||||||
private PathfindingSystem _pathfindingSystem = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queued region updates
|
/// Queued region updates
|
||||||
@@ -80,7 +79,6 @@ namespace Content.Server.AI.Pathfinding.Accessible
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
_pathfindingSystem = Get<PathfindingSystem>();
|
|
||||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||||
SubscribeLocalEvent<PathfindingChunkUpdateMessage>(RecalculateNodeRegions);
|
SubscribeLocalEvent<PathfindingChunkUpdateMessage>(RecalculateNodeRegions);
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ namespace Content.Server.AI.Steering
|
|||||||
// http://www.red3d.com/cwr/papers/1999/gdc99steer.html for a steering overview
|
// http://www.red3d.com/cwr/papers/1999/gdc99steer.html for a steering overview
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IPauseManager _pauseManager = default!;
|
[Dependency] private readonly IPauseManager _pauseManager = default!;
|
||||||
|
[Dependency] private readonly PathfindingSystem _pathfindingSystem = default!;
|
||||||
private PathfindingSystem _pathfindingSystem = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we try to avoid non-blocking physics objects
|
/// Whether we try to avoid non-blocking physics objects
|
||||||
@@ -87,7 +86,6 @@ namespace Content.Server.AI.Steering
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
_pathfindingSystem = Get<PathfindingSystem>();
|
|
||||||
|
|
||||||
for (var i = 0; i < AgentListCount; i++)
|
for (var i = 0; i < AgentListCount; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace Content.Server.Atmos.Commands
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grid.HasComponent<IGridAtmosphereComponent>())
|
if (grid.HasComponent<IAtmosphereComponent>())
|
||||||
{
|
{
|
||||||
shell.WriteLine("Grid already has an atmosphere.");
|
shell.WriteLine("Grid already has an atmosphere.");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace Content.Server.Atmos.Commands
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grid.HasComponent<IGridAtmosphereComponent>())
|
if (grid.HasComponent<IAtmosphereComponent>())
|
||||||
{
|
{
|
||||||
shell.WriteLine("Grid already has an atmosphere.");
|
shell.WriteLine("Grid already has an atmosphere.");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -23,23 +23,20 @@ namespace Content.Server.Atmos.Components
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[ComponentDependency] private readonly FlammableComponent? _flammableComponent = null;
|
[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 (_temperatureComponent != null)
|
||||||
{
|
{
|
||||||
if (tile.Air != null)
|
var temperatureDelta = air.Temperature - _temperatureComponent.CurrentTemperature;
|
||||||
{
|
var tileHeatCapacity = atmosphereSystem.GetHeatCapacity(air);
|
||||||
var temperatureDelta = tile.Air.Temperature - _temperatureComponent.CurrentTemperature;
|
var heat = temperatureDelta * (tileHeatCapacity * _temperatureComponent.HeatCapacity / (tileHeatCapacity + _temperatureComponent.HeatCapacity));
|
||||||
var tileHeatCapacity = atmosphereSystem.GetHeatCapacity(tile.Air);
|
_temperatureComponent.ReceiveHeat(heat);
|
||||||
var heat = temperatureDelta * (tileHeatCapacity * _temperatureComponent.HeatCapacity / (tileHeatCapacity + _temperatureComponent.HeatCapacity));
|
|
||||||
_temperatureComponent.ReceiveHeat(heat);
|
|
||||||
}
|
|
||||||
_temperatureComponent.Update();
|
_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();
|
UpdateAppearance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(TileAtmosphere tile)
|
public void Update(GasMixture air)
|
||||||
{
|
{
|
||||||
// Slowly dry ourselves off if wet.
|
// Slowly dry ourselves off if wet.
|
||||||
if (FireStacks < 0)
|
if (FireStacks < 0)
|
||||||
@@ -104,13 +104,13 @@ namespace Content.Server.Atmos.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we're in an oxygenless environment, put the fire out.
|
// 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();
|
Extinguish();
|
||||||
return;
|
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>();
|
var physics = Owner.GetComponent<IPhysBody>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#nullable disable warnings
|
|
||||||
using System;
|
using System;
|
||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Body.Respiratory;
|
using Content.Server.Body.Respiratory;
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ using Dependency = Robust.Shared.IoC.DependencyAttribute;
|
|||||||
namespace Content.Server.Atmos.Components
|
namespace Content.Server.Atmos.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is our SSAir equivalent.
|
/// Internal Atmos class. Use <see cref="AtmosphereSystem"/> to interact with atmos instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ComponentReference(typeof(IGridAtmosphereComponent))]
|
[ComponentReference(typeof(IAtmosphereComponent))]
|
||||||
[RegisterComponent, Serializable]
|
[RegisterComponent, Serializable]
|
||||||
public class GridAtmosphereComponent : Component, IGridAtmosphereComponent, ISerializationHooks
|
public class GridAtmosphereComponent : Component, IAtmosphereComponent, ISerializationHooks
|
||||||
{
|
{
|
||||||
public override string Name => "GridAtmosphere";
|
public override string Name => "GridAtmosphere";
|
||||||
|
|
||||||
public virtual bool Simulated => true;
|
public virtual bool Simulated => true;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Content.Server.Atmos.Components
|
namespace Content.Server.Atmos.Components
|
||||||
{
|
{
|
||||||
public interface IGridAtmosphereComponent : IComponent
|
public interface IAtmosphereComponent : IComponent
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this atmosphere is simulated or not.
|
/// 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
|
namespace Content.Server.Atmos.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(IGridAtmosphereComponent))]
|
[ComponentReference(typeof(IAtmosphereComponent))]
|
||||||
[ComponentReference(typeof(GridAtmosphereComponent))]
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent, IGridAtmosphereComponent
|
public class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent
|
||||||
{
|
{
|
||||||
public override string Name => "UnsimulatedGridAtmosphere";
|
public override string Name => "UnsimulatedGridAtmosphere";
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
public class AirtightSystem : EntitySystem
|
public class AirtightSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -42,7 +43,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
if (airtight.FixVacuum)
|
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())
|
if (!gridId.IsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
_atmosphereSystem.UpdateAdjacent(gridId, pos);
|
||||||
atmosphereSystem.UpdateAdjacent(gridId, pos);
|
_atmosphereSystem.InvalidateTile(gridId, pos);
|
||||||
atmosphereSystem.InvalidateTile(gridId, pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle)
|
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 IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Players allowed to see the atmos debug overlay.
|
/// Players allowed to see the atmos debug overlay.
|
||||||
@@ -127,7 +128,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
AccumulatedFrameTime -= _updateCooldown;
|
AccumulatedFrameTime -= _updateCooldown;
|
||||||
|
|
||||||
var currentTick = _gameTiming.CurTick;
|
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
|
// 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).
|
// 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++)
|
for (var x = 0; x < LocalViewRange; x++)
|
||||||
{
|
{
|
||||||
var Vector2i = new Vector2i(baseTile.X + x, baseTile.Y + y);
|
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 MonstermosRipTiles { get; private set; }
|
||||||
public bool GridImpulse { get; private set; }
|
public bool GridImpulse { get; private set; }
|
||||||
public bool Superconduction { get; private set; }
|
public bool Superconduction { get; private set; }
|
||||||
|
public bool ExcitedGroups { get; private set; }
|
||||||
public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; }
|
public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; }
|
||||||
public float AtmosMaxProcessTime { get; private set; }
|
public float AtmosMaxProcessTime { get; private set; }
|
||||||
public float AtmosTickRate { 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.Superconduction, value => Superconduction = value, true);
|
||||||
_cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true);
|
_cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true);
|
||||||
_cfg.OnValueChanged(CCVars.AtmosTickRate, value => AtmosTickRate = 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);
|
_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);
|
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)
|
public float GetThermalEnergy(GasMixture mixture)
|
||||||
{
|
{
|
||||||
return mixture.Temperature * GetHeatCapacity(mixture);
|
return mixture.Temperature * GetHeatCapacity(mixture);
|
||||||
@@ -79,7 +72,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
public float Share(GasMixture receiver, GasMixture sharer, int atmosAdjacentTurfs)
|
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 absTemperatureDelta = Math.Abs(temperatureDelta);
|
||||||
var oldHeatCapacity = 0f;
|
var oldHeatCapacity = 0f;
|
||||||
var oldSharerHeatCapacity = 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.
|
// Transfer of thermal energy (via changed heat capacity) between self and sharer.
|
||||||
if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity)
|
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)
|
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.
|
// Thermal energy of the system (self and sharer) is unchanged.
|
||||||
@@ -154,17 +147,17 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
var moles = receiver.TotalMoles;
|
var moles = receiver.TotalMoles;
|
||||||
var theirMoles = sharer.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)
|
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)
|
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||||
{
|
{
|
||||||
var heatCapacity = GetHeatCapacityArchived(receiver);
|
var heatCapacity = GetHeatCapacity(receiver);
|
||||||
var sharerHeatCapacity = GetHeatCapacityArchived(sharer);
|
var sharerHeatCapacity = GetHeatCapacity(sharer);
|
||||||
|
|
||||||
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
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)
|
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)
|
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||||
{
|
{
|
||||||
var heatCapacity = GetHeatCapacityArchived(receiver);
|
var heatCapacity = GetHeatCapacity(receiver);
|
||||||
|
|
||||||
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,13 +23,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
public partial class AtmosphereSystem
|
public partial class AtmosphereSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||||
|
[Dependency] private readonly GasTileOverlaySystem _gasTileOverlaySystem = default!;
|
||||||
private GasTileOverlaySystem _gasTileOverlaySystem = default!;
|
|
||||||
|
|
||||||
private void InitializeGrid()
|
private void InitializeGrid()
|
||||||
{
|
{
|
||||||
_gasTileOverlaySystem = Get<GasTileOverlaySystem>();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, ComponentInit>(OnGridAtmosphereInit);
|
SubscribeLocalEvent<GridAtmosphereComponent, ComponentInit>(OnGridAtmosphereInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +117,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
/// <returns>All tile mixtures in a grid.</returns>
|
/// <returns>All tile mixtures in a grid.</returns>
|
||||||
public IEnumerable<GasMixture> GetAllTileMixtures(GridId grid, bool invalidate = false)
|
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))
|
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
|
||||||
return Enumerable.Empty<GasMixture>();
|
return Enumerable.Empty<GasMixture>();
|
||||||
|
|
||||||
@@ -669,7 +670,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
public GasMixture? GetTileMixture(EntityCoordinates coordinates, bool invalidate = false)
|
public GasMixture? GetTileMixture(EntityCoordinates coordinates, bool invalidate = false)
|
||||||
{
|
{
|
||||||
return TryGetGridAndTile(coordinates, out var tuple)
|
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>
|
/// <summary>
|
||||||
@@ -681,6 +682,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
/// <returns>The tile mixture, or null</returns>
|
/// <returns>The tile mixture, or null</returns>
|
||||||
public GasMixture? GetTileMixture(GridId grid, Vector2i tile, bool invalidate = false)
|
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))
|
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -689,7 +694,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
return GetTileMixture(gridAtmosphere, tile, invalidate);
|
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.
|
// Always return a new space gas mixture in this case.
|
||||||
return GasMixture.SpaceGas;
|
return GasMixture.SpaceGas;
|
||||||
@@ -970,6 +975,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
/// <returns>All adjacent tile gas mixtures to the tile in question</returns>
|
/// <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)
|
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))
|
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
|
||||||
return Enumerable.Empty<GasMixture>();
|
return Enumerable.Empty<GasMixture>();
|
||||||
|
|
||||||
@@ -1377,7 +1386,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)
|
if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)
|
||||||
&& gridAtmosphere.AtmosDevices.Contains(atmosDevice))
|
&& gridAtmosphere.AtmosDevices.Contains(atmosDevice))
|
||||||
{
|
{
|
||||||
atmosDevice.JoinedGrid = null;
|
atmosDevice.JoinedGrid = null;
|
||||||
gridAtmosphere.AtmosDevices.Remove(atmosDevice);
|
gridAtmosphere.AtmosDevices.Remove(atmosDevice);
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
#nullable disable warnings
|
|
||||||
#nullable enable annotations
|
|
||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Atmos.Reactions;
|
using Content.Server.Atmos.Reactions;
|
||||||
using Content.Server.Coordinates.Helpers;
|
using Content.Server.Coordinates.Helpers;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
using Robust.Shared.Map;
|
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems
|
namespace Content.Server.Atmos.EntitySystems
|
||||||
{
|
{
|
||||||
@@ -140,7 +137,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
Merge(tile.Air, affected);
|
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())
|
foreach (var entity in tileRef.GetEntitiesInTileFast())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#nullable disable warnings
|
|
||||||
#nullable enable annotations
|
|
||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
|
|
||||||
@@ -16,9 +14,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tile.ArchivedCycle < fireCount)
|
|
||||||
Archive(tile, fireCount);
|
|
||||||
|
|
||||||
tile.CurrentCycle = fireCount;
|
tile.CurrentCycle = fireCount;
|
||||||
var adjacentTileLength = 0;
|
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 the tile is null or has no air, we don't do anything for it.
|
||||||
if(enemyTile?.Air == null) continue;
|
if(enemyTile?.Air == null) continue;
|
||||||
if (fireCount <= enemyTile.CurrentCycle) continue;
|
if (fireCount <= enemyTile.CurrentCycle) continue;
|
||||||
Archive(enemyTile, fireCount);
|
|
||||||
|
|
||||||
var shouldShareAir = false;
|
var shouldShareAir = false;
|
||||||
|
|
||||||
if (tile.ExcitedGroup != null && enemyTile.ExcitedGroup != null)
|
if (ExcitedGroups && tile.ExcitedGroup != null && enemyTile.ExcitedGroup != null)
|
||||||
{
|
{
|
||||||
if (tile.ExcitedGroup != enemyTile.ExcitedGroup)
|
if (tile.ExcitedGroup != enemyTile.ExcitedGroup)
|
||||||
{
|
{
|
||||||
@@ -57,21 +51,24 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
AddActiveTile(gridAtmosphere, enemyTile);
|
AddActiveTile(gridAtmosphere, enemyTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
var excitedGroup = tile.ExcitedGroup;
|
if (ExcitedGroups)
|
||||||
excitedGroup ??= enemyTile.ExcitedGroup;
|
|
||||||
|
|
||||||
if (excitedGroup == null)
|
|
||||||
{
|
{
|
||||||
excitedGroup = new ExcitedGroup();
|
var excitedGroup = tile.ExcitedGroup;
|
||||||
gridAtmosphere.ExcitedGroups.Add(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;
|
shouldShareAir = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,17 +103,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (ConsiderSuperconductivity(gridAtmosphere, tile, true))
|
if (ConsiderSuperconductivity(gridAtmosphere, tile, true))
|
||||||
remove = false;
|
remove = false;
|
||||||
|
|
||||||
if(tile.ExcitedGroup == null && remove)
|
if(ExcitedGroups && tile.ExcitedGroup == null && remove)
|
||||||
RemoveActiveTile(gridAtmosphere, tile);
|
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)
|
private void LastShareCheck(TileAtmosphere tile)
|
||||||
{
|
{
|
||||||
if (tile.Air == null || tile.ExcitedGroup == null)
|
if (tile.Air == null || tile.ExcitedGroup == null)
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
#nullable disable warnings
|
|
||||||
#nullable enable annotations
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Coordinates.Helpers;
|
using Content.Server.Doors.Components;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems
|
namespace Content.Server.Atmos.EntitySystems
|
||||||
{
|
{
|
||||||
@@ -20,7 +18,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
private readonly TileAtmosphereComparer _monstermosComparer = new();
|
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[] _equalizeGiverTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
|
||||||
private readonly TileAtmosphere[] _equalizeTakerTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
|
private readonly TileAtmosphere[] _equalizeTakerTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
|
||||||
private readonly TileAtmosphere[] _equalizeQueue = 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[] _depressurizeSpaceTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
|
||||||
private readonly TileAtmosphere[] _depressurizeProgressionOrder = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit * 2];
|
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))
|
if (tile.Air == null || (tile.MonstermosInfo.LastCycle >= cycleNum))
|
||||||
return; // Already done.
|
return; // Already done.
|
||||||
@@ -65,11 +63,12 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
for (var i = 0; i < tileCount; i++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
if (i > Atmospherics.MonstermosHardTileLimit) break;
|
if (i > Atmospherics.MonstermosHardTileLimit) break;
|
||||||
var exploring = _equalizeTiles[i];
|
var exploring = _equalizeTiles[i]!;
|
||||||
|
|
||||||
if (i < Atmospherics.MonstermosTileLimit)
|
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;
|
exploring.MonstermosInfo.MoleDelta = tileMoles;
|
||||||
totalMoles += tileMoles;
|
totalMoles += tileMoles;
|
||||||
}
|
}
|
||||||
@@ -106,7 +105,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (otherTile == null)
|
if (otherTile == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_equalizeTiles[i].MonstermosInfo.LastQueueCycle = 0;
|
otherTile.MonstermosInfo.LastQueueCycle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tileCount = Atmospherics.MonstermosTileLimit;
|
tileCount = Atmospherics.MonstermosTileLimit;
|
||||||
@@ -118,7 +117,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
for (var i = 0; i < tileCount; i++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
var otherTile = _equalizeTiles[i];
|
var otherTile = _equalizeTiles[i]!;
|
||||||
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
||||||
otherTile.MonstermosInfo.MoleDelta -= averageMoles;
|
otherTile.MonstermosInfo.MoleDelta -= averageMoles;
|
||||||
if (otherTile.MonstermosInfo.MoleDelta > 0)
|
if (otherTile.MonstermosInfo.MoleDelta > 0)
|
||||||
@@ -133,7 +132,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
var logN = MathF.Log2(tileCount);
|
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)
|
if (giverTilesLength > logN && takerTilesLength > logN)
|
||||||
{
|
{
|
||||||
// Even if it fails, it will speed up the next part.
|
// 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++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
var otherTile = _equalizeTiles[i];
|
var otherTile = _equalizeTiles[i]!;
|
||||||
otherTile.MonstermosInfo.FastDone = true;
|
otherTile.MonstermosInfo.FastDone = true;
|
||||||
if (!(otherTile.MonstermosInfo.MoleDelta > 0)) continue;
|
if (!(otherTile.MonstermosInfo.MoleDelta > 0)) continue;
|
||||||
var eligibleDirections = AtmosDirection.Invalid;
|
var eligibleDirections = AtmosDirection.Invalid;
|
||||||
@@ -150,7 +149,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << j);
|
var direction = (AtmosDirection) (1 << j);
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
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.
|
// skip anything that isn't part of our current processing block.
|
||||||
if (tile2.MonstermosInfo.FastDone || tile2.MonstermosInfo.LastQueueCycle != queueCycle)
|
if (tile2.MonstermosInfo.FastDone || tile2.MonstermosInfo.LastQueueCycle != queueCycle)
|
||||||
@@ -171,7 +170,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
AdjustEqMovement(otherTile, direction, molesToMove);
|
AdjustEqMovement(otherTile, direction, molesToMove);
|
||||||
otherTile.MonstermosInfo.MoleDelta -= 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++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
var otherTile = _equalizeTiles[i];
|
var otherTile = _equalizeTiles[i]!;
|
||||||
if (otherTile.MonstermosInfo.MoleDelta > 0)
|
if (otherTile.MonstermosInfo.MoleDelta > 0)
|
||||||
{
|
{
|
||||||
_equalizeGiverTiles[giverTilesLength++] = otherTile;
|
_equalizeGiverTiles[giverTilesLength++] = otherTile;
|
||||||
@@ -252,7 +251,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (otherTile.MonstermosInfo.CurrentTransferAmount != 0 && otherTile.MonstermosInfo.CurrentTransferDirection != AtmosDirection.Invalid)
|
if (otherTile.MonstermosInfo.CurrentTransferAmount != 0 && otherTile.MonstermosInfo.CurrentTransferDirection != AtmosDirection.Invalid)
|
||||||
{
|
{
|
||||||
AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount);
|
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;
|
.MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount;
|
||||||
otherTile.MonstermosInfo.CurrentTransferAmount = 0;
|
otherTile.MonstermosInfo.CurrentTransferAmount = 0;
|
||||||
}
|
}
|
||||||
@@ -319,7 +318,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount);
|
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;
|
.MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount;
|
||||||
otherTile.MonstermosInfo.CurrentTransferAmount = 0;
|
otherTile.MonstermosInfo.CurrentTransferAmount = 0;
|
||||||
}
|
}
|
||||||
@@ -328,19 +327,19 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
for (var i = 0; i < tileCount; i++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
var otherTile = _equalizeTiles[i];
|
var otherTile = _equalizeTiles[i]!;
|
||||||
FinalizeEq(gridAtmosphere, otherTile);
|
FinalizeEq(gridAtmosphere, otherTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < tileCount; i++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
var otherTile = _equalizeTiles[i];
|
var otherTile = _equalizeTiles[i]!;
|
||||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << j);
|
var direction = (AtmosDirection) (1 << j);
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||||
var otherTile2 = otherTile.AdjacentTiles[j];
|
var otherTile2 = otherTile.AdjacentTiles[j]!;
|
||||||
if (otherTile2?.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue;
|
if (otherTile2.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue;
|
||||||
AddActiveTile(gridAtmosphere, otherTile2);
|
AddActiveTile(gridAtmosphere, otherTile2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -353,7 +352,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
Array.Clear(_equalizeQueue, 0, Atmospherics.MonstermosTileLimit);
|
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.
|
// Check if explosive depressurization is enabled and if the tile is valid.
|
||||||
if (!MonstermosDepressurization || tile.Air == null)
|
if (!MonstermosDepressurization || tile.Air == null)
|
||||||
@@ -376,7 +375,8 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
var otherTile = _depressurizeTiles[i];
|
var otherTile = _depressurizeTiles[i];
|
||||||
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
||||||
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
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;
|
_depressurizeSpaceTiles[spaceTileCount++] = otherTile;
|
||||||
otherTile.PressureSpecificTarget = otherTile;
|
otherTile.PressureSpecificTarget = otherTile;
|
||||||
@@ -388,7 +388,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
var direction = (AtmosDirection) (1 << j);
|
var direction = (AtmosDirection) (1 << j);
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||||
var otherTile2 = otherTile.AdjacentTiles[j];
|
var otherTile2 = otherTile.AdjacentTiles[j];
|
||||||
if (otherTile2.Air == null) continue;
|
if (otherTile2?.Air == null) continue;
|
||||||
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue;
|
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue;
|
||||||
|
|
||||||
ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2);
|
ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2);
|
||||||
@@ -421,8 +421,8 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << j);
|
var direction = (AtmosDirection) (1 << j);
|
||||||
// TODO ATMOS This is a terrible hack that accounts for the mess that are space TileAtmospheres.
|
// Tiles in _depressurizeProgressionOrder cannot have null air.
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air.Immutable) continue;
|
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air!.Immutable) continue;
|
||||||
var tile2 = otherTile.AdjacentTiles[j];
|
var tile2 = otherTile.AdjacentTiles[j];
|
||||||
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
||||||
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||||
@@ -509,7 +509,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
InvalidateVisuals(other.GridIndex, other.GridIndices);
|
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];
|
Span<float> transferDirections = stackalloc float[Atmospherics.Directions];
|
||||||
var hasTransferDirs = false;
|
var hasTransferDirs = false;
|
||||||
@@ -533,7 +533,8 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (otherTile?.Air == null) continue;
|
if (otherTile?.Air == null) continue;
|
||||||
if (amount > 0)
|
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);
|
FinalizeEqNeighbors(gridAtmosphere, tile, transferDirections);
|
||||||
|
|
||||||
otherTile.MonstermosInfo[direction.GetOpposite()] = 0;
|
otherTile.MonstermosInfo[direction.GetOpposite()] = 0;
|
||||||
@@ -551,15 +552,19 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << i);
|
var direction = (AtmosDirection) (1 << i);
|
||||||
var amount = transferDirs[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))
|
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)
|
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.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)
|
private void HandleDecompressionFloorRip(IMapGrid mapGrid, TileAtmosphere tile, float sum)
|
||||||
@@ -573,9 +578,9 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
PryTile(mapGrid, tile.GridIndices);
|
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)
|
if (a == null && b == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
|
private readonly AtmosDeviceUpdateEvent _updateEvent = new();
|
||||||
private readonly Stopwatch _simulationStopwatch = new();
|
private readonly Stopwatch _simulationStopwatch = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -204,11 +205,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
atmosphere.CurrentRunAtmosDevices = new Queue<AtmosDeviceComponent>(atmosphere.AtmosDevices);
|
atmosphere.CurrentRunAtmosDevices = new Queue<AtmosDeviceComponent>(atmosphere.AtmosDevices);
|
||||||
|
|
||||||
var time = _gameTiming.CurTime;
|
var time = _gameTiming.CurTime;
|
||||||
var updateEvent = new AtmosDeviceUpdateEvent();
|
|
||||||
var number = 0;
|
var number = 0;
|
||||||
while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device))
|
while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device))
|
||||||
{
|
{
|
||||||
EntityManager.EventBus.RaiseLocalEvent(device.Owner.Uid, updateEvent, false);
|
RaiseLocalEvent(device.Owner.Uid, _updateEvent, false);
|
||||||
device.LastProcess = time;
|
device.LastProcess = time;
|
||||||
|
|
||||||
if (number++ < LagCheckIterations) continue;
|
if (number++ < LagCheckIterations) continue;
|
||||||
@@ -241,7 +241,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
var atmosphere = _currentRunAtmosphere[_currentRunAtmosphereIndex];
|
var atmosphere = _currentRunAtmosphere[_currentRunAtmosphereIndex];
|
||||||
|
|
||||||
if (atmosphere.Paused || atmosphere.LifeStage >= ComponentLifeStage.Stopping)
|
if (atmosphere.Paused || !atmosphere.Simulated || atmosphere.LifeStage >= ComponentLifeStage.Stopping)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
atmosphere.Timer += frameTime;
|
atmosphere.Timer += frameTime;
|
||||||
@@ -281,7 +281,8 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
}
|
}
|
||||||
|
|
||||||
atmosphere.ProcessingPaused = false;
|
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;
|
continue;
|
||||||
case AtmosphereProcessingState.ExcitedGroups:
|
case AtmosphereProcessingState.ExcitedGroups:
|
||||||
if (!ProcessExcitedGroups(atmosphere))
|
if (!ProcessExcitedGroups(atmosphere))
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (adjacent == null || adjacent.ThermalConductivity == 0f)
|
if (adjacent == null || adjacent.ThermalConductivity == 0f)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(adjacent.ArchivedCycle < gridAtmosphere.UpdateCounter)
|
|
||||||
Archive(adjacent, gridAtmosphere.UpdateCounter);
|
|
||||||
|
|
||||||
NeighborConductWithSource(gridAtmosphere, adjacent, tile);
|
NeighborConductWithSource(gridAtmosphere, adjacent, tile);
|
||||||
|
|
||||||
ConsiderSuperconductivity(gridAtmosphere, adjacent);
|
ConsiderSuperconductivity(gridAtmosphere, adjacent);
|
||||||
@@ -37,8 +34,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
if(tile.Air == null)
|
if(tile.Air == null)
|
||||||
{
|
{
|
||||||
if(tile.ArchivedCycle < gridAtmosphere.UpdateCounter)
|
|
||||||
Archive(tile, gridAtmosphere.UpdateCounter);
|
|
||||||
return AtmosDirection.All;
|
return AtmosDirection.All;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +123,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient)
|
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
|
if (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider
|
||||||
&& tile.HeatCapacity != 0f && other.HeatCapacity != 0f)
|
&& tile.HeatCapacity != 0f && other.HeatCapacity != 0f)
|
||||||
{
|
{
|
||||||
@@ -146,7 +141,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (tile.Temperature > Atmospherics.T0C)
|
if (tile.Temperature > Atmospherics.T0C)
|
||||||
{
|
{
|
||||||
// Hardcoded space temperature.
|
// Hardcoded space temperature.
|
||||||
var deltaTemperature = (tile.TemperatureArchived - Atmospherics.TCMB);
|
var deltaTemperature = (tile.Temperature - Atmospherics.TCMB);
|
||||||
if ((tile.HeatCapacity > 0) && (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider))
|
if ((tile.HeatCapacity > 0) && (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider))
|
||||||
{
|
{
|
||||||
var heat = tile.ThermalConductivity * deltaTemperature * (tile.HeatCapacity *
|
var heat = tile.ThermalConductivity * deltaTemperature * (tile.HeatCapacity *
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ using Robust.Shared.Map;
|
|||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems
|
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]
|
[UsedImplicitly]
|
||||||
public partial class AtmosphereSystem : SharedAtmosphereSystem
|
public partial class AtmosphereSystem : SharedAtmosphereSystem
|
||||||
{
|
{
|
||||||
@@ -29,7 +32,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
// Map events.
|
// Map events.
|
||||||
_mapManager.MapCreated += OnMapCreated;
|
|
||||||
_mapManager.TileChanged += OnTileChanged;
|
_mapManager.TileChanged += OnTileChanged;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -39,7 +41,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
|
|
||||||
_mapManager.MapCreated -= OnMapCreated;
|
|
||||||
_mapManager.TileChanged -= OnTileChanged;
|
_mapManager.TileChanged -= OnTileChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,17 +58,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
InvalidateTile(eventArgs.NewTile.GridIndex, eventArgs.NewTile.GridIndices);
|
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)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
@@ -81,7 +71,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
foreach (var exposed in EntityManager.ComponentManager.EntityQuery<AtmosExposedComponent>())
|
foreach (var exposed in EntityManager.ComponentManager.EntityQuery<AtmosExposedComponent>())
|
||||||
{
|
{
|
||||||
// TODO ATMOS: Kill this with fire.
|
// TODO ATMOS: Kill this with fire.
|
||||||
var tile = GetTileAtmosphereOrCreateSpace(exposed.Owner.Transform.Coordinates);
|
var tile = GetTileMixture(exposed.Owner.Transform.Coordinates);
|
||||||
if (tile == null) continue;
|
if (tile == null) continue;
|
||||||
exposed.Update(tile, _exposedTimer, this);
|
exposed.Update(tile, _exposedTimer, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems
|
namespace Content.Server.Atmos.EntitySystems
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class GasTankSystem : EntitySystem
|
public class GasTankSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
|
||||||
private const float TimerDelay = 0.5f;
|
private const float TimerDelay = 0.5f;
|
||||||
private float _timer = 0f;
|
private float _timer = 0f;
|
||||||
|
|
||||||
@@ -19,11 +22,9 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (_timer < TimerDelay) return;
|
if (_timer < TimerDelay) return;
|
||||||
_timer -= TimerDelay;
|
_timer -= TimerDelay;
|
||||||
|
|
||||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
|
||||||
|
|
||||||
foreach (var gasTank in EntityManager.ComponentManager.EntityQuery<GasTankComponent>())
|
foreach (var gasTank in EntityManager.ComponentManager.EntityQuery<GasTankComponent>())
|
||||||
{
|
{
|
||||||
atmosphereSystem.React(gasTank.Air, gasTank);
|
_atmosphereSystem.React(gasTank.Air, gasTank);
|
||||||
gasTank.CheckStatus();
|
gasTank.CheckStatus();
|
||||||
gasTank.UpdateUserInterface();
|
gasTank.UpdateUserInterface();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The tiles that have had their atmos data updated since last tick
|
/// The tiles that have had their atmos data updated since last tick
|
||||||
@@ -58,8 +59,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private float _updateCooldown;
|
private float _updateCooldown;
|
||||||
|
|
||||||
private AtmosphereSystem _atmosphereSystem = default!;
|
|
||||||
|
|
||||||
private int _thresholds;
|
private int _thresholds;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -68,7 +67,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||||
|
|
||||||
_atmosphereSystem = Get<AtmosphereSystem>();
|
|
||||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||||
_mapManager.OnGridRemoved += OnGridRemoved;
|
_mapManager.OnGridRemoved += OnGridRemoved;
|
||||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ namespace Content.Server.Atmos
|
|||||||
[DataField("moles")] [ViewVariables]
|
[DataField("moles")] [ViewVariables]
|
||||||
public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases];
|
public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||||
|
|
||||||
[DataField("molesArchived")] [ViewVariables]
|
|
||||||
public float[] MolesArchived = new float[Atmospherics.AdjustedNumberOfGases];
|
|
||||||
|
|
||||||
[DataField("temperature")] [ViewVariables]
|
[DataField("temperature")] [ViewVariables]
|
||||||
private float _temperature = Atmospherics.TCMB;
|
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]
|
[DataField("volume")] [ViewVariables]
|
||||||
public float Volume { get; set; }
|
public float Volume { get; set; }
|
||||||
|
|
||||||
@@ -96,13 +90,6 @@ namespace Content.Server.Atmos
|
|||||||
Immutable = true;
|
Immutable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Archive()
|
|
||||||
{
|
|
||||||
Moles.AsSpan().CopyTo(MolesArchived.AsSpan());
|
|
||||||
TemperatureArchived = Temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public float GetMoles(int gasId)
|
public float GetMoles(int gasId)
|
||||||
{
|
{
|
||||||
@@ -253,7 +240,6 @@ namespace Content.Server.Atmos
|
|||||||
{
|
{
|
||||||
// The arrays MUST have a specific length.
|
// The arrays MUST have a specific length.
|
||||||
Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases);
|
Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases);
|
||||||
Array.Resize(ref MolesArchived, Atmospherics.AdjustedNumberOfGases);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
@@ -268,12 +254,10 @@ namespace Content.Server.Atmos
|
|||||||
if (ReferenceEquals(null, other)) return false;
|
if (ReferenceEquals(null, other)) return false;
|
||||||
if (ReferenceEquals(this, other)) return true;
|
if (ReferenceEquals(this, other)) return true;
|
||||||
return Moles.SequenceEqual(other.Moles)
|
return Moles.SequenceEqual(other.Moles)
|
||||||
&& MolesArchived.SequenceEqual(other.MolesArchived)
|
|
||||||
&& _temperature.Equals(other._temperature)
|
&& _temperature.Equals(other._temperature)
|
||||||
&& ReactionResults.SequenceEqual(other.ReactionResults)
|
&& ReactionResults.SequenceEqual(other.ReactionResults)
|
||||||
&& Immutable == other.Immutable
|
&& Immutable == other.Immutable
|
||||||
&& LastShare.Equals(other.LastShare)
|
&& LastShare.Equals(other.LastShare)
|
||||||
&& TemperatureArchived.Equals(other.TemperatureArchived)
|
|
||||||
&& Volume.Equals(other.Volume);
|
&& Volume.Equals(other.Volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,13 +268,10 @@ namespace Content.Server.Atmos
|
|||||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||||
{
|
{
|
||||||
var moles = Moles[i];
|
var moles = Moles[i];
|
||||||
var molesArchived = MolesArchived[i];
|
|
||||||
hashCode.Add(moles);
|
hashCode.Add(moles);
|
||||||
hashCode.Add(molesArchived);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hashCode.Add(_temperature);
|
hashCode.Add(_temperature);
|
||||||
hashCode.Add(TemperatureArchived);
|
|
||||||
hashCode.Add(Immutable);
|
hashCode.Add(Immutable);
|
||||||
hashCode.Add(LastShare);
|
hashCode.Add(LastShare);
|
||||||
hashCode.Add(Volume);
|
hashCode.Add(Volume);
|
||||||
@@ -303,11 +284,9 @@ namespace Content.Server.Atmos
|
|||||||
var newMixture = new GasMixture()
|
var newMixture = new GasMixture()
|
||||||
{
|
{
|
||||||
Moles = (float[])Moles.Clone(),
|
Moles = (float[])Moles.Clone(),
|
||||||
MolesArchived = (float[])MolesArchived.Clone(),
|
|
||||||
_temperature = _temperature,
|
_temperature = _temperature,
|
||||||
Immutable = Immutable,
|
Immutable = Immutable,
|
||||||
LastShare = LastShare,
|
LastShare = LastShare,
|
||||||
TemperatureArchived = TemperatureArchived,
|
|
||||||
Volume = Volume,
|
Volume = Volume,
|
||||||
};
|
};
|
||||||
return newMixture;
|
return newMixture;
|
||||||
|
|||||||
@@ -6,20 +6,5 @@ namespace Content.Server.Atmos
|
|||||||
public interface IGasMixtureHolder
|
public interface IGasMixtureHolder
|
||||||
{
|
{
|
||||||
public GasMixture Air { get; set; }
|
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 JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class GasDualPortVentPumpSystem : EntitySystem
|
public class GasDualPortVentPumpSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -34,24 +37,23 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off);
|
if (!vent.Enabled
|
||||||
|
|| !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||||
if (!vent.Enabled)
|
|| !nodeContainer.TryGetNode(vent.InletName, out PipeNode? inlet)
|
||||||
return;
|
|
||||||
|
|
||||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!nodeContainer.TryGetNode(vent.InletName, out PipeNode? inlet)
|
|
||||||
|| !nodeContainer.TryGetNode(vent.OutletName, out PipeNode? outlet))
|
|| !nodeContainer.TryGetNode(vent.OutletName, out PipeNode? outlet))
|
||||||
|
{
|
||||||
|
appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off);
|
||||||
return;
|
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.
|
// We're in an air-blocked tile... Do nothing.
|
||||||
if (environment == null)
|
if (environment == null)
|
||||||
|
{
|
||||||
|
appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (vent.PumpDirection == VentPumpDirection.Releasing)
|
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 transferMoles = pressureDelta * environment.Volume / inlet.Air.Temperature * Atmospherics.R;
|
||||||
var removed = inlet.Air.Remove(transferMoles);
|
var removed = inlet.Air.Remove(transferMoles);
|
||||||
atmosphereSystem.Merge(environment, removed);
|
_atmosphereSystem.Merge(environment, removed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (vent.PumpDirection == VentPumpDirection.Siphoning && environment.Pressure > 0f)
|
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);
|
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)
|
private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, AtmosDeviceUpdateEvent args)
|
||||||
{
|
{
|
||||||
var appearance = pump.Owner.GetComponentOrNull<AppearanceComponent>();
|
var appearance = pump.Owner.GetComponentOrNull<AppearanceComponent>();
|
||||||
appearance?.SetData(PressurePumpVisuals.Enabled, false);
|
|
||||||
|
|
||||||
if (!pump.Enabled)
|
if (!pump.Enabled
|
||||||
return;
|
|| !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||||
|
|| !nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|
||||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|
|
||||||
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
|
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
|
||||||
|
{
|
||||||
|
appearance?.SetData(PressurePumpVisuals.Enabled, false);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var outputStartingPressure = outlet.Air.Pressure;
|
var outputStartingPressure = outlet.Air.Pressure;
|
||||||
|
|
||||||
if (MathHelper.CloseTo(pump.TargetPressure, outputStartingPressure))
|
if (MathHelper.CloseTo(pump.TargetPressure, outputStartingPressure))
|
||||||
|
{
|
||||||
|
appearance?.SetData(PressurePumpVisuals.Enabled, false);
|
||||||
return; // No need to pump gas if target has been reached.
|
return; // No need to pump gas if target has been reached.
|
||||||
|
}
|
||||||
|
|
||||||
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
|
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class GasVolumePumpSystem : EntitySystem
|
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()
|
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.
|
// Some of the gas from the mixture leaks when overclocked.
|
||||||
if (pump.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)
|
if (tile != null)
|
||||||
{
|
{
|
||||||
var leaked = removed.RemoveRatio(pump.LeakRatio);
|
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
|
namespace Content.Server.Atmos.Piping.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds itself to a <see cref="IGridAtmosphereComponent"/> to be updated by.
|
/// Adds itself to a <see cref="IAtmosphereComponent"/> to be updated by.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class AtmosDeviceComponent : Component
|
public class AtmosDeviceComponent : Component
|
||||||
@@ -22,6 +22,19 @@ namespace Content.Server.Atmos.Piping.Components
|
|||||||
[DataField("requireAnchored")]
|
[DataField("requireAnchored")]
|
||||||
public bool RequireAnchored { get; private set; } = true;
|
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]
|
[ViewVariables]
|
||||||
public TimeSpan LastProcess { get; set; } = TimeSpan.Zero;
|
public TimeSpan LastProcess { get; set; } = TimeSpan.Zero;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Atmos.Piping.Components;
|
using Content.Server.Atmos.Piping.Components;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Piping.EntitySystems
|
namespace Content.Server.Atmos.Piping.EntitySystems
|
||||||
@@ -12,7 +12,13 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class AtmosDeviceSystem : EntitySystem
|
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()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -32,11 +38,24 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
|||||||
public void JoinAtmosphere(AtmosDeviceComponent component)
|
public void JoinAtmosphere(AtmosDeviceComponent component)
|
||||||
{
|
{
|
||||||
if (!CanJoinAtmosphere(component))
|
if (!CanJoinAtmosphere(component))
|
||||||
|
{
|
||||||
return;
|
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;
|
component.LastProcess = _gameTiming.CurTime;
|
||||||
|
|
||||||
@@ -45,8 +64,19 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
|||||||
|
|
||||||
public void LeaveAtmosphere(AtmosDeviceComponent component)
|
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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.JoinedSystem)
|
||||||
|
{
|
||||||
|
_joinedDevices.Remove(component);
|
||||||
|
component.JoinedSystem = false;
|
||||||
|
}
|
||||||
|
|
||||||
component.LastProcess = TimeSpan.Zero;
|
component.LastProcess = TimeSpan.Zero;
|
||||||
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceDisabledEvent(), false);
|
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceDisabledEvent(), false);
|
||||||
@@ -84,5 +114,22 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
|||||||
{
|
{
|
||||||
RejoinAtmosphere(component);
|
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 Content.Shared.Notification.Managers;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Piping.EntitySystems
|
namespace Content.Server.Atmos.Piping.EntitySystems
|
||||||
@@ -15,6 +16,8 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class AtmosUnsafeUnanchorSystem : EntitySystem
|
public class AtmosUnsafeUnanchorSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<AtmosUnsafeUnanchorComponent, BeforeUnanchoredEvent>(OnBeforeUnanchored);
|
SubscribeLocalEvent<AtmosUnsafeUnanchorComponent, BeforeUnanchoredEvent>(OnBeforeUnanchored);
|
||||||
@@ -26,7 +29,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
|||||||
if (!component.Enabled || !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
|
if (!component.Enabled || !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Get<AtmosphereSystem>().GetTileMixture(component.Owner.Transform.Coordinates) is not {} environment)
|
if (_atmosphereSystem.GetTileMixture(component.Owner.Transform.Coordinates) is not {} environment)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var node in nodes.Nodes.Values)
|
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))
|
if (!component.Enabled || !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
|
||||||
return;
|
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;
|
environment = GasMixture.SpaceGas;
|
||||||
|
|
||||||
var lost = 0f;
|
var lost = 0f;
|
||||||
@@ -71,10 +72,10 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
|||||||
{
|
{
|
||||||
if (node is not PipeNode pipe) continue;
|
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 Content.Shared.Atmos;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class GasMinerSystem : EntitySystem
|
public class GasMinerSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -21,9 +24,7 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
|||||||
|
|
||||||
private void OnMinerUpdated(EntityUid uid, GasMinerComponent miner, AtmosDeviceUpdateEvent args)
|
private void OnMinerUpdated(EntityUid uid, GasMinerComponent miner, AtmosDeviceUpdateEvent args)
|
||||||
{
|
{
|
||||||
var atmosphereSystem = Get<AtmosphereSystem>();
|
if (!CheckMinerOperation(miner, out var environment) || !miner.Enabled || !miner.SpawnGas.HasValue || miner.SpawnAmount <= 0f)
|
||||||
|
|
||||||
if (!CheckMinerOperation(atmosphereSystem, miner, out var environment) || !miner.Enabled || !miner.SpawnGas.HasValue || miner.SpawnAmount <= 0f)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Time to mine some gas.
|
// Time to mine some gas.
|
||||||
@@ -31,15 +32,15 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
|||||||
var merger = new GasMixture(1) { Temperature = miner.SpawnTemperature };
|
var merger = new GasMixture(1) { Temperature = miner.SpawnTemperature };
|
||||||
merger.SetMoles(miner.SpawnGas.Value, miner.SpawnAmount);
|
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.
|
// Space.
|
||||||
if (atmosphereSystem.IsTileSpace(miner.Owner.Transform.Coordinates))
|
if (_atmosphereSystem.IsTileSpace(miner.Owner.Transform.Coordinates))
|
||||||
{
|
{
|
||||||
miner.Broken = true;
|
miner.Broken = true;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ using Content.Server.Atmos.Piping.Trinary.Components;
|
|||||||
using Content.Server.NodeContainer;
|
using Content.Server.NodeContainer;
|
||||||
using Content.Server.NodeContainer.Nodes;
|
using Content.Server.NodeContainer.Nodes;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
|
using Content.Shared.Atmos.Piping;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
@@ -24,33 +26,35 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
|
|||||||
|
|
||||||
private void OnFilterUpdated(EntityUid uid, GasFilterComponent filter, AtmosDeviceUpdateEvent args)
|
private void OnFilterUpdated(EntityUid uid, GasFilterComponent filter, AtmosDeviceUpdateEvent args)
|
||||||
{
|
{
|
||||||
if (!filter.Enabled)
|
var appearance = filter.Owner.GetComponentOrNull<AppearanceComponent>();
|
||||||
return;
|
|
||||||
|
|
||||||
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;
|
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.
|
// 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;
|
var transferRatio = (float)(filter.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds) / inletNode.Air.Volume;
|
||||||
|
|
||||||
if (transferRatio <= 0)
|
if (transferRatio <= 0)
|
||||||
|
{
|
||||||
|
appearance?.SetData(FilterVisuals.Enabled, false);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var removed = inletNode.Air.RemoveRatio(transferRatio);
|
var removed = inletNode.Air.RemoveRatio(transferRatio);
|
||||||
|
|
||||||
if (filter.FilteredGas.HasValue)
|
if (filter.FilteredGas.HasValue)
|
||||||
{
|
{
|
||||||
|
appearance?.SetData(FilterVisuals.Enabled, true);
|
||||||
|
|
||||||
var filteredOut = new GasMixture() {Temperature = removed.Temperature};
|
var filteredOut = new GasMixture() {Temperature = removed.Temperature};
|
||||||
|
|
||||||
filteredOut.SetMoles(filter.FilteredGas.Value, removed.GetMoles(filter.FilteredGas.Value));
|
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