Mjollnir and Singularity Hammer for Wizard (#34446)
@@ -36,9 +36,9 @@ public sealed class SharedAnomalyCoreSystem : EntitySystem
|
|||||||
var (uid, comp) = ent;
|
var (uid, comp) = ent;
|
||||||
|
|
||||||
// don't waste charges on non-anchorable non-anomalous static bodies.
|
// don't waste charges on non-anchorable non-anomalous static bodies.
|
||||||
if (!HasComp<AnomalyComponent>(args.Hit)
|
if (!HasComp<AnomalyComponent>(args.Target)
|
||||||
&& !HasComp<AnchorableComponent>(args.Hit)
|
&& !HasComp<AnchorableComponent>(args.Target)
|
||||||
&& TryComp<PhysicsComponent>(args.Hit, out var body)
|
&& TryComp<PhysicsComponent>(args.Target, out var body)
|
||||||
&& body.BodyType == BodyType.Static)
|
&& body.BodyType == BodyType.Static)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Shared.Anomaly.Prototypes;
|
|||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Throwing;
|
||||||
using Content.Shared.Weapons.Melee.Components;
|
using Content.Shared.Weapons.Melee.Components;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
@@ -41,19 +42,22 @@ public abstract class SharedAnomalySystem : EntitySystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitStartEvent>(OnAnomalyThrowStart);
|
SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitStartEvent>(OnAnomalyThrowStart);
|
||||||
SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitEndEvent>(OnAnomalyThrowEnd);
|
SubscribeLocalEvent<AnomalyComponent, LandEvent>(OnLand);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAnomalyThrowStart(Entity<AnomalyComponent> ent, ref MeleeThrowOnHitStartEvent args)
|
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;
|
return;
|
||||||
|
|
||||||
|
// anomalies are static by default, so we have set them to dynamic to be throwable
|
||||||
_physics.SetBodyType(ent, BodyType.Dynamic, body: body);
|
_physics.SetBodyType(ent, BodyType.Dynamic, body: body);
|
||||||
ChangeAnomalyStability(ent, Random.NextFloat(corePowered.StabilityPerThrow.X, corePowered.StabilityPerThrow.Y), ent.Comp);
|
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);
|
_physics.SetBodyType(ent, BodyType.Static);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
38
Content.Shared/RepulseAttract/RepulseAttractComponent.cs
Normal file
@@ -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;
|
||||||
|
}
|
||||||
78
Content.Shared/RepulseAttract/RepulseAttractSystem.cs
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ using System.Numerics;
|
|||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.Construction.Components;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Friction;
|
using Content.Shared.Friction;
|
||||||
using Content.Shared.Gravity;
|
using Content.Shared.Gravity;
|
||||||
@@ -57,7 +58,8 @@ public sealed class ThrowingSystem : EntitySystem
|
|||||||
bool recoil = true,
|
bool recoil = true,
|
||||||
bool animated = true,
|
bool animated = true,
|
||||||
bool playSound = true,
|
bool playSound = true,
|
||||||
bool doSpin = true)
|
bool doSpin = true,
|
||||||
|
bool unanchor = false)
|
||||||
{
|
{
|
||||||
var thrownPos = _transform.GetMapCoordinates(uid);
|
var thrownPos = _transform.GetMapCoordinates(uid);
|
||||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||||
@@ -65,7 +67,7 @@ public sealed class ThrowingSystem : EntitySystem
|
|||||||
if (mapPos.MapId != thrownPos.MapId)
|
if (mapPos.MapId != thrownPos.MapId)
|
||||||
return;
|
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>
|
/// <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="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="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="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,
|
public void TryThrow(EntityUid uid,
|
||||||
Vector2 direction,
|
Vector2 direction,
|
||||||
float baseThrowSpeed = 10.0f,
|
float baseThrowSpeed = 10.0f,
|
||||||
@@ -88,7 +91,8 @@ public sealed class ThrowingSystem : EntitySystem
|
|||||||
bool recoil = true,
|
bool recoil = true,
|
||||||
bool animated = true,
|
bool animated = true,
|
||||||
bool playSound = true,
|
bool playSound = true,
|
||||||
bool doSpin = true)
|
bool doSpin = true,
|
||||||
|
bool unanchor = false)
|
||||||
{
|
{
|
||||||
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||||
if (!physicsQuery.TryGetComponent(uid, out var physics))
|
if (!physicsQuery.TryGetComponent(uid, out var physics))
|
||||||
@@ -105,7 +109,7 @@ public sealed class ThrowingSystem : EntitySystem
|
|||||||
baseThrowSpeed,
|
baseThrowSpeed,
|
||||||
user,
|
user,
|
||||||
pushbackRatio,
|
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>
|
/// <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="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="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="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,
|
public void TryThrow(EntityUid uid,
|
||||||
Vector2 direction,
|
Vector2 direction,
|
||||||
PhysicsComponent physics,
|
PhysicsComponent physics,
|
||||||
@@ -131,16 +136,17 @@ public sealed class ThrowingSystem : EntitySystem
|
|||||||
bool recoil = true,
|
bool recoil = true,
|
||||||
bool animated = true,
|
bool animated = true,
|
||||||
bool playSound = 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)
|
if (baseThrowSpeed <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero || friction < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (unanchor && HasComp<AnchorableComponent>(uid))
|
||||||
|
_transform.Unanchor(uid);
|
||||||
|
|
||||||
if ((physics.BodyType & (BodyType.Dynamic | BodyType.KinematicController)) == 0x0)
|
if ((physics.BodyType & (BodyType.Dynamic | BodyType.KinematicController)) == 0x0)
|
||||||
{
|
|
||||||
Log.Warning($"Tried to throw entity {ToPrettyString(uid)} but can't throw {physics.BodyType} bodies!");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Allow throwing if this projectile only acts as a projectile when shot, otherwise disallow
|
// Allow throwing if this projectile only acts as a projectile when shot, otherwise disallow
|
||||||
if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot)
|
if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot)
|
||||||
|
|||||||
@@ -88,8 +88,11 @@ public sealed class UseDelaySystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the entity has a currently active UseDelay with the specified ID.
|
/// Returns true if the entity has a currently active UseDelay with the specified ID.
|
||||||
/// </summary>
|
/// </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))
|
if (!ent.Comp.Delays.TryGetValue(id, out var entry))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -144,7 +147,7 @@ public sealed class UseDelaySystem : EntitySystem
|
|||||||
/// Otherwise reset it and return true.</param>
|
/// Otherwise reset it and return true.</param>
|
||||||
public bool TryResetDelay(Entity<UseDelayComponent> ent, bool checkDelayed = false, string id = DefaultId)
|
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;
|
return false;
|
||||||
|
|
||||||
if (!ent.Comp.Delays.TryGetValue(id, out var entry))
|
if (!ent.Comp.Delays.TryGetValue(id, out var entry))
|
||||||
|
|||||||
@@ -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
|
/// 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.
|
/// until it hits a wall or a time limit is exhausted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
[Access(typeof(MeleeThrowOnHitSystem))]
|
[Access(typeof(MeleeThrowOnHitSystem))]
|
||||||
[AutoGenerateComponentState]
|
|
||||||
public sealed partial class MeleeThrowOnHitComponent : Component
|
public sealed partial class MeleeThrowOnHitComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The speed at which hit entities should be thrown.
|
/// The speed at which hit entities should be thrown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
[AutoNetworkedField]
|
|
||||||
public float Speed = 10f;
|
public float Speed = 10f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How long hit entities remain thrown, max.
|
/// The maximum distance the hit entity should be thrown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
[AutoNetworkedField]
|
public float Distance = 20f;
|
||||||
public float Lifetime = 3f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How long we wait to start accepting collision.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float MinLifetime = 0.05f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not anchorable entities should be unanchored when hit.
|
/// Whether or not anchorable entities should be unanchored when hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
[AutoNetworkedField]
|
|
||||||
public bool UnanchorOnHit;
|
public bool UnanchorOnHit;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not the throwing behavior occurs by default.
|
/// How long should this stun the target, if applicable?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
[AutoNetworkedField]
|
public TimeSpan? StunTime;
|
||||||
public bool Enabled = true;
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should this also work on a throw-hit?
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool ActivateOnThrown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component used to track entities that have been yeeted by <see cref="MeleeThrowOnHitComponent"/>
|
/// Raised a weapon entity with <see cref="MeleeThrowOnHitComponent"/> to see if a throw is allowed.
|
||||||
/// </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.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[AutoNetworkedField]
|
|
||||||
public float Lifetime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How long we wait to start accepting collision.
|
|
||||||
/// </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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public record struct AttemptMeleeThrowOnHitEvent(EntityUid Hit, bool Cancelled = false, bool Handled = false);
|
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]
|
[ByRefEvent]
|
||||||
public record struct MeleeThrowOnHitStartEvent(EntityUid User, EntityUid Used);
|
public record struct MeleeThrowOnHitStartEvent(EntityUid Weapon, EntityUid? User);
|
||||||
|
|
||||||
[ByRefEvent]
|
|
||||||
public record struct MeleeThrowOnHitEndEvent();
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Content.Shared.Construction.Components;
|
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.Components;
|
||||||
using Content.Shared.Weapons.Melee.Events;
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Physics.Events;
|
|
||||||
using Robust.Shared.Physics.Systems;
|
using Robust.Shared.Physics.Systems;
|
||||||
using Robust.Shared.Timing;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Content.Shared.Weapons.Melee;
|
namespace Content.Shared.Weapons.Melee;
|
||||||
|
|
||||||
@@ -15,113 +16,67 @@ namespace Content.Shared.Weapons.Melee;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class MeleeThrowOnHitSystem : EntitySystem
|
public sealed class MeleeThrowOnHitSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly SharedPhysicsSystem _physics = 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/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<MeleeThrowOnHitComponent, MeleeHitEvent>(OnMeleeHit);
|
SubscribeLocalEvent<MeleeThrowOnHitComponent, MeleeHitEvent>(OnMeleeHit);
|
||||||
SubscribeLocalEvent<MeleeThrownComponent, ComponentStartup>(OnThrownStartup);
|
SubscribeLocalEvent<MeleeThrowOnHitComponent, ThrowDoHitEvent>(OnThrowHit);
|
||||||
SubscribeLocalEvent<MeleeThrownComponent, ComponentShutdown>(OnThrownShutdown);
|
|
||||||
SubscribeLocalEvent<MeleeThrownComponent, StartCollideEvent>(OnStartCollide);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (!args.IsHit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var mapPos = _transform.GetMapCoordinates(args.User).Position;
|
if (_delay.IsDelayed(weapon.Owner))
|
||||||
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)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
comp.PreviousStatus = body.BodyStatus;
|
if (args.HitEntities.Count == 0)
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStartCollide(Entity<MeleeThrownComponent> ent, ref StartCollideEvent args)
|
|
||||||
{
|
|
||||||
var (_, comp) = ent;
|
|
||||||
if (!args.OtherFixture.Hard || !args.OtherBody.CanCollide || !args.OurFixture.Hard || !args.OurBody.CanCollide)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_timing.CurTime < comp.MinLifetimeTime)
|
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 OnThrowHit(Entity<MeleeThrowOnHitComponent> weapon, ref ThrowDoHitEvent args)
|
||||||
|
{
|
||||||
|
if (!weapon.Comp.ActivateOnThrown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RemCompDeferred(ent, ent.Comp);
|
if (!TryComp<PhysicsComponent>(args.Thrown, out var weaponPhysics))
|
||||||
|
return;
|
||||||
|
|
||||||
|
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);
|
if (attemptEvent.Cancelled)
|
||||||
RaiseLocalEvent(uid, ref ev);
|
return;
|
||||||
|
|
||||||
if (ev.Handled)
|
var startEvent = new MeleeThrowOnHitStartEvent(ent.Owner, user);
|
||||||
return !ev.Cancelled;
|
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)
|
if (direction == Vector2.Zero)
|
||||||
{
|
return;
|
||||||
base.Update(frameTime);
|
|
||||||
|
|
||||||
var query = EntityQueryEnumerator<MeleeThrownComponent>();
|
_throwing.TryThrow(target, direction.Normalized() * ent.Comp.Distance, ent.Comp.Speed, user, unanchor: ent.Comp.UnanchorOnHit);
|
||||||
while (query.MoveNext(out var uid, out var comp))
|
|
||||||
{
|
|
||||||
if (_timing.CurTime > comp.ThrownEndTime)
|
|
||||||
RemCompDeferred(uid, comp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
Content.Shared/Weapons/Melee/UseDelayOnMeleeHitSystem.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,12 @@ public sealed partial class WieldableComponent : Component
|
|||||||
[DataField]
|
[DataField]
|
||||||
public bool UnwieldOnUse = true;
|
public bool UnwieldOnUse = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should use delay trigger after the wield/unwield?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool UseDelayOnWield = true;
|
||||||
|
|
||||||
[DataField("wieldedInhandPrefix")]
|
[DataField("wieldedInhandPrefix")]
|
||||||
public string? WieldedInhandPrefix = "wielded";
|
public string? WieldedInhandPrefix = "wielded";
|
||||||
|
|
||||||
|
|||||||
@@ -191,6 +191,9 @@ public abstract class SharedWieldableSystem : EntitySystem
|
|||||||
args.Handled = TryWield(uid, component, args.User);
|
args.Handled = TryWield(uid, component, args.User);
|
||||||
else if (component.UnwieldOnUse)
|
else if (component.UnwieldOnUse)
|
||||||
args.Handled = TryUnwield(uid, component, args.User);
|
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)
|
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))
|
if (!CanWield(used, component, user))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (TryComp(used, out UseDelayComponent? useDelay)
|
if (TryComp(used, out UseDelayComponent? useDelay) && component.UseDelayOnWield)
|
||||||
&& !_delay.TryResetDelay((used, useDelay), true))
|
{
|
||||||
|
if (!_delay.TryResetDelay((used, useDelay), true))
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var attemptEv = new WieldAttemptEvent(user);
|
var attemptEv = new WieldAttemptEvent(user);
|
||||||
RaiseLocalEvent(used, ref attemptEv);
|
RaiseLocalEvent(used, ref attemptEv);
|
||||||
|
|||||||
@@ -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-name = Wand of the Locker
|
||||||
spellbook-wand-locker-description = Shoot cursed lockers at your enemies and lock em away!
|
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-name = Staff of Animation
|
||||||
spellbook-staff-animation-description = Bring inanimate objects to life!
|
spellbook-staff-animation-description = Bring inanimate objects to life!
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,32 @@
|
|||||||
- !type:ListingLimitedStockCondition
|
- !type:ListingLimitedStockCondition
|
||||||
stock: 1
|
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
|
- type: listing
|
||||||
id: SpellbookStaffAnimation
|
id: SpellbookStaffAnimation
|
||||||
name: spellbook-staff-animation-name
|
name: spellbook-staff-animation-name
|
||||||
|
|||||||
@@ -142,7 +142,6 @@
|
|||||||
- type: CorePoweredThrower
|
- type: CorePoweredThrower
|
||||||
- type: MeleeThrowOnHit
|
- type: MeleeThrowOnHit
|
||||||
unanchorOnHit: true
|
unanchorOnHit: true
|
||||||
enabled: false
|
|
||||||
- type: ItemSlots
|
- type: ItemSlots
|
||||||
slots:
|
slots:
|
||||||
core_slot:
|
core_slot:
|
||||||
|
|||||||
109
Resources/Prototypes/Entities/Objects/Weapons/Melee/hammers.yml
Normal file
@@ -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
|
||||||
@@ -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
|
|
||||||
BIN
Resources/Textures/Objects/Weapons/Melee/mjollnir.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 686 B |
|
After Width: | Height: | Size: 681 B |
@@ -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
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 695 B |
|
After Width: | Height: | Size: 684 B |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
@@ -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
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 6.3 KiB |