]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Mjollnir and Singularity Hammer for Wizard (#34446)
authorkeronshb <54602815+keronshb@users.noreply.github.com>
Thu, 20 Feb 2025 23:38:52 +0000 (18:38 -0500)
committerGitHub <noreply@github.com>
Thu, 20 Feb 2025 23:38:52 +0000 (18:38 -0500)
29 files changed:
Content.Shared/Anomaly/SharedAnomalyCoreSystem.cs
Content.Shared/Anomaly/SharedAnomalySystem.cs
Content.Shared/RepulseAttract/RepulseAttractComponent.cs [new file with mode: 0644]
Content.Shared/RepulseAttract/RepulseAttractSystem.cs [new file with mode: 0644]
Content.Shared/Throwing/ThrowingSystem.cs
Content.Shared/Timing/UseDelaySystem.cs
Content.Shared/Weapons/Melee/Components/MeleeThrowOnHitComponent.cs
Content.Shared/Weapons/Melee/Components/UseDelayOnMeleeHitComponent.cs [new file with mode: 0644]
Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs
Content.Shared/Weapons/Melee/UseDelayOnMeleeHitSystem.cs [new file with mode: 0644]
Content.Shared/Wieldable/Components/WieldableComponent.cs
Content.Shared/Wieldable/SharedWieldableSystem.cs
Resources/Locale/en-US/store/spellbook-catalog.ftl
Resources/Prototypes/Catalog/spellbook_catalog.yml
Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/hammers.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml [deleted file]
Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/wielded-inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/wielded-inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/wielded-inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/wielded-inhand-right.png [new file with mode: 0644]

index f4864a532b842d673d9cdc62acaba19d27a036ee..70735dbb40568dd528e7b547f0e633559839e511 100644 (file)
@@ -36,9 +36,9 @@ public sealed class SharedAnomalyCoreSystem : EntitySystem
         var (uid, comp) = ent;
 
         // don't waste charges on non-anchorable non-anomalous static bodies.
-        if (!HasComp<AnomalyComponent>(args.Hit)
-            && !HasComp<AnchorableComponent>(args.Hit)
-            && TryComp<PhysicsComponent>(args.Hit, out var body)
+        if (!HasComp<AnomalyComponent>(args.Target)
+            && !HasComp<AnchorableComponent>(args.Target)
+            && TryComp<PhysicsComponent>(args.Target, out var body)
             && body.BodyType == BodyType.Static)
             return;
 
index 05e12d406e22409c8e48a2616a8f6e0c4749cae9..3f8da9e3c8bec7f7fc1d978aff1953c0be0a3436 100644 (file)
@@ -4,6 +4,7 @@ using Content.Shared.Anomaly.Prototypes;
 using Content.Shared.Database;
 using Content.Shared.Physics;
 using Content.Shared.Popups;
+using Content.Shared.Throwing;
 using Content.Shared.Weapons.Melee.Components;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Map;
@@ -41,19 +42,22 @@ public abstract class SharedAnomalySystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitStartEvent>(OnAnomalyThrowStart);
-        SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitEndEvent>(OnAnomalyThrowEnd);
+        SubscribeLocalEvent<AnomalyComponent, LandEvent>(OnLand);
     }
 
     private void OnAnomalyThrowStart(Entity<AnomalyComponent> ent, ref MeleeThrowOnHitStartEvent args)
     {
-        if (!TryComp<CorePoweredThrowerComponent>(args.Used, out var corePowered) || !TryComp<PhysicsComponent>(ent, out var body))
+        if (!TryComp<CorePoweredThrowerComponent>(args.Weapon, out var corePowered) || !TryComp<PhysicsComponent>(ent, out var body))
             return;
+
+        // anomalies are static by default, so we have set them to dynamic to be throwable
         _physics.SetBodyType(ent, BodyType.Dynamic, body: body);
         ChangeAnomalyStability(ent, Random.NextFloat(corePowered.StabilityPerThrow.X, corePowered.StabilityPerThrow.Y), ent.Comp);
     }
 
-    private void OnAnomalyThrowEnd(Entity<AnomalyComponent> ent, ref MeleeThrowOnHitEndEvent args)
+    private void OnLand(Entity<AnomalyComponent> ent, ref LandEvent args)
     {
+        // revert back to static
         _physics.SetBodyType(ent, BodyType.Static);
     }
 
diff --git a/Content.Shared/RepulseAttract/RepulseAttractComponent.cs b/Content.Shared/RepulseAttract/RepulseAttractComponent.cs
new file mode 100644 (file)
index 0000000..c167d81
--- /dev/null
@@ -0,0 +1,38 @@
+using Content.Shared.Physics;
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.RepulseAttract;
+
+/// <summary>
+///     Used to repulse or attract entities away from the entity this is on
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(RepulseAttractSystem))]
+public sealed partial class RepulseAttractComponent : Component
+{
+    /// <summary>
+    ///     How fast should the Repulsion/Attraction be?
+    ///     A positive value will repulse objects, a negative value will attract
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public float Speed = 5.0f;
+
+    /// <summary>
+    ///     How close do the entities need to be?
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public float Range = 5.0f;
+
+    /// <summary>
+    ///     What kind of entities should this effect apply to?
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public EntityWhitelist? Whitelist;
+
+    /// <summary>
+    ///     What collision layers should be excluded?
+    ///     The default excludes ghost mobs, revenants, the AI camera etc.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public CollisionGroup CollisionMask = CollisionGroup.GhostImpassable;
+}
diff --git a/Content.Shared/RepulseAttract/RepulseAttractSystem.cs b/Content.Shared/RepulseAttract/RepulseAttractSystem.cs
new file mode 100644 (file)
index 0000000..9363d7b
--- /dev/null
@@ -0,0 +1,78 @@
+using Content.Shared.Physics;
+using Content.Shared.Throwing;
+using Content.Shared.Timing;
+using Content.Shared.Weapons.Melee.Events;
+using Content.Shared.Whitelist;
+using Content.Shared.Wieldable;
+using Robust.Shared.Map;
+using Robust.Shared.Physics.Components;
+using System.Numerics;
+using Content.Shared.Weapons.Melee;
+
+namespace Content.Shared.RepulseAttract;
+
+public sealed class RepulseAttractSystem : EntitySystem
+{
+    [Dependency] private readonly EntityLookupSystem _lookup = default!;
+    [Dependency] private readonly ThrowingSystem _throw = default!;
+    [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
+    [Dependency] private readonly SharedTransformSystem _xForm = default!;
+    [Dependency] private readonly UseDelaySystem _delay = default!;
+
+    private EntityQuery<PhysicsComponent> _physicsQuery;
+    private HashSet<EntityUid> _entSet = new();
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        _physicsQuery = GetEntityQuery<PhysicsComponent>();
+
+        SubscribeLocalEvent<RepulseAttractComponent, MeleeHitEvent>(OnMeleeAttempt, before: [typeof(UseDelayOnMeleeHitSystem)], after: [typeof(SharedWieldableSystem)]);
+    }
+    private void OnMeleeAttempt(Entity<RepulseAttractComponent> ent, ref MeleeHitEvent args)
+    {
+        if (_delay.IsDelayed(ent.Owner))
+            return;
+
+        TryRepulseAttract(ent, args.User);
+    }
+
+    public bool TryRepulseAttract(Entity<RepulseAttractComponent> ent, EntityUid user)
+    {
+        var position = _xForm.GetMapCoordinates(ent.Owner);
+        return TryRepulseAttract(position, user, ent.Comp.Speed, ent.Comp.Range, ent.Comp.Whitelist, ent.Comp.CollisionMask);
+    }
+
+    public bool TryRepulseAttract(MapCoordinates position, EntityUid? user, float speed, float range, EntityWhitelist? whitelist = null, CollisionGroup layer = CollisionGroup.SingularityLayer)
+    {
+        _entSet.Clear();
+        var epicenter = position.Position;
+        _lookup.GetEntitiesInRange(position.MapId, epicenter, range, _entSet, flags: LookupFlags.Dynamic | LookupFlags.Sundries);
+
+        foreach (var target in _entSet)
+        {
+            if (!_physicsQuery.TryGetComponent(target, out var physics)
+                || (physics.CollisionLayer & (int)layer) != 0x0) // exclude layers like ghosts
+                continue;
+
+            if (_whitelist.IsWhitelistFail(whitelist, target))
+                continue;
+
+            var targetPos = _xForm.GetWorldPosition(target);
+
+            // vector from epicenter to target entity
+            var direction = targetPos - epicenter;
+
+            if (direction == Vector2.Zero)
+                continue;
+
+            // attract: throw all items directly to to the epicenter
+            // repulse: throw them up to the maximum range
+            var throwDirection = speed < 0 ? -direction : direction.Normalized() * (range - direction.Length());
+
+            _throw.TryThrow(target, throwDirection, Math.Abs(speed), user, recoil: false, compensateFriction: true);
+        }
+
+        return true;
+    }
+}
index 549473278e40126c133b6ea13b4e49fa8ff66372..c37f2356989fc2ad895348f8f68d4b20acc0e478 100644 (file)
@@ -2,6 +2,7 @@ using System.Numerics;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Camera;
 using Content.Shared.CCVar;
