diff --git a/Content.Client/Crayon/CrayonComponent.cs b/Content.Client/Crayon/CrayonComponent.cs index b48b218e62..6fa182cc92 100644 --- a/Content.Client/Crayon/CrayonComponent.cs +++ b/Content.Client/Crayon/CrayonComponent.cs @@ -1,72 +1,15 @@ -using Content.Client.Items.Components; -using Content.Client.Message; -using Content.Client.Stylesheets; using Content.Shared.Crayon; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; using Robust.Shared.GameObjects; -using Robust.Shared.Localization; -using Robust.Shared.Timing; using Robust.Shared.ViewVariables; namespace Content.Client.Crayon { [RegisterComponent] - public class CrayonComponent : SharedCrayonComponent, IItemStatus + public sealed class CrayonComponent : SharedCrayonComponent { - [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; - [ViewVariables(VVAccess.ReadWrite)] private string Color => _color; - [ViewVariables] private int Charges { get; set; } - [ViewVariables] private int Capacity { get; set; } - - Control IItemStatus.MakeControl() - { - return new StatusControl(this); - } - - public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) - { - if (curState is not CrayonComponentState state) - return; - - _color = state.Color; - SelectedState = state.State; - Charges = state.Charges; - Capacity = state.Capacity; - - _uiUpdateNeeded = true; - } - - private sealed class StatusControl : Control - { - private readonly CrayonComponent _parent; - private readonly RichTextLabel _label; - - public StatusControl(CrayonComponent parent) - { - _parent = parent; - _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; - AddChild(_label); - - parent._uiUpdateNeeded = true; - } - - protected override void FrameUpdate(FrameEventArgs args) - { - base.FrameUpdate(args); - - if (!_parent._uiUpdateNeeded) - { - return; - } - - _parent._uiUpdateNeeded = false; - _label.SetMarkup(Loc.GetString("crayon-drawing-label", - ("color",_parent.Color), - ("state",_parent.SelectedState), - ("charges", _parent.Charges), - ("capacity",_parent.Capacity))); - } - } + [ViewVariables(VVAccess.ReadWrite)] public bool UIUpdateNeeded; + [ViewVariables(VVAccess.ReadWrite)] public string Color => _color; + [ViewVariables] public int Charges { get; set; } + [ViewVariables] public int Capacity { get; set; } } } diff --git a/Content.Client/Crayon/CrayonSystem.cs b/Content.Client/Crayon/CrayonSystem.cs new file mode 100644 index 0000000000..51e2f55694 --- /dev/null +++ b/Content.Client/Crayon/CrayonSystem.cs @@ -0,0 +1,72 @@ +using Content.Client.Items; +using Content.Client.Message; +using Content.Client.Stylesheets; +using Content.Shared.Crayon; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Localization; +using Robust.Shared.Timing; + +namespace Content.Client.Crayon; + +public sealed class CrayonSystem : EntitySystem +{ + // Didn't do in shared because I don't think most of the server stuff can be predicted. + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnCrayonHandleState); + SubscribeLocalEvent(OnCrayonItemStatus); + } + + private static void OnCrayonHandleState(EntityUid uid, CrayonComponent component, ref ComponentHandleState args) + { + if (args.Current is not CrayonComponentState state) return; + + component._color = state.Color; + component.SelectedState = state.State; + component.Charges = state.Charges; + component.Capacity = state.Capacity; + + component.UIUpdateNeeded = true; + } + + private static void OnCrayonItemStatus(EntityUid uid, CrayonComponent component, ItemStatusCollectMessage args) + { + args.Controls.Add(new StatusControl(component)); + } + + private sealed class StatusControl : Control + { + private readonly CrayonComponent _parent; + private readonly RichTextLabel _label; + + public StatusControl(CrayonComponent parent) + { + _parent = parent; + _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; + AddChild(_label); + + parent.UIUpdateNeeded = true; + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + if (!_parent.UIUpdateNeeded) + { + return; + } + + _parent.UIUpdateNeeded = false; + _label.SetMarkup(Loc.GetString("crayon-drawing-label", + ("color",_parent.Color), + ("state",_parent.SelectedState), + ("charges", _parent.Charges), + ("capacity",_parent.Capacity))); + } + } +} diff --git a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs index 37439e21f8..24d7f5c73d 100644 --- a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs +++ b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs @@ -47,6 +47,7 @@ namespace Content.Client.Crayon.UI if (disposing) { _menu?.Close(); + _menu = null; } } } diff --git a/Content.Server/Crayon/CrayonComponent.cs b/Content.Server/Crayon/CrayonComponent.cs index 2559261f80..90c810e742 100644 --- a/Content.Server/Crayon/CrayonComponent.cs +++ b/Content.Server/Crayon/CrayonComponent.cs @@ -1,25 +1,9 @@ -using System.Linq; -using System.Threading.Tasks; -using Content.Server.Administration.Logs; using Content.Server.UserInterface; -using Content.Shared.Audio; using Content.Shared.Crayon; -using Content.Server.Decals; -using Content.Shared.Decals; -using Content.Shared.Database; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Helpers; -using Content.Shared.Popups; using Content.Shared.Sound; using Robust.Server.GameObjects; -using Robust.Shared.Audio; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Localization; using Robust.Shared.Maths; -using Robust.Shared.Player; -using Robust.Shared.Players; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -27,16 +11,12 @@ using Robust.Shared.ViewVariables; namespace Content.Server.Crayon { [RegisterComponent] - public class CrayonComponent : SharedCrayonComponent, IAfterInteract, IUse, IDropped, ISerializationHooks + public sealed class CrayonComponent : SharedCrayonComponent, ISerializationHooks { - [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - [DataField("useSound")] - private SoundSpecifier? _useSound = null; + [DataField("useSound")] public SoundSpecifier? UseSound; [ViewVariables] - public Color Color { get; set; } + public Color Color { get; private set; } [ViewVariables(VVAccess.ReadWrite)] public int Charges { get; set; } @@ -45,104 +25,11 @@ namespace Content.Server.Crayon [DataField("capacity")] public int Capacity { get; set; } = 30; - [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(CrayonUiKey.Key); + [ViewVariables] public BoundUserInterface? UserInterface => Owner.GetUIOrNull(CrayonUiKey.Key); void ISerializationHooks.AfterDeserialization() { Color = Color.FromName(_color); } - - protected override void Initialize() - { - base.Initialize(); - if (UserInterface != null) - { - UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage; - } - Charges = Capacity; - - // Get the first one from the catalog and set it as default - var decal = _prototypeManager.EnumeratePrototypes().FirstOrDefault(x => x.Tags.Contains("crayon")); - SelectedState = decal?.ID ?? string.Empty; - Dirty(); - } - - private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) - { - switch (serverMsg.Message) - { - case CrayonSelectMessage msg: - // Check if the selected state is valid - if (_prototypeManager.TryIndex(msg.State, out var prototype) && prototype.Tags.Contains("crayon")) - { - SelectedState = msg.State; - Dirty(); - } - break; - default: - break; - } - } - - public override ComponentState GetComponentState() - { - return new CrayonComponentState(_color, SelectedState, Charges, Capacity); - } - - // Opens the selection window - bool IUse.UseEntity(UseEntityEventArgs eventArgs) - { - if (_entMan.TryGetComponent(eventArgs.User, out ActorComponent? actor)) - { - UserInterface?.Toggle(actor.PlayerSession); - if (UserInterface?.SessionHasOpen(actor.PlayerSession) == true) - { - // Tell the user interface the selected stuff - UserInterface.SetState( - new CrayonBoundUserInterfaceState(SelectedState, Color)); - } - return true; - } - return false; - } - - async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) - { - if (!eventArgs.User.InRangeUnobstructed(eventArgs.ClickLocation, ignoreInsideBlocker: false, popup: true, - collisionMask: Shared.Physics.CollisionGroup.MobImpassable)) - { - return true; - } - - if (Charges <= 0) - { - eventArgs.User.PopupMessage(Loc.GetString("crayon-interact-not-enough-left-text")); - return true; - } - - if (!eventArgs.ClickLocation.IsValid(_entMan)) - { - eventArgs.User.PopupMessage(Loc.GetString("crayon-interact-invalid-location")); - return true; - } - - if(!EntitySystem.Get().TryAddDecal(SelectedState, eventArgs.ClickLocation.Offset(new Vector2(-0.5f,-0.5f)), out _, Color.FromName(_color), cleanable: true)) - return false; - - if (_useSound != null) - SoundSystem.Play(Filter.Pvs(Owner), _useSound.GetSound(), Owner, AudioHelpers.WithVariation(0.125f)); - - // Decrease "Ammo" - Charges--; - Dirty(); - EntitySystem.Get().Add(LogType.CrayonDraw, LogImpact.Low, $"{_entMan.ToPrettyString(eventArgs.User):user} drew a {_color:color} {SelectedState}"); - return true; - } - - void IDropped.Dropped(DroppedEventArgs eventArgs) - { - if (_entMan.TryGetComponent(eventArgs.User, out ActorComponent? actor)) - UserInterface?.Close(actor.PlayerSession); - } } } diff --git a/Content.Server/Crayon/CrayonSystem.cs b/Content.Server/Crayon/CrayonSystem.cs new file mode 100644 index 0000000000..e4ebc0b8a4 --- /dev/null +++ b/Content.Server/Crayon/CrayonSystem.cs @@ -0,0 +1,121 @@ +using System.Linq; +using Content.Server.Administration.Logs; +using Content.Server.Decals; +using Content.Server.Popups; +using Content.Shared.Audio; +using Content.Shared.Crayon; +using Content.Shared.Database; +using Content.Shared.Decals; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Helpers; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.Crayon; + +public sealed class CrayonSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly AdminLogSystem _logs = default!; + [Dependency] private readonly DecalSystem _decals = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnCrayonInit); + SubscribeLocalEvent(OnCrayonBoundUI); + SubscribeLocalEvent(OnCrayonUse); + SubscribeLocalEvent(OnCrayonAfterInteract); + SubscribeLocalEvent(OnCrayonDropped); + SubscribeLocalEvent(OnCrayonGetState); + } + + private static void OnCrayonGetState(EntityUid uid, CrayonComponent component, ref ComponentGetState args) + { + args.State = new CrayonComponentState(component._color, component.SelectedState, component.Charges, component.Capacity); + } + + private void OnCrayonAfterInteract(EntityUid uid, CrayonComponent component, AfterInteractEvent args) + { + if (args.Handled || !args.CanReach) + return; + + if (component.Charges <= 0) + { + _popup.PopupEntity(Loc.GetString("crayon-interact-not-enough-left-text"), uid, Filter.Entities(args.User)); + args.Handled = true; + return; + } + + if (!args.ClickLocation.IsValid(EntityManager)) + { + _popup.PopupEntity(Loc.GetString("crayon-interact-invalid-location"), uid, Filter.Entities(args.User)); + args.Handled = true; + return; + } + + if(!_decals.TryAddDecal(component.SelectedState, args.ClickLocation.Offset(new Vector2(-0.5f,-0.5f)), out _, Color.FromName(component._color), cleanable: true)) + return; + + if (component.UseSound != null) + SoundSystem.Play(Filter.Pvs(uid), component.UseSound.GetSound(), uid, AudioHelpers.WithVariation(0.125f)); + + // Decrease "Ammo" + component.Charges--; + Dirty(component); + _logs.Add(LogType.CrayonDraw, LogImpact.Low, $"{EntityManager.ToPrettyString(args.User):user} drew a {component._color:color} {component.SelectedState}"); + args.Handled = true; + } + + private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args) + { + // Open crayon window if neccessary. + if (args.Handled) + return; + + if (!TryComp(args.User, out var actor)) return; + + component.UserInterface?.Toggle(actor.PlayerSession); + + if (component.UserInterface?.SessionHasOpen(actor.PlayerSession) == true) + { + // Tell the user interface the selected stuff + component.UserInterface.SetState(new CrayonBoundUserInterfaceState(component.SelectedState, component.Color)); + } + + args.Handled = true; + } + + private void OnCrayonBoundUI(EntityUid uid, CrayonComponent component, CrayonSelectMessage args) + { + // Check if the selected state is valid + if (!_prototypeManager.TryIndex(args.State, out var prototype) || !prototype.Tags.Contains("crayon")) return; + + component.SelectedState = args.State; + Dirty(component); + } + + private void OnCrayonInit(EntityUid uid, CrayonComponent component, ComponentInit args) + { + component.Charges = component.Capacity; + + // Get the first one from the catalog and set it as default + var decal = _prototypeManager.EnumeratePrototypes().FirstOrDefault(x => x.Tags.Contains("crayon")); + component.SelectedState = decal?.ID ?? string.Empty; + Dirty(component); + } + + private void OnCrayonDropped(EntityUid uid, CrayonComponent component, DroppedEvent args) + { + if (TryComp(args.UserUid, out var actor)) + component.UserInterface?.Close(actor.PlayerSession); + } +} diff --git a/Content.Shared/Crayon/SharedCrayonComponent.cs b/Content.Shared/Crayon/SharedCrayonComponent.cs index 7869a775e0..5088002cdf 100644 --- a/Content.Shared/Crayon/SharedCrayonComponent.cs +++ b/Content.Shared/Crayon/SharedCrayonComponent.cs @@ -1,25 +1,21 @@ using System; -using System.Collections.Generic; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Maths; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; namespace Content.Shared.Crayon { - [NetworkedComponent()] - public class SharedCrayonComponent : Component + [NetworkedComponent, ComponentProtoName("Crayon")] + public abstract class SharedCrayonComponent : Component { public string SelectedState { get; set; } = string.Empty; - [DataField("color")] - protected string _color = "white"; + [DataField("color")] public string _color = "white"; [Serializable, NetSerializable] - public enum CrayonUiKey + public enum CrayonUiKey : byte { Key, }