Merge branch 'master' into ed-14-04-2025-content

This commit is contained in:
Ed
2025-04-23 12:58:23 +03:00
committed by GitHub
930 changed files with 180196 additions and 169811 deletions

49
.github/CODEOWNERS vendored
View File

@@ -1,45 +1,6 @@
# Last match in file takes precedence.
# TheShuEd
* @TheShuEd
# Sorting by path instead of by who added it one day :(
# this isn't how codeowners rules work pls read the first comment instead of trying to force a sorting order
/Resources/ConfigPresets/WizardsDen/ @nikthechampiongr @crazybrain23
/Content.*/Administration/ @DrSmugleaf @nikthechampiongr @crazybrain23
/Resources/ServerInfo/ @nikthechampiongr @crazybrain23
/Resources/ServerInfo/Guidebook/ServerRules/ @nikthechampiongr @crazybrain23
/Resources/Prototypes/Maps/** @Emisse
/Resources/Prototypes/Body/ @DrSmugleaf # suffering
/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf
/Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf
/Resources/Prototypes/Guidebook/rules.yml @nikthechampiongr @crazybrain23
/Content.*/Body/ @DrSmugleaf
/Content.YAMLLinter @DrSmugleaf
/Content.Shared/Damage/ @DrSmugleaf
/Content.*/Anomaly/ @TheShuEd
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @TheShuEd
/Content.*/Forensics/ @ficcialfaint
# SKREEEE
/Content.*.Database/ @PJB3005 @DrSmugleaf
/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @nikthechampiongr @crazybrain23
/Pow3r/ @PJB3005
/Content.Server/Power/Pow3r/ @PJB3005
# notafet
/Content.*/Atmos/ @Partmedia
/Content.*/Botany/ @Partmedia
# Jezi
/Content.*/Medical @Jezithyr
/Content.*/Body @Jezithyr
# Sloth
/Content.*/Audio @metalgearsloth
/Content.*/Movement @metalgearsloth
/Content.*/NPC @metalgearsloth
/Content.*/Shuttles @metalgearsloth
/Content.*/Weapons @metalgearsloth
# TornadoTechnology
*.cs @Tornado-Technology
*.xaml @Tornado-Technology

View File

@@ -1,4 +1,3 @@
using Content.Client.Atmos.UI;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Piping.Binary.Components;
@@ -15,7 +14,12 @@ public sealed class GasPressurePumpSystem : SharedGasPressurePumpSystem
private void OnPumpUpdate(Entity<GasPressurePumpComponent> ent, ref AfterAutoHandleStateEvent args)
{
if (UserInterfaceSystem.TryGetOpenUi<GasPressurePumpBoundUserInterface>(ent.Owner, GasPressurePumpUiKey.Key, out var bui))
UpdateUi(ent);
}
protected override void UpdateUi(Entity<GasPressurePumpComponent> ent)
{
if (UserInterfaceSystem.TryGetOpenUi(ent.Owner, GasPressurePumpUiKey.Key, out var bui))
{
bui.Update();
}

View File

@@ -103,7 +103,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
_temperature.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature", ("tempC", $"{TemperatureHelpers.KelvinToCelsius(state.TemperatureAverage):0.#}"), ("temperature", $"{state.TemperatureAverage:0.##}")));
_alarmState.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state",
("color", ColorForAlarm(state.AlarmType)),
("state", $"{state.AlarmType}")));
("state", state.AlarmType)));
UpdateModeSelector(state.Mode);
UpdateAutoMode(state.AutoMode);
foreach (var (addr, dev) in state.DeviceData)

View File

@@ -27,11 +27,11 @@ public sealed partial class SensorInfo : BoxContainer
_address = address;
SensorAddress.Title = $"{address} : {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",
("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)),
("state", $"{data.AlarmState}")));
("state", data.AlarmState)));
PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure-indicator",
("color", AirAlarmWindow.ColorForThreshold(data.Pressure, data.PressureThreshold)),
("pressure", $"{data.Pressure:0.##}")));
@@ -90,11 +90,11 @@ public sealed partial class SensorInfo : BoxContainer
public void ChangeData(AtmosSensorData data)
{
SensorAddress.Title = $"{_address} : {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",
("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)),
("state", $"{data.AlarmState}")));
("state", data.AlarmState)));
PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure-indicator",
("color", AirAlarmWindow.ColorForThreshold(data.Pressure, data.PressureThreshold)),

View File

@@ -1,8 +1,7 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
@@ -12,7 +11,7 @@ namespace Content.Client.Atmos.UI;
/// Initializes a <see cref="GasPressurePumpWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface
public sealed class GasPressurePumpBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
[ViewVariables]
private const float MaxPressure = Atmospherics.MaxOutputPressure;
@@ -20,10 +19,6 @@ public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface
[ViewVariables]
private GasPressurePumpWindow? _window;
public GasPressurePumpBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
@@ -35,7 +30,7 @@ public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface
Update();
}
public void Update()
public override void Update()
{
if (_window == null)
return;
@@ -52,7 +47,9 @@ public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface
private void OnToggleStatusButtonPressed()
{
if (_window is null) return;
if (_window is null)
return;
SendPredictedMessage(new GasPressurePumpToggleStatusMessage(_window.PumpStatus));
}

View File

@@ -1,7 +1,6 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Atmos;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Atmos.UI

View File

@@ -1,6 +1,7 @@
using Content.Client.Rotation;
using Content.Shared.Buckle;
using Content.Shared.Buckle.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Rotation;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -21,6 +22,15 @@ internal sealed class BuckleSystem : SharedBuckleSystem
SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapMoveEvent);
SubscribeLocalEvent<BuckleComponent, BuckledEvent>(OnBuckledEvent);
SubscribeLocalEvent<BuckleComponent, UnbuckledEvent>(OnUnbuckledEvent);
SubscribeLocalEvent<BuckleComponent, AttemptMobCollideEvent>(OnMobCollide);
}
private void OnMobCollide(Entity<BuckleComponent> ent, ref AttemptMobCollideEvent args)
{
if (ent.Comp.Buckled)
{
args.Cancelled = true;
}
}
private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args)

View File

@@ -67,8 +67,10 @@ public sealed partial class CargoSystem
if (!Resolve(uid, ref sprite))
return;
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
return;
_appearance.TryGetData<CargoTelepadState?>(uid, CargoTelepadVisuals.State, out var state);
AnimationPlayerComponent? player = null;
switch (state)
{
@@ -76,7 +78,7 @@ public sealed partial class CargoSystem
if (_player.HasRunningAnimation(uid, TelepadBeamKey))
return;
_player.Stop(uid, player, TelepadIdleKey);
_player.Play(uid, player, CargoTelepadBeamAnimation, TelepadBeamKey);
_player.Play((uid, player), CargoTelepadBeamAnimation, TelepadBeamKey);
break;
case CargoTelepadState.Unpowered:
sprite.LayerSetVisible(CargoTelepadLayers.Beam, false);
@@ -90,7 +92,7 @@ public sealed partial class CargoSystem
_player.HasRunningAnimation(uid, player, TelepadBeamKey))
return;
_player.Play(uid, player, CargoTelepadIdleAnimation, TelepadIdleKey);
_player.Play((uid, player), CargoTelepadIdleAnimation, TelepadIdleKey);
break;
}
}

View File

@@ -8,8 +8,6 @@ using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
namespace Content.Client.Changelog
@@ -19,7 +17,6 @@ namespace Content.Client.Changelog
{
[Dependency] private readonly ChangelogManager _changelog = default!;
[Dependency] private readonly IClientAdminManager _adminManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
public ChangelogWindow()
{

View File

@@ -1,4 +1,4 @@
using Content.Shared.Chasm;
using Content.Shared.Chasm;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
@@ -32,7 +32,9 @@ public sealed class ChasmFallingVisualsSystem : EntitySystem
component.OriginalScale = sprite.Scale;
var player = EnsureComp<AnimationPlayerComponent>(uid);
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
return;
if (_anim.HasRunningAnimation(player, _chasmFallAnimationKey))
return;
@@ -44,11 +46,13 @@ public sealed class ChasmFallingVisualsSystem : EntitySystem
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
var player = EnsureComp<AnimationPlayerComponent>(uid);
if (_anim.HasRunningAnimation(player, _chasmFallAnimationKey))
_anim.Stop(player, _chasmFallAnimationKey);
sprite.Scale = component.OriginalScale;
if (!TryComp<AnimationPlayerComponent>(uid, out var player))
return;
if (_anim.HasRunningAnimation(player, _chasmFallAnimationKey))
_anim.Stop((uid, player), _chasmFallAnimationKey);
}
private Animation GetFallingAnimation(ChasmFallingComponent component)

View File

@@ -1,31 +0,0 @@
<ui:RadialMenu xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
BackButtonStyleClass="RadialMenuBackButton"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="450 450">
<!-- Main -->
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False">
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'emote-menu-category-general'}" TargetLayer="General" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'emote-menu-category-vocal'}" TargetLayer="Vocal" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Emotes/vocal.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'emote-menu-category-hands'}" TargetLayer="Hands" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"/>
</ui:RadialMenuTextureButtonWithSector>
</ui:RadialContainer>
<!-- General -->
<ui:RadialContainer Name="General" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Vocal -->
<ui:RadialContainer Name="Vocal" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Hands -->
<ui:RadialContainer Name="Hands" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
</ui:RadialMenu>

View File

@@ -1,111 +0,0 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Speech;
using Content.Shared.Whitelist;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Chat.UI;
[GenerateTypedNameReferences]
public sealed partial class EmotesMenu : RadialMenu
{
[Dependency] private readonly EntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
public event Action<ProtoId<EmotePrototype>>? OnPlayEmote;
public EmotesMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
var spriteSystem = _entManager.System<SpriteSystem>();
var whitelistSystem = _entManager.System<EntityWhitelistSystem>();
var main = FindControl<RadialContainer>("Main");
var emotes = _prototypeManager.EnumeratePrototypes<EmotePrototype>();
foreach (var emote in emotes)
{
var player = _playerManager.LocalSession?.AttachedEntity;
if (emote.Category == EmoteCategory.Invalid ||
emote.ChatTriggers.Count == 0 ||
!(player.HasValue && whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) ||
whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value))
continue;
if (!emote.Available &&
_entManager.TryGetComponent<SpeechComponent>(player.Value, out var speech) &&
!speech.AllowedEmotes.Contains(emote.ID))
continue;
var parent = FindControl<RadialContainer>(emote.Category.ToString());
var button = new EmoteMenuButton
{
SetSize = new Vector2(64f, 64f),
ToolTip = Loc.GetString(emote.Name),
ProtoId = emote.ID,
};
var tex = new TextureRect
{
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Texture = spriteSystem.Frame0(emote.Icon),
TextureScale = new Vector2(2f, 2f),
};
button.AddChild(tex);
parent.AddChild(button);
foreach (var child in main.Children)
{
if (child is not RadialMenuTextureButton castChild)
continue;
if (castChild.TargetLayer == emote.Category.ToString())
{
castChild.Visible = true;
break;
}
}
}
// Set up menu actions
foreach (var child in Children)
{
if (child is not RadialContainer container)
continue;
AddEmoteClickAction(container);
}
}
private void AddEmoteClickAction(RadialContainer container)
{
foreach (var child in container.Children)
{
if (child is not EmoteMenuButton castChild)
continue;
castChild.OnButtonUp += _ =>
{
OnPlayEmote?.Invoke(castChild.ProtoId);
Close();
};
}
}
}
public sealed class EmoteMenuButton : RadialMenuTextureButtonWithSector
{
public ProtoId<EmotePrototype> ProtoId { get; set; }
}

View File

@@ -125,7 +125,7 @@ namespace Content.Client.Chat.UI
_verticalOffsetAchieved = MathHelper.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.DeltaSeconds);
}
if (!_entityManager.TryGetComponent<TransformComponent>(_senderEntity, out var xform) || xform.MapID != _eyeManager.CurrentMap)
if (!_entityManager.TryGetComponent<TransformComponent>(_senderEntity, out var xform) || xform.MapID != _eyeManager.CurrentEye.Position.MapId)
{
Modulate = Color.White.WithAlpha(0);
return;

View File

@@ -6,7 +6,6 @@ using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using System.Linq;
@@ -14,6 +13,7 @@ using System.Numerics;
using Content.Shared.FixedPoint;
using Robust.Client.Graphics;
using static Robust.Client.UserInterface.Controls.BoxContainer;
using Robust.Client.GameObjects;
namespace Content.Client.Chemistry.UI
{
@@ -24,6 +24,10 @@ namespace Content.Client.Chemistry.UI
public sealed partial class ChemMasterWindow : FancyWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly SpriteSystem _sprite;
public event Action<BaseButton.ButtonEventArgs, ReagentButton>? OnReagentButtonPressed;
public readonly Button[] PillTypeButtons;
@@ -38,6 +42,8 @@ namespace Content.Client.Chemistry.UI
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entityManager.System<SpriteSystem>();
// Pill type selection buttons, in total there are 20 pills.
// Pill rsi file should have states named as pill1, pill2, and so on.
var resourcePath = new ResPath(PillsRsiPath);
@@ -69,7 +75,7 @@ namespace Content.Client.Chemistry.UI
var specifier = new SpriteSpecifier.Rsi(resourcePath, "pill" + (i + 1));
TextureRect pillTypeTexture = new TextureRect
{
Texture = specifier.Frame0(),
Texture = _sprite.Frame0(specifier),
TextureScale = new Vector2(1.75f, 1.75f),
Stretch = TextureRect.StretchMode.KeepCentered,
};

View File

@@ -1,4 +1,4 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Timing;
@@ -37,7 +37,7 @@ public sealed class FoamVisualizerSystem : VisualizerSystem<FoamVisualsComponent
if (TryComp(uid, out AnimationPlayerComponent? animPlayer)
&& !AnimationSystem.HasRunningAnimation(uid, animPlayer, FoamVisualsComponent.AnimationKey))
{
AnimationSystem.Play(uid, animPlayer, comp.Animation, FoamVisualsComponent.AnimationKey);
AnimationSystem.Play((uid, animPlayer), comp.Animation, FoamVisualsComponent.AnimationKey);
}
}
}