+using Content.Shared.Construction.Components;
 using Content.Shared.Database;
 using Content.Shared.Friction;
 using Content.Shared.Gravity;
@@ -57,7 +58,8 @@ public sealed class ThrowingSystem : EntitySystem
         bool recoil = true,
         bool animated = true,
         bool playSound = true,
-        bool doSpin = true)
+        bool doSpin = true,
+        bool unanchor = false)
     {
         var thrownPos = _transform.GetMapCoordinates(uid);
         var mapPos = _transform.ToMapCoordinates(coordinates);
@@ -65,7 +67,7 @@ public sealed class ThrowingSystem : EntitySystem
         if (mapPos.MapId != thrownPos.MapId)
             return;
 
-        TryThrow(uid, mapPos.Position - thrownPos.Position, baseThrowSpeed, user, pushbackRatio, friction, compensateFriction: compensateFriction, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin);
+        TryThrow(uid, mapPos.Position - thrownPos.Position, baseThrowSpeed, user, pushbackRatio, friction, compensateFriction: compensateFriction, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin, unanchor: unanchor);
     }
 
     /// <summary>
@@ -78,6 +80,7 @@ public sealed class ThrowingSystem : EntitySystem
     /// <param name="friction">friction value used for the distance calculation. If set to null this defaults to the standard tile values</param>
     /// <param name="compensateFriction">True will adjust the throw so the item stops at the target coordinates. False means it will land at the target and keep sliding.</param>
     /// <param name="doSpin">Whether spin will be applied to the thrown entity.</param>
