diff --git a/Content.Client/_NF/Tools/Components/ComponentCyclerStatusControl.cs b/Content.Client/_NF/Tools/Components/ComponentCyclerStatusControl.cs new file mode 100644 index 00000000000..43c293dec2c --- /dev/null +++ b/Content.Client/_NF/Tools/Components/ComponentCyclerStatusControl.cs @@ -0,0 +1,39 @@ +using Content.Client.Message; +using Content.Client.Stylesheets; +using Content.Shared._NF.Item.ItemToggle.Components; +using Content.Shared.Tools.Components; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Timing; + +namespace Content.Client._NF.Tools.Components; + +public sealed class ComponentCyclerStatusControl : Control +{ + private readonly ComponentCyclerComponent _parent; + private readonly RichTextLabel _label; + + public ComponentCyclerStatusControl(ComponentCyclerComponent parent) + { + _parent = parent; + _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; + _label.SetMarkup(_parent.StatusShowBehavior ? _parent.Entries[_parent.CurrentEntry].QualityName : string.Empty); + AddChild(_label); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + if (_parent.UiUpdateNeeded) + { + _parent.UiUpdateNeeded = false; + Update(); + } + } + + public void Update() + { + _label.SetMarkup(_parent.StatusShowBehavior ? _parent.Entries[_parent.CurrentEntry].QualityName : string.Empty); + } +} diff --git a/Content.Client/_NF/Tools/Systems/ComponentCyclerSystem.cs b/Content.Client/_NF/Tools/Systems/ComponentCyclerSystem.cs new file mode 100644 index 00000000000..78df0a0552a --- /dev/null +++ b/Content.Client/_NF/Tools/Systems/ComponentCyclerSystem.cs @@ -0,0 +1,15 @@ +using Content.Client._NF.Tools.Components; +using Content.Client.Items; +using Content.Shared._NF.Item.ItemToggle.Components; + +namespace Content.Client._NF.Tools.Systems; + +public sealed class ComponentCyclerSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + Subs.ItemStatus(ent => new ComponentCyclerStatusControl(ent)); + } +} diff --git a/Content.Shared/_NF/Item/ItemToggle/ComponentCyclerSystem.cs b/Content.Shared/_NF/Item/ItemToggle/ComponentCyclerSystem.cs new file mode 100644 index 00000000000..248162a0e64 --- /dev/null +++ b/Content.Shared/_NF/Item/ItemToggle/ComponentCyclerSystem.cs @@ -0,0 +1,101 @@ +using Content.Shared._NF.Item.ItemToggle.Components; +using Content.Shared.Interaction; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; + +namespace Content.Shared._NF.Item.ItemToggle; + +/// +/// Handles component manipulation. +/// +public sealed class ComponentCyclerSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; + + public override void Initialize() + { + base.Initialize(); + + //SubscribeLocalEvent(OnCycle); + + SubscribeLocalEvent(OnComponentCyclerStartup); + SubscribeLocalEvent(OnComponentCyclerActivated); + SubscribeLocalEvent(OnComponentCyclerHandleState); + } + + private void OnComponentCyclerHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + SetComponentCyclerInternal(ent, ent.Comp.CurrentEntry); + } + + private void OnComponentCyclerStartup(Entity ent, ref ComponentStartup args) + { + SetComponentCyclerInternal(ent, ent.Comp.CurrentEntry); + } + + private void OnComponentCyclerActivated(Entity ent, ref ActivateInWorldEvent args) + { + if (args.Handled || !args.Complex) + return; + + args.Handled = CycleComponentCyclerInternal(ent, args.User); + } + + public bool CycleComponentCycler(Entity ent, EntityUid? user = null) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + return CycleComponentCyclerInternal((ent.Owner, ent.Comp), user); + } + + public void SetComponentCycler(Entity ent, + int newIndex, + ComponentRegistry? comp = null, + bool playSound = false, + EntityUid? user = null) + { + if (!Resolve(ent, ref ent.Comp)) + return; + SetComponentCyclerInternal((ent.Owner, ent.Comp), newIndex, comp, playSound, user); + } + + private bool CycleComponentCyclerInternal(Entity ent, EntityUid? user = null) + { + if (ent.Comp.Entries.Length == 0) + return false; + + var newIndex = ((ent.Comp.CurrentEntry + 1) % ent.Comp.Entries.Length); + SetComponentCyclerInternal(ent, newIndex, playSound: true, user: user); + + return true; + } + + public void SetComponentCyclerInternal(Entity ent, + int newIndex, + ComponentRegistry? comp = null, + bool playSound = false, + EntityUid? user = null) + { + if (newIndex >= ent.Comp.Entries.Length) + return; + + // Remove previous components if index is within bounds + if (ent.Comp.CurrentEntry < ent.Comp.Entries.Length) + { + var previous = ent.Comp.Entries[ent.Comp.CurrentEntry]; + + EntityManager.RemoveComponents(ent, previous.Components); + } + + // Update previous entry + ent.Comp.CurrentEntry = newIndex; + var current = ent.Comp.Entries[ent.Comp.CurrentEntry]; + + // Add new components + EntityManager.AddComponents(ent, current.Components); + + if (playSound && current.ChangeSound != null) + _audioSystem.PlayPredicted(current.ChangeSound, ent, user); + } +} diff --git a/Content.Shared/_NF/Item/ItemToggle/Components/ComponentCyclerComponent.cs b/Content.Shared/_NF/Item/ItemToggle/Components/ComponentCyclerComponent.cs new file mode 100644 index 00000000000..2ee51cfb543 --- /dev/null +++ b/Content.Shared/_NF/Item/ItemToggle/Components/ComponentCyclerComponent.cs @@ -0,0 +1,46 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Shared._NF.Item.ItemToggle.Components; + +/// +/// Adds or removes components when toggled. +/// Requires . +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class ComponentCyclerComponent : Component +{ + [DataDefinition] + public sealed partial class CompEntry + { + [DataField(required: true)] + public ComponentRegistry Components = new(); + + [DataField] + public SoundSpecifier? UseSound; + + [DataField] + public SoundSpecifier? ChangeSound; + + [DataField] + public SpriteSpecifier? Sprite; + + [DataField] + public string QualityName = string.Empty; + } + + [DataField(required: true)] + public CompEntry[] Entries { get; private set; } = Array.Empty(); + + [ViewVariables] + [AutoNetworkedField] + public int CurrentEntry = 0; + + [ViewVariables(VVAccess.ReadWrite)] + public bool UiUpdateNeeded; + + [DataField] + public bool StatusShowBehavior = true; +} diff --git a/Resources/Prototypes/Entities/Objects/Tools/appraisal.yml b/Resources/Prototypes/Entities/Objects/Tools/appraisal.yml index ee6ff71d4ff..29c2187ecea 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/appraisal.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/appraisal.yml @@ -9,9 +9,9 @@ state: icon - type: Item sprite: Objects/Tools/appraisal-tool.rsi - - type: PriceGun +# - type: PriceGun # Frontier - type: UseDelay - delay: 3 + delay: 1 # Frontier 3<1 - type: Clothing sprite: Objects/Tools/appraisal-tool.rsi quickEquip: false @@ -24,6 +24,41 @@ tags: - AppraisalTool - Sidearm + # Frontier changes start # - type: GuideHelp # Frontier: guidebook entry removed # guides: # Frontier: guidebook entry removed # - Cargo # Frontier: guidebook entry removed + - type: ComponentCycler + statusShowBehavior: true + entries: + - components: + - type: PriceGun + sprite: + sprite: Objects/Tools/appraisal-tool.rsi + state: icon + useSound: + path: /Audio/Items/drill_use.ogg + changeSound: + path: /Audio/Items/change_drill.ogg + - components: + - type: ContrabandPriceGun + sprite: + sprite: _NF/Objects/Tools/appraisal-tool.rsi + state: icon + useSound: + path: /Audio/Items/drill_use.ogg + changeSound: + path: /Audio/Items/change_drill.ogg + - components: + - type: MedicalPriceGun + sprite: + sprite: _NF/Objects/Tools/medical-appraisal-tool.rsi + layers: + - state: icon + - state: icon-unlit + shader: unshaded + useSound: + path: /Audio/Items/drill_use.ogg + changeSound: + path: /Audio/Items/change_drill.ogg + # Frontier changes end