View File

@@ -1,4 +1,4 @@
using Content.Shared.Vapor;
using Content.Shared.Vapor;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
@@ -41,7 +41,7 @@ public sealed class VaporVisualizerSystem : VisualizerSystem<VaporVisualsCompone
TryComp<AnimationPlayerComponent>(uid, out var animPlayer) &&
!AnimationSystem.HasRunningAnimation(uid, animPlayer, VaporVisualsComponent.AnimationKey))
{
AnimationSystem.Play(uid, animPlayer, comp.VaporFlick, VaporVisualsComponent.AnimationKey);
AnimationSystem.Play((uid, animPlayer), comp.VaporFlick, VaporVisualsComponent.AnimationKey);
}
}

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Client.Sprite;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Utility;
@@ -17,12 +18,14 @@ public sealed class ClickableSystem : EntitySystem
private EntityQuery<ClickableComponent> _clickableQuery;
private EntityQuery<TransformComponent> _xformQuery;
private EntityQuery<FadingSpriteComponent> _fadingSpriteQuery;
public override void Initialize()
{
base.Initialize();
_clickableQuery = GetEntityQuery<ClickableComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
_fadingSpriteQuery = GetEntityQuery<FadingSpriteComponent>();
}
/// <summary>
@@ -34,7 +37,7 @@ public sealed class ClickableSystem : EntitySystem
/// The draw depth for the sprite that captured the click.
/// </param>
/// <returns>True if the click worked, false otherwise.</returns>
public bool CheckClick(Entity<ClickableComponent?, SpriteComponent, TransformComponent?> entity, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom)
public bool CheckClick(Entity<ClickableComponent?, SpriteComponent, TransformComponent?, FadingSpriteComponent?> entity, Vector2 worldPos, IEye eye, bool excludeFaded, out int drawDepth, out uint renderOrder, out float bottom)
{
if (!_clickableQuery.Resolve(entity.Owner, ref entity.Comp1, false))
{
@@ -52,6 +55,14 @@ public sealed class ClickableSystem : EntitySystem
return false;
}
if (excludeFaded && _fadingSpriteQuery.Resolve(entity.Owner, ref entity.Comp4, false))
{
drawDepth = default;
renderOrder = default;
bottom = default;
return false;
}
var sprite = entity.Comp2;
var transform = entity.Comp3;

View File

@@ -1,4 +1,4 @@
using System.Globalization;
using System.Globalization;
using Content.Client.UserInterface.Controls;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated;
@@ -91,7 +91,7 @@ namespace Content.Client.Communications.UI
if (alerts == null)
{
var name = currentAlert;
if (Loc.TryGetString($"alert-level-{currentAlert}", out var locName))
if (_loc.TryGetString($"alert-level-{currentAlert}", out var locName))
{
name = locName;
}
@@ -103,7 +103,7 @@ namespace Content.Client.Communications.UI
foreach (var alert in alerts)
{
var name = alert;
if (Loc.TryGetString($"alert-level-{alert}", out var locName))
if (_loc.TryGetString($"alert-level-{alert}", out var locName))
{
name = locName;
}

View File

@@ -111,7 +111,7 @@ public sealed class DecalPlacementSystem : EntitySystem
if (args.Handled)
return;
if (args.Target.GetGridUid(EntityManager) == null)
if (_transform.GetGrid(args.Target) == null)
return;
args.Handled = true;

View File

@@ -2,6 +2,7 @@ using System.Linq;
using Content.Client.Stylesheets;
using Content.Shared.Decals;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
@@ -19,6 +20,7 @@ public sealed partial class DecalPlacerWindow : DefaultWindow
[Dependency] private readonly IEntityManager _e = default!;
private readonly DecalPlacementSystem _decalPlacementSystem;
private readonly SpriteSystem _sprite;
public FloatSpinBox RotationSpinBox;
@@ -41,6 +43,7 @@ public sealed partial class DecalPlacerWindow : DefaultWindow
IoCManager.InjectDependencies(this);
_decalPlacementSystem = _e.System<DecalPlacementSystem>();
_sprite = _e.System<SpriteSystem>();
// This needs to be done in C# so we can have custom stuff passed in the constructor
// and thus have a proper step size
@@ -204,7 +207,7 @@ public sealed partial class DecalPlacerWindow : DefaultWindow
foreach (var decalPrototype in prototypes)
{
if (decalPrototype.ShowMenu)
_decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
_decals.Add(decalPrototype.ID, _sprite.Frame0(decalPrototype.Sprite));
}
RefreshList();

View File

@@ -24,6 +24,7 @@ using Content.Client.Singularity;
using Content.Client.Stylesheets;
using Content.Client.Viewport;
using Content.Client.Voting;
using Content.Shared._CP14.Sponsor;
using Content.Shared.Ame.Components;
using Content.Shared.CCVar;
using Content.Shared.Gravity;
@@ -48,6 +49,7 @@ namespace Content.Client.Entry
//CP14
[Dependency] private readonly DiscordAuthManager _discordAuth = default!;
[Dependency] private readonly JoinQueueManager _joinQueueManager = default!;
[Dependency] private readonly ICP14SponsorManager _sponsorManager = default!;
//CP14 end
[Dependency] private readonly IBaseClient _baseClient = default!;
[Dependency] private readonly IGameController _gameController = default!;
@@ -170,6 +172,7 @@ namespace Content.Client.Entry
_overlayManager.AddOverlay(new CP14BasePostProcessOverlay());
_discordAuth.Initialize();
_joinQueueManager.Initialize();
_sponsorManager.Initialize();
//CP14 end
_overlayManager.AddOverlay(new SingularityOverlay());
_overlayManager.AddOverlay(new RadiationPulseOverlay());

View File

@@ -1,11 +1,7 @@
using Content.Client.ContextMenu.UI;
using Content.Client.Stylesheets;
using Content.Shared.Verbs;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.Utility;
@@ -27,14 +23,16 @@ public sealed class ExamineButton : ContainerButton
public TextureRect Icon;
public ExamineVerb Verb;
private SpriteSystem _sprite;
public ExamineButton(ExamineVerb verb)
public ExamineButton(ExamineVerb verb, SpriteSystem spriteSystem)
{
Margin = new Thickness(Thickness, Thickness, Thickness, Thickness);
SetOnlyStyleClass(StyleClassExamineButton);
Verb = verb;
_sprite = spriteSystem;
if (verb.Disabled)
{
@@ -61,7 +59,7 @@ public sealed class ExamineButton : ContainerButton
if (verb.Icon != null)
{
Icon.Texture = verb.Icon.Frame0();
Icon.Texture = _sprite.Frame0(verb.Icon);
Icon.Stretch = TextureRect.StretchMode.KeepAspectCentered;
AddChild(Icon);

View File

@@ -30,6 +30,7 @@ namespace Content.Client.Examine
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly VerbSystem _verbSystem = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public const string StyleClassEntityTooltip = "entity-tooltip";
@@ -332,7 +333,7 @@ namespace Content.Client.Examine
if (!examine.ShowOnExamineTooltip)
continue;
var button = new ExamineButton(examine);
var button = new ExamineButton(examine, _sprite);
if (examine.HoverVerb)
{

View File

@@ -1,4 +1,5 @@
using System.Linq;
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Fluids;
using Robust.Client.AutoGenerated;
@@ -21,6 +22,8 @@ namespace Content.Client.Fluids.UI
RobustXamlLoader.Load(this);
_uid = uid;
_entManager = entManager;
MinBarSize = new Vector2(10, 0);
}
protected override void FrameUpdate(FrameEventArgs args)

View File

@@ -113,18 +113,18 @@ namespace Content.Client.Gameplay
return first.IsValid() ? first : null;
}
public IEnumerable<EntityUid> GetClickableEntities(EntityCoordinates coordinates)
public IEnumerable<EntityUid> GetClickableEntities(EntityCoordinates coordinates, bool excludeFaded = true)
{
var transformSystem = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates));
return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates), excludeFaded);
}
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates)
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates, bool excludeFaded = true)
{
return GetClickableEntities(coordinates, _eyeManager.CurrentEye);
return GetClickableEntities(coordinates, _eyeManager.CurrentEye, excludeFaded);
}
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates, IEye? eye)
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates, IEye? eye, bool excludeFaded = true)
{
/*
* TODO:
@@ -147,7 +147,7 @@ namespace Content.Client.Gameplay
foreach (var entity in entities)
{
if (clickQuery.TryGetComponent(entity.Uid, out var component) &&
clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom))
clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, excludeFaded, out var drawDepthClicked, out var renderOrder, out var bottom))
{
foundEntities.Add((entity.Uid, drawDepthClicked, renderOrder, bottom));
}

View File

@@ -4,11 +4,13 @@ using Content.Client.UserInterface.ControlExtensions;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Controls.FancyTree;
using Content.Client.UserInterface.Systems.Info;
using Content.Shared.CCVar;
using Content.Shared.Guidebook;
using Content.Shared.Localizations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Prototypes;
@@ -19,6 +21,7 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
{
[Dependency] private readonly DocumentParsingManager _parsingMan = default!;
[Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
private Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry> _entries = new();
@@ -159,7 +162,7 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
foreach (var entry in GetSortedEntries(roots))
{
if (!entry.CrystallPunkAllowed) continue; //CrystallEdge guidebook filter
if (entry.LocFilter is not null && entry.LocFilter != ContentLocalizationManager.Culture) continue; //CrystallEdge guidebook filter
if (entry.LocFilter is not null && entry.LocFilter != _cfg.GetCVar(CCVars.Language)) continue; //CrystallEdge guidebook filter
AddEntry(entry.Id, parent, addedEntries);
}

View File

@@ -36,15 +36,17 @@ public sealed partial class DocumentParsingManager
.Assert(_tagControlParsers.ContainsKey, tag => $"unknown tag: {tag}")
.Bind(tag => _tagControlParsers[tag]);
var whitespaceAndCommentParser = SkipWhitespaces.Then(Try(String("<!--").Then(Parser<char>.Any.SkipUntil(Try(String("-->"))))).SkipMany());
_controlParser = OneOf(_tagParser, TryHeaderControl, ListControlParser, TextControlParser)
.Before(SkipWhitespaces);
.Before(whitespaceAndCommentParser);
foreach (var typ in _reflectionManager.GetAllChildren<IDocumentTag>())
{
_tagControlParsers.Add(typ.Name, CreateTagControlParser(typ.Name, typ, _sandboxHelper));
}
ControlParser = SkipWhitespaces.Then(_controlParser.Many());
ControlParser = whitespaceAndCommentParser.Then(_controlParser.Many());
_sawmill = Logger.GetSawmill("Guidebook");
}

View File

@@ -10,10 +10,7 @@ using Content.Shared.Tag;
using Content.Shared.Verbs;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -27,7 +24,6 @@ public sealed class GuidebookSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly VerbSystem _verbSystem = default!;
[Dependency] private readonly RgbLightControllerSystem _rgbLightControllerSystem = default!;
[Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;

View File

@@ -1,6 +1,5 @@
using Content.Shared.Holopad;
using Content.Shared.Silicons.StationAi;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Shared.Player;
using System.Numerics;
@@ -10,7 +9,6 @@ namespace Content.Client.Holopad;
public sealed class HolopadBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
[Dependency] private readonly IClyde _displayManager = default!;
[ViewVariables]
private HolopadWindow? _window;

View File

@@ -3,6 +3,7 @@ using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
@@ -18,6 +19,9 @@ public sealed partial class MarkingPicker : Control
{
[Dependency] private readonly MarkingManager _markingManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly SpriteSystem _sprite;
public Action<MarkingSet>? OnMarkingAdded;
public Action<MarkingSet>? OnMarkingRemoved;
@@ -124,6 +128,8 @@ public sealed partial class MarkingPicker : Control
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entityManager.System<SpriteSystem>();
CMarkingCategoryButton.OnItemSelected += OnCategoryChange;
CMarkingsUnused.OnItemSelected += item =>
_selectedUnusedMarking = CMarkingsUnused[item.ItemIndex];
@@ -222,7 +228,7 @@ public sealed partial class MarkingPicker : Control
continue;
}
var item = CMarkingsUnused.AddItem($"{GetMarkingName(marking)}", marking.Sprites[0].Frame0());
var item = CMarkingsUnused.AddItem($"{GetMarkingName(marking)}", _sprite.Frame0(marking.Sprites[0]));
item.Metadata = marking;
}
@@ -256,7 +262,7 @@ public sealed partial class MarkingPicker : Control
var _item = new ItemList.Item(CMarkingsUsed)
{
Text = text,
Icon = newMarking.Sprites[0].Frame0(),
Icon = _sprite.Frame0(newMarking.Sprites[0]),
Selectable = true,
Metadata = newMarking,
IconModulate = marking.MarkingColors[0]
@@ -512,7 +518,7 @@ public sealed partial class MarkingPicker : Control
var item = new ItemList.Item(CMarkingsUsed)
{
Text = Loc.GetString("marking-used", ("marking-name", $"{GetMarkingName(marking)}"), ("marking-category", Loc.GetString($"markings-category-{marking.MarkingCategory}"))),
Icon = marking.Sprites[0].Frame0(),
Icon = _sprite.Frame0(marking.Sprites[0]),
Selectable = true,
Metadata = marking,
};
@@ -536,7 +542,7 @@ public sealed partial class MarkingPicker : Control
if (marking.MarkingCategory == _selectedMarkingCategory)
{
var item = CMarkingsUnused.AddItem($"{GetMarkingName(marking)}", marking.Sprites[0].Frame0());
var item = CMarkingsUnused.AddItem($"{GetMarkingName(marking)}", _sprite.Frame0(marking.Sprites[0]));
item.Metadata = marking;
}
_selectedMarking = null;

View File

@@ -1,6 +1,7 @@
using System.Linq;
using Content.Shared.Humanoid.Markings;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
@@ -11,7 +12,10 @@ namespace Content.Client.Humanoid;
public sealed partial class SingleMarkingPicker : BoxContainer
{
[Dependency] private readonly MarkingManager _markingManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly SpriteSystem _sprite;
/// <summary>
/// What happens if a marking is selected.
/// It will send the 'slot' (marking index)
@@ -123,6 +127,7 @@ public sealed partial class SingleMarkingPicker : BoxContainer
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entityManager.System<SpriteSystem>();
MarkingList.OnItemSelected += SelectMarking;
AddButton.OnPressed += _ =>
{
@@ -188,7 +193,7 @@ public sealed partial class SingleMarkingPicker : BoxContainer
foreach (var (id, marking) in sortedMarkings)
{
var item = MarkingList.AddItem(Loc.GetString($"marking-{id}"), marking.Sprites[0].Frame0());
var item = MarkingList.AddItem(Loc.GetString($"marking-{id}"), _sprite.Frame0(marking.Sprites[0]));
item.Metadata = marking.ID;
if (_markings[Slot].MarkingId == id)

View File

@@ -0,0 +1,5 @@
using Content.Shared.IgnitionSource;
namespace Content.Client.IgnitionSource;
public sealed partial class IgnitionSourceSystem : SharedIgnitionSourceSystem;

View File

@@ -1,13 +1,10 @@
using Content.Shared.Implants;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Implants.UI;
public sealed class DeimplantBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IPrototypeManager _protomanager = default!;
[ViewVariables]
private DeimplantChoiceWindow? _window;

View File

@@ -1,5 +1,6 @@
using Content.Client._CP14.Discord;
using Content.Client._CP14.JoinQueue;
using Content.Client._CP14.Sponsor;
using Content.Client.Administration.Managers;
using Content.Client.Changelog;
using Content.Client.Chat.Managers;
@@ -22,6 +23,7 @@ using Content.Client.Voting;
using Content.Shared.Administration.Logs;
using Content.Client.Lobby;
using Content.Client.Players.RateLimiting;
using Content.Shared._CP14.Sponsor;
using Content.Shared.Administration.Managers;
using Content.Shared.Chat;
using Content.Shared.Players.PlayTimeTracking;
@@ -38,6 +40,7 @@ namespace Content.Client.IoC
//CP14
collection.Register<DiscordAuthManager>();
collection.Register<JoinQueueManager>();
collection.Register<ICP14SponsorManager, ClientSponsorSystem>();
//CP14 end
collection.Register<IParallaxManager, ParallaxManager>();
collection.Register<IChatManager, ChatManager>();

View File

@@ -31,7 +31,7 @@ namespace Content.Client.Jittering
var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
jittering.StartOffset = sprite.Offset;
_animationPlayer.Play(uid, animationPlayer, GetAnimation(jittering, sprite), _jitterAnimationKey);
_animationPlayer.Play((uid, animationPlayer), GetAnimation(jittering, sprite), _jitterAnimationKey);
}
private void OnShutdown(EntityUid uid, JitteringComponent jittering, ComponentShutdown args)
@@ -53,7 +53,7 @@ namespace Content.Client.Jittering
if (TryComp(uid, out AnimationPlayerComponent? animationPlayer)
&& TryComp(uid, out SpriteComponent? sprite))
_animationPlayer.Play(uid, animationPlayer, GetAnimation(jittering, sprite), _jitterAnimationKey);
_animationPlayer.Play((uid, animationPlayer), GetAnimation(jittering, sprite), _jitterAnimationKey);
}
private Animation GetAnimation(JitteringComponent jittering, SpriteComponent sprite)

View File

@@ -85,7 +85,7 @@ public sealed class RotatingLightSystem : SharedRotatingLightSystem
if (!_animations.HasRunningAnimation(uid, player, AnimKey))
{
_animations.Play(uid, player, GetAnimation(comp.Speed), AnimKey);
_animations.Play((uid, player), GetAnimation(comp.Speed), AnimKey);
}
}
}

View File

@@ -2,7 +2,6 @@ using Content.Shared.Light;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Random;
@@ -53,13 +52,14 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem<PoweredLight
/// </summary>
private void OnAnimationCompleted(EntityUid uid, PoweredLightVisualsComponent comp, AnimationCompletedEvent args)
{
if (!TryComp<AnimationPlayerComponent>(uid, out var animationPlayer))
return;
if (args.Key != PoweredLightVisualsComponent.BlinkingAnimationKey)
return;
if(!comp.IsBlinking)
return;
AnimationSystem.Play(uid, Comp<AnimationPlayerComponent>(uid), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey);
AnimationSystem.Play((uid, animationPlayer), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey);
}
/// <summary>
@@ -76,7 +76,7 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem<PoweredLight
var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
if (shouldBeBlinking)
{
AnimationSystem.Play(uid, animationPlayer, BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey);
AnimationSystem.Play((uid, animationPlayer), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey);
}
else if (AnimationSystem.HasRunningAnimation(uid, animationPlayer, PoweredLightVisualsComponent.BlinkingAnimationKey))
{

View File

@@ -22,6 +22,7 @@ using Content.Shared.Preferences.Loadouts;
using Content.Shared.Roles;
using Content.Shared.Traits;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface;
@@ -51,6 +52,8 @@ namespace Content.Client.Lobby.UI
private readonly JobRequirementsManager _requirements;
private readonly LobbyUIController _controller;
private readonly SpriteSystem _sprite;
private FlavorText.FlavorText? _flavorText;
private TextEdit? _flavorTextEdit;
@@ -128,7 +131,7 @@ namespace Content.Client.Lobby.UI
_resManager = resManager;
_requirements = requirements;
_controller = UserInterfaceManager.GetUIController<LobbyUIController>();
_sprite = _entManager.System<SpriteSystem>();
ImportButton.OnPressed += args =>
{
ImportProfile();
@@ -907,7 +910,7 @@ namespace Content.Client.Lobby.UI
VerticalAlignment = VAlignment.Center
};
var jobIcon = _prototypeManager.Index(job.Icon);
icon.Texture = jobIcon.Icon.Frame0();
icon.Texture = _sprite.Frame0(jobIcon.Icon);
selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon, job.Guides);
if (!_requirements.IsAllowed(job, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Numerics;
using Content.Client.Administration.Managers;
using Content.Client.ContextMenu.UI;
@@ -149,7 +149,7 @@ public sealed class MappingState : GameplayStateBase
{
Deselect();
var coords = args.Coordinates.ToMap(_entityManager, _transform);
var coords = _transform.ToMapCoordinates(args.Coordinates);
if (_verbs.TryGetEntityMenuEntities(coords, out var entities))
_entityMenuController.OpenRootMenu(entities);

View File

@@ -1,7 +1,6 @@
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Robust.Client.GameObjects;
using Robust.Shared.Timing;
namespace Content.Client.Movement.Systems;
@@ -10,8 +9,6 @@ namespace Content.Client.Movement.Systems;
/// </summary>
public sealed class ClientSpriteMovementSystem : SharedSpriteMovementSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
private EntityQuery<SpriteComponent> _spriteQuery;
public override void Initialize()

View File

@@ -1,8 +1,6 @@
using System.Numerics;
using Content.Client.Movement.Components;
using Content.Shared.Camera;
using Content.Shared.Inventory;
using Content.Shared.Movement.Systems;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Shared.Map;
@@ -16,8 +14,6 @@ public sealed partial class EyeCursorOffsetSystem : EntitySystem
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedContentEyeSystem _contentEye = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IClyde _clyde = default!;
// This value is here to make sure the user doesn't have to move their mouse
@@ -42,7 +38,7 @@ public sealed partial class EyeCursorOffsetSystem : EntitySystem
public Vector2? OffsetAfterMouse(EntityUid uid, EyeCursorOffsetComponent? component)
{
var localPlayer = _player.LocalPlayer?.ControlledEntity;
var localPlayer = _player.LocalEntity;
var mousePos = _inputManager.MouseScreenPosition;
var screenSize = _clyde.MainWindow.Size;
var minValue = MathF.Min(screenSize.X / 2, screenSize.Y / 2) * _edgeOffset;

View File

@@ -49,13 +49,17 @@ public sealed class JetpackSystem : SharedJetpackSystem
// TODO: Please don't copy-paste this I beg
// make a generic particle emitter system / actual particles instead.
var query = EntityQueryEnumerator<ActiveJetpackComponent>();
var query = EntityQueryEnumerator<ActiveJetpackComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var comp))
while (query.MoveNext(out var uid, out var comp, out var xform))
{
if (_timing.CurTime < comp.TargetTime)
continue;
if (_transform.InRange(xform.Coordinates, comp.LastCoordinates, comp.MaxDistance))
{
if (_timing.CurTime < comp.TargetTime)
continue;
}
comp.LastCoordinates = _transform.GetMoverCoordinates(xform.Coordinates);
comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
CreateParticles(uid);

View File

@@ -0,0 +1,42 @@
using System.Numerics;
using Content.Shared.CCVar;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Robust.Client.Player;
using Robust.Shared.Physics.Components;
using Robust.Shared.Timing;
namespace Content.Client.Movement.Systems;
public sealed class MobCollisionSystem : SharedMobCollisionSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _player = default!;
public override void Update(float frameTime)
{
if (!CfgManager.GetCVar(CCVars.MovementMobPushing))
return;
if (_timing.IsFirstTimePredicted)
{
var player = _player.LocalEntity;
if (MobQuery.TryComp(player, out var comp) && PhysicsQuery.TryComp(player, out var physics))
{
HandleCollisions((player.Value, comp, physics), frameTime);
}
}
base.Update(frameTime);
}
protected override void RaiseCollisionEvent(EntityUid uid, Vector2 direction, float speedMod)
{
RaisePredictiveEvent(new MobCollisionMessage()
{
Direction = direction,
SpeedModifier = speedMod,
});
}
}

View File

@@ -132,7 +132,7 @@ public sealed class TargetOutlineSystem : EntitySystem
// TODO: Duplicated in SpriteSystem and DragDropSystem. Should probably be cached somewhere for a frame?
var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition).Position;
var bounds = new Box2(mousePos - LookupVector, mousePos + LookupVector);
var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, bounds, LookupFlags.Approximate | LookupFlags.Static);
var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentEye.Position.MapId, bounds, LookupFlags.Approximate | LookupFlags.Static);
var spriteQuery = GetEntityQuery<SpriteComponent>();
foreach (var entity in pvsEntities)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Collections.Generic;
using System.Diagnostics;

View File

@@ -62,16 +62,16 @@ public sealed class MoverController : SharedMoverController
private void OnRelayPlayerAttached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
PhysicsSystem.UpdateIsPredicted(entity.Owner);
PhysicsSystem.UpdateIsPredicted(entity.Comp.RelayEntity);
if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
}
private void OnRelayPlayerDetached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
PhysicsSystem.UpdateIsPredicted(entity.Owner);
PhysicsSystem.UpdateIsPredicted(entity.Comp.RelayEntity);
if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
}

View File

@@ -126,7 +126,8 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager
var reasons = new List<string>();
foreach (var requirement in requirements)
{
if (requirement.Check(_entManager, _prototypes, profile, _roles, out var jobReason))
//CP14 Add NetUserId for sponsorship checks
if (requirement.Check(_playerManager.LocalSession?.UserId, _entManager, _prototypes, profile, _roles, out var jobReason))
continue;
reasons.Add(jobReason.ToMarkup());

View File

@@ -85,7 +85,7 @@ public sealed class PopupOverlay : Overlay
foreach (var popup in _popup.WorldLabels)
{
var mapPos = popup.InitialPos.ToMap(_entManager, _transform);
var mapPos = _transform.ToMapCoordinates(popup.InitialPos);
if (mapPos.MapId != args.MapId)
continue;

View File

@@ -1,47 +0,0 @@
<ui:RadialMenu xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:rcd="clr-namespace:Content.Client.RCD"
BackButtonStyleClass="RadialMenuBackButton"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="450 450">
<!-- Note: The min size of the window just determine how close to the edge of the screen the center of the radial menu can be placed -->
<!-- The radial menu will try to open so that its center is located where the player's cursor is currently -->
<!-- Entry layer (shows main categories) -->
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False">
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-walls-and-flooring'}" TargetLayer="WallsAndFlooring" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/walls_and_flooring.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-windows-and-grilles'}" TargetLayer="WindowsAndGrilles" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/windows_and_grilles.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-airlocks'}" TargetLayer="Airlocks" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/airlocks.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-electrical'}" TargetLayer="Electrical" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/multicoil.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-lighting'}" TargetLayer="Lighting" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/lighting.png"/>
</ui:RadialMenuTextureButtonWithSector>
</ui:RadialContainer>
<!-- Walls and flooring -->
<ui:RadialContainer Name="WallsAndFlooring" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Windows and grilles -->
<ui:RadialContainer Name="WindowsAndGrilles" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Airlocks -->
<ui:RadialContainer Name="Airlocks" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Computer and machine frames -->
<ui:RadialContainer Name="Electrical" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Lighting -->
<ui:RadialContainer Name="Lighting" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
</ui:RadialMenu>

View File

@@ -1,172 +0,0 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Popups;
using Content.Shared.RCD;
using Content.Shared.RCD.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using System.Numerics;
namespace Content.Client.RCD;
[GenerateTypedNameReferences]
public sealed partial class RCDMenu : RadialMenu
{
[Dependency] private readonly EntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
private SharedPopupSystem _popup;
private SpriteSystem _sprites;
public event Action<ProtoId<RCDPrototype>>? SendRCDSystemMessageAction;
private EntityUid _owner;
public RCDMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
_popup = _entManager.System<SharedPopupSystem>();
_sprites = _entManager.System<SpriteSystem>();
OnChildAdded += AddRCDMenuButtonOnClickActions;
}
public void SetEntity(EntityUid uid)
{
_owner = uid;
Refresh();
}
public void Refresh()
{
// Find the main radial container
var main = FindControl<RadialContainer>("Main");
// Populate secondary radial containers
if (!_entManager.TryGetComponent<RCDComponent>(_owner, out var rcd))
return;
foreach (var protoId in rcd.AvailablePrototypes)
{
if (!_protoManager.TryIndex(protoId, out var proto))
continue;
if (proto.Mode == RcdMode.Invalid)
continue;
var parent = FindControl<RadialContainer>(proto.Category);
var tooltip = Loc.GetString(proto.SetName);
if ((proto.Mode == RcdMode.ConstructTile || proto.Mode == RcdMode.ConstructObject) &&
proto.Prototype != null && _protoManager.TryIndex(proto.Prototype, out var entProto, logError: false))
{
tooltip = Loc.GetString(entProto.Name);
}
tooltip = OopsConcat(char.ToUpper(tooltip[0]).ToString(), tooltip.Remove(0, 1));
var button = new RCDMenuButton()
{
SetSize = new Vector2(64f, 64f),
ToolTip = tooltip,
ProtoId = protoId,
};
if (proto.Sprite != null)
{
var tex = new TextureRect()
{
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Texture = _sprites.Frame0(proto.Sprite),
TextureScale = new Vector2(2f, 2f),
};
button.AddChild(tex);
}
parent.AddChild(button);
// Ensure that the button that transitions the menu to the associated category layer
// is visible in the main radial container (as these all start with Visible = false)
foreach (var child in main.Children)
{
if (child is not RadialMenuTextureButton castChild)
continue;
if (castChild.TargetLayer == proto.Category)
{
castChild.Visible = true;
break;
}
}
}
// Set up menu actions
foreach (var child in Children)
{
AddRCDMenuButtonOnClickActions(child);
}
}
private static string OopsConcat(string a, string b)
{
// This exists to prevent Roslyn being clever and compiling something that fails sandbox checks.
return a + b;
}
private void AddRCDMenuButtonOnClickActions(Control control)
{
var radialContainer = control as RadialContainer;
if (radialContainer == null)
return;
foreach (var child in radialContainer.Children)
{
var castChild = child as RCDMenuButton;
if (castChild == null)
continue;
castChild.OnButtonUp += _ =>
{
SendRCDSystemMessageAction?.Invoke(castChild.ProtoId);
if (_playerManager.LocalSession?.AttachedEntity != null &&
_protoManager.TryIndex(castChild.ProtoId, out var proto))
{
var msg = Loc.GetString("rcd-component-change-mode", ("mode", Loc.GetString(proto.SetName)));
if (proto.Mode == RcdMode.ConstructTile || proto.Mode == RcdMode.ConstructObject)
{
var name = Loc.GetString(proto.SetName);
if (proto.Prototype != null &&
_protoManager.TryIndex(proto.Prototype, out var entProto, logError: false))
name = entProto.Name;
msg = Loc.GetString("rcd-component-change-build-mode", ("name", name));
}
// Popup message
_popup.PopupClient(msg, _owner, _playerManager.LocalSession.AttachedEntity);
}
Close();
};
}
}
}
public sealed class RCDMenuButton : RadialMenuTextureButtonWithSector
{
public ProtoId<RCDPrototype> ProtoId { get; set; }
}

View File

@@ -1,20 +1,32 @@
using Content.Client.Popups;
using Content.Client.UserInterface.Controls;
using Content.Shared.RCD;
using Content.Shared.RCD.Components;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.RCD;
[UsedImplicitly]
public sealed class RCDMenuBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
private static readonly Dictionary<string, (string Tooltip, SpriteSpecifier Sprite)> PrototypesGroupingInfo
= new Dictionary<string, (string Tooltip, SpriteSpecifier Sprite)>
{
["WallsAndFlooring"] = ("rcd-component-walls-and-flooring", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/walls_and_flooring.png"))),
["WindowsAndGrilles"] = ("rcd-component-windows-and-grilles", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/windows_and_grilles.png"))),
["Airlocks"] = ("rcd-component-airlocks", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/airlocks.png"))),
["Electrical"] = ("rcd-component-electrical", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/multicoil.png"))),
["Lighting"] = ("rcd-component-lighting", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/lighting.png"))),
};
private RCDMenu? _menu;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
private SimpleRadialMenu? _menu;
public RCDMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
@@ -25,19 +37,107 @@ public sealed class RCDMenuBoundUserInterface : BoundUserInterface
{
base.Open();
_menu = this.CreateWindow<RCDMenu>();
_menu.SetEntity(Owner);
_menu.SendRCDSystemMessageAction += SendRCDSystemMessage;
if (!EntMan.TryGetComponent<RCDComponent>(Owner, out var rcd))
return;
// Open the menu, centered on the mouse
var vpSize = _displayManager.ScreenSize;
_menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize);
_menu = this.CreateWindow<SimpleRadialMenu>();
_menu.Track(Owner);
var models = ConvertToButtons(rcd.AvailablePrototypes);
_menu.SetButtons(models);
_menu.OpenOverMouseScreenPosition();
}
public void SendRCDSystemMessage(ProtoId<RCDPrototype> protoId)
private IEnumerable<RadialMenuNestedLayerOption> ConvertToButtons(HashSet<ProtoId<RCDPrototype>> prototypes)
{
Dictionary<string, List<RadialMenuActionOption>> buttonsByCategory = new();
foreach (var protoId in prototypes)
{
var prototype = _prototypeManager.Index(protoId);
if (!PrototypesGroupingInfo.TryGetValue(prototype.Category, out var groupInfo))
continue;
if (!buttonsByCategory.TryGetValue(prototype.Category, out var list))
{
list = new List<RadialMenuActionOption>();
buttonsByCategory.Add(prototype.Category, list);
}
var actionOption = new RadialMenuActionOption<RCDPrototype>(HandleMenuOptionClick, prototype)
{
Sprite = prototype.Sprite,
ToolTip = GetTooltip(prototype)
};
list.Add(actionOption);
}
var models = new RadialMenuNestedLayerOption[buttonsByCategory.Count];
var i = 0;
foreach (var (key, list) in buttonsByCategory)
{
var groupInfo = PrototypesGroupingInfo[key];
models[i] = new RadialMenuNestedLayerOption(list)
{
Sprite = groupInfo.Sprite,
ToolTip = Loc.GetString(groupInfo.Tooltip)
};
i++;
}
return models;
}
private void HandleMenuOptionClick(RCDPrototype proto)
{
// A predicted message cannot be used here as the RCD UI is closed immediately
// after this message is sent, which will stop the server from receiving it
SendMessage(new RCDSystemMessage(protoId));
SendMessage(new RCDSystemMessage(proto.ID));
if (_playerManager.LocalSession?.AttachedEntity == null)
return;
var msg = Loc.GetString("rcd-component-change-mode", ("mode", Loc.GetString(proto.SetName)));
if (proto.Mode is RcdMode.ConstructTile or RcdMode.ConstructObject)
{
var name = Loc.GetString(proto.SetName);
if (proto.Prototype != null &&
_prototypeManager.TryIndex(proto.Prototype, out var entProto, logError: false))
name = entProto.Name;
msg = Loc.GetString("rcd-component-change-build-mode", ("name", name));
}
// Popup message
var popup = EntMan.System<PopupSystem>();
popup.PopupClient(msg, Owner, _playerManager.LocalSession.AttachedEntity);
}
private string GetTooltip(RCDPrototype proto)
{
string tooltip;
if (proto.Mode is RcdMode.ConstructTile or RcdMode.ConstructObject
&& proto.Prototype != null
&& _prototypeManager.TryIndex(proto.Prototype, out var entProto, logError: false))
{
tooltip = Loc.GetString(entProto.Name);
}
else
{
tooltip = Loc.GetString(proto.SetName);
}
tooltip = OopsConcat(char.ToUpper(tooltip[0]).ToString(), tooltip.Remove(0, 1));
return tooltip;
}
private static string OopsConcat(string a, string b)
{
// This exists to prevent Roslyn being clever and compiling something that fails sandbox checks.
return a + b;
}
}

View File

@@ -52,7 +52,7 @@ public sealed class RotationVisualizerSystem : SharedRotationVisualsSystem
// Stop the current rotate animation and then start a new one
if (_animation.HasRunningAnimation(animationComp, animationKey))
{
_animation.Stop(animationComp, animationKey);
_animation.Stop((uid, animationComp), animationKey);
}
var animation = new Animation

View File

@@ -1,28 +1,46 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Silicons.StationAi;
using Robust.Client.UserInterface;
namespace Content.Client.Silicons.StationAi;
public sealed class StationAiBoundUserInterface : BoundUserInterface
public sealed class StationAiBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private StationAiMenu? _menu;
public StationAiBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
private SimpleRadialMenu? _menu;
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<StationAiMenu>();
_menu.Track(Owner);
_menu.OnAiRadial += args =>
var ev = new GetStationAiRadialEvent();
EntMan.EventBus.RaiseLocalEvent(Owner, ref ev);
_menu = this.CreateWindow<SimpleRadialMenu>();
_menu.Track(Owner);
var buttonModels = ConvertToButtons(ev.Actions);
_menu.SetButtons(buttonModels);
_menu.Open();
}
private IEnumerable<RadialMenuActionOption> ConvertToButtons(IReadOnlyList<StationAiRadial> actions)
{
var models = new RadialMenuActionOption[actions.Count];
for (int i = 0; i < actions.Count; i++)
{
SendPredictedMessage(new StationAiRadialMessage()
var action = actions[i];
models[i] = new RadialMenuActionOption<BaseStationAiAction>(HandleRadialMenuClick, action.Event)
{
Event = args,
});
};
Sprite = action.Sprite,
ToolTip = action.Tooltip
};
}
return models;
}
private void HandleRadialMenuClick(BaseStationAiAction p)
{
SendPredictedMessage(new StationAiRadialMessage { Event = p });
}
}

View File

@@ -1,13 +0,0 @@
<ui:RadialMenu xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
BackButtonStyleClass="RadialMenuBackButton"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="450 450">
<!-- Main -->
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False">
</ui:RadialContainer>
</ui:RadialMenu>

View File

@@ -1,126 +0,0 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Silicons.StationAi;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client.Silicons.StationAi;
[GenerateTypedNameReferences]
public sealed partial class StationAiMenu : RadialMenu
{
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
public event Action<BaseStationAiAction>? OnAiRadial;
private EntityUid _tracked;
public StationAiMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
}
public void Track(EntityUid owner)
{
_tracked = owner;
if (!_entManager.EntityExists(_tracked))
{
Close();
return;
}
BuildButtons();
UpdatePosition();
}
private void BuildButtons()
{
var ev = new GetStationAiRadialEvent();
_entManager.EventBus.RaiseLocalEvent(_tracked, ref ev);
var main = FindControl<RadialContainer>("Main");
main.DisposeAllChildren();
var sprites = _entManager.System<SpriteSystem>();
foreach (var action in ev.Actions)
{
// TODO: This radial boilerplate is quite annoying
var button = new StationAiMenuButton(action.Event)
{
SetSize = new Vector2(64f, 64f),
ToolTip = action.Tooltip != null ? Loc.GetString(action.Tooltip) : null,
};
if (action.Sprite != null)
{
var texture = sprites.Frame0(action.Sprite);
var scale = Vector2.One;
if (texture.Width <= 32)
{
scale *= 2;
}
var tex = new TextureRect
{
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Texture = texture,
TextureScale = scale,
};
button.AddChild(tex);
}
button.OnPressed += args =>
{
OnAiRadial?.Invoke(action.Event);
Close();
};
main.AddChild(button);
}
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
UpdatePosition();
}
private void UpdatePosition()
{
if (!_entManager.TryGetComponent(_tracked, out TransformComponent? xform))
{
Close();
return;
}
if (!xform.Coordinates.IsValid(_entManager))
{
Close();
return;
}
var coords = _entManager.System<SpriteSystem>().GetSpriteScreenCoordinates((_tracked, null, xform));
if (!coords.IsValid)
{
Close();
return;
}
OpenScreenAt(coords.Position, _clyde);
}
}
public sealed class StationAiMenuButton(BaseStationAiAction action) : RadialMenuTextureButtonWithSector
{
public BaseStationAiAction Action = action;
}

View File

@@ -56,10 +56,10 @@ public sealed class RadiationCollectorSystem : VisualizerSystem<RadiationCollect
switch (targetState)
{
case RadiationCollectorVisualState.Activating:
AnimationSystem.Play(uid, animPlayer, comp.ActivateAnimation, RadiationCollectorComponent.AnimationKey);
AnimationSystem.Play((uid, animPlayer), comp.ActivateAnimation, RadiationCollectorComponent.AnimationKey);
break;
case RadiationCollectorVisualState.Deactivating:
AnimationSystem.Play(uid, animPlayer, comp.DeactiveAnimation, RadiationCollectorComponent.AnimationKey);
AnimationSystem.Play((uid, animPlayer), comp.DeactiveAnimation, RadiationCollectorComponent.AnimationKey);
break;
case RadiationCollectorVisualState.Active:

View File

@@ -11,6 +11,8 @@ namespace Content.Client.SprayPainter.UI;
public sealed partial class SprayPainterWindow : DefaultWindow
{
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
[Dependency] private readonly ILocalizationManager _loc = default!;
private readonly SpriteSystem _spriteSystem;
public Action<ItemList.ItemListSelectedEventArgs>? OnSpritePicked;
@@ -32,17 +34,17 @@ public sealed partial class SprayPainterWindow : DefaultWindow
_spriteSystem = _sysMan.GetEntitySystem<SpriteSystem>();
}
private static string GetColorLocString(string? colorKey)
private string GetColorLocString(string? colorKey)
{
if (string.IsNullOrEmpty(colorKey))
return Loc.GetString("pipe-painter-no-color-selected");
var locKey = colorLocKeyPrefix + colorKey;
if (!Loc.TryGetString(locKey, out var locString))
if (!_loc.TryGetString(locKey, out var locString))
locString = colorKey;
return locString;
}
}
public string? IndexToColorKey(int index)
{

View File

@@ -1,9 +1,16 @@
using System.Numerics;
using Content.Client.Gameplay;
using Content.Shared.Sprite;
using Robust.Client.GameObjects;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.State;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface;
using Robust.Shared.Map;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
namespace Content.Client.Sprite;
@@ -16,13 +23,20 @@ public sealed class SpriteFadeSystem : EntitySystem
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly FixtureSystem _fixtures = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
private List<(MapCoordinates Point, bool ExcludeBoundingBox)> _points = new();
private readonly HashSet<FadingSpriteComponent> _comps = new();
private EntityQuery<SpriteComponent> _spriteQuery;
private EntityQuery<SpriteFadeComponent> _fadeQuery;
private EntityQuery<FadingSpriteComponent> _fadingQuery;
private EntityQuery<FixturesComponent> _fixturesQuery;
private const float TargetAlpha = 0.4f;
private const float ChangeRate = 1f;
@@ -34,6 +48,7 @@ public sealed class SpriteFadeSystem : EntitySystem
_spriteQuery = GetEntityQuery<SpriteComponent>();
_fadeQuery = GetEntityQuery<SpriteFadeComponent>();
_fadingQuery = GetEntityQuery<FadingSpriteComponent>();
_fixturesQuery = GetEntityQuery<FixturesComponent>();
SubscribeLocalEvent<FadingSpriteComponent, ComponentShutdown>(OnFadingShutdown);
}
@@ -46,46 +61,89 @@ public sealed class SpriteFadeSystem : EntitySystem
sprite.Color = sprite.Color.WithAlpha(component.OriginalAlpha);
}
public override void FrameUpdate(float frameTime)
/// <summary>
/// Adds sprites to the fade set, and brings their alpha downwards
/// </summary>
private void FadeIn(float change)
{
base.FrameUpdate(frameTime);
var player = _playerManager.LocalEntity;
var change = ChangeRate * frameTime;
// ExcludeBoundingBox is set if we don't want to fade this sprite within the collision bounding boxes for the given POI
_points.Clear();
if (TryComp(player, out TransformComponent? playerXform) &&
_stateManager.CurrentState is GameplayState state &&
_spriteQuery.TryGetComponent(player, out var playerSprite))
if (_uiManager.CurrentlyHovered is IViewportControl vp
&& _inputManager.MouseScreenPosition.IsValid)
{
var mapPos = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform);
_points.Add((vp.PixelToMap(_inputManager.MouseScreenPosition.Position), true));
}
// Also want to handle large entities even if they may not be clickable.
foreach (var ent in state.GetClickableEntities(mapPos))
if (TryComp(player, out TransformComponent? playerXform))
{
_points.Add((_transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform), false));
}
if (_stateManager.CurrentState is GameplayState state && _spriteQuery.TryGetComponent(player, out var playerSprite))
{
foreach (var (mapPos, excludeBB) in _points)
{
if (ent == player ||
!_fadeQuery.HasComponent(ent) ||
!_spriteQuery.TryGetComponent(ent, out var sprite) ||
sprite.DrawDepth < playerSprite.DrawDepth)
// Also want to handle large entities even if they may not be clickable.
foreach (var ent in state.GetClickableEntities(mapPos, excludeFaded: false))
{
continue;
}
if (ent == player ||
!_fadeQuery.HasComponent(ent) ||
!_spriteQuery.TryGetComponent(ent, out var sprite) ||
sprite.DrawDepth < playerSprite.DrawDepth)
{
continue;
}
if (!_fadingQuery.TryComp(ent, out var fading))
{
fading = AddComp<FadingSpriteComponent>(ent);
fading.OriginalAlpha = sprite.Color.A;
}
// If it intersects a fixture ignore it.
if (excludeBB && _fixturesQuery.TryComp(ent, out var body))
{
var transform = _physics.GetPhysicsTransform(ent);
var collided = false;
_comps.Add(fading);
var newColor = Math.Max(sprite.Color.A - change, TargetAlpha);
foreach (var fixture in body.Fixtures.Values)
{
if (!fixture.Hard)
continue;
if (!sprite.Color.A.Equals(newColor))
{
sprite.Color = sprite.Color.WithAlpha(newColor);
if (_fixtures.TestPoint(fixture.Shape, transform, mapPos.Position))
{
collided = true;
break;
}
}
// Check next entity
if (collided)
{
continue;
}
}
if (!_fadingQuery.TryComp(ent, out var fading))
{
fading = AddComp<FadingSpriteComponent>(ent);
fading.OriginalAlpha = sprite.Color.A;
}
_comps.Add(fading);
var newColor = Math.Max(sprite.Color.A - change, TargetAlpha);
if (!sprite.Color.A.Equals(newColor))
{
sprite.Color = sprite.Color.WithAlpha(newColor);
}
}
}
}
}
/// <summary>
/// Bring sprites back up to their original alpha if they aren't in the fade set, and removes their fade component when done
/// </summary>
private void FadeOut(float change)
{
var query = AllEntityQuery<FadingSpriteComponent>();
while (query.MoveNext(out var uid, out var comp))
{
@@ -106,6 +164,16 @@ public sealed class SpriteFadeSystem : EntitySystem
RemCompDeferred<FadingSpriteComponent>(uid);
}
}
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
var change = ChangeRate * frameTime;
FadeIn(change);
FadeOut(change);
_comps.Clear();
}

View File

@@ -0,0 +1,5 @@
using Content.Shared.Temperature.Systems;
namespace Content.Client.Temperature.Systems;
public sealed partial class EntityHeaterSystem : SharedEntityHeaterSystem;

View File

@@ -0,0 +1,121 @@
using Content.Client.Power;
using Content.Shared.Turrets;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
namespace Content.Client.Turrets;
public sealed partial class DeployableTurretSystem : SharedDeployableTurretSystem
{
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DeployableTurretComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<DeployableTurretComponent, AnimationCompletedEvent>(OnAnimationCompleted);
SubscribeLocalEvent<DeployableTurretComponent, AppearanceChangeEvent>(OnAppearanceChange);
}
private void OnComponentInit(Entity<DeployableTurretComponent> ent, ref ComponentInit args)
{
ent.Comp.DeploymentAnimation = new Animation
{
Length = TimeSpan.FromSeconds(ent.Comp.DeploymentLength),
AnimationTracks = {
new AnimationTrackSpriteFlick() {
LayerKey = DeployableTurretVisuals.Turret,
KeyFrames = {new AnimationTrackSpriteFlick.KeyFrame(ent.Comp.DeployingState, 0f)}
},
}
};
ent.Comp.RetractionAnimation = new Animation
{
Length = TimeSpan.FromSeconds(ent.Comp.RetractionLength),
AnimationTracks = {
new AnimationTrackSpriteFlick() {
LayerKey = DeployableTurretVisuals.Turret,
KeyFrames = {new AnimationTrackSpriteFlick.KeyFrame(ent.Comp.RetractingState, 0f)}
},
}
};
}
private void OnAnimationCompleted(Entity<DeployableTurretComponent> ent, ref AnimationCompletedEvent args)
{
if (args.Key != DeployableTurretComponent.AnimationKey)
return;
if (!TryComp<SpriteComponent>(ent, out var sprite))
return;
if (!_appearance.TryGetData<DeployableTurretState>(ent, DeployableTurretVisuals.Turret, out var state))
state = ent.Comp.VisualState;
// Convert to terminal state
var targetState = state & DeployableTurretState.Deployed;
UpdateVisuals(ent, targetState, sprite, args.AnimationPlayer);
}
private void OnAppearanceChange(Entity<DeployableTurretComponent> ent, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
if (!TryComp<AnimationPlayerComponent>(ent, out var animPlayer))
return;
if (!_appearance.TryGetData<DeployableTurretState>(ent, DeployableTurretVisuals.Turret, out var state, args.Component))
state = DeployableTurretState.Retracted;
UpdateVisuals(ent, state, args.Sprite, animPlayer);
}
private void UpdateVisuals(Entity<DeployableTurretComponent> ent, DeployableTurretState state, SpriteComponent sprite, AnimationPlayerComponent? animPlayer = null)
{
if (!Resolve(ent, ref animPlayer))
return;
if (_animation.HasRunningAnimation(ent, animPlayer, DeployableTurretComponent.AnimationKey))
return;
if (state == ent.Comp.VisualState)
return;
var targetState = state & DeployableTurretState.Deployed;
var destinationState = ent.Comp.VisualState & DeployableTurretState.Deployed;
if (targetState != destinationState)
targetState = targetState | DeployableTurretState.Retracting;
ent.Comp.VisualState = state;
// Toggle layer visibility
sprite.LayerSetVisible(DeployableTurretVisuals.Weapon, (targetState & DeployableTurretState.Deployed) > 0);
sprite.LayerSetVisible(PowerDeviceVisualLayers.Powered, HasAmmo(ent) && targetState == DeployableTurretState.Retracted);
// Change the visual state
switch (targetState)
{
case DeployableTurretState.Deploying:
_animation.Play((ent, animPlayer), (Animation)ent.Comp.DeploymentAnimation, DeployableTurretComponent.AnimationKey);
break;
case DeployableTurretState.Retracting:
_animation.Play((ent, animPlayer), (Animation)ent.Comp.RetractionAnimation, DeployableTurretComponent.AnimationKey);
break;
case DeployableTurretState.Deployed:
sprite.LayerSetState(DeployableTurretVisuals.Turret, ent.Comp.DeployedState);
break;
case DeployableTurretState.Retracted:
sprite.LayerSetState(DeployableTurretVisuals.Turret, ent.Comp.RetractedState);
break;
}
}
}

View File

@@ -1,10 +1,10 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using System.Linq;
using System.Numerics;
using Content.Shared.Input;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Input;
namespace Content.Client.UserInterface.Controls;
@@ -143,11 +143,8 @@ public class RadialMenu : BaseWindow
return children.First(x => x.Visible);
}
public bool TryToMoveToNewLayer(string newLayer)
public bool TryToMoveToNewLayer(Control newLayer)
{
if (newLayer == string.Empty)
return false;
var currentLayer = GetCurrentActiveLayer();
if (currentLayer == null)
@@ -161,7 +158,7 @@ public class RadialMenu : BaseWindow
continue;
// Hide layers which are not of interest
if (result == true || child.Name != newLayer)
if (result == true || child != newLayer)
{
child.Visible = false;
}
@@ -186,6 +183,19 @@ public class RadialMenu : BaseWindow
return result;
}
public bool TryToMoveToNewLayer(string targetLayerControlName)
{
foreach (var child in Children)
{
if (child.Name == targetLayerControlName && child is RadialContainer)
{
return TryToMoveToNewLayer(child);
}
}
return false;
}
public void ReturnToPreviousLayer()
{
// Close the menu if the traversal path is empty
@@ -296,9 +306,15 @@ public sealed class RadialMenuOuterAreaButton : RadialMenuTextureButtonBase
public class RadialMenuTextureButton : RadialMenuTextureButtonBase
{
/// <summary>
/// Upon clicking this button the radial menu will be moved to the named layer
/// Upon clicking this button the radial menu will be moved to the layer of this control.
/// </summary>
public string TargetLayer { get; set; } = string.Empty;
public Control? TargetLayer { get; set; }
/// <summary>
/// Other way to set navigation to other container, as <see cref="TargetLayer"/>,
/// but using <see cref="Control.Name"/> property of target <see cref="RadialContainer"/>.
/// </summary>
public string? TargetLayerControlName { get; set; }
/// <summary>
/// A simple texture button that can move the user to a different layer within a radial menu
@@ -311,7 +327,7 @@ public class RadialMenuTextureButton : RadialMenuTextureButtonBase
private void OnClicked(ButtonEventArgs args)
{
if (TargetLayer == string.Empty)
if (TargetLayer == null && TargetLayerControlName == null)
return;
var parent = FindParentMultiLayerContainer(this);
@@ -319,7 +335,14 @@ public class RadialMenuTextureButton : RadialMenuTextureButtonBase
if (parent == null)
return;
parent.TryToMoveToNewLayer(TargetLayer);
if (TargetLayer != null)
{
parent.TryToMoveToNewLayer(TargetLayer);
}
else
{
parent.TryToMoveToNewLayer(TargetLayerControlName!);
}
}
private RadialMenu? FindParentMultiLayerContainer(Control control)
@@ -387,7 +410,7 @@ public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadia
private Color _hoverBorderColorSrgb = Color.ToSrgb(new Color(87, 91, 127, 128));
/// <summary>
/// Marker, that control should render border of segment. Is false by default.
/// Marker, that controls if border of segment should be rendered. Is false by default.
/// </summary>
/// <remarks>
/// By default color of border is same as color of background. Use <see cref="BorderColor"/>
@@ -400,13 +423,6 @@ public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadia
/// </summary>
public bool DrawBackground { get; set; } = true;
/// <summary>
/// Marker, that control should render separator lines.
/// Separator lines are used to visually separate sector of radial menu items.
/// Is true by default
/// </summary>
public bool DrawSeparators { get; set; } = true;
/// <summary>
/// Color of background in non-hovered state. Accepts RGB color, works with sRGB for DrawPrimitive internally.
/// </summary>
@@ -520,7 +536,7 @@ public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadia
DrawAnnulusSector(handle, containerCenter, _innerRadius * UIScale, _outerRadius * UIScale, angleFrom, angleTo, borderColor, false);
}
if (!_isWholeCircle && DrawSeparators)
if (!_isWholeCircle && DrawBorder)
{
DrawSeparatorLines(handle, containerCenter, _innerRadius * UIScale, _outerRadius * UIScale, angleFrom, angleTo, SeparatorColor);
}

View File

@@ -0,0 +1,8 @@
<ui:SimpleRadialMenu xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
BackButtonStyleClass="RadialMenuBackButton"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="450 450">
</ui:SimpleRadialMenu>

View File

@@ -0,0 +1,279 @@
using Robust.Client.UserInterface;
using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Shared.Utility;
using Robust.Client.GameObjects;
using Robust.Shared.Timing;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Input;
namespace Content.Client.UserInterface.Controls;
[GenerateTypedNameReferences]
public partial class SimpleRadialMenu : RadialMenu
{
private EntityUid? _attachMenuToEntity;
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
public SimpleRadialMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
}
public void Track(EntityUid owner)
{
_attachMenuToEntity = owner;
}
public void SetButtons(IEnumerable<RadialMenuOption> models, SimpleRadialMenuSettings? settings = null)
{
ClearExistingChildrenRadialButtons();
var sprites = _entManager.System<SpriteSystem>();
Fill(models, sprites, Children, settings ?? new SimpleRadialMenuSettings());
}
public void OpenOverMouseScreenPosition()
{
var vpSize = _clyde.ScreenSize;
OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize);
}
private void Fill(
IEnumerable<RadialMenuOption> models,
SpriteSystem sprites,
ICollection<Control> rootControlChildren,
SimpleRadialMenuSettings settings
)
{
var rootContainer = new RadialContainer
{
HorizontalExpand = true,
VerticalExpand = true,
InitialRadius = settings.DefaultContainerRadius,
ReserveSpaceForHiddenChildren = false,
Visible = true
};
rootControlChildren.Add(rootContainer);
foreach (var model in models)
{
if (model is RadialMenuNestedLayerOption nestedMenuModel)
{
var linkButton = RecursiveContainerExtraction(sprites, rootControlChildren, nestedMenuModel, settings);
linkButton.Visible = true;
rootContainer.AddChild(linkButton);
}
else
{
var rootButtons = ConvertToButton(model, sprites, settings, false);
rootContainer.AddChild(rootButtons);
}
}
}
private RadialMenuTextureButton RecursiveContainerExtraction(
SpriteSystem sprites,
ICollection<Control> rootControlChildren,
RadialMenuNestedLayerOption model,
SimpleRadialMenuSettings settings
)
{
var container = new RadialContainer
{
HorizontalExpand = true,
VerticalExpand = true,
InitialRadius = model.ContainerRadius!.Value,
ReserveSpaceForHiddenChildren = false,
Visible = false
};
foreach (var nested in model.Nested)
{
if (nested is RadialMenuNestedLayerOption nestedMenuModel)
{
var linkButton = RecursiveContainerExtraction(sprites, rootControlChildren, nestedMenuModel, settings);
container.AddChild(linkButton);
}
else
{
var button = ConvertToButton(nested, sprites, settings, false);
container.AddChild(button);
}
}
rootControlChildren.Add(container);
var thisLayerLinkButton = ConvertToButton(model, sprites, settings, true);
thisLayerLinkButton.TargetLayer = container;
return thisLayerLinkButton;
}
private RadialMenuTextureButton ConvertToButton(
RadialMenuOption model,
SpriteSystem sprites,
SimpleRadialMenuSettings settings,
bool haveNested
)
{
var button = settings.UseSectors
? ConvertToButtonWithSector(model, settings)
: new RadialMenuTextureButton();
button.SetSize = new Vector2(64f, 64f);
button.ToolTip = model.ToolTip;
if (model.Sprite != null)
{
var scale = Vector2.One;
var texture = sprites.Frame0(model.Sprite);
if (texture.Width <= 32)
{
scale *= 2;
}
button.TextureNormal = texture;
button.Scale = scale;
}
if (model is RadialMenuActionOption actionOption)
{
button.OnPressed += _ =>
{
actionOption.OnPressed?.Invoke();
if(!haveNested)
Close();
};
}
return button;
}
private static RadialMenuTextureButtonWithSector ConvertToButtonWithSector(RadialMenuOption model, SimpleRadialMenuSettings settings)
{
var button = new RadialMenuTextureButtonWithSector
{
DrawBorder = settings.DisplayBorders,
DrawBackground = !settings.NoBackground
};
if (model.BackgroundColor.HasValue)
{
button.BackgroundColor = model.BackgroundColor.Value;
}
if (model.HoverBackgroundColor.HasValue)
{
button.HoverBackgroundColor = model.HoverBackgroundColor.Value;
}
return button;
}
private void ClearExistingChildrenRadialButtons()
{
var toRemove = new List<Control>(ChildCount);
foreach (var child in Children)
{
if (child != ContextualButton && child != MenuOuterAreaButton)
{
toRemove.Add(child);
}
}
foreach (var control in toRemove)
{
Children.Remove(control);
}
}
#region target entity tracking
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (_attachMenuToEntity != null)
{
UpdatePosition();
}
}
private void UpdatePosition()
{
if (!_entManager.TryGetComponent(_attachMenuToEntity, out TransformComponent? xform))
{
Close();
return;
}
if (!xform.Coordinates.IsValid(_entManager))
{
Close();
return;
}
var coords = _entManager.System<SpriteSystem>().GetSpriteScreenCoordinates((_attachMenuToEntity.Value, null, xform));
if (!coords.IsValid)
{
Close();
return;
}
OpenScreenAt(coords.Position, _clyde);
}
#endregion
}
public abstract class RadialMenuOption
{
public string? ToolTip { get; init; }
public SpriteSpecifier? Sprite { get; init; }
public Color? BackgroundColor { get; set; }
public Color? HoverBackgroundColor { get; set; }
}
public class RadialMenuActionOption(Action onPressed) : RadialMenuOption
{
public Action OnPressed { get; } = onPressed;
}
public class RadialMenuActionOption<T>(Action<T> onPressed, T data)
: RadialMenuActionOption(onPressed: () => onPressed(data));
public class RadialMenuNestedLayerOption(IReadOnlyCollection<RadialMenuOption> nested, float containerRadius = 100)
: RadialMenuOption
{
public float? ContainerRadius { get; } = containerRadius;
public IReadOnlyCollection<RadialMenuOption> Nested { get; } = nested;
}
public class SimpleRadialMenuSettings
{
/// <summary>
/// Default container draw radius. Is going to be further affected by per sector increment.
/// </summary>
public int DefaultContainerRadius = 100;
/// <summary>
/// Marker, if sector-buttons should be used.
/// </summary>
public bool UseSectors = true;
/// <summary>
/// Marker, if border of buttons should be rendered. Can only be used when <see cref="UseSectors"/> = true.
/// </summary>
public bool DisplayBorders = true;
/// <summary>
/// Marker, if sector background should not be rendered. Can only be used when <see cref="UseSectors"/> = true.
/// </summary>
public bool NoBackground = false;
}

View File

@@ -482,7 +482,7 @@ public sealed class ChatUIController : UIController
private void EnqueueSpeechBubble(EntityUid entity, ChatMessage message, SpeechBubble.SpeechType speechType)
{
// Don't enqueue speech bubbles for other maps. TODO: Support multiple viewports/maps?
if (EntityManager.GetComponent<TransformComponent>(entity).MapID != _eye.CurrentMap)
if (EntityManager.GetComponent<TransformComponent>(entity).MapID != _eye.CurrentEye.Position.MapId)
return;
if (!_queuedSpeechBubbles.TryGetValue(entity, out var queueData))

View File

@@ -1,16 +1,17 @@
using Content.Client.Chat.UI;
using Content.Client.Gameplay;
using Content.Client.UserInterface.Controls;
using Content.Shared.Chat;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Input;
using Content.Shared.Speech;
using Content.Shared.Whitelist;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controllers;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input.Binding;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Emotes;
@@ -18,11 +19,19 @@ namespace Content.Client.UserInterface.Systems.Emotes;
public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayState>
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
private MenuButton? EmotesButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.EmotesButton;
private EmotesMenu? _menu;
private SimpleRadialMenu? _menu;
private static readonly Dictionary<EmoteCategory, (string Tooltip, SpriteSpecifier Sprite)> EmoteGroupingInfo
= new Dictionary<EmoteCategory, (string Tooltip, SpriteSpecifier Sprite)>
{
[EmoteCategory.General] = ("emote-menu-category-general", new SpriteSpecifier.Texture(new ResPath("/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"))),
[EmoteCategory.Hands] = ("emote-menu-category-hands", new SpriteSpecifier.Texture(new ResPath("/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"))),
[EmoteCategory.Vocal] = ("emote-menu-category-vocal", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Emotes/vocal.png"))),
};
public void OnStateEntered(GameplayState state)
{
@@ -42,10 +51,16 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
if (_menu == null)
{
// setup window
_menu = UIManager.CreateWindow<EmotesMenu>();
var prototypes = _prototypeManager.EnumeratePrototypes<EmotePrototype>();
var models = ConvertToButtons(prototypes);
_menu = new SimpleRadialMenu();
_menu.SetButtons(models);
_menu.Open();
_menu.OnClose += OnWindowClosed;
_menu.OnOpen += OnWindowOpen;
_menu.OnPlayEmote += OnPlayEmote;
if (EmotesButton != null)
EmotesButton.SetClickPressed(true);
@@ -56,16 +71,13 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
}
else
{
// Open the menu, centered on the mouse
var vpSize = _displayManager.ScreenSize;
_menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize);
_menu.OpenOverMouseScreenPosition();
}
}
else
{
_menu.OnClose -= OnWindowClosed;
_menu.OnOpen -= OnWindowOpen;
_menu.OnPlayEmote -= OnPlayEmote;
if (EmotesButton != null)
EmotesButton.SetClickPressed(false);
@@ -118,8 +130,62 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
_menu = null;
}
private void OnPlayEmote(ProtoId<EmotePrototype> protoId)
private IEnumerable<RadialMenuOption> ConvertToButtons(IEnumerable<EmotePrototype> emotePrototypes)
{
_entityManager.RaisePredictiveEvent(new PlayEmoteMessage(protoId));
var whitelistSystem = EntitySystemManager.GetEntitySystem<EntityWhitelistSystem>();
var player = _playerManager.LocalSession?.AttachedEntity;
Dictionary<EmoteCategory, List<RadialMenuOption>> emotesByCategory = new();
foreach (var emote in emotePrototypes)
{
if(emote.Category == EmoteCategory.Invalid)
continue;
// only valid emotes that have ways to be triggered by chat and player have access / no restriction on
if (emote.Category == EmoteCategory.Invalid
|| emote.ChatTriggers.Count == 0
|| !(player.HasValue && whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value))
|| whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value))
continue;
if (!emote.Available
&& EntityManager.TryGetComponent<SpeechComponent>(player.Value, out var speech)
&& !speech.AllowedEmotes.Contains(emote.ID))
continue;
if (!emotesByCategory.TryGetValue(emote.Category, out var list))
{
list = new List<RadialMenuOption>();
emotesByCategory.Add(emote.Category, list);
}
var actionOption = new RadialMenuActionOption<EmotePrototype>(HandleRadialButtonClick, emote)
{
Sprite = emote.Icon,
ToolTip = Loc.GetString(emote.Name)
};
list.Add(actionOption);
}
var models = new RadialMenuOption[emotesByCategory.Count];
var i = 0;
foreach (var (key, list) in emotesByCategory)
{
var tuple = EmoteGroupingInfo[key];
models[i] = new RadialMenuNestedLayerOption(list)
{
Sprite = tuple.Sprite,
ToolTip = Loc.GetString(tuple.Tooltip)
};
i++;
}
return models;
}
private void HandleRadialButtonClick(EmotePrototype prototype)
{
_entityManager.RaisePredictiveEvent(new PlayEmoteMessage(prototype.ID));
}
}

View File

@@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Client.Hands.Systems;

View File

@@ -3,7 +3,6 @@ using Content.Client.UserInterface.Systems.Gameplay;
using Content.Shared.CCVar;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
@@ -17,7 +16,6 @@ public sealed class ViewportUIController : UIController
[Dependency] private readonly IPlayerManager _playerMan = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[UISystemDependency] private readonly SharedTransformSystem? _transformSystem = default!;
public static readonly Vector2i ViewportSize = (EyeManager.PixelsPerMeter * 21, EyeManager.PixelsPerMeter * 15);
public const int ViewportHeight = 15;
private MainViewport? Viewport => UIManager.ActiveScreen?.GetWidget<MainViewport>();

View File

@@ -16,6 +16,7 @@ using Robust.Client.State;
using Robust.Shared.Configuration;
using Robust.Shared.Containers;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Verbs
@@ -36,6 +37,8 @@ namespace Content.Client.Verbs
private float _lookupSize;
private static readonly ProtoId<TagPrototype> HideContextMenuTag = "HideContextMenu";
/// <summary>
/// These flags determine what entities the user can see on the context menu.
/// </summary>
@@ -147,7 +150,7 @@ namespace Content.Client.Verbs
for (var i = entities.Count - 1; i >= 0; i--)
{
if (_tagSystem.HasTag(entities[i], "HideContextMenu"))
if (_tagSystem.HasTag(entities[i], HideContextMenuTag))
entities.RemoveSwap(i);
}

View File

@@ -10,6 +10,7 @@ public sealed class DiscordAuthManager
[Dependency] private readonly IStateManager _stateManager = default!;
public string AuthUrl { get; private set; } = "";
public string ErrorMessage { get; private set; } = "";
public void Initialize()
{
@@ -22,6 +23,7 @@ public sealed class DiscordAuthManager
if (_stateManager.CurrentState is DiscordAuthState)
return;
AuthUrl = msg.AuthUrl;
ErrorMessage = msg.ErrorMessage;
_stateManager.RequestStateChange<DiscordAuthState>();
}
}

View File

@@ -21,6 +21,7 @@ public sealed partial class DiscordAuthGui : Control
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
var link = _discordAuthManager.AuthUrl;
var errorMessage = _discordAuthManager.ErrorMessage;
AuthLinkEdit.SetText(link);
DLinkEdit.SetText(DiscordLink);
@@ -42,5 +43,10 @@ public sealed partial class DiscordAuthGui : Control
{
uriOpener.OpenUri(DiscordLink);
};
if (errorMessage != "")
{
InfoLabel.Text = errorMessage;
}
}
}

View File

@@ -1,10 +1,13 @@
using Content.Shared.CCVar;
using Content.Shared.Localizations;
using Robust.Client.GameObjects;
using Robust.Shared.Configuration;
namespace Content.Client._CP14.Localization;
public sealed class CP14LocalizationVisualsSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
public override void Initialize()
{
base.Initialize();
@@ -19,7 +22,7 @@ public sealed class CP14LocalizationVisualsSystem : EntitySystem
foreach (var (map, pDictionary) in visuals.Comp.MapStates)
{
if (!pDictionary.TryGetValue(ContentLocalizationManager.Culture, out var state))
if (!pDictionary.TryGetValue(_cfg.GetCVar(CCVars.Language), out var state))
return;
if (sprite.LayerMapTryGet(map, out _))

View File

@@ -0,0 +1,5 @@
using Content.Shared._CP14.MagicEnergy;
namespace Content.Client._CP14.MagicEnergy;
public sealed class CP14MagicEnergyCrystalSlotSystem : SharedCP14MagicEnergyCrystalSlotSystem;

View File

@@ -0,0 +1,5 @@
using Content.Shared._CP14.MagicEnergy;
namespace Content.Client._CP14.MagicEnergy;
public sealed class CP14MagicEnergySystem : SharedCP14MagicEnergySystem;

View File

@@ -1,3 +1,4 @@
using Content.Shared.CCVar;
using Robust.Shared;
using Robust.Shared.Configuration;
@@ -13,5 +14,14 @@ public sealed class CP14EdSystem : EntitySystem
public override void Initialize()
{
_cfg.SetCVar(CVars.EntitiesCategoryFilter, "ForkFiltered");
_cfg.OnValueChanged(CCVars.Language, OnLanguageChange, true);
_cfg.SetCVar(CVars.LocCultureName, _cfg.GetCVar(CCVars.Language));
}
private void OnLanguageChange(string obj)
{
_cfg.SetCVar(CVars.LocCultureName, obj);
}
}

View File

@@ -0,0 +1,49 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared._CP14.Sponsor;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Sponsor;
public sealed class ClientSponsorSystem : ICP14SponsorManager
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IClientNetManager _net = default!;
private CP14SponsorRolePrototype? _sponsorRole;
public void Initialize()
{
_net.RegisterNetMessage<CP14SponsorRoleUpdate>(OnSponsorRoleUpdate);
_net.Disconnect += NetOnDisconnected;
}
private void NetOnDisconnected(object? sender, NetDisconnectedArgs e)
{
_sponsorRole = null;
}
private void OnSponsorRoleUpdate(CP14SponsorRoleUpdate msg)
{
if (!_proto.TryIndex(msg.Role, out var indexedRole))
return;
_sponsorRole = indexedRole;
}
public bool TryGetSponsorOOCColor(NetUserId userId, [NotNullWhen(true)] out Color? color)
{
throw new NotImplementedException();
}
public bool UserHasFeature(NetUserId userId, ProtoId<CP14SponsorFeaturePrototype> feature, bool ifDisabledSponsorhip = true)
{
if (_sponsorRole is null)
return false;
if (!_proto.TryIndex(feature, out var indexedFeature))
return false;
return _sponsorRole.Priority >= indexedFeature.MinPriority;
}
}

View File

@@ -39,6 +39,7 @@ public static partial class PoolManager
(CVars.NetBufferSize.Name, "0"),
(CCVars.InteractionRateLimitCount.Name, "9999999"),
(CCVars.InteractionRateLimitPeriod.Name, "0.1"),
(CCVars.MovementMobPushing.Name, "false"),
};
public static async Task SetupCVars(RobustIntegrationTest.IntegrationInstance instance, PoolSettings settings)

View File

@@ -13,35 +13,6 @@ namespace Content.IntegrationTests.Tests.Access
public sealed class AccessReaderTest
{
/*
[Test]
public async Task TestProtoTags()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var protoManager = server.ResolveDependency<IPrototypeManager>();
var accessName = server.ResolveDependency<IComponentFactory>().GetComponentName(typeof(AccessReaderComponent));
await server.WaitAssertion(() =>
{
foreach (var ent in protoManager.EnumeratePrototypes<EntityPrototype>())
{
if (!ent.Components.TryGetComponent(accessName, out var access))
continue;
var reader = (AccessReaderComponent) access;
var allTags = reader.AccessLists.SelectMany(c => c).Union(reader.DenyTags);
foreach (var level in allTags)
{
Assert.That(protoManager.HasIndex<AccessLevelPrototype>(level), $"Invalid access level: {level} found on {ent}");
}
}
});
await pair.CleanReturnAsync();
}
[Test]
public async Task TestTags()
{

View File

@@ -80,7 +80,7 @@ namespace Content.IntegrationTests.Tests
var pos = clientEntManager.System<SharedTransformSystem>().GetWorldPosition(clientEnt);
hit = clientEntManager.System<ClickableSystem>().CheckClick((clientEnt, null, sprite, null), new Vector2(clickPosX, clickPosY) + pos, eye, out _, out _, out _);
hit = clientEntManager.System<ClickableSystem>().CheckClick((clientEnt, null, sprite, null), new Vector2(clickPosX, clickPosY) + pos, eye, false, out _, out _, out _);
});
await server.WaitPost(() =>

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Execution;
@@ -52,7 +52,7 @@ public sealed class SuicideCommandTests
name: test version of the material reclaimer
components:
- type: MaterialReclaimer";
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
/// <summary>
/// Run the suicide command in the console
/// Should successfully kill the player and ghost them
@@ -201,7 +201,7 @@ public sealed class SuicideCommandTests
mobStateComp = entManager.GetComponent<MobStateComponent>(player);
});
tagSystem.AddTag(player, "CannotSuicide");
tagSystem.AddTag(player, CannotSuicideTag);
// Check that running the suicide command kills the player
// and properly ghosts them without them being able to return to their body

View File

@@ -1,5 +1,6 @@
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Projectiles;
using Robust.Shared.GameObjects;
using Robust.Shared.Network;
namespace Content.IntegrationTests.Tests.Embedding;
@@ -88,4 +89,84 @@ public sealed class EmbedTest : InteractionTest
AssertExists(projectile);
await AssertEntityLookup(EmbeddableProtoId);
}
/// <summary>
/// Throws two embeddable projectiles at a target, then deletes them
/// one at a time, making sure that they are tracked correctly and that
/// the <see cref="EmbeddedContainerComponent"/> is removed once all
/// projectiles are gone.
/// </summary>
[Test]
public async Task TestDeleteWhileEmbedded()
{
// Spawn the target we're going to throw at
await SpawnTarget(TargetProtoId);
// Give the player the embeddable to throw
var projectile1 = await PlaceInHands(EmbeddableProtoId);
Assert.That(TryComp<EmbeddableProjectileComponent>(projectile1, out var embedComp),
$"{EmbeddableProtoId} does not have EmbeddableProjectileComponent.");
// Make sure the projectile isn't already embedded into anything
Assert.That(embedComp.EmbeddedIntoUid, Is.Null,
$"Projectile already embedded into {SEntMan.ToPrettyString(embedComp.EmbeddedIntoUid)}.");
// Have the player throw the embeddable at the target
await ThrowItem();
// Give the player a second embeddable to throw
var projectile2 = await PlaceInHands(EmbeddableProtoId);
Assert.That(TryComp<EmbeddableProjectileComponent>(projectile1, out var embedComp2),
$"{EmbeddableProtoId} does not have EmbeddableProjectileComponent.");
// Wait a moment for the projectile to hit and embed
await RunSeconds(0.5f);
// Make sure the projectile is embedded into the target
Assert.That(embedComp.EmbeddedIntoUid, Is.EqualTo(ToServer(Target)),
"First projectile not embedded into target.");
Assert.That(TryComp<EmbeddedContainerComponent>(out var containerComp),
"Target was not given EmbeddedContainerComponent.");
Assert.That(containerComp.EmbeddedObjects, Does.Contain(ToServer(projectile1)),
"Target is not tracking the first projectile as embedded.");
Assert.That(containerComp.EmbeddedObjects, Has.Count.EqualTo(1),
"Target has unexpected EmbeddedObjects count.");
// Wait for the cooldown between throws
await RunSeconds(Hands.ThrowCooldown.Seconds);
// Throw the second projectile
await ThrowItem();
// Wait a moment for the second projectile to hit and embed
await RunSeconds(0.5f);
Assert.That(embedComp2.EmbeddedIntoUid, Is.EqualTo(ToServer(Target)),
"Second projectile not embedded into target");
AssertComp<EmbeddedContainerComponent>();
Assert.That(containerComp.EmbeddedObjects, Does.Contain(ToServer(projectile1)),
"Target is not tracking the second projectile as embedded.");
Assert.That(containerComp.EmbeddedObjects, Has.Count.EqualTo(2),
"Target EmbeddedObjects count did not increase with second projectile.");
// Delete the first projectile
await Delete(projectile1);
Assert.That(containerComp.EmbeddedObjects, Does.Not.Contain(ToServer(projectile1)),
"Target did not stop tracking first projectile after it was deleted.");
Assert.That(containerComp.EmbeddedObjects, Does.Not.Contain(EntityUid.Invalid),
"Target EmbeddedObjects contains an invalid entity.");
foreach (var embedded in containerComp.EmbeddedObjects)
{
Assert.That(!SEntMan.Deleted(embedded),
"Target EmbeddedObjects contains a deleted entity.");
}
Assert.That(containerComp.EmbeddedObjects, Has.Count.EqualTo(1),
"Target EmbeddedObjects count did not decrease after deleting first projectile.");
// Delete the second projectile
await Delete(projectile2);
Assert.That(!SEntMan.HasComponent<EmbeddedContainerComponent>(ToServer(Target)),
"Target did not remove EmbeddedContainerComponent after both projectiles were deleted.");
}
}

View File

@@ -41,6 +41,7 @@ namespace Content.IntegrationTests.Tests
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
.Where(p => !p.Components.ContainsKey("CP14BiomeSpawner")) // CP14 this component delete all entities on this tile
.Where(p => !p.Components.ContainsKey("CP14AreaEntityEffect")) // CP14 lightning detonates entities
.Select(p => p.ID)
.ToList();
@@ -105,6 +106,7 @@ namespace Content.IntegrationTests.Tests
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
.Where(p => !p.Components.ContainsKey("CP14BiomeSpawner")) // CP14 this component delete all entities on this tile
.Where(p => !p.Components.ContainsKey("CP14AreaEntityEffect")) // CP14 lightning detonates entities
.Select(p => p.ID)
.ToList();
foreach (var protoId in protoIds)
@@ -347,6 +349,7 @@ namespace Content.IntegrationTests.Tests
"GridFill",
"RoomFill",
"CP14BiomeSpawner", // CP14 this component delete all entities on this tile
"CP14AreaEntityEffect", // CP14 lightning detonates entities
"Map", // We aren't testing a map entity in this test
"MapGrid",
"Broadphase",

View File

@@ -1,5 +1,4 @@
using System.Linq;
using Content.Client.Chat.UI;
using Content.Client.LateJoin;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.ContentPack;
@@ -14,7 +13,6 @@ public sealed class UiControlTest
// You should not be adding to this.
private Type[] _ignored = new Type[]
{
typeof(EmotesMenu),
typeof(LateJoinGui),
};

View File

@@ -42,21 +42,9 @@ namespace Content.Server.Access.Systems
access.Tags.UnionWith(targetAccess.Tags);
var addedLength = access.Tags.Count - beforeLength;
if (addedLength == 0)
{
_popupSystem.PopupEntity(Loc.GetString("agent-id-no-new", ("card", args.Target)), args.Target.Value, args.User);
return;
}
Dirty(uid, access);
if (addedLength == 1)
{
_popupSystem.PopupEntity(Loc.GetString("agent-id-new-1", ("card", args.Target)), args.Target.Value, args.User);
return;
}
_popupSystem.PopupEntity(Loc.GetString("agent-id-new", ("number", addedLength), ("card", args.Target)), args.Target.Value, args.User);
if (addedLength > 0)
Dirty(uid, access);
}
private void AfterUIOpen(EntityUid uid, AgentIDCardComponent component, AfterActivatableUIOpenEvent args)

View File

@@ -168,7 +168,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
/*TODO: ECS SharedIdCardConsoleComponent and then log on card ejection, together with the save.
This current implementation is pretty shit as it logs 27 entries (27 lines) if someone decides to give themselves AA*/
_adminLogger.Add(LogType.Action, LogImpact.High,
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(player):player} has modified {ToPrettyString(targetId):entity} with the following accesses: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
}