+    /// <param name="unanchor">If true and the thrown entity has <see cref="AnchorableComponent"/>, unanchor the thrown entity</param>
     public void TryThrow(EntityUid uid,
         Vector2 direction,
         float baseThrowSpeed = 10.0f,
@@ -88,7 +91,8 @@ public sealed class ThrowingSystem : EntitySystem
         bool recoil = true,
         bool animated = true,
         bool playSound = true,
-        bool doSpin = true)
+        bool doSpin = true,
+        bool unanchor = false)
     {
         var physicsQuery = GetEntityQuery<PhysicsComponent>();
         if (!physicsQuery.TryGetComponent(uid, out var physics))
@@ -105,7 +109,7 @@ public sealed class ThrowingSystem : EntitySystem
             baseThrowSpeed,
             user,
             pushbackRatio,
-            friction, compensateFriction: compensateFriction, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin);
+            friction, compensateFriction: compensateFriction, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin, unanchor: unanchor);
     }
 
     /// <summary>
@@ -118,6 +122,7 @@ public sealed class ThrowingSystem : EntitySystem
     /// <param name="friction">friction value used for the distance calculation. If set to null this defaults to the standard tile values</param>
     /// <param name="compensateFriction">True will adjust the throw so the item stops at the target coordinates. False means it will land at the target and keep sliding.</param>
     /// <param name="doSpin">Whether spin will be applied to the thrown entity.</param>
+    /// <param name="unanchor">If true and the thrown entity has <see cref="AnchorableComponent"/>, unanchor the thrown entity</param>
     public void TryThrow(EntityUid uid,
         Vector2 direction,
         PhysicsComponent physics,
@@ -131,16 +136,17 @@ public sealed class ThrowingSystem : EntitySystem
         bool recoil = true,
         bool animated = true,
         bool playSound = true,
-        bool doSpin = true)
+        bool doSpin = true,
+        bool unanchor = false)
     {
         if (baseThrowSpeed <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero || friction < 0)
             return;
 
+        if (unanchor && HasComp<AnchorableComponent>(uid))
+            _transform.Unanchor(uid);
+
         if ((physics.BodyType & (BodyType.Dynamic | BodyType.KinematicController)) == 0x0)
-        {
-            Log.Warning($"Tried to throw entity {ToPrettyString(uid)} but can't throw {physics.BodyType} bodies!");
             return;
-        }
 
         // Allow throwing if this projectile only acts as a projectile when shot, otherwise disallow
         if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot)
index 9707a4ddbe567fe44b373b1d20aa21f8bf7637be..c6a1818443a2e8637912346b2080ad4079d0e6ea 100644 (file)
@@ -88,8 +88,11 @@ public sealed class UseDelaySystem : EntitySystem
     /// <summary>
     /// Returns true if the entity has a currently active UseDelay with the specified ID.
     /// </summary>
