Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mjollnir and Singularity Hammer for Wizard #34446

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
caad2a5
Adds Repulse Attract ECS
keronshb Jan 1, 2025
30ffe5c
Adds cooldown and method to check cooldown
keronshb Jan 1, 2025
e07a9f2
Adds damage values to mjolnir and singularity hammer
keronshb Jan 3, 2025
09eb58b
Adds sprites and descriptions for mjollnir and singularity hammer
keronshb Jan 3, 2025
d51282f
Adds optional user to comp
keronshb Jan 6, 2025
97151fd
Fixes Mjollnir spelling
keronshb Jan 6, 2025
cfc40f2
Massive cleanup and removes redundant code
keronshb Jan 6, 2025
2beeb08
Adds DisableDuringUseDelay and StunTime to MeleeThrowOnHit
keronshb Jan 10, 2025
4a4352d
Adds UseDelayOnMeleeHit
keronshb Jan 10, 2025
6c3f5c4
Removes repulseattract localization
keronshb Jan 10, 2025
448ddd6
Reworks RepulseAttract ECS and removes fluff
keronshb Jan 10, 2025
f41243e
Inverts If, incase people would like to make adjustments in the metho…
keronshb Jan 10, 2025
5341d8b
Merge branch 'master' of https://github.com/space-wizards/space-stati…
keronshb Jan 10, 2025
fe79081
Changes Blacklistfail to blacklistpass
keronshb Jan 11, 2025
ed207a9
Removes hammers from staves
keronshb Jan 11, 2025
8572189
Cleans up Usedelay on hit code and check for isdelayed
keronshb Jan 12, 2025
c4cf1fd
Adds NoUseDelayOnWield bool, Checks if UseDelay should trigger either…
keronshb Jan 12, 2025
e492998
Adds wield requirement for melee attacks, sets nousedelayonwield to true
keronshb Jan 12, 2025
f0329bd
Adds support to allow comp to work on throw
keronshb Jan 12, 2025
0a29bc8
Checks the attempt melee after wieldable system so we dont accidental…
keronshb Jan 14, 2025
6892020
Adds activate on throw for mjollnir, adds custom sounds for mjollnir …
keronshb Jan 14, 2025
77e2507
Adds mjollnir and singularity hammer to the grimoire
keronshb Jan 14, 2025
f0362d1
Merge branch 'master' of https://github.com/space-wizards/space-stati…
keronshb Jan 15, 2025
ed66418
reverts unintended bat change
keronshb Jan 15, 2025
414e26e
fixes singularity hammer license
keronshb Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Content.Shared/RepulseAttract/RepulseAttractComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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>
/// Optional user, used if the <see cref="RepulseAttractComponent"/> is on an Item
/// </summary>
[DataField]
public EntityUid? User;

/// <summary>
/// Attracts if true, Repulse if false.
/// </summary>
[DataField]
public bool Attract;

/// <summary>
/// How strong should the Repulse/Attract be?
/// </summary>
[DataField]
public float Strength = 1.0F;

/// <summary>
/// How close do the entities need to be?
/// </summary>
[DataField]
public float Range = 1.0F;

/// <summary>
/// Should this work while there's an active UseDelayComponent?
/// </summary>
[DataField]
public bool DisableDuringUseDelay;

/// <summary>
/// What kind of entities should this effect only?
/// </summary>
[DataField, AutoNetworkedField]
public EntityWhitelist? Whitelist;

/// <summary>
/// What kind of entities should be excluded from the effect?
/// </summary>
[DataField, AutoNetworkedField]
public EntityWhitelist? Blacklist;
}
94 changes: 94 additions & 0 deletions Content.Shared/RepulseAttract/RepulseAttractSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Content.Shared.Ghost;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Throwing;
using Content.Shared.Timing;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Whitelist;
using Content.Shared.Wieldable;

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!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RepulseAttractComponent, GettingPickedUpAttemptEvent>(OnPickupAttempt);
SubscribeLocalEvent<RepulseAttractComponent, DroppedEvent>(OnDrop);
SubscribeLocalEvent<RepulseAttractComponent, AttemptMeleeEvent>(OnMeleeAttempt, after: [typeof(WieldableSystem)]);
}

