diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs
index 38bcbe940d..b609193342 100644
--- a/Content.Client/Entry/IgnoredComponents.cs
+++ b/Content.Client/Entry/IgnoredComponents.cs
@@ -274,6 +274,8 @@ namespace Content.Client.Entry
"PowerNetworkBattery",
"BatteryCharger",
"SpawnItemsOnUse",
+ "Wieldable",
+ "IncreaseDamageOnWield",
"AmbientOnPowered",
"TabletopGame"
};
diff --git a/Content.Client/Hands/HandVirtualPullItemStatus.xaml b/Content.Client/Hands/HandVirtualItemStatus.xaml
similarity index 50%
rename from Content.Client/Hands/HandVirtualPullItemStatus.xaml
rename to Content.Client/Hands/HandVirtualItemStatus.xaml
index 9fac1e0d08..02258edd8e 100644
--- a/Content.Client/Hands/HandVirtualPullItemStatus.xaml
+++ b/Content.Client/Hands/HandVirtualItemStatus.xaml
@@ -1,3 +1,3 @@
-
+
diff --git a/Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs b/Content.Client/Hands/HandVirtualItemStatus.xaml.cs
similarity index 64%
rename from Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs
rename to Content.Client/Hands/HandVirtualItemStatus.xaml.cs
index f4324f6e41..8fb8519261 100644
--- a/Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs
+++ b/Content.Client/Hands/HandVirtualItemStatus.xaml.cs
@@ -3,9 +3,9 @@ using Robust.Client.UserInterface.XAML;
namespace Content.Client.Hands
{
- public sealed class HandVirtualPullItemStatus : Control
+ public sealed class HandVirtualItemStatus : Control
{
- public HandVirtualPullItemStatus()
+ public HandVirtualItemStatus()
{
RobustXamlLoader.Load(this);
}
diff --git a/Content.Client/Hands/HandsGui.xaml.cs b/Content.Client/Hands/HandsGui.xaml.cs
index 63590882bf..ccb679790a 100644
--- a/Content.Client/Hands/HandsGui.xaml.cs
+++ b/Content.Client/Hands/HandsGui.xaml.cs
@@ -98,9 +98,9 @@ namespace Content.Client.Hands
_itemSlotManager.SetItemSlot(newButton, hand.HeldItem);
- // Show blocked overlay if hand is pulling.
+ // Show blocked overlay if hand is blocked.
newButton.Blocked.Visible =
- hand.HeldItem != null && hand.HeldItem.HasComponent();
+ hand.HeldItem != null && hand.HeldItem.HasComponent();
}
if (TryGetActiveHand(out var activeHand))
diff --git a/Content.Client/Hands/Systems/HandVirtualPullSystem.cs b/Content.Client/Hands/Systems/HandVirtualItemSystem.cs
similarity index 58%
rename from Content.Client/Hands/Systems/HandVirtualPullSystem.cs
rename to Content.Client/Hands/Systems/HandVirtualItemSystem.cs
index 0f47efd9dc..deb9f993a9 100644
--- a/Content.Client/Hands/Systems/HandVirtualPullSystem.cs
+++ b/Content.Client/Hands/Systems/HandVirtualItemSystem.cs
@@ -3,16 +3,16 @@ using Content.Shared.Hands.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
-namespace Content.Client.Hands
+namespace Content.Client.Hands.Systems
{
[UsedImplicitly]
- public sealed class HandVirtualPullSystem : EntitySystem
+ public sealed class HandVirtualItemSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
- Subs.ItemStatus(_ => new HandVirtualPullItemStatus());
+ Subs.ItemStatus(_ => new HandVirtualItemStatus());
}
}
}
diff --git a/Content.Client/Items/Managers/ItemSlotManager.cs b/Content.Client/Items/Managers/ItemSlotManager.cs
index c274547cda..515eb521ec 100644
--- a/Content.Client/Items/Managers/ItemSlotManager.cs
+++ b/Content.Client/Items/Managers/ItemSlotManager.cs
@@ -48,8 +48,8 @@ namespace Content.Client.Items.Managers
else
{
ISpriteComponent? sprite;
- if (entity.TryGetComponent(out HandVirtualPullComponent? virtPull)
- && _componentManager.TryGetComponent(virtPull.PulledEntity, out ISpriteComponent pulledSprite))
+ if (entity.TryGetComponent(out HandVirtualItemComponent? virtPull)
+ && _componentManager.TryGetComponent(virtPull.BlockingEntity, out ISpriteComponent pulledSprite))
{
sprite = pulledSprite;
}
diff --git a/Content.Client/Items/UI/ItemStatusPanel.cs b/Content.Client/Items/UI/ItemStatusPanel.cs
index d1dbd714c6..e4d7aa1d41 100644
--- a/Content.Client/Items/UI/ItemStatusPanel.cs
+++ b/Content.Client/Items/UI/ItemStatusPanel.cs
@@ -156,10 +156,10 @@ namespace Content.Client.Items.UI
if (_entity == null)
return;
- if (_entity.TryGetComponent(out HandVirtualPullComponent? virtualPull)
- && _entityManager.TryGetEntity(virtualPull.PulledEntity, out var pulledEnt))
+ if (_entity.TryGetComponent(out HandVirtualItemComponent? virtualItem)
+ && _entityManager.TryGetEntity(virtualItem.BlockingEntity, out var blockEnt))
{
- _itemNameLabel.Text = pulledEnt.Name;
+ _itemNameLabel.Text = blockEnt.Name;
}
else
{
diff --git a/Content.Server/DoAfter/DoAfterEventArgs.cs b/Content.Server/DoAfter/DoAfterEventArgs.cs
index 8c1e9e4717..83d1b15c00 100644
--- a/Content.Server/DoAfter/DoAfterEventArgs.cs
+++ b/Content.Server/DoAfter/DoAfterEventArgs.cs
@@ -83,22 +83,22 @@ namespace Content.Server.DoAfter
///
/// Event to be raised directed to the entity when the DoAfter is cancelled.
///
- public EntityEventArgs? UserCancelledEvent { get; set; }
+ public object? UserCancelledEvent { get; set; }
///
/// Event to be raised directed to the entity when the DoAfter is finished successfully.
///
- public EntityEventArgs? UserFinishedEvent { get; set; }
+ public object? UserFinishedEvent { get; set; }
///
/// Event to be raised directed to the entity when the DoAfter is cancelled.
///
- public EntityEventArgs? TargetCancelledEvent { get; set; }
+ public object? TargetCancelledEvent { get; set; }
///
/// Event to be raised directed to the entity when the DoAfter is finished successfully.
///
- public EntityEventArgs? TargetFinishedEvent { get; set; }
+ public object? TargetFinishedEvent { get; set; }
///
/// Event to be broadcast when the DoAfter is cancelled.
diff --git a/Content.Server/Hands/Systems/HandVirtualItemSystem.cs b/Content.Server/Hands/Systems/HandVirtualItemSystem.cs
new file mode 100644
index 0000000000..820c409173
--- /dev/null
+++ b/Content.Server/Hands/Systems/HandVirtualItemSystem.cs
@@ -0,0 +1,106 @@
+using Content.Server.Hands.Components;
+using Content.Server.Pulling;
+using Content.Shared.Hands;
+using Content.Shared.Hands.Components;
+using Content.Shared.Interaction;
+using JetBrains.Annotations;
+using Robust.Shared.GameObjects;
+
+namespace Content.Server.Hands.Systems
+{
+ [UsedImplicitly]
+ public sealed class HandVirtualItemSystem : EntitySystem
+ {
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(HandleItemDropped);
+ SubscribeLocalEvent(HandleItemUnequipped);
+
+ SubscribeLocalEvent(HandleBeforeInteract);
+ }
+
+ public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user)
+ {
+ if (ComponentManager.TryGetComponent(user, out var hands))
+ {
+ foreach (var handName in hands.ActivePriorityEnumerable())
+ {
+ var hand = hands.GetHand(handName);
+ if (!hand.IsEmpty)
+ continue;
+
+ var pos = hands.Owner.Transform.Coordinates;
+ var virtualItem = EntityManager.SpawnEntity("HandVirtualItem", pos);
+ var virtualItemComp = virtualItem.GetComponent();
+ virtualItemComp.BlockingEntity = blockingEnt;
+ hands.PutEntityIntoHand(hand, virtualItem);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static void HandleBeforeInteract(
+ EntityUid uid,
+ HandVirtualItemComponent component,
+ BeforeInteractEvent args)
+ {
+ // No interactions with a virtual item, please.
+ args.Handled = true;
+ }
+
+ // If the virtual item gets removed from the hands for any reason, cancel the pull and delete it.
+ private void HandleItemUnequipped(EntityUid uid, HandVirtualItemComponent component, UnequippedHandEvent args)
+ {
+ Delete(component, args.User.Uid);
+ }
+
+ private void HandleItemDropped(EntityUid uid, HandVirtualItemComponent component, DroppedEvent args)
+ {
+ Delete(component, args.User.Uid);
+ }
+
+ ///
+ /// Queues a deletion for a virtual item and notifies the blocking entity and user.
+ ///
+ public void Delete(HandVirtualItemComponent comp, EntityUid user)
+ {
+ var userEv = new VirtualItemDeletedEvent(comp.BlockingEntity, user);
+ RaiseLocalEvent(user, userEv, false);
+ var targEv = new VirtualItemDeletedEvent(comp.BlockingEntity, user);
+ RaiseLocalEvent(comp.BlockingEntity, targEv, false);
+
+ comp.Owner.QueueDelete();
+ }
+
+ ///
+ /// Deletes all virtual items in a user's hands with
+ /// the specified blocked entity.
+ ///
+ public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
+ {
+ if (ComponentManager.TryGetComponent(user, out var hands))
+ {
+ foreach (var handName in hands.ActivePriorityEnumerable())
+ {
+ var hand = hands.GetHand(handName);
+ if (hand.IsEmpty)
+ continue;
+
+ if (hand.HeldEntity != null)
+ {
+ if (ComponentManager.TryGetComponent(hand.HeldEntity.Uid,
+ out var virt)
+ && virt.BlockingEntity == matching)
+ {
+ Delete(virt, user);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Content.Server/Hands/Systems/HandVirtualPullSystem.cs b/Content.Server/Hands/Systems/HandVirtualPullSystem.cs
deleted file mode 100644
index f95a4747ca..0000000000
--- a/Content.Server/Hands/Systems/HandVirtualPullSystem.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using Content.Server.Pulling;
-using Content.Shared.Hands;
-using Content.Shared.Hands.Components;
-using Content.Shared.Interaction;
-using JetBrains.Annotations;
-using Robust.Shared.GameObjects;
-
-namespace Content.Server.Hands
-{
- [UsedImplicitly]
- public sealed class HandVirtualPullSystem : EntitySystem
- {
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent(HandlePullerDropped);
- SubscribeLocalEvent(HandlePullerUnequipped);
-
- SubscribeLocalEvent(HandleBeforeInteract);
- }
-
- private static void HandleBeforeInteract(
- EntityUid uid,
- HandVirtualPullComponent component,
- BeforeInteractEvent args)
- {
- // No interactions with a virtual pull, please.
- args.Handled = true;
- }
-
- // If the virtual pull gets removed from the hands for any reason, cancel the pull and delete it.
- private void HandlePullerUnequipped(EntityUid uid, HandVirtualPullComponent component, UnequippedHandEvent args)
- {
- MaybeDelete(component, args.User);
- }
-
- private void HandlePullerDropped(EntityUid uid, HandVirtualPullComponent component, DroppedEvent args)
- {
- MaybeDelete(component, args.User);
- }
-
- private void MaybeDelete(HandVirtualPullComponent comp, IEntity? user)
- {
- var pulled = comp.PulledEntity;
-
- if (!ComponentManager.TryGetComponent(pulled, out PullableComponent? pullable))
- return;
-
- if (pullable.Puller != user)
- return;
-
- pullable.TryStopPull(user);
- comp.Owner.QueueDelete();
- }
- }
-}
diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs
index 6461c65013..13bddd4408 100644
--- a/Content.Server/Hands/Systems/HandsSystem.cs
+++ b/Content.Server/Hands/Systems/HandsSystem.cs
@@ -2,6 +2,7 @@ using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Hands.Components;
+using Content.Server.Hands.Systems;
using Content.Server.Interaction;
using Content.Server.Inventory.Components;
using Content.Server.Items;
@@ -26,13 +27,14 @@ using Robust.Shared.Players;
using Robust.Shared.Utility;
using static Content.Shared.Inventory.EquipmentSlotDefines;
-namespace Content.Server.Hands
+namespace Content.Server.Hands.Systems
{
[UsedImplicitly]
internal sealed class HandsSystem : SharedHandsSystem
{
[Dependency] private readonly InteractionSystem _interactionSystem = default!;
[Dependency] private readonly StackSystem _stackSystem = default!;
+ [Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!;
public override void Initialize()
{
@@ -68,21 +70,10 @@ namespace Content.Server.Hands
private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args)
{
- foreach (var handName in component.ActivePriorityEnumerable())
+ if (!_virtualItemSystem.TrySpawnVirtualItemInHand(args.Pulled.Owner.Uid, uid))
{
- var hand = component.GetHand(handName);
- if (!hand.IsEmpty)
- continue;
-
- var pos = component.Owner.Transform.Coordinates;
- var virtualPull = EntityManager.SpawnEntity("HandVirtualPull", pos);
- var virtualPullComp = virtualPull.GetComponent();
- virtualPullComp.PulledEntity = args.Pulled.Owner.Uid;
- component.PutEntityIntoHand(hand, virtualPull);
- return;
+ DebugTools.Assert("Unable to find available hand when starting pulling??");
}
-
- DebugTools.Assert("Unable to find available hand when starting pulling??");
}
private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args)
@@ -92,8 +83,8 @@ namespace Content.Server.Hands
foreach (var hand in component.Hands)
{
if (hand.HeldEntity == null
- || !hand.HeldEntity.TryGetComponent(out HandVirtualPullComponent? virtualPull)
- || virtualPull.PulledEntity != args.Pulled.Owner.Uid)
+ || !hand.HeldEntity.TryGetComponent(out HandVirtualItemComponent? virtualItem)
+ || virtualItem.BlockingEntity != args.Pulled.Owner.Uid)
continue;
hand.HeldEntity.Delete();
diff --git a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs
index bfa2649335..a55c7a1581 100644
--- a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs
+++ b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs
@@ -88,7 +88,9 @@ namespace Content.Server.Weapon.Melee
{
var targets = new[] { target };
SendAnimation(comp.ClickArc, angle, args.User, owner, targets, comp.ClickAttackEffect, false);
- _damageableSystem.TryChangeDamage(target.Uid, comp.Damage);
+
+ _damageableSystem.TryChangeDamage(target.Uid,
+ DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList));
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
}
}
@@ -153,7 +155,8 @@ namespace Content.Server.Weapon.Melee
foreach (var entity in hitEntities)
{
- _damageableSystem.TryChangeDamage(entity.Uid, comp.Damage);
+ _damageableSystem.TryChangeDamage(entity.Uid,
+ DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList));
}
}
@@ -274,6 +277,17 @@ namespace Content.Server.Weapon.Melee
///
public class MeleeHitEvent : HandledEntityEventArgs
{
+ ///
+ /// Modifier sets to apply to the hit event when it's all said and done.
+ /// This should be modified by adding a new entry to the list.
+ ///
+ public List ModifiersList = new();
+
+ ///
+ /// A flat amount of damage to add. Same reason as above with Multiplier.
+ ///
+ public int FlatDamage = 0;
+
///
/// A list containing every hit entity. Can be zero.
///
diff --git a/Content.Server/Wieldable/Components/IncreaseDamageOnWieldComponent.cs b/Content.Server/Wieldable/Components/IncreaseDamageOnWieldComponent.cs
new file mode 100644
index 0000000000..a88eb2d8f3
--- /dev/null
+++ b/Content.Server/Wieldable/Components/IncreaseDamageOnWieldComponent.cs
@@ -0,0 +1,17 @@
+using Content.Shared.Damage;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Robust.Shared.Analyzers;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Serialization.Manager.Attributes;
+
+namespace Content.Server.Wieldable.Components
+{
+ [RegisterComponent, Friend(typeof(WieldableSystem))]
+ public class IncreaseDamageOnWieldComponent : Component
+ {
+ public override string Name { get; } = "IncreaseDamageOnWield";
+
+ [DataField("modifiers", required: true)]
+ public DamageModifierSet Modifiers = default!;
+ }
+}
diff --git a/Content.Server/Wieldable/Components/WieldableComponent.cs b/Content.Server/Wieldable/Components/WieldableComponent.cs
new file mode 100644
index 0000000000..83a2d27c18
--- /dev/null
+++ b/Content.Server/Wieldable/Components/WieldableComponent.cs
@@ -0,0 +1,59 @@
+using System.ComponentModel.DataAnnotations.Schema;
+using Content.Shared.Sound;
+using Content.Shared.Verbs;
+using Robust.Shared.Analyzers;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Serialization.Manager.Attributes;
+
+namespace Content.Server.Wieldable.Components
+{
+ ///
+ /// Used for objects that can be wielded in two or more hands,
+ ///
+ [RegisterComponent, Friend(typeof(WieldableSystem))]
+ public class WieldableComponent : Component
+ {
+ public override string Name => "Wieldable";
+
+ [DataField("wieldSound")]
+ public SoundSpecifier? WieldSound = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg");
+
+ [DataField("unwieldSound")]
+ public SoundSpecifier? UnwieldSound = default!;
+
+ ///
+ /// Number of free hands required (excluding the item itself) required
+ /// to wield it
+ ///
+ [DataField("freeHandsRequired")]
+ public int FreeHandsRequired = 1;
+
+ public bool Wielded = false;
+
+ public string WieldedInhandPrefix = "wielded";
+
+ public string? OldInhandPrefix = null;
+
+ [DataField("wieldTime")]
+ public float WieldTime = 1.5f;
+
+ [Verb]
+ public sealed class ToggleWieldVerb : Verb
+ {
+ protected override void GetData(IEntity user, WieldableComponent component, VerbData data)
+ {
+ data.Visibility = VerbVisibility.Visible;
+ data.Text = component.Wielded ? "Unwield" : "Wield";
+ }
+
+ protected override void Activate(IEntity user, WieldableComponent component)
+ {
+ if(!component.Wielded)
+ EntitySystem.Get().AttemptWield(component.Owner.Uid, component, user);
+ else
+ EntitySystem.Get().AttemptUnwield(component.Owner.Uid, component, user);
+
+ }
+ }
+ }
+}
diff --git a/Content.Server/Wieldable/WieldableSystem.cs b/Content.Server/Wieldable/WieldableSystem.cs
new file mode 100644
index 0000000000..babae5b5ed
--- /dev/null
+++ b/Content.Server/Wieldable/WieldableSystem.cs
@@ -0,0 +1,289 @@
+using Content.Server.DoAfter;
+using Content.Server.Hands.Components;
+using Content.Server.Hands.Systems;
+using Content.Server.Items;
+using Content.Server.Weapon.Melee;
+using Content.Server.Wieldable.Components;
+using Content.Shared.Hands;
+using Content.Shared.Hands.Components;
+using Content.Shared.Interaction;
+using Content.Shared.Notification.Managers;
+using Content.Shared.Throwing;
+using Robust.Shared.Audio;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+using Robust.Shared.Player;
+
+namespace Content.Server.Wieldable
+{
+ public class WieldableSystem : EntitySystem
+ {
+ [Dependency] private readonly DoAfterSystem _doAfter = default!;
+ [Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnUseInHand);
+ SubscribeLocalEvent(OnItemWielded);
+ SubscribeLocalEvent(OnItemUnwielded);
+ SubscribeLocalEvent(OnItemLeaveHand);
+ SubscribeLocalEvent(OnVirtualItemDeleted);
+
+ SubscribeLocalEvent(OnMeleeHit);
+ }
+
+ private void OnUseInHand(EntityUid uid, WieldableComponent component, UseInHandEvent args)
+ {
+ if (args.Handled)
+ return;
+ if(!component.Wielded)
+ AttemptWield(uid, component, args.User);
+ else
+ AttemptUnwield(uid, component, args.User);
+ }
+
+ public bool CanWield(EntityUid uid, WieldableComponent component, IEntity user, bool quiet=false)
+ {
+ // Do they have enough hands free?
+ if (!ComponentManager.TryGetComponent(user.Uid, out var hands))
+ {
+ if(!quiet)
+ user.PopupMessage(Loc.GetString("wieldable-component-no-hands"));
+ return false;
+ }
+
+ if (hands.GetFreeHands() < component.FreeHandsRequired)
+ {
+ // TODO FLUENT need function to change 'hands' to 'hand' when there's only 1 required
+ if (!quiet)
+ {
+ user.PopupMessage(Loc.GetString("wieldable-component-not-enough-free-hands",
+ ("number", component.FreeHandsRequired),
+ ("item", EntityManager.GetEntity(uid))));
+ }
+
+ return false;
+ }
+
+ // Is it.. actually in one of their hands?
+ if (!hands.TryGetHandHoldingEntity(EntityManager.GetEntity(uid), out var _))
+ {
+ if (!quiet)
+ {
+ user.PopupMessage(Loc.GetString("wieldable-component-not-in-hands",
+ ("item", EntityManager.GetEntity(uid))));
+ }
+
+ return false;
+ }
+
+ // Seems legit.
+ return true;
+ }
+
+ ///
+ /// Attempts to wield an item, creating a DoAfter..
+ ///
+ public void AttemptWield(EntityUid uid, WieldableComponent component, IEntity user)
+ {
+ if (!CanWield(uid, component, user))
+ return;
+ var ev = new BeforeWieldEvent();
+ RaiseLocalEvent(uid, ev, false);
+ var used = EntityManager.GetEntity(uid);
+
+ if (ev.Cancelled) return;
+
+ var doargs = new DoAfterEventArgs(
+ user,
+ component.WieldTime,
+ default,
+ used
+ )
+ {
+ BreakOnUserMove = false,
+ BreakOnDamage = true,
+ BreakOnStun = true,
+ BreakOnTargetMove = true,
+ TargetFinishedEvent = new ItemWieldedEvent(user),
+ UserFinishedEvent = new WieldedItemEvent(used)
+ };
+
+ _doAfter.DoAfter(doargs);
+ }
+
+ ///
+ /// Attempts to unwield an item, with no DoAfter.
+ ///
+ public void AttemptUnwield(EntityUid uid, WieldableComponent component, IEntity user)
+ {
+ var ev = new BeforeUnwieldEvent();
+ RaiseLocalEvent(uid, ev, false);
+ var used = EntityManager.GetEntity(uid);
+
+ if (ev.Cancelled) return;
+
+ var targEv = new ItemUnwieldedEvent(user);
+ var userEv = new UnwieldedItemEvent(used);
+
+ RaiseLocalEvent(uid, targEv, false);
+ RaiseLocalEvent(user.Uid, userEv, false);
+ }
+
+ private void OnItemWielded(EntityUid uid, WieldableComponent component, ItemWieldedEvent args)
+ {
+ if (args.User == null)
+ return;
+ if (!CanWield(uid, component, args.User) || component.Wielded)
+ return;
+
+ if (ComponentManager.TryGetComponent(uid, out var item))
+ {
+ component.OldInhandPrefix = item.EquippedPrefix;
+ item.EquippedPrefix = component.WieldedInhandPrefix;
+ }
+
+ component.Wielded = true;
+
+ if (component.WieldSound != null)
+ {
+ SoundSystem.Play(Filter.Pvs(EntityManager.GetEntity(uid)), component.WieldSound.GetSound());
+ }
+
+ for (var i = 0; i < component.FreeHandsRequired; i++)
+ {
+ _virtualItemSystem.TrySpawnVirtualItemInHand(uid, args.User.Uid);
+ }
+
+ args.User.PopupMessage(Loc.GetString("wieldable-component-successful-wield",
+ ("item", EntityManager.GetEntity(uid))));
+ }
+
+ private void OnItemUnwielded(EntityUid uid, WieldableComponent component, ItemUnwieldedEvent args)
+ {
+ if (args.User == null)
+ return;
+ if (!component.Wielded)
+ return;
+
+ if (ComponentManager.TryGetComponent(uid, out var item))
+ {
+ item.EquippedPrefix = component.OldInhandPrefix;
+ }
+
+ component.Wielded = false;
+
+ if (!args.Force) // don't play sound/popup if this was a forced unwield
+ {
+ if (component.UnwieldSound != null)
+ {
+ SoundSystem.Play(Filter.Pvs(EntityManager.GetEntity(uid)),
+ component.UnwieldSound.GetSound());
+ }
+
+ args.User.PopupMessage(Loc.GetString("wieldable-component-failed-wield",
+ ("item", EntityManager.GetEntity(uid))));
+ }
+
+ _virtualItemSystem.DeleteInHandsMatching(args.User.Uid, uid);
+ }
+
+ private void OnItemLeaveHand(EntityUid uid, WieldableComponent component, UnequippedHandEvent args)
+ {
+ if (!component.Wielded || component.Owner.Uid != args.Unequipped.Uid)
+ return;
+ RaiseLocalEvent(uid, new ItemUnwieldedEvent(args.User, force: true));
+ }
+
+ private void OnVirtualItemDeleted(EntityUid uid, WieldableComponent component, VirtualItemDeletedEvent args)
+ {
+ if(args.BlockingEntity == uid && component.Wielded)
+ AttemptUnwield(args.BlockingEntity, component, EntityManager.GetEntity(args.User));
+ }
+
+ private void OnMeleeHit(EntityUid uid, IncreaseDamageOnWieldComponent component, MeleeHitEvent args)
+ {
+ if (ComponentManager.TryGetComponent(uid, out var wield))
+ {
+ if (!wield.Wielded)
+ return;
+ }
+ if (args.Handled)
+ return;
+
+ args.ModifiersList.Add(component.Modifiers);
+ }
+ }
+
+ #region Events
+
+ public class BeforeWieldEvent : CancellableEntityEventArgs
+ {
+ }
+
+ ///
+ /// Raised on the item that has been wielded.
+ ///
+ public class ItemWieldedEvent : EntityEventArgs
+ {
+ public IEntity? User;
+
+ public ItemWieldedEvent(IEntity? user=null)
+ {
+ User = user;
+ }
+ }
+
+ ///
+ /// Raised on the user who wielded the item.
+ ///
+ public class WieldedItemEvent : EntityEventArgs
+ {
+ public IEntity Item;
+
+ public WieldedItemEvent(IEntity item)
+ {
+ Item = item;
+ }
+ }
+
+ public class BeforeUnwieldEvent : CancellableEntityEventArgs
+ {
+ }
+
+ ///
+ /// Raised on the item that has been unwielded.
+ ///
+ public class ItemUnwieldedEvent : EntityEventArgs
+ {
+ public IEntity? User;
+ ///
+ /// Whether the item is being forced to be unwielded, or if the player chose to unwield it themselves.
+ ///
+ public bool Force;
+
+ public ItemUnwieldedEvent(IEntity? user=null, bool force=false)
+ {
+ User = user;
+ Force = force;
+ }
+ }
+
+ ///
+ /// Raised on the user who unwielded the item.
+ ///
+ public class UnwieldedItemEvent : EntityEventArgs
+ {
+ public IEntity Item;
+
+ public UnwieldedItemEvent(IEntity item)
+ {
+ Item = item;
+ }
+ }
+
+ #endregion
+}
diff --git a/Content.Shared/Damage/DamageSpecifier.cs b/Content.Shared/Damage/DamageSpecifier.cs
index 50eb06697b..677caab595 100644
--- a/Content.Shared/Damage/DamageSpecifier.cs
+++ b/Content.Shared/Damage/DamageSpecifier.cs
@@ -183,6 +183,24 @@ namespace Content.Shared.Damage
return newDamage;
}
+ ///
+ /// Reduce (or increase) damages by applying multiple modifier sets.
+ ///
+ ///
+ ///
+ ///
+ public static DamageSpecifier ApplyModifierSets(DamageSpecifier damageSpec, IEnumerable modifierSets)
+ {
+ DamageSpecifier newDamage = new(damageSpec);
+ foreach (var set in modifierSets)
+ {
+ // this is probably really inefficient. just don't call this in a hot path I guess.
+ newDamage = ApplyModifierSet(newDamage, set);
+ }
+
+ return newDamage;
+ }
+
///
/// Remove any damage entries with zero damage.
///
diff --git a/Content.Shared/Hands/Components/HandVirtualItemComponent.cs b/Content.Shared/Hands/Components/HandVirtualItemComponent.cs
new file mode 100644
index 0000000000..53f3c0aa28
--- /dev/null
+++ b/Content.Shared/Hands/Components/HandVirtualItemComponent.cs
@@ -0,0 +1,53 @@
+using System;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameStates;
+using Robust.Shared.Players;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Hands.Components
+{
+ [RegisterComponent]
+ [NetworkedComponent]
+ public sealed class HandVirtualItemComponent : Component
+ {
+ private EntityUid _blockingEntity;
+ public override string Name => "HandVirtualItem";
+
+ ///
+ /// The entity blocking this hand.
+ ///
+ public EntityUid BlockingEntity
+ {
+ get => _blockingEntity;
+ set
+ {
+ _blockingEntity = value;
+ Dirty();
+ }
+ }
+
+ public override ComponentState GetComponentState(ICommonSession player)
+ {
+ return new VirtualItemComponentState(BlockingEntity);
+ }
+
+ public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
+ {
+ if (curState is not VirtualItemComponentState pullState)
+ return;
+
+ _blockingEntity = pullState.BlockingEntity;
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class VirtualItemComponentState : ComponentState
+ {
+ public readonly EntityUid BlockingEntity;
+
+ public VirtualItemComponentState(EntityUid blockingEntity)
+ {
+ BlockingEntity = blockingEntity;
+ }
+ }
+ }
+}
diff --git a/Content.Shared/Hands/Components/HandVirtualPullComponent.cs b/Content.Shared/Hands/Components/HandVirtualPullComponent.cs
deleted file mode 100644
index 42f6bd85f3..0000000000
--- a/Content.Shared/Hands/Components/HandVirtualPullComponent.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System;
-using Robust.Shared.GameObjects;
-using Robust.Shared.GameStates;
-using Robust.Shared.Players;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Hands.Components
-{
- [RegisterComponent]
- [NetworkedComponent]
- public sealed class HandVirtualPullComponent : Component
- {
- private EntityUid _pulledEntity;
- public override string Name => "HandVirtualPull";
-
- public EntityUid PulledEntity
- {
- get => _pulledEntity;
- set
- {
- _pulledEntity = value;
- Dirty();
- }
- }
-
- public override ComponentState GetComponentState(ICommonSession player)
- {
- return new VirtualPullComponentState(_pulledEntity);
- }
-
- public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
- {
- if (curState is not VirtualPullComponentState pullState)
- return;
-
- _pulledEntity = pullState.PulledEntity;
- }
-
- [Serializable, NetSerializable]
- public sealed class VirtualPullComponentState : ComponentState
- {
- public readonly EntityUid PulledEntity;
-
- public VirtualPullComponentState(EntityUid pulledEntity)
- {
- PulledEntity = pulledEntity;
- }
- }
- }
-}
diff --git a/Content.Shared/Hands/Components/SharedHandsComponent.cs b/Content.Shared/Hands/Components/SharedHandsComponent.cs
index 0eb370bc22..bdca7b6410 100644
--- a/Content.Shared/Hands/Components/SharedHandsComponent.cs
+++ b/Content.Shared/Hands/Components/SharedHandsComponent.cs
@@ -228,7 +228,23 @@ namespace Content.Shared.Hands.Components
}
}
- private bool TryGetHandHoldingEntity(IEntity entity, [NotNullWhen(true)] out Hand? handFound)
+ ///
+ /// Returns the number of hands that have no items in them.
+ ///
+ ///
+ public int GetFreeHands()
+ {
+ int acc = 0;
+ foreach (var hand in Hands)
+ {
+ if (hand.HeldEntity == null)
+ acc += 1;
+ }
+
+ return acc;
+ }
+
+ public bool TryGetHandHoldingEntity(IEntity entity, [NotNullWhen(true)] out Hand? handFound)
{
handFound = null;
@@ -418,6 +434,7 @@ namespace Content.Shared.Hands.Components
Logger.Error($"{nameof(SharedHandsComponent)} on {Owner} could not remove {heldEntity} from {handContainer}.");
return;
}
+
OnHeldEntityRemovedFromHand(heldEntity, hand.ToHandState());
HandsModified();
diff --git a/Content.Shared/Hands/SharedHandsSystem.cs b/Content.Shared/Hands/SharedHandsSystem.cs
index 96bd96d793..130676adc5 100644
--- a/Content.Shared/Hands/SharedHandsSystem.cs
+++ b/Content.Shared/Hands/SharedHandsSystem.cs
@@ -96,4 +96,20 @@ namespace Content.Shared.Hands
HandName = handName;
}
}
+
+ ///
+ /// Raised directed on both the blocking entity and user when
+ /// a virtual hand item is deleted.
+ ///
+ public class VirtualItemDeletedEvent : EntityEventArgs
+ {
+ public EntityUid BlockingEntity;
+ public EntityUid User;
+
+ public VirtualItemDeletedEvent(EntityUid blockingEntity, EntityUid user)
+ {
+ BlockingEntity = blockingEntity;
+ User = user;
+ }
+ }
}
diff --git a/Content.Shared/Pulling/Components/SharedPullableComponent.cs b/Content.Shared/Pulling/Components/SharedPullableComponent.cs
index a25f3915ff..50d5d9dbf6 100644
--- a/Content.Shared/Pulling/Components/SharedPullableComponent.cs
+++ b/Content.Shared/Pulling/Components/SharedPullableComponent.cs
@@ -122,6 +122,8 @@ namespace Content.Shared.Pulling.Components
}
}
+ valuePuller.Pulling = Owner;
+
// Continue with pulling process.
var pullAttempt = new PullAttemptMessage(pullerPhysics, _physics);
@@ -241,7 +243,7 @@ namespace Content.Shared.Pulling.Components
Puller = puller;
- if (Puller != puller)
+ if(Puller != puller)
{
return false;
}
@@ -266,6 +268,11 @@ namespace Content.Shared.Pulling.Components
_physics.RemoveJoint(_pullJoint);
}
+ if (user != null && user.TryGetComponent(out var puller))
+ {
+ puller.Pulling = null;
+ }
+
_pullJoint = null;
Puller = null;
return true;
diff --git a/Content.Shared/Pulling/Components/SharedPullerComponent.cs b/Content.Shared/Pulling/Components/SharedPullerComponent.cs
index 1579339958..15afacd88e 100644
--- a/Content.Shared/Pulling/Components/SharedPullerComponent.cs
+++ b/Content.Shared/Pulling/Components/SharedPullerComponent.cs
@@ -1,5 +1,6 @@
using Content.Shared.Movement.Components;
using Robust.Shared.GameObjects;
+using Robust.Shared.ViewVariables;
using Component = Robust.Shared.GameObjects.Component;
namespace Content.Shared.Pulling.Components
@@ -11,14 +12,15 @@ namespace Content.Shared.Pulling.Components
private IEntity? _pulling;
- public float WalkSpeedModifier => Pulling == null ? 1.0f : 0.75f;
+ public float WalkSpeedModifier => _pulling == null ? 1.0f : 0.75f;
- public float SprintSpeedModifier => Pulling == null ? 1.0f : 0.75f;
+ public float SprintSpeedModifier => _pulling == null ? 1.0f : 0.75f;
+ [ViewVariables]
public IEntity? Pulling
{
get => _pulling;
- private set
+ set
{
if (_pulling == value)
{
diff --git a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs b/Content.Shared/Pulling/Systems/SharedPullerSystem.cs
index d05754c851..172a9102ea 100644
--- a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs
+++ b/Content.Shared/Pulling/Systems/SharedPullerSystem.cs
@@ -1,10 +1,11 @@
using Content.Shared.Alert;
+using Content.Shared.Hands;
using Content.Shared.Physics.Pull;
using Content.Shared.Pulling.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
-namespace Content.Shared.Pulling
+namespace Content.Shared.Pulling.Systems
{
[UsedImplicitly]
public sealed class SharedPullerSystem : EntitySystem
@@ -15,6 +16,21 @@ namespace Content.Shared.Pulling
SubscribeLocalEvent(PullerHandlePullStarted);
SubscribeLocalEvent(PullerHandlePullStopped);
+ SubscribeLocalEvent(OnVirtualItemDeleted);
+ }
+
+ private void OnVirtualItemDeleted(EntityUid uid, SharedPullerComponent component, VirtualItemDeletedEvent args)
+ {
+ if (component.Pulling == null)
+ return;
+
+ if (component.Pulling == EntityManager.GetEntity(args.BlockingEntity));
+ {
+ if (ComponentManager.TryGetComponent(args.BlockingEntity, out var comp))
+ {
+ comp.TryStopPull(EntityManager.GetEntity(uid));
+ }
+ }
}
private static void PullerHandlePullStarted(
diff --git a/Resources/Locale/en-US/wieldable/wieldable-component.ftl b/Resources/Locale/en-US/wieldable/wieldable-component.ftl
new file mode 100644
index 0000000000..1e955875cf
--- /dev/null
+++ b/Resources/Locale/en-US/wieldable/wieldable-component.ftl
@@ -0,0 +1,8 @@
+### Locale for wielding items; i.e. two-handing them
+
+wieldable-component-successful-wield = You wield { THE($item) }.
+wieldable-component-failed-wield = You unwield { THE($item) }.
+wieldable-component-no-hands = You don't have enough hands!
+wieldable-component-not-enough-free-hands = You need { $number } free hands to wield { THE($item) }.
+wieldable-component-not-in-hands = { CAPITALIZE(THE($item)) } isn't in your hands!
+
diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml
index 39dd6fceb5..a122aecce0 100644
--- a/Resources/Prototypes/Damage/modifier_sets.yml
+++ b/Resources/Prototypes/Damage/modifier_sets.yml
@@ -23,8 +23,18 @@
coefficients:
Blunt: 0.5
Slash: 0.5
- Piercing: 0.5
+ Piercing: 1.5
Heat: 0
Shock: 0
flatReductions:
Blunt: 5
+
+- type: damageModifierSet
+ id: Wood
+ coefficients:
+ Blunt: 0.5
+ Slash: 2.0
+ Piercing: 1.0
+ Heat: 2.0
+ flatReductions:
+ Blunt: 5
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml
index a4b2a73565..11057b854a 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml
@@ -2,7 +2,7 @@
name: fireaxe
parent: BaseItem
id: FireAxe
- description: A large, robust axe. Can pry open doors and skulls alike.
+ description: Truly, the weapon of a madman. Who would think to fight fire with an axe?
components:
- type: Tag
tags:
@@ -12,10 +12,19 @@
state: icon
- type: MeleeWeapon
damage:
- groups:
- Brute: 25
+ types:
+ # axes are kinda like sharp hammers, you know?
+ Blunt: 7
+ Slash: 7
+ - type: Wieldable
+ - type: IncreaseDamageOnWield
+ modifiers:
+ flatReductions:
+ Blunt: -5 # negative reductions = increases
+ Slash: -5
- type: Clothing
size: 20
sprite: Objects/Weapons/Melee/fireaxe.rsi
+ QuickEquip: false
Slots:
- back
diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml
index 4e4d3b2c17..d59766d80e 100644
--- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml
+++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml
@@ -227,6 +227,8 @@
sprite: Structures/Furniture/Tables/wood.rsi
- type: Icon
sprite: Structures/Furniture/Tables/wood.rsi
+ - type: Damageable
+ damageModifierSet: Wood
- type: Destructible
thresholds:
- trigger:
@@ -246,6 +248,9 @@
- type: Construction
graph: table
node: TableWood
+ - type: Tag
+ tags:
+ - Wooden
- type: entity
id: TableCarpet
@@ -257,6 +262,8 @@
sprite: Structures/Furniture/Tables/carpet.rsi
- type: Icon
sprite: Structures/Furniture/Tables/carpet.rsi
+ - type: Damageable
+ damageModifierSet: Wood
- type: Destructible
thresholds:
- trigger:
@@ -279,6 +286,9 @@
- type: Construction
graph: table
node: TableCarpet
+ - type: Tag
+ tags:
+ - Wooden
- type: entity
id: TableStone
diff --git a/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml b/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml
index 45964eee15..9fcfa50b28 100644
--- a/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml
+++ b/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml
@@ -19,6 +19,7 @@
- book-4
- book-5
- type: Damageable
+ damageModifierSet: Wood
damageContainer: Inorganic
- type: Destructible
thresholds:
@@ -36,3 +37,6 @@
max: 1
- !type:DoActsBehavior
acts: ["Destruction"]
+ - type: Tag
+ tags:
+ - Wooden
diff --git a/Resources/Prototypes/Entities/Structures/Furniture/seats.yml b/Resources/Prototypes/Entities/Structures/Furniture/seats.yml
index 9f2c0178b9..242b1dcbec 100644
--- a/Resources/Prototypes/Entities/Structures/Furniture/seats.yml
+++ b/Resources/Prototypes/Entities/Structures/Furniture/seats.yml
@@ -186,6 +186,8 @@
- type: Construction
graph: seat
node: chairWood
+ - type: Damageable
+ damageModifierSet: Wood
- type: Destructible
thresholds:
- trigger:
@@ -202,6 +204,9 @@
MaterialWoodPlank:
min: 1
max: 1
+ - type: Tag
+ tags:
+ - Wooden
- type: entity
name: pilot seat
diff --git a/Resources/Prototypes/Entities/Structures/Walls/barricades.yml b/Resources/Prototypes/Entities/Structures/Walls/barricades.yml
index 4877f003f7..382c7928ce 100644
--- a/Resources/Prototypes/Entities/Structures/Walls/barricades.yml
+++ b/Resources/Prototypes/Entities/Structures/Walls/barricades.yml
@@ -23,9 +23,10 @@
- type: Tag
tags:
- ExplosivePassable
+ - Wooden
- type: Damageable
+ damageModifierSet: Wood
damageContainer: Inorganic
- damageModifierSet: Metallic
- type: Destructible
thresholds:
- trigger:
diff --git a/Resources/Prototypes/Entities/Virtual/virtual_item.yml b/Resources/Prototypes/Entities/Virtual/virtual_item.yml
new file mode 100644
index 0000000000..8c6a6eabbc
--- /dev/null
+++ b/Resources/Prototypes/Entities/Virtual/virtual_item.yml
@@ -0,0 +1,8 @@
+# This item is stored in the hand slot while it is blocked, to represent what is blocking that hand.
+- type: entity
+ id: HandVirtualItem
+ name: VIRTUAL ITEM YOU SHOULD NOT SEE THIS
+ abstract: true
+ components:
+ - type: Item
+ - type: HandVirtualItem
diff --git a/Resources/Prototypes/Entities/Virtual/virtual_pull_item.yml b/Resources/Prototypes/Entities/Virtual/virtual_pull_item.yml
deleted file mode 100644
index aa5b7bdb22..0000000000
--- a/Resources/Prototypes/Entities/Virtual/virtual_pull_item.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-# This item is stored in the hand slot while you are pulling something, to represent that and what you are pulling.
-- type: entity
- id: HandVirtualPull
- name: VIRTUAL PULL YOU SHOULD NOT SEE THIS
- abstract: true
- components:
- - type: Item
- - type: HandVirtualPull
diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml
index 8e6b33c1fd..a535e7775e 100644
--- a/Resources/Prototypes/tags.yml
+++ b/Resources/Prototypes/tags.yml
@@ -205,5 +205,8 @@
- type: Tag
id: Write
+- type: Tag
+ id: Wooden # just like our atmos
+
- type: Tag
id: EmitterBolt
diff --git a/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/meta.json b/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/meta.json
index 36566042fe..3d83a91ae5 100644
--- a/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/meta.json
+++ b/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/meta.json
@@ -17,6 +17,14 @@
{
"name": "inhand-right",
"directions": 4
+ },
+ {
+ "name": "wielded-inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "wielded-inhand-right",
+ "directions": 4
},
{
"name": "equipped-BACKPACK",
diff --git a/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/wielded-inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/wielded-inhand-left.png
new file mode 100644
index 0000000000..f928b92fb1
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/wielded-inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/wielded-inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/wielded-inhand-right.png
new file mode 100644
index 0000000000..65a9442394
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/fireaxe.rsi/wielded-inhand-right.png differ