View File

@@ -43,5 +43,13 @@ namespace Content.Server.Administration.Commands
_entities.System<MindSystem>().ControlMob(player.UserId, target.Value);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length != 1)
return CompletionResult.Empty;
return CompletionResult.FromOptions(CompletionHelper.NetEntities(args[0], entManager: _entities));
}
}
}

View File

@@ -44,10 +44,10 @@ public sealed partial class AnomalySystem
if (!NetEntity.TryParse(args[0], out var uidNet) || !TryGetEntity(uidNet, out var uid))
return;
if (!HasComp<AnomalyComponent>(uid))
if (!TryComp<AnomalyComponent>(uid, out var anomaly))
return;
StartSupercriticalEvent(uid.Value);
StartSupercriticalEvent((uid.Value, anomaly));
}
private CompletionResult GetAnomalyCompletion(IConsoleShell shell, string[] args)

View File

@@ -81,14 +81,14 @@ public sealed class ProjectileAnomalySystem : EntitySystem
EntityCoordinates targetCoords,
float severity)
{
var mapPos = coords.ToMap(EntityManager, _xform);
var mapPos = _xform.ToMapCoordinates(coords);
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _)
? coords.WithEntityId(gridUid, EntityManager)
? _xform.WithEntityId(coords, gridUid)
: new(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
var ent = Spawn(component.ProjectilePrototype, spawnCoords);
var direction = targetCoords.ToMapPos(EntityManager, _xform) - mapPos.Position;
var direction = _xform.ToMapCoordinates(targetCoords).Position - mapPos.Position;
if (!TryComp<ProjectileComponent>(ent, out var comp))
return;

View File

@@ -16,7 +16,6 @@ public sealed class TechAnomalySystem : EntitySystem
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly BeamSystem _beam = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly EmagSystem _emag = default!;
public override void Initialize()
{

View File

@@ -1,6 +1,5 @@
using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components;
using Content.Server.IgnitionSource;
using Content.Server.Stunnable;
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
@@ -12,6 +11,7 @@ using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.IgnitionSource;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Physics;
@@ -38,7 +38,7 @@ namespace Content.Server.Atmos.EntitySystems
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly StunSystem _stunSystem = default!;
[Dependency] private readonly TemperatureSystem _temperatureSystem = default!;
[Dependency] private readonly IgnitionSourceSystem _ignitionSourceSystem = default!;
[Dependency] private readonly SharedIgnitionSourceSystem _ignitionSourceSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly FixtureSystem _fixture = default!;

View File

@@ -3,6 +3,7 @@ using Content.Server.Atmos.Piping.Components;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
@@ -17,6 +18,7 @@ public sealed class GasPressurePumpSystem : SharedGasPressurePumpSystem
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
[Dependency] private readonly PowerReceiverSystem _power = default!;
public override void Initialize()
{
@@ -25,33 +27,33 @@ public sealed class GasPressurePumpSystem : SharedGasPressurePumpSystem
SubscribeLocalEvent<GasPressurePumpComponent, AtmosDeviceUpdateEvent>(OnPumpUpdated);
}
private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, ref AtmosDeviceUpdateEvent args)
private void OnPumpUpdated(Entity<GasPressurePumpComponent> ent, ref AtmosDeviceUpdateEvent args)
{
if (!pump.Enabled
|| (TryComp<ApcPowerReceiverComponent>(uid, out var power) && !power.Powered)
|| !_nodeContainer.TryGetNodes(uid, pump.InletName, pump.OutletName, out PipeNode? inlet, out PipeNode? outlet))
if (!ent.Comp.Enabled
|| !_power.IsPowered(ent)
|| !_nodeContainer.TryGetNodes(ent.Owner, ent.Comp.InletName, ent.Comp.OutletName, out PipeNode? inlet, out PipeNode? outlet))
{
_ambientSoundSystem.SetAmbience(uid, false);
_ambientSoundSystem.SetAmbience(ent, false);
return;
}
var outputStartingPressure = outlet.Air.Pressure;
if (outputStartingPressure >= pump.TargetPressure)
if (outputStartingPressure >= ent.Comp.TargetPressure)
{
_ambientSoundSystem.SetAmbience(uid, false);
_ambientSoundSystem.SetAmbience(ent, false);
return; // No need to pump gas if target has been reached.
}
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
{
// We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
var pressureDelta = pump.TargetPressure - outputStartingPressure;
var pressureDelta = ent.Comp.TargetPressure - outputStartingPressure;
var transferMoles = (pressureDelta * outlet.Air.Volume) / (inlet.Air.Temperature * Atmospherics.R);
var removed = inlet.Air.Remove(transferMoles);
_atmosphereSystem.Merge(outlet.Air, removed);
_ambientSoundSystem.SetAmbience(uid, removed.TotalMoles > 0f);
_ambientSoundSystem.SetAmbience(ent, removed.TotalMoles > 0f);
}
}
}

