Skip to content
This repository was archived by the owner on Dec 30, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake
2 changes: 2 additions & 0 deletions Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ protected override void Open()
(uint)_window.PillDosage.Value, (uint)_window.PillNumber.Value, _window.LabelLine));
_window.CreateBottleButton.OnPressed += _ => SendMessage(
new ChemMasterOutputToBottleMessage((uint)_window.BottleDosage.Value, _window.LabelLine));
_window.CreateCartridgeButton.OnPressed += _ => SendMessage(
new ChemMasterCreateCartridgeMessage((uint)_window.CartridgeDosage.Value, _window.LabelLine));

for (uint i = 0; i < _window.PillTypeButtons.Length; i++)
{
Expand Down
8 changes: 8 additions & 0 deletions Content.Client/Chemistry/UI/ChemMasterWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@
<SpinBox MinSize="100 0" Name="BottleDosage" Access="Public" Value="0" />
<Button MinSize="80 0" Name="CreateBottleButton" Access="Public" Text="{Loc 'chem-master-window-create-button'}" />
</BoxContainer>

<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-cartridges-label'}" />
<Control HorizontalExpand="True" MinSize="50 0" />
<Label Text="{Loc 'chem-master-window-dose-label'}" Margin="5 0 0 0" StyleClasses="LabelSecondaryColor" />
<SpinBox MinSize="100 0" Name="CartridgeDosage" Access="Public" Value="0" />
<Button MinSize="80 0" Name="CreateCartridgeButton" Access="Public" Text="{Loc 'chem-master-window-create-button'}" />
</BoxContainer>
</BoxContainer>
</PanelContainer>
</BoxContainer>
Expand Down
7 changes: 6 additions & 1 deletion Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
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.Client.Utility;
using Robust.Shared.Prototypes;
Expand Down Expand Up @@ -79,6 +78,7 @@ public ChemMasterWindow()
PillDosage.InitDefaultButtons();
PillNumber.InitDefaultButtons();
BottleDosage.InitDefaultButtons();
CartridgeDosage.InitDefaultButtons();

// Ensure label length is within the character limit.
LabelLineEdit.IsValid = s => s.Length <= SharedChemMaster.LabelMaxLength;
Expand Down Expand Up @@ -114,21 +114,26 @@ public void UpdateState(BoundUserInterfaceState state)
OutputEjectButton.Disabled = output is null;
CreateBottleButton.Disabled = output is null || !output.HoldsReagents;
CreatePillButton.Disabled = output is null || output.HoldsReagents;
CreateCartridgeButton.Disabled = false;

var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int();
var holdsReagents = output?.HoldsReagents ?? false;
var pillNumberMax = holdsReagents ? 0 : remainingCapacity;
var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
var cartridgeAmountMax = holdsReagents ? remainingCapacity : 0;

PillTypeButtons[castState.SelectedPillType].Pressed = true;
PillNumber.IsValid = x => x >= 0 && x <= pillNumberMax;
PillDosage.IsValid = x => x > 0 && x <= castState.PillDosageLimit;
BottleDosage.IsValid = x => x >= 0 && x <= bottleAmountMax;
CartridgeDosage.IsValid = x => x >= 0 && x <= cartridgeAmountMax;

if (PillNumber.Value > pillNumberMax)
PillNumber.Value = pillNumberMax;
if (BottleDosage.Value > bottleAmountMax)
BottleDosage.Value = bottleAmountMax;
if (CartridgeDosage.Value > cartridgeAmountMax)
CartridgeDosage.Value = cartridgeAmountMax;
}

/// <summary>
Expand Down
9 changes: 8 additions & 1 deletion Content.Server/Chemistry/Components/HyposprayComponent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio;

Expand Down Expand Up @@ -30,8 +31,14 @@ public sealed class HyposprayComponent : SharedHyposprayComponent

public override ComponentState GetComponentState()
{
var itemSlotSys = _entMan.EntitySysManager.GetEntitySystem<ItemSlotsSystem>();
var solutionSys = _entMan.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
return solutionSys.TryGetSolution(Owner, SolutionName, out var solution)

EntityUid? container = Owner;
if (SolutionSlot != null) {
container = itemSlotSys.GetItemOrNull(Owner, SolutionSlot);
}
return solutionSys.TryGetSolution(container, SolutionName, out var solution)
? new HyposprayComponentState(solution.Volume, solution.MaxVolume)
: new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero);
}
Expand Down
60 changes: 59 additions & 1 deletion Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Tag;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
Expand All @@ -34,8 +35,13 @@ public sealed class ChemMasterSystem : EntitySystem
[Dependency] private readonly StorageSystem _storageSystem = default!;
[Dependency] private readonly LabelSystem _labelSystem = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;

private const string PillPrototypeId = "Pill";
private const string CartridgePrototypeId = "ChemicalCartridge";

private const string CartridgeEmptyTag = "ChemicalCartridgeEmpty";
private const string CartridgeFullTag = "ChemicalCartridge";

public override void Initialize()
{
Expand All @@ -52,6 +58,7 @@ public override void Initialize()
SubscribeLocalEvent<ChemMasterComponent, ChemMasterReagentAmountButtonMessage>(OnReagentButtonMessage);
SubscribeLocalEvent<ChemMasterComponent, ChemMasterCreatePillsMessage>(OnCreatePillsMessage);
SubscribeLocalEvent<ChemMasterComponent, ChemMasterOutputToBottleMessage>(OnOutputToBottleMessage);
SubscribeLocalEvent<ChemMasterComponent, ChemMasterCreateCartridgeMessage>(OnCreateCartridgeMessage);
}

private void UpdateUiState(ChemMasterComponent chemMaster, bool updateLabel = false)
Expand Down Expand Up @@ -271,6 +278,53 @@ private void OnOutputToBottleMessage(
ClickSound(chemMaster);
}

private void OnCreateCartridgeMessage(
EntityUid uid, ChemMasterComponent chemMaster, ChemMasterCreateCartridgeMessage message)
{
var user = message.Session.AttachedEntity;
// Make sure there's an empty cartridge in the slot
var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.OutputSlotName);
if (maybeContainer is not { Valid: true } container
|| !_tagSystem.HasTag(container, CartridgeEmptyTag)
|| !_solutionContainerSystem.TryGetSolution(container, SharedChemMaster.CartridgeSolutionName, out var solution))
{
return;
}

// Ensure the amount is valid.
if (message.Dosage == 0 || message.Dosage > solution.AvailableVolume)
return;

// Ensure label length is within the character limit.
if (message.Label.Length > SharedChemMaster.LabelMaxLength)
return;

if (!WithdrawFromBuffer(chemMaster, message.Dosage, user, out var withdrawal))
return;

_labelSystem.Label(container, message.Label);
_solutionContainerSystem.TryAddSolution(container, solution, withdrawal);

_tagSystem.RemoveTag(container, CartridgeEmptyTag);
_tagSystem.AddTag(container, CartridgeFullTag);

if (user.HasValue)
{
// Log bottle creation by a user
_adminLogger.Add(LogType.Action, LogImpact.Low,
$"{ToPrettyString(user.Value):user} filled {ToPrettyString(container):cartridge} {SolutionContainerSystem.ToPrettyString(solution)}");
}
else
{
// Log bottle creation by magic? This should never happen... right?
_adminLogger.Add(LogType.Action, LogImpact.Low,
$"Unknown filled {ToPrettyString(container):cartridge} {SolutionContainerSystem.ToPrettyString(solution)}");
}

UpdateUiState(chemMaster);
ClickSound(chemMaster);
}

