Skip to content

Commit

Permalink
Merge pull request #382 from JustKekc/Thinkies
Browse files Browse the repository at this point in the history
вымученный МЕД АПДЕЙТ™
  • Loading branch information
PyotrIgn authored Feb 26, 2024
2 parents 07ffaf4 + d6fe206 commit 9d67800
Show file tree
Hide file tree
Showing 516 changed files with 6,868 additions and 381 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Content.Shared.Chemistry;
using Content.Shared.Containers.ItemSlots;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Content.Shared.ADT.Chemistry;

namespace Content.Client.ADT.Chemistry.UI
{
/// <summary>
/// Initializes a <see cref="ReagentAnalyzerWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public sealed class ReagentAnalyzerBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private ReagentAnalyzerWindow? _window;


public ReagentAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}

/// <summary>
/// Called each time an analyzer UI instance is opened. Generates the dispenser window and fills it with
/// relevant info. Sets the actions for static buttons.
/// </summary>
protected override void Open()
{
base.Open();

// Setup window layout/elements
_window = new()
{
Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName,
};

_window.OpenCentered();
_window.OnClose += Close;

// Setup static button actions.
_window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentAnalyzer.OutputSlotName));
_window.ClearButton.OnPressed += _ => SendMessage(new ReagentAnalyzerClearContainerSolutionMessage());
}

/// <summary>
/// Update the UI each time new state data is sent from the server.
/// </summary>
/// <param name="state">
/// Data of the <see cref="ReagentAnalyzerComponent"/> that this UI represents.
/// Sent from the server.
/// </param>
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);

var castState = (ReagentAnalyzerBoundUserInterfaceState) state;

_window?.UpdateState(castState); //Update window state
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

if (disposing)
{
_window?.Dispose();
}
}
}
}
35 changes: 35 additions & 0 deletions Content.Client/ADT/Chemistry/UI/ReagentAnalyzerWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc 'reagent-dispenser-bound-user-interface-title'}"
SetSize="300 300"
MinSize="300 300">
<BoxContainer Orientation="Vertical">
<Control MinSize="0 10"/>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'reagent-dispenser-window-container-label'}"/>
<Button Name="ClearButton"
Access="Public"
Text="{Loc 'reagent-dispenser-window-clear-button'}"
StyleClasses="OpenRight"/>
<Button Name="EjectButton"
Access="Public"
Text="{Loc 'reagent-dispenser-window-eject-button'}"
StyleClasses="OpenLeft"/>
</BoxContainer>
<Control MinSize="0 10"/>
<ScrollContainer HScrollEnabled="False" HorizontalExpand="True" VerticalExpand="True" MinSize="0 160">
<PanelContainer VerticalExpand="True"
SizeFlagsStretchRatio="6"
MinSize="0 150">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1b1b1e" />
</PanelContainer.PanelOverride>
<BoxContainer Name="ContainerInfo"
Orientation="Vertical"
HorizontalExpand="True">
<Label Text="{Loc 'reagent-dispenser-window-no-container-loaded-text'}"/>
</BoxContainer>
</PanelContainer>
</ScrollContainer>
</BoxContainer>
</DefaultWindow>
112 changes: 112 additions & 0 deletions Content.Client/ADT/Chemistry/UI/ReagentAnalyzerWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System.Linq;
using Content.Client.Stylesheets;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Reagent;
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.Prototypes;
using Content.Shared.ADT.Chemistry;
using static Robust.Client.UserInterface.Controls.BoxContainer;

namespace Content.Client.ADT.Chemistry.UI
{
/// <summary>
/// Client-side UI used to control a <see cref="ReagentAnalyzerComponent"/>.
/// </summary>
[GenerateTypedNameReferences]
public sealed partial class ReagentAnalyzerWindow : DefaultWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;

/// <summary>
/// Create and initialize the dispenser UI client-side. Creates the basic layout,
/// actual data isn't filled in until the server sends data about the dispenser.
/// </summary>
public ReagentAnalyzerWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}


/// <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 = (ReagentAnalyzerBoundUserInterfaceState) state;
UpdateContainerInfo(castState);

// Disable the Clear & Eject button if no beaker
ClearButton.Disabled = castState.OutputContainer is null;
EjectButton.Disabled = castState.OutputContainer is null;
}

/// <summary>
/// Update the fill state and list of reagents held by the current reagent container, if applicable.
/// <para>Also highlights a reagent if it's dispense button is being mouse hovered.</para>
/// </summary>
/// <param name="state">State data for the analyzer.</param>
/// <param name="highlightedReagentId">Prototype ID of the reagent whose dispense button is currently being mouse hovered,
/// or null if no button is being hovered.</param>
public void UpdateContainerInfo(ReagentAnalyzerBoundUserInterfaceState state, ReagentId? highlightedReagentId = null)
{
ContainerInfo.Children.Clear();

if (state.OutputContainer is null)
{
ContainerInfo.Children.Add(new Label {Text = Loc.GetString("reagent-dispenser-window-no-container-loaded-text") });
return;
}

ContainerInfo.Children.Add(new BoxContainer // Name of the container and its fill status (Ex: 44/100u)
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label {Text = $"{state.OutputContainer.DisplayName}: "},
new Label
{
Text = $"{state.OutputContainer.CurrentVolume}/{state.OutputContainer.MaxVolume}",
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
}
}
});

foreach (var (reagent, quantity) in state.OutputContainer.Reagents!)
{
// Try get to the prototype for the given reagent. This gives us its name.
var localizedName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? p)
? p.LocalizedName
: Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");

var nameLabel = new Label {Text = $"{localizedName}: "};
var quantityLabel = new Label
{
Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", quantity)),
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor},
};