View File

@@ -1,6 +1,6 @@
using Content.Server.Atmos.Piping.Binary.Components;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Shared.DeviceLinking.Events;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems;

View File

@@ -1,10 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Atmos.Piping.Binary.Components;
using Content.Server.Atmos.Piping.Unary.Components;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Construction.Components;
using JetBrains.Annotations;
using Robust.Shared.Map;
@@ -16,7 +14,6 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
public sealed class GasPortableSystem : EntitySystem
{
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
public override void Initialize()

View File

@@ -2,14 +2,12 @@ using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Monitor.Systems;
using Content.Server.Atmos.Piping.Components;
using Content.Server.Atmos.Piping.Unary.Components;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Administration.Logs;
using Content.Shared.Atmos;
@@ -20,6 +18,7 @@ using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Visuals;
using Content.Shared.Audio;
using Content.Shared.Database;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.DeviceNetwork;
using Content.Shared.DoAfter;
using Content.Shared.Examine;

View File

@@ -50,6 +50,9 @@ public sealed class PlantHolderSystem : EntitySystem
public const float HydroponicsSpeedMultiplier = 1f;
public const float HydroponicsConsumptionMultiplier = 2f;
private static readonly ProtoId<TagPrototype> HoeTag = "Hoe";
private static readonly ProtoId<TagPrototype> PlantSampleTakerTag = "PlantSampleTaker";
public override void Initialize()
{
base.Initialize();
@@ -203,7 +206,7 @@ public sealed class PlantHolderSystem : EntitySystem
return;
}
if (_tagSystem.HasTag(args.Used, "Hoe"))
if (_tagSystem.HasTag(args.Used, HoeTag))
{
args.Handled = true;
if (component.WeedLevel > 0)
@@ -243,7 +246,7 @@ public sealed class PlantHolderSystem : EntitySystem
return;
}
if (_tagSystem.HasTag(args.Used, "PlantSampleTaker"))
if (_tagSystem.HasTag(args.Used, PlantSampleTakerTag))
{
args.Handled = true;
if (component.Seed == null)

View File

@@ -7,6 +7,7 @@ using Content.Server.Administration.Systems;
using Content.Server.MoMMI;
using Content.Server.Players.RateLimiting;
using Content.Server.Preferences.Managers;
using Content.Shared._CP14.Sponsor;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Chat;
@@ -44,6 +45,7 @@ internal sealed partial class ChatManager : IChatManager
[Dependency] private readonly INetConfigurationManager _netConfigManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly PlayerRateLimitManager _rateLimitManager = default!;
[Dependency] private readonly ICP14SponsorManager _sponsor = default!; //CP14 OCC color
/// <summary>
/// The maximum length a player-sent message can be sent
@@ -256,6 +258,12 @@ internal sealed partial class ChatManager : IChatManager
{
wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
}
//CP14 Sponsor OOC Color
if (_sponsor.TryGetSponsorOOCColor(player.UserId, out var oocColor))
{
wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", oocColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
}
//CP14 Sponsor OOC Color end
//TODO: player.Name color, this will need to change the structure of the MsgChatMessage
ChatMessageToAll(ChatChannel.OOC, message, wrappedMessage, EntityUid.Invalid, hideChat: false, recordReplay: true, colorOverride: colorOverride, author: player.UserId);

View File

@@ -4,6 +4,7 @@ using Content.Shared.Chat;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Mind;
@@ -13,6 +14,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Server.Chat;
@@ -26,6 +28,8 @@ public sealed class SuicideSystem : EntitySystem
[Dependency] private readonly GhostSystem _ghostSystem = default!;
[Dependency] private readonly SharedSuicideSystem _suicide = default!;
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
public override void Initialize()
{
base.Initialize();
@@ -59,7 +63,7 @@ public sealed class SuicideSystem : EntitySystem
// Suicide is considered a fail if the user wasn't able to ghost
// Suiciding with the CannotSuicide tag will ghost the player but not kill the body
if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, "CannotSuicide"))
if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, CannotSuicideTag))
return false;
var suicideEvent = new SuicideEvent(victim);
@@ -94,7 +98,7 @@ public sealed class SuicideSystem : EntitySystem
// CannotSuicide tag will allow the user to ghost, but also return to their mind
// This is kind of weird, not sure what it applies to?
if (_tagSystem.HasTag(victim, "CannotSuicide"))
if (_tagSystem.HasTag(victim, CannotSuicideTag))
args.CanReturnToBody = true;
if (_ghostSystem.OnGhostAttempt(victim.Comp.Mind.Value, args.CanReturnToBody, mind: mindComponent))
@@ -149,7 +153,7 @@ public sealed class SuicideSystem : EntitySystem
if (args.Handled)
return;
var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", victim));
var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", Identity.Entity(victim, EntityManager)));
_popup.PopupEntity(othersMessage, victim, Filter.PvsExcept(victim), true);
var selfMessage = Loc.GetString("suicide-command-default-text-self");

