Merge remote-tracking branch 'upstream/stable' into ed-25-08-2025-upstream-sync

# Conflicts:
#	.github/CODEOWNERS
#	Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs
#	Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs
#	Content.Server/Chat/Systems/ChatSystem.cs
#	Content.Server/Explosion/EntitySystems/TriggerSystem.cs
#	Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs
#	Content.Shared/Lock/LockSystem.cs
#	Content.Shared/Nutrition/Components/FoodComponent.cs
#	Content.Shared/Speech/ListenEvent.cs
#	Resources/Prototypes/Entities/Effects/admin_triggers.yml
This commit is contained in:
Ed
2025-08-25 16:22:32 +03:00
1069 changed files with 42396 additions and 29527 deletions

4
.github/CODEOWNERS vendored
View File

@@ -1,6 +1,2 @@
# TheShuEd # TheShuEd
* @TheShuEd * @TheShuEd
# TornadoTechnology
*.cs @Tornado-Technology
*.xaml @Tornado-Technology

View File

@@ -6,7 +6,6 @@ using BenchmarkDotNet.Attributes;
using Content.IntegrationTests; using Content.IntegrationTests;
using Content.IntegrationTests.Pair; using Content.IntegrationTests.Pair;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Warps;
using Content.Shared.Warps; using Content.Shared.Warps;
using Robust.Shared; using Robust.Shared;
using Robust.Shared.Analyzers; using Robust.Shared.Analyzers;

View File

@@ -1,10 +0,0 @@
using Content.Shared.Administration.Components;
using Robust.Shared.GameStates;
namespace Content.Client.Administration.Components;
[RegisterComponent]
public sealed partial class HeadstandComponent : SharedHeadstandComponent
{
}

View File

@@ -1,4 +1,4 @@
using Content.Client.Administration.Components; using Content.Shared.Administration.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
namespace Content.Client.Administration.Systems; namespace Content.Client.Administration.Systems;

View File

@@ -76,7 +76,7 @@ public sealed partial class ObjectsTab : Control
switch (selection) switch (selection)
{ {
case ObjectsTabSelection.Stations: case ObjectsTabSelection.Stations:
entities.AddRange(_entityManager.EntitySysManager.GetEntitySystem<StationSystem>().Stations); entities.AddRange(_entityManager.EntitySysManager.GetEntitySystem<StationSystem>().GetStationNames());
break; break;
case ObjectsTabSelection.Grids: case ObjectsTabSelection.Grids:
{ {

View File

@@ -0,0 +1,5 @@
using Content.Shared.Animals.Systems;
namespace Content.Client.Animals.Systems;
public sealed class ParrotMemorySystem : SharedParrotMemorySystem;

View File

@@ -1,6 +1,6 @@
using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Components;
using Content.Shared.Anomaly.Effects; using Content.Shared.Anomaly.Effects;
using Content.Shared.Body.Components; using Content.Shared.Humanoid;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
namespace Content.Client.Anomaly.Effects; namespace Content.Client.Anomaly.Effects;
@@ -25,9 +25,8 @@ public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap); var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap);
if (TryComp<BodyComponent>(ent, out var body) && if (TryComp<HumanoidAppearanceComponent>(ent, out var humanoidAppearance) &&
body.Prototype is not null && ent.Comp.SpeciesSprites.TryGetValue(humanoidAppearance.Species, out var speciesSprite))
ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
{ {
_sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite); _sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite);
} }

View File

@@ -1,46 +1,11 @@
using Content.Client.Atmos.Components; using Content.Client.Atmos.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Content.Client.UserInterface.Systems.Storage.Controls;
using Content.Shared.Atmos.Piping; using Content.Shared.Atmos.Piping;
using Content.Shared.Hands;
using Content.Shared.Atmos.Components;
using Content.Shared.Item;
namespace Content.Client.Atmos.EntitySystems; namespace Content.Client.Atmos.EntitySystems;
public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisualsComponent> public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisualsComponent>
{ {
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PipeColorVisualsComponent, GetInhandVisualsEvent>(OnGetVisuals);
SubscribeLocalEvent<PipeColorVisualsComponent, BeforeRenderInGridEvent>(OnDrawInGrid);
}
/// <summary>
/// This method is used to display the color changes of the pipe on the screen..
/// </summary>
private void OnGetVisuals(Entity<PipeColorVisualsComponent> item, ref GetInhandVisualsEvent args)
{
foreach (var (_, layerData) in args.Layers)
{
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
layerData.Color = pipeColor.Color;
}
}
/// <summary>
/// This method is used to change the pipe's color in a container grid.
/// </summary>
private void OnDrawInGrid(Entity<PipeColorVisualsComponent> item, ref BeforeRenderInGridEvent args)
{
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
args.Color = pipeColor.Color;
}
protected override void OnAppearanceChange(EntityUid uid, PipeColorVisualsComponent component, ref AppearanceChangeEvent args) protected override void OnAppearanceChange(EntityUid uid, PipeColorVisualsComponent component, ref AppearanceChangeEvent args)
{ {
if (TryComp<SpriteComponent>(uid, out var sprite) if (TryComp<SpriteComponent>(uid, out var sprite)
@@ -50,8 +15,6 @@ public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisual
var layer = sprite[PipeVisualLayers.Pipe]; var layer = sprite[PipeVisualLayers.Pipe];
layer.Color = color.WithAlpha(layer.Color.A); layer.Color = color.WithAlpha(layer.Color.A);
} }
_itemSystem.VisualsChanged(uid);
} }
} }

View File

@@ -59,7 +59,7 @@ public sealed partial class PumpControl : BoxContainer
foreach (var value in Enum.GetValues<VentPumpDirection>()) foreach (var value in Enum.GetValues<VentPumpDirection>())
{ {
_pumpDirection.AddItem(Loc.GetString($"{value}"), (int) value); _pumpDirection.AddItem(Loc.GetString($"air-alarm-ui-pump-direction-{value.ToString().ToLower()}"), (int) value);
} }
_pumpDirection.SelectId((int) _data.PumpDirection); _pumpDirection.SelectId((int) _data.PumpDirection);
@@ -72,7 +72,7 @@ public sealed partial class PumpControl : BoxContainer
foreach (var value in Enum.GetValues<VentPressureBound>()) foreach (var value in Enum.GetValues<VentPressureBound>())
{ {
_pressureCheck.AddItem(Loc.GetString($"{value}"), (int) value); _pressureCheck.AddItem(Loc.GetString($"air-alarm-ui-pressure-bound-{value.ToString().ToLower()}"), (int) value);
} }
_pressureCheck.SelectId((int) _data.PressureChecks); _pressureCheck.SelectId((int) _data.PressureChecks);

View File

@@ -27,9 +27,15 @@
</BoxContainer> </BoxContainer>
<!-- Lower row: every single gas --> <!-- Lower row: every single gas -->
<Collapsible Margin="2 2 2 2"> <Collapsible Margin="2 2 2 2">
<CollapsibleHeading Title="Gas filters" /> <CollapsibleHeading Title="{Loc 'air-alarm-ui-widget-gas-filters'}" />
<CollapsibleBody Margin="20 0 0 0"> <CollapsibleBody Margin="20 0 0 0">
<GridContainer HorizontalExpand="True" Name="CGasContainer" Columns="3" /> <BoxContainer Orientation="Vertical">
<BoxContainer Margin="2">
<Button Name="CSelectAll" Text="{Loc 'air-alarm-ui-scrubber-select-all-gases-label'}" />
<Button Name="CDeselectAll" Text="{Loc 'air-alarm-ui-scrubber-deselect-all-gases-label'}" />
</BoxContainer>
<GridContainer Name="CGasContainer" Columns="3" />
</BoxContainer>
</CollapsibleBody> </CollapsibleBody>
</Collapsible> </Collapsible>
</BoxContainer> </BoxContainer>

View File

@@ -1,15 +1,21 @@
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor.Components; using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Prototypes;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Atmos.Monitor.UI.Widgets; namespace Content.Client.Atmos.Monitor.UI.Widgets;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class ScrubberControl : BoxContainer public sealed partial class ScrubberControl : BoxContainer
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
private GasVentScrubberData _data; private GasVentScrubberData _data;
private string _address; private string _address;
@@ -22,12 +28,18 @@ public sealed partial class ScrubberControl : BoxContainer
private FloatSpinBox _volumeRate => CVolumeRate; private FloatSpinBox _volumeRate => CVolumeRate;
private CheckBox _wideNet => CWideNet; private CheckBox _wideNet => CWideNet;
private Button _copySettings => CCopySettings; private Button _copySettings => CCopySettings;
private Button _selectAll => CSelectAll;
private Button _deselectAll => CDeselectAll;
private GridContainer _gases => CGasContainer; private GridContainer _gases => CGasContainer;
private Dictionary<Gas, Button> _gasControls = new(); private Dictionary<Gas, Button> _gasControls = new();
public ScrubberControl(GasVentScrubberData data, string address) public ScrubberControl(GasVentScrubberData data, string address)
{ {
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
Name = address; Name = address;
@@ -61,7 +73,7 @@ public sealed partial class ScrubberControl : BoxContainer
foreach (var value in Enum.GetValues<ScrubberPumpDirection>()) foreach (var value in Enum.GetValues<ScrubberPumpDirection>())
{ {
_pumpDirection.AddItem(Loc.GetString($"{value}"), (int) value); _pumpDirection.AddItem(Loc.GetString($"air-alarm-ui-pump-direction-{value.ToString().ToLower()}"), (int) value);
} }
_pumpDirection.SelectId((int) _data.PumpDirection); _pumpDirection.SelectId((int) _data.PumpDirection);
@@ -78,12 +90,28 @@ public sealed partial class ScrubberControl : BoxContainer
ScrubberDataCopied?.Invoke(_data); ScrubberDataCopied?.Invoke(_data);
}; };
foreach (var value in Enum.GetValues<Gas>()) var allGases = Enum.GetValues<Gas>();
_selectAll.OnPressed += _ =>
{ {
_data.FilterGases = new HashSet<Gas>(allGases);
ScrubberDataChanged?.Invoke(_address, _data);
};
_deselectAll.OnPressed += _ =>
{
_data.FilterGases = [];
ScrubberDataChanged?.Invoke(_address, _data);
};
foreach (var value in allGases)
{
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(value);
var gasName = _prototypeManager.Index(gasProtoId).Name;
var gasButton = new Button var gasButton = new Button
{ {
Name = value.ToString(), Name = value.ToString(),
Text = Loc.GetString($"{value}"), Text = Loc.GetString(gasName),
ToggleMode = true, ToggleMode = true,
HorizontalExpand = true, HorizontalExpand = true,
Pressed = _data.FilterGases.Contains(value) Pressed = _data.FilterGases.Contains(value)

View File

@@ -1,16 +1,22 @@
using Content.Client.Message; using Content.Client.Message;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Prototypes;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Atmos.Monitor.UI.Widgets; namespace Content.Client.Atmos.Monitor.UI.Widgets;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class SensorInfo : BoxContainer public sealed partial class SensorInfo : BoxContainer
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
public Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? OnThresholdUpdate; public Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? OnThresholdUpdate;
public event Action<AtmosSensorData>? SensorDataCopied; public event Action<AtmosSensorData>? SensorDataCopied;
private string _address; private string _address;
@@ -23,6 +29,9 @@ public sealed partial class SensorInfo : BoxContainer
public SensorInfo(AtmosSensorData data, string address) public SensorInfo(AtmosSensorData data, string address)
{ {
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
_address = address; _address = address;
@@ -45,8 +54,12 @@ public sealed partial class SensorInfo : BoxContainer
var label = new RichTextLabel(); var label = new RichTextLabel();
var fractionGas = amount / data.TotalMoles; var fractionGas = amount / data.TotalMoles;
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(gas);
var gasName = _prototypeManager.Index(gasProtoId).Name;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
("gas", $"{gas}"), ("gas", Loc.GetString(gasName)),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])), ("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"), ("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}"))); ("percentage", $"{(100 * fractionGas):0.##}")));
@@ -54,7 +67,7 @@ public sealed partial class SensorInfo : BoxContainer
_gasLabels.Add(gas, label); _gasLabels.Add(gas, label);
var threshold = data.GasThresholds[gas]; var threshold = data.GasThresholds[gas];
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100); var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title"), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
gasThresholdControl.Margin = new Thickness(20, 2, 2, 2); gasThresholdControl.Margin = new Thickness(20, 2, 2, 2);
gasThresholdControl.ThresholdDataChanged += (type, alarmThreshold, arg3) => gasThresholdControl.ThresholdDataChanged += (type, alarmThreshold, arg3) =>
{ {
@@ -90,6 +103,9 @@ public sealed partial class SensorInfo : BoxContainer
public void ChangeData(AtmosSensorData data) public void ChangeData(AtmosSensorData data)
{ {
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState)); SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState));
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator", AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
@@ -112,8 +128,12 @@ public sealed partial class SensorInfo : BoxContainer
} }
var fractionGas = amount / data.TotalMoles; var fractionGas = amount / data.TotalMoles;
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(gas);
var gasName = _prototypeManager.Index(gasProtoId).Name;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
("gas", $"{gas}"), ("gas", Loc.GetString(gasName)),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])), ("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"), ("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}"))); ("percentage", $"{(100 * fractionGas):0.##}")));

View File

@@ -2,6 +2,6 @@
HorizontalExpand="True" Orientation="Vertical" HorizontalExpand="True" Orientation="Vertical"
Margin = "20 0 0 0" MinSize="160 0" > Margin = "20 0 0 0" MinSize="160 0" >
<Label Name="CBoundLabel" HorizontalAlignment="Center" /> <Label Name="CBoundLabel" HorizontalAlignment="Center" />
<CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'Enable'}" Pressed="True" /> <CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'air-alarm-ui-widget-enable'}" Pressed="True" />
<FloatSpinBox Name="CSpinner" /> <FloatSpinBox Name="CSpinner" />
</BoxContainer> </BoxContainer>

View File

@@ -6,7 +6,7 @@
<CollapsibleBody Margin="20 0 0 0"> <CollapsibleBody Margin="20 0 0 0">
<BoxContainer Orientation="Vertical"> <BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal"> <BoxContainer Orientation="Horizontal">
<CheckBox Name="CEnabled" Text="{Loc 'Enabled'}" /> <CheckBox Name="CEnabled" Text="{Loc 'air-alarm-ui-widget-enable'}" />
</BoxContainer> </BoxContainer>
<!-- Upper row: Danger bounds --> <!-- Upper row: Danger bounds -->
<BoxContainer Name="CDangerBounds" Orientation="Horizontal" Margin="0 0 0 2"/> <BoxContainer Name="CDangerBounds" Orientation="Horizontal" Margin="0 0 0 2"/>

