mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 18:31:17 +03:00
Merge remote-tracking branch 'upstream/master' into upstream-sync
# Conflicts: # Content.Client/Audio/ContentAudioSystem.cs # Content.Client/Options/UI/Tabs/AudioTab.xaml # Content.Client/Options/UI/Tabs/AudioTab.xaml.cs # Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs # Resources/Prototypes/Datasets/ion_storm.yml # Resources/ServerInfo/Guidebook/Security/Forensics.xml # Resources/Textures/Clothing/Head/Hats/brownfedora.rsi/equipped-HELMET.png # Resources/Textures/Clothing/Head/Hats/brownfedora.rsi/icon.png # Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json # Resources/Textures/Structures/Doors/Airlocks/Glass/atmospherics.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/basic.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/cargo.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/command.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/engineering.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/maint.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/medical.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/science.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/security.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Glass/virology.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/atmospherics.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/basic.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/cargo.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/command.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/engineering.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/freezer.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/maint.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/medical.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/science.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/security.rsi/assembly.png # Resources/Textures/Structures/Doors/Airlocks/Standard/virology.rsi/assembly.png
This commit is contained in:
commit
8ba54ec466
660 changed files with 411196 additions and 289714 deletions
|
|
@ -4,9 +4,6 @@ using Robust.Client.Console;
|
|||
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 Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Administration.UI.ManageSolutions
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
|||
private NetEntity _target = NetEntity.Invalid;
|
||||
private string? _selectedSolution;
|
||||
private AddReagentWindow? _addReagentWindow;
|
||||
private Dictionary<string, Solution>? _solutions;
|
||||
private Dictionary<string, EntityUid>? _solutions;
|
||||
|
||||
public EditSolutionsWindow()
|
||||
{
|
||||
|
|
@ -60,9 +60,11 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
|||
if (_selectedSolution == null || _solutions == null)
|
||||
return;
|
||||
|
||||
if (!_solutions.TryGetValue(_selectedSolution, out var solution))
|
||||
if (!_solutions.TryGetValue(_selectedSolution, out var solutionId) ||
|
||||
!_entityManager.TryGetComponent(solutionId, out SolutionComponent? solutionComp))
|
||||
return;
|
||||
|
||||
var solution = solutionComp.Solution;
|
||||
UpdateVolumeBox(solution);
|
||||
UpdateThermalBox(solution);
|
||||
|
||||
|
|
@ -198,10 +200,13 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
|||
/// </summary>
|
||||
private void SetReagent(FloatSpinBox.FloatSpinBoxEventArgs args, string prototype)
|
||||
{
|
||||
if (_solutions == null || _selectedSolution == null)
|
||||
if (_solutions == null || _selectedSolution == null ||
|
||||
!_solutions.TryGetValue(_selectedSolution, out var solutionId) ||
|
||||
!_entityManager.TryGetComponent(solutionId, out SolutionComponent? solutionComp))
|
||||
return;
|
||||
|
||||
var current = _solutions[_selectedSolution].GetTotalPrototypeQuantity(prototype);
|
||||
var solution = solutionComp.Solution;
|
||||
var current = solution.GetTotalPrototypeQuantity(prototype);
|
||||
var delta = args.Value - current.Float();
|
||||
|
||||
if (MathF.Abs(delta) < 0.01)
|
||||
|
|
@ -275,22 +280,38 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
|||
/// <summary>
|
||||
/// Update the solution options.
|
||||
/// </summary>
|
||||
public void UpdateSolutions(Dictionary<string, Solution>? solutions)
|
||||
public void UpdateSolutions(List<(string, NetEntity)>? solutions)
|
||||
{
|
||||
SolutionOption.Clear();
|
||||
_solutions = solutions;
|
||||
|
||||
if (solutions is { Count: > 0 })
|
||||
{
|
||||
if (_solutions is { Count: > 0 })
|
||||
_solutions.Clear();
|
||||
else
|
||||
_solutions = new(solutions.Count);
|
||||
|
||||
foreach (var (name, netSolution) in solutions)
|
||||
{
|
||||
if (_entityManager.TryGetEntity(netSolution, out var solution))
|
||||
_solutions.Add(name, solution.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
_solutions = null;
|
||||
|
||||
if (_solutions == null)
|
||||
return;
|
||||
|
||||
int i = 0;
|
||||
foreach (var solution in _solutions.Keys)
|
||||
int selectedIndex = 0; // Default to the first solution if none are found.
|
||||
foreach (var (name, _) in _solutions)
|
||||
{
|
||||
SolutionOption.AddItem(solution, i);
|
||||
SolutionOption.SetItemMetadata(i, solution);
|
||||
SolutionOption.AddItem(name, i);
|
||||
SolutionOption.SetItemMetadata(i, name);
|
||||
|
||||
if (solution == _selectedSolution)
|
||||
SolutionOption.Select(i);
|
||||
if (name == _selectedSolution)
|
||||
selectedIndex = i;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
|
@ -300,14 +321,11 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
|||
// No applicable solutions
|
||||
Close();
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectedSolution == null || !_solutions.ContainsKey(_selectedSolution))
|
||||
{
|
||||
// the previously selected solution is no longer valid.
|
||||
SolutionOption.Select(0);
|
||||
_selectedSolution = (string?) SolutionOption.SelectedMetadata;
|
||||
}
|
||||
SolutionOption.Select(selectedIndex);
|
||||
_selectedSolution = (string?) SolutionOption.SelectedMetadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
98
Content.Client/Audio/AudioUIController.cs
Normal file
98
Content.Client/Audio/AudioUIController.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
using Content.Shared.CCVar;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Audio.Sources;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Audio;
|
||||
|
||||
public sealed class AudioUIController : UIController
|
||||
{
|
||||
[Dependency] private readonly IAudioManager _audioManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IResourceCache _cache = default!;
|
||||
|
||||
private float _interfaceGain;
|
||||
private IAudioSource? _clickSource;
|
||||
private IAudioSource? _hoverSource;
|
||||
|
||||
private const float ClickGain = 0.25f;
|
||||
private const float HoverGain = 0.05f;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
/*
|
||||
* This exists to load UI sounds outside of the game sim.
|
||||
*/
|
||||
|
||||
// No unsub coz never shuts down until program exit.
|
||||
_configManager.OnValueChanged(CCVars.UIClickSound, SetClickSound, true);
|
||||
_configManager.OnValueChanged(CCVars.UIHoverSound, SetHoverSound, true);
|
||||
|
||||
_configManager.OnValueChanged(CCVars.InterfaceVolume, SetInterfaceVolume, true);
|
||||
}
|
||||
|
||||
private void SetInterfaceVolume(float obj)
|
||||
{
|
||||
_interfaceGain = obj;
|
||||
|
||||
if (_clickSource != null)
|
||||
{
|
||||
_clickSource.Gain = ClickGain * _interfaceGain;
|
||||
}
|
||||
|
||||
if (_hoverSource != null)
|
||||
{
|
||||
_hoverSource.Gain = HoverGain * _interfaceGain;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetClickSound(string value)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
var resource = _cache.GetResource<AudioResource>(value);
|
||||
var source =
|
||||
_audioManager.CreateAudioSource(resource);
|
||||
|
||||
if (source != null)
|
||||
{
|
||||
source.Gain = ClickGain * _interfaceGain;
|
||||
source.Global = true;
|
||||
}
|
||||
|
||||
_clickSource = source;
|
||||
UIManager.SetClickSound(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.SetClickSound(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetHoverSound(string value)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
var hoverResource = _cache.GetResource<AudioResource>(value);
|
||||
var hoverSource =
|
||||
_audioManager.CreateAudioSource(hoverResource);
|
||||
|
||||
if (hoverSource != null)
|
||||
{
|
||||
hoverSource.Gain = HoverGain * _interfaceGain;
|
||||
hoverSource.Global = true;
|
||||
}
|
||||
|
||||
_hoverSource = hoverSource;
|
||||
UIManager.SetHoverSound(hoverSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.SetHoverSound(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,18 @@
|
|||
using Content.Shared.Audio;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using AudioComponent = Robust.Shared.Audio.Components.AudioComponent;
|
||||
|
||||
namespace Content.Client.Audio;
|
||||
|
||||
public sealed partial class ContentAudioSystem : SharedContentAudioSystem
|
||||
{
|
||||
[Dependency] private readonly IAudioManager _audioManager = default!;
|
||||
[Dependency] private readonly IResourceCache _cache = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
|
||||
// Need how much volume to change per tick and just remove it when it drops below "0"
|
||||
private readonly Dictionary<EntityUid, float> _fadingOut = new();
|
||||
|
||||
|
|
@ -32,6 +35,7 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
|
|||
public const float AmbienceMultiplier = 3f;
|
||||
public const float AmbientMusicMultiplier = 3f;
|
||||
public const float LobbyMultiplier = 3f;
|
||||
public const float InterfaceMultiplier = 2f;
|
||||
public const float TtsMultiplier = 3f; // Corvax-TTS
|
||||
|
||||
public override void Initialize()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Content.Client.Rotation;
|
||||
using Content.Shared.Buckle;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Rotation;
|
||||
using Content.Shared.Vehicle.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
|
|
@ -56,17 +57,15 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||
if (!TryComp<RotationVisualsComponent>(uid, out var rotVisuals))
|
||||
return;
|
||||
|
||||
if (!Appearance.TryGetData<int>(uid, StrapVisuals.RotationAngle, out var angle, args.Component) ||
|
||||
!Appearance.TryGetData<bool>(uid, BuckleVisuals.Buckled, out var buckled, args.Component) ||
|
||||
if (!Appearance.TryGetData<bool>(uid, BuckleVisuals.Buckled, out var buckled, args.Component) ||
|
||||
!buckled ||
|
||||
args.Sprite == null)
|
||||
{
|
||||
_rotationVisualizerSystem.SetHorizontalAngle(uid, rotVisuals.DefaultRotation, rotVisuals);
|
||||
_rotationVisualizerSystem.SetHorizontalAngle((uid, rotVisuals), rotVisuals.DefaultRotation);
|
||||
return;
|
||||
}
|
||||
|
||||
// Animate strapping yourself to something at a given angle
|
||||
_rotationVisualizerSystem.SetHorizontalAngle(uid, Angle.FromDegrees(angle), rotVisuals);
|
||||
// TODO: Dump this when buckle is better
|
||||
_rotationVisualizerSystem.AnimateSpriteRotation(uid, args.Sprite, rotVisuals.HorizontalRotation, 0.125f);
|
||||
}
|
||||
|
|
|
|||
28
Content.Client/CartridgeLoader/Cartridges/LogProbeUi.cs
Normal file
28
Content.Client/CartridgeLoader/Cartridges/LogProbeUi.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Content.Client.UserInterface.Fragments;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.CartridgeLoader.Cartridges;
|
||||
|
||||
public sealed partial class LogProbeUi : UIFragment
|
||||
{
|
||||
private LogProbeUiFragment? _fragment;
|
||||
|
||||
public override Control GetUIFragmentRoot()
|
||||
{
|
||||
return _fragment!;
|
||||
}
|
||||
|
||||
public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
|
||||
{
|
||||
_fragment = new LogProbeUiFragment();
|
||||
}
|
||||
|
||||
public override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
if (state is not LogProbeUiState logProbeUiState)
|
||||
return;
|
||||
|
||||
_fragment?.UpdateState(logProbeUiState.PulledLogs);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Margin="4"
|
||||
Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="NumberLabel"
|
||||
Align="Center"
|
||||
SetWidth="60"
|
||||
ClipText="True"/>
|
||||
<Label Name="TimeLabel"
|
||||
Align="Center"
|
||||
SetWidth="280"
|
||||
ClipText="True"/>
|
||||
<Label Name="AccessorLabel"
|
||||
Align="Center"
|
||||
SetWidth="110"
|
||||
ClipText="True"/>
|
||||
</BoxContainer>
|
||||
<customControls:HSeparator Margin="0 5 0 5"/>
|
||||
</BoxContainer>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.CartridgeLoader.Cartridges;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LogProbeUiEntry : BoxContainer
|
||||
{
|
||||
public LogProbeUiEntry(int numberLabel, string timeText, string accessorText)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
NumberLabel.Text = numberLabel.ToString();
|
||||
TimeLabel.Text = timeText;
|
||||
AccessorLabel.Text = accessorText;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<cartridges:LogProbeUiFragment xmlns="https://spacestation14.io"
|
||||
xmlns:cartridges="clr-namespace:Content.Client.CartridgeLoader.Cartridges"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Orientation="Vertical"
|
||||
VerticalExpand="True">
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#000000FF"
|
||||
BorderColor="#5a5a5a"
|
||||
BorderThickness="0 0 0 1"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Horizontal" Align="Center" Margin="8">
|
||||
<Label HorizontalExpand="True" Text="{Loc 'log-probe-label-number'}"/>
|
||||
<Label HorizontalExpand="True" Text="{Loc 'log-probe-label-time'}"/>
|
||||
<Label HorizontalExpand="True" Text="{Loc 'log-probe-label-accessor'}"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<ScrollContainer VerticalExpand="True" HScrollEnabled="True">
|
||||
<BoxContainer Orientation="Vertical" Name="ProbedDeviceContainer"/>
|
||||
</ScrollContainer>
|
||||
</cartridges:LogProbeUiFragment>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.CartridgeLoader.Cartridges;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LogProbeUiFragment : BoxContainer
|
||||
{
|
||||
public LogProbeUiFragment()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void UpdateState(List<PulledAccessLog> logs)
|
||||
{
|
||||
ProbedDeviceContainer.RemoveAllChildren();
|
||||
|
||||
//Reverse the list so the oldest entries appear at the bottom
|
||||
logs.Reverse();
|
||||
|
||||
var count = 1;
|
||||
foreach (var log in logs)
|
||||
{
|
||||
AddAccessLog(log, count);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAccessLog(PulledAccessLog log, int numberLabelText)
|
||||
{
|
||||
var timeLabelText = TimeSpan.FromSeconds(Math.Truncate(log.Time.TotalSeconds)).ToString();
|
||||
var accessorLabelText = log.Accessor;
|
||||
var entry = new LogProbeUiEntry(numberLabelText, timeLabelText, accessorLabelText);
|
||||
|
||||
ProbedDeviceContainer.AddChild(entry);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Client.Chemistry.Containers.EntitySystems;
|
||||
|
||||
public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem
|
||||
{
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Rounding;
|
||||
using Robust.Client.GameObjects;
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@ namespace Content.Client.Construction.UI
|
|||
if (IsAtFront)
|
||||
{
|
||||
WindowOpen = false;
|
||||
_uiManager.GetActiveUIWidget<GameTopMenuBar>().CraftingButton.Pressed = false; // This does not call CraftingButtonToggled
|
||||
_uiManager.GetActiveUIWidget<GameTopMenuBar>().CraftingButton.SetClickPressed(false); // This does not call CraftingButtonToggled
|
||||
}
|
||||
else
|
||||
_constructionView.MoveToFront();
|
||||
|
|
@ -402,7 +402,7 @@ namespace Content.Client.Construction.UI
|
|||
else
|
||||
{
|
||||
WindowOpen = true;
|
||||
_uiManager.GetActiveUIWidget<GameTopMenuBar>().CraftingButton.Pressed = true; // This does not call CraftingButtonToggled
|
||||
_uiManager.GetActiveUIWidget<GameTopMenuBar>().CraftingButton.SetClickPressed(true); // This does not call CraftingButtonToggled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Robust.Client.Graphics;
|
|||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
|
@ -18,6 +19,7 @@ public sealed class ExplosionOverlaySystem : EntitySystem
|
|||
[Dependency] private readonly IResourceCache _resCache = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _lights = default!;
|
||||
[Dependency] private readonly IMapManager _mapMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -65,15 +67,20 @@ public sealed class ExplosionOverlaySystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
// spawn in a client-side light source at the epicenter
|
||||
var lightEntity = Spawn("ExplosionLight", component.Epicenter);
|
||||
var light = _lights.EnsureLight(lightEntity);
|
||||
// Map may have been deleted.
|
||||
if (_mapMan.MapExists(component.Epicenter.MapId))
|
||||
{
|
||||
// spawn in a client-side light source at the epicenter
|
||||
var lightEntity = Spawn("ExplosionLight", component.Epicenter);
|
||||
var light = _lights.EnsureLight(lightEntity);
|
||||
|
||||
_lights.SetRadius(lightEntity, component.Intensity.Count, light);
|
||||
_lights.SetEnergy(lightEntity, component.Intensity.Count, light);
|
||||
_lights.SetColor(lightEntity, type.LightColor, light);
|
||||
textures.LightEntity = lightEntity;
|
||||
}
|
||||
|
||||
_lights.SetRadius(lightEntity, component.Intensity.Count, light);
|
||||
_lights.SetEnergy(lightEntity, component.Intensity.Count, light);
|
||||
_lights.SetColor(lightEntity, type.LightColor, light);
|
||||
|
||||
textures.LightEntity = lightEntity;
|
||||
textures.FireColor = type.FireColor;
|
||||
textures.IntensityPerState = type.IntensityPerState;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
using Content.Client.Movement.Systems;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Eye.Blinding;
|
||||
using Content.Shared.Eye.Blinding.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
|
||||
namespace Content.Client.Eye.Blinding
|
||||
{
|
||||
|
|
@ -11,9 +14,8 @@ namespace Content.Client.Eye.Blinding
|
|||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] IEntityManager _entityManager = default!;
|
||||
[Dependency] ILightManager _lightManager = default!;
|
||||
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly ILightManager _lightManager = default!;
|
||||
|
||||
public override bool RequestScreenTexture => true;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
|
@ -30,13 +32,13 @@ namespace Content.Client.Eye.Blinding
|
|||
}
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(_playerManager.LocalPlayer?.ControlledEntity, out EyeComponent? eyeComp))
|
||||
if (!_entityManager.TryGetComponent(_playerManager.LocalSession?.AttachedEntity, out EyeComponent? eyeComp))
|
||||
return false;
|
||||
|
||||
if (args.Viewport.Eye != eyeComp.Eye)
|
||||
return false;
|
||||
|
||||
var playerEntity = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
var playerEntity = _playerManager.LocalSession?.AttachedEntity;
|
||||
|
||||
if (playerEntity == null)
|
||||
return false;
|
||||
|
|
@ -64,6 +66,11 @@ namespace Content.Client.Eye.Blinding
|
|||
if (ScreenTexture == null)
|
||||
return;
|
||||
|
||||
var playerEntity = _playerManager.LocalSession?.AttachedEntity;
|
||||
|
||||
if (playerEntity == null)
|
||||
return;
|
||||
|
||||
if (!_blindableComponent.GraceFrame)
|
||||
{
|
||||
_blindableComponent.LightSetup = true; // Ok we touched the lights
|
||||
|
|
@ -73,6 +80,10 @@ namespace Content.Client.Eye.Blinding
|
|||
_blindableComponent.GraceFrame = false;
|
||||
}
|
||||
|
||||
if (_entityManager.TryGetComponent<EyeComponent>(playerEntity, out var content))
|
||||
{
|
||||
_circleMaskShader?.SetParameter("ZOOM", content.Zoom.X);
|
||||
}
|
||||
_greyscaleShader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
|
||||
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Kitchen;
|
||||
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.Prototypes;
|
||||
|
||||
|
|
@ -15,7 +13,7 @@ namespace Content.Client.Kitchen.UI
|
|||
public sealed partial class GrinderMenu : FancyWindow
|
||||
{
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IPrototypeManager _prototypeManager ;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
private readonly ReagentGrinderBoundUserInterface _owner;
|
||||
|
||||
private readonly Dictionary<int, EntityUid> _chamberVisualContents = new();
|
||||
|
|
@ -122,8 +120,8 @@ namespace Content.Client.Kitchen.UI
|
|||
{
|
||||
foreach (var (reagent, quantity) in reagents)
|
||||
{
|
||||
var reagentName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto)
|
||||
? Loc.GetString($"{quantity} {proto.LocalizedName}")
|
||||
var reagentName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto)
|
||||
? Loc.GetString($"{quantity} {proto.LocalizedName}")
|
||||
: "???";
|
||||
BeakerContentBox.BoxContents.AddItem(reagentName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Graphics;
|
||||
|
||||
namespace Content.Client.Kitchen.UI
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,6 +74,19 @@
|
|||
<Label Name="LobbyVolumeLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
|
||||
<Label Text="{Loc 'ui-options-interface-volume'}" HorizontalExpand="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="InterfaceVolumeSlider"
|
||||
MinValue="0"
|
||||
MaxValue="100"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="InterfaceVolumeLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
|
||||
<Label Text="{Loc 'ui-options-tts-volume'}" HorizontalExpand="True" />
|
||||
<Control MinSize="8 0" />
|
||||
|
|
|
|||
|
|
@ -3,13 +3,10 @@ using Content.Shared.CCVar;
|
|||
using Content.Shared.Corvax.CCCVars;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Range = Robust.Client.UserInterface.Controls.Range;
|
||||
|
||||
|
|
@ -40,6 +37,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
AmbienceVolumeSlider.OnValueChanged += OnAmbienceVolumeSliderChanged;
|
||||
AmbienceSoundsSlider.OnValueChanged += OnAmbienceSoundsSliderChanged;
|
||||
LobbyVolumeSlider.OnValueChanged += OnLobbyVolumeSliderChanged;
|
||||
InterfaceVolumeSlider.OnValueChanged += OnInterfaceVolumeSliderChanged;
|
||||
TtsVolumeSlider.OnValueChanged += OnTtsVolumeSliderChanged; // Corvax-TTS
|
||||
LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled;
|
||||
RestartSoundsCheckBox.OnToggled += OnRestartSoundsCheckToggled;
|
||||
|
|
@ -61,6 +59,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
AmbientMusicVolumeSlider.OnValueChanged -= OnAmbientMusicVolumeSliderChanged;
|
||||
AmbienceVolumeSlider.OnValueChanged -= OnAmbienceVolumeSliderChanged;
|
||||
LobbyVolumeSlider.OnValueChanged -= OnLobbyVolumeSliderChanged;
|
||||
InterfaceVolumeSlider.OnValueChanged -= OnInterfaceVolumeSliderChanged;
|
||||
TtsVolumeSlider.OnValueChanged -= OnTtsVolumeSliderChanged; // Corvax-TTS
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
|
@ -70,6 +69,11 @@ namespace Content.Client.Options.UI.Tabs
|
|||
UpdateChanges();
|
||||
}
|
||||
|
||||
private void OnInterfaceVolumeSliderChanged(Range obj)
|
||||
{
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
private void OnAmbientMusicVolumeSliderChanged(Range obj)
|
||||
{
|
||||
UpdateChanges();
|
||||
|
|
@ -130,6 +134,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
_cfg.SetCVar(CCVars.AmbienceVolume, AmbienceVolumeSlider.Value / 100f * ContentAudioSystem.AmbienceMultiplier);
|
||||
_cfg.SetCVar(CCVars.AmbientMusicVolume, AmbientMusicVolumeSlider.Value / 100f * ContentAudioSystem.AmbientMusicMultiplier);
|
||||
_cfg.SetCVar(CCVars.LobbyMusicVolume, LobbyVolumeSlider.Value / 100f * ContentAudioSystem.LobbyMultiplier);
|
||||
_cfg.SetCVar(CCVars.InterfaceVolume, InterfaceVolumeSlider.Value / 100f * ContentAudioSystem.InterfaceMultiplier);
|
||||
_cfg.SetCVar(CCCVars.TTSVolume, TtsVolumeSlider.Value / 100f * ContentAudioSystem.TtsMultiplier); // Corvax-TTS
|
||||
|
||||
_cfg.SetCVar(CCVars.MaxAmbientSources, (int)AmbienceSoundsSlider.Value);
|
||||
|
|
@ -154,6 +159,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
AmbienceVolumeSlider.Value = _cfg.GetCVar(CCVars.AmbienceVolume) * 100f / ContentAudioSystem.AmbienceMultiplier;
|
||||
AmbientMusicVolumeSlider.Value = _cfg.GetCVar(CCVars.AmbientMusicVolume) * 100f / ContentAudioSystem.AmbientMusicMultiplier;
|
||||
LobbyVolumeSlider.Value = _cfg.GetCVar(CCVars.LobbyMusicVolume) * 100f / ContentAudioSystem.LobbyMultiplier;
|
||||
InterfaceVolumeSlider.Value = _cfg.GetCVar(CCVars.InterfaceVolume) * 100f / ContentAudioSystem.InterfaceMultiplier;
|
||||
TtsVolumeSlider.Value = _cfg.GetCVar(CCCVars.TTSVolume) * 100f / ContentAudioSystem.TtsMultiplier; // Corvax-TTS
|
||||
|
||||
AmbienceSoundsSlider.Value = _cfg.GetCVar(CCVars.MaxAmbientSources);
|
||||
|
|
@ -178,6 +184,8 @@ namespace Content.Client.Options.UI.Tabs
|
|||
Math.Abs(AmbientMusicVolumeSlider.Value - _cfg.GetCVar(CCVars.AmbientMusicVolume) * 100f / ContentAudioSystem.AmbientMusicMultiplier) < 0.01f;
|
||||
var isLobbyVolumeSame =
|
||||
Math.Abs(LobbyVolumeSlider.Value - _cfg.GetCVar(CCVars.LobbyMusicVolume) * 100f / ContentAudioSystem.LobbyMultiplier) < 0.01f;
|
||||
var isInterfaceVolumeSame =
|
||||
Math.Abs(InterfaceVolumeSlider.Value - _cfg.GetCVar(CCVars.InterfaceVolume) * 100f / ContentAudioSystem.InterfaceMultiplier) < 0.01f;
|
||||
var isTtsVolumeSame =
|
||||
Math.Abs(LobbyVolumeSlider.Value - _cfg.GetCVar(CCVars.LobbyMusicVolume) * 100f / ContentAudioSystem.TtsMultiplier) < 0.01f;
|
||||
|
||||
|
|
@ -187,7 +195,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||
var isEventSame = EventMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.EventMusicEnabled);
|
||||
var isAdminSoundsSame = AdminSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.AdminSoundsEnabled);
|
||||
var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientMusicVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
|
||||
&& isAdminSoundsSame && isLobbyVolumeSame;
|
||||
&& isAdminSoundsSame && isLobbyVolumeSame && isInterfaceVolumeSame;
|
||||
isEverythingSame = isEverythingSame && isTtsVolumeSame; // Corvax-TTS
|
||||
ApplyButton.Disabled = isEverythingSame;
|
||||
ResetButton.Disabled = isEverythingSame;
|
||||
|
|
@ -201,6 +209,8 @@ namespace Content.Client.Options.UI.Tabs
|
|||
Loc.GetString("ui-options-volume-percent", ("volume", AmbienceVolumeSlider.Value / 100));
|
||||
LobbyVolumeLabel.Text =
|
||||
Loc.GetString("ui-options-volume-percent", ("volume", LobbyVolumeSlider.Value / 100));
|
||||
InterfaceVolumeLabel.Text =
|
||||
Loc.GetString("ui-options-volume-percent", ("volume", InterfaceVolumeSlider.Value / 100));
|
||||
TtsVolumeLabel.Text =
|
||||
Loc.GetString("ui-options-volume-percent", ("volume", TtsVolumeSlider.Value / 100)); // Corvax-TTS
|
||||
AmbienceSoundsLabel.Text = ((int)AmbienceSoundsSlider.Value).ToString();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
using Content.Shared.Pinpointer;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client.Pinpointer.UI;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class NavMapBeaconBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private NavMapBeaconWindow? _window;
|
||||
|
||||
public NavMapBeaconBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new NavMapBeaconWindow(Owner);
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.OnApplyButtonPressed += (label, enabled, color) =>
|
||||
{
|
||||
SendMessage(new NavMapBeaconConfigureBuiMessage(label, enabled, color));
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
17
Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml
Normal file
17
Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'nav-beacon-window-title'}"
|
||||
MinSize="250 230"
|
||||
SetSize="320 230">
|
||||
<BoxContainer Orientation="Vertical" Margin="5">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'nav-beacon-text-label'}" Margin="0 0 5 0"/>
|
||||
<LineEdit Name="LabelLineEdit" HorizontalExpand="True"/>
|
||||
<Button Name="VisibleButton" ToggleMode="True" Text="{Loc 'nav-beacon-toggle-visible'}" Margin="10 0 0 0" MinWidth="100"/>
|
||||
</BoxContainer>
|
||||
<ColorSelectorSliders Name="ColorSelector" Margin="0 5 0 10"/>
|
||||
<BoxContainer HorizontalAlignment="Right" VerticalAlignment="Bottom" VerticalExpand="True">
|
||||
<Button Name="ApplyButton" Text="{Loc 'nav-beacon-button-apply'}" HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
78
Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs
Normal file
78
Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Pinpointer.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class NavMapBeaconWindow : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
private string? _defaultLabel;
|
||||
private bool _defaultEnabled;
|
||||
private Color _defaultColor;
|
||||
|
||||
public event Action<string?, bool, Color>? OnApplyButtonPressed;
|
||||
|
||||
public NavMapBeaconWindow(EntityUid beaconEntity)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
if (!_entityManager.TryGetComponent<NavMapBeaconComponent>(beaconEntity, out var navMap))
|
||||
return;
|
||||
_defaultLabel = navMap.Text;
|
||||
_defaultEnabled = navMap.Enabled;
|
||||
_defaultColor = navMap.Color;
|
||||
|
||||
UpdateVisibleButton(navMap.Enabled);
|
||||
VisibleButton.OnPressed += args => UpdateVisibleButton(args.Button.Pressed);
|
||||
|
||||
LabelLineEdit.Text = navMap.Text ?? string.Empty;
|
||||
LabelLineEdit.OnTextChanged += OnTextChanged;
|
||||
|
||||
ColorSelector.Color = navMap.Color;
|
||||
ColorSelector.OnColorChanged += _ => TryEnableApplyButton();
|
||||
|
||||
TryEnableApplyButton();
|
||||
ApplyButton.OnPressed += OnApplyPressed;
|
||||
}
|
||||
|
||||
private void UpdateVisibleButton(bool value)
|
||||
{
|
||||
VisibleButton.Pressed = value;
|
||||
VisibleButton.Text = Loc.GetString(value
|
||||
? "nav-beacon-toggle-visible"
|
||||
: "nav-beacon-toggle-invisible");
|
||||
|
||||
TryEnableApplyButton();
|
||||
}
|
||||
|
||||
private void OnTextChanged(LineEdit.LineEditEventArgs obj)
|
||||
{
|
||||
if (obj.Text.Length > HumanoidCharacterProfile.MaxNameLength)
|
||||
obj.Control.Text = obj.Text.Substring(0, HumanoidCharacterProfile.MaxNameLength);
|
||||
|
||||
TryEnableApplyButton();
|
||||
}
|
||||
|
||||
private void TryEnableApplyButton()
|
||||
{
|
||||
ApplyButton.Disabled = LabelLineEdit.Text == (_defaultLabel ?? string.Empty) &&
|
||||
VisibleButton.Pressed == _defaultEnabled &&
|
||||
ColorSelector.Color == _defaultColor;
|
||||
}
|
||||
|
||||
private void OnApplyPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
_defaultLabel = LabelLineEdit.Text == string.Empty ? null : LabelLineEdit.Text;
|
||||
_defaultEnabled = VisibleButton.Pressed;
|
||||
_defaultColor = ColorSelector.Color;
|
||||
OnApplyButtonPressed?.Invoke(_defaultLabel, _defaultEnabled, _defaultColor);
|
||||
TryEnableApplyButton();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,29 +5,26 @@ using Robust.Shared.Animations;
|
|||
|
||||
namespace Content.Client.Rotation;
|
||||
|
||||
public sealed class RotationVisualizerSystem : VisualizerSystem<RotationVisualsComponent>
|
||||
public sealed class RotationVisualizerSystem : SharedRotationVisualsSystem
|
||||
{
|
||||
public void SetHorizontalAngle(EntityUid uid, Angle angle, RotationVisualsComponent? component = null)
|
||||
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
base.Initialize();
|
||||
|
||||
if (component.HorizontalRotation.Equals(angle))
|
||||
return;
|
||||
|
||||
component.HorizontalRotation = angle;
|
||||
Dirty(component);
|
||||
SubscribeLocalEvent<RotationVisualsComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, RotationVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
private void OnAppearanceChange(EntityUid uid, RotationVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
base.OnAppearanceChange(uid, component, ref args);
|
||||
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
// If not defined, defaults to standing.
|
||||
AppearanceSystem.TryGetData<RotationState>(uid, RotationVisuals.RotationState, out var state, args.Component);
|
||||
_appearance.TryGetData<RotationState>(uid, RotationVisuals.RotationState, out var state, args.Component);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
|
|
@ -53,9 +50,9 @@ public sealed class RotationVisualizerSystem : VisualizerSystem<RotationVisualsC
|
|||
var animationComp = EnsureComp<AnimationPlayerComponent>(uid);
|
||||
const string animationKey = "rotate";
|
||||
// Stop the current rotate animation and then start a new one
|
||||
if (AnimationSystem.HasRunningAnimation(animationComp, animationKey))
|
||||
if (_animation.HasRunningAnimation(animationComp, animationKey))
|
||||
{
|
||||
AnimationSystem.Stop(animationComp, animationKey);
|
||||
_animation.Stop(animationComp, animationKey);
|
||||
}
|
||||
|
||||
var animation = new Animation
|
||||
|
|
@ -77,6 +74,6 @@ public sealed class RotationVisualizerSystem : VisualizerSystem<RotationVisualsC
|
|||
}
|
||||
};
|
||||
|
||||
AnimationSystem.Play((uid, animationComp), animation, animationKey);
|
||||
_animation.Play((uid, animationComp), animation, animationKey);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
namespace Content.Client.Rotation;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class RotationVisualsComponent : Component
|
||||
{
|
||||
[DataField("defaultRotation")]
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public Angle DefaultRotation = Angle.FromDegrees(90);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Angle VerticalRotation = 0;
|
||||
|
||||
[DataField("horizontalRotation")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Angle HorizontalRotation = Angle.FromDegrees(90);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float AnimationTime = 0.125f;
|
||||
}
|
||||
|
|
@ -125,19 +125,15 @@ namespace Content.Client.RoundEnd
|
|||
VerticalExpand = true,
|
||||
};
|
||||
|
||||
var playerUid = _entityManager.GetEntity(playerInfo.PlayerNetEntity);
|
||||
|
||||
if (_entityManager.HasComponent<SpriteComponent>(playerUid))
|
||||
if (playerInfo.PlayerNetEntity != null)
|
||||
{
|
||||
var spriteView = new SpriteView
|
||||
{
|
||||
OverrideDirection = Direction.South,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SetSize = new Vector2(32, 32),
|
||||
VerticalExpand = true,
|
||||
};
|
||||
spriteView.SetEntity(playerUid);
|
||||
hBox.AddChild(spriteView);
|
||||
hBox.AddChild(new SpriteView(playerInfo.PlayerNetEntity.Value, _entityManager)
|
||||
{
|
||||
OverrideDirection = Direction.South,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SetSize = new Vector2(32, 32),
|
||||
VerticalExpand = true,
|
||||
});
|
||||
}
|
||||
|
||||
if (playerInfo.PlayerICName != null)
|
||||
|
|
|
|||
|
|
@ -26,8 +26,10 @@ public sealed class ThiefBackpackBoundUserInterface : BoundUserInterface
|
|||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_window != null)
|
||||
_window.OnClose -= Close;
|
||||
|
||||
_window?.Dispose();
|
||||
_window = null;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||
private void OnWindowOpened()
|
||||
{
|
||||
if (ActionButton != null)
|
||||
ActionButton.Pressed = true;
|
||||
ActionButton.SetClickPressed(true);
|
||||
|
||||
SearchAndDisplay();
|
||||
}
|
||||
|
|
@ -293,7 +293,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||
private void OnWindowClosed()
|
||||
{
|
||||
if (ActionButton != null)
|
||||
ActionButton.Pressed = false;
|
||||
ActionButton.SetClickPressed(false);
|
||||
}
|
||||
|
||||
public void OnStateExited(GameplayState state)
|
||||
|
|
@ -773,9 +773,10 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||
if (_actionsSystem == null)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < assignments.Count; i++)
|
||||
_actions.Clear();
|
||||
foreach (var assign in assignments)
|
||||
{
|
||||
_actions[i] = assignments[i].ActionId;
|
||||
_actions.Add(assign.ActionId);
|
||||
}
|
||||
|
||||
_container?.SetActionData(_actionsSystem, _actions.ToArray());
|
||||
|
|
|
|||
|
|
@ -355,6 +355,7 @@ public sealed class ActionButton : Control, IEntityControl
|
|||
{
|
||||
base.MouseEntered();
|
||||
|
||||
UserInterfaceManager.HoverSound();
|
||||
_beingHovered = true;
|
||||
DrawModeChanged();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,14 +127,12 @@ public sealed class AdminUIController : UIController, IOnStateEntered<GameplaySt
|
|||
|
||||
private void OnWindowOpen()
|
||||
{
|
||||
if (AdminButton != null)
|
||||
AdminButton.Pressed = true;
|
||||
AdminButton?.SetClickPressed(true);
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (AdminButton != null)
|
||||
AdminButton.Pressed = false;
|
||||
AdminButton?.SetClickPressed(false);
|
||||
}
|
||||
|
||||
private void OnWindowDisposed()
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ public sealed class AHelpUIController: UIController, IOnSystemChanged<BwoinkSyst
|
|||
LobbyAHelpButton.Pressed = pressed;
|
||||
}
|
||||
|
||||
UIManager.ClickSound();
|
||||
UnreadAHelpRead();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
|
|||
|
||||
if (CharacterButton != null)
|
||||
{
|
||||
CharacterButton.Pressed = !_window.IsOpen;
|
||||
CharacterButton.SetClickPressed(!_window.IsOpen);
|
||||
}
|
||||
|
||||
if (_window.IsOpen)
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ public sealed class EscapeUIController : UIController, IOnStateEntered<GameplayS
|
|||
EscapeButton.OnPressed += EscapeButtonOnOnPressed;
|
||||
}
|
||||
|
||||
private void ActivateButton() => EscapeButton!.Pressed = true;
|
||||
private void DeactivateButton() => EscapeButton!.Pressed = false;
|
||||
private void ActivateButton() => EscapeButton!.SetClickPressed(true);
|
||||
private void DeactivateButton() => EscapeButton!.SetClickPressed(false);
|
||||
|
||||
public void OnStateEntered(GameplayState state)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -139,12 +139,13 @@ public sealed class GuidebookUIController : UIController, IOnStateEntered<LobbyS
|
|||
|
||||
if (_guideWindow.IsOpen)
|
||||
{
|
||||
UIManager.ClickSound();
|
||||
_guideWindow.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (GuidebookButton != null)
|
||||
GuidebookButton.Pressed = !_guideWindow.IsOpen;
|
||||
GuidebookButton.SetClickPressed(!_guideWindow.IsOpen);
|
||||
|
||||
if (guides == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ using System.Numerics;
|
|||
using Content.Client.Gameplay;
|
||||
using Content.Client.Hands.Systems;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.Storage.Systems;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.UserInterface.Systems.Gameplay;
|
||||
using Content.Client.UserInterface.Systems.Inventory.Controls;
|
||||
using Content.Client.UserInterface.Systems.Inventory.Widgets;
|
||||
using Content.Client.UserInterface.Systems.Inventory.Windows;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Storage;
|
||||
|
|
@ -241,7 +243,9 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
|
|||
}
|
||||
|
||||
UpdateInventoryHotbar(_playerInventory);
|
||||
_inventoryHotbar.Visible = !_inventoryHotbar.Visible;
|
||||
var shouldBeVisible = !_inventoryHotbar.Visible;
|
||||
_inventoryHotbar.Visible = shouldBeVisible;
|
||||
|
||||
}
|
||||
|
||||
// Neuron Activation
|
||||
|
|
@ -336,6 +340,25 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
|
|||
var fits = _inventorySystem.CanEquip(player.Value, held, control.SlotName, out _, slotDef) &&
|
||||
_container.CanInsert(held, container);
|
||||
|
||||
if (!fits && _entities.TryGetComponent<StorageComponent>(container.ContainedEntity, out var storage))
|
||||
{
|
||||
fits = _entities.System<StorageSystem>().CanInsert(container.ContainedEntity.Value, held, out _, storage);
|
||||
}
|
||||
else if (!fits && _entities.TryGetComponent<ItemSlotsComponent>(container.ContainedEntity, out var itemSlots))
|
||||
{
|
||||
var itemSlotsSys = _entities.System<ItemSlotsSystem>();
|
||||
foreach (var slot in itemSlots.Slots.Values)
|
||||
{
|
||||
if (!slot.InsertOnInteract)
|
||||
continue;
|
||||
|
||||
if (!itemSlotsSys.CanInsert(container.ContainedEntity.Value, held, null, slot))
|
||||
continue;
|
||||
fits = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hoverSprite.CopyFrom(sprite);
|
||||
hoverSprite.Color = fits ? new Color(0, 255, 0, 127) : new Color(255, 0, 0, 127);
|
||||
|
||||
|
|
|
|||
|
|
@ -172,10 +172,12 @@ public sealed class SandboxUIController : UIController, IOnStateChanged<Gameplay
|
|||
return;
|
||||
if (_sandbox.SandboxAllowed && _window.IsOpen != true)
|
||||
{
|
||||
UIManager.ClickSound();
|
||||
_window.OpenCentered();
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.ClickSound();
|
||||
_window.Close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ namespace Content.Client.Verbs.UI
|
|||
public sealed class VerbMenuUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly ContextMenuUIController _context = default!;
|
||||
|
||||
[UISystemDependency] private readonly CombatModeSystem _combatMode = default!;
|
||||
|
|
@ -112,7 +111,7 @@ namespace Content.Client.Verbs.UI
|
|||
|
||||
// Show the menu at mouse pos
|
||||
menu.SetPositionLast();
|
||||
var box = UIBox2.FromDimensions(_userInterfaceManager.MousePositionScaled.Position, new Vector2(1, 1));
|
||||
var box = UIBox2.FromDimensions(UIManager.MousePositionScaled.Position, new Vector2(1, 1));
|
||||
menu.Open(box);
|
||||
}
|
||||
|
||||
|
|
@ -274,6 +273,7 @@ namespace Content.Client.Verbs.UI
|
|||
|
||||
private void ExecuteVerb(Verb verb)
|
||||
{
|
||||
UIManager.ClickSound();
|
||||
_verbSystem.ExecuteVerb(CurrentTarget, verb);
|
||||
|
||||
if (verb.CloseMenu ?? verb.CloseMenuDefault)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public sealed partial class GunSystem
|
|||
var existing = component.Entities[^1];
|
||||
component.Entities.RemoveAt(component.Entities.Count - 1);
|
||||
|
||||
component.Container.Remove(existing);
|
||||
Containers.Remove(existing, component.Container);
|
||||
EnsureShootable(existing);
|
||||
}
|
||||
else if (component.UnspawnedCount > 0)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ public sealed partial class TestPair
|
|||
public RobustIntegrationTest.ServerIntegrationInstance Server { get; private set; } = default!;
|
||||
public RobustIntegrationTest.ClientIntegrationInstance Client { get; private set; } = default!;
|
||||
|
||||
public void Deconstruct(
|
||||
out RobustIntegrationTest.ServerIntegrationInstance server,
|
||||
out RobustIntegrationTest.ClientIntegrationInstance client)
|
||||
{
|
||||
server = Server;
|
||||
client = Client;
|
||||
}
|
||||
|
||||
public ICommonSession? Player => Server.PlayerMan.Sessions.FirstOrDefault();
|
||||
public ContentPlayerData? PlayerData => Player?.Data.ContentData();
|
||||
|
||||
|
|
@ -78,6 +86,8 @@ public sealed partial class TestPair
|
|||
public void Kill()
|
||||
{
|
||||
State = PairState.Dead;
|
||||
ServerLogHandler.ShuttingDown = true;
|
||||
ClientLogHandler.ShuttingDown = true;
|
||||
Server.Dispose();
|
||||
Client.Dispose();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,15 @@ public sealed class PoolTestLogHandler : ILogHandler
|
|||
_prefix = prefix != null ? $"{prefix}: " : "";
|
||||
}
|
||||
|
||||
public bool ShuttingDown;
|
||||
|
||||
public void Log(string sawmillName, LogEvent message)
|
||||
{
|
||||
var level = message.Level.ToRobust();
|
||||
|
||||
if (ShuttingDown && (FailureLevel == null || level < FailureLevel))
|
||||
return;
|
||||
|
||||
if (ActiveContext is not { } testContext)
|
||||
{
|
||||
// If this gets hit it means something is logging to this instance while it's "between" tests.
|
||||
|
|
@ -45,7 +52,6 @@ public sealed class PoolTestLogHandler : ILogHandler
|
|||
throw new InvalidOperationException("Log to pool test log handler without active test context");
|
||||
}
|
||||
|
||||
var level = message.Level.ToRobust();
|
||||
var name = LogMessage.LogLevelToName(level);
|
||||
var seconds = _stopwatch.Elapsed.TotalSeconds;
|
||||
var rendered = message.RenderMessage();
|
||||
|
|
|
|||
43
Content.IntegrationTests/Tests/Body/GibTest.cs
Normal file
43
Content.IntegrationTests/Tests/Body/GibTest.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#nullable enable
|
||||
using Content.Server.Body.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Body;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class GibTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestGib()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true });
|
||||
var (server, client) = (pair.Server, pair.Client);
|
||||
var map = await pair.CreateTestMap();
|
||||
|
||||
EntityUid target1 = default;
|
||||
EntityUid target2 = default;
|
||||
|
||||
await server.WaitAssertion(() => target1 = server.EntMan.Spawn("MobHuman", map.MapCoords));
|
||||
await server.WaitAssertion(() => target2 = server.EntMan.Spawn("MobHuman", map.MapCoords));
|
||||
await pair.WaitCommand($"setoutfit {server.EntMan.GetNetEntity(target1)} CaptainGear");
|
||||
await pair.WaitCommand($"setoutfit {server.EntMan.GetNetEntity(target2)} CaptainGear");
|
||||
|
||||
await pair.RunTicksSync(5);
|
||||
var nuid1 = pair.ToClientUid(target1);
|
||||
var nuid2 = pair.ToClientUid(target2);
|
||||
Assert.That(client.EntMan.EntityExists(nuid1));
|
||||
Assert.That(client.EntMan.EntityExists(nuid2));
|
||||
|
||||
await server.WaitAssertion(() => server.System<BodySystem>().GibBody(target1, gibOrgans: false));
|
||||
await server.WaitAssertion(() => server.System<BodySystem>().GibBody(target2, gibOrgans: true));
|
||||
|
||||
await pair.RunTicksSync(5);
|
||||
await pair.WaitCommand("dirty");
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
Assert.That(!client.EntMan.EntityExists(nuid1));
|
||||
Assert.That(!client.EntMan.EntityExists(nuid2));
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -51,7 +51,7 @@ public sealed class SolutionSystemTests
|
|||
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var protoMan = server.ResolveDependency<IPrototypeManager>();
|
||||
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
|
||||
var containerSystem = entityManager.System<SolutionContainerSystem>();
|
||||
var testMap = await pair.CreateTestMap();
|
||||
var coordinates = testMap.GridCoords;
|
||||
|
||||
|
|
@ -67,11 +67,11 @@ public sealed class SolutionSystemTests
|
|||
|
||||
beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
|
||||
Assert.That(containerSystem
|
||||
.TryGetSolution(beaker, "beaker", out var solution));
|
||||
.TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
|
||||
|
||||
solution.AddSolution(originalWater, protoMan);
|
||||
Assert.That(containerSystem
|
||||
.TryAddSolution(beaker, solution, oilAdded));
|
||||
.TryAddSolution(solutionEnt.Value, oilAdded));
|
||||
|
||||
var water = solution.GetTotalPrototypeQuantity("Water");
|
||||
var oil = solution.GetTotalPrototypeQuantity("Oil");
|
||||
|
|
@ -97,7 +97,7 @@ public sealed class SolutionSystemTests
|
|||
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var protoMan = server.ResolveDependency<IPrototypeManager>();
|
||||
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
|
||||
var containerSystem = entityManager.System<SolutionContainerSystem>();
|
||||
var coordinates = testMap.GridCoords;
|
||||
|
||||
EntityUid beaker;
|
||||
|
|
@ -112,11 +112,11 @@ public sealed class SolutionSystemTests
|
|||
|
||||
beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
|
||||
Assert.That(containerSystem
|
||||
.TryGetSolution(beaker, "beaker", out var solution));
|
||||
.TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
|
||||
|
||||
solution.AddSolution(originalWater, protoMan);
|
||||
Assert.That(containerSystem
|
||||
.TryAddSolution(beaker, solution, oilAdded), Is.False);
|
||||
.TryAddSolution(solutionEnt.Value, oilAdded), Is.False);
|
||||
|
||||
var water = solution.GetTotalPrototypeQuantity("Water");
|
||||
var oil = solution.GetTotalPrototypeQuantity("Oil");
|
||||
|
|
@ -141,7 +141,7 @@ public sealed class SolutionSystemTests
|
|||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var protoMan = server.ResolveDependency<IPrototypeManager>();
|
||||
var testMap = await pair.CreateTestMap();
|
||||
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
|
||||
var containerSystem = entityManager.System<SolutionContainerSystem>();
|
||||
var coordinates = testMap.GridCoords;
|
||||
|
||||
EntityUid beaker;
|
||||
|
|
@ -158,11 +158,11 @@ public sealed class SolutionSystemTests
|
|||
|
||||
beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
|
||||
Assert.That(containerSystem
|
||||
.TryGetSolution(beaker, "beaker", out var solution));
|
||||
.TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
|
||||
|
||||
solution.AddSolution(originalWater, protoMan);
|
||||
Assert.That(containerSystem
|
||||
.TryMixAndOverflow(beaker, solution, oilAdded, threshold, out var overflowingSolution));
|
||||
.TryMixAndOverflow(solutionEnt.Value, oilAdded, threshold, out var overflowingSolution));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
|
|
@ -194,7 +194,7 @@ public sealed class SolutionSystemTests
|
|||
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var protoMan = server.ResolveDependency<IPrototypeManager>();
|
||||
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
|
||||
var containerSystem = entityManager.System<SolutionContainerSystem>();
|
||||
var testMap = await pair.CreateTestMap();
|
||||
var coordinates = testMap.GridCoords;
|
||||
|
||||
|
|
@ -212,11 +212,11 @@ public sealed class SolutionSystemTests
|
|||
|
||||
beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
|
||||
Assert.That(containerSystem
|
||||
.TryGetSolution(beaker, "beaker", out var solution));
|
||||
.TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
|
||||
|
||||
solution.AddSolution(originalWater, protoMan);
|
||||
Assert.That(containerSystem
|
||||
.TryMixAndOverflow(beaker, solution, oilAdded, threshold, out _),
|
||||
.TryMixAndOverflow(solutionEnt.Value, oilAdded, threshold, out _),
|
||||
Is.False);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Chemistry
|
||||
{
|
||||
|
|
@ -34,8 +34,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
|||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var testMap = await pair.CreateTestMap();
|
||||
var coordinates = testMap.GridCoords;
|
||||
var solutionSystem = server.ResolveDependency<IEntitySystemManager>()
|
||||
.GetEntitySystem<SolutionContainerSystem>();
|
||||
var solutionContainerSystem = entityManager.System<SolutionContainerSystem>();
|
||||
|
||||
foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes<ReactionPrototype>())
|
||||
{
|
||||
|
|
@ -43,30 +42,31 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
|||
Console.WriteLine($"Testing {reactionPrototype.ID}");
|
||||
|
||||
EntityUid beaker = default;
|
||||
Solution component = null;
|
||||
Entity<SolutionComponent>? solutionEnt = default!;
|
||||
Solution solution = null;
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
beaker = entityManager.SpawnEntity("TestSolutionContainer", coordinates);
|
||||
Assert.That(solutionSystem
|
||||
.TryGetSolution(beaker, "beaker", out component));
|
||||
Assert.That(solutionContainerSystem
|
||||
.TryGetSolution(beaker, "beaker", out solutionEnt, out solution));
|
||||
foreach (var (id, reactant) in reactionPrototype.Reactants)
|
||||
{
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(solutionSystem
|
||||
.TryAddReagent(beaker, component, id, reactant.Amount, out var quantity));
|
||||
Assert.That(solutionContainerSystem
|
||||
.TryAddReagent(solutionEnt.Value, id, reactant.Amount, out var quantity));
|
||||
Assert.That(reactant.Amount, Is.EqualTo(quantity));
|
||||
#pragma warning restore NUnit2045
|
||||
}
|
||||
|
||||
solutionSystem.SetTemperature(beaker, component, reactionPrototype.MinimumTemperature);
|
||||
solutionContainerSystem.SetTemperature(solutionEnt.Value, reactionPrototype.MinimumTemperature);
|
||||
|
||||
if (reactionPrototype.MixingCategories != null)
|
||||
{
|
||||
var dummyEntity = entityManager.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
var mixerComponent = entityManager.AddComponent<ReactionMixerComponent>(dummyEntity);
|
||||
mixerComponent.ReactionTypes = reactionPrototype.MixingCategories;
|
||||
solutionSystem.UpdateChemicals(beaker, component, true, mixerComponent);
|
||||
solutionContainerSystem.UpdateChemicals(solutionEnt.Value, true, mixerComponent);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
|||
var foundProductsMap = reactionPrototype.Products
|
||||
.Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount))
|
||||
.ToDictionary(x => x, _ => false);
|
||||
foreach (var (reagent, quantity) in component.Contents)
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.Prototype && x.Key.Value == quantity, out var foundProduct));
|
||||
foundProductsMap[foundProduct.Value.Key] = true;
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ public sealed class DoAfterCancellationTests : InteractionTest
|
|||
AssertAnchored(false);
|
||||
|
||||
// Repeat for screwdriver interaction.
|
||||
AssertDeleted(false);
|
||||
AssertExists();
|
||||
await Interact(Screw, awaitDoAfters: false);
|
||||
await CancelDoAfters();
|
||||
AssertDeleted(false);
|
||||
AssertExists();
|
||||
await Interact(Screw);
|
||||
AssertDeleted();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public sealed class AbsorbentTest
|
|||
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var absorbentSystem = entityManager.System<AbsorbentSystem>();
|
||||
var solutionContainerSystem = entityManager.System<SolutionContainerSystem>();
|
||||
var solutionContainerSystem = entityManager.System<SharedSolutionContainerSystem>();
|
||||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||
|
||||
EntityUid user = default;
|
||||
|
|
@ -94,19 +94,19 @@ public sealed class AbsorbentTest
|
|||
refillable = entityManager.SpawnEntity(RefillableDummyId, coordinates);
|
||||
|
||||
entityManager.TryGetComponent(absorbent, out component);
|
||||
solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSolution);
|
||||
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSolution);
|
||||
solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSoln, out var absorbentSolution);
|
||||
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
|
||||
|
||||
// Arrange
|
||||
if (testCase.InitialAbsorbentSolution.VolumeOfEvaporable > FixedPoint2.Zero)
|
||||
solutionContainerSystem.AddSolution(absorbent, absorbentSolution, new Solution(EvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfEvaporable));
|
||||
solutionContainerSystem.AddSolution(absorbentSoln.Value, new Solution(EvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfEvaporable));
|
||||
if (testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable > FixedPoint2.Zero)
|
||||
solutionContainerSystem.AddSolution(absorbent, absorbentSolution, new Solution(NonEvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable));
|
||||
solutionContainerSystem.AddSolution(absorbentSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable));
|
||||
|
||||
if (testCase.InitialRefillableSolution.VolumeOfEvaporable > FixedPoint2.Zero)
|
||||
solutionContainerSystem.AddSolution(refillable, refillableSolution, new Solution(EvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfEvaporable));
|
||||
solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(EvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfEvaporable));
|
||||
if (testCase.InitialRefillableSolution.VolumeOfNonEvaporable > FixedPoint2.Zero)
|
||||
solutionContainerSystem.AddSolution(refillable, refillableSolution, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
|
||||
solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
|
||||
|
||||
// Act
|
||||
absorbentSystem.Mop(user, refillable, absorbent, component);
|
||||
|
|
@ -138,7 +138,7 @@ public sealed class AbsorbentTest
|
|||
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var absorbentSystem = entityManager.System<AbsorbentSystem>();
|
||||
var solutionContainerSystem = entityManager.System<SolutionContainerSystem>();
|
||||
var solutionContainerSystem = entityManager.System<SharedSolutionContainerSystem>();
|
||||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||
|
||||
EntityUid user = default;
|
||||
|
|
@ -152,18 +152,18 @@ public sealed class AbsorbentTest
|
|||
refillable = entityManager.SpawnEntity(SmallRefillableDummyId, coordinates);
|
||||
|
||||
entityManager.TryGetComponent(absorbent, out component);
|
||||
solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSolution);
|
||||
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSolution);
|
||||
solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSoln, out var absorbentSolution);
|
||||
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
|
||||
|
||||
// Arrange
|
||||
solutionContainerSystem.AddSolution(absorbent, absorbentSolution, new Solution(EvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfEvaporable));
|
||||
solutionContainerSystem.AddSolution(absorbentSoln.Value, new Solution(EvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfEvaporable));
|
||||
if (testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable > FixedPoint2.Zero)
|
||||
solutionContainerSystem.AddSolution(absorbent, absorbentSolution, new Solution(NonEvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable));
|
||||
solutionContainerSystem.AddSolution(absorbentSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable));
|
||||
|
||||
if (testCase.InitialRefillableSolution.VolumeOfEvaporable > FixedPoint2.Zero)
|
||||
solutionContainerSystem.AddSolution(refillable, refillableSolution, new Solution(EvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfEvaporable));
|
||||
solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(EvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfEvaporable));
|
||||
if (testCase.InitialRefillableSolution.VolumeOfNonEvaporable > FixedPoint2.Zero)
|
||||
solutionContainerSystem.AddSolution(refillable, refillableSolution, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
|
||||
solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
|
||||
|
||||
// Act
|
||||
absorbentSystem.Mop(user, refillable, absorbent, component);
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
|
|||
await server.WaitAssertion(() =>
|
||||
{
|
||||
#pragma warning disable NUnit2045 // Interdependent assertions.
|
||||
Assert.That(container.Insert(user));
|
||||
Assert.That(conSystem.Insert(user, container));
|
||||
Assert.That(sEntities.GetComponent<TransformComponent>(user).ParentUid, Is.EqualTo(containerEntity));
|
||||
#pragma warning restore NUnit2045
|
||||
|
||||
|
|
|
|||
|
|
@ -134,9 +134,9 @@ public abstract partial class InteractionTest
|
|||
/// <remarks>
|
||||
/// Automatically enables welders.
|
||||
/// </remarks>
|
||||
protected async Task<EntityUid?> PlaceInHands(string? id, int quantity = 1, bool enableWelder = true)
|
||||
protected async Task<NetEntity> PlaceInHands(string id, int quantity = 1, bool enableWelder = true)
|
||||
{
|
||||
return await PlaceInHands(id == null ? null : (id, quantity), enableWelder);
|
||||
return await PlaceInHands((id, quantity), enableWelder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -145,7 +145,7 @@ public abstract partial class InteractionTest
|
|||
/// <remarks>
|
||||
/// Automatically enables welders.
|
||||
/// </remarks>
|
||||
protected async Task<EntityUid?> PlaceInHands(EntitySpecifier? entity, bool enableWelder = true)
|
||||
protected async Task<NetEntity> PlaceInHands(EntitySpecifier entity, bool enableWelder = true)
|
||||
{
|
||||
if (Hands.ActiveHand == null)
|
||||
{
|
||||
|
|
@ -153,15 +153,9 @@ public abstract partial class InteractionTest
|
|||
return default;
|
||||
}
|
||||
|
||||
Assert.That(!string.IsNullOrWhiteSpace(entity.Prototype));
|
||||
await DeleteHeldEntity();
|
||||
|
||||
if (entity == null || string.IsNullOrWhiteSpace(entity.Prototype))
|
||||
{
|
||||
await RunTicks(1);
|
||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
||||
return null;
|
||||
}
|
||||
|
||||
// spawn and pick up the new item
|
||||
var item = await SpawnEntity(entity, SEntMan.GetCoordinates(PlayerCoords));
|
||||
ItemToggleComponent? itemToggle = null;
|
||||
|
|
@ -184,7 +178,7 @@ public abstract partial class InteractionTest
|
|||
if (enableWelder && itemToggle != null)
|
||||
Assert.That(itemToggle.Activated);
|
||||
|
||||
return item;
|
||||
return SEntMan.GetNetEntity(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -330,6 +324,17 @@ public abstract partial class InteractionTest
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throw the currently held entity. Defaults to targeting the current <see cref="TargetCoords"/>
|
||||
/// </summary>
|
||||
protected async Task<bool> ThrowItem(NetCoordinates? target = null, float minDistance = 4)
|
||||
{
|
||||
var actualTarget = SEntMan.GetCoordinates(target ?? TargetCoords);
|
||||
var result = false;
|
||||
await Server.WaitPost(() => result = HandSys.ThrowHeldItem(SEntMan.GetEntity(Player), actualTarget, minDistance));
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -483,7 +488,7 @@ public abstract partial class InteractionTest
|
|||
});
|
||||
}
|
||||
|
||||
protected void AssertDeleted(bool deleted = true, NetEntity? target = null)
|
||||
protected void AssertDeleted(NetEntity? target = null)
|
||||
{
|
||||
target ??= Target;
|
||||
if (target == null)
|
||||
|
|
@ -494,8 +499,24 @@ public abstract partial class InteractionTest
|
|||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(SEntMan.Deleted(SEntMan.GetEntity(target)), Is.EqualTo(deleted));
|
||||
Assert.That(CEntMan.Deleted(CEntMan.GetEntity(target)), Is.EqualTo(deleted));
|
||||
Assert.That(SEntMan.Deleted(SEntMan.GetEntity(target)));
|
||||
Assert.That(CEntMan.Deleted(CEntMan.GetEntity(target)));
|
||||
});
|
||||
}
|
||||
|
||||
protected void AssertExists(NetEntity? target = null)
|
||||
{
|
||||
target ??= Target;
|
||||
if (target == null)
|
||||
{
|
||||
Assert.Fail("No target specified");
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(SEntMan.EntityExists(SEntMan.GetEntity(target)));
|
||||
Assert.That(CEntMan.EntityExists(CEntMan.GetEntity(target)));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -733,6 +754,11 @@ public abstract partial class InteractionTest
|
|||
await RunTicks(5);
|
||||
}
|
||||
|
||||
protected Task Delete(NetEntity nuid)
|
||||
{
|
||||
return Delete(SEntMan.GetEntity(nuid));
|
||||
}
|
||||
|
||||
#region Time/Tick managment
|
||||
|
||||
protected async Task RunTicks(int ticks)
|
||||
|
|
@ -1064,4 +1090,35 @@ public abstract partial class InteractionTest
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Networking
|
||||
|
||||
protected EntityUid ToServer(NetEntity nent) => SEntMan.GetEntity(nent);
|
||||
protected EntityUid ToClient(NetEntity nent) => CEntMan.GetEntity(nent);
|
||||
protected EntityUid? ToServer(NetEntity? nent) => SEntMan.GetEntity(nent);
|
||||
protected EntityUid? ToClient(NetEntity? nent) => CEntMan.GetEntity(nent);
|
||||
protected EntityUid ToServer(EntityUid cuid) => SEntMan.GetEntity(CEntMan.GetNetEntity(cuid));
|
||||
protected EntityUid ToClient(EntityUid cuid) => CEntMan.GetEntity(SEntMan.GetNetEntity(cuid));
|
||||
protected EntityUid? ToServer(EntityUid? cuid) => SEntMan.GetEntity(CEntMan.GetNetEntity(cuid));
|
||||
protected EntityUid? ToClient(EntityUid? cuid) => CEntMan.GetEntity(SEntMan.GetNetEntity(cuid));
|
||||
|
||||
protected EntityCoordinates ToServer(NetCoordinates coords) => SEntMan.GetCoordinates(coords);
|
||||
protected EntityCoordinates ToClient(NetCoordinates coords) => CEntMan.GetCoordinates(coords);
|
||||
protected EntityCoordinates? ToServer(NetCoordinates? coords) => SEntMan.GetCoordinates(coords);
|
||||
protected EntityCoordinates? ToClient(NetCoordinates? coords) => CEntMan.GetCoordinates(coords);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Metadata & Transforms
|
||||
|
||||
protected MetaDataComponent Meta(NetEntity uid) => Meta(ToServer(uid));
|
||||
protected MetaDataComponent Meta(EntityUid uid) => SEntMan.GetComponent<MetaDataComponent>(uid);
|
||||
|
||||
protected TransformComponent Xform(NetEntity uid) => Xform(ToServer(uid));
|
||||
protected TransformComponent Xform(EntityUid uid) => SEntMan.GetComponent<TransformComponent>(uid);
|
||||
|
||||
protected EntityCoordinates Position(NetEntity uid) => Position(ToServer(uid));
|
||||
protected EntityCoordinates Position(EntityUid uid) => Xform(uid).Coordinates;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ using Content.Client.Construction;
|
|||
using Content.Client.Examine;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Tools;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Server.Item;
|
||||
using Content.Shared.Mind;
|
||||
|
|
@ -66,6 +66,9 @@ public abstract partial class InteractionTest
|
|||
/// </summary>
|
||||
protected NetEntity Player;
|
||||
|
||||
protected EntityUid SPlayer => ToServer(Player);
|
||||
protected EntityUid CPlayer => ToClient(Player);
|
||||
|
||||
protected ICommonSession ClientSession = default!;
|
||||
protected ICommonSession ServerSession = default!;
|
||||
|
||||
|
|
@ -81,6 +84,9 @@ public abstract partial class InteractionTest
|
|||
/// </remarks>
|
||||
protected NetEntity? Target;
|
||||
|
||||
protected EntityUid? STarget => ToServer(Target);
|
||||
protected EntityUid? CTarget => ToClient(Target);
|
||||
|
||||
/// <summary>
|
||||
/// When attempting to start construction, this is the client-side ID of the construction ghost.
|
||||
/// </summary>
|
||||
|
|
@ -93,7 +99,7 @@ public abstract partial class InteractionTest
|
|||
protected IPrototypeManager ProtoMan = default!;
|
||||
protected IGameTiming STiming = default!;
|
||||
protected IComponentFactory Factory = default!;
|
||||
protected SharedHandsSystem HandSys = default!;
|
||||
protected HandsSystem HandSys = default!;
|
||||
protected StackSystem Stack = default!;
|
||||
protected SharedInteractionSystem InteractSys = default!;
|
||||
protected Content.Server.Construction.ConstructionSystem SConstruction = default!;
|
||||
|
|
@ -152,7 +158,7 @@ public abstract partial class InteractionTest
|
|||
ProtoMan = Server.ResolveDependency<IPrototypeManager>();
|
||||
Factory = Server.ResolveDependency<IComponentFactory>();
|
||||
STiming = Server.ResolveDependency<IGameTiming>();
|
||||
HandSys = SEntMan.System<SharedHandsSystem>();
|
||||
HandSys = SEntMan.System<HandsSystem>();
|
||||
InteractSys = SEntMan.System<SharedInteractionSystem>();
|
||||
ToolSys = SEntMan.System<ToolSystem>();
|
||||
ItemToggleSys = SEntMan.System<SharedItemToggleSystem>();
|
||||
|
|
|
|||
51
Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs
Normal file
51
Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Networking;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class PvsCommandTest
|
||||
{
|
||||
public static EntProtoId TestEnt = "MobHuman";
|
||||
|
||||
[Test]
|
||||
public async Task TestPvsCommands()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false});
|
||||
var (server, client) = pair;
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
// Spawn a complex entity.
|
||||
EntityUid entity = default;
|
||||
await server.WaitPost(() => entity = server.EntMan.Spawn(TestEnt));
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
// Check that the client has a variety pf entities.
|
||||
Assert.That(client.EntMan.EntityCount, Is.GreaterThan(0));
|
||||
Assert.That(client.EntMan.Count<MapComponent>, Is.GreaterThan(0));
|
||||
Assert.That(client.EntMan.Count<MapGridComponent>, Is.GreaterThan(0));
|
||||
|
||||
var meta = client.MetaData(pair.ToClientUid(entity));
|
||||
var lastApplied = meta.LastStateApplied;
|
||||
|
||||
// Dirty all entities
|
||||
await server.ExecuteCommand("dirty");
|
||||
await pair.RunTicksSync(5);
|
||||
Assert.That(meta.LastStateApplied, Is.GreaterThan(lastApplied));
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
// Do a client-side full state reset
|
||||
await client.ExecuteCommand("resetallents");
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
// Request a full server state.
|
||||
lastApplied = meta.LastStateApplied;
|
||||
await client.ExecuteCommand("fullstatereset");
|
||||
await pair.RunTicksSync(10);
|
||||
Assert.That(meta.LastStateApplied, Is.GreaterThan(lastApplied));
|
||||
|
||||
await server.WaitPost(() => server.EntMan.DeleteEntity(entity));
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ public sealed class ModularGrenadeTests : InteractionTest
|
|||
}
|
||||
|
||||
// Grenade has exploded.
|
||||
await RunTicks(5);
|
||||
await RunTicks(30);
|
||||
AssertDeleted();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
111
Content.IntegrationTests/Tests/Physics/ItemThrowingTest.cs
Normal file
111
Content.IntegrationTests/Tests/Physics/ItemThrowingTest.cs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
using Content.IntegrationTests.Tests.Interaction;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Physics.Components;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Physics;
|
||||
|
||||
public sealed class ItemThrowingTest : InteractionTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that an egg breaks when thrown at a wall.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[TestOf(typeof(ThrownItemComponent))]
|
||||
[TestOf(typeof(DamageOnHighSpeedImpactComponent))]
|
||||
public async Task TestThrownEggBreaks()
|
||||
{
|
||||
// Setup entities
|
||||
var egg = await PlaceInHands("FoodEgg");
|
||||
await SpawnTarget("WallSolid");
|
||||
await RunTicks(5);
|
||||
AssertExists(egg);
|
||||
|
||||
// Currently not a "thrown" item.
|
||||
AssertComp<ThrownItemComponent>(hasComp: false, egg);
|
||||
Assert.That(Comp<PhysicsComponent>(egg).BodyStatus, Is.Not.EqualTo(BodyStatus.InAir));
|
||||
|
||||
// Throw it.
|
||||
await ThrowItem();
|
||||
await RunTicks(1);
|
||||
AssertExists(egg);
|
||||
AssertComp<ThrownItemComponent>(hasComp: true, egg);
|
||||
Assert.That(Comp<PhysicsComponent>(egg).BodyStatus, Is.EqualTo(BodyStatus.InAir));
|
||||
|
||||
// Splat
|
||||
await RunTicks(30);
|
||||
AssertDeleted(egg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that an egg thrown into space continues to be an egg.
|
||||
/// I.e., verify that the deletions that happen in the other two tests aren't coincidental.
|
||||
/// </summary>
|
||||
[Test]
|
||||
//[TestOf(typeof(Egg))]
|
||||
public async Task TestEggIsEgg()
|
||||
{
|
||||
// Setup entities
|
||||
var egg = await PlaceInHands("FoodEgg");
|
||||
await RunTicks(5);
|
||||
AssertExists(egg);
|
||||
|
||||
// Currently not a "thrown" item.
|
||||
AssertComp<ThrownItemComponent>(hasComp: false, egg);
|
||||
Assert.That(Comp<PhysicsComponent>(egg).BodyStatus, Is.Not.EqualTo(BodyStatus.InAir));
|
||||
|
||||
// Throw it
|
||||
await ThrowItem();
|
||||
await RunTicks(5);
|
||||
AssertExists(egg);
|
||||
AssertComp<ThrownItemComponent>(hasComp: true, egg);
|
||||
Assert.That(Comp<PhysicsComponent>(egg).BodyStatus, Is.EqualTo(BodyStatus.InAir));
|
||||
|
||||
// Wait a while
|
||||
await RunTicks(60);
|
||||
|
||||
// Egg is egg
|
||||
AssertExists(egg);
|
||||
AssertPrototype("FoodEgg", egg);
|
||||
AssertComp<ThrownItemComponent>(hasComp: false, egg);
|
||||
Assert.That(Comp<PhysicsComponent>(egg).BodyStatus, Is.Not.EqualTo(BodyStatus.InAir));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that a physics can handle deleting a thrown entity. As to why this exists, see
|
||||
/// https://github.com/space-wizards/RobustToolbox/pull/4746
|
||||
/// </summary>
|
||||
[Test]
|
||||
[TestOf(typeof(ThrownItemComponent))]
|
||||
[TestOf(typeof(PhysicsComponent))]
|
||||
public async Task TestDeleteThrownItem()
|
||||
{
|
||||
// Setup entities
|
||||
var pen = await PlaceInHands("Pen");
|
||||
var physics = Comp<PhysicsComponent>(pen);
|
||||
await RunTicks(5);
|
||||
AssertExists(pen);
|
||||
|
||||
// Currently not a "thrown" item.
|
||||
AssertComp<ThrownItemComponent>(hasComp: false, pen);
|
||||
Assert.That(physics.BodyStatus, Is.Not.EqualTo(BodyStatus.InAir));
|
||||
|
||||
// Throw it
|
||||
await ThrowItem();
|
||||
await RunTicks(5);
|
||||
AssertExists(pen);
|
||||
AssertComp<ThrownItemComponent>(hasComp: true, pen);
|
||||
Assert.That(physics.BodyStatus, Is.EqualTo(BodyStatus.InAir));
|
||||
Assert.That(physics.CanCollide);
|
||||
|
||||
// Attempt to make it sleep mid-air. This happens automatically due to the sleep timer, but we just do it manually.
|
||||
await Server.WaitPost(() => Server.System<PhysicsSystem>().SetAwake((ToServer(pen), physics), false));
|
||||
|
||||
// Then try and delete it
|
||||
await Delete(pen);
|
||||
await RunTicks(5);
|
||||
AssertDeleted(pen);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.Errors;
|
||||
|
|
@ -95,6 +96,7 @@ public abstract class ToolshedTest : IInvocationContext
|
|||
}
|
||||
|
||||
protected ICommonSession? InvocationSession { get; set; }
|
||||
public NetUserId? User => Session?.UserId;
|
||||
|
||||
public ICommonSession? Session
|
||||
{
|
||||
|
|
|
|||
42
Content.Server/Access/AddAccessLogCommand.cs
Normal file
42
Content.Server/Access/AddAccessLogCommand.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using Content.Server.Administration;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.Syntax;
|
||||
|
||||
namespace Content.Server.Access;
|
||||
|
||||
[ToolshedCommand, AdminCommand(AdminFlags.Mapping)]
|
||||
public sealed class AddAccessLogCommand : ToolshedCommand
|
||||
{
|
||||
[CommandImplementation]
|
||||
public void AddAccessLog(
|
||||
[CommandInvocationContext] IInvocationContext ctx,
|
||||
[CommandArgument] EntityUid input,
|
||||
[CommandArgument] float seconds,
|
||||
[CommandArgument] ValueRef<string> accessor)
|
||||
{
|
||||
var accessReader = EnsureComp<AccessReaderComponent>(input);
|
||||
|
||||
var accessLogCount = accessReader.AccessLog.Count;
|
||||
if (accessLogCount >= accessReader.AccessLogLimit)
|
||||
ctx.WriteLine($"WARNING: Surpassing the limit of the log by {accessLogCount - accessReader.AccessLogLimit+1} entries!");
|
||||
|
||||
var accessTime = TimeSpan.FromSeconds(seconds);
|
||||
var accessName = accessor.Evaluate(ctx)!;
|
||||
accessReader.AccessLog.Enqueue(new AccessRecord(accessTime, accessName));
|
||||
ctx.WriteLine($"Successfully added access log to {input} with this information inside:\n " +
|
||||
$"Time of access: {accessTime}\n " +
|
||||
$"Accessed by: {accessName}");
|
||||
}
|
||||
|
||||
[CommandImplementation]
|
||||
public void AddAccessLogPiped(
|
||||
[CommandInvocationContext] IInvocationContext ctx,
|
||||
[PipedArgument] EntityUid input,
|
||||
[CommandArgument] float seconds,
|
||||
[CommandArgument] ValueRef<string> accessor)
|
||||
{
|
||||
AddAccessLog(ctx, input, seconds, accessor);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Administration.Commands
|
||||
{
|
||||
|
|
@ -41,13 +42,13 @@ namespace Content.Server.Administration.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
if (!man.Solutions.ContainsKey(args[1]))
|
||||
var solutionContainerSystem = _entManager.System<SolutionContainerSystem>();
|
||||
if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
|
||||
{
|
||||
var validSolutions = string.Join(", ", man.Solutions.Keys);
|
||||
var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
|
||||
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||
return;
|
||||
}
|
||||
var solution = man.Solutions[args[1]];
|
||||
|
||||
if (!_protomanager.HasIndex<ReagentPrototype>(args[2]))
|
||||
{
|
||||
|
|
@ -63,9 +64,9 @@ namespace Content.Server.Administration.Commands
|
|||
var quantity = FixedPoint2.New(MathF.Abs(quantityFloat));
|
||||
|
||||
if (quantityFloat > 0)
|
||||
_entManager.System<SolutionContainerSystem>().TryAddReagent(uid.Value, solution, args[2], quantity, out _);
|
||||
solutionContainerSystem.TryAddReagent(solution.Value, args[2], quantity, out _);
|
||||
else
|
||||
_entManager.System<SolutionContainerSystem>().RemoveReagent(uid.Value, solution, args[2], quantity);
|
||||
solutionContainerSystem.RemoveReagent(solution.Value, args[2], quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ public sealed class DirtyCommand : IConsoleCommand
|
|||
|
||||
private static void DirtyAll(IEntityManager manager, EntityUid entityUid)
|
||||
{
|
||||
foreach (var component in manager.GetComponents(entityUid))
|
||||
foreach (var component in manager.GetNetComponents(entityUid))
|
||||
{
|
||||
manager.Dirty((Component)component);
|
||||
manager.Dirty(entityUid, component.component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Console;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Administration.Commands
|
||||
{
|
||||
|
|
@ -35,13 +36,13 @@ namespace Content.Server.Administration.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
if (!man.Solutions.ContainsKey(args[1]))
|
||||
var solutionContainerSystem = _entManager.System<SolutionContainerSystem>();
|
||||
if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
|
||||
{
|
||||
var validSolutions = string.Join(", ", man.Solutions.Keys);
|
||||
var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
|
||||
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||
return;
|
||||
}
|
||||
var solution = man.Solutions[args[1]];
|
||||
|
||||
if (!float.TryParse(args[2], out var quantityFloat))
|
||||
{
|
||||
|
|
@ -49,14 +50,14 @@ namespace Content.Server.Administration.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
if(quantityFloat < 0.0f)
|
||||
if (quantityFloat < 0.0f)
|
||||
{
|
||||
shell.WriteLine($"Cannot set the maximum volume of a solution to a negative number.");
|
||||
return;
|
||||
}
|
||||
|
||||
var quantity = FixedPoint2.New(quantityFloat);
|
||||
_entManager.System<SolutionContainerSystem>().SetCapacity(uid.Value, solution, quantity);
|
||||
solutionContainerSystem.SetCapacity(solution.Value, quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Console;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Administration.Commands
|
||||
{
|
||||
|
|
@ -34,13 +35,13 @@ namespace Content.Server.Administration.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
if (!man.Solutions.ContainsKey(args[1]))
|
||||
var solutionContainerSystem = _entManager.System<SolutionContainerSystem>();
|
||||
if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
|
||||
{
|
||||
var validSolutions = string.Join(", ", man.Solutions.Keys);
|
||||
var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
|
||||
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||
return;
|
||||
}
|
||||
var solution = man.Solutions[args[1]];
|
||||
|
||||
if (!float.TryParse(args[2], out var quantity))
|
||||
{
|
||||
|
|
@ -54,7 +55,7 @@ namespace Content.Server.Administration.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
_entManager.System<SolutionContainerSystem>().SetTemperature(uid.Value, solution, quantity);
|
||||
solutionContainerSystem.SetTemperature(solution.Value, quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Console;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Administration.Commands
|
||||
{
|
||||
|
|
@ -34,13 +35,13 @@ namespace Content.Server.Administration.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
if (!man.Solutions.ContainsKey(args[1]))
|
||||
var solutionContainerSystem = _entManager.System<SolutionContainerSystem>();
|
||||
if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solutionEnt, out var solution))
|
||||
{
|
||||
var validSolutions = string.Join(", ", man.Solutions.Keys);
|
||||
var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
|
||||
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||
return;
|
||||
}
|
||||
var solution = man.Solutions[args[1]];
|
||||
|
||||
if (!float.TryParse(args[2], out var quantity))
|
||||
{
|
||||
|
|
@ -50,19 +51,19 @@ namespace Content.Server.Administration.Commands
|
|||
|
||||
if (solution.GetHeatCapacity(null) <= 0.0f)
|
||||
{
|
||||
if(quantity != 0.0f)
|
||||
if (quantity != 0.0f)
|
||||
{
|
||||
shell.WriteLine($"Cannot set the thermal energy of a solution with 0 heat capacity to a non-zero number.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(quantity <= 0.0f)
|
||||
else if (quantity <= 0.0f)
|
||||
{
|
||||
shell.WriteLine($"Cannot set the thermal energy of a solution with heat capacity to a non-positive number.");
|
||||
return;
|
||||
}
|
||||
|
||||
_entManager.System<SolutionContainerSystem>().SetThermalEnergy(uid.Value, solution, quantity);
|
||||
solutionContainerSystem.SetThermalEnergy(solutionEnt.Value, quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ public sealed partial class AdminVerbSystem
|
|||
if (!_minds.TryGetSession(targetMindComp.Mind, out var session))
|
||||
return;
|
||||
|
||||
_thief.MakeThief(session, false); //Midround add pacific is bad
|
||||
_thief.AdminMakeThief(session, false); //Midround add pacific is bad
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Message = Loc.GetString("admin-verb-make-thief"),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Administration.UI;
|
||||
|
|
@ -34,6 +33,7 @@ using Robust.Shared.Prototypes;
|
|||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using static Content.Shared.Configurable.ConfigurationComponent;
|
||||
|
||||
namespace Content.Server.Administration.Systems
|
||||
|
|
@ -71,7 +71,7 @@ namespace Content.Server.Administration.Systems
|
|||
{
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(GetVerbs);
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, SolutionChangedEvent>(OnSolutionChanged);
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
|
||||
}
|
||||
|
||||
private void GetVerbs(GetVerbsEvent<Verb> ev)
|
||||
|
|
@ -470,11 +470,11 @@ namespace Content.Server.Administration.Systems
|
|||
}
|
||||
|
||||
#region SolutionsEui
|
||||
private void OnSolutionChanged(EntityUid uid, SolutionContainerManagerComponent component, SolutionChangedEvent args)
|
||||
private void OnSolutionChanged(Entity<SolutionContainerManagerComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
foreach (var eui in _openSolutionUis.Values)
|
||||
{
|
||||
if (eui.Target == uid)
|
||||
if (eui.Target == entity.Owner)
|
||||
eui.StateDirty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.Syntax;
|
||||
using Robust.Shared.Toolshed.TypeParsers;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Administration.Toolshed;
|
||||
|
||||
|
|
@ -24,10 +24,8 @@ public sealed class SolutionCommand : ToolshedCommand
|
|||
{
|
||||
_solutionContainer ??= GetSys<SolutionContainerSystem>();
|
||||
|
||||
_solutionContainer.TryGetSolution(input, name.Evaluate(ctx)!, out var solution);
|
||||
|
||||
if (solution is not null)
|
||||
return new SolutionRef(input, solution);
|
||||
if (_solutionContainer.TryGetSolution(input, name.Evaluate(ctx)!, out var solution))
|
||||
return new SolutionRef(solution.Value);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -55,11 +53,11 @@ public sealed class SolutionCommand : ToolshedCommand
|
|||
var amount = amountRef.Evaluate(ctx);
|
||||
if (amount > 0)
|
||||
{
|
||||
_solutionContainer.TryAddReagent(input.Owner, input.Solution, name.Value.ID, amount, out _);
|
||||
_solutionContainer.TryAddReagent(input.Solution, name.Value.ID, amount, out _);
|
||||
}
|
||||
else if (amount < 0)
|
||||
{
|
||||
_solutionContainer.RemoveReagent(input.Owner, input.Solution, name.Value.ID, -amount);
|
||||
_solutionContainer.RemoveReagent(input.Solution, name.Value.ID, -amount);
|
||||
}
|
||||
|
||||
return input;
|
||||
|
|
@ -75,10 +73,10 @@ public sealed class SolutionCommand : ToolshedCommand
|
|||
=> input.Select(x => AdjReagent(ctx, x, name, amountRef));
|
||||
}
|
||||
|
||||
public readonly record struct SolutionRef(EntityUid Owner, Solution Solution)
|
||||
public readonly record struct SolutionRef(Entity<SolutionComponent> Solution)
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Owner} {Solution}";
|
||||
return $"{Solution.Owner} {Solution.Comp.Solution}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
|
|
@ -14,11 +15,13 @@ namespace Content.Server.Administration.UI
|
|||
public sealed class EditSolutionsEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
public readonly EntityUid Target;
|
||||
|
||||
public EditSolutionsEui(EntityUid entity)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_solutionContainerSystem = _entityManager.System<SolutionContainerSystem>();
|
||||
Target = entity;
|
||||
}
|
||||
|
||||
|
|
@ -36,8 +39,23 @@ namespace Content.Server.Administration.UI
|
|||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
var solutions = _entityManager.GetComponentOrNull<SolutionContainerManagerComponent>(Target)?.Solutions;
|
||||
return new EditSolutionsEuiState(_entityManager.GetNetEntity(Target), solutions);
|
||||
List<(string Name, NetEntity Solution)>? netSolutions;
|
||||
|
||||
if (_entityManager.TryGetComponent(Target, out SolutionContainerManagerComponent? container) && container.Containers.Count > 0)
|
||||
{
|
||||
netSolutions = new();
|
||||
foreach (var (name, solution) in _solutionContainerSystem.EnumerateSolutions((Target, container)))
|
||||
{
|
||||
if (name is null || !_entityManager.TryGetNetEntity(solution, out var netSolution))
|
||||
continue;
|
||||
|
||||
netSolutions.Add((name, netSolution.Value));
|
||||
}
|
||||
}
|
||||
else
|
||||
netSolutions = null;
|
||||
|
||||
return new EditSolutionsEuiState(_entityManager.GetNetEntity(Target), netSolutions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ public sealed class AmeControllerSystem : EntitySystem
|
|||
if (!Exists(jar))
|
||||
return;
|
||||
|
||||
controller.JarSlot.Remove(jar!.Value);
|
||||
_containerSystem.Remove(jar!.Value, controller.JarSlot);
|
||||
UpdateUi(uid, controller);
|
||||
if (Exists(user))
|
||||
_handsSystem.PickupOrDrop(user, jar!.Value);
|
||||
|
|
@ -303,7 +303,7 @@ public sealed class AmeControllerSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
comp.JarSlot.Insert(args.Used);
|
||||
_containerSystem.Insert(args.Used, comp.JarSlot);
|
||||
_popupSystem.PopupEntity(Loc.GetString("ame-controller-component-interact-using-success"), uid, args.User, PopupType.Medium);
|
||||
|
||||
UpdateUi(uid, comp);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using Content.Server.Animals.Systems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
|
||||
namespace Content.Server.Animals.Components
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -21,10 +21,16 @@ namespace Content.Server.Animals.Components
|
|||
public ProtoId<ReagentPrototype> ReagentId = "Milk";
|
||||
|
||||
/// <summary>
|
||||
/// The solution to add reagent to.
|
||||
/// The name of <see cref="Solution"/>.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public string Solution = "udder";
|
||||
public string SolutionName = "udder";
|
||||
|
||||
/// <summary>
|
||||
/// The solution to add reagent to.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? Solution = null;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of reagent to be generated on update.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Server.Animals.Systems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -20,10 +21,16 @@ public sealed partial class WoolyComponent : Component
|
|||
public ProtoId<ReagentPrototype> ReagentId = "Fiber";
|
||||
|
||||
/// <summary>
|
||||
/// The solution to add reagent to.
|
||||
/// The name of <see cref="Solution"/>.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public string Solution = "wool";
|
||||
public string SolutionName = "wool";
|
||||
|
||||
/// <summary>
|
||||
/// The solution to add reagent to.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? Solution;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of reagent to be generated on update.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using Content.Server.Animals.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
|
|
@ -61,11 +61,11 @@ internal sealed class UdderSystem : EntitySystem
|
|||
_hunger.ModifyHunger(uid, -udder.HungerUsage, hunger);
|
||||
}
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(uid, udder.Solution, out var solution))
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, udder.SolutionName, ref udder.Solution))
|
||||
continue;
|
||||
|
||||
//TODO: toxins from bloodstream !?
|
||||
_solutionContainerSystem.TryAddReagent(uid, solution, udder.ReagentId, udder.QuantityPerUpdate, out _);
|
||||
_solutionContainerSystem.TryAddReagent(udder.Solution.Value, udder.ReagentId, udder.QuantityPerUpdate, out _);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,47 +85,50 @@ internal sealed class UdderSystem : EntitySystem
|
|||
_doAfterSystem.TryStartDoAfter(doargs);
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, UdderComponent component, MilkingDoAfterEvent args)
|
||||
private void OnDoAfter(Entity<UdderComponent> entity, ref MilkingDoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled || args.Args.Used == null)
|
||||
return;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(uid, component.Solution, out var solution))
|
||||
if (!_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
|
||||
return;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSolution))
|
||||
if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
var quantity = solution.Volume;
|
||||
if (quantity == 0)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, args.Args.User);
|
||||
_popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), entity.Owner, args.Args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (quantity > targetSolution.AvailableVolume)
|
||||
quantity = targetSolution.AvailableVolume;
|
||||
|
||||
var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity);
|
||||
_solutionContainerSystem.TryAddSolution(args.Args.Used.Value, targetSolution, split);
|
||||
var split = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, quantity);
|
||||
_solutionContainerSystem.TryAddSolution(targetSoln.Value, split);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), uid,
|
||||
_popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner,
|
||||
args.Args.User, PopupType.Medium);
|
||||
}
|
||||
|
||||
private void AddMilkVerb(EntityUid uid, UdderComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
private void AddMilkVerb(Entity<UdderComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (args.Using == null ||
|
||||
!args.CanInteract ||
|
||||
!EntityManager.HasComponent<RefillableSolutionComponent>(args.Using.Value))
|
||||
return;
|
||||
|
||||
var uid = entity.Owner;
|
||||
var user = args.User;
|
||||
var used = args.Using.Value;
|
||||
AlternativeVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
AttemptMilk(uid, args.User, args.Using.Value);
|
||||
AttemptMilk(uid, user, used);
|
||||
},
|
||||
Text = Loc.GetString("udder-system-verb-milk"),
|
||||
Priority = 2
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using Content.Server.Animals.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Nutrition;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
|
|
@ -52,10 +52,10 @@ public sealed class WoolySystem : EntitySystem
|
|||
_hunger.ModifyHunger(uid, -wooly.HungerUsage, hunger);
|
||||
}
|
||||
|
||||
if (!_solutionContainer.TryGetSolution(uid, wooly.Solution, out var solution))
|
||||
if (!_solutionContainer.ResolveSolution(uid, wooly.SolutionName, ref wooly.Solution))
|
||||
continue;
|
||||
|
||||
_solutionContainer.TryAddReagent(uid, solution, wooly.ReagentId, wooly.Quantity, out _);
|
||||
_solutionContainer.TryAddReagent(wooly.Solution.Value, wooly.ReagentId, wooly.Quantity, out _);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Server.Anomaly.Effects;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -85,10 +86,17 @@ public sealed partial class ReagentProducerAnomalyComponent : Component
|
|||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public ProtoId<ReagentPrototype> ProducingReagent = "Water";
|
||||
|
||||
/// <summary>
|
||||
/// Solution name where the substance is generated
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("solution")]
|
||||
public string Solution = "default";
|
||||
public string SolutionName = "default";
|
||||
|
||||
/// <summary>
|
||||
/// Solution where the substance is generated
|
||||
/// </summary>
|
||||
[DataField("solutionRef")]
|
||||
public Entity<SolutionComponent>? Solution = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Content.Server.Anomaly.Components;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Shared.Anomaly;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Anomaly.Effects;
|
||||
|
|
@ -10,47 +9,19 @@ namespace Content.Server.Anomaly.Effects;
|
|||
/// </summary>
|
||||
public sealed class AnomalyCoreSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<AnomalyCoreComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<AnomalyCoreComponent, PriceCalculationEvent>(OnGetPrice);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<AnomalyCoreComponent> core, ref MapInitEvent args)
|
||||
{
|
||||
core.Comp.DecayMoment = _gameTiming.CurTime + TimeSpan.FromSeconds(core.Comp.TimeToDecay);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<AnomalyCoreComponent>();
|
||||
while (query.MoveNext(out var uid, out var component))
|
||||
{
|
||||
if (component.IsDecayed)
|
||||
continue;
|
||||
|
||||
//When time runs out, we completely decompose
|
||||
if (component.DecayMoment < _gameTiming.CurTime)
|
||||
Decay(uid, component);
|
||||
}
|
||||
}
|
||||
private void OnGetPrice(Entity<AnomalyCoreComponent> core, ref PriceCalculationEvent args)
|
||||
{
|
||||
var timeLeft = core.Comp.DecayMoment - _gameTiming.CurTime;
|
||||
var lerp = (double) (timeLeft.TotalSeconds / core.Comp.TimeToDecay);
|
||||
var lerp = timeLeft.TotalSeconds / core.Comp.TimeToDecay;
|
||||
lerp = Math.Clamp(lerp, 0, 1);
|
||||
|
||||
args.Price = MathHelper.Lerp(core.Comp.EndPrice, core.Comp.StartPrice, lerp);
|
||||
}
|
||||
|
||||
private void Decay(EntityUid uid, AnomalyCoreComponent component)
|
||||
{
|
||||
_appearance.SetData(uid, AnomalyCoreVisuals.Decaying, false);
|
||||
component.IsDecayed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Anomaly.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Anomaly.Effects;
|
||||
/// <summary>
|
||||
|
|
@ -27,41 +27,40 @@ public sealed class InjectionAnomalySystem : EntitySystem
|
|||
_injectableQuery = GetEntityQuery<InjectableSolutionComponent>();
|
||||
}
|
||||
|
||||
private void OnPulse(EntityUid uid, InjectionAnomalyComponent component, ref AnomalyPulseEvent args)
|
||||
private void OnPulse(Entity<InjectionAnomalyComponent> entity, ref AnomalyPulseEvent args)
|
||||
{
|
||||
PulseScalableEffect(uid, component, component.InjectRadius, component.MaxSolutionInjection * args.Severity);
|
||||
PulseScalableEffect(entity, entity.Comp.InjectRadius, entity.Comp.MaxSolutionInjection * args.Severity);
|
||||
}
|
||||
|
||||
private void OnSupercritical(EntityUid uid, InjectionAnomalyComponent component, ref AnomalySupercriticalEvent args)
|
||||
private void OnSupercritical(Entity<InjectionAnomalyComponent> entity, ref AnomalySupercriticalEvent args)
|
||||
{
|
||||
PulseScalableEffect(uid, component, component.SuperCriticalInjectRadius, component.SuperCriticalSolutionInjection);
|
||||
PulseScalableEffect(entity, entity.Comp.SuperCriticalInjectRadius, entity.Comp.SuperCriticalSolutionInjection);
|
||||
}
|
||||
|
||||
private void PulseScalableEffect(EntityUid uid, InjectionAnomalyComponent component, float injectRadius, float maxInject)
|
||||
private void PulseScalableEffect(Entity<InjectionAnomalyComponent> entity, float injectRadius, float maxInject)
|
||||
{
|
||||
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
|
||||
if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var sol))
|
||||
return;
|
||||
|
||||
//We get all the entity in the radius into which the reagent will be injected.
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var xform = xformQuery.GetComponent(uid);
|
||||
var xform = xformQuery.GetComponent(entity);
|
||||
var allEnts = _lookup.GetEntitiesInRange<InjectableSolutionComponent>(xform.MapPosition, injectRadius)
|
||||
.Select(x => x.Owner).ToList();
|
||||
|
||||
//for each matching entity found
|
||||
foreach (var ent in allEnts)
|
||||
{
|
||||
if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable))
|
||||
if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable, out _))
|
||||
continue;
|
||||
|
||||
if (_injectableQuery.TryGetComponent(ent, out var injEnt))
|
||||
{
|
||||
var buffer = sol;
|
||||
_solutionContainer.TryTransferSolution(ent, injectable, buffer, maxInject);
|
||||
_solutionContainer.TryTransferSolution(injectable.Value, sol, maxInject);
|
||||
//Spawn Effect
|
||||
var uidXform = Transform(ent);
|
||||
Spawn(component.VisualEffectPrototype, uidXform.Coordinates);
|
||||
Spawn(entity.Comp.VisualEffectPrototype, uidXform.Coordinates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using Content.Server.Anomaly.Components;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
|
||||
namespace Content.Server.Anomaly.Effects;
|
||||
|
||||
|
|
@ -19,21 +19,21 @@ public sealed class PuddleCreateAnomalySystem : EntitySystem
|
|||
SubscribeLocalEvent<PuddleCreateAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical, before: new[] { typeof(InjectionAnomalySystem) });
|
||||
}
|
||||
|
||||
private void OnPulse(EntityUid uid, PuddleCreateAnomalyComponent component, ref AnomalyPulseEvent args)
|
||||
private void OnPulse(Entity<PuddleCreateAnomalyComponent> entity, ref AnomalyPulseEvent args)
|
||||
{
|
||||
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
|
||||
if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out var sol, out _))
|
||||
return;
|
||||
|
||||
var xform = Transform(uid);
|
||||
var puddleSol = _solutionContainer.SplitSolution(uid, sol, component.MaxPuddleSize * args.Severity);
|
||||
_puddle.TrySplashSpillAt(uid, xform.Coordinates, puddleSol, out _);
|
||||
var xform = Transform(entity.Owner);
|
||||
var puddleSol = _solutionContainer.SplitSolution(sol.Value, entity.Comp.MaxPuddleSize * args.Severity);
|
||||
_puddle.TrySplashSpillAt(entity.Owner, xform.Coordinates, puddleSol, out _);
|
||||
}
|
||||
private void OnSupercritical(EntityUid uid, PuddleCreateAnomalyComponent component, ref AnomalySupercriticalEvent args)
|
||||
private void OnSupercritical(Entity<PuddleCreateAnomalyComponent> entity, ref AnomalySupercriticalEvent args)
|
||||
{
|
||||
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
|
||||
if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var sol))
|
||||
return;
|
||||
var buffer = sol;
|
||||
var xform = Transform(uid);
|
||||
_puddle.TrySpillAt(xform.Coordinates, buffer, out _);
|
||||
|
||||
var xform = Transform(entity.Owner);
|
||||
_puddle.TrySpillAt(xform.Coordinates, sol, out _);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using Content.Server.Anomaly.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Sprite;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Anomaly.Effects;
|
||||
|
||||
|
|
@ -42,17 +42,17 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
|
|||
SubscribeLocalEvent<ReagentProducerAnomalyComponent, MapInitEvent>(OnMapInit);
|
||||
}
|
||||
|
||||
private void OnPulse(EntityUid uid, ReagentProducerAnomalyComponent component, ref AnomalyPulseEvent args)
|
||||
private void OnPulse(Entity<ReagentProducerAnomalyComponent> entity, ref AnomalyPulseEvent args)
|
||||
{
|
||||
if (_random.NextFloat(0.0f, 1.0f) > args.Stability)
|
||||
ChangeReagent(uid, component, args.Severity);
|
||||
ChangeReagent(entity, args.Severity);
|
||||
}
|
||||
|
||||
private void ChangeReagent(EntityUid uid, ReagentProducerAnomalyComponent component, float severity)
|
||||
private void ChangeReagent(Entity<ReagentProducerAnomalyComponent> entity, float severity)
|
||||
{
|
||||
var reagent = GetRandomReagentType(uid, component, severity);
|
||||
component.ProducingReagent = reagent;
|
||||
_audio.PlayPvs(component.ChangeSound, uid);
|
||||
var reagent = GetRandomReagentType(entity, severity);
|
||||
entity.Comp.ProducingReagent = reagent;
|
||||
_audio.PlayPvs(entity.Comp.ChangeSound, entity);
|
||||
}
|
||||
|
||||
//reagent realtime generation
|
||||
|
|
@ -68,7 +68,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
|
|||
if (component.AccumulatedFrametime < component.UpdateInterval)
|
||||
continue;
|
||||
|
||||
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var producerSol))
|
||||
if (!_solutionContainer.ResolveSolution(uid, component.SolutionName, ref component.Solution, out var producerSolution))
|
||||
continue;
|
||||
|
||||
Solution newSol = new();
|
||||
|
|
@ -76,7 +76,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
|
|||
if (anomaly.Severity >= 0.97) reagentProducingAmount *= component.SupercriticalReagentProducingModifier;
|
||||
|
||||
newSol.AddReagent(component.ProducingReagent, reagentProducingAmount);
|
||||
_solutionContainer.TryAddSolution(uid, producerSol, newSol); //TO DO - the container is not fully filled.
|
||||
_solutionContainer.TryAddSolution(component.Solution.Value, newSol); //TO DO - the container is not fully filled.
|
||||
|
||||
component.AccumulatedFrametime = 0;
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
|
|||
// and nothing worked out for me. So for now it will be like this.
|
||||
if (component.NeedRecolor)
|
||||
{
|
||||
var color = producerSol.GetColor(_prototypeManager);
|
||||
var color = producerSolution.GetColor(_prototypeManager);
|
||||
_light.SetColor(uid, color);
|
||||
if (TryComp<RandomSpriteComponent>(uid, out var randomSprite))
|
||||
{
|
||||
|
|
@ -103,9 +103,9 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, ReagentProducerAnomalyComponent component, MapInitEvent args)
|
||||
private void OnMapInit(Entity<ReagentProducerAnomalyComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
ChangeReagent(uid, component, 0.1f); //MapInit Reagent 100% change
|
||||
ChangeReagent(entity, 0.1f); //MapInit Reagent 100% change
|
||||
}
|
||||
|
||||
// returns a random reagent based on a system of random weights.
|
||||
|
|
@ -117,33 +117,33 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
|
|||
// After that, a random reagent in the selected category is selected.
|
||||
//
|
||||
// Such a system is made to control the danger and interest of the anomaly more.
|
||||
private string GetRandomReagentType(EntityUid uid, ReagentProducerAnomalyComponent component, float severity)
|
||||
private string GetRandomReagentType(Entity<ReagentProducerAnomalyComponent> entity, float severity)
|
||||
{
|
||||
//Category Weight Randomization
|
||||
var currentWeightDangerous = MathHelper.Lerp(component.WeightSpreadDangerous.X, component.WeightSpreadDangerous.Y, severity);
|
||||
var currentWeightFun = MathHelper.Lerp(component.WeightSpreadFun.X, component.WeightSpreadFun.Y, severity);
|
||||
var currentWeightUseful = MathHelper.Lerp(component.WeightSpreadUseful.X, component.WeightSpreadUseful.Y, severity);
|
||||
var currentWeightDangerous = MathHelper.Lerp(entity.Comp.WeightSpreadDangerous.X, entity.Comp.WeightSpreadDangerous.Y, severity);
|
||||
var currentWeightFun = MathHelper.Lerp(entity.Comp.WeightSpreadFun.X, entity.Comp.WeightSpreadFun.Y, severity);
|
||||
var currentWeightUseful = MathHelper.Lerp(entity.Comp.WeightSpreadUseful.X, entity.Comp.WeightSpreadUseful.Y, severity);
|
||||
|
||||
var sumWeight = currentWeightDangerous + currentWeightFun + currentWeightUseful;
|
||||
var rnd = _random.NextFloat(0f, sumWeight);
|
||||
//Dangerous
|
||||
if (rnd <= currentWeightDangerous && component.DangerousChemicals.Count > 0)
|
||||
if (rnd <= currentWeightDangerous && entity.Comp.DangerousChemicals.Count > 0)
|
||||
{
|
||||
var reagent = _random.Pick(component.DangerousChemicals);
|
||||
var reagent = _random.Pick(entity.Comp.DangerousChemicals);
|
||||
return reagent;
|
||||
}
|
||||
else rnd -= currentWeightDangerous;
|
||||
//Fun
|
||||
if (rnd <= currentWeightFun && component.FunChemicals.Count > 0)
|
||||
if (rnd <= currentWeightFun && entity.Comp.FunChemicals.Count > 0)
|
||||
{
|
||||
var reagent = _random.Pick(component.FunChemicals);
|
||||
var reagent = _random.Pick(entity.Comp.FunChemicals);
|
||||
return reagent;
|
||||
}
|
||||
else rnd -= currentWeightFun;
|
||||
//Useful
|
||||
if (rnd <= currentWeightUseful && component.UsefulChemicals.Count > 0)
|
||||
if (rnd <= currentWeightUseful && entity.Comp.UsefulChemicals.Count > 0)
|
||||
{
|
||||
var reagent = _random.Pick(component.UsefulChemicals);
|
||||
var reagent = _random.Pick(entity.Comp.UsefulChemicals);
|
||||
return reagent;
|
||||
}
|
||||
//We should never end up here.
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ public sealed class AntagSelectionSystem : GameRuleSystem<GameRuleComponent>
|
|||
{
|
||||
if (_containerSystem.CanInsert(itemToSpawn, pocket1Slot))
|
||||
{
|
||||
pocket1Slot.Insert(itemToSpawn);
|
||||
_containerSystem.Insert(itemToSpawn, pocket1Slot);
|
||||
}
|
||||
}
|
||||
else if (_inventory.TryGetSlotContainer(antag, "pocket2", out var pocket2Slot, out _))
|
||||
|
|
@ -294,7 +294,7 @@ public sealed class AntagSelectionSystem : GameRuleSystem<GameRuleComponent>
|
|||
{
|
||||
if (_containerSystem.CanInsert(itemToSpawn, pocket2Slot))
|
||||
{
|
||||
pocket2Slot.Insert(itemToSpawn);
|
||||
_containerSystem.Insert(itemToSpawn, pocket2Slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.IgnitionSource;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Server.Temperature.Systems;
|
||||
|
|
@ -35,6 +36,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly StunSystem _stunSystem = default!;
|
||||
[Dependency] private readonly TemperatureSystem _temperatureSystem = default!;
|
||||
[Dependency] private readonly IgnitionSourceSystem _ignitionSourceSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
|
@ -285,6 +287,8 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
flammable.OnFire = false;
|
||||
flammable.FireStacks = 0;
|
||||
|
||||
_ignitionSourceSystem.SetIgnited(uid, false);
|
||||
|
||||
UpdateAppearance(uid, flammable);
|
||||
}
|
||||
|
||||
|
|
@ -378,6 +382,18 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
|
||||
if (flammable.FireStacks > 0)
|
||||
{
|
||||
var air = _atmosphereSystem.GetContainingMixture(uid);
|
||||
|
||||
// If we're in an oxygenless environment, put the fire out.
|
||||
if (air == null || air.GetMoles(Gas.Oxygen) < 1f)
|
||||
{
|
||||
Extinguish(uid, flammable);
|
||||
continue;
|
||||
}
|
||||
|
||||
EnsureComp<IgnitionSourceComponent>(uid);
|
||||
_ignitionSourceSystem.SetIgnited(uid);
|
||||
|
||||
// TODO FLAMMABLE: further balancing
|
||||
var damageScale = Math.Min((int)flammable.FireStacks, 5);
|
||||
|
||||
|
|
@ -391,24 +407,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||
else
|
||||
{
|
||||
Extinguish(uid, flammable);
|
||||
continue;
|
||||
}
|
||||
|
||||
var air = _atmosphereSystem.GetContainingMixture(uid);
|
||||
|
||||
// If we're in an oxygenless environment, put the fire out.
|
||||
if (air == null || air.GetMoles(Gas.Oxygen) < 1f)
|
||||
{
|
||||
Extinguish(uid, flammable);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(transform.GridUid != null)
|
||||
{
|
||||
_atmosphereSystem.HotspotExpose(transform.GridUid.Value,
|
||||
_transformSystem.GetGridOrMapTilePosition(uid, transform),
|
||||
700f, 50f, uid, true);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Server.Atmos.Piping.Unary.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Unary.Components;
|
||||
|
||||
|
|
@ -21,6 +22,12 @@ public sealed partial class GasCondenserComponent : Component
|
|||
[DataField]
|
||||
public string SolutionId = "tank";
|
||||
|
||||
/// <summary>
|
||||
/// The solution that gases are condensed into.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? Solution = null;
|
||||
|
||||
/// <summary>
|
||||
/// For a condenser, how many U of reagents are given per each mole of gas.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ using Content.Server.NodeContainer;
|
|||
using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Server.NodeContainer.Nodes;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Unary.EntitySystems;
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ public sealed class GasCondenserSystem : EntitySystem
|
|||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly PowerReceiverSystem _power = default!;
|
||||
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solution = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -27,12 +28,12 @@ public sealed class GasCondenserSystem : EntitySystem
|
|||
SubscribeLocalEvent<GasCondenserComponent, AtmosDeviceUpdateEvent>(OnCondenserUpdated);
|
||||
}
|
||||
|
||||
private void OnCondenserUpdated(EntityUid uid, GasCondenserComponent component, ref AtmosDeviceUpdateEvent args)
|
||||
private void OnCondenserUpdated(Entity<GasCondenserComponent> entity, ref AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
if (!(_power.IsPowered(uid) && TryComp<ApcPowerReceiverComponent>(uid, out var receiver))
|
||||
|| !TryComp<NodeContainerComponent>(uid, out var nodeContainer)
|
||||
|| !_nodeContainer.TryGetNode(nodeContainer, component.Inlet, out PipeNode? inlet)
|
||||
|| !_solution.TryGetSolution(uid, component.SolutionId, out var solution))
|
||||
if (!(_power.IsPowered(entity) && TryComp<ApcPowerReceiverComponent>(entity, out var receiver))
|
||||
|| !TryComp<NodeContainerComponent>(entity, out var nodeContainer)
|
||||
|| !_nodeContainer.TryGetNode(nodeContainer, entity.Comp.Inlet, out PipeNode? inlet)
|
||||
|| !_solution.ResolveSolution(entity.Owner, entity.Comp.SolutionId, ref entity.Comp.Solution, out var solution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -48,18 +49,21 @@ public sealed class GasCondenserSystem : EntitySystem
|
|||
if (moles <= 0)
|
||||
continue;
|
||||
|
||||
if (_atmosphereSystem.GetGas(i).Reagent is not {} gasReagent)
|
||||
if (_atmosphereSystem.GetGas(i).Reagent is not { } gasReagent)
|
||||
continue;
|
||||
|
||||
var moleToReagentMultiplier = component.MolesToReagentMultiplier;
|
||||
var amount = moles * moleToReagentMultiplier;
|
||||
|
||||
if (_solution.TryAddReagent(uid, solution, gasReagent, amount, out var remaining))
|
||||
var moleToReagentMultiplier = entity.Comp.MolesToReagentMultiplier;
|
||||
var amount = FixedPoint2.Min(FixedPoint2.New(moles * moleToReagentMultiplier), solution.AvailableVolume);
|
||||
if (amount <= 0)
|
||||
continue;
|
||||
|
||||
solution.AddReagent(gasReagent, amount);
|
||||
|
||||
// if we have leftover reagent, then convert it back to moles and put it back in the mixture.
|
||||
inlet.Air.AdjustMoles(i, remaining.Float() / moleToReagentMultiplier);
|
||||
inlet.Air.AdjustMoles(i, moles - (amount.Float() / moleToReagentMultiplier));
|
||||
}
|
||||
|
||||
_solution.UpdateChemicals(entity.Comp.Solution.Value);
|
||||
}
|
||||
|
||||
public float NumberOfMolesToConvert(ApcPowerReceiverComponent comp, GasMixture mix, float dt)
|
||||
|
|
|
|||
|
|
@ -34,52 +34,52 @@ namespace Content.Server.Body.Components
|
|||
/// <summary>
|
||||
/// How much should bleeding should be reduced every update interval?
|
||||
/// </summary>
|
||||
[DataField("bleedReductionAmount")]
|
||||
[DataField]
|
||||
public float BleedReductionAmount = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How high can <see cref="BleedAmount"/> go?
|
||||
/// </summary>
|
||||
[DataField("maxBleedAmount")]
|
||||
[DataField]
|
||||
public float MaxBleedAmount = 10.0f;
|
||||
|
||||
/// <summary>
|
||||
/// What percentage of current blood is necessary to avoid dealing blood loss damage?
|
||||
/// </summary>
|
||||
[DataField("bloodlossThreshold")]
|
||||
[DataField]
|
||||
public float BloodlossThreshold = 0.9f;
|
||||
|
||||
/// <summary>
|
||||
/// The base bloodloss damage to be incurred if below <see cref="BloodlossThreshold"/>
|
||||
/// The default values are defined per mob/species in YML.
|
||||
/// </summary>
|
||||
[DataField("bloodlossDamage", required: true)]
|
||||
[DataField(required: true)]
|
||||
public DamageSpecifier BloodlossDamage = new();
|
||||
|
||||
/// <summary>
|
||||
/// The base bloodloss damage to be healed if above <see cref="BloodlossThreshold"/>
|
||||
/// The default values are defined per mob/species in YML.
|
||||
/// </summary>
|
||||
[DataField("bloodlossHealDamage", required: true)]
|
||||
[DataField(required: true)]
|
||||
public DamageSpecifier BloodlossHealDamage = new();
|
||||
|
||||
/// <summary>
|
||||
/// How frequently should this bloodstream update, in seconds?
|
||||
/// </summary>
|
||||
[DataField("updateInterval")]
|
||||
[DataField]
|
||||
public float UpdateInterval = 3.0f;
|
||||
|
||||
// TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth.
|
||||
/// <summary>
|
||||
/// How much reagent of blood should be restored each update interval?
|
||||
/// </summary>
|
||||
[DataField("bloodRefreshAmount")]
|
||||
[DataField]
|
||||
public float BloodRefreshAmount = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How much blood needs to be in the temporary solution in order to create a puddle?
|
||||
/// </summary>
|
||||
[DataField("bleedPuddleThreshold")]
|
||||
[DataField]
|
||||
public FixedPoint2 BleedPuddleThreshold = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -89,19 +89,19 @@ namespace Content.Server.Body.Components
|
|||
/// <remarks>
|
||||
/// For example, piercing damage is increased while poison damage is nullified entirely.
|
||||
/// </remarks>
|
||||
[DataField("damageBleedModifiers", customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
|
||||
[DataField(customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
|
||||
public string DamageBleedModifiers = "BloodlossHuman";
|
||||
|
||||
/// <summary>
|
||||
/// The sound to be played when a weapon instantly deals blood loss damage.
|
||||
/// </summary>
|
||||
[DataField("instantBloodSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood");
|
||||
|
||||
/// <summary>
|
||||
/// The sound to be played when some damage actually heals bleeding rather than starting it.
|
||||
/// </summary>
|
||||
[DataField("bloodHealedSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier BloodHealedSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
|
||||
|
||||
// TODO probably damage bleed thresholds.
|
||||
|
|
@ -109,14 +109,14 @@ namespace Content.Server.Body.Components
|
|||
/// <summary>
|
||||
/// Max volume of internal chemical solution storage
|
||||
/// </summary>
|
||||
[DataField("chemicalMaxVolume")]
|
||||
[DataField]
|
||||
public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250);
|
||||
|
||||
/// <summary>
|
||||
/// Max volume of internal blood storage,
|
||||
/// and starting level of blood.
|
||||
/// </summary>
|
||||
[DataField("bloodMaxVolume")]
|
||||
[DataField]
|
||||
public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -125,29 +125,40 @@ namespace Content.Server.Body.Components
|
|||
/// <remarks>
|
||||
/// Slime-people might use slime as their blood or something like that.
|
||||
/// </remarks>
|
||||
[DataField("bloodReagent")]
|
||||
[DataField]
|
||||
public string BloodReagent = "Blood";
|
||||
|
||||
/// <summary>
|
||||
/// Internal solution for reagent storage
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[Access(typeof(BloodstreamSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
|
||||
public Solution ChemicalSolution = default!;
|
||||
/// <summary>Name/Key that <see cref="BloodSolution"/> is indexed by.</summary>
|
||||
[DataField]
|
||||
public string BloodSolutionName = DefaultBloodSolutionName;
|
||||
|
||||
/// <summary>Name/Key that <see cref="ChemicalSolution"/> is indexed by.</summary>
|
||||
[DataField]
|
||||
public string ChemicalSolutionName = DefaultChemicalsSolutionName;
|
||||
|
||||
/// <summary>Name/Key that <see cref="TemporarySolution"/> is indexed by.</summary>
|
||||
[DataField]
|
||||
public string BloodTemporarySolutionName = DefaultBloodTemporarySolutionName;
|
||||
|
||||
/// <summary>
|
||||
/// Internal solution for blood storage
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Solution BloodSolution = default!;
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? BloodSolution = null;
|
||||
|
||||
/// <summary>
|
||||
/// Internal solution for reagent storage
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? ChemicalSolution = null;
|
||||
|
||||
/// <summary>
|
||||
/// Temporary blood solution.
|
||||
/// When blood is lost, it goes to this solution, and when this
|
||||
/// solution hits a certain cap, the blood is actually spilled as a puddle.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Solution BloodTemporarySolution = default!;
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? TemporarySolution = null;
|
||||
|
||||
/// <summary>
|
||||
/// Variable that stores the amount of status time added by having a low blood level.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Content.Server.Body.Components;
|
|||
[RegisterComponent, Access(typeof(LungSystem))]
|
||||
public sealed partial class LungComponent : Component
|
||||
{
|
||||
[DataField("air")]
|
||||
[DataField]
|
||||
[Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
|
||||
public GasMixture Air { get; set; } = new()
|
||||
{
|
||||
|
|
@ -16,7 +16,15 @@ public sealed partial class LungComponent : Component
|
|||
Temperature = Atmospherics.NormalBodyTemperature
|
||||
};
|
||||
|
||||
[ViewVariables]
|
||||
[Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
|
||||
public Solution LungSolution = default!;
|
||||
/// <summary>
|
||||
/// The name/key of the solution on this entity which these lungs act on.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string SolutionName = LungSystem.LungSolutionName;
|
||||
|
||||
/// <summary>
|
||||
/// The solution on this entity that these lungs act on.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? Solution = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Content.Server.Body.Components
|
|||
/// How often to metabolize reagents, in seconds.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DataField("updateFrequency")]
|
||||
[DataField]
|
||||
public float UpdateFrequency = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -33,13 +33,13 @@ namespace Content.Server.Body.Components
|
|||
/// <remarks>
|
||||
/// Most things will use the parent entity (bloodstream).
|
||||
/// </remarks>
|
||||
[DataField("solutionOnBody")]
|
||||
[DataField]
|
||||
public bool SolutionOnBody = true;
|
||||
|
||||
/// <summary>
|
||||
/// List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e.
|
||||
/// </summary>
|
||||
[DataField("metabolizerTypes", customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))]
|
||||
[DataField(customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))]
|
||||
[Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
|
||||
public HashSet<string>? MetabolizerTypes = null;
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ namespace Content.Server.Body.Components
|
|||
/// Should this metabolizer remove chemicals that have no metabolisms defined?
|
||||
/// As a stop-gap, basically.
|
||||
/// </summary>
|
||||
[DataField("removeEmpty")]
|
||||
[DataField]
|
||||
public bool RemoveEmpty = false;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -72,7 +72,7 @@ namespace Content.Server.Body.Components
|
|||
[DataDefinition]
|
||||
public sealed partial class MetabolismGroupEntry
|
||||
{
|
||||
[DataField("id", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
|
||||
[DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
|
||||
public string Id = default!;
|
||||
|
||||
[DataField("rateModifier")]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Server.Body.Components
|
||||
|
|
@ -15,26 +14,32 @@ namespace Content.Server.Body.Components
|
|||
/// <summary>
|
||||
/// How fast should this component update, in seconds?
|
||||
/// </summary>
|
||||
[DataField("updateInterval")]
|
||||
[DataField]
|
||||
public float UpdateInterval = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The solution inside of this stomach this transfers reagents to the body.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? Solution = null;
|
||||
|
||||
/// <summary>
|
||||
/// What solution should this stomach push reagents into, on the body?
|
||||
/// </summary>
|
||||
[DataField("bodySolutionName")]
|
||||
[DataField]
|
||||
public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds between reagents being ingested and them being
|
||||
/// transferred to <see cref="BloodstreamComponent"/>
|
||||
/// </summary>
|
||||
[DataField("digestionDelay")]
|
||||
[DataField]
|
||||
public float DigestionDelay = 20;
|
||||
|
||||
/// <summary>
|
||||
/// A whitelist for what special-digestible-required foods this stomach is capable of eating.
|
||||
/// </summary>
|
||||
[DataField("specialDigestible")]
|
||||
[DataField]
|
||||
public EntityWhitelist? SpecialDigestible = null;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Chemistry.ReactionEffects;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Forensics;
|
||||
using Content.Server.HealthExaminable;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Alert;
|
||||
|
|
@ -16,13 +18,9 @@ using Content.Shared.Mobs.Systems;
|
|||
using Content.Shared.Popups;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.Speech.EntitySystems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Content.Shared.Speech.EntitySystems;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Content.Server.Forensics;
|
||||
|
||||
namespace Content.Server.Body.Systems;
|
||||
|
||||
|
|
@ -51,14 +49,13 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<BloodstreamComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
SubscribeLocalEvent<BloodstreamComponent, ReactionAttemptEvent>(OnReactionAttempt);
|
||||
SubscribeLocalEvent<BloodstreamComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
|
||||
SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
}
|
||||
|
||||
private void OnReactionAttempt(EntityUid uid, BloodstreamComponent component, ReactionAttemptEvent args)
|
||||
private void OnReactionAttempt(Entity<BloodstreamComponent> entity, ref ReactionAttemptEvent args)
|
||||
{
|
||||
if (args.Solution.Name != BloodstreamComponent.DefaultBloodSolutionName
|
||||
&& args.Solution.Name != BloodstreamComponent.DefaultChemicalsSolutionName
|
||||
&& args.Solution.Name != BloodstreamComponent.DefaultBloodTemporarySolutionName)
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
foreach (var effect in args.Reaction.Effects)
|
||||
|
|
@ -67,7 +64,7 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
{
|
||||
case CreateEntityReactionEffect: // Prevent entities from spawning in the bloodstream
|
||||
case AreaReactionEffect: // No spontaneous smoke or foam leaking out of blood vessels.
|
||||
args.Cancel();
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -81,6 +78,16 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
// Having cheese-clots form in your veins can't be good for you.
|
||||
}
|
||||
|
||||
private void OnReactionAttempt(Entity<BloodstreamComponent> entity, ref SolutionRelayEvent<ReactionAttemptEvent> args)
|
||||
{
|
||||
if (args.Name != entity.Comp.BloodSolutionName
|
||||
&& args.Name != entity.Comp.ChemicalSolutionName
|
||||
&& args.Name != entity.Comp.BloodTemporarySolutionName)
|
||||
return;
|
||||
|
||||
OnReactionAttempt(entity, ref args.Event);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
|
@ -95,8 +102,11 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
|
||||
bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
|
||||
continue;
|
||||
|
||||
// Adds blood to their blood level if it is below the maximum; Blood regeneration. Must be alive.
|
||||
if (bloodstream.BloodSolution.Volume < bloodstream.BloodSolution.MaxVolume && !_mobStateSystem.IsDead(uid))
|
||||
if (bloodSolution.Volume < bloodSolution.MaxVolume && !_mobStateSystem.IsDead(uid))
|
||||
{
|
||||
TryModifyBloodLevel(uid, bloodstream.BloodRefreshAmount, bloodstream);
|
||||
}
|
||||
|
|
@ -143,19 +153,18 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, BloodstreamComponent component, ComponentInit args)
|
||||
private void OnComponentInit(Entity<BloodstreamComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
component.ChemicalSolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultChemicalsSolutionName);
|
||||
component.BloodSolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultBloodSolutionName);
|
||||
component.BloodTemporarySolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultBloodTemporarySolutionName);
|
||||
var chemicalSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.ChemicalSolutionName);
|
||||
var bloodSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.BloodSolutionName);
|
||||
var tempSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.BloodTemporarySolutionName);
|
||||
|
||||
component.ChemicalSolution.MaxVolume = component.ChemicalMaxVolume;
|
||||
component.BloodSolution.MaxVolume = component.BloodMaxVolume;
|
||||
component.BloodTemporarySolution.MaxVolume = component.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
|
||||
chemicalSolution.MaxVolume = entity.Comp.ChemicalMaxVolume;
|
||||
bloodSolution.MaxVolume = entity.Comp.BloodMaxVolume;
|
||||
tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
|
||||
|
||||
// Fill blood solution with BLOOD
|
||||
_solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent,
|
||||
component.BloodMaxVolume, out _);
|
||||
bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume);
|
||||
}
|
||||
|
||||
private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args)
|
||||
|
|
@ -250,34 +259,45 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
component.AccumulatedFrametime = component.UpdateInterval;
|
||||
}
|
||||
|
||||
private void OnRejuvenate(EntityUid uid, BloodstreamComponent component, RejuvenateEvent args)
|
||||
private void OnRejuvenate(Entity<BloodstreamComponent> entity, ref RejuvenateEvent args)
|
||||
{
|
||||
TryModifyBleedAmount(uid, -component.BleedAmount, component);
|
||||
TryModifyBloodLevel(uid, component.BloodSolution.AvailableVolume, component);
|
||||
_solutionContainerSystem.RemoveAllSolution(uid, component.ChemicalSolution);
|
||||
TryModifyBleedAmount(entity.Owner, -entity.Comp.BleedAmount, entity.Comp);
|
||||
|
||||
if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution))
|
||||
TryModifyBloodLevel(entity.Owner, bloodSolution.AvailableVolume, entity.Comp);
|
||||
|
||||
if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.ChemicalSolutionName, ref entity.Comp.ChemicalSolution))
|
||||
_solutionContainerSystem.RemoveAllSolution(entity.Comp.ChemicalSolution.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to transfer provided solution to internal solution.
|
||||
/// </summary>
|
||||
public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component=null)
|
||||
public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
return _solutionContainerSystem.TryAddSolution(uid, component.ChemicalSolution, solution);
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution))
|
||||
return false;
|
||||
|
||||
return _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution);
|
||||
}
|
||||
|
||||
public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null) {
|
||||
public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
for (var i = component.ChemicalSolution.Contents.Count - 1; i >= 0; i--)
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution))
|
||||
return false;
|
||||
|
||||
for (var i = chemSolution.Contents.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var (reagentId, _) = component.ChemicalSolution.Contents[i];
|
||||
var (reagentId, _) = chemSolution.Contents[i];
|
||||
if (reagentId.Prototype != excludedReagentID)
|
||||
{
|
||||
_solutionContainerSystem.RemoveReagent(uid, component.ChemicalSolution, reagentId, quantity);
|
||||
_solutionContainerSystem.RemoveReagent(component.ChemicalSolution.Value, reagentId, quantity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +309,10 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
if (!Resolve(uid, ref component))
|
||||
return 0.0f;
|
||||
|
||||
return component.BloodSolution.FillFraction;
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
|
||||
return 0.0f;
|
||||
|
||||
return bloodSolution.FillFraction;
|
||||
}
|
||||
|
||||
public void SetBloodLossThreshold(EntityUid uid, float threshold, BloodstreamComponent? comp = null)
|
||||
|
|
@ -308,28 +331,41 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution))
|
||||
return false;
|
||||
|
||||
if (amount >= 0)
|
||||
return _solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent, amount, out _);
|
||||
return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, out _);
|
||||
|
||||
// Removal is more involved,
|
||||
// since we also wanna handle moving it to the temporary solution
|
||||
// and then spilling it if necessary.
|
||||
var newSol = component.BloodSolution.SplitSolution(-amount);
|
||||
component.BloodTemporarySolution.AddSolution(newSol, _prototypeManager);
|
||||
var newSol = _solutionContainerSystem.SplitSolution(component.BloodSolution.Value, -amount);
|
||||
|
||||
if (component.BloodTemporarySolution.Volume > component.BleedPuddleThreshold)
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodTemporarySolutionName, ref component.TemporarySolution, out var tempSolution))
|
||||
return true;
|
||||
|
||||
tempSolution.AddSolution(newSol, _prototypeManager);
|
||||
|
||||
if (tempSolution.Volume > component.BleedPuddleThreshold)
|
||||
{
|
||||
// Pass some of the chemstream into the spilled blood.
|
||||
var temp = component.ChemicalSolution.SplitSolution(component.BloodTemporarySolution.Volume / 10);
|
||||
component.BloodTemporarySolution.AddSolution(temp, _prototypeManager);
|
||||
if (_puddleSystem.TrySpillAt(uid, component.BloodTemporarySolution, out var puddleUid, false))
|
||||
if (_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution))
|
||||
{
|
||||
var temp = _solutionContainerSystem.SplitSolution(component.ChemicalSolution.Value, tempSolution.Volume / 10);
|
||||
tempSolution.AddSolution(temp, _prototypeManager);
|
||||
}
|
||||
|
||||
if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, false))
|
||||
{
|
||||
_forensicsSystem.TransferDna(puddleUid, uid, false);
|
||||
}
|
||||
|
||||
component.BloodTemporarySolution.RemoveAllSolution();
|
||||
tempSolution.RemoveAllSolution();
|
||||
}
|
||||
|
||||
_solutionContainerSystem.UpdateChemicals(component.TemporarySolution.Value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -363,16 +399,28 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var max = component.BloodSolution.MaxVolume + component.BloodTemporarySolution.MaxVolume +
|
||||
component.ChemicalSolution.MaxVolume;
|
||||
var tempSol = new Solution() { MaxVolume = max };
|
||||
var tempSol = new Solution();
|
||||
|
||||
tempSol.AddSolution(component.BloodSolution, _prototypeManager);
|
||||
component.BloodSolution.RemoveAllSolution();
|
||||
tempSol.AddSolution(component.BloodTemporarySolution, _prototypeManager);
|
||||
component.BloodTemporarySolution.RemoveAllSolution();
|
||||
tempSol.AddSolution(component.ChemicalSolution, _prototypeManager);
|
||||
component.ChemicalSolution.RemoveAllSolution();
|
||||
if (_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
|
||||
{
|
||||
tempSol.MaxVolume += bloodSolution.MaxVolume;
|
||||
tempSol.AddSolution(bloodSolution, _prototypeManager);
|
||||
_solutionContainerSystem.RemoveAllSolution(component.BloodSolution.Value);
|
||||
}
|
||||
|
||||
if (_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution))
|
||||
{
|
||||
tempSol.MaxVolume += chemSolution.MaxVolume;
|
||||
tempSol.AddSolution(chemSolution, _prototypeManager);
|
||||
_solutionContainerSystem.RemoveAllSolution(component.ChemicalSolution.Value);
|
||||
}
|
||||
|
||||
if (_solutionContainerSystem.ResolveSolution(uid, component.BloodTemporarySolutionName, ref component.TemporarySolution, out var tempSolution))
|
||||
{
|
||||
tempSol.MaxVolume += tempSolution.MaxVolume;
|
||||
tempSol.AddSolution(tempSolution, _prototypeManager);
|
||||
_solutionContainerSystem.RemoveAllSolution(component.TemporarySolution.Value);
|
||||
}
|
||||
|
||||
if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid))
|
||||
{
|
||||
|
|
@ -388,13 +436,20 @@ public sealed class BloodstreamSystem : EntitySystem
|
|||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
if(reagent == component.BloodReagent)
|
||||
if (reagent == component.BloodReagent)
|
||||
return;
|
||||
|
||||
var currentVolume = component.BloodSolution.Volume;
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
|
||||
{
|
||||
component.BloodReagent = reagent;
|
||||
return;
|
||||
}
|
||||
|
||||
var currentVolume = bloodSolution.RemoveReagent(component.BloodReagent, bloodSolution.Volume);
|
||||
|
||||
component.BloodReagent = reagent;
|
||||
component.BloodSolution.RemoveAllSolution();
|
||||
_solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent, currentVolume, out _);
|
||||
|
||||
if (currentVolume > 0)
|
||||
_solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, currentVolume, out _);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Inventory.Events;
|
||||
|
||||
|
|
@ -44,11 +44,11 @@ public sealed class LungSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, LungComponent component, ComponentInit args)
|
||||
private void OnComponentInit(Entity<LungComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
component.LungSolution = _solutionContainerSystem.EnsureSolution(uid, LungSolutionName);
|
||||
component.LungSolution.MaxVolume = 100.0f;
|
||||
component.LungSolution.CanReact = false; // No dexalin lungs
|
||||
var solution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName);
|
||||
solution.MaxVolume = 100.0f;
|
||||
solution.CanReact = false; // No dexalin lungs
|
||||
}
|
||||
|
||||
private void OnMaskToggled(Entity<BreathToolComponent> ent, ref ItemMaskToggledEvent args)
|
||||
|
|
@ -71,6 +71,9 @@ public sealed class LungSystem : EntitySystem
|
|||
|
||||
public void GasToReagent(EntityUid uid, LungComponent lung)
|
||||
{
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, lung.SolutionName, ref lung.Solution, out var solution))
|
||||
return;
|
||||
|
||||
foreach (var gas in Enum.GetValues<Gas>())
|
||||
{
|
||||
var i = (int) gas;
|
||||
|
|
@ -81,11 +84,13 @@ public sealed class LungSystem : EntitySystem
|
|||
if (reagent == null) continue;
|
||||
|
||||
var amount = moles * Atmospherics.BreathMolesToReagentMultiplier;
|
||||
_solutionContainerSystem.TryAddReagent(uid, lung.LungSolution, reagent, amount, out _);
|
||||
solution.AddReagent(reagent, amount);
|
||||
|
||||
// We don't remove the gas from the lung mix,
|
||||
// that's the responsibility of whatever gas is being metabolized.
|
||||
// Most things will just want to exhale again.
|
||||
}
|
||||
|
||||
_solutionContainerSystem.UpdateChemicals(lung.Solution.Value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Body.Organ;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.FixedPoint;
|
||||
|
|
@ -37,15 +37,15 @@ namespace Content.Server.Body.Systems
|
|||
SubscribeLocalEvent<MetabolizerComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
}
|
||||
|
||||
private void OnMetabolizerInit(EntityUid uid, MetabolizerComponent component, ComponentInit args)
|
||||
private void OnMetabolizerInit(Entity<MetabolizerComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
if (!component.SolutionOnBody)
|
||||
if (!entity.Comp.SolutionOnBody)
|
||||
{
|
||||
_solutionContainerSystem.EnsureSolution(uid, component.SolutionName);
|
||||
_solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName);
|
||||
}
|
||||
else if (_organQuery.CompOrNull(uid)?.Body is { } body)
|
||||
else if (_organQuery.CompOrNull(entity)?.Body is { } body)
|
||||
{
|
||||
_solutionContainerSystem.EnsureSolution(body, component.SolutionName);
|
||||
_solutionContainerSystem.EnsureSolution(body, entity.Comp.SolutionName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +95,7 @@ namespace Content.Server.Body.Systems
|
|||
|
||||
// First step is get the solution we actually care about
|
||||
Solution? solution = null;
|
||||
Entity<SolutionComponent>? soln = default!;
|
||||
EntityUid? solutionEntityUid = null;
|
||||
|
||||
SolutionContainerManagerComponent? manager = null;
|
||||
|
|
@ -106,7 +107,7 @@ namespace Content.Server.Body.Systems
|
|||
if (!_solutionQuery.Resolve(body, ref manager, false))
|
||||
return;
|
||||
|
||||
_solutionContainerSystem.TryGetSolution(body, meta.SolutionName, out solution, manager);
|
||||
_solutionContainerSystem.TryGetSolution((body, manager), meta.SolutionName, out soln, out solution);
|
||||
solutionEntityUid = body;
|
||||
}
|
||||
}
|
||||
|
|
@ -115,11 +116,11 @@ namespace Content.Server.Body.Systems
|
|||
if (!_solutionQuery.Resolve(uid, ref manager, false))
|
||||
return;
|
||||
|
||||
_solutionContainerSystem.TryGetSolution(uid, meta.SolutionName, out solution, manager);
|
||||
_solutionContainerSystem.TryGetSolution((uid, manager), meta.SolutionName, out soln, out solution);
|
||||
solutionEntityUid = uid;
|
||||
}
|
||||
|
||||
if (solutionEntityUid == null || solution == null || solution.Contents.Count == 0)
|
||||
if (solutionEntityUid == null || soln is null || solution is null || solution.Contents.Count == 0)
|
||||
return;
|
||||
|
||||
// randomize the reagent list so we don't have any weird quirks
|
||||
|
|
@ -138,8 +139,7 @@ namespace Content.Server.Body.Systems
|
|||
{
|
||||
if (meta.RemoveEmpty)
|
||||
{
|
||||
_solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent,
|
||||
FixedPoint2.New(1));
|
||||
solution.RemoveReagent(reagent, FixedPoint2.New(1));
|
||||
}
|
||||
|
||||
continue;
|
||||
|
|
@ -198,12 +198,14 @@ namespace Content.Server.Body.Systems
|
|||
// remove a certain amount of reagent
|
||||
if (mostToRemove > FixedPoint2.Zero)
|
||||
{
|
||||
_solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent, mostToRemove);
|
||||
solution.RemoveReagent(reagent, mostToRemove);
|
||||
|
||||
// We have processed a reagant, so count it towards the cap
|
||||
reagents += 1;
|
||||
}
|
||||
}
|
||||
|
||||
_solutionContainerSystem.UpdateChemicals(soln.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Content.Server.Administration.Logs;
|
|||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
|
|
@ -26,6 +27,7 @@ namespace Content.Server.Body.Systems
|
|||
[Dependency] private readonly LungSystem _lungSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -144,7 +146,9 @@ namespace Content.Server.Body.Systems
|
|||
{
|
||||
_atmosSys.Merge(outGas, lung.Air);
|
||||
lung.Air.Clear();
|
||||
lung.LungSolution.RemoveAllSolution();
|
||||
|
||||
if (_solutionContainerSystem.ResolveSolution(lung.Owner, lung.SolutionName, ref lung.Solution))
|
||||
_solutionContainerSystem.RemoveAllSolution(lung.Solution.Value);
|
||||
}
|
||||
|
||||
_atmosSys.Merge(ev.Gas, outGas);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Body.Organ;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Body.Systems
|
||||
|
|
@ -31,8 +31,7 @@ namespace Content.Server.Body.Systems
|
|||
stomach.AccumulatedFrameTime -= stomach.UpdateInterval;
|
||||
|
||||
// Get our solutions
|
||||
if (!_solutionContainerSystem.TryGetSolution(uid, DefaultSolutionName,
|
||||
out var stomachSolution, sol))
|
||||
if (!_solutionContainerSystem.ResolveSolution((uid, sol), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
|
||||
continue;
|
||||
|
||||
if (organ.Body is not { } body || !_solutionContainerSystem.TryGetSolution(body, stomach.BodySolutionName, out var bodySolution))
|
||||
|
|
@ -51,7 +50,7 @@ namespace Content.Server.Body.Systems
|
|||
if (reagent.Quantity > delta.ReagentQuantity.Quantity)
|
||||
reagent = new(reagent.Reagent, delta.ReagentQuantity.Quantity);
|
||||
|
||||
_solutionContainerSystem.RemoveReagent(uid, stomachSolution, reagent);
|
||||
stomachSolution.RemoveReagent(reagent);
|
||||
transferSolution.AddReagent(reagent);
|
||||
}
|
||||
|
||||
|
|
@ -64,8 +63,10 @@ namespace Content.Server.Body.Systems
|
|||
stomach.ReagentDeltas.Remove(item);
|
||||
}
|
||||
|
||||
_solutionContainerSystem.UpdateChemicals(stomach.Solution.Value);
|
||||
|
||||
// Transfer everything to the body solution!
|
||||
_solutionContainerSystem.TryAddSolution(body, bodySolution, transferSolution);
|
||||
_solutionContainerSystem.TryAddSolution(bodySolution.Value, transferSolution);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,12 +87,13 @@ namespace Content.Server.Body.Systems
|
|||
}
|
||||
|
||||
public bool CanTransferSolution(EntityUid uid, Solution solution,
|
||||
StomachComponent? stomach = null,
|
||||
SolutionContainerManagerComponent? solutions = null)
|
||||
{
|
||||
if (!Resolve(uid, ref solutions, false))
|
||||
if (!Resolve(uid, ref stomach, ref solutions, false))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(uid, DefaultSolutionName, out var stomachSolution, solutions))
|
||||
if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
|
||||
return false;
|
||||
|
||||
// TODO: For now no partial transfers. Potentially change by design
|
||||
|
|
@ -108,11 +110,11 @@ namespace Content.Server.Body.Systems
|
|||
if (!Resolve(uid, ref stomach, ref solutions, false))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(uid, DefaultSolutionName, out var stomachSolution, solutions)
|
||||
|| !CanTransferSolution(uid, solution, solutions))
|
||||
if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution)
|
||||
|| !CanTransferSolution(uid, solution, stomach, solutions))
|
||||
return false;
|
||||
|
||||
_solutionContainerSystem.TryAddSolution(uid, stomachSolution, solution);
|
||||
_solutionContainerSystem.TryAddSolution(stomach.Solution.Value, solution);
|
||||
// Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach
|
||||
foreach (var reagent in solution.Contents)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Botany.Components;
|
||||
|
|
@ -90,4 +91,7 @@ public sealed partial class PlantHolderComponent : Component
|
|||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("solution")]
|
||||
public string SoilSolutionName = "soil";
|
||||
|
||||
[DataField]
|
||||
public Entity<SolutionComponent>? SoilSolution = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Content.Server.Botany.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Botany.Systems;
|
||||
|
||||
|
|
@ -11,7 +10,7 @@ public sealed partial class BotanySystem
|
|||
if (!TryGetSeed(produce, out var seed))
|
||||
return;
|
||||
|
||||
var solutionContainer = _solutionContainerSystem.EnsureSolution(uid, produce.SolutionName);
|
||||
var solutionContainer = _solutionContainerSystem.EnsureSolution(uid, produce.SolutionName, FixedPoint2.Zero, out _);
|
||||
|
||||
solutionContainer.RemoveAllSolution();
|
||||
foreach (var (chem, quantity) in seed.Chemicals)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Botany.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Botany;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Physics;
|
||||
|
|
@ -20,6 +18,8 @@ using Robust.Shared.Physics.Components;
|
|||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Botany.Systems;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Botany.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Botany;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Examine;
|
||||
|
|
@ -38,7 +38,7 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _pointLight = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly RandomHelperSystem _randomHelper = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
|
@ -70,11 +70,13 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, PlantHolderComponent component, ExaminedEvent args)
|
||||
private void OnExamine(Entity<PlantHolderComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
var (_, component) = entity;
|
||||
|
||||
if (component.Seed == null)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
|
||||
|
|
@ -131,8 +133,10 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, PlantHolderComponent component, InteractUsingEvent args)
|
||||
private void OnInteractUsing(Entity<PlantHolderComponent> entity, ref InteractUsingEvent args)
|
||||
{
|
||||
var (uid, component) = entity;
|
||||
|
||||
if (TryComp(args.Used, out SeedComponent? seeds))
|
||||
{
|
||||
if (component.Seed == null)
|
||||
|
|
@ -203,8 +207,8 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
if (_solutionSystem.TryGetDrainableSolution(args.Used, out var solution)
|
||||
&& _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution)
|
||||
if (_solutionContainerSystem.TryGetDrainableSolution(args.Used, out var solution, out _)
|
||||
&& _solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution)
|
||||
&& TryComp(args.Used, out SprayComponent? spray))
|
||||
{
|
||||
var amount = FixedPoint2.New(1);
|
||||
|
|
@ -214,7 +218,7 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
|
||||
_audio.PlayPvs(spray.SpraySound, args.Used, AudioParams.Default.WithVariation(0.125f));
|
||||
|
||||
var split = _solutionSystem.Drain(solutionEntity, solution, amount);
|
||||
var split = _solutionContainerSystem.Drain(solutionEntity, solution.Value, amount);
|
||||
|
||||
if (split.Volume == 0)
|
||||
{
|
||||
|
|
@ -227,7 +231,7 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
("owner", uid),
|
||||
("amount", split.Volume)), args.User, PopupType.Medium);
|
||||
|
||||
_solutionSystem.TryAddSolution(targetEntity, targetSolution, split);
|
||||
_solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, split);
|
||||
|
||||
ForceUpdateByExternalCause(uid, component);
|
||||
|
||||
|
|
@ -290,16 +294,15 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
("usingItem", args.Used),
|
||||
("owner", uid)), uid, Filter.PvsExcept(args.User), true);
|
||||
|
||||
if (_solutionSystem.TryGetSolution(args.Used, produce.SolutionName, out var solution2))
|
||||
if (_solutionContainerSystem.TryGetSolution(args.Used, produce.SolutionName, out var soln2, out var solution2))
|
||||
{
|
||||
if (_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution1))
|
||||
if (_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution1))
|
||||
{
|
||||
// We try to fit as much of the composted plant's contained solution into the hydroponics tray as we can,
|
||||
// since the plant will be consumed anyway.
|
||||
|
||||
var fillAmount = FixedPoint2.Min(solution2.Volume, solution1.AvailableVolume);
|
||||
_solutionSystem.TryAddSolution(uid, solution1,
|
||||
_solutionSystem.SplitSolution(args.Used, solution2, fillAmount));
|
||||
_solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, _solutionContainerSystem.SplitSolution(soln2.Value, fillAmount));
|
||||
|
||||
ForceUpdateByExternalCause(uid, component);
|
||||
}
|
||||
|
|
@ -314,9 +317,9 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnInteractHand(EntityUid uid, PlantHolderComponent component, InteractHandEvent args)
|
||||
private void OnInteractHand(Entity<PlantHolderComponent> entity, ref InteractHandEvent args)
|
||||
{
|
||||
DoHarvest(uid, args.User, component);
|
||||
DoHarvest(entity, args.User, entity.Comp);
|
||||
}
|
||||
|
||||
public void WeedInvasion()
|
||||
|
|
@ -819,13 +822,13 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (!_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution))
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution))
|
||||
return;
|
||||
|
||||
if (solution.Volume > 0 && component.MutationLevel < 25)
|
||||
{
|
||||
var amt = FixedPoint2.New(1);
|
||||
foreach (var entry in _solutionSystem.RemoveEachReagent(uid, solution, amt))
|
||||
foreach (var entry in _solutionContainerSystem.RemoveEachReagent(component.SoilSolution.Value, amt))
|
||||
{
|
||||
var reagentProto = _prototype.Index<ReagentPrototype>(entry.Reagent.Prototype);
|
||||
reagentProto.ReactionPlant(uid, entry, solution);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
|
|
@ -16,6 +16,7 @@ using Robust.Shared.Map;
|
|||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Cargo.Systems;
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ public sealed class PricingSystem : EntitySystem
|
|||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
|
|
@ -106,13 +108,36 @@ public sealed class PricingSystem : EntitySystem
|
|||
args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty);
|
||||
}
|
||||
|
||||
private double GetSolutionPrice(Entity<SolutionContainerManagerComponent> entity)
|
||||
{
|
||||
if (Comp<MetaDataComponent>(entity).EntityLifeStage < EntityLifeStage.MapInitialized)
|
||||
return GetSolutionPrice(entity.Comp);
|
||||
|
||||
var price = 0.0;
|
||||
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((entity.Owner, entity.Comp)))
|
||||
{
|
||||
var solution = soln.Comp.Solution;
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
|
||||
continue;
|
||||
|
||||
// TODO check ReagentData for price information?
|
||||
price += (float) quantity * reagentProto.PricePerUnit;
|
||||
}
|
||||
}
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
private double GetSolutionPrice(SolutionContainerManagerComponent component)
|
||||
{
|
||||
var price = 0.0;
|
||||
|
||||
foreach (var solution in component.Solutions.Values)
|
||||
foreach (var (_, prototype) in _solutionContainerSystem.EnumerateSolutions(component))
|
||||
{
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
foreach (var (reagent, quantity) in prototype.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
|
||||
continue;
|
||||
|
|
@ -257,7 +282,7 @@ public sealed class PricingSystem : EntitySystem
|
|||
|
||||
if (TryComp<SolutionContainerManagerComponent>(uid, out var solComp))
|
||||
{
|
||||
price += GetSolutionPrice(solComp);
|
||||
price += GetSolutionPrice((uid, solComp));
|
||||
}
|
||||
|
||||
return price;
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ public sealed class CartridgeLoaderSystem : SharedCartridgeLoaderSystem
|
|||
return false;
|
||||
|
||||
var installedProgram = Spawn(prototype, new EntityCoordinates(loaderUid, 0, 0));
|
||||
container.Insert(installedProgram);
|
||||
_containerSystem.Insert(installedProgram, container);
|
||||
|
||||
UpdateCartridgeInstallationStatus(installedProgram, deinstallable ? InstallationStatus.Installed : InstallationStatus.Readonly);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent]
|
||||
[Access(typeof(LogProbeCartridgeSystem))]
|
||||
public sealed partial class LogProbeCartridgeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of pulled access logs
|
||||
/// </summary>
|
||||
[DataField, ViewVariables]
|
||||
public List<PulledAccessLog> PulledAccessLogs = new();
|
||||
|
||||
/// <summary>
|
||||
/// The sound to make when we scan something with access
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier SoundScan = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg");
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.CartridgeLoader.Cartridges;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
||||
public sealed class LogProbeCartridgeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly CartridgeLoaderSystem? _cartridgeLoaderSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<LogProbeCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
|
||||
SubscribeLocalEvent<LogProbeCartridgeComponent, CartridgeAfterInteractEvent>(AfterInteract);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CartridgeAfterInteractEvent" /> gets relayed to this system if the cartridge loader is running
|
||||
/// the LogProbe program and someone clicks on something with it. <br/>
|
||||
/// <br/>
|
||||
/// Updates the program's list of logs with those from the device.
|
||||
/// </summary>
|
||||
private void AfterInteract(Entity<LogProbeCartridgeComponent> ent, ref CartridgeAfterInteractEvent args)
|
||||
{
|
||||
if (args.InteractEvent.Handled || !args.InteractEvent.CanReach || args.InteractEvent.Target is not { } target)
|
||||
return;
|
||||
|
||||
if (!TryComp(target, out AccessReaderComponent? accessReaderComponent))
|
||||
return;
|
||||
|
||||
//Play scanning sound with slightly randomized pitch
|
||||
_audioSystem.PlayEntity(ent.Comp.SoundScan, args.InteractEvent.User, target, AudioHelpers.WithVariation(0.25f, _random));
|
||||
_popupSystem.PopupCursor(Loc.GetString("log-probe-scan", ("device", target)), args.InteractEvent.User);
|
||||
|
||||
ent.Comp.PulledAccessLogs.Clear();
|
||||
|
||||
foreach (var accessRecord in accessReaderComponent.AccessLog)
|
||||
{
|
||||
var log = new PulledAccessLog(
|
||||
accessRecord.AccessTime,
|
||||
accessRecord.Accessor
|
||||
);
|
||||
|
||||
ent.Comp.PulledAccessLogs.Add(log);
|
||||
}
|
||||
|
||||
UpdateUiState(ent, args.Loader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This gets called when the ui fragment needs to be updated for the first time after activating
|
||||
/// </summary>
|
||||
private void OnUiReady(Entity<LogProbeCartridgeComponent> ent, ref CartridgeUiReadyEvent args)
|
||||
{
|
||||
UpdateUiState(ent, args.Loader);
|
||||
}
|
||||
|
||||
private void UpdateUiState(Entity<LogProbeCartridgeComponent> ent, EntityUid loaderUid)
|
||||
{
|
||||
var state = new LogProbeUiState(ent.Comp.PulledAccessLogs);
|
||||
_cartridgeLoaderSystem?.UpdateCartridgeUiState(loaderUid, state);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Audio;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Chemistry.Components;
|
||||
|
||||
|
|
@ -17,7 +15,13 @@ public sealed partial class SolutionRegenerationComponent : Component
|
|||
/// The name of the solution to add to.
|
||||
/// </summary>
|
||||
[DataField("solution", required: true), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Solution = string.Empty;
|
||||
public string SolutionName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The solution to add reagents to.
|
||||
/// </summary>
|
||||
[DataField("solutionRef")]
|
||||
public Entity<SolutionComponent>? Solution = null;
|
||||
|
||||
/// <summary>
|
||||
/// The reagent(s) to be regenerated in the solution.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,186 @@
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Server.Chemistry.Containers.EntitySystems;
|
||||
|
||||
public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, ComponentShutdown>(OnComponentShutdown);
|
||||
SubscribeLocalEvent<ContainedSolutionComponent, ComponentShutdown>(OnComponentShutdown);
|
||||
}
|
||||
|
||||
|
||||
public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name)
|
||||
=> EnsureSolution(entity, name, out _);
|
||||
|
||||
public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, out bool existed)
|
||||
=> EnsureSolution(entity, name, FixedPoint2.Zero, out existed);
|
||||
|
||||
public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, FixedPoint2 minVol, out bool existed)
|
||||
=> EnsureSolution(entity, name, minVol, null, out existed);
|
||||
|
||||
public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, FixedPoint2 minVol, Solution? prototype, out bool existed)
|
||||
{
|
||||
var (uid, meta) = entity;
|
||||
if (!Resolve(uid, ref meta))
|
||||
throw new InvalidOperationException("Attempted to ensure solution on invalid entity.");
|
||||
|
||||
var manager = EnsureComp<SolutionContainerManagerComponent>(uid);
|
||||
if (meta.EntityLifeStage >= EntityLifeStage.MapInitialized)
|
||||
return EnsureSolutionEntity((uid, manager), name, minVol, prototype, out existed).Comp.Solution;
|
||||
else
|
||||
return EnsureSolutionPrototype((uid, manager), name, minVol, prototype, out existed);
|
||||
}
|
||||
|
||||
public Entity<SolutionComponent> EnsureSolutionEntity(Entity<SolutionContainerManagerComponent?> entity, string name, FixedPoint2 minVol, Solution? prototype, out bool existed)
|
||||
{
|
||||
existed = true;
|
||||
|
||||
var (uid, container) = entity;
|
||||
|
||||
var solutionSlot = ContainerSystem.EnsureContainer<ContainerSlot>(uid, $"solution@{name}", out existed);
|
||||
if (!Resolve(uid, ref container, logMissing: false))
|
||||
{
|
||||
existed = false;
|
||||
container = AddComp<SolutionContainerManagerComponent>(uid);
|
||||
container.Containers.Add(name);
|
||||
}
|
||||
else if (!existed)
|
||||
{
|
||||
container.Containers.Add(name);
|
||||
Dirty(uid, container);
|
||||
}
|
||||
|
||||
var needsInit = false;
|
||||
SolutionComponent solutionComp;
|
||||
if (solutionSlot.ContainedEntity is not { } solutionId)
|
||||
{
|
||||
prototype ??= new() { MaxVolume = minVol };
|
||||
prototype.Name = name;
|
||||
(solutionId, solutionComp, _) = SpawnSolutionUninitialized(solutionSlot, name, minVol, prototype);
|
||||
existed = false;
|
||||
needsInit = true;
|
||||
Dirty(uid, container);
|
||||
}
|
||||
else
|
||||
{
|
||||
solutionComp = Comp<SolutionComponent>(solutionId);
|
||||
DebugTools.Assert(TryComp(solutionId, out ContainedSolutionComponent? relation) && relation.Container == uid && relation.ContainerName == name);
|
||||
DebugTools.Assert(solutionComp.Solution.Name == name);
|
||||
|
||||
var solution = solutionComp.Solution;
|
||||
solution.MaxVolume = FixedPoint2.Max(solution.MaxVolume, minVol);
|
||||
|
||||
// Depending on MapInitEvent order some systems can ensure solution empty solutions and conflict with the prototype solutions.
|
||||
// We want the reagents from the prototype to exist even if something else already created the solution.
|
||||
if (prototype is { Volume.Value: > 0 })
|
||||
solution.AddSolution(prototype, PrototypeManager);
|
||||
|
||||
Dirty(solutionId, solutionComp);
|
||||
}
|
||||
|
||||
if (needsInit)
|
||||
EntityManager.InitializeAndStartEntity(solutionId, Transform(solutionId).MapID);
|
||||
|
||||
return (solutionId, solutionComp);
|
||||
}
|
||||
|
||||
private Solution EnsureSolutionPrototype(Entity<SolutionContainerManagerComponent?> entity, string name, FixedPoint2 minVol, Solution? prototype, out bool existed)
|
||||
{
|
||||
existed = true;
|
||||
|
||||
var (uid, container) = entity;
|
||||
if (!Resolve(uid, ref container, logMissing: false))
|
||||
{
|
||||
container = AddComp<SolutionContainerManagerComponent>(uid);
|
||||
existed = false;
|
||||
}
|
||||
|
||||
if (container.Solutions is null)
|
||||
container.Solutions = new(SolutionContainerManagerComponent.DefaultCapacity);
|
||||
|
||||
if (!container.Solutions.TryGetValue(name, out var solution))
|
||||
{
|
||||
solution = prototype ?? new() { Name = name, MaxVolume = minVol };
|
||||
container.Solutions.Add(name, solution);
|
||||
existed = false;
|
||||
}
|
||||
else
|
||||
solution.MaxVolume = FixedPoint2.Max(solution.MaxVolume, minVol);
|
||||
|
||||
Dirty(uid, container);
|
||||
return solution;
|
||||
}
|
||||
|
||||
|
||||
private Entity<SolutionComponent, ContainedSolutionComponent> SpawnSolutionUninitialized(ContainerSlot container, string name, FixedPoint2 minVol, Solution prototype)
|
||||
{
|
||||
var coords = new EntityCoordinates(container.Owner, Vector2.Zero);
|
||||
var uid = EntityManager.CreateEntityUninitialized(null, coords, null);
|
||||
|
||||
var solution = new SolutionComponent() { Solution = prototype };
|
||||
AddComp(uid, solution);
|
||||
|
||||
var relation = new ContainedSolutionComponent() { Container = container.Owner, ContainerName = name };
|
||||
AddComp(uid, relation);
|
||||
|
||||
ContainerSystem.Insert(uid, container, force: true);
|
||||
|
||||
return (uid, solution, relation);
|
||||
}
|
||||
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OnMapInit(Entity<SolutionContainerManagerComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
if (entity.Comp.Solutions is not { } prototypes)
|
||||
return;
|
||||
|
||||
foreach (var (name, prototype) in prototypes)
|
||||
{
|
||||
EnsureSolutionEntity((entity.Owner, entity.Comp), name, prototype.MaxVolume, prototype, out _);
|
||||
}
|
||||
|
||||
entity.Comp.Solutions = null;
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
private void OnComponentShutdown(Entity<SolutionContainerManagerComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
foreach (var name in entity.Comp.Containers)
|
||||
{
|
||||
if (ContainerSystem.TryGetContainer(entity, $"solution@{name}", out var solutionContainer))
|
||||
ContainerSystem.ShutdownContainer(solutionContainer);
|
||||
}
|
||||
entity.Comp.Containers.Clear();
|
||||
}
|
||||
|
||||
private void OnComponentShutdown(Entity<ContainedSolutionComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
if (TryComp(entity.Comp.Container, out SolutionContainerManagerComponent? container))
|
||||
{
|
||||
container.Containers.Remove(entity.Comp.ContainerName);
|
||||
Dirty(entity.Comp.Container, container);
|
||||
}
|
||||
|
||||
if (ContainerSystem.TryGetContainer(entity, $"solution@{entity.Comp.ContainerName}", out var solutionContainer))
|
||||
ContainerSystem.ShutdownContainer(solutionContainer);
|
||||
}
|
||||
|
||||
#endregion Event Handlers
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Labels;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Storage.EntitySystems;
|
||||
|
|
@ -19,6 +18,8 @@ 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
|
||||
{
|
||||
|
|
@ -47,7 +48,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ChemMasterComponent, ComponentStartup>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, SolutionChangedEvent>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
|
||||
|
|
@ -67,7 +68,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
private void UpdateUiState(Entity<ChemMasterComponent> ent, bool updateLabel = false)
|
||||
{
|
||||
var (owner, chemMaster) = ent;
|
||||
if (!_solutionContainerSystem.TryGetSolution(owner, SharedChemMaster.BufferSolutionName, out var bufferSolution))
|
||||
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);
|
||||
|
|
@ -130,8 +131,8 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
{
|
||||
var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName);
|
||||
if (container is null ||
|
||||
!_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution) ||
|
||||
!_solutionContainerSystem.TryGetSolution(chemMaster, SharedChemMaster.BufferSolutionName, out var bufferSolution))
|
||||
!_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSoln, out var containerSolution) ||
|
||||
!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -140,12 +141,12 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
{
|
||||
amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume);
|
||||
amount = bufferSolution.RemoveReagent(id, amount);
|
||||
_solutionContainerSystem.TryAddReagent(container.Value, containerSolution, id, amount, out var _);
|
||||
_solutionContainerSystem.TryAddReagent(containerSoln.Value, id, amount, out var _);
|
||||
}
|
||||
else // Container to buffer
|
||||
{
|
||||
amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(id));
|
||||
_solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount);
|
||||
_solutionContainerSystem.RemoveReagent(containerSoln.Value, id, amount);
|
||||
bufferSolution.AddReagent(id, amount);
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +157,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
{
|
||||
if (fromBuffer)
|
||||
{
|
||||
if (_solutionContainerSystem.TryGetSolution(chemMaster, SharedChemMaster.BufferSolutionName, out var bufferSolution))
|
||||
if (_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
|
||||
bufferSolution.RemoveReagent(id, amount);
|
||||
else
|
||||
return;
|
||||
|
|
@ -165,9 +166,9 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
{
|
||||
var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName);
|
||||
if (container is not null &&
|
||||
_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution))
|
||||
_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution, out _))
|
||||
{
|
||||
_solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount);
|
||||
_solutionContainerSystem.RemoveReagent(containerSolution.Value, id, amount);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
|
@ -210,10 +211,8 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
_storageSystem.Insert(container, item, out _, user: user, storage);
|
||||
_labelSystem.Label(item, message.Label);
|
||||
|
||||
var itemSolution = _solutionContainerSystem.EnsureSolution(item, SharedChemMaster.PillSolutionName);
|
||||
|
||||
_solutionContainerSystem.TryAddSolution(
|
||||
item, itemSolution, withdrawal.SplitSolution(message.Dosage));
|
||||
var itemSolution = _solutionContainerSystem.EnsureSolutionEntity(item, SharedChemMaster.PillSolutionName, message.Dosage, null, out _);
|
||||
_solutionContainerSystem.TryAddSolution(itemSolution, withdrawal.SplitSolution(message.Dosage));
|
||||
|
||||
var pill = EnsureComp<PillComponent>(item);
|
||||
pill.PillType = chemMaster.Comp.PillType;
|
||||
|
|
@ -223,13 +222,13 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
{
|
||||
// Log pill creation by a user
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low,
|
||||
$"{ToPrettyString(user.Value):user} printed {ToPrettyString(item):pill} {SolutionContainerSystem.ToPrettyString(itemSolution)}");
|
||||
$"{ToPrettyString(user.Value):user} printed {ToPrettyString(item):pill} {SolutionContainerSystem.ToPrettyString(itemSolution.Comp.Solution)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log pill creation by magic? This should never happen... right?
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low,
|
||||
$"Unknown printed {ToPrettyString(item):pill} {SolutionContainerSystem.ToPrettyString(itemSolution)}");
|
||||
$"Unknown printed {ToPrettyString(item):pill} {SolutionContainerSystem.ToPrettyString(itemSolution.Comp.Solution)}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,8 +241,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
var user = message.Session.AttachedEntity;
|
||||
var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.OutputSlotName);
|
||||
if (maybeContainer is not { Valid: true } container
|
||||
|| !_solutionContainerSystem.TryGetSolution(
|
||||
container, SharedChemMaster.BottleSolutionName, out var solution))
|
||||
|| !_solutionContainerSystem.TryGetSolution(container, SharedChemMaster.BottleSolutionName, out var soln, out var solution))
|
||||
{
|
||||
return; // output can't fit reagents
|
||||
}
|
||||
|
|
@ -260,8 +258,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
return;
|
||||
|
||||
_labelSystem.Label(container, message.Label);
|
||||
_solutionContainerSystem.TryAddSolution(
|
||||
container, solution, withdrawal);
|
||||
_solutionContainerSystem.TryAddSolution(soln.Value, withdrawal);
|
||||
|
||||
if (user.HasValue)
|
||||
{
|
||||
|
|
@ -287,8 +284,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
{
|
||||
outputSolution = null;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(
|
||||
chemMaster, SharedChemMaster.BufferSolutionName, out var solution))
|
||||
if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -323,7 +319,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
return null;
|
||||
|
||||
if (!TryComp(container, out FitsInDispenserComponent? fits)
|
||||
|| !_solutionContainerSystem.TryGetSolution(container.Value, fits.Solution, out var solution))
|
||||
|| !_solutionContainerSystem.TryGetSolution(container.Value, fits.Solution, out _, out var solution))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
@ -339,7 +335,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
var name = Name(container.Value);
|
||||
{
|
||||
if (_solutionContainerSystem.TryGetSolution(
|
||||
container.Value, SharedChemMaster.BottleSolutionName, out var solution))
|
||||
container.Value, SharedChemMaster.BottleSolutionName, out _, out var solution))
|
||||
{
|
||||
return BuildContainerInfo(name, solution);
|
||||
}
|
||||
|
|
@ -350,7 +346,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
|
||||
var pills = storage.Container.ContainedEntities.Select((Func<EntityUid, (string, FixedPoint2 quantity)>) (pill =>
|
||||
{
|
||||
_solutionContainerSystem.TryGetSolution(pill, SharedChemMaster.PillSolutionName, out var solution);
|
||||
_solutionContainerSystem.TryGetSolution(pill, SharedChemMaster.PillSolutionName, out _, out var solution);
|
||||
var quantity = solution?.Volume ?? FixedPoint2.Zero;
|
||||
return (Name(pill), quantity);
|
||||
})).ToList();
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Forensics;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Robust.Shared.GameStates;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using Content.Shared.Forensics;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems;
|
||||
|
||||
|
|
@ -25,11 +26,11 @@ public sealed partial class ChemistrySystem
|
|||
/// <summary>
|
||||
/// Default transfer amounts for the set-transfer verb.
|
||||
/// </summary>
|
||||
public static readonly List<int> TransferAmounts = new() {1, 5, 10, 15};
|
||||
public static readonly List<int> TransferAmounts = new() { 1, 5, 10, 15 };
|
||||
private void InitializeInjector()
|
||||
{
|
||||
SubscribeLocalEvent<InjectorComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs);
|
||||
SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<InjectorComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<InjectorComponent, InjectorDoAfterEvent>(OnInjectDoAfter);
|
||||
SubscribeLocalEvent<InjectorComponent, ComponentStartup>(OnInjectorStartup);
|
||||
SubscribeLocalEvent<InjectorComponent, UseInHandEvent>(OnInjectorUse);
|
||||
|
|
@ -37,7 +38,7 @@ public sealed partial class ChemistrySystem
|
|||
SubscribeLocalEvent<InjectorComponent, ComponentGetState>(OnInjectorGetState);
|
||||
}
|
||||
|
||||
private void AddSetTransferVerbs(EntityUid uid, InjectorComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
private void AddSetTransferVerbs(Entity<InjectorComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
|
||||
return;
|
||||
|
|
@ -45,11 +46,14 @@ public sealed partial class ChemistrySystem
|
|||
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
var (uid, component) = entity;
|
||||
|
||||
// Add specific transfer verbs according to the container's size
|
||||
var priority = 0;
|
||||
var user = args.User;
|
||||
foreach (var amount in TransferAmounts)
|
||||
{
|
||||
if ( amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
|
||||
if (amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
|
||||
continue;
|
||||
|
||||
AlternativeVerb verb = new();
|
||||
|
|
@ -58,7 +62,7 @@ public sealed partial class ChemistrySystem
|
|||
verb.Act = () =>
|
||||
{
|
||||
component.TransferAmount = FixedPoint2.New(amount);
|
||||
_popup.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), args.User, args.User);
|
||||
_popup.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), user, user);
|
||||
};
|
||||
|
||||
// we want to sort by size, not alphabetically by the verb text.
|
||||
|
|
@ -69,22 +73,22 @@ public sealed partial class ChemistrySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void UseInjector(EntityUid target, EntityUid user, EntityUid injector, InjectorComponent component)
|
||||
private void UseInjector(Entity<InjectorComponent> injector, EntityUid target, EntityUid user)
|
||||
{
|
||||
// Handle injecting/drawing for solutions
|
||||
if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
|
||||
if (injector.Comp.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
|
||||
{
|
||||
if (_solutions.TryGetInjectableSolution(target, out var injectableSolution))
|
||||
if (_solutionContainers.TryGetInjectableSolution(target, out var injectableSolution, out _))
|
||||
{
|
||||
TryInject(component, injector, target, injectableSolution, user, false);
|
||||
TryInject(injector, target, injectableSolution.Value, user, false);
|
||||
}
|
||||
else if (_solutions.TryGetRefillableSolution(target, out var refillableSolution))
|
||||
else if (_solutionContainers.TryGetRefillableSolution(target, out var refillableSolution, out _))
|
||||
{
|
||||
TryInject(component, injector, target, refillableSolution, user, true);
|
||||
TryInject(injector, target, refillableSolution.Value, user, true);
|
||||
}
|
||||
else if (TryComp<BloodstreamComponent>(target, out var bloodstream))
|
||||
{
|
||||
TryInjectIntoBloodstream(component, injector, target, bloodstream, user);
|
||||
TryInjectIntoBloodstream(injector, (target, bloodstream), user);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -92,111 +96,112 @@ public sealed partial class ChemistrySystem
|
|||
("target", Identity.Entity(target, EntityManager))), injector, user);
|
||||
}
|
||||
}
|
||||
else if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Draw)
|
||||
else if (injector.Comp.ToggleState == SharedInjectorComponent.InjectorToggleMode.Draw)
|
||||
{
|
||||
// Draw from a bloodstream, if the target has that
|
||||
if (TryComp<BloodstreamComponent>(target, out var stream))
|
||||
if (TryComp<BloodstreamComponent>(target, out var stream) &&
|
||||
_solutionContainers.ResolveSolution(target, stream.BloodSolutionName, ref stream.BloodSolution))
|
||||
{
|
||||
TryDraw(component, injector, target, stream.BloodSolution, user, stream);
|
||||
TryDraw(injector, (target, stream), stream.BloodSolution.Value, user);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw from an object (food, beaker, etc)
|
||||
if (_solutions.TryGetDrawableSolution(target, out var drawableSolution))
|
||||
if (_solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _))
|
||||
{
|
||||
TryDraw(component, injector, target, drawableSolution, user);
|
||||
TryDraw(injector, target, drawableSolution.Value, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-cannot-draw-message",
|
||||
("target", Identity.Entity(target, EntityManager))), injector, user);
|
||||
("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, InjectorComponent component, SolutionChangedEvent args)
|
||||
private void OnSolutionChange(Entity<InjectorComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
Dirty(component);
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
private void OnInjectorGetState(EntityUid uid, InjectorComponent component, ref ComponentGetState args)
|
||||
private void OnInjectorGetState(Entity<InjectorComponent> entity, ref ComponentGetState args)
|
||||
{
|
||||
_solutions.TryGetSolution(uid, InjectorComponent.SolutionName, out var solution);
|
||||
_solutionContainers.TryGetSolution(entity.Owner, InjectorComponent.SolutionName, out _, out var solution);
|
||||
|
||||
var currentVolume = solution?.Volume ?? FixedPoint2.Zero;
|
||||
var maxVolume = solution?.MaxVolume ?? FixedPoint2.Zero;
|
||||
|
||||
args.State = new SharedInjectorComponent.InjectorComponentState(currentVolume, maxVolume, component.ToggleState);
|
||||
args.State = new SharedInjectorComponent.InjectorComponentState(currentVolume, maxVolume, entity.Comp.ToggleState);
|
||||
}
|
||||
|
||||
private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args)
|
||||
private void OnInjectDoAfter(Entity<InjectorComponent> entity, ref InjectorDoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled || args.Args.Target == null)
|
||||
return;
|
||||
|
||||
UseInjector(args.Args.Target.Value, args.Args.User, uid, component);
|
||||
UseInjector(entity, args.Args.Target.Value, args.Args.User);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnInjectorAfterInteract(EntityUid uid, InjectorComponent component, AfterInteractEvent args)
|
||||
private void OnInjectorAfterInteract(Entity<InjectorComponent> entity, ref AfterInteractEvent args)
|
||||
{
|
||||
if (args.Handled || !args.CanReach)
|
||||
return;
|
||||
|
||||
//Make sure we have the attacking entity
|
||||
if (args.Target is not { Valid: true } target || !HasComp<SolutionContainerManagerComponent>(uid))
|
||||
if (args.Target is not { Valid: true } target || !HasComp<SolutionContainerManagerComponent>(entity))
|
||||
return;
|
||||
|
||||
// Is the target a mob? If yes, use a do-after to give them time to respond.
|
||||
if (HasComp<MobStateComponent>(target) || HasComp<BloodstreamComponent>(target))
|
||||
{
|
||||
// Are use using an injector capible of targeting a mob?
|
||||
if (component.IgnoreMobs)
|
||||
if (entity.Comp.IgnoreMobs)
|
||||
return;
|
||||
|
||||
InjectDoAfter(component, args.User, target, uid);
|
||||
InjectDoAfter(entity, target, args.User);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
UseInjector(target, args.User, uid, component);
|
||||
UseInjector(entity, target, args.User);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnInjectorStartup(EntityUid uid, InjectorComponent component, ComponentStartup args)
|
||||
private void OnInjectorStartup(Entity<InjectorComponent> entity, ref ComponentStartup args)
|
||||
{
|
||||
// ???? why ?????
|
||||
Dirty(component);
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
private void OnInjectorUse(EntityUid uid, InjectorComponent component, UseInHandEvent args)
|
||||
private void OnInjectorUse(Entity<InjectorComponent> entity, ref UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
Toggle(component, args.User, uid);
|
||||
Toggle(entity, args.User);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle between draw/inject state if applicable
|
||||
/// </summary>
|
||||
private void Toggle(InjectorComponent component, EntityUid user, EntityUid injector)
|
||||
private void Toggle(Entity<InjectorComponent> injector, EntityUid user)
|
||||
{
|
||||
if (component.InjectOnly)
|
||||
if (injector.Comp.InjectOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string msg;
|
||||
switch (component.ToggleState)
|
||||
switch (injector.Comp.ToggleState)
|
||||
{
|
||||
case SharedInjectorComponent.InjectorToggleMode.Inject:
|
||||
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
|
||||
injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
|
||||
msg = "injector-component-drawing-text";
|
||||
break;
|
||||
case SharedInjectorComponent.InjectorToggleMode.Draw:
|
||||
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
|
||||
injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
|
||||
msg = "injector-component-injecting-text";
|
||||
break;
|
||||
default:
|
||||
|
|
@ -209,18 +214,18 @@ public sealed partial class ChemistrySystem
|
|||
/// <summary>
|
||||
/// Send informative pop-up messages and wait for a do-after to complete.
|
||||
/// </summary>
|
||||
private void InjectDoAfter(InjectorComponent component, EntityUid user, EntityUid target, EntityUid injector)
|
||||
private void InjectDoAfter(Entity<InjectorComponent> injector, EntityUid target, EntityUid user)
|
||||
{
|
||||
// Create a pop-up for the user
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user);
|
||||
|
||||
if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution))
|
||||
if (!_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out _, out var solution))
|
||||
return;
|
||||
|
||||
var actualDelay = MathF.Max(component.Delay, 1f);
|
||||
var actualDelay = MathF.Max(injector.Comp.Delay, 1f);
|
||||
|
||||
// Injections take 0.5 seconds longer per additional 5u
|
||||
actualDelay += (float) component.TransferAmount / component.Delay - 0.5f;
|
||||
actualDelay += (float) injector.Comp.TransferAmount / injector.Comp.Delay - 0.5f;
|
||||
|
||||
var isTarget = user != target;
|
||||
|
||||
|
|
@ -244,7 +249,7 @@ public sealed partial class ChemistrySystem
|
|||
}
|
||||
|
||||
// Add an admin log, using the "force feed" log type. It's not quite feeding, but the effect is the same.
|
||||
if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
|
||||
if (injector.Comp.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
|
||||
{
|
||||
_adminLogger.Add(LogType.ForceFeed,
|
||||
$"{EntityManager.ToPrettyString(user):user} is attempting to inject {EntityManager.ToPrettyString(target):target} with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}");
|
||||
|
|
@ -255,11 +260,11 @@ public sealed partial class ChemistrySystem
|
|||
// Self-injections take half as long.
|
||||
actualDelay /= 2;
|
||||
|
||||
if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
|
||||
if (injector.Comp.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
|
||||
_adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}.");
|
||||
}
|
||||
|
||||
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, actualDelay, new InjectorDoAfterEvent(), injector, target: target, used: injector)
|
||||
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, actualDelay, new InjectorDoAfterEvent(), injector.Owner, target: target, used: injector.Owner)
|
||||
{
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
|
|
@ -268,77 +273,80 @@ public sealed partial class ChemistrySystem
|
|||
});
|
||||
}
|
||||
|
||||
private void TryInjectIntoBloodstream(InjectorComponent component, EntityUid injector, EntityUid target, BloodstreamComponent targetBloodstream, EntityUid user)
|
||||
private void TryInjectIntoBloodstream(Entity<InjectorComponent> injector, Entity<BloodstreamComponent> target, EntityUid user)
|
||||
{
|
||||
// Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetBloodstream.ChemicalSolution.AvailableVolume);
|
||||
if (!_solutionContainers.ResolveSolution(target.Owner, target.Comp.ChemicalSolutionName, ref target.Comp.ChemicalSolution, out var chemSolution))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
|
||||
return;
|
||||
}
|
||||
|
||||
var realTransferAmount = FixedPoint2.Min(injector.Comp.TransferAmount, chemSolution.AvailableVolume);
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(target, EntityManager))), injector, user);
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move units from attackSolution to targetSolution
|
||||
var removedSolution = _solutions.SplitSolution(user, targetBloodstream.ChemicalSolution, realTransferAmount);
|
||||
var removedSolution = _solutionContainers.SplitSolution(target.Comp.ChemicalSolution.Value, realTransferAmount);
|
||||
|
||||
_blood.TryAddToChemicals(target, removedSolution, targetBloodstream);
|
||||
_blood.TryAddToChemicals(target, removedSolution, target.Comp);
|
||||
|
||||
_reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection);
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-inject-success-message",
|
||||
("amount", removedSolution.Volume),
|
||||
("target", Identity.Entity(target, EntityManager))), injector, user);
|
||||
("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
|
||||
|
||||
Dirty(component);
|
||||
AfterInject(component, injector, target);
|
||||
Dirty(injector);
|
||||
AfterInject(injector, target);
|
||||
}
|
||||
|
||||
private void TryInject(InjectorComponent component, EntityUid injector, EntityUid targetEntity, Solution targetSolution, EntityUid user, bool asRefill)
|
||||
private void TryInject(Entity<InjectorComponent> injector, EntityUid targetEntity, Entity<SolutionComponent> targetSolution, EntityUid user, bool asRefill)
|
||||
{
|
||||
if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
|
||||
|| solution.Volume == 0)
|
||||
if (!_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out var soln, out var solution) || solution.Volume == 0)
|
||||
return;
|
||||
|
||||
// Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume);
|
||||
var realTransferAmount = FixedPoint2.Min(injector.Comp.TransferAmount, targetSolution.Comp.Solution.AvailableVolume);
|
||||
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-target-already-full-message", ("target", Identity.Entity(targetEntity, EntityManager))),
|
||||
injector, user);
|
||||
injector.Owner, user);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move units from attackSolution to targetSolution
|
||||
Solution removedSolution;
|
||||
if (TryComp<StackComponent>(targetEntity, out var stack))
|
||||
removedSolution = _solutions.SplitStackSolution(injector, solution, realTransferAmount, stack.Count);
|
||||
removedSolution = _solutionContainers.SplitStackSolution(soln.Value, realTransferAmount, stack.Count);
|
||||
else
|
||||
removedSolution = _solutions.SplitSolution(injector, solution, realTransferAmount);
|
||||
removedSolution = _solutionContainers.SplitSolution(soln.Value, realTransferAmount);
|
||||
|
||||
_reactiveSystem.DoEntityReaction(targetEntity, removedSolution, ReactionMethod.Injection);
|
||||
|
||||
if (!asRefill)
|
||||
_solutions.Inject(targetEntity, targetSolution, removedSolution);
|
||||
_solutionContainers.Inject(targetEntity, targetSolution, removedSolution);
|
||||
else
|
||||
_solutions.Refill(targetEntity, targetSolution, removedSolution);
|
||||
_solutionContainers.Refill(targetEntity, targetSolution, removedSolution);
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-transfer-success-message",
|
||||
("amount", removedSolution.Volume),
|
||||
("target", Identity.Entity(targetEntity, EntityManager))), injector, user);
|
||||
("target", Identity.Entity(targetEntity, EntityManager))), injector.Owner, user);
|
||||
|
||||
Dirty(component);
|
||||
AfterInject(component, injector, targetEntity);
|
||||
Dirty(injector);
|
||||
AfterInject(injector, targetEntity);
|
||||
}
|
||||
|
||||
private void AfterInject(InjectorComponent component, EntityUid injector, EntityUid target)
|
||||
private void AfterInject(Entity<InjectorComponent> injector, EntityUid target)
|
||||
{
|
||||
// Automatically set syringe to draw after completely draining it.
|
||||
if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
|
||||
&& solution.Volume == 0)
|
||||
if (_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out _, out var solution) && solution.Volume == 0)
|
||||
{
|
||||
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
|
||||
injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
|
||||
}
|
||||
|
||||
// Leave some DNA from the injectee on it
|
||||
|
|
@ -346,13 +354,12 @@ public sealed partial class ChemistrySystem
|
|||
RaiseLocalEvent(target, ref ev);
|
||||
}
|
||||
|
||||
private void AfterDraw(InjectorComponent component, EntityUid injector, EntityUid target)
|
||||
private void AfterDraw(Entity<InjectorComponent> injector, EntityUid target)
|
||||
{
|
||||
// Automatically set syringe to inject after completely filling it.
|
||||
if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
|
||||
&& solution.AvailableVolume == 0)
|
||||
if (_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out _, out var solution) && solution.AvailableVolume == 0)
|
||||
{
|
||||
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
|
||||
injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
|
||||
}
|
||||
|
||||
// Leave some DNA from the drawee on it
|
||||
|
|
@ -360,70 +367,68 @@ public sealed partial class ChemistrySystem
|
|||
RaiseLocalEvent(target, ref ev);
|
||||
}
|
||||
|
||||
private void TryDraw(InjectorComponent component, EntityUid injector, EntityUid targetEntity, Solution targetSolution, EntityUid user, BloodstreamComponent? stream = null)
|
||||
private void TryDraw(Entity<InjectorComponent> injector, Entity<BloodstreamComponent?> target, Entity<SolutionComponent> targetSolution, EntityUid user)
|
||||
{
|
||||
if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
|
||||
|| solution.AvailableVolume == 0)
|
||||
if (!_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out var soln, out var solution) || solution.AvailableVolume == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector
|
||||
var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.Volume, solution.AvailableVolume);
|
||||
var realTransferAmount = FixedPoint2.Min(injector.Comp.TransferAmount, targetSolution.Comp.Solution.Volume, solution.AvailableVolume);
|
||||
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", Identity.Entity(targetEntity, EntityManager))),
|
||||
injector, user);
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", Identity.Entity(target, EntityManager))),
|
||||
injector.Owner, user);
|
||||
return;
|
||||
}
|
||||
|
||||
// We have some snowflaked behavior for streams.
|
||||
if (stream != null)
|
||||
if (target.Comp != null)
|
||||
{
|
||||
DrawFromBlood(user, injector, targetEntity, component, solution, stream, realTransferAmount);
|
||||
DrawFromBlood(injector, (target.Owner, target.Comp), soln.Value, realTransferAmount, user);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move units from attackSolution to targetSolution
|
||||
var removedSolution = _solutions.Draw(targetEntity, targetSolution, realTransferAmount);
|
||||
var removedSolution = _solutionContainers.Draw(target.Owner, targetSolution, realTransferAmount);
|
||||
|
||||
if (!_solutions.TryAddSolution(injector, solution, removedSolution))
|
||||
if (!_solutionContainers.TryAddSolution(soln.Value, removedSolution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
|
||||
("amount", removedSolution.Volume),
|
||||
("target", Identity.Entity(targetEntity, EntityManager))), injector, user);
|
||||
("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
|
||||
|
||||
Dirty(component);
|
||||
AfterDraw(component, injector, targetEntity);
|
||||
Dirty(injector);
|
||||
AfterDraw(injector, target);
|
||||
}
|
||||
|
||||
private void DrawFromBlood(EntityUid user, EntityUid injector, EntityUid target, InjectorComponent component, Solution injectorSolution, BloodstreamComponent stream, FixedPoint2 transferAmount)
|
||||
private void DrawFromBlood(Entity<InjectorComponent> injector, Entity<BloodstreamComponent> target, Entity<SolutionComponent> injectorSolution, FixedPoint2 transferAmount, EntityUid user)
|
||||
{
|
||||
var drawAmount = (float) transferAmount;
|
||||
var bloodAmount = drawAmount;
|
||||
var chemAmount = 0f;
|
||||
if (stream.ChemicalSolution.Volume > 0f) // If they have stuff in their chem stream, we'll draw some of that
|
||||
|
||||
if (_solutionContainers.ResolveSolution(target.Owner, target.Comp.ChemicalSolutionName, ref target.Comp.ChemicalSolution))
|
||||
{
|
||||
bloodAmount = drawAmount * 0.85f;
|
||||
chemAmount = drawAmount * 0.15f;
|
||||
var chemTemp = _solutionContainers.SplitSolution(target.Comp.ChemicalSolution.Value, drawAmount * 0.15f);
|
||||
_solutionContainers.TryAddSolution(injectorSolution, chemTemp);
|
||||
drawAmount -= (float) chemTemp.Volume;
|
||||
}
|
||||
|
||||
var bloodTemp = stream.BloodSolution.SplitSolution(bloodAmount);
|
||||
var chemTemp = stream.ChemicalSolution.SplitSolution(chemAmount);
|
||||
|
||||
_solutions.TryAddSolution(injector, injectorSolution, bloodTemp);
|
||||
_solutions.TryAddSolution(injector, injectorSolution, chemTemp);
|
||||
if (_solutionContainers.ResolveSolution(target.Owner, target.Comp.BloodSolutionName, ref target.Comp.BloodSolution))
|
||||
{
|
||||
var bloodTemp = _solutionContainers.SplitSolution(target.Comp.BloodSolution.Value, drawAmount);
|
||||
_solutionContainers.TryAddSolution(injectorSolution, bloodTemp);
|
||||
}
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
|
||||
("amount", transferAmount),
|
||||
("target", Identity.Entity(target, EntityManager))), injector, user);
|
||||
("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
|
||||
|
||||
Dirty(component);
|
||||
AfterDraw(component, injector, target);
|
||||
Dirty(injector);
|
||||
AfterDraw(injector, target);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems;
|
||||
|
|
@ -24,7 +23,7 @@ public sealed partial class ChemistrySystem : EntitySystem
|
|||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutions = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainers = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
using System.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Forensics;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.GameStates;
|
||||
using Content.Shared.Forensics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
|
|
@ -26,33 +27,33 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
{
|
||||
SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
|
||||
SubscribeLocalEvent<HyposprayComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<HyposprayComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<HyposprayComponent, ComponentGetState>(OnHypoGetState);
|
||||
}
|
||||
|
||||
private void OnHypoGetState(EntityUid uid, HyposprayComponent component, ref ComponentGetState args)
|
||||
private void OnHypoGetState(Entity<HyposprayComponent> entity, ref ComponentGetState args)
|
||||
{
|
||||
args.State = _solutions.TryGetSolution(uid, component.SolutionName, out var solution)
|
||||
args.State = _solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out _, out var solution)
|
||||
? new HyposprayComponentState(solution.Volume, solution.MaxVolume)
|
||||
: new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, HyposprayComponent component, UseInHandEvent args)
|
||||
private void OnUseInHand(Entity<HyposprayComponent> entity, ref UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
TryDoInject(uid, args.User, args.User);
|
||||
TryDoInject(entity, args.User, args.User);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, HyposprayComponent component, SolutionChangedEvent args)
|
||||
private void OnSolutionChange(Entity<HyposprayComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
Dirty(component);
|
||||
Dirty(entity);
|
||||
}
|
||||
|
||||
public void OnAfterInteract(EntityUid uid, HyposprayComponent component, AfterInteractEvent args)
|
||||
public void OnAfterInteract(Entity<HyposprayComponent> entity, ref AfterInteractEvent args)
|
||||
{
|
||||
if (!args.CanReach)
|
||||
return;
|
||||
|
|
@ -60,21 +61,20 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
var target = args.Target;
|
||||
var user = args.User;
|
||||
|
||||
TryDoInject(uid, target, user);
|
||||
TryDoInject(entity, target, user);
|
||||
}
|
||||
|
||||
public void OnAttack(EntityUid uid, HyposprayComponent component, MeleeHitEvent args)
|
||||
public void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
|
||||
{
|
||||
if (!args.HitEntities.Any())
|
||||
return;
|
||||
|
||||
TryDoInject(uid, args.HitEntities.First(), args.User);
|
||||
TryDoInject(entity, args.HitEntities.First(), args.User);
|
||||
}
|
||||
|
||||
public bool TryDoInject(EntityUid uid, EntityUid? target, EntityUid user, HyposprayComponent? component=null)
|
||||
public bool TryDoInject(Entity<HyposprayComponent> hypo, EntityUid? target, EntityUid user)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
var (uid, component) = hypo;
|
||||
|
||||
if (!EligibleEntity(target, _entMan, component))
|
||||
return false;
|
||||
|
|
@ -92,15 +92,13 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
target = user;
|
||||
}
|
||||
|
||||
_solutions.TryGetSolution(uid, component.SolutionName, out var hypoSpraySolution);
|
||||
|
||||
if (hypoSpraySolution == null || hypoSpraySolution.Volume == 0)
|
||||
if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0)
|
||||
{
|
||||
_popup.PopupCursor(Loc.GetString("hypospray-component-empty-message"), user);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_solutions.TryGetInjectableSolution(target.Value, out var targetSolution))
|
||||
if (!_solutionContainers.TryGetInjectableSolution(target.Value, out var targetSoln, out var targetSolution))
|
||||
{
|
||||
_popup.PopupCursor(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target.Value, _entMan))), user);
|
||||
return false;
|
||||
|
|
@ -127,17 +125,17 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
_popup.PopupCursor(Loc.GetString("hypospray-component-transfer-already-full-message",("owner", target)), user);
|
||||
_popup.PopupCursor(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), user);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move units from attackSolution to targetSolution
|
||||
var removedSolution = _solutions.SplitSolution(uid, hypoSpraySolution, realTransferAmount);
|
||||
var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount);
|
||||
|
||||
if (!targetSolution.CanAddSolution(removedSolution))
|
||||
return true;
|
||||
_reactiveSystem.DoEntityReaction(target.Value, removedSolution, ReactionMethod.Injection);
|
||||
_solutions.TryAddSolution(target.Value, targetSolution, removedSolution);
|
||||
_solutionContainers.TryAddSolution(targetSoln.Value, removedSolution);
|
||||
|
||||
var ev = new TransferDnaEvent { Donor = target.Value, Recipient = uid };
|
||||
RaiseLocalEvent(target.Value, ref ev);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems;
|
||||
|
||||
|
|
@ -15,27 +11,26 @@ public sealed partial class ChemistrySystem
|
|||
SubscribeLocalEvent<ReactionMixerComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
}
|
||||
|
||||
private void OnAfterInteract(EntityUid uid, ReactionMixerComponent component, AfterInteractEvent args)
|
||||
private void OnAfterInteract(Entity<ReactionMixerComponent> entity, ref AfterInteractEvent args)
|
||||
{
|
||||
if (!args.Target.HasValue || !args.CanReach)
|
||||
return;
|
||||
|
||||
var mixAttemptEvent = new MixingAttemptEvent(uid);
|
||||
RaiseLocalEvent(uid, ref mixAttemptEvent);
|
||||
if(mixAttemptEvent.Cancelled)
|
||||
var mixAttemptEvent = new MixingAttemptEvent(entity);
|
||||
RaiseLocalEvent(entity, ref mixAttemptEvent);
|
||||
if (mixAttemptEvent.Cancelled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Solution? solution = null;
|
||||
if (!_solutions.TryGetMixableSolution(args.Target.Value, out solution))
|
||||
return;
|
||||
if (!_solutionContainers.TryGetMixableSolution(args.Target.Value, out var solution))
|
||||
return;
|
||||
|
||||
_popup.PopupEntity(Loc.GetString(component.MixMessage, ("mixed", Identity.Entity(args.Target.Value, EntityManager)), ("mixer", Identity.Entity(uid, EntityManager))), args.User, args.User);
|
||||
_popup.PopupEntity(Loc.GetString(entity.Comp.MixMessage, ("mixed", Identity.Entity(args.Target.Value, EntityManager)), ("mixer", Identity.Entity(entity.Owner, EntityManager))), args.User, args.User);
|
||||
|
||||
_solutions.UpdateChemicals(args.Target.Value, solution, true, component);
|
||||
_solutionContainers.UpdateChemicals(solution.Value, true, entity.Comp);
|
||||
|
||||
var afterMixingEvent = new AfterMixingEvent(uid, args.Target.Value);
|
||||
RaiseLocalEvent(uid, afterMixingEvent);
|
||||
var afterMixingEvent = new AfterMixingEvent(entity, args.Target.Value);
|
||||
RaiseLocalEvent(entity, afterMixingEvent);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue