diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index 457e3875d4..43508e5312 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -83,7 +83,7 @@ namespace Content.Shared.ActionBlocker /// check other blockers like /// /// - public bool CanInteract(EntityUid user, EntityUid? target) + public bool CanInteract(EntityUid user, EntityUid? target, bool showFailedPopup = false) { if (!CanConsciouslyPerformAction(user)) return false; @@ -97,7 +97,7 @@ namespace Content.Shared.ActionBlocker if (target == null || target == user) return true; - var targetEv = new GettingInteractedWithAttemptEvent(user, target); + var targetEv = new GettingInteractedWithAttemptEvent(user, target, showFailedPopup); RaiseLocalEvent(target.Value, ref targetEv); return !targetEv.Cancelled; diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs index 7a9844b48d..c652a439a5 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs @@ -221,7 +221,7 @@ namespace Content.Shared.Containers.ItemSlots /// [DataField] [Access(typeof(ItemSlotsSystem), Other = AccessPermissions.ReadWriteExecute)] - public bool Swap = true; + public bool Swap = false; // WWDP immersive interactions public string? ID => ContainerSlot?.ID; diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs index 3952d9870b..c995771bcd 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs @@ -85,7 +85,7 @@ public abstract partial class SharedHandsSystem : EntitySystem RemComp(uid); return false; } - + private readonly Angle _dropRotationIncrement = Angle.FromDegrees(5); private bool PreciseDropMWheelUp(in PointerInputCmdHandler.PointerInputCmdArgs args) { @@ -163,7 +163,7 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!TryComp(session?.AttachedEntity, out HandsComponent? component)) return; - if (!_actionBlocker.CanInteract(session.AttachedEntity.Value, null)) + if (!_actionBlocker.CanInteract(session.AttachedEntity.Value, null, true)) return; if (component.ActiveHand == null || component.Hands.Count < 2) diff --git a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs index a04c053635..92be214635 100644 --- a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs +++ b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs @@ -26,9 +26,11 @@ /// generic interaction. /// [ByRefEvent] - public struct GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target) + public struct GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target, bool showFailedPopup = false) { public bool Cancelled; + public bool Handled; + public bool ShowFailedPopup = showFailedPopup; public readonly EntityUid Uid = uid; public readonly EntityUid? Target = target; } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index be2fbaf84d..3b254abd65 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -161,7 +161,7 @@ namespace Content.Shared.Interaction private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev) { _uiQuery.TryComp(ev.Target, out var uiComp); - if (!_actionBlockerSystem.CanInteract(ev.Actor, ev.Target)) + if (!_actionBlockerSystem.CanInteract(ev.Actor, ev.Target, true)) // WWDP interaction popups { // We permit ghosts to open uis unless explicitly blocked if (ev.Message is not OpenBoundInterfaceMessage || !HasComp(ev.Actor) || uiComp?.BlockSpectators == true) @@ -381,7 +381,7 @@ namespace Content.Shared.Interaction if (_relayQuery.TryComp(user, out var relay) && relay.RelayEntity is not null) { // TODO this needs to be handled better. This probably bypasses many complex can-interact checks in weird roundabout ways. - if (_actionBlockerSystem.CanInteract(user, target)) + if (_actionBlockerSystem.CanInteract(user, target, true)) // WWDP interaction popups { UserInteraction(relay.RelayEntity.Value, coordinates, @@ -414,7 +414,7 @@ namespace Content.Shared.Interaction return; } - if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target, true)) // WWDP interaction popups return; // Check if interacted entity is in the same container, the direct child, or direct parent of the user. @@ -988,10 +988,7 @@ namespace Content.Shared.Interaction if (IsDeleted(user) || IsDeleted(used) || IsDeleted(target)) return false; - if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) - return false; - - if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target, true)) // WWDP interaction popups return false; if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used)) @@ -1005,7 +1002,9 @@ namespace Content.Shared.Interaction if (RangedInteractDoBefore(user, used, target, clickLocation, true)) return true; - // all interactions should only happen when in range / unobstructed, so no range check is needed + if (!InRangeAndAccessible(user, target)) // WWDP no interacting with items inside containers + return false; + var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation); RaiseLocalEvent(target, interactUsingEvent, true); DoContactInteraction(user, used, interactUsingEvent); @@ -1107,7 +1106,7 @@ namespace Content.Shared.Interaction if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent))) return false; - if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used, true)) // WWDP interaction popups return false; if (checkAccess && !InRangeUnobstructed(user, used)) @@ -1161,7 +1160,7 @@ namespace Content.Shared.Interaction if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent))) return true; // if the item is on cooldown, we consider this handled. - if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used)) + if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used, true)) // WWDP interaction popups return false; if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used)) @@ -1212,7 +1211,7 @@ namespace Content.Shared.Interaction if (ev.Handled) return true; // WD EDIT END - + // Get list of alt-interact verbs var verbs = _verbSystem.GetLocalVerbs(target, user, typeof(AlternativeVerb)).Where(verb => ((AlternativeVerb) verb).InActiveHandOnly == false); // WD EDIT @@ -1301,7 +1300,7 @@ namespace Content.Shared.Interaction if (_containerSystem.IsInSameOrParentContainer(user, target, out _, out var container)) return true; - return container != null && CanAccessViaStorage(user, target, container); + return false; // WWDP no interacting with items inside containers } /// diff --git a/Content.Shared/Interaction/SmartEquipSystem.cs b/Content.Shared/Interaction/SmartEquipSystem.cs index bba294db28..185f95f59d 100644 --- a/Content.Shared/Interaction/SmartEquipSystem.cs +++ b/Content.Shared/Interaction/SmartEquipSystem.cs @@ -127,6 +127,9 @@ public sealed class SmartEquipSystem : EntitySystem // case 2 (storage item): if (TryComp(slotItem, out var storage)) { + if (!_actionBlocker.CanInteract(uid, slotItem, true)) // WWDP Interactions + return; + switch (handItem) { case null when storage.Container.ContainedEntities.Count == 0: @@ -159,6 +162,9 @@ public sealed class SmartEquipSystem : EntitySystem // case 3 (itemslot item): if (TryComp(slotItem, out var slots)) { + if (!_actionBlocker.CanInteract(uid, slotItem, true)) // WWDP Interactions + return; + if (handItem == null) { ItemSlot? toEjectFrom = null; diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index 2c930834e8..30135ecc14 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -10,6 +10,7 @@ using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Movement.Systems; using Content.Shared.Popups; +using Content.Shared.Storage; using Content.Shared.Strip; using Content.Shared.Strip.Components; using Content.Shared.Whitelist; @@ -30,6 +31,7 @@ public abstract partial class InventorySystem [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedUserInterfaceSystem _interface = default!; // WWDP [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; @@ -44,6 +46,8 @@ public abstract partial class InventorySystem SubscribeLocalEvent(OnEntInserted); SubscribeLocalEvent(OnEntRemoved); + SubscribeLocalEvent(OnEquipped); // WWDP + SubscribeAllEvent(OnUseSlot); } @@ -124,6 +128,15 @@ public abstract partial class InventorySystem TryEquip(actor, actor, held.Value, ev.Slot, predicted: true, inventory: inventory, force: true, checkDoafter:true); } + // WWDP IF WE PUT IT ON AND ITS NOT ACCESSIBLE CLOSE THE UI + private void OnEquipped(Entity item, ref GotEquippedEvent args) + { + if (item.Comp.CanBeUsedWhileWorn) + return; + + _interface.CloseUis(item.Owner); + } + public bool TryEquip(EntityUid uid, EntityUid itemUid, string slot, bool silent = false, bool force = false, bool predicted = false, InventoryComponent? inventory = null, ClothingComponent? clothing = null, bool checkDoafter = false) => TryEquip(uid, uid, itemUid, slot, silent, force, predicted, inventory, clothing, checkDoafter); @@ -173,7 +186,7 @@ public abstract partial class InventorySystem { BlockDuplicate = true, BreakOnHandChange = true, - BreakOnMove = true, + BreakOnMove = false, // WWDP no breaking on move BreakOnDamage = false, // White Dream: Do not break on recieving damage CancelDuplicate = true, RequireCanInteract = true, @@ -422,7 +435,7 @@ public abstract partial class InventorySystem { BlockDuplicate = true, BreakOnHandChange = true, - BreakOnMove = true, + BreakOnMove = false, // WWDP no breaking on move BreakOnDamage = false, // White Dream: Do not break on recieving damage CancelDuplicate = true, RequireCanInteract = true, diff --git a/Content.Shared/Item/ItemComponent.cs b/Content.Shared/Item/ItemComponent.cs index 0f599ebdbe..c939c5b4f1 100644 --- a/Content.Shared/Item/ItemComponent.cs +++ b/Content.Shared/Item/ItemComponent.cs @@ -29,6 +29,12 @@ public sealed partial class ItemComponent : Component [DataField, AutoNetworkedField] public string? HeldPrefix; + /// + /// WWDP - Can this item be used while equipped in someone's inventory, including pockets + /// + [DataField, AutoNetworkedField] + public bool CanBeUsedWhileWorn; + /// /// Rsi of the sprite shown on the player when this item is in their hands. Used to generate a default entry for /// diff --git a/Content.Shared/Item/SharedItemSystem.cs b/Content.Shared/Item/SharedItemSystem.cs index a058cb8658..611dcfe546 100644 --- a/Content.Shared/Item/SharedItemSystem.cs +++ b/Content.Shared/Item/SharedItemSystem.cs @@ -2,7 +2,10 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Verbs; using Content.Shared.Examine; +using Content.Shared.Interaction.Events; +using Content.Shared.Inventory; using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Popups; using Content.Shared.Storage; using JetBrains.Annotations; using Robust.Shared.Containers; @@ -16,6 +19,8 @@ public abstract class SharedItemSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; // WWDP + [Dependency] private readonly SharedPopupSystem _popup = default!; // WWDP [Dependency] protected readonly SharedContainerSystem Container = default!; public override void Initialize() @@ -24,6 +29,7 @@ public abstract class SharedItemSystem : EntitySystem SubscribeLocalEvent>(AddPickupVerb); SubscribeLocalEvent(OnHandInteract); SubscribeLocalEvent(OnItemAutoState); + SubscribeLocalEvent(OnItemInteracted); // WWDP SubscribeLocalEvent(OnExamine); @@ -94,6 +100,28 @@ public abstract class SharedItemSystem : EntitySystem args.Handled = _handsSystem.TryPickup(args.User, uid, animateUser: false); } + /// + /// WWDP - Handle attempt to interact with this item + /// Block interactions if it is equipped unless CanBeUsedWhileWorn + /// + private void OnItemInteracted(EntityUid uid, ItemComponent component, ref GettingInteractedWithAttemptEvent args) + { + if (uid != args.Target || args.Handled) + return; + + if (!_inventory.TryGetContainingSlot(uid, out _)) + return; + + if (component.CanBeUsedWhileWorn) + return; + + if (args.ShowFailedPopup) + _popup.PopupClient(Loc.GetString("must-be-in-hand"), args.Uid); + + args.Cancelled = true; + args.Handled = true; + } + private void AddPickupVerb(EntityUid uid, ItemComponent component, GetVerbsEvent args) { if (args.Hands == null || diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 3afb52b959..4944b0d7c7 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Input; using Content.Shared.Interaction; using Content.Shared.Interaction.Components; using Content.Shared.Inventory; +using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Lock; using Content.Shared.Materials; diff --git a/Resources/Locale/en-US/_white/popups/interactions.ftl b/Resources/Locale/en-US/_white/popups/interactions.ftl new file mode 100644 index 0000000000..ee5e9628b6 --- /dev/null +++ b/Resources/Locale/en-US/_white/popups/interactions.ftl @@ -0,0 +1 @@ +must-be-in-hand = Must be in hand! diff --git a/Resources/Locale/ru-RU/_white/popups/interactions.ftl b/Resources/Locale/ru-RU/_white/popups/interactions.ftl new file mode 100644 index 0000000000..6752450d3a --- /dev/null +++ b/Resources/Locale/ru-RU/_white/popups/interactions.ftl @@ -0,0 +1 @@ +must-be-in-hand = Должно быть в руке! diff --git a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml index 1a5f89dd13..c6f346ef38 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml @@ -8,6 +8,7 @@ sprite: Clothing/Back/Backpacks/backpack.rsi state: icon - type: Item + canBeUsedWhileWorn: false # WWDP size: Huge - type: Clothing equipSound: /Audio/_White/Equip/clothingrustle4.ogg # WWDP @@ -34,6 +35,10 @@ - type: ExplosionResistance damageCoefficient: 0.9 - type: AllowsSleepInside # DeltaV - enable sleeping inside bags + - type: HeldSpeedModifier # WWDP slow down with bags in hand + mirrorClothingModifier: false + sprintModifier: 0.85 + walkModifier: 0.85 - type: entity parent: ClothingBackpack diff --git a/Resources/Prototypes/Entities/Clothing/Back/duffel.yml b/Resources/Prototypes/Entities/Clothing/Back/duffel.yml index 969d9a6fde..4d28a46a04 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/duffel.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/duffel.yml @@ -13,7 +13,18 @@ - type: ClothingSpeedModifier walkModifier: 1 sprintModifier: 0.9 - - type: HeldSpeedModifier + - type: HeldSpeedModifier # WWDP slow down with bags in hand + mirrorClothingModifier: false + sprintModifier: 0.8 + walkModifier: 0.8 + - type: Clothing + equipDelay: 1 + unequipDelay: 1 + equipSound: /Audio/_White/Equip/clothingrustle2.ogg # WWDP + - type: EmitSoundOnPickup # WWDP + sound: /Audio/_White/Equip/clothingrustle2.ogg + - type: Item + canBeUsedWhileWorn: false # WWDP - type: entity parent: ClothingBackpackDuffel diff --git a/Resources/Prototypes/Entities/Clothing/Back/satchel.yml b/Resources/Prototypes/Entities/Clothing/Back/satchel.yml index 33daadcbe9..465ee411a1 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/satchel.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/satchel.yml @@ -11,6 +11,8 @@ - 0,0,1,3 - 3,0,6,3 - 8,0,9,3 + - type: Clothing # WWDP + #equipSound: /Audio/_White/Equip/clothingrustle4.ogg todo sounds from the gunnening - type: entity parent: ClothingBackpackSatchel diff --git a/Resources/Prototypes/Entities/Clothing/base_clothing.yml b/Resources/Prototypes/Entities/Clothing/base_clothing.yml index 70fd8bffd7..8e1b1ac8e8 100644 --- a/Resources/Prototypes/Entities/Clothing/base_clothing.yml +++ b/Resources/Prototypes/Entities/Clothing/base_clothing.yml @@ -5,6 +5,7 @@ components: - type: Item size: Normal + canBeUsedWhileWorn: true # WWDP - type: Sprite - type: Tag tags: @@ -12,6 +13,8 @@ - type: StaticPrice price: 10 - type: Clothing + equipDelay: 0.5 # WWDP + unequipDelay: 0.5 # WWDP - type: entity abstract: true