Merge branch 'master' of https://github.com/space-wizards/space-station-14 into moss-seniorcargo

This commit is contained in:
Hitlinemoss
2025-07-07 18:09:10 -04:00
617 changed files with 10860 additions and 4884 deletions

View File

@@ -8,6 +8,7 @@ using Robust.Shared;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Benchmarks;
@@ -18,9 +19,11 @@ namespace Content.Benchmarks;
[Virtual, MemoryDiagnoser]
public class SpawnEquipDeleteBenchmark
{
private static readonly EntProtoId Mob = "MobHuman";
private static readonly ProtoId<StartingGearPrototype> CaptainStartingGear = "CaptainGear";
private TestPair _pair = default!;
private StationSpawningSystem _spawnSys = default!;
private const string Mob = "MobHuman";
private StartingGearPrototype _gear = default!;
private EntityUid _entity;
private EntityCoordinates _coords;
@@ -39,7 +42,7 @@ public class SpawnEquipDeleteBenchmark
var mapData = await _pair.CreateTestMap();
_coords = mapData.GridCoords;
_spawnSys = server.System<StationSpawningSystem>();
_gear = server.ProtoMan.Index<StartingGearPrototype>("CaptainGear");
_gear = server.ProtoMan.Index(CaptainStartingGear);
}
[GlobalCleanup]

View File

@@ -15,6 +15,8 @@ namespace Content.Client.Access.UI;
[GenerateTypedNameReferences]
public sealed partial class GroupedAccessLevelChecklist : BoxContainer
{
private static readonly ProtoId<AccessGroupPrototype> GeneralAccessGroup = "General";
[Dependency] private readonly IPrototypeManager _protoManager = default!;
private bool _isMonotone;
@@ -63,7 +65,7 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer
// Ensure that the 'general' access group is added to handle
// misc. access levels that aren't associated with any group
if (_protoManager.TryIndex<AccessGroupPrototype>("General", out var generalAccessProto))
if (_protoManager.TryIndex(GeneralAccessGroup, out var generalAccessProto))
_groupedAccessLevels.TryAdd(generalAccessProto, new());
// Assign known access levels with their associated groups

View File

@@ -20,6 +20,10 @@ public sealed class AdminLogsEui : BaseEui
[Dependency] private readonly IFileDialogManager _dialogManager = default!;
[Dependency] private readonly ILogManager _log = default!;
private const char CsvSeparator = ',';
private const string CsvQuote = "\"";
private const string CsvHeader = "Date,ID,PlayerID,Severity,Type,Message";
private ISawmill _sawmill;
private bool _currentlyExportingLogs = false;
@@ -100,7 +104,9 @@ public sealed class AdminLogsEui : BaseEui
try
{
await using var writer = new StreamWriter(file.Value.fileStream);
// Buffer is set to 4KB for performance reasons. As the average export of 1000 logs is ~200KB
await using var writer = new StreamWriter(file.Value.fileStream, bufferSize: 4096);
await writer.WriteLineAsync(CsvHeader);
foreach (var child in LogsControl.LogsContainer.Children)
{
if (child is not AdminLogLabel logLabel || !child.Visible)
@@ -108,28 +114,31 @@ public sealed class AdminLogsEui : BaseEui
var log = logLabel.Log;
// Date
// I swear to god if someone adds ,s or "s to the other fields...
await writer.WriteAsync(log.Date.ToString("s", System.Globalization.CultureInfo.InvariantCulture));
await writer.WriteAsync(',');
await writer.WriteAsync(CsvSeparator);
// ID
await writer.WriteAsync(log.Id.ToString());
await writer.WriteAsync(',');
await writer.WriteAsync(log.Impact.ToString());
await writer.WriteAsync(',');
// Message
await writer.WriteAsync('"');
await writer.WriteAsync(log.Message.Replace("\"", "\"\""));
await writer.WriteAsync('"');
// End of message
await writer.WriteAsync(',');
await writer.WriteAsync(CsvSeparator);
// PlayerID
var players = log.Players;
for (var i = 0; i < players.Length; i++)
{
await writer.WriteAsync(players[i] + (i == players.Length - 1 ? "" : " "));
}
await writer.WriteAsync(',');
await writer.WriteAsync(CsvSeparator);
// Severity
await writer.WriteAsync(log.Impact.ToString());
await writer.WriteAsync(CsvSeparator);
// Type
await writer.WriteAsync(log.Type.ToString());
await writer.WriteAsync(CsvSeparator);
// Message
await writer.WriteAsync(CsvQuote);
await writer.WriteAsync(log.Message.Replace(CsvQuote, CsvQuote + CsvQuote));
await writer.WriteAsync(CsvQuote);
await writer.WriteLineAsync();
}
}

View File

@@ -0,0 +1,31 @@
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Piping.Binary.Components;
namespace Content.Client.Atmos.EntitySystems;
/// <summary>
/// Represents the client system responsible for managing and updating the gas pressure regulator interface.
/// Inherits from the shared system <see cref="SharedGasPressureRegulatorSystem"/>.
/// </summary>
public sealed partial class GasPressureRegulatorSystem : SharedGasPressureRegulatorSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasPressureRegulatorComponent, AfterAutoHandleStateEvent>(OnValveUpdate);
}
private void OnValveUpdate(Entity<GasPressureRegulatorComponent> ent, ref AfterAutoHandleStateEvent args)
{
UpdateUi(ent);
}
protected override void UpdateUi(Entity<GasPressureRegulatorComponent> ent)
{
if (UserInterfaceSystem.TryGetOpenUi(ent.Owner, GasPressureRegulatorUiKey.Key, out var bui))
{
bui.Update();
}
}
}

View File

@@ -19,6 +19,8 @@ namespace Content.Client.Atmos.Overlays
{
public sealed class GasTileOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
private readonly IEntityManager _entManager;
private readonly IMapManager _mapManager;
private readonly SharedMapSystem _mapSystem;
@@ -54,7 +56,7 @@ namespace Content.Client.Atmos.Overlays
_mapManager = IoCManager.Resolve<IMapManager>();
_mapSystem = entManager.System<SharedMapSystem>();
_xformSys = xformSys;
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
_shader = protoMan.Index(UnshadedShader).Instance();
ZIndex = GasOverlayZIndex;
_gasCount = system.VisibleGasId.Length;

View File

@@ -0,0 +1,58 @@
using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Localizations;
using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI;
public sealed class GasPressureRegulatorBoundUserInterface(EntityUid owner, Enum uiKey)
: BoundUserInterface(owner, uiKey)
{
private GasPressureRegulatorWindow? _window;
protected override void Open()
{
base.Open();
_window = this.CreateWindow<GasPressureRegulatorWindow>();
_window.SetEntity(Owner);
_window.ThresholdPressureChanged += OnThresholdChanged;
if (EntMan.TryGetComponent(Owner, out GasPressureRegulatorComponent? comp))
_window.SetThresholdPressureInput(comp.Threshold);
Update();
}
public override void Update()
{
if (_window == null)
return;
_window.Title = Identity.Name(Owner, EntMan);
if (!EntMan.TryGetComponent(Owner, out GasPressureRegulatorComponent? comp))
return;
_window.SetThresholdPressureLabel(comp.Threshold);
_window.UpdateInfo(comp.InletPressure, comp.OutletPressure, comp.FlowRate);
}
private void OnThresholdChanged(string newThreshold)
{
var sentThreshold = 0f;
if (UserInputParser.TryFloat(newThreshold, out var parsedNewThreshold) && parsedNewThreshold >= 0 &&
!float.IsInfinity(parsedNewThreshold))
{
sentThreshold = parsedNewThreshold;
}
// Autofill to zero if the user inputs an invalid value.
_window?.SetThresholdPressureInput(sentThreshold);
SendPredictedMessage(new GasPressureRegulatorChangeThresholdMessage(sentThreshold));
}
}

View File

@@ -0,0 +1,96 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
SetSize="345 380"
MinSize="345 380"
Title="{Loc gas-pressure-regulator-ui-title}"
Resizable="False">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Vertical" Margin="0 10 0 10">
<BoxContainer Orientation="Vertical" Align="Center">
<Label Text="{Loc gas-pressure-regulator-ui-outlet}" Align="Center" StyleClasses="LabelKeyText" />
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center">
<Label Name="OutletPressureLabel" Text="N/A" Margin="0 0 4 0" />
<Label Text="{Loc gas-pressure-regulator-ui-pressure-unit}" />
</BoxContainer>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Align="Center">
<BoxContainer Orientation="Vertical" Align="Center" HorizontalExpand="True">
<Label Text="{Loc gas-pressure-regulator-ui-target}" Align="Right" StyleClasses="LabelKeyText" />
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Right">
<Label Name="TargetPressureLabel" Margin="0 0 4 0" />
<Label Text="{Loc gas-pressure-regulator-ui-pressure-unit}" />
</BoxContainer>
</BoxContainer>
<ProgressBar Name="ToTargetBar" MaxValue="1" SetSize="5 75" Margin="10" Vertical="True" />
<SpriteView Name="EntityView" SetSize="64 64" Scale="3 3" OverrideDirection="North" Margin="0" />
<ProgressBar Name="FlowRateBar" MaxValue="1" SetSize="5 75" Margin="10" Vertical="True" />
<BoxContainer Orientation="Vertical" Align="Center" HorizontalExpand="True">
<Label Text="{Loc gas-pressure-regulator-ui-flow}" StyleClasses="LabelKeyText" />
<BoxContainer Orientation="Horizontal">
<Label Name="CurrentFlowLabel" Text="N/A" Margin="0 0 4 0" />
<Label Text="{Loc gas-pressure-regulator-ui-flow-rate-unit}" />
</BoxContainer>
</BoxContainer>
</BoxContainer>
<BoxContainer Orientation="Vertical" Align="Center" Margin="1">
<Label Text="{Loc gas-pressure-regulator-ui-inlet}" Align="Center" StyleClasses="LabelKeyText" />
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center">
<Label Name="InletPressureLabel" Text="N/A" Margin="0 0 4 0" />
<Label Text="{Loc gas-pressure-regulator-ui-pressure-unit}" />
</BoxContainer>
</BoxContainer>
</BoxContainer>
<!-- Controls to Set Pressure -->
<controls:StripeBack Name="SetPressureStripeBack" HorizontalExpand="True">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="10 10 10 10">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<LineEdit Name="ThresholdInput" HorizontalExpand="True" MinSize="70 0" />
<Button Name="SetThresholdButton" Text="{Loc gas-pressure-regulator-ui-set-threshold}"
Disabled="True" Margin="5 0 0 0" />
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 5 0 0">
<Button Name="Subtract1000Button" Text="{Loc gas-pressure-regulator-ui-subtract-1000}"
HorizontalExpand="True" Margin="0 2 2 0"
StyleClasses="OpenBoth" />
<Button Name="Subtract100Button" Text="{Loc gas-pressure-regulator-ui-subtract-100}"
HorizontalExpand="True" Margin="0 2 2 0"
StyleClasses="OpenBoth" />
<Button Name="Subtract10Button" Text="{Loc gas-pressure-regulator-ui-subtract-10}"
HorizontalExpand="True" Margin="0 2 2 0"
StyleClasses="OpenBoth" />
<Button Name="Add10Button" Text="{Loc gas-pressure-regulator-ui-add-10}" HorizontalExpand="True"
Margin="0 2 2 0"
StyleClasses="OpenBoth" />
<Button Name="Add100Button" Text="{Loc gas-pressure-regulator-ui-add-100}"
HorizontalExpand="True" Margin="0 2 2 0"
StyleClasses="OpenBoth" />
<Button Name="Add1000Button" Text="{Loc gas-pressure-regulator-ui-add-1000}"
HorizontalExpand="True" Margin="0 2 2 0"
StyleClasses="OpenBoth" />
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 5 0 0">
<Button Name="ZeroThresholdButton" Text="{Loc gas-pressure-regulator-ui-zero-threshold}"
HorizontalExpand="True" Margin="0 0 5 0" />
<Button Name="SetToCurrentPressureButton"
Text="{Loc gas-pressure-regulator-ui-set-to-current-pressure}" HorizontalExpand="True" />
</BoxContainer>
</BoxContainer>
</controls:StripeBack>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -0,0 +1,129 @@
using System.Globalization;
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client.Atmos.UI;
/// <summary>
/// Client-side UI for controlling a pressure regulator.
/// </summary>
[GenerateTypedNameReferences]
public sealed partial class GasPressureRegulatorWindow : FancyWindow
{
private float _flowRate;
public GasPressureRegulatorWindow()
{
RobustXamlLoader.Load(this);
ThresholdInput.OnTextChanged += _ => SetThresholdButton.Disabled = false;
SetThresholdButton.OnPressed += _ =>
{
ThresholdPressureChanged?.Invoke(ThresholdInput.Text ??= "");
SetThresholdButton.Disabled = true;
};
SetToCurrentPressureButton.OnPressed += _ =>
{
if (InletPressureLabel.Text != null)
{
ThresholdInput.Text = InletPressureLabel.Text;
}
SetThresholdButton.Disabled = false;
};
ZeroThresholdButton.OnPressed += _ =>
{
ThresholdInput.Text = "0";
SetThresholdButton.Disabled = false;
};
Add1000Button.OnPressed += _ => AdjustThreshold(1000);
Add100Button.OnPressed += _ => AdjustThreshold(100);
Add10Button.OnPressed += _ => AdjustThreshold(10);
Subtract10Button.OnPressed += _ => AdjustThreshold(-10);
Subtract100Button.OnPressed += _ => AdjustThreshold(-100);
Subtract1000Button.OnPressed += _ => AdjustThreshold(-1000);
return;
void AdjustThreshold(float adjustment)
{
if (float.TryParse(ThresholdInput.Text, out var currentValue))
{
ThresholdInput.Text = (currentValue + adjustment).ToString(CultureInfo.CurrentCulture);
SetThresholdButton.Disabled = false;
}
}
}
public event Action<string>? ThresholdPressureChanged;
/// <summary>
/// Sets the current threshold pressure label. This is not setting the threshold input box.
/// </summary>
/// <param name="threshold"> Threshold to set.</param>
public void SetThresholdPressureLabel(float threshold)
{
TargetPressureLabel.Text = threshold.ToString(CultureInfo.CurrentCulture);
}
/// <summary>
/// Sets the threshold pressure input field with the given value.
/// When the client opens the UI the field will be autofilled with the current threshold pressure.
/// </summary>
/// <param name="input">The threshold pressure value to autofill into the input field.</param>
public void SetThresholdPressureInput(float input)
{
ThresholdInput.Text = input.ToString(CultureInfo.CurrentCulture);
}
/// <summary>
/// Sets the entity to be visible in the UI.
/// </summary>
/// <param name="entity"></param>
public void SetEntity(EntityUid entity)
{
EntityView.SetEntity(entity);
}
/// <summary>
/// Updates the UI for the labels.
/// </summary>
/// <param name="inletPressure">The current pressure at the valve's inlet.</param>
/// <param name="outletPressure">The current pressure at the valve's outlet.</param>
/// <param name="flowRate">The current flow rate through the valve.</param>
public void UpdateInfo(float inletPressure, float outletPressure, float flowRate)
{
if (float.TryParse(TargetPressureLabel.Text, out var parsedfloat))
ToTargetBar.Value = inletPressure / parsedfloat;
InletPressureLabel.Text = float.Round(inletPressure).ToString(CultureInfo.CurrentCulture);
OutletPressureLabel.Text = float.Round(outletPressure).ToString(CultureInfo.CurrentCulture);
CurrentFlowLabel.Text = float.IsNaN(flowRate) ? "0" : flowRate.ToString(CultureInfo.CurrentCulture);
_flowRate = flowRate;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
// Defines the flow rate at which the progress bar fills in one second.
// If the flow rate is >50 L/s, the bar will take <1 second to fill.
// If the flow rate is <50 L/s, the bar will take >1 second to fill.
const int barFillPerSecond = 50;
var maxValue = FlowRateBar.MaxValue;
// Increment the progress bar value based on elapsed time
FlowRateBar.Value += (_flowRate / barFillPerSecond) * args.DeltaSeconds;
// Reset the progress bar when it is fully filled
if (FlowRateBar.Value >= maxValue)
{
FlowRateBar.Value = 0f;
}
}
}

View File

@@ -1,21 +0,0 @@
using Content.Shared.Bed;
using Robust.Client.GameObjects;
namespace Content.Client.Bed;
public sealed class StasisBedSystem : VisualizerSystem<StasisBedVisualsComponent>
{
protected override void OnAppearanceChange(EntityUid uid, StasisBedVisualsComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite != null
&& AppearanceSystem.TryGetData<bool>(uid, StasisBedVisuals.IsOn, out var isOn, args.Component))
{
SpriteSystem.LayerSetVisible((uid, args.Sprite), StasisBedVisualLayers.IsOn, isOn);
}
}
}
public enum StasisBedVisualLayers : byte
{
IsOn,
}