private bool WithdrawFromBuffer(
IComponent chemMaster,
FixedPoint2 neededVolume, EntityUid? user,
Expand Down Expand Up @@ -329,8 +383,12 @@ private void ClickSound(ChemMasterComponent chemMaster)

var name = Name(container.Value);
{
Solution? solution = null;

if (_solutionContainerSystem.TryGetSolution(
container.Value, SharedChemMaster.BottleSolutionName, out var solution))
container.Value, SharedChemMaster.BottleSolutionName, out solution)
|| _solutionContainerSystem.TryGetSolution(
container.Value, SharedChemMaster.CartridgeSolutionName, out solution))
{
return BuildContainerInfo(name, solution);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,30 @@
using Content.Shared.Tag;
using Content.Shared.Popups;
using Content.Shared.Timing;
using Robust.Shared.Player;
using Content.Shared.Containers.ItemSlots;
using Robust.Shared.Containers;
using Content.Shared.Examine;
using Robust.Shared.Prototypes;
using Content.Shared.Chemistry.Components;

namespace Content.Server.Chemistry.EntitySystems
{
public sealed partial class ChemistrySystem
{
[Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;

private void InitializeHypospray()
{
SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
SubscribeLocalEvent<HyposprayComponent, SolutionChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<HyposprayComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
SubscribeLocalEvent<HyposprayComponent, EntRemovedFromContainerMessage>(OnContainerModified);
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<HyposprayComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<HyposprayComponent, ExaminedEvent>(OnExamine);
}

private void OnStartup(EntityUid uid, HyposprayComponent component, ComponentStartup args)
Expand All @@ -50,6 +59,11 @@ private void OnSolutionChange(EntityUid uid, HyposprayComponent component, Solut
Dirty(component);
}

private void OnContainerModified(EntityUid uid, HyposprayComponent component, ContainerModifiedMessage args)
{
Dirty(component);
}

public void OnAfterInteract(EntityUid uid, HyposprayComponent component, AfterInteractEvent args)
{
if (!args.CanReach)
Expand Down Expand Up @@ -88,9 +102,7 @@ public bool TryDoInject(EntityUid uid, EntityUid? target, EntityUid user, Hyposp
if (tag.Tags.Contains("HardsuitOn"))
{
if (target == null) return false;
var taget = (EntityUid) target;

_popup.PopupEntity("You cant get the needle to go through the thick plating!", taget, user, PopupType.MediumCaution);
_popup.PopupEntity("You cant get the needle to go through the thick plating!", target.Value, user, PopupType.MediumCaution);
return false;
}
}
Expand All @@ -103,7 +115,18 @@ public bool TryDoInject(EntityUid uid, EntityUid? target, EntityUid user, Hyposp
target = user;
}

_solutions.TryGetSolution(uid, component.SolutionName, out var hypoSpraySolution);
EntityUid? container = uid;

if (component.SolutionSlot != null) {
container = _itemSlotsSystem.GetItemOrNull(uid, component.SolutionSlot);
}

if (container == null) {
_popup.PopupCursor(Loc.GetString("hypospray-component-no-container-message"), user);
return false;
}

_solutions.TryGetSolution(container, component.SolutionName, out var hypoSpraySolution);

if (hypoSpraySolution == null || hypoSpraySolution.Volume == 0)
{
Expand Down Expand Up @@ -145,13 +168,15 @@ public bool TryDoInject(EntityUid uid, EntityUid? target, EntityUid user, Hyposp
}

// Move units from attackSolution to targetSolution
var removedSolution = _solutions.SplitSolution(uid, hypoSpraySolution, realTransferAmount);
var removedSolution = _solutions.SplitSolution(container.Value, hypoSpraySolution, realTransferAmount);

if (!targetSolution.CanAddSolution(removedSolution))
return true;
_reactiveSystem.DoEntityReaction(target.Value, removedSolution, ReactionMethod.Injection);
_solutions.TryAddSolution(target.Value, targetSolution, removedSolution);

Dirty(component);

//same logtype as syringes...
_adminLogger.Add(LogType.ForceFeed, $"{_entMan.ToPrettyString(user):user} injected {_entMan.ToPrettyString(target.Value):target} with a solution {SolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {_entMan.ToPrettyString(uid):using}");

Expand All @@ -166,5 +191,52 @@ static bool EligibleEntity([NotNullWhen(true)] EntityUid? entity, IEntityManager
return entMan.HasComponent<SolutionContainerManagerComponent>(entity)
&& entMan.HasComponent<MobStateComponent>(entity);
}

private void OnExamine(EntityUid uid, HyposprayComponent component, ExaminedEvent args)
{
if (component.SolutionSlot != null) {
var container = _itemSlotsSystem.GetItemOrNull(uid, component.SolutionSlot);
if (container == null) {
args.PushText(Loc.GetString("hypospray-component-on-examine-no-container"));
return;
}

if (!TryComp(container, out ExaminableSolutionComponent? examinableComponent))
return;

// Mostly copied from SolutionContainerSystem.OnExamineSolution

SolutionContainerManagerComponent? solutionsManager = null;
if (!Resolve(container.Value, ref solutionsManager)
|| !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solutionHolder))
return;

var primaryReagent = solutionHolder.GetPrimaryReagentId();

if (string.IsNullOrEmpty(primaryReagent))
{
args.PushText(Loc.GetString("shared-solution-container-component-on-examine-empty-container"));
return;
}

if (!_prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? proto))
{
Logger.Error(
$"{nameof(Solution)} could not find the prototype associated with {primaryReagent}.");
return;
}

var colorHex = solutionHolder.GetColor(_prototypeManager)
.ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable.
var messageString = "shared-solution-container-component-on-examine-main-text";

args.PushMarkup(Loc.GetString(messageString,
("color", colorHex),
("wordedAmount", Loc.GetString(solutionHolder.Contents.Count == 1
? "shared-solution-container-component-on-examine-worded-amount-one-reagent"
: "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
("desc", proto.LocalizedPhysicalDescription)));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace Content.Shared.Chemistry.Components
[NetworkedComponent()]
public abstract class SharedHyposprayComponent : Component
{
[DataField("solutionSlot")]
public string? SolutionSlot = null;
[DataField("solutionName")]
public string SolutionName = "hypospray";
}
Expand Down
14 changes: 14 additions & 0 deletions Content.Shared/Chemistry/SharedChemMaster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public sealed class SharedChemMaster
public const string OutputSlotName = "outputSlot";
public const string PillSolutionName = "food";
public const string BottleSolutionName = "drink";
public const string CartridgeSolutionName = "cartridge";
public const uint LabelMaxLength = 50;
}

Expand Down Expand Up @@ -83,6 +84,19 @@ public ChemMasterOutputToBottleMessage(uint dosage, string label)
}
}

[Serializable, NetSerializable]
public sealed class ChemMasterCreateCartridgeMessage : BoundUserInterfaceMessage
{
public readonly uint Dosage;
public readonly string Label;

public ChemMasterCreateCartridgeMessage(uint dosage, string label)
{
Dosage = dosage;
Label = label;
}
}

public enum ChemMasterMode
{
Transfer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ chem-master-window-pills-number-label = Count:
chem-master-window-dose-label = Dose (u):
chem-master-window-create-button = Create
chem-master-window-bottles-label = Bottles:
chem-master-window-cartridges-label = Cartridges:
chem-master-window-unknown-reagent-text = Unknown reagent
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ hypospray-volume-text = Volume: [color=white]{$currentVolume}/{$totalVolume}[/co
hypospray-component-inject-other-message = You inject {$other}.
hypospray-component-inject-self-message = You inject yourself.
hypospray-component-inject-self-clumsy-message = Oops! You injected yourself.
hypospray-component-no-container-message = It has no container to draw from!
hypospray-component-empty-message = It's empty!
hypospray-component-feel-prick-message = You feel a tiny prick!
hypospray-component-transfer-already-full-message = {$owner} is already full!
hypospray-cant-inject = Can't inject into {$target}!
hypospray-component-on-examine-no-container = It has no solution container.
Loading