mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-10-02 09:07:27 +00:00
* Remove discard functionality from the ChemMaster This duplicates other methods of discarding chemicals, including the drain conventionally given to chemistry labs. Any problems it solves are miniscule compared to the severe UI error that is having a destructive action without confirmation attached to a mode, the most common source of user error. * Improve animal cube interactions (#31668) * Working on dehydrate recipes to renew animal cubes * Added remaining cube dehydration recipes * Add OnExamine to RehydratableSystem.cs, need to add functionality to check total volume vs current volume to determine if 'soaked' text needs to be displayed * Added logic to append text to the description when the wrong reagent has been added to a compressed animal cube * Re-adding deleted summary * Update default SolutionName to be correct * Remove cube recipes to prevent infinite nutrient generation * Add OnMicrowaved event to RehydratableSystem.cs in order to clear solution of cube when microwaved. CURRENT ERROR REQUIRES REFACTOR OF MICROWAVECOMPONENT * Subscribe to microwave event * Refactor of MicrowaveComponent.cs to be in shared folder, accessible to RehydratableSystem.cs. Updated imports * Modify cube to only dehydrate in microwave when cooked for at least 5 seconds * now using event to check access to fingerprint * Tweak - Reflective vest and Energy Katana should reflect only in correct slots (#31902) * ReflectComponentLogicFix Added bool InRightPlace and updated relevant system * Using SlotFlags * prevent friendly fire for dragon/carp (#32231) * add NoFriendlyFire * make zombies use NoFriendlyFire * no friendly fire for dragon * let dragon kill naughty fish and validhunting syndies * add button to print logprobe logs (#32255) * add EntityName at the bottom of LogProbe * add button to print logprobe logs * Add camera mod to diagnostic hud (#32254) * ai-glass * weh to protect against bad grammar * transmutation into diagnostic hud * Apply forensics when loading with an ammo box * inaprovaline metabolizes slower * :trollface: --------- Co-authored-by: Janet Blackquill <uhhadd@gmail.com> Co-authored-by: Preston Smith <Blackfoot03@outlook.com> Co-authored-by: YourUsername <you@example.com> Co-authored-by: BIGZi0348 <svalker0348@gmail.com> Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: ArZarLordOfMango <arzarchannel@gmail.com> Co-authored-by: themias <themias5@gmail.com> Co-authored-by: Alzore <blackern5000@gmail.com>
305 lines
13 KiB
C#
305 lines
13 KiB
C#
using Content.Server.Chemistry.Components;
|
|
using Content.Server.Labels;
|
|
using Content.Server.Popups;
|
|
using Content.Server.Storage.EntitySystems;
|
|
using Content.Shared.Administration.Logs;
|
|
using Content.Shared.Chemistry;
|
|
using Content.Shared.Chemistry.Components;
|
|
using Content.Shared.Chemistry.EntitySystems;
|
|
using Content.Shared.Chemistry.Reagent;
|
|
using Content.Shared.Containers.ItemSlots;
|
|
using Content.Shared.Database;
|
|
using Content.Shared.FixedPoint;
|
|
using Content.Shared.Storage;
|
|
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.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
|
|
namespace Content.Server.Chemistry.EntitySystems
|
|
{
|
|
|
|
/// <summary>
|
|
/// Contains all the server-side logic for ChemMasters.
|
|
/// <seealso cref="ChemMasterComponent"/>
|
|
/// </summary>
|
|
[UsedImplicitly]
|
|
public sealed class ChemMasterSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
|
[Dependency] private readonly AudioSystem _audioSystem = default!;
|
|
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
|
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
|
|
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
|
[Dependency] private readonly StorageSystem _storageSystem = default!;
|
|
[Dependency] private readonly LabelSystem _labelSystem = default!;
|
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
|
|
|
[ValidatePrototypeId<EntityPrototype>]
|
|
private const string PillPrototypeId = "Pill";
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<ChemMasterComponent, ComponentStartup>(SubscribeUpdateUiState);
|
|
SubscribeLocalEvent<ChemMasterComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
|
|
SubscribeLocalEvent<ChemMasterComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
|
|
SubscribeLocalEvent<ChemMasterComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
|
|
SubscribeLocalEvent<ChemMasterComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
|
|
|
|
SubscribeLocalEvent<ChemMasterComponent, ChemMasterSetPillTypeMessage>(OnSetPillTypeMessage);
|
|
SubscribeLocalEvent<ChemMasterComponent, ChemMasterReagentAmountButtonMessage>(OnReagentButtonMessage);
|
|
SubscribeLocalEvent<ChemMasterComponent, ChemMasterCreatePillsMessage>(OnCreatePillsMessage);
|
|
SubscribeLocalEvent<ChemMasterComponent, ChemMasterOutputToBottleMessage>(OnOutputToBottleMessage);
|
|
}
|
|
|
|
private void SubscribeUpdateUiState<T>(Entity<ChemMasterComponent> ent, ref T ev)
|
|
{
|
|
UpdateUiState(ent);
|
|
}
|
|
|
|
private void UpdateUiState(Entity<ChemMasterComponent> ent, bool updateLabel = false)
|
|
{
|
|
var (owner, chemMaster) = ent;
|
|
if (!_solutionContainerSystem.TryGetSolution(owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
|
|
return;
|
|
var inputContainer = _itemSlotsSystem.GetItemOrNull(owner, SharedChemMaster.InputSlotName);
|
|
var outputContainer = _itemSlotsSystem.GetItemOrNull(owner, SharedChemMaster.OutputSlotName);
|
|
|
|
var bufferReagents = bufferSolution.Contents;
|
|
var bufferCurrentVolume = bufferSolution.Volume;
|
|
|
|
var state = new ChemMasterBoundUserInterfaceState(
|
|
BuildInputContainerInfo(inputContainer), BuildOutputContainerInfo(outputContainer),
|
|
bufferReagents, bufferCurrentVolume, chemMaster.PillType, chemMaster.PillDosageLimit, updateLabel);
|
|
|
|
_userInterfaceSystem.SetUiState(owner, ChemMasterUiKey.Key, state);
|
|
}
|
|
|
|
private void OnSetPillTypeMessage(Entity<ChemMasterComponent> chemMaster, ref ChemMasterSetPillTypeMessage message)
|
|
{
|
|
// Ensure valid pill type. There are 20 pills selectable, 0-19.
|
|
if (message.PillType > SharedChemMaster.PillTypes - 1)
|
|
return;
|
|
|
|
chemMaster.Comp.PillType = message.PillType;
|
|
UpdateUiState(chemMaster);
|
|
ClickSound(chemMaster);
|
|
}
|
|
|
|
private void OnReagentButtonMessage(Entity<ChemMasterComponent> chemMaster, ref ChemMasterReagentAmountButtonMessage message)
|
|
{
|
|
// Ensure the amount corresponds to one of the reagent amount buttons.
|
|
if (!Enum.IsDefined(typeof(ChemMasterReagentAmount), message.Amount))
|
|
return;
|
|
|
|
TransferReagents(chemMaster, message.ReagentId, message.Amount.GetFixedPoint(), message.FromBuffer);
|
|
ClickSound(chemMaster);
|
|
}
|
|
|
|
private void TransferReagents(Entity<ChemMasterComponent> chemMaster, ReagentId id, FixedPoint2 amount, bool fromBuffer)
|
|
{
|
|
var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName);
|
|
if (container is null ||
|
|
!_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSoln, out var containerSolution) ||
|
|
!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (fromBuffer) // Buffer to container
|
|
{
|
|
amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume);
|
|
amount = bufferSolution.RemoveReagent(id, amount, preserveOrder: true);
|
|
_solutionContainerSystem.TryAddReagent(containerSoln.Value, id, amount, out var _);
|
|
}
|
|
else // Container to buffer
|
|
{
|
|
amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(id));
|
|
_solutionContainerSystem.RemoveReagent(containerSoln.Value, id, amount);
|
|
bufferSolution.AddReagent(id, amount);
|
|
}
|
|
|
|
UpdateUiState(chemMaster, updateLabel: true);
|
|
}
|
|
|
|
private void OnCreatePillsMessage(Entity<ChemMasterComponent> chemMaster, ref ChemMasterCreatePillsMessage message)
|
|
{
|
|
var user = message.Actor;
|
|
var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.OutputSlotName);
|
|
if (maybeContainer is not { Valid: true } container
|
|
|| !TryComp(container, out StorageComponent? storage))
|
|
{
|
|
return; // output can't fit pills
|
|
}
|
|
|
|
// Ensure the number is valid.
|
|
if (message.Number == 0 || !_storageSystem.HasSpace((container, storage)))
|
|
return;
|
|
|
|
// Ensure the amount is valid.
|
|
if (message.Dosage == 0 || message.Dosage > chemMaster.Comp.PillDosageLimit)
|
|
return;
|
|
|
|
// Ensure label length is within the character limit.
|
|
if (message.Label.Length > SharedChemMaster.LabelMaxLength)
|
|
return;
|
|
|
|
var needed = message.Dosage * message.Number;
|
|
if (!WithdrawFromBuffer(chemMaster, needed, user, out var withdrawal))
|
|
return;
|
|
|
|
_labelSystem.Label(container, message.Label);
|
|
|
|
for (var i = 0; i < message.Number; i++)
|
|
{
|
|
var item = Spawn(PillPrototypeId, Transform(container).Coordinates);
|
|
_storageSystem.Insert(container, item, out _, user: user, storage);
|
|
_labelSystem.Label(item, message.Label);
|
|
|
|
_solutionContainerSystem.EnsureSolutionEntity(item, SharedChemMaster.PillSolutionName,out var itemSolution ,message.Dosage);
|
|
if (!itemSolution.HasValue)
|
|
return;
|
|
|
|
_solutionContainerSystem.TryAddSolution(itemSolution.Value, withdrawal.SplitSolution(message.Dosage));
|
|
|
|
var pill = EnsureComp<PillComponent>(item);
|
|
pill.PillType = chemMaster.Comp.PillType;
|
|
Dirty(item, pill);
|
|
|
|
// Log pill creation by a user
|
|
_adminLogger.Add(LogType.Action, LogImpact.Low,
|
|
$"{ToPrettyString(user):user} printed {ToPrettyString(item):pill} {SharedSolutionContainerSystem.ToPrettyString(itemSolution.Value.Comp.Solution)}");
|
|
}
|
|
|
|
UpdateUiState(chemMaster);
|
|
ClickSound(chemMaster);
|
|
}
|
|
|
|
private void OnOutputToBottleMessage(Entity<ChemMasterComponent> chemMaster, ref ChemMasterOutputToBottleMessage message)
|
|
{
|
|
var user = message.Actor;
|
|
var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.OutputSlotName);
|
|
if (maybeContainer is not { Valid: true } container
|
|
|| !_solutionContainerSystem.TryGetSolution(container, SharedChemMaster.BottleSolutionName, out var soln, out var solution))
|
|
{
|
|
return; // output can't fit reagents
|
|
}
|
|
|
|
// 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(soln.Value, withdrawal);
|
|
|
|
// Log bottle creation by a user
|
|
_adminLogger.Add(LogType.Action, LogImpact.Low,
|
|
$"{ToPrettyString(user):user} bottled {ToPrettyString(container):bottle} {SharedSolutionContainerSystem.ToPrettyString(solution)}");
|
|
|
|
UpdateUiState(chemMaster);
|
|
ClickSound(chemMaster);
|
|
}
|
|
|
|
private bool WithdrawFromBuffer(
|
|
Entity<ChemMasterComponent> chemMaster,
|
|
FixedPoint2 neededVolume, EntityUid? user,
|
|
[NotNullWhen(returnValue: true)] out Solution? outputSolution)
|
|
{
|
|
outputSolution = null;
|
|
|
|
if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var solution))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (solution.Volume == 0)
|
|
{
|
|
if (user.HasValue)
|
|
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-empty-text"), user.Value);
|
|
return false;
|
|
}
|
|
|
|
// ReSharper disable once InvertIf
|
|
if (neededVolume > solution.Volume)
|
|
{
|
|
if (user.HasValue)
|
|
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-low-text"), user.Value);
|
|
return false;
|
|
}
|
|
|
|
outputSolution = solution.SplitSolution(neededVolume);
|
|
return true;
|
|
}
|
|
|
|
private void ClickSound(Entity<ChemMasterComponent> chemMaster)
|
|
{
|
|
_audioSystem.PlayPvs(chemMaster.Comp.ClickSound, chemMaster, AudioParams.Default.WithVolume(-2f));
|
|
}
|
|
|
|
private ContainerInfo? BuildInputContainerInfo(EntityUid? container)
|
|
{
|
|
if (container is not { Valid: true })
|
|
return null;
|
|
|
|
if (!TryComp(container, out FitsInDispenserComponent? fits)
|
|
|| !_solutionContainerSystem.TryGetSolution(container.Value, fits.Solution, out _, out var solution))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return BuildContainerInfo(Name(container.Value), solution);
|
|
}
|
|
|
|
private ContainerInfo? BuildOutputContainerInfo(EntityUid? container)
|
|
{
|
|
if (container is not { Valid: true })
|
|
return null;
|
|
|
|
var name = Name(container.Value);
|
|
{
|
|
if (_solutionContainerSystem.TryGetSolution(
|
|
container.Value, SharedChemMaster.BottleSolutionName, out _, out var solution))
|
|
{
|
|
return BuildContainerInfo(name, solution);
|
|
}
|
|
}
|
|
|
|
if (!TryComp(container, out StorageComponent? storage))
|
|
return null;
|
|
|
|
var pills = storage.Container.ContainedEntities.Select((Func<EntityUid, (string, FixedPoint2 quantity)>) (pill =>
|
|
{
|
|
_solutionContainerSystem.TryGetSolution(pill, SharedChemMaster.PillSolutionName, out _, out var solution);
|
|
var quantity = solution?.Volume ?? FixedPoint2.Zero;
|
|
return (Name(pill), quantity);
|
|
})).ToList();
|
|
|
|
return new ContainerInfo(name, _storageSystem.GetCumulativeItemAreas((container.Value, storage)), storage.Grid.GetArea())
|
|
{
|
|
Entities = pills
|
|
};
|
|
}
|
|
|
|
private static ContainerInfo BuildContainerInfo(string name, Solution solution)
|
|
{
|
|
return new ContainerInfo(name, solution.Volume, solution.MaxVolume)
|
|
{
|
|
Reagents = solution.Contents
|
|
};
|
|
}
|
|
}
|
|
}
|