View File

@@ -0,0 +1,5 @@
using Content.Shared.Body.Systems;
namespace Content.Client.Body.Systems;
public sealed class BloodstreamSystem : SharedBloodstreamSystem;

View File

@@ -0,0 +1,6 @@
using Content.Shared.Body.Systems;
namespace Content.Client.Body.Systems;
/// <inheritdoc/>
public sealed class MetabolizerSystem : SharedMetabolizerSystem;

View File

@@ -18,14 +18,10 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
{
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[ValidatePrototypeId<MixingCategoryPrototype>]
private const string DefaultMixingCategory = "DummyMix";
[ValidatePrototypeId<MixingCategoryPrototype>]
private const string DefaultGrindCategory = "DummyGrind";
[ValidatePrototypeId<MixingCategoryPrototype>]
private const string DefaultJuiceCategory = "DummyJuice";
[ValidatePrototypeId<MixingCategoryPrototype>]
private const string DefaultCondenseCategory = "DummyCondense";
private static readonly ProtoId<MixingCategoryPrototype> DefaultMixingCategory = "DummyMix";
private static readonly ProtoId<MixingCategoryPrototype> DefaultGrindCategory = "DummyGrind";
private static readonly ProtoId<MixingCategoryPrototype> DefaultJuiceCategory = "DummyJuice";
private static readonly ProtoId<MixingCategoryPrototype> DefaultCondenseCategory = "DummyCondense";
private readonly Dictionary<string, List<ReagentSourceData>> _reagentSources = new();

View File

@@ -8,6 +8,8 @@ namespace Content.Client.CombatMode
{
public sealed class ColoredScreenBorderOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> Shader = "ColoredScreenBorder";
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
@@ -16,7 +18,7 @@ namespace Content.Client.CombatMode
public ColoredScreenBorderOverlay()
{
IoCManager.InjectDependencies(this);
_shader = _prototypeManager.Index<ShaderPrototype>("ColoredScreenBorder").Instance();
_shader = _prototypeManager.Index(Shader).Instance();
}
protected override void Draw(in OverlayDrawArgs args)

View File

@@ -27,8 +27,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
private EntityUid _owner;
[ValidatePrototypeId<EntityPrototype>]
public const string NoBoardEffectId = "FlatpackerNoBoardEffect";
public static readonly EntProtoId NoBoardEffectId = "FlatpackerNoBoardEffect";
private EntityUid? _currentBoard = EntityUid.Invalid;

View File

@@ -7,6 +7,8 @@ namespace Content.Client.Cooldown
{
public sealed class CooldownGraphic : Control
{
private static readonly ProtoId<ShaderPrototype> Shader = "CooldownAnimation";
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
@@ -15,7 +17,7 @@ namespace Content.Client.Cooldown
public CooldownGraphic()
{
IoCManager.InjectDependencies(this);
_shader = _protoMan.Index<ShaderPrototype>("CooldownAnimation").InstanceUnique();
_shader = _protoMan.Index(Shader).InstanceUnique();
}
/// <summary>

View File

@@ -33,8 +33,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
public readonly EntityUid Console;
[ValidatePrototypeId<LocalizedDatasetPrototype>]
private const string ReasonPlaceholders = "CriminalRecordsWantedReasonPlaceholders";
private static readonly ProtoId<LocalizedDatasetPrototype> ReasonPlaceholders = "CriminalRecordsWantedReasonPlaceholders";
public Action<uint?>? OnKeySelected;
public Action<StationRecordFilterType, string>? OnFiltersChanged;
@@ -296,7 +295,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
var field = "reason";
var title = Loc.GetString("criminal-records-status-" + status.ToString().ToLower());
var placeholders = _proto.Index<LocalizedDatasetPrototype>(ReasonPlaceholders);
var placeholders = _proto.Index(ReasonPlaceholders);
var placeholder = Loc.GetString("criminal-records-console-reason-placeholder", ("placeholder", _random.Pick(placeholders))); // just funny it doesn't actually get used
var prompt = Loc.GetString("criminal-records-console-reason");
var entry = new QuickDialogEntry(field, QuickDialogEntryType.LongText, prompt, placeholder);

View File

@@ -24,7 +24,7 @@ public sealed class DeliveryVisualizerSystem : VisualizerSystem<DeliveryComponen
if (!_prototype.TryIndex<JobIconPrototype>(job, out var icon))
{
SpriteSystem.LayerSetTexture((uid, args.Sprite), DeliveryVisualLayers.JobStamp, SpriteSystem.Frame0(_prototype.Index("JobIconUnknown")));
SpriteSystem.LayerSetTexture((uid, args.Sprite), DeliveryVisualLayers.JobStamp, SpriteSystem.Frame0(_prototype.Index(UnknownIcon).Icon));
return;
}

View File

@@ -14,6 +14,8 @@ namespace Content.Client.DoAfter;
public sealed class DoAfterOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
private readonly IEntityManager _entManager;
private readonly IGameTiming _timing;
private readonly IPlayerManager _player;
@@ -50,7 +52,7 @@ public sealed class DoAfterOverlay : Overlay
var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
_barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
_unshadedShader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
_unshadedShader = protoManager.Index(UnshadedShader).Instance();
}
protected override void Draw(in OverlayDrawArgs args)

View File

@@ -10,6 +10,8 @@ namespace Content.Client.Drowsiness;
public sealed class DrowsinessOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> Shader = "Drowsiness";
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -33,7 +35,7 @@ public sealed class DrowsinessOverlay : Overlay
_statusEffects = _sysMan.GetEntitySystem<SharedStatusEffectsSystem>();
_drowsinessShader = _prototypeManager.Index<ShaderPrototype>("Drowsiness").InstanceUnique();
_drowsinessShader = _prototypeManager.Index(Shader).InstanceUnique();
}
protected override void FrameUpdate(FrameEventArgs args)

View File

@@ -12,6 +12,8 @@ namespace Content.Client.Drugs;
public sealed class RainbowOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> Shader = "Rainbow";
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
@@ -41,7 +43,7 @@ public sealed class RainbowOverlay : Overlay
_statusEffects = _sysMan.GetEntitySystem<SharedStatusEffectsSystem>();
_rainbowShader = _prototypeManager.Index<ShaderPrototype>("Rainbow").InstanceUnique();
_rainbowShader = _prototypeManager.Index(Shader).InstanceUnique();
_config.OnValueChanged(CCVars.ReducedMotion, OnReducedMotionChanged, invokeImmediately: true);
}

View File

@@ -10,6 +10,8 @@ namespace Content.Client.Drunk;
public sealed class DrunkOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> Shader = "Drunk";
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -30,7 +32,7 @@ public sealed class DrunkOverlay : Overlay
public DrunkOverlay()
{
IoCManager.InjectDependencies(this);
_drunkShader = _prototypeManager.Index<ShaderPrototype>("Drunk").InstanceUnique();
_drunkShader = _prototypeManager.Index(Shader).InstanceUnique();
}
protected override void FrameUpdate(FrameEventArgs args)

View File

@@ -13,6 +13,8 @@ namespace Content.Client.Explosion;
[UsedImplicitly]
public sealed class ExplosionOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
@@ -26,7 +28,7 @@ public sealed class ExplosionOverlay : Overlay
public ExplosionOverlay(SharedAppearanceSystem appearanceSystem)
{
IoCManager.InjectDependencies(this);
_shader = _proto.Index<ShaderPrototype>("unshaded").Instance();
_shader = _proto.Index(UnshadedShader).Instance();
_transformSystem = _entMan.System<SharedTransformSystem>();
_appearance = appearanceSystem;
}

View File

@@ -12,6 +12,9 @@ namespace Content.Client.Eye.Blinding
{
public sealed class BlindOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> GreyscaleShader = "GreyscaleFullscreen";
private static readonly ProtoId<ShaderPrototype> CircleShader = "CircleMask";
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
@@ -27,8 +30,8 @@ namespace Content.Client.Eye.Blinding
public BlindOverlay()
{
IoCManager.InjectDependencies(this);
_greyscaleShader = _prototypeManager.Index<ShaderPrototype>("GreyscaleFullscreen").InstanceUnique();
_circleMaskShader = _prototypeManager.Index<ShaderPrototype>("CircleMask").InstanceUnique();
_greyscaleShader = _prototypeManager.Index(GreyscaleShader).InstanceUnique();
_circleMaskShader = _prototypeManager.Index(CircleShader).InstanceUnique();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{

View File

@@ -10,6 +10,9 @@ namespace Content.Client.Eye.Blinding
{
public sealed class BlurryVisionOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> CataractsShader = "Cataracts";
private static readonly ProtoId<ShaderPrototype> CircleShader = "CircleMask";
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
@@ -33,8 +36,8 @@ namespace Content.Client.Eye.Blinding
public BlurryVisionOverlay()
{
IoCManager.InjectDependencies(this);
_cataractsShader = _prototypeManager.Index<ShaderPrototype>("Cataracts").InstanceUnique();
_circleMaskShader = _prototypeManager.Index<ShaderPrototype>("CircleMask").InstanceUnique();
_cataractsShader = _prototypeManager.Index(CataractsShader).InstanceUnique();
_circleMaskShader = _prototypeManager.Index(CircleShader).InstanceUnique();
_circleMaskShader.SetParameter("CircleMinDist", 0.0f);
_circleMaskShader.SetParameter("CirclePow", NoMotion_Pow);

View File

@@ -11,6 +11,8 @@ namespace Content.Client.Flash
{
public sealed class FlashOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> FlashedEffectShader = "FlashedEffect";
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -27,7 +29,7 @@ namespace Content.Client.Flash
public FlashOverlay()
{
IoCManager.InjectDependencies(this);
_shader = _prototypeManager.Index<ShaderPrototype>("FlashedEffect").InstanceUnique();
_shader = _prototypeManager.Index(FlashedEffectShader).InstanceUnique();
_flash = _entityManager.System<SharedFlashSystem>();
_statusSys = _entityManager.System<StatusEffectsSystem>();
}

View File

@@ -21,8 +21,7 @@ namespace Content.Client.Guidebook.Controls;
[UsedImplicitly, GenerateTypedNameReferences]
public sealed partial class GuideReagentReaction : BoxContainer, ISearchableControl
{
[ValidatePrototypeId<MixingCategoryPrototype>]
private const string DefaultMixingCategory = "DummyMix";
private static readonly ProtoId<MixingCategoryPrototype> DefaultMixingCategory = "DummyMix";
private readonly IPrototypeManager _protoMan;
@@ -55,7 +54,7 @@ public sealed partial class GuideReagentReaction : BoxContainer, ISearchableCont
}
else
{
mixingCategories.Add(protoMan.Index<MixingCategoryPrototype>(DefaultMixingCategory));
mixingCategories.Add(protoMan.Index(DefaultMixingCategory));
}
SetMixingCategory(mixingCategories, prototype, sysMan);
}

View File

@@ -6,7 +6,7 @@ using Robust.Shared.Utility;
namespace Content.Client.Guidebook.Richtext;
[UsedImplicitly]
public sealed class KeyBindTag : IMarkupTag
public sealed class KeyBindTag : IMarkupTagHandler
{
[Dependency] private readonly IInputManager _inputManager = default!;

View File

@@ -9,7 +9,7 @@ namespace Content.Client.Guidebook.RichText;
/// In order to be accessed by this tag, the desired field/property must
/// be tagged with <see cref="Shared.Guidebook.GuidebookDataAttribute"/>.
/// </summary>
public sealed class ProtodataTag : IMarkupTag
public sealed class ProtodataTag : IMarkupTagHandler
{
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly IEntityManager _entMan = default!;

View File

@@ -10,16 +10,14 @@ using Content.Client.UserInterface.ControlExtensions;
namespace Content.Client.Guidebook.RichText;
[UsedImplicitly]
public sealed class TextLinkTag : IMarkupTag
public sealed class TextLinkTag : IMarkupTagHandler
{
public static Color LinkColor => Color.CornflowerBlue;
public string Name => "textlink";
public Control? Control;
/// <inheritdoc/>
public bool TryGetControl(MarkupNode node, [NotNullWhen(true)] out Control? control)
public bool TryCreateControl(MarkupNode node, [NotNullWhen(true)] out Control? control)
{
if (!node.Value.TryGetString(out var text)
|| !node.Attributes.TryGetValue("link", out var linkParameter)
@@ -38,22 +36,21 @@ public sealed class TextLinkTag : IMarkupTag
label.OnMouseEntered += _ => label.FontColorOverride = Color.LightSkyBlue;
label.OnMouseExited += _ => label.FontColorOverride = Color.CornflowerBlue;
label.OnKeyBindDown += args => OnKeybindDown(args, link);
label.OnKeyBindDown += args => OnKeybindDown(args, link, label);
control = label;
Control = label;
return true;
}
private void OnKeybindDown(GUIBoundKeyEventArgs args, string link)
private void OnKeybindDown(GUIBoundKeyEventArgs args, string link, Control? control)
{
if (args.Function != EngineKeyFunctions.UIClick)
return;
if (Control == null)
if (control == null)
return;
if (Control.TryGetParentHandler<ILinkClickHandler>(out var handler))
if (control.TryGetParentHandler<ILinkClickHandler>(out var handler))
handler.HandleClick(link);
else
Logger.Warning("Warning! No valid ILinkClickHandler found.");

View File

@@ -27,6 +27,7 @@ public sealed class EyeColorPicker : Control
AddChild(vBox);
vBox.AddChild(_colorSelectors = new ColorSelectorSliders());
_colorSelectors.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV
_colorSelectors.OnColorChanged += ColorValueChanged;
}

View File

@@ -416,6 +416,7 @@ public sealed partial class MarkingPicker : Control
CMarkingColors.AddChild(colorContainer);
ColorSelectorSliders colorSelector = new ColorSelectorSliders();
colorSelector.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV
colorSliders.Add(colorSelector);
colorContainer.AddChild(new Label { Text = $"{stateNames[i]} color:" });

View File

@@ -231,6 +231,7 @@ public sealed partial class SingleMarkingPicker : BoxContainer
HorizontalExpand = true
};
selector.Color = marking.MarkingColors[i];
selector.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV
var colorIndex = i;
selector.OnColorChanged += color =>

View File

@@ -102,6 +102,8 @@ public static class MidiParser
// 0x03 is TrackName,
// 0x04 is InstrumentName
// This string can potentially contain control characters, including 0x00 which can cause problems if it ends up in database entries via admin logs
// we sanitize TrackName and InstrumentName after they have been send to the server
var text = Encoding.ASCII.GetString(metaData, 0, (int)metaLength);
switch (metaType)
{

View File

@@ -7,17 +7,14 @@ namespace Content.Client.Interactable.Components
[RegisterComponent]
public sealed partial class InteractionOutlineComponent : Component
{
private static readonly ProtoId<ShaderPrototype> ShaderInRange = "SelectionOutlineInrange";
private static readonly ProtoId<ShaderPrototype> ShaderOutOfRange = "SelectionOutline";
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
private const float DefaultWidth = 1;
[ValidatePrototypeId<ShaderPrototype>]
private const string ShaderInRange = "SelectionOutlineInrange";
[ValidatePrototypeId<ShaderPrototype>]
private const string ShaderOutOfRange = "SelectionOutline";
private bool _inRange;
private ShaderInstance? _shader;
private int _lastRenderScale;
@@ -65,7 +62,7 @@ namespace Content.Client.Interactable.Components
{
var shaderName = inRange ? ShaderInRange : ShaderOutOfRange;
var instance = _prototypeManager.Index<ShaderPrototype>(shaderName).InstanceUnique();
var instance = _prototypeManager.Index(shaderName).InstanceUnique();
instance.SetParameter("outline_width", DefaultWidth * renderScale);
return instance;
}

View File

@@ -29,6 +29,10 @@ namespace Content.Client.Interaction;
/// </summary>
public sealed class DragDropSystem : SharedDragDropSystem
{
private static readonly ProtoId<ShaderPrototype> ShaderDropTargetInRange = "SelectionOutlineInrange";
private static readonly ProtoId<ShaderPrototype> ShaderDropTargetOutOfRange = "SelectionOutline";
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
@@ -54,12 +58,6 @@ public sealed class DragDropSystem : SharedDragDropSystem
// mousedown event so it can be treated like a regular click
private const float MaxMouseDownTimeForReplayingClick = 0.85f;
[ValidatePrototypeId<ShaderPrototype>]
private const string ShaderDropTargetInRange = "SelectionOutlineInrange";
[ValidatePrototypeId<ShaderPrototype>]
private const string ShaderDropTargetOutOfRange = "SelectionOutline";
/// <summary>
/// Current entity being dragged around.
/// </summary>
@@ -113,8 +111,8 @@ public sealed class DragDropSystem : SharedDragDropSystem
Subs.CVar(_cfgMan, CCVars.DragDropDeadZone, SetDeadZone, true);
_dropTargetInRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetInRange).Instance();
_dropTargetOutOfRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetOutOfRange).Instance();
_dropTargetInRangeShader = _prototypeManager.Index(ShaderDropTargetInRange).Instance();
_dropTargetOutOfRangeShader = _prototypeManager.Index(ShaderDropTargetOutOfRange).Instance();
// needs to fire on mouseup and mousedown so we can detect a drag / drop
CommandBinds.Builder
.BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false, true), new[] { typeof(SharedInteractionSystem) })

View File

@@ -15,6 +15,10 @@ namespace Content.Client.Light;
/// </summary>
public sealed class AmbientOcclusionOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
private static readonly ProtoId<ShaderPrototype> StencilMaskShader = "StencilMask";
private static readonly ProtoId<ShaderPrototype> StencilEqualDrawShader = "StencilEqualDraw";
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
@@ -87,7 +91,7 @@ public sealed class AmbientOcclusionOverlay : Overlay
args.WorldHandle.RenderInRenderTarget(_aoTarget,
() =>
{
worldHandle.UseShader(_proto.Index<ShaderPrototype>("unshaded").Instance());
worldHandle.UseShader(_proto.Index(UnshadedShader).Instance());
var invMatrix = _aoTarget.GetWorldToLocalMatrix(viewport.Eye!, scale);
foreach (var entry in query.QueryAabb(mapId, worldBounds))
@@ -110,7 +114,7 @@ public sealed class AmbientOcclusionOverlay : Overlay
() =>
{
// Don't want lighting affecting it.
worldHandle.UseShader(_proto.Index<ShaderPrototype>("unshaded").Instance());
worldHandle.UseShader(_proto.Index(UnshadedShader).Instance());
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
{
@@ -131,11 +135,11 @@ public sealed class AmbientOcclusionOverlay : Overlay
}, Color.Transparent);
// Draw the stencil texture to depth buffer.
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.UseShader(_proto.Index(StencilMaskShader).Instance());
worldHandle.DrawTextureRect(_aoStencilTarget!.Texture, worldBounds);
// Draw the Blurred AO texture finally.
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilEqualDraw").Instance());
worldHandle.UseShader(_proto.Index(StencilEqualDrawShader).Instance());
worldHandle.DrawTextureRect(_aoTarget!.Texture, worldBounds, color);
args.WorldHandle.SetTransform(Matrix3x2.Identity);

View File

@@ -11,6 +11,8 @@ namespace Content.Client.Light;
public sealed class SunShadowOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> MixShader = "Mix";
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
[Dependency] private readonly IClyde _clyde = default!;
@@ -150,7 +152,7 @@ public sealed class SunShadowOverlay : Overlay
viewport.LightRenderTarget.GetWorldToLocalMatrix(eye, scale);
worldHandle.SetTransform(invMatrix);
var maskShader = _protoManager.Index<ShaderPrototype>("Mix").Instance();
var maskShader = _protoManager.Index(MixShader).Instance();
worldHandle.UseShader(maskShader);
worldHandle.DrawTextureRect(_target.Texture, worldBounds, Color.Black.WithAlpha(alpha));

View File

@@ -103,8 +103,7 @@ namespace Content.Client.Lobby.UI
private bool _isDirty;
[ValidatePrototypeId<GuideEntryPrototype>]
private const string DefaultSpeciesGuidebook = "Species";
private static readonly ProtoId<GuideEntryPrototype> DefaultSpeciesGuidebook = "Species";
public event Action<List<ProtoId<GuideEntryPrototype>>>? OnOpenGuidebook;
@@ -240,6 +239,7 @@ namespace Content.Client.Lobby.UI
};
RgbSkinColorContainer.AddChild(_rgbSkinColorSelector = new ColorSelectorSliders());
_rgbSkinColorSelector.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV
_rgbSkinColorSelector.OnColorChanged += _ =>
{
OnSkinColorOnValueChanged();
@@ -808,9 +808,9 @@ namespace Content.Client.Lobby.UI
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
var page = DefaultSpeciesGuidebook;
if (_prototypeManager.HasIndex<GuideEntryPrototype>(species))
page = species;
page = new ProtoId<GuideEntryPrototype>(species.Id); // Gross. See above todo comment.
if (_prototypeManager.TryIndex<GuideEntryPrototype>(DefaultSpeciesGuidebook, out var guideRoot))
if (_prototypeManager.TryIndex(DefaultSpeciesGuidebook, out var guideRoot))
{
var dict = new Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry>();
dict.Add(DefaultSpeciesGuidebook, guideRoot);
@@ -1433,17 +1433,13 @@ namespace Content.Client.Lobby.UI
{
return;
}
var hairMarking = Profile.Appearance.HairStyleId switch
{
HairStyles.DefaultHairStyle => new List<Marking>(),
_ => new() { new(Profile.Appearance.HairStyleId, new List<Color>() { Profile.Appearance.HairColor }) },
};
var hairMarking = Profile.Appearance.HairStyleId == HairStyles.DefaultHairStyle
? new List<Marking>()
: new() { new(Profile.Appearance.HairStyleId, new List<Color>() { Profile.Appearance.HairColor }) };
var facialHairMarking = Profile.Appearance.FacialHairStyleId switch
{
HairStyles.DefaultFacialHairStyle => new List<Marking>(),
_ => new() { new(Profile.Appearance.FacialHairStyleId, new List<Color>() { Profile.Appearance.FacialHairColor }) },
};
var facialHairMarking = Profile.Appearance.FacialHairStyleId == HairStyles.DefaultFacialHairStyle
? new List<Marking>()
: new() { new(Profile.Appearance.FacialHairStyleId, new List<Color>() { Profile.Appearance.FacialHairColor }) };
HairStylePicker.UpdateData(
hairMarking,

View File

@@ -11,6 +11,8 @@ namespace Content.Client.Mapping;
public sealed class MappingOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _prototypes = default!;
@@ -35,7 +37,7 @@ public sealed class MappingOverlay : Overlay
_sprite = _entities.System<SpriteSystem>();
_state = state;
_shader = _prototypes.Index<ShaderPrototype>("unshaded").Instance();
_shader = _prototypes.Index(UnshadedShader).Instance();
}
protected override void Draw(in OverlayDrawArgs args)

View File

@@ -8,6 +8,8 @@ namespace Content.Client.Movement.Systems;
public sealed class FloorOcclusionSystem : SharedFloorOcclusionSystem
{
private static readonly ProtoId<ShaderPrototype> HorizontalCut = "HorizontalCut";
[Dependency] private readonly IPrototypeManager _proto = default!;
private EntityQuery<SpriteComponent> _spriteQuery;
@@ -48,7 +50,7 @@ public sealed class FloorOcclusionSystem : SharedFloorOcclusionSystem
if (!_spriteQuery.Resolve(sprite.Owner, ref sprite.Comp, false))
return;
var shader = _proto.Index<ShaderPrototype>("HorizontalCut").Instance();
var shader = _proto.Index(HorizontalCut).Instance();
if (sprite.Comp.PostShader is not null && sprite.Comp.PostShader != shader)
return;

View File

@@ -23,8 +23,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[ValidatePrototypeId<EntityPrototype>]
private const string Action = "ActionClearNetworkLinkOverlays";
private static readonly EntProtoId Action = "ActionClearNetworkLinkOverlays";
public override void Initialize()
{

View File

@@ -6,13 +6,16 @@
Resizable="False">
<BoxContainer Orientation="Vertical" SeparationOverride="4" MinWidth="150">
<changelog:ChangelogButton Access="Public" Name="ChangelogButton"/>
<ui:VoteCallMenuButton />
<Button Access="Public" Name="OptionsButton" Text="{Loc 'ui-escape-options'}" />
<PanelContainer StyleClasses="LowDivider" Margin="0 2.5 0 2.5" />
<Button Access="Public" Name="RulesButton" Text="{Loc 'ui-escape-rules'}" />
<Button Access="Public" Name="GuidebookButton" Text="{Loc 'ui-escape-guidebook'}" />
<Button Access="Public" Name="WikiButton" Text="{Loc 'ui-escape-wiki'}" />
<changelog:ChangelogButton Access="Public" Name="ChangelogButton" />
<PanelContainer StyleClasses="LowDivider" Margin="0 2.5 0 2.5" />
<Button Access="Public" Name="OptionsButton" Text="{Loc 'ui-escape-options'}" />
<PanelContainer StyleClasses="LowDivider" Margin="0 2.5 0 2.5" />
<Button Access="Public" Name="DisconnectButton" Text="{Loc 'ui-escape-disconnect'}" />
<Button Access="Public" Name="QuitButton" Text="{Loc 'ui-escape-quit'}" />
<Button Access="Public" Name="QuitButton" Text="{Loc 'ui-escape-quit'}" StyleClasses="ButtonColorRed" />
</BoxContainer>
</ui1:EscapeMenu>

View File

@@ -15,6 +15,9 @@ namespace Content.Client.Outline;
/// </summary>
public sealed class TargetOutlineSystem : EntitySystem
{
private static readonly ProtoId<ShaderPrototype> ShaderTargetValid = "SelectionOutlineInrange";
private static readonly ProtoId<ShaderPrototype> ShaderTargetInvalid = "SelectionOutline";
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
@@ -70,12 +73,6 @@ public sealed class TargetOutlineSystem : EntitySystem
private Vector2 LookupVector => new(LookupSize, LookupSize);
[ValidatePrototypeId<ShaderPrototype>]
private const string ShaderTargetValid = "SelectionOutlineInrange";
[ValidatePrototypeId<ShaderPrototype>]
private const string ShaderTargetInvalid = "SelectionOutline";
private ShaderInstance? _shaderTargetValid;
private ShaderInstance? _shaderTargetInvalid;
@@ -85,8 +82,8 @@ public sealed class TargetOutlineSystem : EntitySystem
{
base.Initialize();
_shaderTargetValid = _prototypeManager.Index<ShaderPrototype>(ShaderTargetValid).InstanceUnique();
_shaderTargetInvalid = _prototypeManager.Index<ShaderPrototype>(ShaderTargetInvalid).InstanceUnique();
_shaderTargetValid = _prototypeManager.Index(ShaderTargetValid).InstanceUnique();
_shaderTargetInvalid = _prototypeManager.Index(ShaderTargetInvalid).InstanceUnique();
}
public void Disable()

View File

@@ -6,6 +6,8 @@ namespace Content.Client.Overlays;
public sealed partial class BlackAndWhiteOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> Shader = "GreyscaleFullscreen";
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
@@ -15,7 +17,7 @@ public sealed partial class BlackAndWhiteOverlay : Overlay
public BlackAndWhiteOverlay()
{
IoCManager.InjectDependencies(this);
_greyscaleShader = _prototypeManager.Index<ShaderPrototype>("GreyscaleFullscreen").InstanceUnique();
_greyscaleShader = _prototypeManager.Index(Shader).InstanceUnique();
ZIndex = 10; // draw this over the DamageOverlay, RainbowOverlay etc.
}

View File

@@ -6,6 +6,8 @@ namespace Content.Client.Overlays;
public sealed partial class NoirOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> Shader = "Noir";
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
@@ -15,7 +17,7 @@ public sealed partial class NoirOverlay : Overlay
public NoirOverlay()
{
IoCManager.InjectDependencies(this);
_noirShader = _prototypeManager.Index<ShaderPrototype>("Noir").InstanceUnique();
_noirShader = _prototypeManager.Index(Shader).InstanceUnique();
ZIndex = 9; // draw this over the DamageOverlay, RainbowOverlay etc, but before the black and white shader
}

View File

@@ -13,8 +13,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
[ValidatePrototypeId<JobIconPrototype>]
private const string JobIconForNoId = "JobIconNoId";
private static readonly ProtoId<JobIconPrototype> JobIconForNoId = "JobIconNoId";
public override void Initialize()
{
@@ -52,7 +51,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
}
}
if (_prototype.TryIndex<JobIconPrototype>(iconId, out var iconPrototype))
if (_prototype.TryIndex(iconId, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype);
else
Log.Error($"Invalid job icon prototype: {iconPrototype}");

View File

@@ -45,13 +45,13 @@ public sealed partial class StencilOverlay
}, Color.Transparent);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.UseShader(_protoManager.Index(StencilMask).Instance());
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
var curTime = _timing.RealTime;
var sprite = _sprite.GetFrame(new SpriteSpecifier.Texture(new ResPath("/Textures/Parallaxes/noise.png")), curTime);
// Draw the rain
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
worldHandle.UseShader(_protoManager.Index(StencilDraw).Instance());
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, new Vector2(0.5f, 0f));
}
}

View File

@@ -55,13 +55,13 @@ public sealed partial class StencilOverlay
}, Color.Transparent);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.UseShader(_protoManager.Index(StencilMask).Instance());
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
var curTime = _timing.RealTime;
var sprite = _sprite.GetFrame(weatherProto.Sprite, curTime);
// Draw the rain
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
worldHandle.UseShader(_protoManager.Index(StencilDraw).Instance());
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, Vector2.Zero, modulate: (weatherProto.Color ?? Color.White).WithAlpha(alpha));
worldHandle.SetTransform(Matrix3x2.Identity);

View File

@@ -17,6 +17,10 @@ namespace Content.Client.Overlays;
/// </summary>
public sealed partial class StencilOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> CircleShader = "WorldGradientCircle";
private static readonly ProtoId<ShaderPrototype> StencilMask = "StencilMask";
private static readonly ProtoId<ShaderPrototype> StencilDraw = "StencilDraw";
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
@@ -43,7 +47,7 @@ public sealed partial class StencilOverlay : Overlay
_sprite = sprite;
_weather = weather;
IoCManager.InjectDependencies(this);
_shader = _protoManager.Index<ShaderPrototype>("WorldGradientCircle").InstanceUnique();
_shader = _protoManager.Index(CircleShader).InstanceUnique();
}
protected override void Draw(in OverlayDrawArgs args)

View File

@@ -10,6 +10,8 @@ namespace Content.Client.Paper.UI;
[GenerateTypedNameReferences]
public sealed partial class StampLabel : Label
{
private static readonly ProtoId<ShaderPrototype> PaperStamp = "PaperStamp";
/// A scale that's applied to the text to ensure it
/// fits in the allowed space.
private Vector2 _textScaling = Vector2.One;
@@ -26,7 +28,7 @@ public sealed partial class StampLabel : Label
RobustXamlLoader.Load(this);
var prototypes = IoCManager.Resolve<IPrototypeManager>();
_stampShader = prototypes.Index<ShaderPrototype>("PaperStamp").InstanceUnique();
_stampShader = prototypes.Index(PaperStamp).InstanceUnique();
}
protected override Vector2 MeasureOverride(Vector2 availableSize)

View File

@@ -12,6 +12,8 @@ namespace Content.Client.Paper.UI;
[GenerateTypedNameReferences]
public sealed partial class StampWidget : PanelContainer
{
private static readonly ProtoId<ShaderPrototype> PaperStamp = "PaperStamp";
private StyleBoxTexture _borderTexture;
private ShaderInstance? _stampShader;
@@ -42,7 +44,7 @@ public sealed partial class StampWidget : PanelContainer
PanelOverride = _borderTexture;
var prototypes = IoCManager.Resolve<IPrototypeManager>();
_stampShader = prototypes.Index<ShaderPrototype>("PaperStamp").InstanceUnique();
_stampShader = prototypes.Index(PaperStamp).InstanceUnique();
}
protected override void Draw(DrawingHandleScreen handle)

View File

@@ -14,8 +14,7 @@ public sealed class ParallaxSystem : SharedParallaxSystem
[Dependency] private readonly IParallaxManager _parallax = default!;
[Dependency] private readonly SharedMapSystem _map = default!;
[ValidatePrototypeId<ParallaxPrototype>]
private const string Fallback = "Default";
private static readonly ProtoId<ParallaxPrototype> Fallback = "Default";
public const int ParallaxZIndex = 0;

View File

@@ -262,6 +262,7 @@ public sealed partial class ParticleAcceleratorControlMenu : FancyWindow
public sealed class PASegmentControl : Control
{
private static readonly ProtoId<ShaderPrototype> GreyscaleShaderId = "Greyscale";
private readonly ShaderInstance _greyScaleShader;
private readonly TextureRect _base;
private readonly TextureRect _unlit;
@@ -272,7 +273,7 @@ public sealed class PASegmentControl : Control
public PASegmentControl()
{
_greyScaleShader = IoCManager.Resolve<IPrototypeManager>().Index<ShaderPrototype>("Greyscale").Instance();
_greyScaleShader = IoCManager.Resolve<IPrototypeManager>().Index(GreyscaleShaderId).Instance();
AddChild(_base = new TextureRect());
AddChild(_unlit = new TextureRect());

View File

@@ -16,6 +16,8 @@ namespace Content.Client.Popups;
/// </summary>
public sealed class PopupOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
private readonly IConfigurationManager _configManager;
private readonly IEntityManager _entManager;
private readonly IPlayerManager _playerMgr;
@@ -48,7 +50,7 @@ public sealed class PopupOverlay : Overlay
_popup = popup;
_controller = controller;
_shader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
_shader = protoManager.Index(UnshadedShader).Instance();
}
protected override void Draw(in OverlayDrawArgs args)

View File

@@ -15,8 +15,7 @@ namespace Content.Client.Power.Generation.Teg;
/// <seealso cref="TegCirculatorComponent"/>
public sealed class TegSystem : EntitySystem
{
[ValidatePrototypeId<EntityPrototype>]
private const string ArrowPrototype = "TegCirculatorArrow";
private static readonly EntProtoId ArrowPrototype = "TegCirculatorArrow";
public override void Initialize()
{

View File

@@ -48,8 +48,9 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
if (!_sprite.LayerExists((uid, args.Sprite), PowerCellVisualLayers.Unshaded))
return;
// If no appearance data is set, rely on whatever existing sprite state is set being correct.
if (!_appearance.TryGetData<byte>(uid, PowerCellVisuals.ChargeLevel, out var level, args.Component))
level = 0;
return;
var positiveCharge = level > 0;
_sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, positiveCharge);

View File

@@ -13,6 +13,8 @@ namespace Content.Client.Radiation.Overlays
{
public sealed class RadiationPulseOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> RadiationShader = "Radiation";
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -29,7 +31,7 @@ namespace Content.Client.Radiation.Overlays
public RadiationPulseOverlay()
{
IoCManager.InjectDependencies(this);
_baseShader = _prototypeManager.Index<ShaderPrototype>("Radiation").Instance().Duplicate();
_baseShader = _prototypeManager.Index(RadiationShader).Instance().Duplicate();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)

View File

@@ -14,6 +14,8 @@ namespace Content.Client.Shuttles;
/// </summary>
public sealed class FtlArrivalOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
private EntityLookupSystem _lookups;
@@ -36,7 +38,7 @@ public sealed class FtlArrivalOverlay : Overlay
_maps = _entManager.System<SharedMapSystem>();
_sprites = _entManager.System<SpriteSystem>();
_shader = _protos.Index<ShaderPrototype>("unshaded").Instance();
_shader = _protos.Index(UnshadedShader).Instance();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)

View File

@@ -25,7 +25,6 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
public event Action<ProtoId<BorgTypePrototype>>? ConfirmedBorgType;
[ValidatePrototypeId<GuideEntryPrototype>]
private static readonly List<ProtoId<GuideEntryPrototype>> GuidebookEntries = new() { "Cyborgs", "Robotics" };
public BorgSelectTypeMenu()

View File

@@ -84,12 +84,13 @@ public sealed partial class LawDisplay : Control
radioChannelButton.OnPressed += _ =>
{
switch (radioChannel)
if (radioChannel == SharedChatSystem.CommonChannel)
{
case SharedChatSystem.CommonChannel:
_chatManager.SendMessage($"{SharedChatSystem.RadioCommonPrefix} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio); break;
default:
_chatManager.SendMessage($"{SharedChatSystem.RadioChannelPrefix}{radioChannelProto.KeyCode} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio); break;
_chatManager.SendMessage($"{SharedChatSystem.RadioCommonPrefix} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio);
}
else
{
_chatManager.SendMessage($"{SharedChatSystem.RadioChannelPrefix}{radioChannelProto.KeyCode} {lawIdentifierPlaintext}: {lawDescriptionPlaintext}", ChatSelectChannel.Radio);
}
_nextAllowedPress[radioChannelButton] = _timing.CurTime + PressCooldown;
};

View File

@@ -12,6 +12,10 @@ namespace Content.Client.Silicons.StationAi;
public sealed class StationAiOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> CameraStaticShader = "CameraStatic";
private static readonly ProtoId<ShaderPrototype> StencilMaskShader = "StencilMask";
private static readonly ProtoId<ShaderPrototype> StencilDrawShader = "StencilDraw";
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
@@ -91,7 +95,7 @@ public sealed class StationAiOverlay : Overlay
() =>
{
worldHandle.SetTransform(invMatrix);
var shader = _proto.Index<ShaderPrototype>("CameraStatic").Instance();
var shader = _proto.Index(CameraStaticShader).Instance();
worldHandle.UseShader(shader);
worldHandle.DrawRect(worldBounds, Color.White);
},
@@ -114,11 +118,11 @@ public sealed class StationAiOverlay : Overlay
}
// Use the lighting as a mask
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.UseShader(_proto.Index(StencilMaskShader).Instance());
worldHandle.DrawTextureRect(_stencilTexture!.Texture, worldBounds);
// Draw the static
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilDraw").Instance());
worldHandle.UseShader(_proto.Index(StencilDrawShader).Instance());
worldHandle.DrawTextureRect(_staticTexture!.Texture, worldBounds);
worldHandle.SetTransform(Matrix3x2.Identity);

View File

@@ -9,6 +9,8 @@ namespace Content.Client.Singularity
{
public sealed class SingularityOverlay : Overlay, IEntityEventSubscriber
{
private static readonly ProtoId<ShaderPrototype> Shader = "Singularity";
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private SharedTransformSystem? _xformSystem = null;
@@ -29,7 +31,7 @@ namespace Content.Client.Singularity
public SingularityOverlay()
{
IoCManager.InjectDependencies(this);
_shader = _prototypeManager.Index<ShaderPrototype>("Singularity").Instance().Duplicate();
_shader = _prototypeManager.Index(Shader).Instance().Duplicate();
_shader.SetParameter("maxDistance", MaxDistance * EyeManager.PixelsPerMeter);
_entMan.EventBus.SubscribeEvent<PixelToMapEvent>(EventSource.Local, this, OnProjectFromScreenToMap);
ZIndex = 101; // Should be drawn after the placement overlay so admins placing items near the singularity can tell where they're going.

View File

@@ -11,6 +11,8 @@ namespace Content.Client.StatusIcon;
public sealed class StatusIconOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IGameTiming _timing = default!;
@@ -29,7 +31,7 @@ public sealed class StatusIconOverlay : Overlay
_sprite = _entity.System<SpriteSystem>();
_transform = _entity.System<TransformSystem>();
_statusIcon = _entity.System<StatusIconSystem>();
_unshadedShader = _prototype.Index<ShaderPrototype>("unshaded").Instance();
_unshadedShader = _prototype.Index(UnshadedShader).Instance();
}
protected override void Draw(in OverlayDrawArgs args)

View File

@@ -10,6 +10,8 @@ namespace Content.Client.Stealth;
public sealed class StealthSystem : SharedStealthSystem
{
private static readonly ProtoId<ShaderPrototype> Shader = "Stealth";
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
@@ -20,7 +22,7 @@ public sealed class StealthSystem : SharedStealthSystem
{
base.Initialize();
_shader = _protoMan.Index<ShaderPrototype>("Stealth").InstanceUnique();
_shader = _protoMan.Index(Shader).InstanceUnique();
SubscribeLocalEvent<StealthComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<StealthComponent, ComponentStartup>(OnStartup);

View File

@@ -1,24 +1,28 @@
<Control xmlns="https://spacestation14.io">
<BoxContainer Margin="8,8,8,8" Orientation="Vertical">
<BoxContainer Margin="8,0,8,8" Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Label Name="StoreItemName" HorizontalExpand="True" />
<Label Name="DiscountSubText"
HorizontalAlignment="Right"/>
<Button
Name="StoreItemBuyButton"
MinWidth="64"
HorizontalAlignment="Right"
Access="Public" />
</BoxContainer>
<PanelContainer StyleClasses="HighDivider" />
<PanelContainer StyleClasses="HighDivider" Margin="0,1,0,2"/>
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
<TextureRect
Name="StoreItemTexture"
Margin="0,0,4,0"
MinSize="48 48"
Stretch="KeepAspectCentered" />
<Control MinWidth="5"/>
<RichTextLabel Name="StoreItemDescription" />
<BoxContainer Orientation="Vertical" VerticalAlignment="Center" Margin="0,0,4,0">
<TextureRect
Name="StoreItemTexture"
Margin="0,0,4,0"
MinSize="48 48"
Stretch="KeepAspectCentered" />
<Button
Name="StoreItemBuyButton"
MinWidth="48"
MaxHeight="48"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Margin="0,4,0,0"
Access="Public" />
<Label Name="DiscountSubText"
HorizontalAlignment="Center"/>
</BoxContainer>
<RichTextLabel Name="StoreItemDescription" VerticalAlignment="Top"/>
</BoxContainer>
</BoxContainer>
</Control>

View File

@@ -17,6 +17,8 @@ namespace Content.Client.SurveillanceCamera.UI;
[GenerateTypedNameReferences]
public sealed partial class SurveillanceCameraMonitorWindow : DefaultWindow
{
private static readonly ProtoId<ShaderPrototype> CameraStaticShader = "CameraStatic";
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
@@ -53,7 +55,7 @@ public sealed partial class SurveillanceCameraMonitorWindow : DefaultWindow
// This could be done better. I don't want to deal with stylesheets at the moment.
var texture = _resourceCache.GetTexture("/Textures/Interface/Nano/square_black.png");
var shader = _prototypeManager.Index<ShaderPrototype>("CameraStatic").Instance().Duplicate();
var shader = _prototypeManager.Index(CameraStaticShader).Instance().Duplicate();
CameraView.ViewportSize = new Vector2i(500, 500);
CameraView.Eye = _defaultEye; // sure

View File

@@ -12,7 +12,7 @@ namespace Content.Client.UserInterface.RichText;
/// </summary>
public sealed class MonoTag : IMarkupTag
{
[ValidatePrototypeId<FontPrototype>] public const string MonoFont = "Monospace";
public static readonly ProtoId<FontPrototype> MonoFont = "Monospace";
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;

View File

@@ -67,8 +67,7 @@ public sealed partial class ChatUIController : UIController
[UISystemDependency] private readonly MindSystem? _mindSystem = default!;
[UISystemDependency] private readonly RoleCodewordSystem? _roleCodewordSystem = default!;
[ValidatePrototypeId<ColorPalettePrototype>]
private const string ChatNamePalette = "ChatNames";
private static readonly ProtoId<ColorPalettePrototype> ChatNamePalette = "ChatNames";
private string[] _chatNameColors = default!;
private bool _chatNameColorsEnabled;
@@ -232,7 +231,7 @@ public sealed partial class ChatUIController : UIController
gameplayStateLoad.OnScreenLoad += OnScreenLoad;
gameplayStateLoad.OnScreenUnload += OnScreenUnload;
var nameColors = _prototypeManager.Index<ColorPalettePrototype>(ChatNamePalette).Colors.Values.ToArray();
var nameColors = _prototypeManager.Index(ChatNamePalette).Colors.Values.ToArray();
_chatNameColors = new string[nameColors.Length];
for (var i = 0; i < nameColors.Length; i++)
{

View File

@@ -9,6 +9,8 @@ namespace Content.Client.UserInterface.Systems.DamageOverlays.Overlays;
public sealed class DamageOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> CircleMaskShader = "GradientCircleMask";
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
@@ -49,9 +51,9 @@ public sealed class DamageOverlay : Overlay
{
// TODO: Replace
IoCManager.InjectDependencies(this);
_oxygenShader = _prototypeManager.Index<ShaderPrototype>("GradientCircleMask").InstanceUnique();
_critShader = _prototypeManager.Index<ShaderPrototype>("GradientCircleMask").InstanceUnique();
_bruteShader = _prototypeManager.Index<ShaderPrototype>("GradientCircleMask").InstanceUnique();
_oxygenShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
_critShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
_bruteShader = _prototypeManager.Index(CircleMaskShader).InstanceUnique();
}
protected override void Draw(in OverlayDrawArgs args)

View File

@@ -19,8 +19,7 @@ public sealed class InfoUIController : UIController, IOnStateExited<GameplayStat
private RulesPopup? _rulesPopup;
private RulesAndInfoWindow? _infoWindow;
[ValidatePrototypeId<GuideEntryPrototype>]
private const string DefaultRuleset = "DefaultRuleset";
private static readonly ProtoId<GuideEntryPrototype> DefaultRuleset = "DefaultRuleset";
public ProtoId<GuideEntryPrototype> RulesEntryId = DefaultRuleset;
@@ -92,7 +91,7 @@ public sealed class InfoUIController : UIController, IOnStateExited<GameplayStat
{
if (!_prototype.TryIndex(RulesEntryId, out var guideEntryPrototype))
{
guideEntryPrototype = _prototype.Index<GuideEntryPrototype>(DefaultRuleset);
guideEntryPrototype = _prototype.Index(DefaultRuleset);
Log.Error($"Couldn't find the following prototype: {RulesEntryId}. Falling back to {DefaultRuleset}, please check that the server has the rules set up correctly");
return guideEntryPrototype;
}

View File

@@ -39,8 +39,7 @@ public sealed partial class GunSystem : SharedGunSystem
[Dependency] private readonly SharedTransformSystem _xform = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[ValidatePrototypeId<EntityPrototype>]
public const string HitscanProto = "HitscanEffect";
public static readonly EntProtoId HitscanProto = "HitscanEffect";
public bool SpreadOverlay
{

View File

@@ -7,10 +7,12 @@ namespace Content.IntegrationTests.Tests.Atmos
[TestOf(typeof(AtmosAlarmThreshold))]
public sealed class AlarmThresholdTest
{
private const string AlarmThresholdTestDummyId = "AlarmThresholdTestDummy";
[TestPrototypes]
private const string Prototypes = @"
private const string Prototypes = $@"
- type: alarmThreshold
id: AlarmThresholdTestDummy
id: {AlarmThresholdTestDummyId}
upperBound: !type:AlarmThresholdSetting
threshold: 5
lowerBound: !type:AlarmThresholdSetting
@@ -30,7 +32,7 @@ namespace Content.IntegrationTests.Tests.Atmos
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
AtmosAlarmThreshold threshold = default!;
var proto = prototypeManager.Index<AtmosAlarmThresholdPrototype>("AlarmThresholdTestDummy");
var proto = prototypeManager.Index<AtmosAlarmThresholdPrototype>(AlarmThresholdTestDummyId);
threshold = new(proto);
await server.WaitAssertion(() =>

View File

@@ -0,0 +1,46 @@
using Content.Shared.Cloning;
namespace Content.IntegrationTests.Tests.Cloning;
public sealed class CloningSettingsPrototypeTest
{
/// <summary>
/// Checks that the components named in every <see cref="CloningSettingsPrototype"/> are valid components known to the server.
/// This is used instead of <see cref="ComponentNameSerializer"/> because we only care if the components are registered with the server,
/// and instead of a <see cref="ComponentRegistry"/> because we only need component names.
/// </summary>
[Test]
public async Task ValidatePrototypes()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var protoMan = server.ProtoMan;
var compFactory = server.EntMan.ComponentFactory;
await server.WaitAssertion(() =>
{
Assert.Multiple(() =>
{
var protos = protoMan.EnumeratePrototypes<CloningSettingsPrototype>();
foreach (var proto in protos)
{
foreach (var compName in proto.Components)
{
Assert.That(compFactory.TryGetRegistration(compName, out _),
$"Failed to find a component named {compName} for {nameof(CloningSettingsPrototype)} \"{proto.ID}\""
);
}
foreach (var eventCompName in proto.EventComponents)
{
Assert.That(compFactory.TryGetRegistration(eventCompName, out _),
$"Failed to find a component named {eventCompName} for {nameof(CloningSettingsPrototype)} \"{proto.ID}\""
);
}
}
});
});
await pair.CleanReturnAsync();
}
}

View File

@@ -0,0 +1,72 @@
#nullable enable
using System.Linq;
using Content.Server.Objectives;
using Content.Shared.Mind;
using Robust.Shared.GameObjects;
using Robust.Shared.Player;
namespace Content.IntegrationTests.Tests.Commands;
public sealed class ObjectiveCommandsTest
{
private const string ObjectiveProtoId = "MindCommandsTestObjective";
private const string DummyUsername = "MindCommandsTestUser";
[TestPrototypes]
private const string Prototypes = $"""
- type: entity
id: {ObjectiveProtoId}
components:
- type: Objective
difficulty: 1
issuer: objective-issuer-syndicate
icon:
sprite: error.rsi
state: error
- type: DieCondition
""";
/// <summary>
/// Creates a dummy session, and assigns it a mind, then
/// tests using <c>addobjective</c>, <c>lsobjectives</c>,
/// and <c>rmobjective</c> on it.
/// </summary>
[Test]
public async Task AddListRemoveObjectiveTest()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var entMan = server.EntMan;
var playerMan = server.ResolveDependency<ISharedPlayerManager>();
var mindSys = server.System<SharedMindSystem>();
var objectivesSystem = server.System<ObjectivesSystem>();
await server.AddDummySession(DummyUsername);
await server.WaitRunTicks(5);
var playerSession = playerMan.Sessions.Single();
Entity<MindComponent>? mindEnt = null;
await server.WaitPost(() =>
{
mindEnt = mindSys.CreateMind(playerSession.UserId);
});
Assert.That(mindEnt, Is.Not.Null);
var mindComp = mindEnt.Value.Comp;
Assert.That(mindComp.Objectives, Is.Empty, "Dummy player started with objectives.");
await pair.WaitCommand($"addobjective {playerSession.Name} {ObjectiveProtoId}");
Assert.That(mindComp.Objectives, Has.Count.EqualTo(1), "addobjective failed to increase Objectives count.");
await pair.WaitCommand($"lsobjectives {playerSession.Name}");
await pair.WaitCommand($"rmobjective {playerSession.Name} 0");
Assert.That(mindComp.Objectives, Is.Empty, "rmobjective failed to remove objective");
await pair.CleanReturnAsync();
}
}

View File

@@ -15,6 +15,8 @@ namespace Content.IntegrationTests.Tests.Commands
[TestOf(typeof(RejuvenateSystem))]
public sealed class RejuvenateTest
{
private static readonly ProtoId<DamageGroupPrototype> TestDamageGroup = "Toxin";
[TestPrototypes]
private const string Prototypes = @"
- type: entity
@@ -62,7 +64,7 @@ namespace Content.IntegrationTests.Tests.Commands
});
// Kill the entity
DamageSpecifier damage = new(prototypeManager.Index<DamageGroupPrototype>("Toxin"), FixedPoint2.New(10000000));
DamageSpecifier damage = new(prototypeManager.Index(TestDamageGroup), FixedPoint2.New(10000000));
damSystem.TryChangeDamage(human, damage, true);

View File

@@ -53,6 +53,8 @@ public sealed class SuicideCommandTests
components:
- type: MaterialReclaimer";
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
private static readonly ProtoId<DamageTypePrototype> DamageType = "Slash";
/// <summary>
/// Run the suicide command in the console
/// Should successfully kill the player and ghost them
@@ -144,7 +146,7 @@ public sealed class SuicideCommandTests
mobThresholdsComp = entManager.GetComponent<MobThresholdsComponent>(player);
damageableComp = entManager.GetComponent<DamageableComponent>(player);
if (protoMan.TryIndex<DamageTypePrototype>("Slash", out var slashProto))
if (protoMan.TryIndex(DamageType, out var slashProto))
damageableSystem.TryChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5)));
});

View File

@@ -8,6 +8,8 @@ namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class WindowRepair : InteractionTest
{
private static readonly ProtoId<DamageTypePrototype> BluntDamageType = "Blunt";
[Test]
public async Task RepairReinforcedWindow()
{
@@ -16,7 +18,7 @@ public sealed class WindowRepair : InteractionTest
// Damage the entity.
var sys = SEntMan.System<DamageableSystem>();
var comp = Comp<DamageableComponent>();
var damageType = Server.ResolveDependency<IPrototypeManager>().Index<DamageTypePrototype>("Blunt");
var damageType = Server.ProtoMan.Index(BluntDamageType);
var damage = new DamageSpecifier(damageType, FixedPoint2.New(10));
Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero));
await Server.WaitPost(() => sys.TryChangeDamage(SEntMan.GetEntity(Target), damage, ignoreResistances: true));

View File

@@ -1,9 +1,7 @@
using System.Linq;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
@@ -14,66 +12,79 @@ namespace Content.IntegrationTests.Tests.Damageable
[TestOf(typeof(DamageableSystem))]
public sealed class DamageableTest
{
private const string TestDamageableEntityId = "TestDamageableEntityId";
private const string TestGroup1 = "TestGroup1";
private const string TestGroup2 = "TestGroup2";
private const string TestGroup3 = "TestGroup3";
private const string TestDamage1 = "TestDamage1";
private const string TestDamage2a = "TestDamage2a";
private const string TestDamage2b = "TestDamage2b";
private const string TestDamage3a = "TestDamage3a";
private const string TestDamage3b = "TestDamage3b";
private const string TestDamage3c = "TestDamage3c";
[TestPrototypes]
private const string Prototypes = @"
private const string Prototypes = $@"
# Define some damage groups
- type: damageType
id: TestDamage1
id: {TestDamage1}
name: damage-type-blunt
- type: damageType
id: TestDamage2a
id: {TestDamage2a}
name: damage-type-blunt
- type: damageType
id: TestDamage2b
id: {TestDamage2b}
name: damage-type-blunt
- type: damageType
id: TestDamage3a
id: {TestDamage3a}
name: damage-type-blunt
- type: damageType
id: TestDamage3b
id: {TestDamage3b}
name: damage-type-blunt
- type: damageType
id: TestDamage3c
id: {TestDamage3c}
name: damage-type-blunt
# Define damage Groups with 1,2,3 damage types
- type: damageGroup
id: TestGroup1
id: {TestGroup1}
name: damage-group-brute
damageTypes:
- TestDamage1
- {TestDamage1}
- type: damageGroup
id: TestGroup2
id: {TestGroup2}
name: damage-group-brute
damageTypes:
- TestDamage2a
- TestDamage2b
- {TestDamage2a}
- {TestDamage2b}
- type: damageGroup
id: TestGroup3
id: {TestGroup3}
name: damage-group-brute
damageTypes:
- TestDamage3a
- TestDamage3b
- TestDamage3c
- {TestDamage3a}
- {TestDamage3b}
- {TestDamage3c}
# This container should not support TestDamage1 or TestDamage2b
- type: damageContainer
id: testDamageContainer
supportedGroups:
- TestGroup3
- {TestGroup3}
supportedTypes:
- TestDamage2a
- {TestDamage2a}
- type: entity
id: TestDamageableEntityId
name: TestDamageableEntityId
id: {TestDamageableEntityId}
name: {TestDamageableEntityId}
components:
- type: Damageable
damageContainer: testDamageContainer
@@ -113,20 +124,20 @@ namespace Content.IntegrationTests.Tests.Damageable
{
var coordinates = map.MapCoords;
sDamageableEntity = sEntityManager.SpawnEntity("TestDamageableEntityId", coordinates);
sDamageableEntity = sEntityManager.SpawnEntity(TestDamageableEntityId, coordinates);
sDamageableComponent = sEntityManager.GetComponent<DamageableComponent>(sDamageableEntity);
sDamageableSystem = sEntitySystemManager.GetEntitySystem<DamageableSystem>();
group1 = sPrototypeManager.Index<DamageGroupPrototype>("TestGroup1");
group2 = sPrototypeManager.Index<DamageGroupPrototype>("TestGroup2");
group3 = sPrototypeManager.Index<DamageGroupPrototype>("TestGroup3");
group1 = sPrototypeManager.Index<DamageGroupPrototype>(TestGroup1);
group2 = sPrototypeManager.Index<DamageGroupPrototype>(TestGroup2);
group3 = sPrototypeManager.Index<DamageGroupPrototype>(TestGroup3);
type1 = sPrototypeManager.Index<DamageTypePrototype>("TestDamage1");
type2a = sPrototypeManager.Index<DamageTypePrototype>("TestDamage2a");
type2b = sPrototypeManager.Index<DamageTypePrototype>("TestDamage2b");
type3a = sPrototypeManager.Index<DamageTypePrototype>("TestDamage3a");
type3b = sPrototypeManager.Index<DamageTypePrototype>("TestDamage3b");
type3c = sPrototypeManager.Index<DamageTypePrototype>("TestDamage3c");
type1 = sPrototypeManager.Index<DamageTypePrototype>(TestDamage1);
type2a = sPrototypeManager.Index<DamageTypePrototype>(TestDamage2a);
type2b = sPrototypeManager.Index<DamageTypePrototype>(TestDamage2b);
type3a = sPrototypeManager.Index<DamageTypePrototype>(TestDamage3a);
type3b = sPrototypeManager.Index<DamageTypePrototype>(TestDamage3b);
type3c = sPrototypeManager.Index<DamageTypePrototype>(TestDamage3c);
});
await server.WaitRunTicks(5);

View File

@@ -54,8 +54,8 @@ namespace Content.IntegrationTests.Tests.Destructible
await server.WaitAssertion(() =>
{
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
var burnDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBurn");
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>(TestBruteDamageGroupId);
var burnDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>(TestBurnDamageGroupId);
DamageSpecifier bruteDamage = new(bruteDamageGroup, FixedPoint2.New(5));
DamageSpecifier burnDamage = new(burnDamageGroup, FixedPoint2.New(5));

View File

@@ -49,8 +49,8 @@ namespace Content.IntegrationTests.Tests.Destructible
await server.WaitAssertion(() =>
{
var bluntDamageType = protoManager.Index<DamageTypePrototype>("TestBlunt");
var slashDamageType = protoManager.Index<DamageTypePrototype>("TestSlash");
var bluntDamageType = protoManager.Index<DamageTypePrototype>(TestBluntDamageTypeId);
var slashDamageType = protoManager.Index<DamageTypePrototype>(TestSlashDamageTypeId);
var bluntDamage = new DamageSpecifier(bluntDamageType, 5);
var slashDamage = new DamageSpecifier(slashDamageType, 5);

View File

@@ -39,7 +39,7 @@ namespace Content.IntegrationTests.Tests.Destructible
await server.WaitAssertion(() =>
{
var coordinates = sEntityManager.GetComponent<TransformComponent>(sDestructibleEntity).Coordinates;
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>(TestBruteDamageGroupId);
DamageSpecifier bruteDamage = new(bruteDamageGroup, 50);
#pragma warning disable NUnit2045 // Interdependent assertions.

View File

@@ -7,48 +7,56 @@ namespace Content.IntegrationTests.Tests.Destructible
public const string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity";
public const string DestructibleDamageTypeEntityId = "DestructibleTestsDestructibleDamageTypeEntity";
public const string DestructibleDamageGroupEntityId = "DestructibleTestsDestructibleDamageGroupEntity";
public const string TestBruteDamageGroupId = "TestBrute";
public const string TestBurnDamageGroupId = "TestBurn";
public const string TestBluntDamageTypeId = "TestBlunt";
public const string TestSlashDamageTypeId = "TestSlash";
public const string TestPiercingDamageTypeId = "TestPiercing";
public const string TestHeatDamageTypeId = "TestHeat";
public const string TestShockDamageTypeId = "TestShock";
public const string TestColdDamageTypeId = "TestCold";
[TestPrototypes]
public const string DamagePrototypes = $@"
- type: damageType
id: TestBlunt
id: {TestBluntDamageTypeId}
name: damage-type-blunt
- type: damageType
id: TestSlash
id: {TestSlashDamageTypeId}
name: damage-type-slash
- type: damageType
id: TestPiercing
id: {TestPiercingDamageTypeId}
name: damage-type-piercing
- type: damageType
id: TestHeat
id: {TestHeatDamageTypeId}
name: damage-type-heat
- type: damageType
id: TestShock
id: {TestShockDamageTypeId}
name: damage-type-shock
- type: damageType
id: TestCold
id: {TestColdDamageTypeId}
name: damage-type-cold
- type: damageGroup
id: TestBrute
id: {TestBruteDamageGroupId}
name: damage-group-brute
damageTypes:
- TestBlunt
- TestSlash
- TestPiercing
- {TestBluntDamageTypeId}
- {TestSlashDamageTypeId}
- {TestPiercingDamageTypeId}
- type: damageGroup
id: TestBurn
id: {TestBurnDamageGroupId}
name: damage-group-burn
damageTypes:
- TestHeat
- TestShock
- TestCold
- {TestHeatDamageTypeId}
- {TestShockDamageTypeId}
- {TestColdDamageTypeId}
- type: entity
id: {SpawnedEntityId}
@@ -114,10 +122,10 @@ namespace Content.IntegrationTests.Tests.Destructible
!type:AndTrigger
triggers:
- !type:DamageTypeTrigger
damageType: TestBlunt
damageType: {TestBluntDamageTypeId}
damage: 10
- !type:DamageTypeTrigger
damageType: TestSlash
damageType: {TestSlashDamageTypeId}
damage: 10
- type: entity
@@ -131,10 +139,10 @@ namespace Content.IntegrationTests.Tests.Destructible
!type:AndTrigger
triggers:
- !type:DamageGroupTrigger
damageGroup: TestBrute
damageGroup: {TestBruteDamageGroupId}
damage: 10
- !type:DamageGroupTrigger
damageGroup: TestBurn
damageGroup: {TestBurnDamageGroupId}
damage: 10";
}
}

View File

@@ -61,7 +61,7 @@ namespace Content.IntegrationTests.Tests.Destructible
await server.WaitAssertion(() =>
{
var bluntDamage = new DamageSpecifier(sPrototypeManager.Index<DamageTypePrototype>("TestBlunt"), 10);
var bluntDamage = new DamageSpecifier(sPrototypeManager.Index<DamageTypePrototype>(TestBluntDamageTypeId), 10);
sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true);

View File

@@ -4,7 +4,6 @@ using Content.Shared.Tag;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.IntegrationTests.Tests.Linter;
@@ -66,25 +65,25 @@ public sealed class StaticFieldValidationTest
[Reflect(false)]
private sealed class StringValid
{
[ValidatePrototypeId<TagPrototype>] public static string Tag = "StaticFieldTestTag";
public static readonly ProtoId<TagPrototype> Tag = "StaticFieldTestTag";
}
[Reflect(false)]
private sealed class StringInvalid
{
[ValidatePrototypeId<TagPrototype>] public static string Tag = string.Empty;
public static readonly ProtoId<TagPrototype> Tag = string.Empty;
}
[Reflect(false)]
private sealed class StringArrayValid
{
[ValidatePrototypeId<TagPrototype>] public static string[] Tag = ["StaticFieldTestTag", "StaticFieldTestTag"];
public static readonly ProtoId<TagPrototype>[] Tag = ["StaticFieldTestTag", "StaticFieldTestTag"];
}
[Reflect(false)]
private sealed class StringArrayInvalid
{
[ValidatePrototypeId<TagPrototype>] public static string[] Tag = [string.Empty, "StaticFieldTestTag", string.Empty];
public static readonly ProtoId<TagPrototype>[] Tag = [string.Empty, "StaticFieldTestTag", string.Empty];
}
[Reflect(false)]

View File

@@ -24,6 +24,8 @@ namespace Content.IntegrationTests.Tests.Minds;
[TestFixture]
public sealed partial class MindTests
{
private static readonly ProtoId<DamageTypePrototype> BluntDamageType = "Blunt";
[TestPrototypes]
private const string Prototypes = @"
- type: entity
@@ -144,7 +146,7 @@ public sealed partial class MindTests
await server.WaitAssertion(() =>
{
var damageable = entMan.GetComponent<DamageableComponent>(entity);
if (!protoMan.TryIndex<DamageTypePrototype>("Blunt", out var prototype))
if (!protoMan.TryIndex(BluntDamageType, out var prototype))
{
return;
}

View File

@@ -77,6 +77,8 @@ namespace Content.IntegrationTests.Tests
"Exo",
};
private static readonly ProtoId<EntityCategoryPrototype> DoNotMapCategory = "DoNotMap";
/// <summary>
/// Asserts that specific files have been saved as grids and not maps.
/// </summary>
@@ -254,7 +256,7 @@ namespace Content.IntegrationTests.Tests
return;
var yamlEntities = node["entities"];
if (!protoManager.TryIndex<EntityCategoryPrototype>("DoNotMap", out var dnmCategory))
if (!protoManager.TryIndex(DoNotMapCategory, out var dnmCategory))
return;
Assert.Multiple(() =>

View File

@@ -17,8 +17,10 @@ namespace Content.IntegrationTests.Tests.Station;
[TestOf(typeof(StationJobsSystem))]
public sealed class StationJobsTest
{
private const string StationMapId = "FooStation";
[TestPrototypes]
private const string Prototypes = @"
private const string Prototypes = $@"
- type: playTimeTracker
id: PlayTimeDummyAssistant
@@ -35,13 +37,13 @@ public sealed class StationJobsTest
id: PlayTimeDummyChaplain
- type: gameMap
id: FooStation
id: {StationMapId}
minPlayers: 0
mapName: FooStation
mapName: {StationMapId}
mapPath: /Maps/Test/empty.yml
stations:
Station:
mapNameTemplate: FooStation
mapNameTemplate: {StationMapId}
stationProto: StandardNanotrasenStation
components:
- type: StationJobs
@@ -87,7 +89,7 @@ public sealed class StationJobsTest
var server = pair.Server;
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
var fooStationProto = prototypeManager.Index<GameMapPrototype>("FooStation");
var fooStationProto = prototypeManager.Index<GameMapPrototype>(StationMapId);
var entSysMan = server.ResolveDependency<IEntityManager>().EntitySysManager;
var stationJobs = entSysMan.GetEntitySystem<StationJobsSystem>();
var stationSystem = entSysMan.GetEntitySystem<StationSystem>();
@@ -161,7 +163,7 @@ public sealed class StationJobsTest
var server = pair.Server;
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
var fooStationProto = prototypeManager.Index<GameMapPrototype>("FooStation");
var fooStationProto = prototypeManager.Index<GameMapPrototype>(StationMapId);
var entSysMan = server.ResolveDependency<IEntityManager>().EntitySysManager;
var stationJobs = entSysMan.GetEntitySystem<StationJobsSystem>();
var stationSystem = entSysMan.GetEntitySystem<StationSystem>();

View File

@@ -5,6 +5,7 @@ using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.VendingMachines;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Vending;
@@ -17,6 +18,7 @@ public sealed class VendingInteractionTest : InteractionTest
private const string RestockBoxProtoId = "InteractionTestRestockBox";
private const string RestockBoxOtherProtoId = "InteractionTestRestockBoxOther";
private static readonly ProtoId<DamageTypePrototype> TestDamageType = "Blunt";
[TestPrototypes]
private const string TestPrototypes = $@"
@@ -196,7 +198,7 @@ public sealed class VendingInteractionTest : InteractionTest
Assert.That(damageableComp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero), $"{VendingMachineProtoId} started with unexpected damage.");
// Damage the vending machine to the point that it breaks
var damageType = ProtoMan.Index<DamageTypePrototype>("Blunt");
var damageType = ProtoMan.Index(TestDamageType);
var damage = new DamageSpecifier(damageType, FixedPoint2.New(100));
await Server.WaitPost(() => damageableSys.TryChangeDamage(SEntMan.GetEntity(Target), damage, ignoreResistances: true));
await RunTicks(5);

View File

@@ -20,6 +20,8 @@ namespace Content.IntegrationTests.Tests
[TestOf(typeof(VendingMachineSystem))]
public sealed class VendingMachineRestockTest : EntitySystem
{
private static readonly ProtoId<DamageTypePrototype> TestDamageType = "Blunt";
[TestPrototypes]
private const string Prototypes = @"
- type: entity
@@ -293,7 +295,7 @@ namespace Content.IntegrationTests.Tests
"Did not start with zero ramen.");
restock = entityManager.SpawnEntity("TestRestockExplode", coordinates);
var damageSpec = new DamageSpecifier(prototypeManager.Index<DamageTypePrototype>("Blunt"), 100);
var damageSpec = new DamageSpecifier(prototypeManager.Index(TestDamageType), 100);
var damageResult = damageableSystem.TryChangeDamage(restock, damageSpec);
#pragma warning disable NUnit2045

View File

@@ -2,39 +2,36 @@ using Content.Server.Chat.Systems;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Moderator)]
public sealed class DsayCommand : LocalizedEntityCommands
{
[AdminCommand(AdminFlags.Moderator)]
sealed class DSay : IConsoleCommand
[Dependency] private readonly ChatSystem _chatSystem = default!;
public override string Command => "dsay";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
[Dependency] private readonly IEntityManager _e = default!;
public string Command => "dsay";
public string Description => Loc.GetString("dsay-command-description");
public string Help => Loc.GetString("dsay-command-help-text", ("command", Command));
public void Execute(IConsoleShell shell, string argStr, string[] args)
if (shell.Player is not { } player)
{
if (shell.Player is not { } player)
{
shell.WriteError(Loc.GetString("shell-cannot-run-command-from-server"));
return;
}
if (player.AttachedEntity is not { Valid: true } entity)
return;
if (args.Length < 1)
return;
var message = string.Join(" ", args).Trim();
if (string.IsNullOrEmpty(message))
return;
var chat = _e.System<ChatSystem>();
chat.TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Dead, false, shell, player);
shell.WriteError(Loc.GetString("shell-cannot-run-command-from-server"));
return;
}
if (player.AttachedEntity is not { Valid: true } entity)
{
shell.WriteError(Loc.GetString("shell-must-be-attached-to-entity"));
return;
}
if (args.Length < 1)
return;
var message = string.Join(" ", args).Trim();
if (string.IsNullOrEmpty(message))
return;
_chatSystem.TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Dead, false, shell, player);
}
}

View File

@@ -4,22 +4,18 @@ using Robust.Shared.Console;
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Debug)]
public sealed class DirtyCommand : IConsoleCommand
public sealed class DirtyCommand : LocalizedEntityCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
public override string Command => "dirty";
public string Command => "dirty";
public string Description => "Marks all components on an entity as dirty, if not specified, dirties everything";
public string Help => $"Usage: {Command} [entityUid]";
public async void Execute(IConsoleShell shell, string argStr, string[] args)
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
{
switch (args.Length)
{
case 0:
foreach (var entity in _entManager.GetEntities())
foreach (var entity in EntityManager.GetEntities())
{
DirtyAll(_entManager, entity);
DirtyAll(entity);
}
break;
case 1:
@@ -28,7 +24,7 @@ public sealed class DirtyCommand : IConsoleCommand
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
return;
}
DirtyAll(_entManager, _entManager.GetEntity(parsedTarget));
DirtyAll(EntityManager.GetEntity(parsedTarget));
break;
default:
shell.WriteLine(Loc.GetString("shell-wrong-arguments-number"));
@@ -36,11 +32,11 @@ public sealed class DirtyCommand : IConsoleCommand
}
}
private static void DirtyAll(IEntityManager manager, EntityUid entityUid)
private void DirtyAll(EntityUid entityUid)
{
foreach (var component in manager.GetNetComponents(entityUid))
foreach (var component in EntityManager.GetNetComponents(entityUid))
{
manager.Dirty(entityUid, component.component);
EntityManager.Dirty(entityUid, component.component);
}
}
}

View File

@@ -6,15 +6,13 @@ using Robust.Shared.Enums;
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Admin)]
public sealed class FollowCommand : IConsoleCommand
public sealed class FollowCommand : LocalizedEntityCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly FollowerSystem _followerSystem = default!;
public string Command => "follow";
public string Description => Loc.GetString("follow-command-description");
public string Help => Loc.GetString("follow-command-help");
public override string Command => "follow";
public void Execute(IConsoleShell shell, string argStr, string[] args)
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (shell.Player is not { } player)
{
@@ -34,10 +32,7 @@ public sealed class FollowCommand : IConsoleCommand
return;
}
var entity = args[0];
if (NetEntity.TryParse(entity, out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid))
{
_entManager.System<FollowerSystem>().StartFollowingEntity(playerEntity, uid.Value);
}
if (NetEntity.TryParse(args[0], out var uidNet) && EntityManager.TryGetEntity(uidNet, out var uid))
_followerSystem.StartFollowingEntity(playerEntity, uid.Value);
}
}

View File

@@ -302,6 +302,13 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
return;
}
// PostgreSQL does not support storing null chars in text values.
if (message.Contains('\0'))
{
_sawmill.Error($"Null character detected in admin log message '{message}'! LogType: {type}, LogImpact: {impact}");
message = message.Replace("\0", "");
}
var log = new AdminLog
{
Id = NextLogId,

View File

@@ -22,25 +22,14 @@ public sealed partial class AdminVerbSystem
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly OutfitSystem _outfit = default!;
[ValidatePrototypeId<EntityPrototype>]
private const string DefaultTraitorRule = "Traitor";
private static readonly EntProtoId DefaultTraitorRule = "Traitor";
private static readonly EntProtoId DefaultInitialInfectedRule = "Zombie";
private static readonly EntProtoId DefaultNukeOpRule = "LoneOpsSpawn";
private static readonly EntProtoId DefaultRevsRule = "Revolutionary";
private static readonly EntProtoId DefaultThiefRule = "Thief";
private static readonly ProtoId<StartingGearPrototype> PirateGearId = "PirateGear";
[ValidatePrototypeId<EntityPrototype>]
private const string DefaultInitialInfectedRule = "Zombie";
[ValidatePrototypeId<EntityPrototype>]
private const string DefaultNukeOpRule = "LoneOpsSpawn";
[ValidatePrototypeId<EntityPrototype>]
private const string DefaultRevsRule = "Revolutionary";
[ValidatePrototypeId<EntityPrototype>]
private const string DefaultThiefRule = "Thief";
[ValidatePrototypeId<StartingGearPrototype>]
private const string PirateGearId = "PirateGear";
private readonly EntProtoId _paradoxCloneRuleId = "ParadoxCloneSpawn";
private static readonly EntProtoId ParadoxCloneRuleId = "ParadoxCloneSpawn";
// All antag verbs have names so invokeverb works.
private void AddAntagVerbs(GetVerbsEvent<Verb> args)
@@ -172,7 +161,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/job_icons.rsi"), "ParadoxClone"),
Act = () =>
{
var ruleEnt = _gameTicker.AddGameRule(_paradoxCloneRuleId);
var ruleEnt = _gameTicker.AddGameRule(ParadoxCloneRuleId);
if (!TryComp<ParadoxCloneRuleComponent>(ruleEnt, out var paradoxCloneRuleComp))
return;

View File

@@ -273,7 +273,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Fluids/tomato_splat.rsi"), "puddle-1"),
Act = () =>
{
_bloodstreamSystem.SpillAllSolutions(args.Target, bloodstream);
_bloodstreamSystem.SpillAllSolutions((args.Target, bloodstream));
var xform = Transform(args.Target);
_popupSystem.PopupEntity(Loc.GetString("admin-smite-remove-blood-self"), args.Target,
args.Target, PopupType.LargeCaution);

View File

@@ -44,8 +44,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
public const float MinParticleVariation = 0.8f;
public const float MaxParticleVariation = 1.2f;
[ValidatePrototypeId<WeightedRandomPrototype>]
const string WeightListProto = "AnomalyBehaviorList";
private static readonly ProtoId<WeightedRandomPrototype> WeightListProto = "AnomalyBehaviorList";
/// <inheritdoc/>
public override void Initialize()
@@ -189,7 +188,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
#region Behavior
private string GetRandomBehavior()
{
var weightList = _prototype.Index<WeightedRandomPrototype>(WeightListProto);
var weightList = _prototype.Index(WeightListProto);
return weightList.Pick(_random);
}

View File

@@ -252,6 +252,128 @@ namespace Content.Server.Atmos.EntitySystems
Merge(destination, buffer);
}
/// <summary>
/// Calculates the dimensionless fraction of gas required to equalize pressure between two gas mixtures.
/// </summary>
/// <param name="gasMixture1">The first gas mixture involved in the pressure equalization.
/// This mixture should be the one you always expect to be the highest pressure.</param>
/// <param name="gasMixture2">The second gas mixture involved in the pressure equalization.</param>
/// <returns>A float (from 0 to 1) representing the dimensionless fraction of gas that needs to be transferred from the
/// mixture of higher pressure to the mixture of lower pressure.</returns>
/// <remarks>
/// <para>
/// This properly takes into account the effect
/// of gas merging from inlet to outlet affecting the temperature
/// (and possibly increasing the pressure) in the outlet.
/// </para>
/// <para>
/// The gas is assumed to expand freely,
/// so the temperature of the gas with the greater pressure is not changing.
/// </para>
/// </remarks>
/// <example>
/// If you want to calculate the moles required to equalize pressure between an inlet and an outlet,
/// multiply the fraction returned by the source moles.
/// </example>
public float FractionToEqualizePressure(GasMixture gasMixture1, GasMixture gasMixture2)
{
/*
Problem: the gas being merged from the inlet to the outlet could affect the
temp. of the gas and cause a pressure rise.
We want the pressure to be equalized, so we have to account for this.
For clarity, let's assume that gasMixture1 is the inlet and gasMixture2 is the outlet.
We require mechanical equilibrium, so \( P_1' = P_2' \)
Before the transfer, we have:
\( P_1 = \frac{n_1 R T_1}{V_1} \)
\( P_2 = \frac{n_2 R T_2}{V_2} \)
After removing fraction \( x \) moles from the inlet, we have:
\( P_1' = \frac{(1 - x) n_1 R T_1}{V_1} \)
The outlet will gain the same \( x n_1 \) moles of gas.
So \( n_2' = n_2 + x n_1 \)
After mixing, the outlet temperature will be changed.
Denote the new mixture temperature as \( T_2' \).
Volume is constant.
So we have:
\( P_2' = \frac{(n_2 + x n_1) R T_2}{V_2} \)
The total energy of the incoming inlet to outlet gas at \( T_1 \) plus the existing energy of the outlet gas at \( T_2 \)
will be equal to the energy of the new outlet gas at \( T_2' \).
This leads to the following derivation:
\( x n_1 C_1 T_1 + n_2 C_2 T_2 = (x n_1 C_1 + n_2 C_2) T_2' \)
Where \( C_1 \) and \( C_2 \) are the heat capacities of the inlet and outlet gases, respectively.
Solving for \( T_2' \) gives us:
\( T_2' = \frac{x n_1 C_1 T_1 + n_2 C_2 T_2}{x n_1 C_1 + n_2 C_2} \)
Once again, we require mechanical equilibrium (\( P_1' = P_2' \)),
so we can substitute \( T_2' \) into the pressure equation:
\( \frac{(1 - x) n_1 R T_1}{V_1} =
\frac{(n_2 + x n_1) R}{V_2} \cdot
\frac{x n_1 C_1 T_1 + n_2 C_2 T_2}
{x n_1 C_1 + n_2 C_2} \)
Now it's a matter of solving for \( x \).
Not going to show the full derivation here, just steps.
1. Cancel common factor \( R \).
2. Multiply both sides by \( x n_1 C_1 + n_2 C_2 \), so that everything
becomes a polynomial in terms of \( x \).
3. Expand both sides.
4. Collect like powers of \( x \).
5. After collecting, you should end up with a polynomial of the form:
\( (-n_1 C_1 T_1 (1 + \frac{V_2}{V_1})) x^2 +
(n_1 T_1 \frac{V_2}{V_1} (C_1 - C_2) - n_2 C_1 T_1 - n_1 C_2 T_2) x +
(n_1 T_1 \frac{V_2}{V_1} C_2 - n_2 C_2 T_2) = 0 \)
Divide through by \( n_1 C_1 T_1 \) and replace each ratio with a symbol for clarity:
\( k_V = \frac{V_2}{V_1} \)
\( k_n = \frac{n_2}{n_1} \)
\( k_T = \frac{T_2}{T_1} \)
\( k_C = \frac{C_2}{C_1} \)
*/
// Ensure that P_1 > P_2 so the quadratic works out.
if (gasMixture1.Pressure < gasMixture2.Pressure)
{
(gasMixture1, gasMixture2) = (gasMixture2, gasMixture1);
}
// Establish the dimensionless ratios.
var volumeRatio = gasMixture2.Volume / gasMixture1.Volume;
var molesRatio = gasMixture2.TotalMoles / gasMixture1.TotalMoles;
var temperatureRatio = gasMixture2.Temperature / gasMixture1.Temperature;
var heatCapacityRatio = GetHeatCapacity(gasMixture2) / GetHeatCapacity(gasMixture1);
// The quadratic equation is solved for the transfer fraction.
var quadraticA = 1 + volumeRatio;
var quadraticB = molesRatio - volumeRatio + heatCapacityRatio * (temperatureRatio + volumeRatio);
var quadraticC = heatCapacityRatio * (molesRatio * temperatureRatio - volumeRatio);
return (-quadraticB + MathF.Sqrt(quadraticB * quadraticB - 4 * quadraticA * quadraticC)) / (2 * quadraticA);
}
/// <summary>
/// Determines the number of moles that need to be removed from a <see cref="GasMixture"/> to reach a target pressure threshold.
/// </summary>
/// <param name="gasMixture">The gas mixture whose moles and properties will be used in the calculation.</param>
/// <param name="targetPressure">The target pressure threshold to calculate against.</param>
/// <returns>The difference in moles required to reach the target pressure threshold.</returns>
/// <remarks>The temperature of the gas is assumed to be not changing due to a free expansion.</remarks>
public static float MolesToPressureThreshold(GasMixture gasMixture, float targetPressure)
{
// Kid named PV = nRT.
return gasMixture.TotalMoles -
targetPressure * gasMixture.Volume / (Atmospherics.R * gasMixture.Temperature);
}
/// <summary>
/// Checks whether a gas mixture is probably safe.
/// This only checks temperature and pressure, not gas composition.

View File

@@ -0,0 +1,202 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Components;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Piping;
using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.Audio;
using JetBrains.Annotations;
using Robust.Shared.Timing;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems;
/// <summary>
/// Handles serverside logic for pressure regulators. Gas will only flow through the regulator
/// if the pressure on the inlet side is over a certain pressure threshold.
/// See https://en.wikipedia.org/wiki/Pressure_regulator
/// </summary>
[UsedImplicitly]
public sealed class GasPressureRegulatorSystem : SharedGasPressureRegulatorSystem
{
[Dependency] private readonly SharedAmbientSoundSystem _ambientSound = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasPressureRegulatorComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<GasPressureRegulatorComponent, AtmosDeviceUpdateEvent>(OnPressureRegulatorUpdated);
SubscribeLocalEvent<GasPressureRegulatorComponent, MapInitEvent>(OnMapInit);
}
private void OnMapInit(Entity<GasPressureRegulatorComponent> ent, ref MapInitEvent args)
{
ent.Comp.NextUiUpdate = _timing.CurTime + ent.Comp.UpdateInterval;
}
/// <summary>
/// Dirties the regulator every second or so, so that the UI can update.
/// The UI automatically updates after an AutoHandleStateEvent.
/// </summary>
/// <param name="frameTime"></param>
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<GasPressureRegulatorComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (comp.NextUiUpdate > _timing.CurTime)
continue;
comp.NextUiUpdate += comp.UpdateInterval;
DirtyFields(uid,
comp,
null,
nameof(comp.InletPressure),
nameof(comp.OutletPressure),
nameof(comp.FlowRate));
}
}
private void OnInit(Entity<GasPressureRegulatorComponent> ent, ref ComponentInit args)
{
UpdateAppearance(ent);
}
/// <summary>
/// Handles the updating logic for the pressure regulator.
/// </summary>
/// <param name="ent"> the <see cref="Entity{T}" /> of the pressure regulator</param>
/// <param name="args"> Args provided to us via <see cref="AtmosDeviceUpdateEvent" /></param>
private void OnPressureRegulatorUpdated(Entity<GasPressureRegulatorComponent> ent,
ref AtmosDeviceUpdateEvent args)
{
if (!_nodeContainer.TryGetNodes(ent.Owner,
ent.Comp.InletName,
ent.Comp.OutletName,
out PipeNode? inletPipeNode,
out PipeNode? outletPipeNode))
{
ChangeStatus(false, ent, inletPipeNode, outletPipeNode, 0);
return;
}
/*
It's time for some math! :)
Gas is simply transferred from the inlet to the outlet, restricted by flow rate and pressure.
We want to transfer enough gas to bring the inlet pressure below the threshold,
and only as much as our max flow rate allows.
The equations:
PV = nRT
P1 = P2
Can be used to calculate the amount of gas we need to transfer.
*/
var p1 = inletPipeNode.Air.Pressure;
var p2 = outletPipeNode.Air.Pressure;
if (p1 <= ent.Comp.Threshold || p2 >= p1)
{
ChangeStatus(false, ent, inletPipeNode, outletPipeNode, 0);
return;
}
var t1 = inletPipeNode.Air.Temperature;
// First, calculate the amount of gas we need to transfer to bring us below the threshold.
var deltaMolesToPressureThreshold =
AtmosphereSystem.MolesToPressureThreshold(inletPipeNode.Air, ent.Comp.Threshold);
// Second, calculate the moles required to equalize the pressure.
// We round here to avoid the valve staying enabled for 0.00001 pressure differences.
var deltaMolesToEqualizePressure =
float.Round(_atmosphere.FractionToEqualizePressure(inletPipeNode.Air, outletPipeNode.Air) *
inletPipeNode.Air.TotalMoles,
1,
MidpointRounding.ToPositiveInfinity);
// Third, make sure we only transfer the minimum of the two.
// We do this so that we don't accidentally transfer so much gas to the point
// where the outlet pressure is higher than the inlet.
var deltaMolesToTransfer = Math.Min(deltaMolesToPressureThreshold, deltaMolesToEqualizePressure);
// Fourth, convert to the desired volume to transfer.
var desiredVolumeToTransfer = deltaMolesToTransfer * ((Atmospherics.R * t1) / p1);
// And finally, limit the transfer volume to the max flow rate of the valve.
var actualVolumeToTransfer = Math.Min(desiredVolumeToTransfer,
ent.Comp.MaxTransferRate * _atmosphere.PumpSpeedup() * args.dt);
// We remove the gas from the inlet and merge it into the outlet.
var removed = inletPipeNode.Air.RemoveVolume(actualVolumeToTransfer);
_atmosphere.Merge(outletPipeNode.Air, removed);
// Calculate the flow rate in L/s for the UI.
var sentFlowRate = MathF.Round(actualVolumeToTransfer / args.dt, 1);
ChangeStatus(true, ent, inletPipeNode, outletPipeNode, sentFlowRate);
}
/// <summary>
/// Updates the visual appearance of the pressure regulator based on its current state.
/// </summary>
/// <param name="ent">The <see cref="Entity{GasPressureRegulatorComponent, AppearanceComponent}"/>
/// representing the pressure regulator with respective components.</param>
private void UpdateAppearance(Entity<GasPressureRegulatorComponent> ent)
{
_appearance.SetData(ent,
PressureRegulatorVisuals.State,
ent.Comp.Enabled);
}
/// <summary>
/// Updates the pressure regulator's appearance and sound based on its current state, while
/// also preventing network spamming.
/// Also prepares data for dirtying.
/// </summary>
/// <param name="enabled">The new state to set</param>
/// <param name="ent">The pressure regulator to update</param>
/// <param name="inletNode">The inlet node of the pressure regulator</param>
/// <param name="outletNode">The outlet node of the pressure regulator</param>
/// <param name="flowRate">Current flow rate of the pressure regulator</param>
private void ChangeStatus(bool enabled,
Entity<GasPressureRegulatorComponent> ent,
PipeNode? inletNode,
PipeNode? outletNode,
float flowRate)
{
// First, set data on the component server-side.
ent.Comp.InletPressure = inletNode?.Air.Pressure ?? 0f;
ent.Comp.OutletPressure = outletNode?.Air.Pressure ?? 0f;
ent.Comp.FlowRate = flowRate;
// We need to prevent spamming the network with updates, so only check if we've
// switched states.
if (ent.Comp.Enabled == enabled)
return;
ent.Comp.Enabled = enabled;
_ambientSound.SetAmbience(ent, enabled);
UpdateAppearance(ent);
// The regulator has changed state, so we need to dirty all applicable fields *right now* so the UI updates
// at the same time as everything else.
DirtyFields(ent.AsNullable(),
null,
nameof(ent.Comp.InletPressure),
nameof(ent.Comp.OutletPressure),
nameof(ent.Comp.FlowRate));
}
}

View File

@@ -1,8 +1,8 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Temperature.Components;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Rotting;
using Content.Shared.Body.Events;
using Content.Shared.Damage;
using Robust.Server.Containers;
using Robust.Shared.Physics.Components;

View File

@@ -1,23 +1,15 @@
using Content.Server.Bed.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Bed;
using Content.Shared.Bed.Components;
using Content.Shared.Bed.Sleep;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events;
using Content.Shared.Buckle.Components;
using Content.Shared.Damage;
using Content.Shared.Emag.Systems;
using Content.Shared.Mobs.Systems;
using Content.Shared.Power;
namespace Content.Server.Bed
{
public sealed class BedSystem : SharedBedSystem
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
private EntityQuery<SleepingComponent> _sleepingQuery;
@@ -27,11 +19,6 @@ namespace Content.Server.Bed
base.Initialize();
_sleepingQuery = GetEntityQuery<SleepingComponent>();
SubscribeLocalEvent<StasisBedComponent, StrappedEvent>(OnStasisStrapped);
SubscribeLocalEvent<StasisBedComponent, UnstrappedEvent>(OnStasisUnstrapped);
SubscribeLocalEvent<StasisBedComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<StasisBedComponent, GotEmaggedEvent>(OnEmagged);
}
public override void Update(float frameTime)
@@ -63,61 +50,5 @@ namespace Content.Server.Bed
}
}
}
private void UpdateAppearance(EntityUid uid, bool isOn)
{
_appearance.SetData(uid, StasisBedVisuals.IsOn, isOn);
}
private void OnStasisStrapped(Entity<StasisBedComponent> bed, ref StrappedEvent args)
{
if (!HasComp<BodyComponent>(args.Buckle) || !this.IsPowered(bed, EntityManager))
return;
var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.Buckle, bed.Comp.Multiplier, true);
RaiseLocalEvent(args.Buckle, ref metabolicEvent);
}
private void OnStasisUnstrapped(Entity<StasisBedComponent> bed, ref UnstrappedEvent args)
{
if (!HasComp<BodyComponent>(args.Buckle) || !this.IsPowered(bed, EntityManager))
return;
var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.Buckle, bed.Comp.Multiplier, false);
RaiseLocalEvent(args.Buckle, ref metabolicEvent);
}
private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref PowerChangedEvent args)
{
UpdateAppearance(uid, args.Powered);
UpdateMetabolisms(uid, component, args.Powered);
}
private void OnEmagged(EntityUid uid, StasisBedComponent component, ref GotEmaggedEvent args)
{
if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
return;
if (_emag.CheckFlag(uid, EmagType.Interaction))
return;
// Reset any metabolisms first so they receive the multiplier correctly
UpdateMetabolisms(uid, component, false);
component.Multiplier = 1 / component.Multiplier;
UpdateMetabolisms(uid, component, true);
args.Handled = true;
}
private void UpdateMetabolisms(EntityUid uid, StasisBedComponent component, bool shouldApply)
{
if (!TryComp<StrapComponent>(uid, out var strap) || strap.BuckledEntities.Count == 0)
return;
foreach (var buckledEntity in strap.BuckledEntities)
{
var metabolicEvent = new ApplyMetabolicMultiplierEvent(buckledEntity, component.Multiplier, shouldApply);
RaiseLocalEvent(buckledEntity, ref metabolicEvent);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More