-    public bool IsDelayed(Entity<UseDelayComponent> ent, string id = DefaultId)
+    public bool IsDelayed(Entity<UseDelayComponent?> ent, string id = DefaultId)
     {
+        if (!Resolve(ent, ref ent.Comp, false))
+            return false;
+
         if (!ent.Comp.Delays.TryGetValue(id, out var entry))
             return false;
 
@@ -144,7 +147,7 @@ public sealed class UseDelaySystem : EntitySystem
     /// Otherwise reset it and return true.</param>
     public bool TryResetDelay(Entity<UseDelayComponent> ent, bool checkDelayed = false, string id = DefaultId)
     {
-        if (checkDelayed && IsDelayed(ent, id))
+        if (checkDelayed && IsDelayed((ent.Owner, ent.Comp), id))
             return false;
 
         if (!ent.Comp.Delays.TryGetValue(id, out var entry))
index 82ffc5e51fc662a6dc8bf4f583cbad691c509e57..84e75129b141001c9fb7e86e70d8540d93421c49 100644 (file)
@@ -9,104 +9,49 @@ namespace Content.Shared.Weapons.Melee.Components;
 /// This is used for a melee weapon that throws whatever gets hit by it in a line
 /// until it hits a wall or a time limit is exhausted.
 /// </summary>
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
 [Access(typeof(MeleeThrowOnHitSystem))]
-[AutoGenerateComponentState]
 public sealed partial class MeleeThrowOnHitComponent : Component
 {
     /// <summary>
     /// The speed at which hit entities should be thrown.
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    [AutoNetworkedField]
+    [DataField, AutoNetworkedField]
     public float Speed = 10f;
 
     /// <summary>
-    /// How long hit entities remain thrown, max.
+    /// The maximum distance the hit entity should be thrown.
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    [AutoNetworkedField]
-    public float Lifetime = 3f;
-
-    /// <summary>
-    /// How long we wait to start accepting collision.
-    /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public float MinLifetime = 0.05f;
+    [DataField, AutoNetworkedField]
+    public float Distance = 20f;
 
     /// <summary>
     /// Whether or not anchorable entities should be unanchored when hit.
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    [AutoNetworkedField]
+    [DataField, AutoNetworkedField]
     public bool UnanchorOnHit;
 
     /// <summary>
-    /// Whether or not the throwing behavior occurs by default.
-    /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    [AutoNetworkedField]
-    public bool Enabled = true;
-}
-
-/// <summary>
-/// Component used to track entities that have been yeeted by <see cref="MeleeThrowOnHitComponent"/>
-/// </summary>
-[RegisterComponent, NetworkedComponent]
-[AutoGenerateComponentState]
-[Access(typeof(MeleeThrowOnHitSystem))]
-public sealed partial class MeleeThrownComponent : Component
-{
-    /// <summary>
-    /// The velocity of the throw
-    /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    [AutoNetworkedField]
-    public Vector2 Velocity;
-
-    /// <summary>
-    /// How long the throw will last.
+    /// How long should this stun the target, if applicable?
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    [AutoNetworkedField]
-    public float Lifetime;
+    [DataField, AutoNetworkedField]
+    public TimeSpan? StunTime;
 
     /// <summary>
-    /// How long we wait to start accepting collision.
+    /// Should this also work on a throw-hit?
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
-    public float MinLifetime;
-
-    /// <summary>
-    /// At what point in time will the throw be complete?
-    /// </summary>
-    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
-    [AutoNetworkedField]
-    public TimeSpan ThrownEndTime;
-
-    /// <summary>
-    /// At what point in time will the <see cref="MinLifetime"/> be exhausted
-    /// </summary>
-    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
-    [AutoNetworkedField]
-    public TimeSpan MinLifetimeTime;
-
-    /// <summary>
-    /// the status to which the entity will return when the thrown ends
-    /// </summary>
-    [DataField]
-    public BodyStatus PreviousStatus;
+    [DataField, AutoNetworkedField]
+    public bool ActivateOnThrown;
 }
 
 /// <summary>
-/// Event raised before an entity is thrown by <see cref="MeleeThrowOnHitComponent"/> to see if a throw is allowed.
-/// If not handled, the enabled field on the component will be used instead.
+/// Raised a weapon entity with <see cref="MeleeThrowOnHitComponent"/> to see if a throw is allowed.
 /// </summary>
 [ByRefEvent]
-public record struct AttemptMeleeThrowOnHitEvent(EntityUid Hit, bool Cancelled = false, bool Handled = false);
-
-[ByRefEvent]
-public record struct MeleeThrowOnHitStartEvent(EntityUid User, EntityUid Used);
+public record struct AttemptMeleeThrowOnHitEvent(EntityUid Target, EntityUid? User, bool Cancelled = false, bool Handled = false);
 
+/// <summary>
+/// Raised a target entity before it is thrown by <see cref="MeleeThrowOnHitComponent"/>.
+/// </summary>
 [ByRefEvent]
-public record struct MeleeThrowOnHitEndEvent();
+public record struct MeleeThrowOnHitStartEvent(EntityUid Weapon, EntityUid? User);
diff --git a/Content.Shared/Weapons/Melee/Components/UseDelayOnMeleeHitComponent.cs b/Content.Shared/Weapons/Melee/Components/UseDelayOnMeleeHitComponent.cs
new file mode 100644 (file)
index 0000000..644971b
--- /dev/null
@@ -0,0 +1,12 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Weapons.Melee.Components;
+
+/// <summary>
+///     Activates UseDelay when a Melee Weapon is used to hit something.
+/// </summary>
+[RegisterComponent, NetworkedComponent, Access(typeof(UseDelayOnMeleeHitSystem))]
+public sealed partial class UseDelayOnMeleeHitComponent : Component
+{
+
+}
index 7886356233034f7448e9a5c485aa2a9a37d729ea..bc95be926abdfd8c20d545a8348290d619881fe4 100644 (file)
@@ -1,12 +1,13 @@
-using System.Numerics;
 using Content.Shared.Construction.Components;
+using Content.Shared.Stunnable;
+using Content.Shared.Throwing;
+using Content.Shared.Timing;
 using Content.Shared.Weapons.Melee.Components;
 using Content.Shared.Weapons.Melee.Events;
 using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
-using Robust.Shared.Physics.Events;
 using Robust.Shared.Physics.Systems;
-using Robust.Shared.Timing;
+using System.Numerics;
 
 namespace Content.Shared.Weapons.Melee;
 
@@ -15,113 +16,67 @@ namespace Content.Shared.Weapons.Melee;
 /// </summary>
 public sealed class MeleeThrowOnHitSystem : EntitySystem
 {
-    [Dependency] private readonly IGameTiming _timing = default!;
     [Dependency] private readonly SharedTransformSystem _transform = default!;
     [Dependency] private readonly SharedPhysicsSystem _physics = default!;
-
+    [Dependency] private readonly UseDelaySystem _delay = default!;
+    [Dependency] private readonly SharedStunSystem _stun = default!;
+    [Dependency] private readonly ThrowingSystem _throwing = default!;
     /// <inheritdoc/>
     public override void Initialize()
     {
         SubscribeLocalEvent<MeleeThrowOnHitComponent, MeleeHitEvent>(OnMeleeHit);
-        SubscribeLocalEvent<MeleeThrownComponent, ComponentStartup>(OnThrownStartup);
-        SubscribeLocalEvent<MeleeThrownComponent, ComponentShutdown>(OnThrownShutdown);
-        SubscribeLocalEvent<MeleeThrownComponent, StartCollideEvent>(OnStartCollide);
+        SubscribeLocalEvent<MeleeThrowOnHitComponent, ThrowDoHitEvent>(OnThrowHit);
     }
 
-    private void OnMeleeHit(Entity<MeleeThrowOnHitComponent> ent, ref MeleeHitEvent args)
+    private void OnMeleeHit(Entity<MeleeThrowOnHitComponent> weapon, ref MeleeHitEvent args)
     {
-        var (_, comp) = ent;
+        // TODO: MeleeHitEvent is weird. Why is this even raised if we don't hit something?
         if (!args.IsHit)
             return;
 
-        var mapPos = _transform.GetMapCoordinates(args.User).Position;
-        foreach (var hit in args.HitEntities)
-        {
-            var hitPos = _transform.GetMapCoordinates(hit).Position;
-            var angle = args.Direction ?? hitPos - mapPos;
-            if (angle == Vector2.Zero)
-                continue;
-
-            if (!CanThrowOnHit(ent, hit))
-                continue;
-
-            if (comp.UnanchorOnHit && HasComp<AnchorableComponent>(hit))
-            {
-                _transform.Unanchor(hit, Transform(hit));
-            }
-
-            RemComp<MeleeThrownComponent>(hit);
-            var ev = new MeleeThrowOnHitStartEvent(args.User, ent);
-            RaiseLocalEvent(hit, ref ev);
-            var thrownComp = new MeleeThrownComponent
-            {
-                Velocity = angle.Normalized() * comp.Speed,
-                Lifetime = comp.Lifetime,
-                MinLifetime = comp.MinLifetime
-            };
-            AddComp(hit, thrownComp);
-        }
-    }
-
-    private void OnThrownStartup(Entity<MeleeThrownComponent> ent, ref ComponentStartup args)
-    {
-        var (_, comp) = ent;
-
-        if (!TryComp<PhysicsComponent>(ent, out var body) ||
-            (body.BodyType & (BodyType.Dynamic | BodyType.KinematicController)) == 0x0)
+        if (_delay.IsDelayed(weapon.Owner))
             return;
 
-        comp.PreviousStatus = body.BodyStatus;
-        comp.ThrownEndTime = _timing.CurTime + TimeSpan.FromSeconds(comp.Lifetime);
-        comp.MinLifetimeTime = _timing.CurTime + TimeSpan.FromSeconds(comp.MinLifetime);
-        _physics.SetBodyStatus(ent, body, BodyStatus.InAir);
-        _physics.SetLinearVelocity(ent, Vector2.Zero, body: body);
-        _physics.ApplyLinearImpulse(ent, comp.Velocity * body.Mass, body: body);
-        Dirty(ent, ent.Comp);
-    }
+        if (args.HitEntities.Count == 0)
+            return;
 
-    private void OnThrownShutdown(Entity<MeleeThrownComponent> ent, ref ComponentShutdown args)
-    {
-        if (TryComp<PhysicsComponent>(ent, out var body))
-            _physics.SetBodyStatus(ent, body, ent.Comp.PreviousStatus);
-        var ev = new MeleeThrowOnHitEndEvent();
-        RaiseLocalEvent(ent, ref ev);
+        var userPos = _transform.GetWorldPosition(args.User);
+        foreach (var target in args.HitEntities)
+        {
+            var targetPos = _transform.GetMapCoordinates(target).Position;
+            var direction = args.Direction ?? targetPos - userPos;
+            ThrowOnHitHelper(weapon, args.User, target, direction);
+        }
     }
 
-    private void OnStartCollide(Entity<MeleeThrownComponent> ent, ref StartCollideEvent args)
+    private void OnThrowHit(Entity<MeleeThrowOnHitComponent> weapon, ref ThrowDoHitEvent args)
     {
-        var (_, comp) = ent;
-        if (!args.OtherFixture.Hard || !args.OtherBody.CanCollide || !args.OurFixture.Hard || !args.OurBody.CanCollide)
+        if (!weapon.Comp.ActivateOnThrown)
             return;
 
-        if (_timing.CurTime < comp.MinLifetimeTime)
+        if (!TryComp<PhysicsComponent>(args.Thrown, out var weaponPhysics))
             return;
 
-        RemCompDeferred(ent, ent.Comp);
+        ThrowOnHitHelper(weapon, args.Component.Thrower, args.Target, weaponPhysics.LinearVelocity);
     }
 
-    public bool CanThrowOnHit(Entity<MeleeThrowOnHitComponent> ent, EntityUid target)
+    private void ThrowOnHitHelper(Entity<MeleeThrowOnHitComponent> ent, EntityUid? user, EntityUid target, Vector2 direction)
     {
-        var (uid, comp) = ent;
+        var attemptEvent = new AttemptMeleeThrowOnHitEvent(target, user);
+        RaiseLocalEvent(ent.Owner, ref attemptEvent);
 
-        var ev = new AttemptMeleeThrowOnHitEvent(target);
-        RaiseLocalEvent(uid, ref ev);
+        if (attemptEvent.Cancelled)
+            return;
 
-        if (ev.Handled)
-            return !ev.Cancelled;
+        var startEvent = new MeleeThrowOnHitStartEvent(ent.Owner, user);
+        RaiseLocalEvent(target, ref startEvent);
 
-        return comp.Enabled;
-    }
+        if (ent.Comp.StunTime != null)
+            _stun.TryParalyze(target, ent.Comp.StunTime.Value, false);
 
-    public override void Update(float frameTime)
-    {
-        base.Update(frameTime);
+        if (direction == Vector2.Zero)
+            return;
 
-        var query = EntityQueryEnumerator<MeleeThrownComponent>();
-        while (query.MoveNext(out var uid, out var comp))
-        {
-            if (_timing.CurTime > comp.ThrownEndTime)
-                RemCompDeferred(uid, comp);
-        }
+        _throwing.TryThrow(target, direction.Normalized() * ent.Comp.Distance, ent.Comp.Speed, user, unanchor: ent.Comp.UnanchorOnHit);
     }
 }
diff --git a/Content.Shared/Weapons/Melee/UseDelayOnMeleeHitSystem.cs b/Content.Shared/Weapons/Melee/UseDelayOnMeleeHitSystem.cs
new file mode 100644 (file)
index 0000000..0400bf5
--- /dev/null
@@ -0,0 +1,39 @@
+using Content.Shared.Throwing;
+using Content.Shared.Timing;
+using Content.Shared.Weapons.Melee.Components;
+using Content.Shared.Weapons.Melee.Events;
+
+namespace Content.Shared.Weapons.Melee;
+
+/// <inheritdoc cref="UseDelayOnMeleeHitComponent"/>
+public sealed class UseDelayOnMeleeHitSystem : EntitySystem
+{
+    [Dependency] private readonly UseDelaySystem _delay = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<UseDelayOnMeleeHitComponent, MeleeHitEvent>(OnMeleeHit);
+        SubscribeLocalEvent<UseDelayOnMeleeHitComponent, ThrowDoHitEvent>(OnThrowHitEvent);
+    }
+
+    private void OnThrowHitEvent(Entity<UseDelayOnMeleeHitComponent> ent, ref ThrowDoHitEvent args)
+    {
+        TryResetDelay(ent);
+    }
+
+    private void OnMeleeHit(Entity<UseDelayOnMeleeHitComponent> ent, ref MeleeHitEvent args)
+    {
+        TryResetDelay(ent);
+    }
+
+    private void TryResetDelay(Entity<UseDelayOnMeleeHitComponent> ent)
+    {
+        var uid = ent.Owner;
+
+        if (!TryComp<UseDelayComponent>(uid, out var useDelay))
+            return;
+
+        _delay.TryResetDelay((uid, useDelay), checkDelayed: true);
+    }
+}
index c146940b7a2c5841f0356e20cbd0c46d0fdc9e90..c7be70e7c9607ffafb5fa8709b213b4286bf2736 100644 (file)
@@ -28,11 +28,17 @@ public sealed partial class WieldableComponent : Component
 
     /// <summary>
     ///     Whether using the item inhand while wielding causes the item to unwield.
-    ///     Unwielding can conflict with other inhand actions. 
+    ///     Unwielding can conflict with other inhand actions.
     /// </summary>
     [DataField]
     public bool UnwieldOnUse = true;
 
+    /// <summary>
+    ///     Should use delay trigger after the wield/unwield?
+    /// </summary>
+    [DataField]
+    public bool UseDelayOnWield = true;
+
     [DataField("wieldedInhandPrefix")]
     public string? WieldedInhandPrefix = "wielded";
 
index 110c941dac73a030ff00f8040ef5a15ea3ecb3b2..b3d2e1bdf153822c45c13d7dbac09bc1f4f44f9a 100644 (file)
@@ -191,6 +191,9 @@ public abstract class SharedWieldableSystem : EntitySystem
             args.Handled = TryWield(uid, component, args.User);
         else if (component.UnwieldOnUse)
             args.Handled = TryUnwield(uid, component, args.User);
+
+        if (HasComp<UseDelayComponent>(uid) && !component.UseDelayOnWield)
+            args.ApplyDelay = false;
     }
 
     public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user, bool quiet = false)