View File

@@ -9,6 +9,7 @@ using Content.Shared.Projectiles;
using Content.Shared.Tag;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Collections;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.EntitySystems;
@@ -24,6 +25,8 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly TagSystem _tag = default!;
private static readonly ProtoId<TagPrototype> HardsuitTag = "Hardsuit";
public override void Initialize()
{
base.Initialize();
@@ -93,7 +96,7 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
// Yuck, this is way to hardcodey for my tastes
// TODO blocking injection with a hardsuit should probably done with a cancellable event or something
if (!injector.Comp.PierceArmor && _inventory.TryGetSlotEntity(target, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit"))
if (!injector.Comp.PierceArmor && _inventory.TryGetSlotEntity(target, "outerClothing", out var suit) && _tag.HasTag(suit.Value, HardsuitTag))
{
// Only show popup to attacker
if (source != null)

View File

@@ -28,7 +28,7 @@ namespace Content.Server.Chemistry.TileReactions
if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices))
return FixedPoint2.Zero;
environment.Temperature *= MathF.Max(_temperatureMultiplier * reactVolume.Float(), 1f);
environment.Temperature += MathF.Max(_temperatureMultiplier * reactVolume.Float(), 1f);
atmosphereSystem.ReactTile(tile.GridUid, tile.GridIndices);
return reactVolume;

View File

@@ -2,12 +2,14 @@ using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Content.Server._CP14.Sponsor;
using Content.Server.Administration.Managers;
using Content.Server.Chat.Managers;
using Content.Server.Connection.IPIntel;
using Content.Server.Database;
using Content.Server.GameTicking;
using Content.Server.Preferences.Managers;
using Content.Shared._CP14.Sponsor;
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Players.PlayTimeTracking;
@@ -64,6 +66,7 @@ namespace Content.Server.Connection
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IHttpClientHolder _http = default!;
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly ICP14SponsorManager _sponsor = default!; //CP14 Priority Join
private ISawmill _sawmill = default!;
private readonly Dictionary<NetUserId, TimeSpan> _temporaryBypasses = [];
@@ -375,11 +378,11 @@ namespace Content.Server.Connection
public async Task<bool> HavePrivilegedJoin(NetUserId userId)
{
var adminBypass = _cfg.GetCVar(CCVars.AdminBypassMaxPlayers) && await _db.GetAdminDataForAsync(userId) != null;
//var havePriorityJoin = _sponsors
var havePriorityJoin = _sponsor.UserHasFeature(userId, "PriorityJoin", false);
var wasInGame = EntitySystem.TryGet<GameTicker>(out var ticker) &&
ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
status == PlayerGameStatus.JoinedGame;
return adminBypass || wasInGame;
return adminBypass || wasInGame || havePriorityJoin;
}
//CP14 Join Queue end
}

View File

@@ -259,7 +259,7 @@ public sealed class IPIntel
{
_chatManager.SendAdminAlert(Loc.GetString("admin-alert-ipintel-warning",
("player", username),
("percent", Math.Round(score))));
("percent", score)));
}
if (!decisionIsReject)
@@ -269,7 +269,7 @@ public sealed class IPIntel
{
_chatManager.SendAdminAlert(Loc.GetString("admin-alert-ipintel-blocked",
("player", username),
("percent", Math.Round(score))));
("percent", score)));
}
return _rejectBad ? (true, Loc.GetString("ipintel-suspicious")) : (false, string.Empty);

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