private void OnPickupAttempt(Entity<RepulseAttractComponent> ent, ref GettingPickedUpAttemptEvent args)
{
if (args.Cancelled || ent.Owner == args.User)
return;

ent.Comp.User = args.User;
}

private void OnDrop(Entity<RepulseAttractComponent> ent, ref DroppedEvent args)
{
ent.Comp.User = null;
}

private void OnMeleeAttempt(Entity<RepulseAttractComponent> ent, ref AttemptMeleeEvent args)
{
if (args.Cancelled)
return;

if (ent.Comp.DisableDuringUseDelay)
{
if (TryComp<UseDelayComponent>(ent.Owner, out var useDelay) && _delay.IsDelayed((ent.Owner, useDelay)))
return;
}

TryRepulseAttract(ent);
}

/// <summary>
/// Try to Repulse or Attract
/// </summary>
/// <returns></returns>
private bool TryRepulseAttract(Entity<RepulseAttractComponent> ent)
{
var caster = ent.Owner;
var comp = ent.Comp;

if (comp.User != null)
caster = comp.User.Value;

var xForm = Transform(caster);

var entsInRange = _lookup.GetEntitiesInRange(caster, comp.Range);

foreach (var target in entsInRange)
{
if (_whitelist.IsWhitelistFail(comp.Whitelist, target) || _whitelist.IsBlacklistPass(comp.Blacklist, target))
continue;

var targetXForm = Transform(target);

if (targetXForm.Anchored || HasComp<GhostComponent>(target))
continue;

var userWorldPos = _xForm.GetWorldPosition(xForm);
var targetWorldPos = _xForm.GetWorldPosition(target);

var direction = targetWorldPos - userWorldPos;

if (comp.Attract)
direction = userWorldPos - targetWorldPos;

_throw.TryThrow(target, direction, comp.Strength, doSpin: true);
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ public sealed partial class MeleeThrowOnHitComponent : Component
[DataField, ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
public bool Enabled = true;

/// <summary>
/// Should this set <see cref="Enabled"/> to false if usedelay is active?
/// </summary>
[DataField]
[AutoNetworkedField]
public bool DisableDuringUseDelay;

/// <summary>
/// How long should this stun the target, if applicable?
/// </summary>
[DataField]
[AutoNetworkedField]
public TimeSpan? StunTime;

/// <summary>
/// Should this also work on a throw-hit?
/// </summary>
[DataField]
[AutoNetworkedField]
public bool ActivateOnThrown;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(UseDelayOnMeleeHitSystem))]
public sealed partial class UseDelayOnMeleeHitComponent : Component
{

}
51 changes: 46 additions & 5 deletions Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using System.Linq;
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;
Expand All @@ -18,27 +22,61 @@ 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!;

/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<MeleeThrowOnHitComponent, MeleeHitEvent>(OnMeleeHit);
SubscribeLocalEvent<MeleeThrowOnHitComponent, ThrowDoHitEvent>(OnThrowHit);
SubscribeLocalEvent<MeleeThrownComponent, ComponentStartup>(OnThrownStartup);
SubscribeLocalEvent<MeleeThrownComponent, ComponentShutdown>(OnThrownShutdown);
SubscribeLocalEvent<MeleeThrownComponent, StartCollideEvent>(OnStartCollide);
SubscribeLocalEvent<MeleeThrowOnHitComponent, AttemptMeleeThrowOnHitEvent>(OnAttempt);
}