@@ -235,9 +238,11 @@ public abstract class SharedWieldableSystem : EntitySystem
         if (!CanWield(used, component, user))
             return false;
 
-        if (TryComp(used, out UseDelayComponent? useDelay)
-            && !_delay.TryResetDelay((used, useDelay), true))
-            return false;
+        if (TryComp(used, out UseDelayComponent? useDelay) && component.UseDelayOnWield)
+        {
+            if (!_delay.TryResetDelay((used, useDelay), true))
+                return false;
+        }
 
         var attemptEv = new WieldAttemptEvent(user);
         RaiseLocalEvent(used, ref attemptEv);
index 0c650c0d4d22be116b5d7645d5581842d7029e49..73d8118fc53fe4c459a319a46b0bc3a7266223db 100644 (file)
@@ -52,6 +52,12 @@ spellbook-wand-polymorph-carp-description = For when you need a carp filet quick
 spellbook-wand-locker-name = Wand of the Locker
 spellbook-wand-locker-description = Shoot cursed lockers at your enemies and lock em away!
 
+spellbook-hammer-mjollnir-name = Mjollnir
+spellbook-hammer-mjollnir-description = Wield the power of THUNDER in your hands. Send foes flying with a mighty swing or by throwing it right at em!
+
+spellbook-hammer-singularity-name = Singularity Hammer
+spellbook-hammer-singularity-description = Ever wonder what it'd be like to be the singularity? Swing this hammer to draw in your surroundings, even works if you miss!
+
 spellbook-staff-animation-name = Staff of Animation
 spellbook-staff-animation-description = Bring inanimate objects to life!
 