// Check if the reagent is being moused over. If so, color it green.
if (reagent == highlightedReagentId) {
nameLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
quantityLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
}

ContainerInfo.Children.Add(new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
nameLabel,
quantityLabel,
}
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ await server.WaitAssertion(() =>
.ToDictionary(x => x, _ => false);
foreach (var (reagent, quantity) in solution.Contents)
{
Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.Prototype && x.Key.Value == quantity, out var foundProduct));
Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.Prototype && x.Key.Value == quantity, out var foundProduct), Is.True);
foundProductsMap[foundProduct.Value.Key] = true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Content.Server.ADT.Chemistry.EntitySystems;
using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Server.ADT.Chemistry.Components
{
/// <summary>
/// A thing that shows reagents in it.
/// </summary>
[RegisterComponent]
[Access(typeof(ReagentAnalyzerSystem))]
public sealed partial class ReagentAnalyzerComponent : Component
{
[DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
}
}
94 changes: 94 additions & 0 deletions Content.Server/ADT/Chemistry/ReagentAnalyzerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Content.Server.Administration.Logs;
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Database;
using JetBrains.Annotations;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using System.Linq;
using Content.Shared.ADT.Chemistry;
using Content.Server.ADT.Chemistry.Components;

namespace Content.Server.ADT.Chemistry.EntitySystems
{
/// <summary>
/// Contains all the server-side logic for reagent analyzer.
/// <seealso cref="ReagentAnalyzerComponent"/>
/// </summary>
[UsedImplicitly]
public sealed class ReagentAnalyzerSystem : EntitySystem
{
[Dependency] private readonly AudioSystem _audioSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<ReagentAnalyzerComponent, ComponentStartup>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentAnalyzerComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentAnalyzerComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentAnalyzerComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentAnalyzerComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);

SubscribeLocalEvent<ReagentAnalyzerComponent, ReagentAnalyzerClearContainerSolutionMessage>(OnClearContainerSolutionMessage);
}

private void SubscribeUpdateUiState<T>(Entity<ReagentAnalyzerComponent> ent, ref T ev)
{
UpdateUiState(ent);
}

private void UpdateUiState(Entity<ReagentAnalyzerComponent> reagentAnalyzer)
{
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentAnalyzer, SharedReagentAnalyzer.OutputSlotName);
var outputContainerInfo = BuildOutputContainerInfo(outputContainer);

var state = new ReagentAnalyzerBoundUserInterfaceState(outputContainerInfo);
_userInterfaceSystem.TrySetUiState(reagentAnalyzer, ReagentAnalyzerUiKey.Key, state);
}

private ContainerInfo? BuildOutputContainerInfo(EntityUid? container)
{
if (container is not { Valid: true })
return null;

if (_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var solution))
{
return new ContainerInfo(Name(container.Value), solution.Volume, solution.MaxVolume)
{
Reagents = solution.Contents
};
}

return null;
}


private void OnClearContainerSolutionMessage(Entity<ReagentAnalyzerComponent> reagentAnalyzer, ref ReagentAnalyzerClearContainerSolutionMessage message)
{
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentAnalyzer, SharedReagentAnalyzer.OutputSlotName);
if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _))
return;

_solutionContainerSystem.RemoveAllSolution(solution.Value);
UpdateUiState(reagentAnalyzer);
ClickSound(reagentAnalyzer);
}

private void ClickSound(Entity<ReagentAnalyzerComponent> reagentAnalyzer)
{
_audioSystem.PlayPvs(reagentAnalyzer.Comp.ClickSound, reagentAnalyzer, AudioParams.Default.WithVolume(-2f));
}
}
}
23 changes: 23 additions & 0 deletions Content.Server/Atmos/Rotting/EmbalmedSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Content.Shared.Atmos.Miasma;
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.Mobs.Systems;

namespace Content.Server.Atmos.Rotting;

public sealed partial class EmbalmedSystem : EntitySystem
{
[Dependency] private readonly MobStateSystem _mobState = default!;
public override void Initialize()
{
SubscribeLocalEvent<EmbalmedComponent, ExaminedEvent>(OnExamine);
base.Initialize();
}

private void OnExamine(EntityUid uid, EmbalmedComponent component, ExaminedEvent args)
{
if (!_mobState.IsDead(uid))
return;
args.PushMarkup(Loc.GetString("adt-rotting-embalmed"));
}
}
4 changes: 4 additions & 0 deletions Content.Server/Atmos/Rotting/RottingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Robust.Server.Containers;
using Robust.Shared.Physics.Components;
using Robust.Shared.Timing;
using Content.Shared.Atmos.Miasma;

namespace Content.Server.Atmos.Rotting;

Expand Down Expand Up @@ -94,6 +95,9 @@ public bool IsRotProgressing(EntityUid uid, PerishableComponent? perishable)
if (TryComp<MobStateComponent>(uid, out var mobState) && !_mobState.IsDead(uid, mobState))
return false;

if (HasComp<EmbalmedComponent>(uid))
return false;

if (_container.TryGetOuterContainer(uid, Transform(uid), out var container) &&
HasComp<AntiRottingContainerComponent>(container.Owner))
{
Expand Down
2 changes: 1 addition & 1 deletion Content.Server/Body/Systems/MetabolizerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganCompon

float scale = (float) mostToRemove / (float) rate;

// if it's possible for them to be dead, and they are,
// if it's possible for them to be dead, and they are (unless stated otherwise by the reagent), [Thanks to Gotimanga]
// then we shouldn't process any effects, but should probably
// still remove reagents
if (EntityManager.TryGetComponent<MobStateComponent>(solutionEntityUid.Value, out var state))
Expand Down
Loading

0 comments on commit 9d67800

Please sign in to comment.