diff --git a/.vscode/launch.json b/.vscode/launch.json index 5390b914093..e74344399a8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,8 @@ "program": "${workspaceFolder}/bin/Content.Client/Content.Client.dll", "args": [], "console": "internalConsole", - "stopAtEntry": false + "stopAtEntry": false, + "brokeredServicePipeName": "undefined" }, { "name": "Client (Compatibility renderer)", @@ -29,7 +30,8 @@ "program": "${workspaceFolder}/bin/Content.Server/Content.Server.dll", "args": [], "console": "integratedTerminal", - "stopAtEntry": false + "stopAtEntry": false, + "brokeredServicePipeName": "undefined" }, { "name": "YAML Linter", @@ -52,4 +54,4 @@ "preLaunchTask": "build" } ] -} \ No newline at end of file +} diff --git a/Content.Client/ADT/CollectiveMind/Systems/CollectiveMindSystem.cs b/Content.Client/ADT/CollectiveMind/Systems/CollectiveMindSystem.cs new file mode 100644 index 00000000000..5949eac6cc2 --- /dev/null +++ b/Content.Client/ADT/CollectiveMind/Systems/CollectiveMindSystem.cs @@ -0,0 +1,31 @@ +using Content.Client.Chat.Managers; +using Content.Shared.Sirena.CollectiveMind; +using Robust.Client.Player; + +namespace Content.Client.Sirena.CollectiveMind; + +public sealed class CollectiveMindSystem : EntitySystem +{ + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnRemove); + } + + public CollectiveMindComponent? Player => CompOrNull(_playerManager.LocalPlayer?.ControlledEntity); + public bool IsCollectiveMind => Player != null; + + private void OnInit(EntityUid uid, CollectiveMindComponent component, ComponentInit args) + { + _chatManager.UpdatePermissions(); + } + + private void OnRemove(EntityUid uid, CollectiveMindComponent component, ComponentRemove args) + { + _chatManager.UpdatePermissions(); + } +} diff --git a/Content.Client/ADT/Heretic/HereticRitualRuneBoundUserInterface.cs b/Content.Client/ADT/Heretic/HereticRitualRuneBoundUserInterface.cs new file mode 100644 index 00000000000..38b3b49aeb6 --- /dev/null +++ b/Content.Client/ADT/Heretic/HereticRitualRuneBoundUserInterface.cs @@ -0,0 +1,39 @@ +using Content.Client.ADT.Heretic.UI; +using Content.Shared.ADT.Heretic.Components; +using Content.Shared.Heretic.Prototypes; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Client.UserInterface; +using Robust.Shared.Prototypes; + +namespace Content.Client.ADT.Heretic; + +public sealed class HereticRitualRuneBoundUserInterface : BoundUserInterface +{ + [Dependency] private readonly IClyde _displayManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + + private HereticRitualRuneRadialMenu? _hereticRitualMenu; + + public HereticRitualRuneBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + IoCManager.InjectDependencies(this); + } + + protected override void Open() + { + base.Open(); + + _hereticRitualMenu = this.CreateWindow(); + _hereticRitualMenu.SetEntity(Owner); + _hereticRitualMenu.SendHereticRitualRuneMessageAction += SendHereticRitualMessage; + + var vpSize = _displayManager.ScreenSize; + _hereticRitualMenu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize); + } + + private void SendHereticRitualMessage(ProtoId protoId) + { + SendMessage(new HereticRitualMessage(protoId)); + } +} diff --git a/Content.Client/ADT/Heretic/UI/HereticRitualRuneRadialMenu.xaml b/Content.Client/ADT/Heretic/UI/HereticRitualRuneRadialMenu.xaml new file mode 100644 index 00000000000..425ba588c12 --- /dev/null +++ b/Content.Client/ADT/Heretic/UI/HereticRitualRuneRadialMenu.xaml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/Content.Client/ADT/Heretic/UI/HereticRitualRuneRadialMenu.xaml.cs b/Content.Client/ADT/Heretic/UI/HereticRitualRuneRadialMenu.xaml.cs new file mode 100644 index 00000000000..4ab95b8f9e4 --- /dev/null +++ b/Content.Client/ADT/Heretic/UI/HereticRitualRuneRadialMenu.xaml.cs @@ -0,0 +1,101 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.Heretic; +using Content.Shared.Heretic.Prototypes; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using System.Numerics; + +namespace Content.Client.ADT.Heretic.UI; + +public sealed partial class HereticRitualRuneRadialMenu : RadialMenu +{ + [Dependency] private readonly EntityManager _entityManager = default!; + [Dependency] private readonly IEntitySystemManager _entitySystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + private readonly SpriteSystem _spriteSystem; + + public event Action>? SendHereticRitualRuneMessageAction; + + public EntityUid Entity { get; set; } + + public HereticRitualRuneRadialMenu() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + _spriteSystem = _entitySystem.GetEntitySystem(); + } + + public void SetEntity(EntityUid uid) + { + Entity = uid; + RefreshUI(); + } + + private void RefreshUI() + { + var main = FindControl("Main"); + if (main == null) + return; + + var player = _playerManager.LocalEntity; + + if (!_entityManager.TryGetComponent(player, out var heretic)) + return; + + foreach (var ritual in heretic.KnownRituals) + { + if (!_prototypeManager.TryIndex(ritual, out var ritualPrototype)) + continue; + + var button = new HereticRitualMenuButton + { + StyleClasses = { "RadialMenuButton" }, + SetSize = new Vector2(64, 64), + ToolTip = Loc.GetString(ritualPrototype.Name), + ProtoId = ritualPrototype.ID + }; + + var texture = new TextureRect + { + VerticalAlignment = VAlignment.Center, + HorizontalAlignment = HAlignment.Center, + Texture = _spriteSystem.Frame0(ritualPrototype.Icon), + TextureScale = new Vector2(2f, 2f) + }; + + button.AddChild(texture); + main.AddChild(button); + } + + AddHereticRitualMenuButtonOnClickAction(main); + } + + private void AddHereticRitualMenuButtonOnClickAction(RadialContainer mainControl) + { + if (mainControl == null) + return; + + foreach(var child in mainControl.Children) + { + var castChild = child as HereticRitualMenuButton; + + if (castChild == null) + continue; + + castChild.OnButtonUp += _ => + { + SendHereticRitualRuneMessageAction?.Invoke(castChild.ProtoId); + Close(); + }; + } + } + + public sealed class HereticRitualMenuButton : RadialMenuTextureButton + { + public ProtoId ProtoId { get; set; } + } +} diff --git a/Content.Client/ADT/Heretic/UI/LivingHeartMenu.xaml b/Content.Client/ADT/Heretic/UI/LivingHeartMenu.xaml new file mode 100644 index 00000000000..fd06facd081 --- /dev/null +++ b/Content.Client/ADT/Heretic/UI/LivingHeartMenu.xaml @@ -0,0 +1,16 @@ + + + + + + diff --git a/Content.Client/ADT/Heretic/UI/LivingHeartMenu.xaml.cs b/Content.Client/ADT/Heretic/UI/LivingHeartMenu.xaml.cs new file mode 100644 index 00000000000..a85077584ca --- /dev/null +++ b/Content.Client/ADT/Heretic/UI/LivingHeartMenu.xaml.cs @@ -0,0 +1,97 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.Heretic; +using Robust.Client.Player; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; +using System.Numerics; + +namespace Content.Client.ADT.Heretic.UI; + +public sealed partial class LivingHeartMenu : RadialMenu +{ + [Dependency] private readonly EntityManager _ent = default!; + [Dependency] private readonly IPrototypeManager _prot = default!; + [Dependency] private readonly IPlayerManager _player = default!; + + public EntityUid Entity { get; private set; } + + public event Action? SendActivateMessageAction; + + public LivingHeartMenu() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + } + + public void SetEntity(EntityUid ent) + { + Entity = ent; + UpdateUI(); + } + + private void UpdateUI() + { + var main = FindControl("Main"); + if (main == null) return; + + var player = _player.LocalEntity; + + if (!_ent.TryGetComponent(player, out var heretic)) + return; + + foreach (var target in heretic.SacrificeTargets) + { + if (target == null) continue; + + var ent = _ent.GetEntity(target); + if (ent == null) + continue; + + var button = new EmbeddedEntityMenuButton + { + StyleClasses = { "RadialMenuButton" }, + SetSize = new Vector2(64, 64), + ToolTip = _ent.TryGetComponent(ent.Value, out var md) ? md.EntityName : "Unknown", + NetEntity = (NetEntity) target, + }; + + var texture = new SpriteView(ent.Value, _ent) + { + OverrideDirection = Direction.South, + VerticalAlignment = VAlignment.Center, + SetSize = new Vector2(64, 64), + VerticalExpand = true, + Stretch = SpriteView.StretchMode.Fill, + }; + button.AddChild(texture); + + main.AddChild(button); + } + AddAction(main); + } + + private void AddAction(RadialContainer main) + { + if (main == null) + return; + + foreach (var child in main.Children) + { + var castChild = child as EmbeddedEntityMenuButton; + if (castChild == null) + continue; + + castChild.OnButtonUp += _ => + { + SendActivateMessageAction?.Invoke(castChild.NetEntity); + Close(); + }; + } + } + + public sealed class EmbeddedEntityMenuButton : RadialMenuTextureButton + { + public NetEntity NetEntity; + } +} diff --git a/Content.Client/ADT/Heretic/UI/LivingHeartMenuBoundUserInterface.cs b/Content.Client/ADT/Heretic/UI/LivingHeartMenuBoundUserInterface.cs new file mode 100644 index 00000000000..d39372539d0 --- /dev/null +++ b/Content.Client/ADT/Heretic/UI/LivingHeartMenuBoundUserInterface.cs @@ -0,0 +1,34 @@ +using Content.Shared.Heretic; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Client.UserInterface; + +namespace Content.Client.ADT.Heretic.UI; + +public sealed partial class LivingHeartMenuBoundUserInterface : BoundUserInterface +{ + [Dependency] private readonly IClyde _displayManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + + [NonSerialized] private LivingHeartMenu? _menu; + + public LivingHeartMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + IoCManager.InjectDependencies(this); + } + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.SetEntity(Owner); + _menu.SendActivateMessageAction += SendMessage; + _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / _displayManager.ScreenSize); + } + + private void SendMessage(NetEntity netent) + { + base.SendMessage(new EventHereticLivingHeartActivate() { Target = netent }); + } +} diff --git a/Content.Client/ADT/UI/AnimatedBackground/AnimatedBackgroundControl.cs b/Content.Client/ADT/UI/AnimatedBackground/AnimatedBackgroundControl.cs deleted file mode 100644 index 1a6aa4cb625..00000000000 --- a/Content.Client/ADT/UI/AnimatedBackground/AnimatedBackgroundControl.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Linq; -using Content.Shared.ADT; -using Robust.Client.Graphics; -using Robust.Client.ResourceManagement; -using Robust.Client.UserInterface.Controls; -using Robust.Shared.Graphics.RSI; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; - -namespace Content.Client.ADT.UI.AnimatedBackground; - -public sealed class AnimatedBackgroundControl : TextureRect -{ - [Dependency] private readonly IResourceCache _resourceCache = default!; - [Dependency] private readonly IClyde _clyde = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - private string _rsiPath = "/Textures/ADT/LobbyScreens/backgrounds/native.rsi"; - public RSI? _RSI; - private const int States = 1; - - private IRenderTexture? _buffer; - - private readonly float[] _timer = new float[States]; - private readonly float[][] _frameDelays = new float[States][]; - private readonly int[] _frameCounter = new int[States]; - private readonly Texture[][] _frames = new Texture[States][]; - - public AnimatedBackgroundControl() - { - IoCManager.InjectDependencies(this); - - InitializeStates(); - } - - private void InitializeStates() - { - _RSI ??= _resourceCache.GetResource(_rsiPath).RSI; - - for (var i = 0; i < States; i++) - { - if (!_RSI.TryGetState((i + 1).ToString(), out var state)) - continue; - - _frames[i] = state.GetFrames(RsiDirection.South); - _frameDelays[i] = state.GetDelays(); - _frameCounter[i] = 0; - } - } - - public void SetRSI(RSI? rsi) - { - _RSI = rsi; - InitializeStates(); - } - - protected override void FrameUpdate(FrameEventArgs args) - { - base.FrameUpdate(args); - - for (var i = 0; i < _frames.Length; i++) - { - var delays = _frameDelays[i]; - if (delays.Length == 0) - continue; - - _timer[i] += args.DeltaSeconds; - - var currentFrameIndex = _frameCounter[i]; - - if (!(_timer[i] >= delays[currentFrameIndex])) - continue; - - _timer[i] -= delays[currentFrameIndex]; - _frameCounter[i] = (currentFrameIndex + 1) % _frames[i].Length; - Texture = _frames[i][_frameCounter[i]]; - } - } - - protected override void Draw(DrawingHandleScreen handle) - { - base.Draw(handle); - - if (_buffer is null) - return; - - handle.DrawTextureRect(_buffer.Texture, PixelSizeBox); - } - - protected override void Resized() - { - base.Resized(); - _buffer?.Dispose(); - _buffer = _clyde.CreateRenderTarget(PixelSize, RenderTargetColorFormat.Rgba8Srgb); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - _buffer?.Dispose(); - } - - public void RandomizeBackground() - { - var backgroundsProto = _prototypeManager.EnumeratePrototypes().ToList(); - var random = new Random(); - var index = random.Next(backgroundsProto.Count); - _rsiPath = $"/Textures/{backgroundsProto[index].Path}"; - InitializeStates(); - } -} \ No newline at end of file diff --git a/Content.Client/ADT/UserInterface/RichText/MonospaceTag.cs b/Content.Client/ADT/UserInterface/RichText/MonospaceTag.cs new file mode 100644 index 00000000000..44f561a6a99 --- /dev/null +++ b/Content.Client/ADT/UserInterface/RichText/MonospaceTag.cs @@ -0,0 +1,38 @@ +using System.Linq; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface.RichText; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Client.ADT.UserInterface.RichText; + +public sealed class MonospaceTag : IMarkupTag +{ + public const string MonospaceFont = "Monospace"; + + [Dependency] private readonly IResourceCache _resourceCache = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public string Name => "mono"; + + /// + public void PushDrawContext(MarkupNode node, MarkupDrawingContext context) + { + var font = FontTag.CreateFont( + context.Font, + node, + _resourceCache, + _prototypeManager, + MonospaceFont + ); + context.Font.Push(font); + } + + /// + public void PopDrawContext(MarkupNode node, MarkupDrawingContext context) + { + context.Font.Pop(); + } +} diff --git a/Content.Client/ADT/UserInterface/Systems/Ghost/Controls/GhostBarRulesWindow.xaml b/Content.Client/ADT/UserInterface/Systems/Ghost/Controls/GhostBarRulesWindow.xaml new file mode 100644 index 00000000000..e0e79c2e83e --- /dev/null +++ b/Content.Client/ADT/UserInterface/Systems/Ghost/Controls/GhostBarRulesWindow.xaml @@ -0,0 +1,15 @@ + + + +