index 0486330940df0b0d4b69311e9dab88354e801141..768475a03b4ab31143b3ea32e981e2078557062a 100644 (file)
   - !type:ListingLimitedStockCondition
     stock: 1
 
+- type: listing
+  id: SpellbookHammerMjollnir
+  name: spellbook-hammer-mjollnir-name
+  description: spellbook-hammer-mjollnir-description
+  productEntity: Mjollnir
+  cost:
+    WizCoin: 2
+  categories:
+  - SpellbookEquipment
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+
+- type: listing
+  id: SpellbookHammerSingularity
+  name: spellbook-hammer-singularity-name
+  description: spellbook-hammer-singularity-description
+  productEntity: SingularityHammer
+  cost:
+    WizCoin: 2
+  categories:
+  - SpellbookEquipment
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+
 - type: listing
   id: SpellbookStaffAnimation
   name: spellbook-staff-animation-name
index efbd7c2db4f199c3ae6d2aa4e6119e03df4b05d7..0872f0fc3eb0222dc8214517f116804bbb49ae2a 100644 (file)
   - type: CorePoweredThrower
   - type: MeleeThrowOnHit
     unanchorOnHit: true
-    enabled: false
   - type: ItemSlots
     slots:
       core_slot:
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/hammers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/hammers.yml
new file mode 100644 (file)
index 0000000..d2a0ecd
--- /dev/null
@@ -0,0 +1,109 @@
+- type: entity
+  name: sledgehammer
+  parent: BaseItem
+  id: Sledgehammer
+  description: The perfect tool for wanton carnage.
+  components:
+  - type: Sprite
+    sprite: Objects/Weapons/Melee/sledgehammer.rsi
+    state: icon
+  - type: MeleeWeapon
+    wideAnimationRotation: -135
+    damage:
+      types:
+        Blunt: 10
+        Structural: 10
+    soundHit:
+      collection: MetalThud
+  - type: Wieldable
+  - type: IncreaseDamageOnWield
+    damage:
+      types:
+        Blunt: 10
+        Structural: 10
+  - type: Item
+    size: Large
+
+- type: entity
+  id: Mjollnir
+  parent: [ BaseItem, BaseMagicalContraband ]
+  name: Mjollnir
+  description: A weapon worthy of a god, able to strike with the force of a lightning bolt. It crackles with barely contained energy.
+  components:
+  - type: Wieldable
+    useDelayOnWield: false
+  - type: MeleeRequiresWield
+  - type: LandAtCursor
+  - type: Sprite
+    sprite: Objects/Weapons/Melee/mjollnir.rsi
+    layers:
+    - state: icon
+  - type: UseDelay
+    delay: 10
+  - type: UseDelayOnMeleeHit
+  - type: MeleeThrowOnHit
+    stunTime: 3
+    activateOnThrown: true
+  - type: MeleeWeapon
+    wideAnimationRotation: -135
+    damage:
+      types:
+        Blunt: 5
+        Structural: 5
+    soundHit:
+      path: /Audio/Effects/tesla_consume.ogg
+      params:
+        variation: 0.10
+  - type: IncreaseDamageOnWield
+    damage:
+      types:
+        Blunt: 20
+        Structural: 25
+  - type: DamageOtherOnHit
+    damage:
+      types:
+        Blunt: 15
+        Structural: 15
+  - type: Item
+    size: Ginormous
+
+- type: entity
+  id: SingularityHammer
+  parent: [ BaseItem, BaseMagicalContraband ]
+  name: Singularity Hammer
+  description: The pinnacle of close combat technology, the hammer harnesses the power of a miniaturized singularity to deal crushing blows.
+  components:
+  - type: Wieldable
+    useDelayOnWield: false
+  - type: MeleeRequiresWield
+  - type: Sprite
+    sprite: Objects/Weapons/Melee/singularityhammer.rsi
+    layers:
+    - state: icon
+  - type: RepulseAttract
+    speed: -15 #Anything above this pushes things too far away from the Wizard
+    range: 5
+    whitelist:
+      components:
+      - MobMover
+      - Item
+  - type: UseDelay
+    delay: 10
+  - type: UseDelayOnMeleeHit
+  - type: MeleeWeapon
+    wideAnimationRotation: -135
+    damage:
+      types:
+        Blunt: 5
+        Structural: 5
+    soundHit:
+      path: /Audio/Effects/radpulse5.ogg
+      params:
+        variation: 0.10
+  - type: IncreaseDamageOnWield
+    damage:
+      types:
+        Blunt: 15
+        Structural: 15
+  - type: Item
+    size: Ginormous
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml
deleted file mode 100644 (file)
index 0c75015..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-- type: entity
-  name: sledgehammer
-  parent: BaseItem
-  id: Sledgehammer
-  description: The perfect tool for wanton carnage.
-  components:
-  - type: Sprite
-    sprite: Objects/Weapons/Melee/sledgehammer.rsi
-    state: icon
-  - type: MeleeWeapon
-    wideAnimationRotation: -135
-    damage:
-      types:
-        Blunt: 10
-        Structural: 10
-    soundHit:
-      collection: MetalThud
-  - type: Wieldable
-  - type: IncreaseDamageOnWield
-    damage:
-      types:
-        Blunt: 10
-        Structural: 10
-  - type: Item
-    size: Large
diff --git a/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/icon.png b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/icon.png
new file mode 100644 (file)
index 0000000..420a46f
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/inhand-left.png
new file mode 100644 (file)
index 0000000..2b5216c
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/inhand-right.png
new file mode 100644 (file)
index 0000000..3c41284
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/meta.json b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/meta.json
new file mode 100644 (file)
index 0000000..d304313
--- /dev/null
@@ -0,0 +1,42 @@
+{
+  "version": 1,
+  "license": "CC-BY-NC-SA-3.0",
+  "copyright": "Taken from and modified by ProtivogaSpriter on TGStation at commit https://github.com/tgstation/tgstation/commit/2614518661bfac1dcc96f07cfcc70b4abacd27bf",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "inhand-left",
+      "directions": 4
+    },
+    {
+      "name": "wielded-inhand-left",
+      "directions": 4
+    },
+    {
+      "name": "inhand-right",
+      "directions": 4
+    },
+    {
+      "name": "wielded-inhand-right",
+      "directions": 4
+    },
+    {
+      "name": "icon",
+      "delays": [
+        [
+          2,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    }
+  ]
+}
diff --git a/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/wielded-inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/wielded-inhand-left.png
new file mode 100644 (file)
index 0000000..499a827
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/wielded-inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/wielded-inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/wielded-inhand-right.png
new file mode 100644 (file)
index 0000000..5ef1c92
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/wielded-inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/icon.png b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/icon.png
new file mode 100644 (file)
index 0000000..1c3a04b
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/inhand-left.png
new file mode 100644 (file)
index 0000000..f385980
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/inhand-right.png
new file mode 100644 (file)
index 0000000..51774ac
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/meta.json b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/meta.json
new file mode 100644 (file)
index 0000000..3959103
--- /dev/null
@@ -0,0 +1,244 @@
+{
+  "version": 1,
+  "license": "CC-BY-NC-SA-3.0",
+  "copyright": "Taken from and modified by LemonInTheDark on TGStation at commit https://github.com/tgstation/tgstation/commit/a64baadebe20b10c69c1747c42dc51c7377c9c97",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "inhand-left",
+      "directions": 4,
+      "delays": [
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ]
+      ]
+    },
+    {
+      "name": "wielded-inhand-left",
+      "directions": 4,
+      "delays": [
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ]
+      ]
+    },
+    {
+      "name": "inhand-right",
+      "directions": 4,
+      "delays": [
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ]
+      ]
+    },
+    {
+      "name": "wielded-inhand-right",
+      "directions": 4,
+      "delays": [
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ],
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ]
+      ]
+    },
+    {
+      "name": "icon",
+      "delays": [
+        [
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05,
+          0.05
+        ]
+      ]
+    }
+  ]
+}
diff --git a/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/wielded-inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/wielded-inhand-left.png
new file mode 100644 (file)
index 0000000..f798047
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/wielded-inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/wielded-inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/wielded-inhand-right.png
new file mode 100644 (file)
index 0000000..cec47b0
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Melee/singularityhammer.rsi/wielded-inhand-right.png differ