View File

@@ -1,8 +1,8 @@
using System.Numerics; using System.Numerics;
using Content.Shared.Body.Components;
using Content.Shared.CardboardBox; using Content.Shared.CardboardBox;
using Content.Shared.CardboardBox.Components; using Content.Shared.CardboardBox.Components;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -15,13 +15,13 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
[Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly SpriteSystem _sprite = default!;
private EntityQuery<BodyComponent> _bodyQuery; private EntityQuery<MobStateComponent> _mobStateQuery;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_bodyQuery = GetEntityQuery<BodyComponent>(); _mobStateQuery = GetEntityQuery<MobStateComponent>();
SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect); SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect);
} }
@@ -66,8 +66,8 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null)) if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null))
continue; continue;
// no effect for anything too exotic // no effect for non-mobs that have MobMover, such as mechs and vehicles.
if (!_bodyQuery.HasComp(mob)) if (!_mobStateQuery.HasComp(mob))
continue; continue;
var ent = Spawn(box.Effect, mapPos); var ent = Spawn(box.Effect, mapPos);

View File

@@ -0,0 +1,35 @@
using Content.Shared.Changeling.Transform;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Changeling.Transform;
[UsedImplicitly]
public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private ChangelingTransformMenu? _window;
protected override void Open()
{
base.Open();
_window = this.CreateWindow<ChangelingTransformMenu>();
_window.OnIdentitySelect += SendIdentitySelect;
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not ChangelingTransformBoundUserInterfaceState current)
return;
_window?.UpdateState(current);
}
public void SendIdentitySelect(NetEntity identityId)
{
SendPredictedMessage(new ChangelingTransformIdentitySelectMessage(identityId));
}
}

View File

@@ -0,0 +1,8 @@
<ui:RadialMenu
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True">
<ui:RadialContainer Name="Main">
</ui:RadialContainer>
</ui:RadialMenu>

View File

@@ -0,0 +1,60 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Changeling.Transform;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Changeling.Transform;
[GenerateTypedNameReferences]
public sealed partial class ChangelingTransformMenu : RadialMenu
{
[Dependency] private readonly IEntityManager _entity = default!;
public event Action<NetEntity>? OnIdentitySelect;
public ChangelingTransformMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
public void UpdateState(ChangelingTransformBoundUserInterfaceState state)
{
Main.DisposeAllChildren();
foreach (var identity in state.Identites)
{
var identityUid = _entity.GetEntity(identity);
if (!_entity.TryGetComponent<MetaDataComponent>(identityUid, out var metadata))
continue;
var identityName = metadata.EntityName;
var button = new ChangelingTransformMenuButton()
{
StyleClasses = { "RadialMenuButton" },
SetSize = new Vector2(64, 64),
ToolTip = identityName,
};
var entView = new SpriteView()
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill,
};
entView.SetEntity(identityUid);
button.OnButtonUp += _ =>
{
OnIdentitySelect?.Invoke(identity);
Close();
};
button.AddChild(entView);
Main.AddChild(button);
}
}
}
public sealed class ChangelingTransformMenuButton : RadialMenuTextureButtonWithSector;

View File

@@ -0,0 +1,5 @@
using Content.Shared.Cloning;
namespace Content.Client.Cloning;
public sealed partial class CloningSystem : SharedCloningSystem;

View File

@@ -356,6 +356,7 @@ public sealed partial class CreditsWindow : DefaultWindow
AddSection(Loc.GetString("credits-window-contributors-section-title"), "GitHub.txt"); AddSection(Loc.GetString("credits-window-contributors-section-title"), "GitHub.txt");
AddSection(Loc.GetString("credits-window-codebases-section-title"), "SpaceStation13.txt"); AddSection(Loc.GetString("credits-window-codebases-section-title"), "SpaceStation13.txt");
AddSection(Loc.GetString("credits-window-original-remake-team-section-title"), "OriginalRemake.txt"); AddSection(Loc.GetString("credits-window-original-remake-team-section-title"), "OriginalRemake.txt");
AddSection(Loc.GetString("credits-window-immortals-title"), "Immortals.txt", true);
AddSection(Loc.GetString("credits-window-special-thanks-section-title"), "SpecialThanks.txt", true); AddSection(Loc.GetString("credits-window-special-thanks-section-title"), "SpecialThanks.txt", true);
var linkGithub = _cfg.GetCVar(CCVars.InfoLinksGithub); var linkGithub = _cfg.GetCVar(CCVars.InfoLinksGithub);

View File

@@ -1,7 +0,0 @@
using Content.Shared.Explosion.EntitySystems;
namespace Content.Client.Explosion;
public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem
{
}

View File

@@ -1,7 +0,0 @@
using Content.Shared.Explosion;
using Content.Shared.Explosion.Components;
namespace Content.Client.Explosion;
[RegisterComponent, Access(typeof(TriggerSystem))]
public sealed partial class TriggerOnProximityComponent : SharedTriggerOnProximityComponent {}

View File

@@ -1,10 +0,0 @@
namespace Content.Client.Explosion;
public sealed partial class TriggerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
InitializeProximity();
}
}

View File

@@ -0,0 +1,5 @@
using Content.Shared.Forensics.Systems;
namespace Content.Client.Forensics.Systems;
public sealed class ForensicsSystem : SharedForensicsSystem;

View File

@@ -1,5 +1,6 @@
using Content.Shared.HotPotato; using Content.Shared.HotPotato;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Client.HotPotato; namespace Content.Client.HotPotato;
@@ -10,6 +11,9 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedTransformSystem _transform = default!;
private readonly EntProtoId _hotPotatoEffectId = "HotPotatoEffect";
// TODO: particle system
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
base.Update(frameTime); base.Update(frameTime);
@@ -23,7 +27,7 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
if (_timing.CurTime < comp.TargetTime) if (_timing.CurTime < comp.TargetTime)
continue; continue;
comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown); comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
Spawn("HotPotatoEffect", _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f))); Spawn(_hotPotatoEffectId, _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
} }
} }
} }

View File

@@ -294,7 +294,6 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
SetMaster(uid, null); SetMaster(uid, null);
TrySetChannels(uid, data); TrySetChannels(uid, data);
instrument.MidiEventBuffer.Clear();
instrument.Renderer.OnMidiEvent += instrument.MidiEventBuffer.Add; instrument.Renderer.OnMidiEvent += instrument.MidiEventBuffer.Add;
return true; return true;
} }

View File

@@ -115,6 +115,13 @@ namespace Content.Client.Inventory
OnLinkInventorySlots?.Invoke(uid, component); OnLinkInventorySlots?.Invoke(uid, component);
} }
protected override void OnInit(Entity<InventoryComponent> ent, ref ComponentInit args)
{
base.OnInit(ent, ref args);
_clothingVisualsSystem.InitClothing(ent.Owner, ent.Comp);
}
public override void Shutdown() public override void Shutdown()
{ {
CommandBinds.Unregister<ClientInventorySystem>(); CommandBinds.Unregister<ClientInventorySystem>();
@@ -261,7 +268,6 @@ namespace Content.Client.Inventory
TryAddSlotData((ent.Owner, inventorySlots), (SlotData)slot); TryAddSlotData((ent.Owner, inventorySlots), (SlotData)slot);
} }
_clothingVisualsSystem.InitClothing(ent, ent.Comp);
if (ent.Owner == _playerManager.LocalEntity) if (ent.Owner == _playerManager.LocalEntity)
ReloadInventory(inventorySlots); ReloadInventory(inventorySlots);
} }

View File

@@ -1,36 +1,46 @@
using Content.Shared.Light.Components; using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
namespace Content.Client.Light.Visualizers; namespace Content.Client.Light.EntitySystems;
public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent> public sealed class LightBulbSystem : SharedLightBulbSystem
{ {
protected override void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args) [Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LightBulbComponent, AppearanceChangeEvent>(OnAppearanceChange);
}
private void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
{ {
if (args.Sprite == null) if (args.Sprite == null)
return; return;
// update sprite state // update sprite state
if (AppearanceSystem.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component)) if (_appearance.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
{ {
switch (state) switch (state)
{ {
case LightBulbState.Normal: case LightBulbState.Normal:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState); _sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
break; break;
case LightBulbState.Broken: case LightBulbState.Broken:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState); _sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
break; break;
case LightBulbState.Burned: case LightBulbState.Burned:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState); _sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
break; break;
} }
} }
// also update sprites color // also update sprites color
if (AppearanceSystem.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component)) if (_appearance.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
{ {
SpriteSystem.SetColor((uid, args.Sprite), color); _sprite.SetColor((uid, args.Sprite), color);
} }
} }
} }

View File

@@ -0,0 +1,5 @@
using Content.Shared.Light.EntitySystems;
namespace Content.Client.Light.EntitySystems;
public sealed class PoweredLightSystem : SharedPoweredLightSystem;

View File

@@ -1021,7 +1021,7 @@ namespace Content.Client.Lobby.UI
_loadoutWindow = new LoadoutWindow(Profile, roleLoadout, roleLoadoutProto, _playerManager.LocalSession, collection) _loadoutWindow = new LoadoutWindow(Profile, roleLoadout, roleLoadoutProto, _playerManager.LocalSession, collection)
{ {
Title = jobProto?.ID + "-loadout", Title = Loc.GetString("loadout-window-title-loadout", ("job", $"{jobProto?.LocalizedName}")),
}; };
// Refresh the buttons etc. // Refresh the buttons etc.

View File

@@ -1,7 +1,5 @@
using System.Numerics; using System.Numerics;
using Content.Shared.Emag.Systems;
using Content.Shared.Medical.Cryogenics; using Content.Shared.Medical.Cryogenics;
using Content.Shared.Verbs;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
namespace Content.Client.Medical.Cryogenics; namespace Content.Client.Medical.Cryogenics;
@@ -15,11 +13,6 @@ public sealed class CryoPodSystem : SharedCryoPodSystem
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished);
SubscribeLocalEvent<CryoPodComponent, AppearanceChangeEvent>(OnAppearanceChange); SubscribeLocalEvent<CryoPodComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<InsideCryoPodComponent, ComponentStartup>(OnCryoPodInsertion); SubscribeLocalEvent<InsideCryoPodComponent, ComponentStartup>(OnCryoPodInsertion);
SubscribeLocalEvent<InsideCryoPodComponent, ComponentRemove>(OnCryoPodRemoval); SubscribeLocalEvent<InsideCryoPodComponent, ComponentRemove>(OnCryoPodRemoval);
@@ -53,8 +46,8 @@ public sealed class CryoPodSystem : SharedCryoPodSystem
return; return;
} }
if (!_appearance.TryGetData<bool>(uid, CryoPodComponent.CryoPodVisuals.ContainsEntity, out var isOpen, args.Component) if (!_appearance.TryGetData<bool>(uid, CryoPodVisuals.ContainsEntity, out var isOpen, args.Component)
|| !_appearance.TryGetData<bool>(uid, CryoPodComponent.CryoPodVisuals.IsOn, out var isOn, args.Component)) || !_appearance.TryGetData<bool>(uid, CryoPodVisuals.IsOn, out var isOn, args.Component))
{ {
return; return;
} }

View File

@@ -0,0 +1,5 @@
using Content.Shared.Medical.SuitSensors;
namespace Content.Client.Medical.SuitSensors;
public sealed class SuitSensorSystem : SharedSuitSensorSystem;

View File

@@ -5,15 +5,15 @@
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"> <BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" MinHeight="500" /> <networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" MinHeight="500" />
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 8 8 1"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 8 8 1">
<Button Name="Set" Text="Set" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-set'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/> <Button Name="Set" Text="{Loc 'network-configurator-text-set'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-set'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Add" Text="Add" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-add'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/> <Button Name="Add" Text="{Loc 'network-configurator-text-add'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-add'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<!-- Edit might not be needed --> <!-- Edit might not be needed -->
<!--<Button Name="Edit" Text="Edit" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-edit'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>--> <!--<Button Name="Edit" Text="Edit" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-edit'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>-->
<Button Name="Clear" Text="Clear" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-clear'}" HorizontalExpand="True"/> <Button Name="Clear" Text="{Loc 'network-configurator-text-clear'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-clear'}" HorizontalExpand="True"/>
</BoxContainer> </BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 0 8 8"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 0 8 8">
<Button Name="Copy" Text="Copy" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/> <Button Name="Copy" Text="{Loc 'network-configurator-text-copy'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/>
<Button Name="Show" Text="Show" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/> <Button Name="Show" Text="{Loc 'network-configurator-text-show'}" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
</BoxContainer> </BoxContainer>
<Label Name="Count" StyleClasses="LabelSubText" HorizontalAlignment="Right" Margin="0 0 12 8"/> <Label Name="Count" StyleClasses="LabelSubText" HorizontalAlignment="Right" Margin="0 0 12 8"/>
</BoxContainer> </BoxContainer>

View File

@@ -94,7 +94,7 @@ public sealed class ClientsidePlaytimeTrackingManager
return; return;
} }
// At less than 1 minute of time diff, there's not much point, and saving regardless will brick tests // At less than 1 minute of time diff, there's not much point
// The reason this isn't checking for 0 is because TotalMinutes is fractional, rather than solely whole minutes // The reason this isn't checking for 0 is because TotalMinutes is fractional, rather than solely whole minutes
if (timeDiffMinutes < 1) if (timeDiffMinutes < 1)
return; return;

View File

@@ -5,4 +5,5 @@ namespace Content.Client.Power.Components;
[RegisterComponent] [RegisterComponent]
public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent
{ {
public override float Load { get; set; }
} }

View File