private void OnAttempt(Entity<MeleeThrowOnHitComponent> ent, ref AttemptMeleeThrowOnHitEvent args)
{
// This is in an instance where you'd like this to control if the melee throw on hit works instead of an anomaly core like it does for Gorilla Gauntlets
// Only nesting here in cases where enabled is set elsewhere
if (ent.Comp.DisableDuringUseDelay)
{
if (TryComp<UseDelayComponent>(ent.Owner, out var useDelay) && _delay.IsDelayed((ent.Owner, useDelay)))
{
ent.Comp.Enabled = false;
return;
}

ent.Comp.Enabled = true;
}
}

private void OnMeleeHit(Entity<MeleeThrowOnHitComponent> ent, ref MeleeHitEvent args)
{
var (_, comp) = ent;
if (!args.IsHit)
return;

var mapPos = _transform.GetMapCoordinates(args.User).Position;
foreach (var hit in args.HitEntities)
ThrowOnHitHelper(ent, args.User, args.HitEntities.ToHashSet(), direction: args.Direction);
}

private void OnThrowHit(Entity<MeleeThrowOnHitComponent> ent, ref ThrowDoHitEvent args)
{
if (args.Component.Thrower is null || !ent.Comp.ActivateOnThrown)
return;

ThrowOnHitHelper(ent, args.Component.Thrower.Value, [args.Target]);
}

private void ThrowOnHitHelper(Entity<MeleeThrowOnHitComponent> ent, EntityUid user, HashSet<EntityUid> hitEntities, Vector2? direction = null)
{
var (_, comp) = ent;

var mapPos = _transform.GetMapCoordinates(user).Position;
foreach (var hit in hitEntities)
{
var hitPos = _transform.GetMapCoordinates(hit).Position;
var angle = args.Direction ?? hitPos - mapPos;
var angle = direction ?? hitPos - mapPos;
if (angle == Vector2.Zero)
continue;

Expand All @@ -51,7 +89,7 @@ private void OnMeleeHit(Entity<MeleeThrowOnHitComponent> ent, ref MeleeHitEvent
}

RemComp<MeleeThrownComponent>(hit);
var ev = new MeleeThrowOnHitStartEvent(args.User, ent);
var ev = new MeleeThrowOnHitStartEvent(user, ent);
RaiseLocalEvent(hit, ref ev);
var thrownComp = new MeleeThrownComponent
{
Expand All @@ -60,6 +98,9 @@ private void OnMeleeHit(Entity<MeleeThrowOnHitComponent> ent, ref MeleeHitEvent
MinLifetime = comp.MinLifetime
};
AddComp(hit, thrownComp);

if (ent.Comp.StunTime != null)
_stun.TryParalyze(hit, ent.Comp.StunTime.Value, false);
}
}

Expand Down
38 changes: 38 additions & 0 deletions Content.Shared/Weapons/Melee/UseDelayOnMeleeHitSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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;

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);
}
}
8 changes: 7 additions & 1 deletion Content.Shared/Wieldable/Components/WieldableComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 NoUseDelayOnWield;

[DataField("wieldedInhandPrefix")]
public string? WieldedInhandPrefix = "wielded";

Expand Down
11 changes: 8 additions & 3 deletions Content.Shared/Wieldable/WieldableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ private void OnUseInHand(EntityUid uid, WieldableComponent component, UseInHandE
args.Handled = TryWield(uid, component, args.User);
else if (component.UnwieldOnUse)
args.Handled = TryUnwield(uid, component, args.User);

if (HasComp<UseDelayComponent>(uid) && component.NoUseDelayOnWield)
args.ApplyDelay = false;
}

public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user, bool quiet = false)
Expand Down Expand Up @@ -208,9 +211,11 @@ public bool TryWield(EntityUid used, WieldableComponent component, EntityUid use
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.NoUseDelayOnWield)
{
if (!_delay.TryResetDelay((used, useDelay), true))
return false;
}

var attemptEv = new WieldAttemptEvent(user);
RaiseLocalEvent(used, ref attemptEv);
Expand Down
6 changes: 6 additions & 0 deletions Resources/Locale/en-US/store/spellbook-catalog.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,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!

# Events

spellbook-event-summon-ghosts-name = Summon Ghosts
Expand Down
Loading
Loading