From e8bc811f7a62d413715dfc81573b74f9fcb70927 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Mon, 26 May 2025 05:50:30 +0200 Subject: [PATCH] Add WieldingBlockerComponent (#37778) initial commit --- .../EntitySystems/SharedHandsSystem.Relay.cs | 3 + .../Inventory/InventorySystem.Relay.cs | 3 + .../Components/WieldingBlockerComponent.cs | 23 +++++ Content.Shared/Wieldable/Events.cs | 22 ++++- .../Wieldable/SharedWieldableSystem.cs | 84 +++++++++++++++++-- .../en-US/wieldable/wieldable-component.ftl | 1 + 6 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 Content.Shared/Wieldable/Components/WieldingBlockerComponent.cs diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs index aa367f71f7..67db193894 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs @@ -4,6 +4,7 @@ using Content.Shared.Hands.Components; using Content.Shared.Movement.Systems; using Content.Shared.Projectiles; using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.Wieldable; namespace Content.Shared.Hands.EntitySystems; @@ -19,6 +20,8 @@ public abstract partial class SharedHandsSystem SubscribeLocalEvent(RefRelayEvent); SubscribeLocalEvent(RefRelayEvent); SubscribeLocalEvent(RefRelayEvent); + SubscribeLocalEvent(RefRelayEvent); + SubscribeLocalEvent(RefRelayEvent); } private void RelayEvent(Entity entity, ref T args) where T : EntityEventArgs diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 84f2dc374b..f3ab87be38 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -24,6 +24,7 @@ using Content.Shared.Strip.Components; using Content.Shared.Temperature; using Content.Shared.Verbs; using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.Wieldable; using Content.Shared.Zombies; namespace Content.Shared.Inventory; @@ -63,6 +64,8 @@ public partial class InventorySystem SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); + SubscribeLocalEvent(RefRelayInventoryEvent); + SubscribeLocalEvent(RefRelayInventoryEvent); // Eye/vision events SubscribeLocalEvent(RelayInventoryEvent); diff --git a/Content.Shared/Wieldable/Components/WieldingBlockerComponent.cs b/Content.Shared/Wieldable/Components/WieldingBlockerComponent.cs new file mode 100644 index 0000000000..320322d708 --- /dev/null +++ b/Content.Shared/Wieldable/Components/WieldingBlockerComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Wieldable.Components; + +/// +/// Blocks an entity from wielding items. +/// When added to an item, it will block wielding when held in hand or equipped. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class WieldingBlockerComponent : Component +{ + /// + /// Block wielding when this item is held in a hand? + /// + [DataField, AutoNetworkedField] + public bool BlockInHand = true; + + /// + /// Block wielding when this item is equipped? + /// + [DataField, AutoNetworkedField] + public bool BlockEquipped = true; +} diff --git a/Content.Shared/Wieldable/Events.cs b/Content.Shared/Wieldable/Events.cs index fe7e084c02..f682ce8b80 100644 --- a/Content.Shared/Wieldable/Events.cs +++ b/Content.Shared/Wieldable/Events.cs @@ -1,3 +1,5 @@ +using Content.Shared.Inventory; + namespace Content.Shared.Wieldable; /// @@ -14,12 +16,18 @@ public readonly record struct ItemWieldedEvent(EntityUid User); public readonly record struct ItemUnwieldedEvent(EntityUid User, bool Force); /// -/// Raised directed on an item before a user tries to wield it. +/// Raised directed on an user and all the items in their inventory and hands before they wield an item. /// If this event is cancelled wielding will not happen. /// [ByRefEvent] -public record struct WieldAttemptEvent(EntityUid User, bool Cancelled = false) +public record struct WieldAttemptEvent(EntityUid User, EntityUid Wielded, bool Cancelled = false) : IInventoryRelayEvent { + /// + /// Popup message for the user to tell them why they cannot wield if Cancelled + /// + public string? Message; + + SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.WITHOUT_POCKET; public void Cancel() { Cancelled = true; @@ -27,15 +35,21 @@ public record struct WieldAttemptEvent(EntityUid User, bool Cancelled = false) } /// -/// Raised directed on an item before a user tries to stop wielding it willingly. +/// Raised directed on an user and all the items in their inventory and hands before they unwield an item willingly. /// If this event is cancelled unwielding will not happen. /// /// /// This event is not raised if the user is forced to unwield the item. /// [ByRefEvent] -public record struct UnwieldAttemptEvent(EntityUid User, bool Cancelled = false) +public record struct UnwieldAttemptEvent(EntityUid User, EntityUid Wielded, bool Cancelled = false) : IInventoryRelayEvent { + /// + /// Popup message for the user to tell them why they cannot unwield if Cancelled + /// + public string? Message; + + SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.WITHOUT_POCKET; public void Cancel() { Cancelled = true; diff --git a/Content.Shared/Wieldable/SharedWieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs index ec476e6db6..83ed58388a 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -1,11 +1,12 @@ using System.Linq; -using Content.Shared.Camera; using Content.Shared.Examine; using Content.Shared.Hands; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; using Content.Shared.Interaction.Events; +using Content.Shared.Inventory; +using Content.Shared.Inventory.Events; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item; using Content.Shared.Movement.Components; @@ -22,7 +23,6 @@ using Content.Shared.Weapons.Ranged.Systems; using Content.Shared.Wieldable.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Collections; -using Robust.Shared.Network; using Robust.Shared.Timing; namespace Content.Shared.Wieldable; @@ -51,6 +51,12 @@ public abstract class SharedWieldableSystem : EntitySystem SubscribeLocalEvent>(AddToggleWieldVerb); SubscribeLocalEvent(OnDeselectWieldable); + SubscribeLocalEvent(OnBlockerEquipped); + SubscribeLocalEvent(OnBlockerEquippedHand); + SubscribeLocalEvent(OnBlockerAttempt); + SubscribeLocalEvent>(OnBlockerAttempt); + SubscribeLocalEvent>(OnBlockerAttempt); + SubscribeLocalEvent(OnMeleeAttempt); SubscribeLocalEvent(OnExamineRequires); SubscribeLocalEvent(OnShootAttempt); @@ -186,14 +192,55 @@ public abstract class SharedWieldableSystem : EntitySystem return; if (!component.Wielded) - args.Handled = TryWield(uid, component, args.User); + { + TryWield(uid, component, args.User); + args.Handled = true; // always mark as handled or we will cycle ammo when wielding is blocked + } else if (component.UnwieldOnUse) - args.Handled = TryUnwield(uid, component, args.User); + { + TryUnwield(uid, component, args.User); + args.Handled = true; + } if (HasComp(uid) && !component.UseDelayOnWield) args.ApplyDelay = false; } + private void OnBlockerEquipped(Entity ent, ref GotEquippedEvent args) + { + if (ent.Comp.BlockEquipped) + UnwieldAll(args.Equipee, force: true); + } + + private void OnBlockerEquippedHand(Entity ent, ref GotEquippedHandEvent args) + { + if (ent.Comp.BlockInHand) + UnwieldAll(args.User, force: true); + } + + private void OnBlockerAttempt(Entity ent, ref InventoryRelayedEvent args) + { + if (ent.Comp.BlockEquipped) + { + args.Args.Message = Loc.GetString("wieldable-component-blocked-wield", ("blocker", ent.Owner), ("item", args.Args.Wielded)); + args.Args.Cancelled = true; + } + } + + private void OnBlockerAttempt(Entity ent, ref HeldRelayedEvent args) + { + if (ent.Comp.BlockInHand) + { + args.Args.Message = Loc.GetString("wieldable-component-blocked-wield", ("blocker", ent.Owner), ("item", args.Args.Wielded)); + args.Args.Cancelled = true; + } + } + + private void OnBlockerAttempt(Entity ent, ref WieldAttemptEvent args) + { + args.Cancelled = true; + } + public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user, bool quiet = false) { // Do they have enough hands free? @@ -242,11 +289,15 @@ public abstract class SharedWieldableSystem : EntitySystem return false; } - var attemptEv = new WieldAttemptEvent(user); - RaiseLocalEvent(used, ref attemptEv); + var attemptEv = new WieldAttemptEvent(user, used); + RaiseLocalEvent(user, ref attemptEv); if (attemptEv.Cancelled) + { + if (attemptEv.Message != null) + _popup.PopupClient(attemptEv.Message, user, user); return false; + } if (TryComp(used, out var item)) { @@ -298,11 +349,15 @@ public abstract class SharedWieldableSystem : EntitySystem if (!force) { - var attemptEv = new UnwieldAttemptEvent(user); - RaiseLocalEvent(used, ref attemptEv); + var attemptEv = new UnwieldAttemptEvent(user, used); + RaiseLocalEvent(user, ref attemptEv); if (attemptEv.Cancelled) + { + if (attemptEv.Message != null) + _popup.PopupClient(attemptEv.Message, user, user); return false; + } } SetWielded((used, component), false); @@ -312,6 +367,19 @@ public abstract class SharedWieldableSystem : EntitySystem return true; } + /// + /// Makes an entity unwield all currently wielded items. + /// + /// If this is true we will bypass UnwieldAttemptEvent. + public void UnwieldAll(Entity wielder, bool force = false) + { + foreach (var held in _hands.EnumerateHeld(wielder.Owner, wielder.Comp)) + { + if (TryComp(held, out var wieldable)) + TryUnwield(held, wieldable, wielder, force); + } + } + /// /// Sets wielded without doing any checks. /// diff --git a/Resources/Locale/en-US/wieldable/wieldable-component.ftl b/Resources/Locale/en-US/wieldable/wieldable-component.ftl index 1c88e45727..f4c5446960 100644 --- a/Resources/Locale/en-US/wieldable/wieldable-component.ftl +++ b/Resources/Locale/en-US/wieldable/wieldable-component.ftl @@ -7,6 +7,7 @@ wieldable-component-successful-wield = You wield { THE($item) }. wieldable-component-failed-wield = You unwield { THE($item) }. wieldable-component-successful-wield-other = { CAPITALIZE(THE($user)) } wields { THE($item) }. wieldable-component-failed-wield-other = { CAPITALIZE(THE($user)) } unwields { THE($item) }. +wieldable-component-blocked-wield = { CAPITALIZE(THE($blocker)) } blocks you from wielding { THE($item) }. wieldable-component-no-hands = You don't have enough hands! wieldable-component-not-enough-free-hands = {$number ->