@@ -28,6 +28,8 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
public EntityUid Entity; public EntityUid Entity;
private bool _allowBorgControl = true;
public RoboticsConsoleWindow() public RoboticsConsoleWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -72,6 +74,7 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
public void UpdateState(RoboticsConsoleState state) public void UpdateState(RoboticsConsoleState state)
{ {
_cyborgs = state.Cyborgs; _cyborgs = state.Cyborgs;
_allowBorgControl = state.AllowBorgControl;
// clear invalid selection // clear invalid selection
if (_selected is {} selected && !_cyborgs.ContainsKey(selected)) if (_selected is {} selected && !_cyborgs.ContainsKey(selected))
@@ -85,8 +88,8 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
PopulateData(); PopulateData();
var locked = _lock.IsLocked(Entity); var locked = _lock.IsLocked(Entity);
DangerZone.Visible = !locked; DangerZone.Visible = !locked && _allowBorgControl;
LockedMessage.Visible = locked; LockedMessage.Visible = locked && _allowBorgControl; // Only show if locked AND control is allowed
} }
private void PopulateCyborgs() private void PopulateCyborgs()
@@ -120,11 +123,19 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
BorgSprite.Texture = _sprite.Frame0(data.ChassisSprite!); BorgSprite.Texture = _sprite.Frame0(data.ChassisSprite!);
var batteryColor = data.Charge switch { var batteryColor = data.Charge switch {
< 0.2f => "red", < 0.2f => "#FF6C7F", // red
< 0.4f => "orange", < 0.4f => "#EF973C", // orange
< 0.6f => "yellow", < 0.6f => "#E8CB2D", // yellow
< 0.8f => "green", < 0.8f => "#30CC19", // green
_ => "blue" _ => "#00D3B8" // cyan
};
var hpPercentColor = data.HpPercent switch {
< 0.2f => "#FF6C7F", // red
< 0.4f => "#EF973C", // orange
< 0.6f => "#E8CB2D", // yellow
< 0.8f => "#30CC19", // green
_ => "#00D3B8" // cyan
}; };
var text = new FormattedMessage(); var text = new FormattedMessage();
@@ -132,12 +143,14 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
text.AddMarkupOrThrow(Loc.GetString("robotics-console-designation")); text.AddMarkupOrThrow(Loc.GetString("robotics-console-designation"));
text.AddText($" {data.Name}\n"); // prevent players trolling by naming borg [color=red]satan[/color] text.AddText($" {data.Name}\n"); // prevent players trolling by naming borg [color=red]satan[/color]
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-battery", ("charge", (int)(data.Charge * 100f)), ("color", batteryColor))}\n"); text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-battery", ("charge", (int)(data.Charge * 100f)), ("color", batteryColor))}\n");
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-hp", ("hp", (int)(data.HpPercent * 100f)), ("color", hpPercentColor))}\n");
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-brain", ("brain", data.HasBrain))}\n"); text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-brain", ("brain", data.HasBrain))}\n");
text.AddMarkupOrThrow(Loc.GetString("robotics-console-modules", ("count", data.ModuleCount))); text.AddMarkupOrThrow(Loc.GetString("robotics-console-modules", ("count", data.ModuleCount)));
BorgInfo.SetMessage(text); BorgInfo.SetMessage(text);
// how the turntables // how the turntables
DisableButton.Disabled = !(data.HasBrain && data.CanDisable); DisableButton.Disabled = !_allowBorgControl || !(data.HasBrain && data.CanDisable);
DestroyButton.Disabled = !_allowBorgControl;
} }
protected override void FrameUpdate(FrameEventArgs args) protected override void FrameUpdate(FrameEventArgs args)

View File

@@ -24,7 +24,7 @@ public sealed class RotationVisualizerSystem : SharedRotationVisualsSystem
return; return;
if (!_appearance.TryGetData<RotationState>(uid, RotationVisuals.RotationState, out var state, args.Component)) if (!_appearance.TryGetData<RotationState>(uid, RotationVisuals.RotationState, out var state, args.Component))
return; state = RotationState.Vertical;
switch (state) switch (state)
{ {

View File

@@ -40,6 +40,8 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
private readonly HashSet<DockingPortState> _drawnDocks = new(); private readonly HashSet<DockingPortState> _drawnDocks = new();
private readonly Dictionary<DockingPortState, Button> _dockButtons = new(); private readonly Dictionary<DockingPortState, Button> _dockButtons = new();
private readonly Color _fallbackHighlightedColor = Color.Magenta;
/// <summary> /// <summary>
/// Store buttons for every other dock /// Store buttons for every other dock
/// </summary> /// </summary>
@@ -213,11 +215,11 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
if (HighlightedDock == dock.Entity) if (HighlightedDock == dock.Entity)
{ {
otherDockColor = Color.ToSrgb(Color.Magenta); otherDockColor = Color.ToSrgb(dock.HighlightedColor);
} }
else else
{ {
otherDockColor = Color.ToSrgb(Color.Purple); otherDockColor = Color.ToSrgb(dock.Color);
} }
/* /*
@@ -311,7 +313,7 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
ScalePosition(Vector2.Transform(new Vector2(-0.5f, 0.5f), rotation)), ScalePosition(Vector2.Transform(new Vector2(-0.5f, 0.5f), rotation)),
ScalePosition(Vector2.Transform(new Vector2(0.5f, -0.5f), rotation))); ScalePosition(Vector2.Transform(new Vector2(0.5f, -0.5f), rotation)));
var dockColor = Color.Magenta; var dockColor = _viewedState?.HighlightedColor ?? _fallbackHighlightedColor;
var connectionColor = Color.Pink; var connectionColor = Color.Pink;
handle.DrawRect(ourDockConnection, connectionColor.WithAlpha(0.2f)); handle.DrawRect(ourDockConnection, connectionColor.WithAlpha(0.2f));

View File

@@ -308,7 +308,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
-dockRadius * UIScale, -dockRadius * UIScale,
(Size.X + dockRadius) * UIScale, (Size.X + dockRadius) * UIScale,
(Size.Y + dockRadius) * UIScale); (Size.Y + dockRadius) * UIScale);
if (_docks.TryGetValue(nent, out var docks)) if (_docks.TryGetValue(nent, out var docks))
{ {
foreach (var state in docks) foreach (var state in docks)
@@ -321,7 +321,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
continue; continue;
} }
var color = Color.ToSrgb(Color.Magenta); var color = Color.ToSrgb(state.HighlightedColor);
var verts = new[] var verts = new[]
{ {

View File

@@ -12,6 +12,8 @@ public sealed class EmitterSystem : SharedEmitterSystem
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
base.Initialize();
SubscribeLocalEvent<EmitterComponent, AppearanceChangeEvent>(OnAppearanceChange); SubscribeLocalEvent<EmitterComponent, AppearanceChangeEvent>(OnAppearanceChange);
} }

View File

@@ -0,0 +1,38 @@
using System.Numerics;
using Content.Shared.Sprite;
using Robust.Client.GameObjects;
namespace Content.Client.Sprite;
public sealed class ScaleVisualsSystem : SharedScaleVisualsSystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ScaleVisualsComponent, AppearanceChangeEvent>(OnChangeData);
}
private void OnChangeData(Entity<ScaleVisualsComponent> ent, ref AppearanceChangeEvent args)
{
if (!args.AppearanceData.TryGetValue(ScaleVisuals.Scale, out var scale) ||
args.Sprite == null) return;
// save the original scale
ent.Comp.OriginalScale ??= args.Sprite.Scale;
var vecScale = (Vector2)scale;
_sprite.SetScale((ent.Owner, args.Sprite), vecScale);
}
// revert to the original scale
protected override void ResetScale(Entity<ScaleVisualsComponent> ent)
{
base.ResetScale(ent);
if (ent.Comp.OriginalScale != null)
_sprite.SetScale(ent.Owner, ent.Comp.OriginalScale.Value);
}
}

View File

@@ -28,22 +28,8 @@ namespace Content.Client.Stack
base.SetCount(uid, amount, component); base.SetCount(uid, amount, component);
if (component.Lingering &&
TryComp<SpriteComponent>(uid, out var sprite))
{
// tint the stack gray and make it transparent if it's lingering.
var color = component.Count == 0 && component.Lingering
? Color.DarkGray.WithAlpha(0.65f)
: Color.White;
for (var i = 0; i < sprite.AllLayers.Count(); i++)
{
_sprite.LayerSetColor((uid, sprite), i, color);
}
}
// TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call. // TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call.
if (component.Count <= 0 && !component.Lingering) if (component.Count <= 0)
{ {
Xform.DetachEntity(uid, Transform(uid)); Xform.DetachEntity(uid, Transform(uid));
return; return;

View File

@@ -2,34 +2,5 @@
namespace Content.Client.Station; namespace Content.Client.Station;
/// <summary> /// <inheritdoc/>
/// This handles letting the client know stations are a thing. Only really used by an admin menu. public sealed partial class StationSystem : SharedStationSystem;
/// </summary>
public sealed partial class StationSystem : SharedStationSystem
{
private readonly List<(string Name, NetEntity Entity)> _stations = new();
/// <summary>
/// All stations that currently exist.
/// </summary>
/// <remarks>
/// I'd have this just invoke an entity query, but we're on the client and the client barely knows about stations.
/// </remarks>
// TODO: Stations have a global PVS override now, this can probably be changed into a query.
public IReadOnlyList<(string Name, NetEntity Entity)> Stations => _stations;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<StationsUpdatedEvent>(StationsUpdated);
}
private void StationsUpdated(StationsUpdatedEvent ev)
{
_stations.Clear();
// TODO this needs to be done in component states and with the Ensure() methods
_stations.AddRange(ev.Stations);
}
}

View File

@@ -40,6 +40,11 @@ public sealed class StorageSystem : SharedStorageSystem
component.MaxItemSize = state.MaxItemSize; component.MaxItemSize = state.MaxItemSize;
component.Whitelist = state.Whitelist; component.Whitelist = state.Whitelist;
component.Blacklist = state.Blacklist; component.Blacklist = state.Blacklist;
component.StorageInsertSound = state.StorageInsertSound;
component.StorageRemoveSound = state.StorageRemoveSound;
component.StorageOpenSound = state.StorageOpenSound;
component.StorageCloseSound = state.StorageCloseSound;
component.DefaultStorageOrientation = state.DefaultStorageOrientation;
_oldStoredItems.Clear(); _oldStoredItems.Clear();

View File

@@ -21,7 +21,7 @@
Name="RefundButton" Name="RefundButton"
MinWidth="64" MinWidth="64"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Text="Refund" /> Text="{Loc 'store-ui-refund-text'}" />
</BoxContainer> </BoxContainer>
<LineEdit Name="SearchBar" Margin="4" PlaceHolder="Search" HorizontalExpand="True"/> <LineEdit Name="SearchBar" Margin="4" PlaceHolder="Search" HorizontalExpand="True"/>
<PanelContainer VerticalExpand="True"> <PanelContainer VerticalExpand="True">

View File

@@ -1,7 +1,8 @@
using Content.Client.Trigger.Systems;
using Robust.Client.Animations; using Robust.Client.Animations;
using Robust.Shared.Audio; using Robust.Shared.Audio;
namespace Content.Client.Trigger; namespace Content.Client.Trigger.Components;
[RegisterComponent] [RegisterComponent]
[Access(typeof(TimerTriggerVisualizerSystem))] [Access(typeof(TimerTriggerVisualizerSystem))]
@@ -16,28 +17,27 @@ public sealed partial class TimerTriggerVisualsComponent : Component
/// <summary> /// <summary>
/// The RSI state used while the device has not been primed. /// The RSI state used while the device has not been primed.
/// </summary> /// </summary>
[DataField("unprimedSprite")] [DataField]
[ViewVariables(VVAccess.ReadWrite)]
public string UnprimedSprite = "icon"; public string UnprimedSprite = "icon";
/// <summary> /// <summary>
/// The RSI state used when the device is primed. /// The RSI state used when the device is primed.
/// Not VVWrite-able because it's only used at component init to construct the priming animation. /// Not VVWrite-able because it's only used at component init to construct the priming animation.
/// </summary> /// </summary>
[DataField("primingSprite")] [DataField]
public string PrimingSprite = "primed"; public string PrimingSprite = "primed";
/// <summary> /// <summary>
/// The sound played when the device is primed. /// The sound played when the device is primed.
/// Not VVWrite-able because it's only used at component init to construct the priming animation. /// Not VVWrite-able because it's only used at component init to construct the priming animation.
/// </summary> /// </summary>
[DataField("primingSound")] [DataField, ViewVariables]
public SoundSpecifier? PrimingSound; public SoundSpecifier? PrimingSound;
/// <summary> /// <summary>
/// The actual priming animation. /// The actual priming animation.
/// Constructed at component init from the sprite and sound. /// Constructed at component init from the sprite and sound.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables]
public Animation PrimingAnimation = default!; public Animation PrimingAnimation = default!;
} }

View File

@@ -1,11 +1,12 @@
using Content.Shared.Trigger; using Content.Shared.Trigger;
using Content.Shared.Trigger.Components.Triggers;
using Robust.Client.Animations; using Robust.Client.Animations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Animations; using Robust.Shared.Animations;
namespace Content.Client.Explosion; namespace Content.Client.Trigger.Systems;
public sealed partial class TriggerSystem public sealed class ProximityTriggerAnimationSystem : EntitySystem
{ {
[Dependency] private readonly AnimationPlayerSystem _player = default!; [Dependency] private readonly AnimationPlayerSystem _player = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -18,7 +19,7 @@ public sealed partial class TriggerSystem
private const string AnimKey = "proximity"; private const string AnimKey = "proximity";
private static readonly Animation _flasherAnimation = new Animation private static readonly Animation FlasherAnimation = new Animation
{ {
Length = TimeSpan.FromSeconds(0.6f), Length = TimeSpan.FromSeconds(0.6f),
AnimationTracks = { AnimationTracks = {
@@ -42,8 +43,10 @@ public sealed partial class TriggerSystem
} }
}; };
private void InitializeProximity() public override void Initialize()
{ {
base.Initialize();
SubscribeLocalEvent<TriggerOnProximityComponent, ComponentInit>(OnProximityInit); SubscribeLocalEvent<TriggerOnProximityComponent, ComponentInit>(OnProximityInit);
SubscribeLocalEvent<TriggerOnProximityComponent, AppearanceChangeEvent>(OnProxAppChange); SubscribeLocalEvent<TriggerOnProximityComponent, AppearanceChangeEvent>(OnProxAppChange);
SubscribeLocalEvent<TriggerOnProximityComponent, AnimationCompletedEvent>(OnProxAnimation); SubscribeLocalEvent<TriggerOnProximityComponent, AnimationCompletedEvent>(OnProxAnimation);
@@ -94,7 +97,7 @@ public sealed partial class TriggerSystem
break; break;
case ProximityTriggerVisuals.Active: case ProximityTriggerVisuals.Active:
if (_player.HasRunningAnimation(uid, player, AnimKey)) return; if (_player.HasRunningAnimation(uid, player, AnimKey)) return;
_player.Play((uid, player), _flasherAnimation, AnimKey); _player.Play((uid, player), FlasherAnimation, AnimKey);
break; break;
case ProximityTriggerVisuals.Off: case ProximityTriggerVisuals.Off:
default: default:

View File

@@ -0,0 +1,5 @@
using Content.Shared.Trigger.Systems;
namespace Content.Client.Trigger.Systems;
public sealed class ReleaseGasOnTriggerSystem : SharedReleaseGasOnTriggerSystem;

View File

@@ -1,11 +1,10 @@
using Content.Client.Trigger.Components;
using Content.Shared.Trigger; using Content.Shared.Trigger;
using Robust.Client.Animations; using Robust.Client.Animations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.GameObjects;
namespace Content.Client.Trigger; namespace Content.Client.Trigger.Systems;
public sealed class TimerTriggerVisualizerSystem : VisualizerSystem<TimerTriggerVisualsComponent> public sealed class TimerTriggerVisualizerSystem : VisualizerSystem<TimerTriggerVisualsComponent>
{ {
@@ -17,25 +16,26 @@ public sealed class TimerTriggerVisualizerSystem : VisualizerSystem<TimerTrigger
SubscribeLocalEvent<TimerTriggerVisualsComponent, ComponentInit>(OnComponentInit); SubscribeLocalEvent<TimerTriggerVisualsComponent, ComponentInit>(OnComponentInit);
} }
private void OnComponentInit(EntityUid uid, TimerTriggerVisualsComponent comp, ComponentInit args) private void OnComponentInit(Entity<TimerTriggerVisualsComponent> ent, ref ComponentInit args)
{ {
comp.PrimingAnimation = new Animation ent.Comp.PrimingAnimation = new Animation
{ {
Length = TimeSpan.MaxValue, Length = TimeSpan.MaxValue,
AnimationTracks = { AnimationTracks = {
new AnimationTrackSpriteFlick() { new AnimationTrackSpriteFlick()
{
LayerKey = TriggerVisualLayers.Base, LayerKey = TriggerVisualLayers.Base,
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.PrimingSprite, 0f) } KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(ent.Comp.PrimingSprite, 0f) }
} }
}, },
}; };
if (comp.PrimingSound != null) if (ent.Comp.PrimingSound != null)
{ {
comp.PrimingAnimation.AnimationTracks.Add( ent.Comp.PrimingAnimation.AnimationTracks.Add(
new AnimationTrackPlaySound() new AnimationTrackPlaySound()
{ {
KeyFrames = { new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(comp.PrimingSound), 0) } KeyFrames = { new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(ent.Comp.PrimingSound), 0) }
} }
); );
} }

View File

@@ -1,9 +1,11 @@
using System.Numerics; using System.Numerics;
using Content.Client.Cooldown; using Content.Client.Cooldown;
using Content.Client.UserInterface.Systems.Inventory.Controls; using Content.Client.UserInterface.Systems.Inventory.Controls;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Prototypes;
namespace Content.Client.UserInterface.Controls namespace Content.Client.UserInterface.Controls
{ {
@@ -20,6 +22,7 @@ namespace Content.Client.UserInterface.Controls
public CooldownGraphic CooldownDisplay { get; } public CooldownGraphic CooldownDisplay { get; }
private SpriteView SpriteView { get; } private SpriteView SpriteView { get; }
private EntityPrototypeView ProtoView { get; }
public EntityUid? Entity => SpriteView.Entity; public EntityUid? Entity => SpriteView.Entity;
@@ -141,6 +144,13 @@ namespace Content.Client.UserInterface.Controls
SetSize = new Vector2(DefaultButtonSize, DefaultButtonSize), SetSize = new Vector2(DefaultButtonSize, DefaultButtonSize),
OverrideDirection = Direction.South OverrideDirection = Direction.South
}); });
AddChild(ProtoView = new EntityPrototypeView
{
Visible = false,
Scale = new Vector2(2, 2),
SetSize = new Vector2(DefaultButtonSize, DefaultButtonSize),
OverrideDirection = Direction.South
});
AddChild(HoverSpriteView = new SpriteView AddChild(HoverSpriteView = new SpriteView
{ {
@@ -209,12 +219,35 @@ namespace Content.Client.UserInterface.Controls
HoverSpriteView.SetEntity(null); HoverSpriteView.SetEntity(null);
} }
/// <summary>
/// Causes the control to display a placeholder prototype, optionally faded
/// </summary>
public void SetEntity(EntityUid? ent) public void SetEntity(EntityUid? ent)
{ {
SpriteView.SetEntity(ent); SpriteView.SetEntity(ent);
SpriteView.Visible = true;
ProtoView.Visible = false;
UpdateButtonTexture(); UpdateButtonTexture();
} }
/// <summary>
/// Causes the control to display a placeholder prototype, optionally faded
/// </summary>
public void SetPrototype(EntProtoId? proto, bool fade)
{
ProtoView.SetPrototype(proto);
SpriteView.Visible = false;
ProtoView.Visible = true;
UpdateButtonTexture();
if (ProtoView.Entity is not { } ent || !fade)
return;
var sprites = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SpriteSystem>();
sprites.SetColor((ent.Owner, ent.Comp1), Color.DarkGray.WithAlpha(0.65f));
}
private void UpdateButtonTexture() private void UpdateButtonTexture()
{ {
var fullTexture = Theme.ResolveTextureOrNull(_fullButtonTexturePath); var fullTexture = Theme.ResolveTextureOrNull(_fullButtonTexturePath);

View File

@@ -5,7 +5,7 @@
<ScrollContainer HorizontalExpand="True" <ScrollContainer HorizontalExpand="True"
VerticalExpand="True" VerticalExpand="True"
SizeFlagsStretchRatio="6"> SizeFlagsStretchRatio="6">
<GridContainer Name="Values"/> <GridContainer Name="Values" HSeparationOverride="0" VSeparationOverride="15"/>
</ScrollContainer> </ScrollContainer>
</BoxContainer> </BoxContainer>
</DefaultWindow> </DefaultWindow>

View File

@@ -4,7 +4,6 @@ using Content.Client.Actions.UI;
using Content.Client.Cooldown; using Content.Client.Cooldown;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.Actions.Components; using Content.Shared.Actions.Components;
using Content.Shared.Charges.Components;
using Content.Shared.Charges.Systems; using Content.Shared.Charges.Systems;
using Content.Shared.Examine; using Content.Shared.Examine;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -27,7 +26,6 @@ public sealed class ActionButton : Control, IEntityControl
private IPlayerManager _player; private IPlayerManager _player;
private SpriteSystem? _spriteSys; private SpriteSystem? _spriteSys;
private ActionUIController? _controller; private ActionUIController? _controller;
private SharedChargesSystem _sharedChargesSys;
private bool _beingHovered; private bool _beingHovered;
private bool _depressed; private bool _depressed;
private bool _toggled; private bool _toggled;
@@ -71,7 +69,6 @@ public sealed class ActionButton : Control, IEntityControl
_entities = entities; _entities = entities;
_player = IoCManager.Resolve<IPlayerManager>(); _player = IoCManager.Resolve<IPlayerManager>();
_spriteSys = spriteSys; _spriteSys = spriteSys;
_sharedChargesSys = _entities.System<SharedChargesSystem>();
_controller = controller; _controller = controller;
MouseFilter = MouseFilterMode.Pass; MouseFilter = MouseFilterMode.Pass;

View File

@@ -21,17 +21,20 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
private MenuButton? EmotesButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.EmotesButton; private MenuButton? EmotesButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.EmotesButton;
private SimpleRadialMenu? _menu; private SimpleRadialMenu? _menu;
private static readonly Dictionary<EmoteCategory, (string Tooltip, SpriteSpecifier Sprite)> EmoteGroupingInfo private static readonly Dictionary<EmoteCategory, (string Tooltip, SpriteSpecifier Sprite)> EmoteGroupingInfo =
= new Dictionary<EmoteCategory, (string Tooltip, SpriteSpecifier Sprite)> new()
{ {
[EmoteCategory.General] = ("emote-menu-category-general", new SpriteSpecifier.Texture(new ResPath("/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"))), [EmoteCategory.General] = ("emote-menu-category-general",
[EmoteCategory.Hands] = ("emote-menu-category-hands", new SpriteSpecifier.Texture(new ResPath("/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"))), new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Head/Soft/mimesoft.rsi"), "icon")),
[EmoteCategory.Vocal] = ("emote-menu-category-vocal", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Emotes/vocal.png"))), [EmoteCategory.Hands] = ("emote-menu-category-hands",
}; new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Hands/Gloves/latex.rsi"), "icon")),
[EmoteCategory.Vocal] = ("emote-menu-category-vocal",
new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Emotes/vocal.png"))),
};
public void OnStateEntered(GameplayState state) public void OnStateEntered(GameplayState state)
{ {
@@ -135,7 +138,7 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
var whitelistSystem = EntitySystemManager.GetEntitySystem<EntityWhitelistSystem>(); var whitelistSystem = EntitySystemManager.GetEntitySystem<EntityWhitelistSystem>();
var player = _playerManager.LocalSession?.AttachedEntity; var player = _playerManager.LocalSession?.AttachedEntity;
Dictionary<EmoteCategory, List<RadialMenuOption>> emotesByCategory = new(); Dictionary<EmoteCategory, List<RadialMenuOption>> emotesByCategory = new();
foreach (var emote in emotePrototypes) foreach (var emote in emotePrototypes)
{ {
if(emote.Category == EmoteCategory.Invalid) if(emote.Category == EmoteCategory.Invalid)

View File

@@ -12,6 +12,7 @@ using Robust.Client.Player;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers; using Robust.Client.UserInterface.Controllers;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -73,7 +74,8 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
{ {
if (entity.Owner != _player.LocalEntity) if (entity.Owner != _player.LocalEntity)
return; return;
AddHand(name, location); if (_handsSystem.TryGetHand((entity.Owner, entity.Comp), name, out var hand))
AddHand(name, hand.Value);
} }
private void OnRemoveHand(Entity<HandsComponent> entity, string name) private void OnRemoveHand(Entity<HandsComponent> entity, string name)
@@ -139,7 +141,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
_playerHandsComponent = handsComp; _playerHandsComponent = handsComp;
foreach (var (name, hand) in handsComp.Comp.Hands) foreach (var (name, hand) in handsComp.Comp.Hands)
{ {
var handButton = AddHand(name, hand.Location); var handButton = AddHand(name, hand);
if (_handsSystem.TryGetHeldItem(handsComp.AsNullable(), name, out var held) && if (_handsSystem.TryGetHeldItem(handsComp.AsNullable(), name, out var held) &&
_entities.TryGetComponent(held, out VirtualItemComponent? virt)) _entities.TryGetComponent(held, out VirtualItemComponent? virt))
@@ -147,11 +149,25 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
handButton.SetEntity(virt.BlockingEntity); handButton.SetEntity(virt.BlockingEntity);
handButton.Blocked = true; handButton.Blocked = true;
} }
else else if (held != null)
{ {
handButton.SetEntity(held); handButton.SetEntity(held);
handButton.Blocked = false; handButton.Blocked = false;
} }
else
{
if (hand.EmptyRepresentative is { } representative)
{
// placeholder, view it
SetRepresentative(handButton, representative);
}
else
{
// otherwise empty
handButton.SetEntity(null);
}
handButton.Blocked = false;
}
} }
if (handsComp.Comp.ActiveHandId == null) if (handsComp.Comp.ActiveHandId == null)
@@ -159,6 +175,11 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
SetActiveHand(handsComp.Comp.ActiveHandId); SetActiveHand(handsComp.Comp.ActiveHandId);
} }
private void SetRepresentative(HandButton handButton, EntProtoId prototype)
{
handButton.SetPrototype(prototype, true);
}
private void HandBlocked(string handName) private void HandBlocked(string handName)
{ {
if (!_handLookup.TryGetValue(handName, out var hand)) if (!_handLookup.TryGetValue(handName, out var hand))
@@ -203,7 +224,12 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
hand.Blocked = false; hand.Blocked = false;
} }
UpdateHandStatus(hand, entity); if (_playerHandsComponent != null &&
_player.LocalSession?.AttachedEntity is { } playerEntity &&
_handsSystem.TryGetHand((playerEntity, _playerHandsComponent), name, out var handData))
{
UpdateHandStatus(hand, entity, handData);
}
} }
private void OnItemRemoved(string name, EntityUid entity) private void OnItemRemoved(string name, EntityUid entity)
@@ -212,8 +238,19 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
if (hand == null) if (hand == null)
return; return;
if (_playerHandsComponent != null &&
_player.LocalSession?.AttachedEntity is { } playerEntity &&
_handsSystem.TryGetHand((playerEntity, _playerHandsComponent), name, out var handData))
{
UpdateHandStatus(hand, null, handData);
if (handData?.EmptyRepresentative is { } representative)
{
SetRepresentative(hand, representative);
return;
}
}
hand.SetEntity(null); hand.SetEntity(null);
UpdateHandStatus(hand, null);
} }
private HandsContainer GetFirstAvailableContainer() private HandsContainer GetFirstAvailableContainer()
@@ -276,13 +313,13 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
if (foldedLocation == HandUILocation.Left) if (foldedLocation == HandUILocation.Left)
{ {
_statusHandLeft = handControl; _statusHandLeft = handControl;
HandsGui.UpdatePanelEntityLeft(heldEnt); HandsGui.UpdatePanelEntityLeft(heldEnt, hand.Value);
} }
else else
{ {
// Middle or right // Middle or right
_statusHandRight = handControl; _statusHandRight = handControl;
HandsGui.UpdatePanelEntityRight(heldEnt); HandsGui.UpdatePanelEntityRight(heldEnt, hand.Value);
} }
HandsGui.SetHighlightHand(foldedLocation); HandsGui.SetHighlightHand(foldedLocation);
@@ -295,9 +332,9 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
return handControl; return handControl;
} }
private HandButton AddHand(string handName, HandLocation location) private HandButton AddHand(string handName, Hand hand)
{ {
var button = new HandButton(handName, location); var button = new HandButton(handName, hand.Location);
button.StoragePressed += StorageActivate; button.StoragePressed += StorageActivate;
button.Pressed += HandPressed; button.Pressed += HandPressed;
@@ -313,10 +350,16 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
GetFirstAvailableContainer().AddButton(button); GetFirstAvailableContainer().AddButton(button);
} }
if (hand.EmptyRepresentative is { } representative)
{
SetRepresentative(button, representative);
}
UpdateHandStatus(button, null, hand);
// If we don't have a status for this hand type yet, set it. // If we don't have a status for this hand type yet, set it.
// This means we have status filled by default in most scenarios, // This means we have status filled by default in most scenarios,
// otherwise the user'd need to switch hands to "activate" the hands the first time. // otherwise the user'd need to switch hands to "activate" the hands the first time.
if (location.GetUILocation() == HandUILocation.Left) if (hand.Location.GetUILocation() == HandUILocation.Left)
_statusHandLeft ??= button; _statusHandLeft ??= button;
else else
_statusHandRight ??= button; _statusHandRight ??= button;
@@ -480,12 +523,12 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
} }
} }
private void UpdateHandStatus(HandButton hand, EntityUid? entity) private void UpdateHandStatus(HandButton hand, EntityUid? entity, Hand? handData)
{ {
if (hand == _statusHandLeft) if (hand == _statusHandLeft)
HandsGui?.UpdatePanelEntityLeft(entity); HandsGui?.UpdatePanelEntityLeft(entity, handData);
if (hand == _statusHandRight) if (hand == _statusHandRight)
HandsGui?.UpdatePanelEntityRight(entity); HandsGui?.UpdatePanelEntityRight(entity, handData);
} }
} }

View File

@@ -19,14 +19,14 @@ public sealed partial class HotbarGui : UIWidget
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin); LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
} }
public void UpdatePanelEntityLeft(EntityUid? entity) public void UpdatePanelEntityLeft(EntityUid? entity, Hand? hand)
{ {
StatusPanelLeft.Update(entity); StatusPanelLeft.Update(entity, hand);
} }
public void UpdatePanelEntityRight(EntityUid? entity) public void UpdatePanelEntityRight(EntityUid? entity, Hand? hand)
{ {
StatusPanelRight.Update(entity); StatusPanelRight.Update(entity, hand);
} }
public void SetHighlightHand(HandUILocation? hand) public void SetHighlightHand(HandUILocation? hand)

View File

@@ -17,6 +17,7 @@ public sealed partial class ItemStatusPanel : Control
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[ViewVariables] private EntityUid? _entity; [ViewVariables] private EntityUid? _entity;
[ViewVariables] private Hand? _hand;
// Tracked so we can re-run SetSide() if the theme changes. // Tracked so we can re-run SetSide() if the theme changes.
private HandUILocation _side; private HandUILocation _side;
@@ -101,29 +102,45 @@ public sealed partial class ItemStatusPanel : Control
protected override void FrameUpdate(FrameEventArgs args) protected override void FrameUpdate(FrameEventArgs args)
{ {
base.FrameUpdate(args); base.FrameUpdate(args);
UpdateItemName(); UpdateItemName(_hand);
} }
public void Update(EntityUid? entity) public void Update(EntityUid? entity, Hand? hand)
{ {
ItemNameLabel.Visible = entity != null; if (entity == _entity && hand == _hand)
NoItemLabel.Visible = entity == null; return;
_hand = hand;
if (entity == null) if (entity == null)
{ {
ItemNameLabel.Text = "";
ClearOldStatus(); ClearOldStatus();
_entity = null; _entity = null;
if (hand?.EmptyLabel is { } label)
{
ItemNameLabel.Visible = true;
NoItemLabel.Visible = false;
ItemNameLabel.Text = Loc.GetString(label);
}
else
{
ItemNameLabel.Visible = false;
NoItemLabel.Visible = true;
ItemNameLabel.Text = "";
}
return; return;
} }
if (entity != _entity) ItemNameLabel.Visible = true;
{ NoItemLabel.Visible = false;
_entity = entity.Value;
BuildNewEntityStatus();
UpdateItemName(); _entity = entity.Value;
} BuildNewEntityStatus();
UpdateItemName(hand);
} }
public void UpdateHighlight(bool highlight) public void UpdateHighlight(bool highlight)
@@ -131,14 +148,14 @@ public sealed partial class ItemStatusPanel : Control
HighlightPanel.Visible = highlight; HighlightPanel.Visible = highlight;
} }
private void UpdateItemName() private void UpdateItemName(Hand? hand)
{ {
if (_entity == null) if (_entity == null)
return; return;
if (!_entityManager.TryGetComponent<MetaDataComponent>(_entity, out var meta) || meta.Deleted) if (!_entityManager.TryGetComponent<MetaDataComponent>(_entity, out var meta) || meta.Deleted)
{ {
Update(null); Update(null, hand);
return; return;
} }

View File

@@ -194,8 +194,6 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
} }
} }
return;
int GetIndex(Vector2i position) int GetIndex(Vector2i position)
{ {
return position.Y * maxWidth + position.X; return position.Y * maxWidth + position.X;

View File

@@ -185,12 +185,7 @@ public sealed class ItemGridPiece : Control, IEntityControl
handle.SetTransform(pos, iconRotation); handle.SetTransform(pos, iconRotation);
var box = new UIBox2(root, root + sprite.Size * scale); var box = new UIBox2(root, root + sprite.Size * scale);
handle.DrawTextureRect(sprite, box);
var ev = new BeforeRenderInGridEvent(new Color(255, 255, 255));
_entityManager.EventBus.RaiseLocalEvent(Entity, ev);
handle.DrawTextureRect(sprite, box, ev.Color);
handle.SetTransform(GlobalPixelPosition, Angle.Zero); handle.SetTransform(GlobalPixelPosition, Angle.Zero);
} }
else else
@@ -303,19 +298,6 @@ public sealed class ItemGridPiece : Control, IEntityControl
public EntityUid? UiEntity => Entity; public EntityUid? UiEntity => Entity;
} }
/// <summary>
/// This event gets raised before a sprite gets drawn in a grid and lets to change the sprite color for several gameobjects that have special sprites to render in containers.
/// </summary>
public sealed class BeforeRenderInGridEvent : EntityEventArgs
{
public Color Color { get; set; }
public BeforeRenderInGridEvent(Color color)
{
Color = color;
}
}
public enum ItemGridPieceMarks public enum ItemGridPieceMarks
{ {
First, First,

View File

@@ -5,4 +5,4 @@
// https://github.com/dotnet/runtime/issues/107197 // https://github.com/dotnet/runtime/issues/107197
// So we can't really parallelize integration tests harder either until the runtime fixes that, // So we can't really parallelize integration tests harder either until the runtime fixes that,
// *or* we fix serv3 to not spam expression trees. // *or* we fix serv3 to not spam expression trees.
[assembly: LevelOfParallelism(3)] [assembly: LevelOfParallelism(2)]

View File

@@ -0,0 +1,29 @@
using Content.Shared.Damage.Components;
namespace Content.IntegrationTests.Tests.Damageable;
public sealed class StaminaComponentTest
{
[Test]
public async Task ValidatePrototypes()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var protos = pair.GetPrototypesWithComponent<StaminaComponent>();
await server.WaitAssertion(() =>
{
Assert.Multiple(() =>
{
foreach (var (proto, comp) in protos)
{
Assert.That(comp.AnimationThreshold, Is.LessThan(comp.CritThreshold),
$"Animation threshold on {proto.ID} must be less than its crit threshold.");
}
});
});
await pair.CleanReturnAsync();
}
}

View File

@@ -1,11 +1,9 @@
#nullable enable #nullable enable
using Content.Server.Cuffs; using Content.Server.Cuffs;
using Content.Shared.Body.Components;
using Content.Shared.Cuffs.Components; using Content.Shared.Cuffs.Components;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Robust.Server.Console; using Robust.Server.Console;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
{ {
@@ -22,9 +20,15 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
components: components:
- type: Cuffable - type: Cuffable
- type: Hands - type: Hands
hands:
hand_right:
location: Right
hand_left:
location: Left
sortedHands:
- hand_right
- hand_left
- type: ComplexInteraction - type: ComplexInteraction
- type: Body
prototype: Human
- type: entity - type: entity
name: HandcuffsDummy name: HandcuffsDummy
@@ -47,7 +51,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
HandsComponent hands = default!; HandsComponent hands = default!;
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var host = server.ResolveDependency<IServerConsoleHost>(); var host = server.ResolveDependency<IServerConsoleHost>();
var map = await pair.CreateTestMap(); var map = await pair.CreateTestMap();
@@ -73,7 +76,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
{ {
Assert.That(entityManager.TryGetComponent(human, out cuffed!), $"Human has no {nameof(CuffableComponent)}"); Assert.That(entityManager.TryGetComponent(human, out cuffed!), $"Human has no {nameof(CuffableComponent)}");
Assert.That(entityManager.TryGetComponent(human, out hands!), $"Human has no {nameof(HandsComponent)}"); Assert.That(entityManager.TryGetComponent(human, out hands!), $"Human has no {nameof(HandsComponent)}");
Assert.That(entityManager.TryGetComponent(human, out BodyComponent? _), $"Human has no {nameof(BodyComponent)}");
Assert.That(entityManager.TryGetComponent(cuffs, out HandcuffComponent? _), $"Handcuff has no {nameof(HandcuffComponent)}"); Assert.That(entityManager.TryGetComponent(cuffs, out HandcuffComponent? _), $"Handcuff has no {nameof(HandcuffComponent)}");
Assert.That(entityManager.TryGetComponent(secondCuffs, out HandcuffComponent? _), $"Second handcuffs has no {nameof(HandcuffComponent)}"); Assert.That(entityManager.TryGetComponent(secondCuffs, out HandcuffComponent? _), $"Second handcuffs has no {nameof(HandcuffComponent)}");
}); });

View File

@@ -9,7 +9,6 @@ using Content.Server.Mind;
using Content.Server.Roles; using Content.Server.Roles;
using Content.Server.RoundEnd; using Content.Server.RoundEnd;
using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Components;
using Content.Server.Station.Components;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
@@ -20,6 +19,7 @@ using Content.Shared.NPC.Prototypes;
using Content.Shared.NPC.Systems; using Content.Shared.NPC.Systems;
using Content.Shared.NukeOps; using Content.Shared.NukeOps;
using Content.Shared.Pinpointer; using Content.Shared.Pinpointer;
using Content.Shared.Roles.Components;
using Content.Shared.Station.Components; using Content.Shared.Station.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;

View File

@@ -3,7 +3,6 @@ using System.Linq;
using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles;
using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Components;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Roles;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
@@ -11,7 +10,7 @@ using Content.Shared.Mind;
using Content.Shared.Mind.Components; using Content.Shared.Mind.Components;
using Content.Shared.Players; using Content.Shared.Players;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Roles.Jobs; using Content.Shared.Roles.Components;
using Robust.Server.Console; using Robust.Server.Console;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;

View File

@@ -1,7 +1,5 @@
using System.Linq; using System.Linq;
using Content.Server.Roles; using Content.Shared.Roles.Components;
using Content.Shared.Roles;
using Content.Shared.Roles.Jobs;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Reflection; using Robust.Shared.Reflection;

View File

@@ -1,6 +1,6 @@
using Content.IntegrationTests.Tests.Interaction; using Content.IntegrationTests.Tests.Interaction;
using Content.Server.Explosion.Components; using Content.Shared.Trigger.Components;
using Content.Shared.Explosion.Components; using Content.Shared.Trigger.Systems;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -25,19 +25,19 @@ public sealed class ModularGrenadeTests : InteractionTest
await InteractUsing(Cable); await InteractUsing(Cable);
// Insert & remove trigger // Insert & remove trigger
AssertComp<OnUseTimerTriggerComponent>(false); AssertComp<TimerTriggerComponent>(false);
await InteractUsing(Trigger); await InteractUsing(Trigger);
AssertComp<OnUseTimerTriggerComponent>(); AssertComp<TimerTriggerComponent>();
await FindEntity(Trigger, LookupFlags.Uncontained, shouldSucceed: false); await FindEntity(Trigger, LookupFlags.Uncontained, shouldSucceed: false);
await InteractUsing(Pry); await InteractUsing(Pry);
AssertComp<OnUseTimerTriggerComponent>(false); AssertComp<TimerTriggerComponent>(false);
// Trigger was dropped to floor, not deleted. // Trigger was dropped to floor, not deleted.
await FindEntity(Trigger, LookupFlags.Uncontained); await FindEntity(Trigger, LookupFlags.Uncontained);
// Re-insert // Re-insert
await InteractUsing(Trigger); await InteractUsing(Trigger);
AssertComp<OnUseTimerTriggerComponent>(); AssertComp<TimerTriggerComponent>();
// Insert & remove payload. // Insert & remove payload.
await InteractUsing(Payload); await InteractUsing(Payload);
@@ -56,13 +56,14 @@ public sealed class ModularGrenadeTests : InteractionTest
await Pickup(); await Pickup();
AssertComp<ActiveTimerTriggerComponent>(false); AssertComp<ActiveTimerTriggerComponent>(false);
await UseInHand(); await UseInHand();
AssertComp<ActiveTimerTriggerComponent>(true);
// So uhhh grenades in hands don't destroy themselves when exploding. Maybe that will be fixed eventually. // So uhhh grenades in hands don't destroy themselves when exploding. Maybe that will be fixed eventually.
await Drop(); await Drop();
// Wait until grenade explodes // Wait until grenade explodes
var timer = Comp<ActiveTimerTriggerComponent>(); var triggerSys = SEntMan.System<TriggerSystem>();
while (timer.TimeRemaining >= 0) while (Target != null && triggerSys.GetRemainingTime(SEntMan.GetEntity(Target.Value))?.TotalSeconds >= 0.0)
{ {
await RunTicks(10); await RunTicks(10);
} }

View File

@@ -2,9 +2,9 @@ using System.Linq;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Systems; using Content.Server.Shuttles.Systems;
using Content.Server.Station.Components;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.Components;
using Content.Shared.Station.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;

View File

@@ -128,7 +128,7 @@ public sealed class StoreTests
var buyMsg = new StoreBuyListingMessage(discountedListingItem.ID){Actor = human}; var buyMsg = new StoreBuyListingMessage(discountedListingItem.ID){Actor = human};
server.EntMan.EventBus.RaiseComponentEvent(pda, storeComponent, buyMsg); server.EntMan.EventBus.RaiseLocalEvent(pda, buyMsg);
var newBalance = storeComponent.Balance[UplinkSystem.TelecrystalCurrencyPrototype]; var newBalance = storeComponent.Balance[UplinkSystem.TelecrystalCurrencyPrototype];
Assert.That(newBalance.Value, Is.EqualTo((originalBalance - plainDiscountedCost).Value), "Expected to have balance reduced by discounted cost"); Assert.That(newBalance.Value, Is.EqualTo((originalBalance - plainDiscountedCost).Value), "Expected to have balance reduced by discounted cost");
@@ -141,7 +141,7 @@ public sealed class StoreTests
Assert.That(costAfterBuy.Value, Is.EqualTo(prototypeCost.Value), "Expected cost after discount refund to be equal to prototype cost."); Assert.That(costAfterBuy.Value, Is.EqualTo(prototypeCost.Value), "Expected cost after discount refund to be equal to prototype cost.");
var refundMsg = new StoreRequestRefundMessage { Actor = human }; var refundMsg = new StoreRequestRefundMessage { Actor = human };
server.EntMan.EventBus.RaiseComponentEvent(pda, storeComponent, refundMsg); server.EntMan.EventBus.RaiseLocalEvent(pda, refundMsg);
// get refreshed item after refund re-generated items // get refreshed item after refund re-generated items
discountedListingItem = storeComponent.FullListingsCatalog.First(x => x.ID == itemId); discountedListingItem = storeComponent.FullListingsCatalog.First(x => x.ID == itemId);

View File

@@ -13,6 +13,7 @@ using Content.Server.Clothing.Systems;
using Content.Server.Implants; using Content.Server.Implants;
using Content.Shared.Implants; using Content.Shared.Implants;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Lock;
using Content.Shared.PDA; using Content.Shared.PDA;
namespace Content.Server.Access.Systems namespace Content.Server.Access.Systems
@@ -25,6 +26,7 @@ namespace Content.Server.Access.Systems
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ChameleonClothingSystem _chameleon = default!; [Dependency] private readonly ChameleonClothingSystem _chameleon = default!;
[Dependency] private readonly ChameleonControllerSystem _chamController = default!; [Dependency] private readonly ChameleonControllerSystem _chamController = default!;
[Dependency] private readonly LockSystem _lock = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -79,7 +81,8 @@ namespace Content.Server.Access.Systems
private void OnAfterInteract(EntityUid uid, AgentIDCardComponent component, AfterInteractEvent args) private void OnAfterInteract(EntityUid uid, AgentIDCardComponent component, AfterInteractEvent args)
{ {
if (args.Target == null || !args.CanReach || !TryComp<AccessComponent>(args.Target, out var targetAccess) || !HasComp<IdCardComponent>(args.Target)) if (args.Target == null || !args.CanReach || _lock.IsLocked(uid) ||
!TryComp<AccessComponent>(args.Target, out var targetAccess) || !HasComp<IdCardComponent>(args.Target))
return; return;
if (!TryComp<AccessComponent>(uid, out var access) || !HasComp<IdCardComponent>(uid)) if (!TryComp<AccessComponent>(uid, out var access) || !HasComp<IdCardComponent>(uid))

View File

@@ -44,7 +44,7 @@ public sealed class VariantizeCommand : IConsoleCommand
foreach (var tile in mapsSystem.GetAllTiles(euid.Value, gridComp)) foreach (var tile in mapsSystem.GetAllTiles(euid.Value, gridComp))
{ {
var def = turfSystem.GetContentTileDefinition(tile); var def = turfSystem.GetContentTileDefinition(tile);
var newTile = new Tile(tile.Tile.TypeId, tile.Tile.Flags, tileSystem.PickVariant(def)); var newTile = new Tile(tile.Tile.TypeId, tile.Tile.Flags, tileSystem.PickVariant(def), tile.Tile.RotationMirroring);
mapsSystem.SetTile(euid.Value, gridComp, tile.GridIndices, newTile); mapsSystem.SetTile(euid.Value, gridComp, tile.GridIndices, newTile);
} }
} }

View File

@@ -1,6 +1,5 @@
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Server.Warps;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Follower; using Content.Shared.Follower;
using Content.Shared.Ghost; using Content.Shared.Ghost;

View File

@@ -1,10 +0,0 @@
using Content.Shared.Administration.Components;
using Robust.Shared.GameStates;
namespace Content.Server.Administration.Components;
[RegisterComponent]
public sealed partial class HeadstandComponent : SharedHeadstandComponent
{
}

View File

@@ -1,7 +1,6 @@
using System.Linq; using System.Linq;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.Forensics;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Hands.Systems; using Content.Server.Hands.Systems;
using Content.Server.Mind; using Content.Server.Mind;
@@ -21,6 +20,7 @@ using Content.Shared.PDA;
using Content.Shared.Players.PlayTimeTracking; using Content.Shared.Players.PlayTimeTracking;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Roles.Components;
using Content.Shared.Roles.Jobs; using Content.Shared.Roles.Jobs;
using Content.Shared.StationRecords; using Content.Shared.StationRecords;
using Content.Shared.Throwing; using Content.Shared.Throwing;

View File

@@ -29,15 +29,16 @@ public sealed partial class AdminVerbSystem
private static readonly EntProtoId DefaultNukeOpRule = "LoneOpsSpawn"; private static readonly EntProtoId DefaultNukeOpRule = "LoneOpsSpawn";
private static readonly EntProtoId DefaultRevsRule = "Revolutionary"; private static readonly EntProtoId DefaultRevsRule = "Revolutionary";
private static readonly EntProtoId DefaultThiefRule = "Thief"; private static readonly EntProtoId DefaultThiefRule = "Thief";
private static readonly EntProtoId DefaultChangelingRule = "Changeling";
private static readonly EntProtoId ParadoxCloneRuleId = "ParadoxCloneSpawn";
private static readonly ProtoId<StartingGearPrototype> PirateGearId = "PirateGear"; private static readonly ProtoId<StartingGearPrototype> PirateGearId = "PirateGear";
//CP14 //CP14
private static readonly EntProtoId CP14VampireUnnameable = "CP14GameRuleVampireClanUnnameable"; private static readonly EntProtoId CP14VampireUnnameable = "CP14GameRuleVampireClanUnnameable";
private static readonly EntProtoId CP14VampireDevourers = "CP14GameRuleVampireClanDevourers"; private static readonly EntProtoId CP14VampireDevourers = "CP14GameRuleVampireClanDevourers";
private static readonly EntProtoId CP14VampireNightChildrens = "CP14GameRuleVampireClanNightChildrens"; private static readonly EntProtoId CP14VampireNightChildrens = "CP14GameRuleVampireClanNightChildrens";
//CP14 end //CP14 end
private static readonly EntProtoId ParadoxCloneRuleId = "ParadoxCloneSpawn";
// All antag verbs have names so invokeverb works. // All antag verbs have names so invokeverb works.
private void AddAntagVerbs(GetVerbsEvent<Verb> args) private void AddAntagVerbs(GetVerbsEvent<Verb> args)
{ {
@@ -108,7 +109,7 @@ public sealed partial class AdminVerbSystem
_antag.ForceMakeAntag<TraitorRuleComponent>(targetPlayer, DefaultTraitorRule); _antag.ForceMakeAntag<TraitorRuleComponent>(targetPlayer, DefaultTraitorRule);
}, },
Impact = LogImpact.High, Impact = LogImpact.High,
Message = string.Join(": ", traitorName, Loc.GetString("admin-verb-make-traitor")), Message = string.Join(": ", traitorName, Loc.GetString("admin-verb-make-traitor")),
}; };
args.Verbs.Add(traitor); args.Verbs.Add(traitor);
@@ -203,6 +204,21 @@ public sealed partial class AdminVerbSystem
}; };
args.Verbs.Add(thief); args.Verbs.Add(thief);
var changelingName = Loc.GetString("admin-verb-text-make-changeling");
Verb changeling = new()
{
Text = changelingName,
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Objects/Weapons/Melee/armblade.rsi"), "icon"),
Act = () =>
{
_antag.ForceMakeAntag<ChangelingRuleComponent>(targetPlayer, DefaultChangelingRule);
},
Impact = LogImpact.High,
Message = string.Join(": ", changelingName, Loc.GetString("admin-verb-make-changeling")),
};
args.Verbs.Add(changeling);
var paradoxCloneName = Loc.GetString("admin-verb-text-make-paradox-clone"); var paradoxCloneName = Loc.GetString("admin-verb-text-make-paradox-clone");
Verb paradox = new() Verb paradox = new()
{ {

View File

@@ -2,15 +2,12 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Server.Administration.Components; using Content.Server.Administration.Components;
using Content.Server.Atmos;
using Content.Server.Atmos.Components;
using Content.Server.Cargo.Components; using Content.Server.Cargo.Components;
using Content.Server.Doors.Systems; using Content.Server.Doors.Systems;
using Content.Server.Hands.Systems; using Content.Server.Hands.Systems;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Server.Stack; using Content.Server.Stack;
using Content.Server.Station.Components;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Server.Weapons.Ranged.Systems; using Content.Server.Weapons.Ranged.Systems;
using Content.Shared.Access; using Content.Shared.Access;
@@ -28,6 +25,7 @@ using Content.Shared.Hands.Components;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.PDA; using Content.Shared.PDA;
using Content.Shared.Stacks; using Content.Shared.Stacks;
using Content.Shared.Station.Components;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Components;
using Robust.Server.Physics; using Robust.Server.Physics;

View File

@@ -1,33 +0,0 @@
using Content.Server.AlertLevel.Systems;
namespace Content.Server.AlertLevel;
/// <summary>
/// This component is for changing the alert level of the station when triggered.
/// </summary>
[RegisterComponent, Access(typeof(AlertLevelChangeOnTriggerSystem))]
public sealed partial class AlertLevelChangeOnTriggerComponent : Component
{
///<summary>
///The alert level to change to when triggered.
///</summary>
[DataField]
public string Level = "blue";
/// <summary>
///Whether to play the sound when the alert level changes.
/// </summary>
[DataField]
public bool PlaySound = true;
/// <summary>
///Whether to say the announcement when the alert level changes.
/// </summary>
[DataField]
public bool Announce = true;
/// <summary>
///Force the alert change. This applies if the alert level is not selectable or not.
/// </summary>
[DataField]
public bool Force = false;
}

View File

@@ -5,13 +5,13 @@ using Content.Server.Animals.Components;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Radio; using Content.Server.Radio;
using Content.Server.Speech;
using Content.Server.Speech.Components;
using Content.Server.Vocalization.Systems; using Content.Server.Vocalization.Systems;
using Content.Shared.Animals.Components;
using Content.Shared.Animals.Systems;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Popups; using Content.Shared.Speech;
using Content.Shared.Verbs; using Content.Shared.Speech.Components;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -25,7 +25,7 @@ namespace Content.Server.Animals.Systems;
/// (radiovocalizer) and stores them in a list. When an entity with a VocalizerComponent attempts to vocalize, this will /// (radiovocalizer) and stores them in a list. When an entity with a VocalizerComponent attempts to vocalize, this will
/// try to set the message from memory. /// try to set the message from memory.
/// </summary> /// </summary>
public sealed partial class ParrotMemorySystem : EntitySystem public sealed partial class ParrotMemorySystem : SharedParrotMemorySystem
{ {
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
@@ -42,8 +42,6 @@ public sealed partial class ParrotMemorySystem : EntitySystem
SubscribeLocalEvent<EraseEvent>(OnErase); SubscribeLocalEvent<EraseEvent>(OnErase);
SubscribeLocalEvent<ParrotMemoryComponent, GetVerbsEvent<Verb>>(OnGetVerbs);
SubscribeLocalEvent<ParrotListenerComponent, MapInitEvent>(ListenerOnMapInit); SubscribeLocalEvent<ParrotListenerComponent, MapInitEvent>(ListenerOnMapInit);
SubscribeLocalEvent<ParrotListenerComponent, ListenEvent>(OnListen); SubscribeLocalEvent<ParrotListenerComponent, ListenEvent>(OnListen);
@@ -57,30 +55,6 @@ public sealed partial class ParrotMemorySystem : EntitySystem
DeletePlayerMessages(args.PlayerNetUserId); DeletePlayerMessages(args.PlayerNetUserId);
} }
private void OnGetVerbs(Entity<ParrotMemoryComponent> entity, ref GetVerbsEvent<Verb> args)
{
var user = args.User;
// limit this to admins
if (!_admin.IsAdmin(user))
return;
// simple verb that just clears the memory list
var clearMemoryVerb = new Verb()
{
Text = Loc.GetString("parrot-verb-clear-memory"),
Category = VerbCategory.Admin,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/clear-parrot.png")),
Act = () =>
{
entity.Comp.SpeechMemories.Clear();
_popup.PopupEntity(Loc.GetString("parrot-popup-memory-cleared"), entity, user, PopupType.Medium);
},
};
args.Verbs.Add(clearMemoryVerb);
}
private void ListenerOnMapInit(Entity<ParrotListenerComponent> entity, ref MapInitEvent args) private void ListenerOnMapInit(Entity<ParrotListenerComponent> entity, ref MapInitEvent args)
{ {
// If an entity has a ParrotListenerComponent it really ought to have an ActiveListenerComponent // If an entity has a ParrotListenerComponent it really ought to have an ActiveListenerComponent

View File

@@ -1,6 +1,5 @@
using Content.Server.Anomaly.Components; using Content.Server.Anomaly.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Server.Station.Components;
using Content.Shared.Anomaly; using Content.Shared.Anomaly;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Materials; using Content.Shared.Materials;
@@ -164,8 +163,7 @@ public sealed partial class AnomalySystem
var xform = Transform(uid); var xform = Transform(uid);
if (_station.GetStationInMap(xform.MapID) is not { } station || if (_station.GetStationInMap(xform.MapID) is not { } station ||
!TryComp<StationDataComponent>(station, out var data) || _station.GetLargestGrid(station) is not { } grid)
_station.GetLargestGrid(data) is not { } grid)
{ {
if (xform.GridUid == null) if (xform.GridUid == null)
return; return;

View File

@@ -1,22 +1,19 @@
using Content.Shared.Atmos.EntitySystems; using Content.Server.Atmos.Piping.EntitySystems;
using Robust.Shared.GameStates;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Content.Shared.Atmos.Components; namespace Content.Server.Atmos.Piping.Components;
[RegisterComponent, NetworkedComponent] [RegisterComponent]
[AutoGenerateComponentState]
public sealed partial class AtmosPipeColorComponent : Component public sealed partial class AtmosPipeColorComponent : Component
{ {
[DataField] [DataField]
[AutoNetworkedField]
public Color Color { get; set; } = Color.White; public Color Color { get; set; } = Color.White;
[ViewVariables(VVAccess.ReadWrite), UsedImplicitly] [ViewVariables(VVAccess.ReadWrite), UsedImplicitly]
public Color ColorVV public Color ColorVV
{ {
get => Color; get => Color;
set => IoCManager.Resolve<IEntityManager>().System<AtmosPipeColorSystem>().SetColor((Owner, this), value); set => IoCManager.Resolve<IEntityManager>().System<AtmosPipeColorSystem>().SetColor(Owner, this, value);
} }
} }

View File

@@ -0,0 +1,48 @@
using Content.Server.Atmos.Piping.Components;
using Content.Shared.Atmos.Piping;
using Robust.Server.GameObjects;
namespace Content.Server.Atmos.Piping.EntitySystems
{
public sealed class AtmosPipeColorSystem : EntitySystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AtmosPipeColorComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<AtmosPipeColorComponent, ComponentShutdown>(OnShutdown);
}
private void OnStartup(EntityUid uid, AtmosPipeColorComponent component, ComponentStartup args)
{
if (!TryComp(uid, out AppearanceComponent? appearance))
return;
_appearance.SetData(uid, PipeColorVisuals.Color, component.Color, appearance);
}
private void OnShutdown(EntityUid uid, AtmosPipeColorComponent component, ComponentShutdown args)
{
if (!TryComp(uid, out AppearanceComponent? appearance))
return;
_appearance.SetData(uid, PipeColorVisuals.Color, Color.White, appearance);
}
public void SetColor(EntityUid uid, AtmosPipeColorComponent component, Color color)
{
component.Color = color;
if (!TryComp(uid, out AppearanceComponent? appearance))
return;
_appearance.SetData(uid, PipeColorVisuals.Color, color, appearance);
var ev = new AtmosPipeColorChangedEvent(color);
RaiseLocalEvent(uid, ref ev);
}
}
}

View File

@@ -1,47 +1,46 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Ghost.Components; using Content.Server.Ghost.Components;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events; using Content.Shared.Body.Events;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Mind.Components; using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Pointing; using Content.Shared.Pointing;
namespace Content.Server.Body.Systems namespace Content.Server.Body.Systems;
public sealed class BrainSystem : EntitySystem
{ {
public sealed class BrainSystem : EntitySystem [Dependency] private readonly SharedMindSystem _mindSystem = default!;
public override void Initialize()
{ {
[Dependency] private readonly SharedMindSystem _mindSystem = default!; base.Initialize();
public override void Initialize() SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
{ SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
base.Initialize(); SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
}
SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid)); private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody)); {
SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt); if (TerminatingOrDeleted(newEntity) || TerminatingOrDeleted(oldEntity))
} return;
private void HandleMind(EntityUid newEntity, EntityUid oldEntity) EnsureComp<MindContainerComponent>(newEntity);
{ EnsureComp<MindContainerComponent>(oldEntity);
if (TerminatingOrDeleted(newEntity) || TerminatingOrDeleted(oldEntity))
return;
EnsureComp<MindContainerComponent>(newEntity); var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity);
EnsureComp<MindContainerComponent>(oldEntity); ghostOnMove.MustBeDead = HasComp<MobStateComponent>(newEntity); // Don't ghost living players out of their bodies.
var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity); if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind))
if (HasComp<BodyComponent>(newEntity)) return;
ghostOnMove.MustBeDead = true;
if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind)) _mindSystem.TransferTo(mindId, newEntity, mind: mind);
return; }
_mindSystem.TransferTo(mindId, newEntity, mind: mind); private void OnPointAttempt(Entity<BrainComponent> ent, ref PointAttemptEvent args)
} {
args.Cancel();
private void OnPointAttempt(Entity<BrainComponent> ent, ref PointAttemptEvent args)
{
args.Cancel();
}
} }
} }

View File

@@ -1,8 +1,8 @@
using System.Linq; using System.Linq;
using Content.Server.Station.Components;
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Prototypes; using Content.Shared.Cargo.Prototypes;
using Content.Shared.Station.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Server.Cargo.Components; namespace Content.Server.Cargo.Components;

View File

@@ -1,7 +1,6 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Server.Cargo.Components; using Content.Server.Cargo.Components;
using Content.Server.Station.Components;
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Content.Shared.Cargo.BUI; using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
@@ -13,8 +12,8 @@ using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Labels.Components; using Content.Shared.Labels.Components;
using Content.Shared.Paper; using Content.Shared.Paper;
using Content.Shared.Station.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;

View File

@@ -3,11 +3,11 @@ using System.Linq;
using Content.Server.Cargo.Components; using Content.Server.Cargo.Components;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Server.Station.Components;
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
using Content.Shared.DeviceLinking; using Content.Shared.DeviceLinking;
using Content.Shared.Power; using Content.Shared.Power;
using Content.Shared.Station.Components;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;

View File

@@ -92,19 +92,22 @@ public sealed class PricingSystem : EntitySystem
if (args.Handled) if (args.Handled)
return; return;
if (!TryComp<BodyComponent>(uid, out var body) || !TryComp<MobStateComponent>(uid, out var state)) if (!TryComp<MobStateComponent>(uid, out var state))
{ {
Log.Error($"Tried to get the mob price of {ToPrettyString(uid)}, which has no {nameof(BodyComponent)} and no {nameof(MobStateComponent)}."); Log.Error($"Tried to get the mob price of {ToPrettyString(uid)}, which has no {nameof(MobStateComponent)}.");
return; return;
} }
// TODO: Better handling of missing. var partPenalty = 0.0;
var partList = _bodySystem.GetBodyChildren(uid, body).ToList(); if (TryComp<BodyComponent>(uid, out var body))
var totalPartsPresent = partList.Sum(_ => 1); {
var totalParts = partList.Count; var partList = _bodySystem.GetBodyChildren(uid, body).ToList();
var totalPartsPresent = partList.Sum(_ => 1);
var totalParts = partList.Count;
var partRatio = totalPartsPresent / (double) totalParts; var partRatio = totalPartsPresent / (double) totalParts;
var partPenalty = component.Price * (1 - partRatio) * component.MissingBodyPartPenalty; partPenalty = component.Price * (1 - partRatio) * component.MissingBodyPartPenalty;
}
args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty); args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty);
} }

View File

@@ -1,17 +0,0 @@
using Content.Shared.Dataset;
using Robust.Shared.Prototypes;
namespace Content.Server.Chat;
/// <summary>
/// Makes the entity speak when triggered. If the item has UseDelay component, the system will respect that cooldown.
/// </summary>
[RegisterComponent]
public sealed partial class SpeakOnTriggerComponent : Component
{
/// <summary>
/// The identifier for the dataset prototype containing messages to be spoken by this entity.
/// </summary>
[DataField(required: true)]
public ProtoId<LocalizedDatasetPrototype> Pack = string.Empty;
}

View File

@@ -67,6 +67,9 @@ public sealed class SuicideSystem : EntitySystem
if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, CannotSuicideTag)) if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, CannotSuicideTag))
return false; return false;
// TODO: fix this
// This is a handled event, but the result is never used
// It looks like TriggerOnMobstateChange is supposed to prevent you from suiciding
var suicideEvent = new SuicideEvent(victim); var suicideEvent = new SuicideEvent(victim);
RaiseLocalEvent(victim, suicideEvent); RaiseLocalEvent(victim, suicideEvent);

View File

@@ -0,0 +1,105 @@
using Content.Server.Chat.Managers;
using Content.Shared.Chat;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Mind;
using Content.Shared.Roles;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.Chat.Systems;
/// <summary>
/// This system is used to notify specific players of the occurance of predefined events.
/// </summary>
public sealed partial class ChatNotificationSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IChatManager _chats = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ILogManager _logManager = default!;
private ISawmill _sawmill = default!;
// The following data does not need to be saved
// Local cache for rate limiting chat notifications by source
// (Recipient, ChatNotification) -> Dictionary<Source, next allowed TOA>
private readonly Dictionary<(EntityUid, ProtoId<ChatNotificationPrototype>), Dictionary<EntityUid, TimeSpan>> _chatNotificationsBySource = new();
// Local cache for rate limiting chat notifications by type
// (Recipient, ChatNotification) -> next allowed TOA
private readonly Dictionary<(EntityUid, ProtoId<ChatNotificationPrototype>), TimeSpan> _chatNotificationsByType = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ActorComponent, ChatNotificationEvent>(OnChatNotification);
_sawmill = _logManager.GetSawmill("chatnotification");
}
/// <summary>
/// Triggered when the specified player recieves a chat notification event.
/// </summary>
/// <param name="ent">The player receiving the chat notification.</param>
/// <param name="args">The chat notification event</param>
public void OnChatNotification(Entity<ActorComponent> ent, ref ChatNotificationEvent args)
{
if (!_proto.TryIndex(args.ChatNotification, out var chatNotification))
{
_sawmill.Warning("Attempted to index ChatNotificationPrototype " + args.ChatNotification + " but the prototype does not exist.");
return;
}
var source = args.Source;
var playerNotification = (ent, args.ChatNotification);
// Exit without notifying the player if we received a notification before the appropriate time has elasped
if (chatNotification.NotifyBySource)
{
if (!_chatNotificationsBySource.TryGetValue(playerNotification, out var trackedSources))
trackedSources = new();
trackedSources.TryGetValue(source, out var timeSpan);
trackedSources[source] = _timing.CurTime + chatNotification.NextDelay;
_chatNotificationsBySource[playerNotification] = trackedSources;
if (_timing.CurTime < timeSpan)
return;
}
else
{
_chatNotificationsByType.TryGetValue(playerNotification, out var timeSpan);
_chatNotificationsByType[playerNotification] = _timing.CurTime + chatNotification.NextDelay;
if (_timing.CurTime < timeSpan)
return;
}
var sourceName = args.SourceNameOverride ?? Name(source);
var userName = args.UserNameOverride ?? (args.User.HasValue ? Name(args.User.Value) : string.Empty);
var targetName = Name(ent);
var message = Loc.GetString(chatNotification.Message, ("source", sourceName), ("user", userName), ("target", targetName));
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chats.ChatMessageToOne(
ChatChannel.Notifications,
message,
wrappedMessage,
default,
false,
ent.Comp.PlayerSession.Channel,
colorOverride: chatNotification.Color
);
if (chatNotification.Sound != null && _mind.TryGetMind(ent, out var mindId, out _))
_roles.MindPlaySound(mindId, chatNotification.Sound);
}
}

View File

@@ -9,7 +9,6 @@ using Content.Server.GameTicking;
using Content.Server.Speech.Prototypes; using Content.Server.Speech.Prototypes;
using Content.Server.Speech.EntitySystems; using Content.Server.Speech.EntitySystems;
using Content.Server.Speech.Prototypes; using Content.Server.Speech.Prototypes;
using Content.Server.Station.Components;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Administration; using Content.Shared.Administration;
@@ -23,6 +22,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Players; using Content.Shared.Players;
using Content.Shared.Players.RateLimiting; using Content.Shared.Players.RateLimiting;
using Content.Shared.Radio; using Content.Shared.Radio;
using Content.Shared.Station.Components;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Audio; using Robust.Shared.Audio;
@@ -62,11 +62,6 @@ public sealed partial class ChatSystem : SharedChatSystem
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly ExamineSystemShared _examineSystem = default!; [Dependency] private readonly ExamineSystemShared _examineSystem = default!;
public const int VoiceRange = 10; // how far voice goes in world units
public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units
public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units
public const string DefaultAnnouncementSound = "/Audio/_CP14/Announce/event_boom.ogg"; //CP14 replaced default sound
private bool _loocEnabled = true; private bool _loocEnabled = true;
private bool _deadLoocEnabled; private bool _deadLoocEnabled;
private bool _critLoocEnabled; private bool _critLoocEnabled;
@@ -194,13 +189,6 @@ public sealed partial class ChatSystem : SharedChatSystem
if (!CanSendInGame(message, shell, player)) if (!CanSendInGame(message, shell, player))
return; return;
//CP14 Prevent god from default speaking. In waiting of chatcode refactor
var ev = new CP14SpokeAttemptEvent(message, desiredType, player);
RaiseLocalEvent(source, ev);
if (ev.Cancelled)
return;
//CP14 end
ignoreActionBlocker = CheckIgnoreSpeechBlocker(source, ignoreActionBlocker); ignoreActionBlocker = CheckIgnoreSpeechBlocker(source, ignoreActionBlocker);
// this method is a disaster // this method is a disaster

View File

@@ -1,11 +1,16 @@
using Content.Server.Forensics; using Content.Server.Forensics;
using Content.Server.Speech.EntitySystems;
using Content.Shared.Cloning.Events; using Content.Shared.Cloning.Events;
using Content.Shared.Clothing.Components;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
using Content.Shared.Labels.Components; using Content.Shared.Labels.Components;
using Content.Shared.Labels.EntitySystems; using Content.Shared.Labels.EntitySystems;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Paper; using Content.Shared.Paper;
using Content.Shared.Stacks; using Content.Shared.Stacks;
using Content.Shared.Speech.Components;
using Content.Shared.Storage;
using Content.Shared.Store; using Content.Shared.Store;
using Content.Shared.Store.Components; using Content.Shared.Store.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -13,47 +18,58 @@ using Robust.Shared.Prototypes;
namespace Content.Server.Cloning; namespace Content.Server.Cloning;
/// <summary> /// <summary>
/// The part of item cloning responsible for copying over important components. /// The part of item cloning responsible for copying over important components.
/// This is used for <see cref="CopyItem"/>.
/// Anything not copied over here gets reverted to the values the item had in its prototype.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This method of copying items is of course not perfect as we cannot clone every single component, which would be pretty much impossible with our ECS. /// These are all not part of their corresponding systems because we don't want systems every system to depend on a CloningSystem namespace import, which is still heavily coupled to med code.
/// We only consider the most important components so the paradox clone gets similar equipment. /// TODO: Create a more generic "CopyEntity" method/event (probably in RT) that doesn't have this problem and then move all these subscriptions.
/// This method of using subscriptions was chosen to make it easy for forks to add their own custom components that need to be copied.
/// </remarks> /// </remarks>
public sealed partial class CloningSystem : EntitySystem public sealed partial class CloningSystem
{ {
[Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] private readonly SharedStackSystem _stack = default!;
[Dependency] private readonly LabelSystem _label = default!; [Dependency] private readonly LabelSystem _label = default!;
[Dependency] private readonly ForensicsSystem _forensics = default!; [Dependency] private readonly ForensicsSystem _forensics = default!;
[Dependency] private readonly PaperSystem _paper = default!; [Dependency] private readonly PaperSystem _paper = default!;
[Dependency] private readonly VocalSystem _vocal = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<StackComponent, CloningItemEvent>(OnCloneStack); // These are used for <see cref="CopyItem"/>.
SubscribeLocalEvent<LabelComponent, CloningItemEvent>(OnCloneLabel); // Anything not copied over here gets reverted to the values the item had in its prototype.
SubscribeLocalEvent<PaperComponent, CloningItemEvent>(OnClonePaper); // This method of copying items is of course not perfect as we cannot clone every single component, which would be pretty much impossible with our ECS.
SubscribeLocalEvent<ForensicsComponent, CloningItemEvent>(OnCloneForensics); // We only consider the most important components so the paradox clone gets similar equipment.
SubscribeLocalEvent<StoreComponent, CloningItemEvent>(OnCloneStore); // This method of using subscriptions was chosen to make it easy for forks to add their own custom components that need to be copied.
SubscribeLocalEvent<StackComponent, CloningItemEvent>(OnCloneItemStack);
SubscribeLocalEvent<LabelComponent, CloningItemEvent>(OnCloneItemLabel);
SubscribeLocalEvent<PaperComponent, CloningItemEvent>(OnCloneItemPaper);
SubscribeLocalEvent<ForensicsComponent, CloningItemEvent>(OnCloneItemForensics);
SubscribeLocalEvent<StoreComponent, CloningItemEvent>(OnCloneItemStore);
// These are for cloning components that cannot be cloned using CopyComp.
// Put them into CloningSettingsPrototype.EventComponents to have them be applied to the clone.
SubscribeLocalEvent<VocalComponent, CloningEvent>(OnCloneVocal);
SubscribeLocalEvent<StorageComponent, CloningEvent>(OnCloneStorage);
SubscribeLocalEvent<InventoryComponent, CloningEvent>(OnCloneInventory);
SubscribeLocalEvent<MovementSpeedModifierComponent, CloningEvent>(OnCloneInventory);
} }
private void OnCloneStack(Entity<StackComponent> ent, ref CloningItemEvent args) private void OnCloneItemStack(Entity<StackComponent> ent, ref CloningItemEvent args)
{ {
// if the clone is a stack as well, adjust the count of the copy // if the clone is a stack as well, adjust the count of the copy
if (TryComp<StackComponent>(args.CloneUid, out var cloneStackComp)) if (TryComp<StackComponent>(args.CloneUid, out var cloneStackComp))
_stack.SetCount(args.CloneUid, ent.Comp.Count, cloneStackComp); _stack.SetCount(args.CloneUid, ent.Comp.Count, cloneStackComp);
} }
private void OnCloneLabel(Entity<LabelComponent> ent, ref CloningItemEvent args) private void OnCloneItemLabel(Entity<LabelComponent> ent, ref CloningItemEvent args)
{ {
// copy the label // copy the label
_label.Label(args.CloneUid, ent.Comp.CurrentLabel); _label.Label(args.CloneUid, ent.Comp.CurrentLabel);
} }
private void OnClonePaper(Entity<PaperComponent> ent, ref CloningItemEvent args) private void OnCloneItemPaper(Entity<PaperComponent> ent, ref CloningItemEvent args)
{ {
// copy the text and any stamps // copy the text and any stamps
if (TryComp<PaperComponent>(args.CloneUid, out var clonePaperComp)) if (TryComp<PaperComponent>(args.CloneUid, out var clonePaperComp))
@@ -63,13 +79,13 @@ public sealed partial class CloningSystem : EntitySystem
} }
} }
private void OnCloneForensics(Entity<ForensicsComponent> ent, ref CloningItemEvent args) private void OnCloneItemForensics(Entity<ForensicsComponent> ent, ref CloningItemEvent args)
{ {
// copy any forensics to the cloned item // copy any forensics to the cloned item
_forensics.CopyForensicsFrom(ent.Comp, args.CloneUid); _forensics.CopyForensicsFrom(ent.Comp, args.CloneUid);
} }
private void OnCloneStore(Entity<StoreComponent> ent, ref CloningItemEvent args) private void OnCloneItemStore(Entity<StoreComponent> ent, ref CloningItemEvent args)
{ {
// copy the current amount of currency in the store // copy the current amount of currency in the store
// at the moment this takes care of uplink implants and the portable nukie uplinks // at the moment this takes care of uplink implants and the portable nukie uplinks
@@ -80,4 +96,35 @@ public sealed partial class CloningSystem : EntitySystem
} }
} }
private void OnCloneVocal(Entity<VocalComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_vocal.CopyComponent(ent.AsNullable(), args.CloneUid);
}
private void OnCloneStorage(Entity<StorageComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_storage.CopyComponent(ent.AsNullable(), args.CloneUid);
}
private void OnCloneInventory(Entity<InventoryComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_inventory.CopyComponent(ent.AsNullable(), args.CloneUid);
}
private void OnCloneInventory(Entity<MovementSpeedModifierComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_movementSpeedModifier.CopyComponent(ent.AsNullable(), args.CloneUid);
}
} }

View File

@@ -24,7 +24,7 @@ namespace Content.Server.Cloning;
/// System responsible for making a copy of a humanoid's body. /// System responsible for making a copy of a humanoid's body.
/// For the cloning machines themselves look at CloningPodSystem, CloningConsoleSystem and MedicalScannerSystem instead. /// For the cloning machines themselves look at CloningPodSystem, CloningConsoleSystem and MedicalScannerSystem instead.
/// </summary> /// </summary>
public sealed partial class CloningSystem : EntitySystem public sealed partial class CloningSystem : SharedCloningSystem
{ {
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!; [Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
@@ -84,13 +84,7 @@ public sealed partial class CloningSystem : EntitySystem
return true; return true;
} }
/// <summary> public override void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings)
/// Copy components from one entity to another based on a CloningSettingsPrototype.
/// </summary>
/// <param name="original">The orignal Entity to clone components from.</param>
/// <param name="clone">The target Entity to clone components to.</param>
/// <param name="settings">The clone settings prototype containing the list of components to clone.</param>
public void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings)
{ {
var componentsToCopy = settings.Components; var componentsToCopy = settings.Components;
var componentsToEvent = settings.EventComponents; var componentsToEvent = settings.EventComponents;
@@ -128,7 +122,8 @@ public sealed partial class CloningSystem : EntitySystem
} }
// If the original does not have the component, then the clone shouldn't have it either. // If the original does not have the component, then the clone shouldn't have it either.
RemComp(clone, componentRegistration.Type); if (!HasComp(original, componentRegistration.Type))
RemComp(clone, componentRegistration.Type);
} }
var cloningEv = new CloningEvent(settings, clone); var cloningEv = new CloningEvent(settings, clone);

View File

@@ -1,4 +1,4 @@
using Content.Server.Station.Components; using Content.Shared.Station.Components;
using Robust.Shared.Console; using Robust.Shared.Console;
namespace Content.Server.Commands; namespace Content.Server.Commands;

View File

@@ -276,8 +276,8 @@ namespace Content.Server.Construction
if(!insertStep.EntityValid(insert, EntityManager, Factory)) if(!insertStep.EntityValid(insert, EntityManager, Factory))
return HandleResult.False; return HandleResult.False;
// Unremovable items can't be inserted, unless they are a lingering stack // Unremovable items can't be inserted
if(HasComp<UnremoveableComponent>(insert) && (!TryComp<StackComponent>(insert, out var comp) || !comp.Lingering)) if(HasComp<UnremoveableComponent>(insert))
return HandleResult.False; return HandleResult.False;
// If we're only testing whether this step would be handled by the given event, then we're done. // If we're only testing whether this step would be handled by the given event, then we're done.

View File

@@ -1,7 +1,6 @@
using System.Linq; using System.Linq;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.EUI; using Content.Server.EUI;
using Content.Server.Station.Components;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Server.StationRecords; using Content.Server.StationRecords;
using Content.Server.StationRecords.Systems; using Content.Server.StationRecords.Systems;
@@ -10,12 +9,12 @@ using Content.Shared.CCVar;
using Content.Shared.CrewManifest; using Content.Shared.CrewManifest;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Station.Components;
using Content.Shared.StationRecords; using Content.Shared.StationRecords;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.CrewManifest; namespace Content.Server.CrewManifest;

View File

@@ -1,50 +0,0 @@
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
namespace Content.Server.Damage.Systems;
// System for damage that occurs on specific trigger, towards the user..
public sealed class DamageUserOnTriggerSystem : EntitySystem
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
SubscribeLocalEvent<DamageUserOnTriggerComponent, TriggerEvent>(OnTrigger);
}
private void OnTrigger(EntityUid uid, DamageUserOnTriggerComponent component, TriggerEvent args)
{
if (args.User is null)
return;
args.Handled |= OnDamageTrigger(uid, args.User.Value, component);
}
private bool OnDamageTrigger(EntityUid source, EntityUid target, DamageUserOnTriggerComponent? component = null)
{
if (!Resolve(source, ref component))
{
return false;
}
var damage = new DamageSpecifier(component.Damage);
var ev = new BeforeDamageUserOnTriggerEvent(damage, target);
RaiseLocalEvent(source, ev);
return _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: source) is not null;
}
}
public sealed class BeforeDamageUserOnTriggerEvent : EntityEventArgs
{
public DamageSpecifier Damage { get; set; }
public EntityUid Tripper { get; }
public BeforeDamageUserOnTriggerEvent(DamageSpecifier damage, EntityUid target)
{
Damage = damage;
Tripper = target;
}
}

View File

@@ -1,5 +1,4 @@
using Content.Server.Defusable.Components; using Content.Server.Defusable.Components;
using Content.Server.Explosion.Components;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Wires; using Content.Server.Wires;
@@ -8,13 +7,13 @@ using Content.Shared.Construction.Components;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Defusable; using Content.Shared.Defusable;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Explosion.Components;
using Content.Shared.Explosion.Components.OnTrigger;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Trigger.Components;
using Content.Shared.Trigger.Components.Effects;
using Content.Shared.Trigger.Systems;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Content.Shared.Wires; using Content.Shared.Wires;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
namespace Content.Server.Defusable.Systems; namespace Content.Server.Defusable.Systems;
@@ -74,12 +73,13 @@ public sealed class DefusableSystem : SharedDefusableSystem
{ {
args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid))); args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
} }
else if (comp.Activated && TryComp<ActiveTimerTriggerComponent>(uid, out var activeComp)) else if (comp.Activated)
{ {
if (comp.DisplayTime) var remaining = _trigger.GetRemainingTime(uid);
if (comp.DisplayTime && remaining != null)
{ {
args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid), args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid),
("time", MathF.Floor(activeComp.TimeRemaining)))); ("time", Math.Floor(remaining.Value.TotalSeconds))));
} }
else else
{ {
@@ -139,16 +139,9 @@ public sealed class DefusableSystem : SharedDefusableSystem
SetActivated(comp, true); SetActivated(comp, true);
_popup.PopupEntity(Loc.GetString("defusable-popup-begun", ("name", uid)), uid); _popup.PopupEntity(Loc.GetString("defusable-popup-begun", ("name", uid)), uid);
if (TryComp<OnUseTimerTriggerComponent>(uid, out var timerTrigger)) if (TryComp<TimerTriggerComponent>(uid, out var timerTrigger))
{ {
_trigger.HandleTimerTrigger( _trigger.ActivateTimerTrigger((uid, timerTrigger));
uid,
user,
timerTrigger.Delay,
timerTrigger.BeepInterval,
timerTrigger.InitialBeepDelay,
timerTrigger.BeepSound
);
} }
RaiseLocalEvent(uid, new BombArmedEvent(uid)); RaiseLocalEvent(uid, new BombArmedEvent(uid));
@@ -168,7 +161,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
RaiseLocalEvent(uid, new BombDetonatedEvent(uid)); RaiseLocalEvent(uid, new BombDetonatedEvent(uid));
_explosion.TriggerExplosive(uid, user:detonator); _explosion.TriggerExplosive(uid, user: detonator);
QueueDel(uid); QueueDel(uid);
_appearance.SetData(uid, DefusableVisuals.Active, comp.Activated); _appearance.SetData(uid, DefusableVisuals.Active, comp.Activated);
@@ -188,7 +181,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
{ {
SetUsable(comp, false); SetUsable(comp, false);
RemComp<ExplodeOnTriggerComponent>(uid); RemComp<ExplodeOnTriggerComponent>(uid);
RemComp<OnUseTimerTriggerComponent>(uid); RemComp<TimerTriggerComponent>(uid);
} }
RemComp<ActiveTimerTriggerComponent>(uid); RemComp<ActiveTimerTriggerComponent>(uid);
@@ -246,7 +239,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
if (comp is not { Activated: true, DelayWireUsed: false }) if (comp is not { Activated: true, DelayWireUsed: false })
return; return;
_trigger.TryDelay(wire.Owner, 30f); _trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(30));
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner); _popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner);
comp.DelayWireUsed = true; comp.DelayWireUsed = true;
} }
@@ -268,7 +261,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
if (comp is { Activated: true, ProceedWireUsed: false }) if (comp is { Activated: true, ProceedWireUsed: false })
{ {
comp.ProceedWireUsed = true; comp.ProceedWireUsed = true;
_trigger.TryDelay(wire.Owner, -15f); _trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(-15));
} }
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-proceed-pulse", ("name", wire.Owner)), wire.Owner); _popup.PopupEntity(Loc.GetString("defusable-popup-wire-proceed-pulse", ("name", wire.Owner)), wire.Owner);
@@ -298,7 +291,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
{ {
if (!comp.ActivatedWireUsed) if (!comp.ActivatedWireUsed)
{ {
_trigger.TryDelay(wire.Owner, 30f); _trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(30));
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner); _popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner);
comp.ActivatedWireUsed = true; comp.ActivatedWireUsed = true;
} }

View File

@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
@@ -14,15 +15,13 @@ using Content.Shared.Damage;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Destructible; using Content.Shared.Destructible;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Humanoid;
using Content.Shared.Trigger.Systems;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Audio; using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using System.Linq;
using Content.Shared.Humanoid;
using Robust.Shared.Player;
namespace Content.Server.Destructible namespace Content.Server.Destructible
{ {

View File

@@ -5,6 +5,6 @@ public sealed partial class TimerStartBehavior : IThresholdBehavior
{ {
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
{ {
system.TriggerSystem.StartTimer(owner, cause); system.TriggerSystem.ActivateTimerTrigger(owner, cause);
} }
} }

View File

@@ -1,10 +1,18 @@
namespace Content.Server.Destructible.Thresholds.Behaviors; using Content.Shared.Trigger.Systems;
namespace Content.Server.Destructible.Thresholds.Behaviors;
[DataDefinition] [DataDefinition]
public sealed partial class TriggerBehavior : IThresholdBehavior public sealed partial class TriggerBehavior : IThresholdBehavior
{ {
/// <summary>
/// The trigger key to use when triggering.
/// </summary>
[DataField]
public string? KeyOut { get; set; } = TriggerSystem.DefaultTriggerKey;
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
{ {
system.TriggerSystem.Trigger(owner, cause); system.TriggerSystem.Trigger(owner, cause, KeyOut);
} }
} }

View File

@@ -1,4 +1,6 @@
using Content.Server.DeviceLinking.Components; using Content.Server.DeviceLinking.Components;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems; using Content.Server.DeviceNetwork.Systems;
using Content.Shared.DeviceLinking; using Content.Shared.DeviceLinking;
using Content.Shared.DeviceLinking.Events; using Content.Shared.DeviceLinking.Events;

View File

@@ -1,5 +1,4 @@
using Content.Server.DeviceLinking.Components; using Content.Server.DeviceLinking.Components;
using Content.Server.DeviceNetwork;
using Content.Shared.DeviceLinking; using Content.Shared.DeviceLinking;
using Content.Shared.DeviceLinking.Events; using Content.Shared.DeviceLinking.Events;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;

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