From 9bd03824ac863cb4d87bfc984dcc163b84926649 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Wed, 27 Dec 2023 22:11:13 -0500 Subject: [PATCH] GORILLA Gauntlets (#23012) * GORILLA gauntlets * oh shit this too --- .../Anomaly/Effects/AnomalyCoreSystem.cs | 33 +---- .../Components/AnomalyCoreComponent.cs | 18 ++- .../Components/CorePoweredThrowerComponent.cs | 25 ++++ .../Anomaly/SharedAnomalyCoreSystem.cs | 112 ++++++++++++++++ Content.Shared/Anomaly/SharedAnomalySystem.cs | 26 +++- .../Components/MeleeThrowOnHitComponent.cs | 105 +++++++++++++++ .../Weapons/Melee/Events/MeleeHitEvent.cs | 10 +- .../Weapons/Melee/MeleeThrowOnHitSystem.cs | 126 ++++++++++++++++++ .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 8 +- Resources/Locale/en-US/anomaly/anomaly.ftl | 14 ++ .../Locale/en-US/research/technologies.ftl | 1 + .../Objects/Specific/Research/anomaly.yml | 36 +++++ .../Objects/Weapons/Melee/baseball_bat.yml | 13 ++ .../Entities/Structures/Machines/lathe.yml | 1 + .../Structures/Specific/Anomaly/anomalies.yml | 2 +- .../Structures/Specific/Anomaly/cores.yml | 13 +- .../Prototypes/Recipes/Lathes/devices.yml | 11 ++ .../Prototypes/Research/experimental.yml | 12 ++ .../Weapons/Melee/gorilla.rsi/icon.png | Bin 0 -> 753 bytes .../Weapons/Melee/gorilla.rsi/inhand-left.png | Bin 0 -> 1055 bytes .../Melee/gorilla.rsi/inhand-right.png | Bin 0 -> 1039 bytes .../Weapons/Melee/gorilla.rsi/meta.json | 22 +++ 22 files changed, 542 insertions(+), 46 deletions(-) rename {Content.Server => Content.Shared}/Anomaly/Components/AnomalyCoreComponent.cs (72%) create mode 100644 Content.Shared/Anomaly/Components/CorePoweredThrowerComponent.cs create mode 100644 Content.Shared/Anomaly/SharedAnomalyCoreSystem.cs create mode 100644 Content.Shared/Weapons/Melee/Components/MeleeThrowOnHitComponent.cs create mode 100644 Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs create mode 100644 Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/icon.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/meta.json diff --git a/Content.Server/Anomaly/Effects/AnomalyCoreSystem.cs b/Content.Server/Anomaly/Effects/AnomalyCoreSystem.cs index eb45101373..dea116a65e 100644 --- a/Content.Server/Anomaly/Effects/AnomalyCoreSystem.cs +++ b/Content.Server/Anomaly/Effects/AnomalyCoreSystem.cs @@ -1,6 +1,5 @@ -using Content.Server.Anomaly.Components; using Content.Server.Cargo.Systems; -using Content.Shared.Anomaly; +using Content.Shared.Anomaly.Components; using Robust.Shared.Timing; namespace Content.Server.Anomaly.Effects; @@ -10,47 +9,19 @@ namespace Content.Server.Anomaly.Effects; /// public sealed class AnomalyCoreSystem : EntitySystem { - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; public override void Initialize() { - SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnGetPrice); } - private void OnMapInit(Entity core, ref MapInitEvent args) - { - core.Comp.DecayMoment = _gameTiming.CurTime + TimeSpan.FromSeconds(core.Comp.TimeToDecay); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var component)) - { - if (component.IsDecayed) - continue; - - //When time runs out, we completely decompose - if (component.DecayMoment < _gameTiming.CurTime) - Decay(uid, component); - } - } private void OnGetPrice(Entity core, ref PriceCalculationEvent args) { var timeLeft = core.Comp.DecayMoment - _gameTiming.CurTime; - var lerp = (double) (timeLeft.TotalSeconds / core.Comp.TimeToDecay); + var lerp = timeLeft.TotalSeconds / core.Comp.TimeToDecay; lerp = Math.Clamp(lerp, 0, 1); args.Price = MathHelper.Lerp(core.Comp.EndPrice, core.Comp.StartPrice, lerp); } - - private void Decay(EntityUid uid, AnomalyCoreComponent component) - { - _appearance.SetData(uid, AnomalyCoreVisuals.Decaying, false); - component.IsDecayed = true; - } } diff --git a/Content.Server/Anomaly/Components/AnomalyCoreComponent.cs b/Content.Shared/Anomaly/Components/AnomalyCoreComponent.cs similarity index 72% rename from Content.Server/Anomaly/Components/AnomalyCoreComponent.cs rename to Content.Shared/Anomaly/Components/AnomalyCoreComponent.cs index f86f95c71a..68d00f03ec 100644 --- a/Content.Server/Anomaly/Components/AnomalyCoreComponent.cs +++ b/Content.Shared/Anomaly/Components/AnomalyCoreComponent.cs @@ -1,15 +1,15 @@ -using Content.Server.Anomaly.Effects; +using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.Anomaly.Components; +namespace Content.Shared.Anomaly.Components; /// /// This component exists for a limited time, and after it expires it modifies the entity, greatly reducing its value and changing its visuals /// -[RegisterComponent, Access(typeof(AnomalyCoreSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedAnomalyCoreSystem))] +[AutoGenerateComponentState] public sealed partial class AnomalyCoreComponent : Component { - /// /// Amount of time required for the core to decompose into an inert core /// @@ -20,6 +20,7 @@ public sealed partial class AnomalyCoreComponent : Component /// The moment of core decay. It is set during entity initialization. /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] public TimeSpan DecayMoment; /// @@ -38,5 +39,14 @@ public sealed partial class AnomalyCoreComponent : Component /// Has the core decayed? /// [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] public bool IsDecayed; + + /// + /// The amount of GORILLA charges the core has. + /// Not used when is false. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public int Charge = 5; } diff --git a/Content.Shared/Anomaly/Components/CorePoweredThrowerComponent.cs b/Content.Shared/Anomaly/Components/CorePoweredThrowerComponent.cs new file mode 100644 index 0000000000..ee21ce1782 --- /dev/null +++ b/Content.Shared/Anomaly/Components/CorePoweredThrowerComponent.cs @@ -0,0 +1,25 @@ +using System.Numerics; +using Content.Shared.Weapons.Melee.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Anomaly.Components; + +/// +/// This is used for an entity with that is governed by an anomaly core inside of it. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedAnomalyCoreSystem))] +public sealed partial class CorePoweredThrowerComponent : Component +{ + /// + /// The ID of the item slot containing the core. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public string CoreSlotId = "core_slot"; + + /// + /// A range for how much the stability variable on the anomaly will increase with each throw. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public Vector2 StabilityPerThrow = new(0.1f, 0.2f); +} diff --git a/Content.Shared/Anomaly/SharedAnomalyCoreSystem.cs b/Content.Shared/Anomaly/SharedAnomalyCoreSystem.cs new file mode 100644 index 0000000000..f4864a532b --- /dev/null +++ b/Content.Shared/Anomaly/SharedAnomalyCoreSystem.cs @@ -0,0 +1,112 @@ +using Content.Shared.Anomaly.Components; +using Content.Shared.Construction.Components; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Examine; +using Content.Shared.Weapons.Melee.Components; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Timing; + +namespace Content.Shared.Anomaly; + +/// +/// This component reduces the value of the entity during decay +/// +public sealed class SharedAnomalyCoreSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnAttemptMeleeThrowOnHit); + SubscribeLocalEvent(OnCorePoweredExamined); + } + + private void OnMapInit(Entity core, ref MapInitEvent args) + { + core.Comp.DecayMoment = _gameTiming.CurTime + TimeSpan.FromSeconds(core.Comp.TimeToDecay); + Dirty(core, core.Comp); + } + + private void OnAttemptMeleeThrowOnHit(Entity ent, ref AttemptMeleeThrowOnHitEvent args) + { + var (uid, comp) = ent; + + // don't waste charges on non-anchorable non-anomalous static bodies. + if (!HasComp(args.Hit) + && !HasComp(args.Hit) + && TryComp(args.Hit, out var body) + && body.BodyType == BodyType.Static) + return; + + args.Cancelled = true; + args.Handled = true; + + if (!_itemSlots.TryGetSlot(uid, comp.CoreSlotId, out var slot)) + return; + + if (!TryComp(slot.Item, out var coreComponent)) + return; + + if (coreComponent.IsDecayed) + { + if (coreComponent.Charge <= 0) + return; + args.Cancelled = false; + coreComponent.Charge--; + } + else + { + args.Cancelled = false; + } + } + + private void OnCorePoweredExamined(Entity ent, ref ExaminedEvent args) + { + var (uid, comp) = ent; + if (!args.IsInDetailsRange) + return; + + if (!_itemSlots.TryGetSlot(uid, comp.CoreSlotId, out var slot) || + !TryComp(slot.Item, out var coreComponent)) + { + args.PushMarkup(Loc.GetString("anomaly-gorilla-charge-none")); + return; + } + + if (coreComponent.IsDecayed) + { + args.PushMarkup(Loc.GetString("anomaly-gorilla-charge-limit", ("count", coreComponent.Charge))); + } + else + { + args.PushMarkup(Loc.GetString("anomaly-gorilla-charge-infinite")); + } + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component)) + { + if (component.IsDecayed) + continue; + + //When time runs out, we completely decompose + if (component.DecayMoment < _gameTiming.CurTime) + Decay(uid, component); + } + } + + private void Decay(EntityUid uid, AnomalyCoreComponent component) + { + _appearance.SetData(uid, AnomalyCoreVisuals.Decaying, false); + component.IsDecayed = true; + Dirty(uid, component); + } +} diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index cf937b761e..c014ff90e1 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -4,10 +4,13 @@ using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Popups; +using Content.Shared.Weapons.Melee.Components; using Content.Shared.Weapons.Melee.Events; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -23,6 +26,7 @@ public abstract class SharedAnomalySystem : EntitySystem [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; private ISawmill _sawmill = default!; @@ -33,6 +37,8 @@ public abstract class SharedAnomalySystem : EntitySystem SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent(OnAttacked); + SubscribeLocalEvent(OnAnomalyThrowStart); + SubscribeLocalEvent(OnAnomalyThrowEnd); SubscribeLocalEvent(OnAnomalyUnpause); SubscribeLocalEvent(OnPulsingUnpause); @@ -49,9 +55,25 @@ public abstract class SharedAnomalySystem : EntitySystem private void OnAttacked(EntityUid uid, AnomalyComponent component, AttackedEvent args) { + if (HasComp(args.Used)) + return; + DoAnomalyBurnDamage(uid, args.User, component); } + private void OnAnomalyThrowStart(Entity ent, ref MeleeThrowOnHitStartEvent args) + { + if (!TryComp(args.Used, out var corePowered) || !TryComp(ent, out var body)) + return; + _physics.SetBodyType(ent, BodyType.Dynamic, body: body); + ChangeAnomalyStability(ent, Random.NextFloat(corePowered.StabilityPerThrow.X, corePowered.StabilityPerThrow.Y), ent.Comp); + } + + private void OnAnomalyThrowEnd(Entity ent, ref MeleeThrowOnHitEndEvent args) + { + _physics.SetBodyType(ent, BodyType.Static); + } + public void DoAnomalyBurnDamage(EntityUid source, EntityUid target, AnomalyComponent component) { _damageable.TryChangeDamage(target, component.AnomalyContactDamage, true); @@ -111,7 +133,7 @@ public abstract class SharedAnomalySystem : EntitySystem var pulse = EnsureComp(uid); pulse.EndTime = Timing.CurTime + pulse.PulseDuration; Appearance.SetData(uid, AnomalyVisuals.IsPulsing, true); - + var ev = new AnomalyPulseEvent(uid, component.Stability, component.Severity); RaiseLocalEvent(uid, ref ev, true); } diff --git a/Content.Shared/Weapons/Melee/Components/MeleeThrowOnHitComponent.cs b/Content.Shared/Weapons/Melee/Components/MeleeThrowOnHitComponent.cs new file mode 100644 index 0000000000..ef8ee3f89c --- /dev/null +++ b/Content.Shared/Weapons/Melee/Components/MeleeThrowOnHitComponent.cs @@ -0,0 +1,105 @@ +using System.Numerics; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +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. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(MeleeThrowOnHitSystem))] +[AutoGenerateComponentState] +public sealed partial class MeleeThrowOnHitComponent : Component +{ + /// + /// The speed at which hit entities should be thrown. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public float Speed = 10f; + + /// + /// How long hit entities remain thrown, max. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public float Lifetime = 3f; + + /// + /// How long we wait to start accepting collision. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float MinLifetime = 0.05f; + + /// + /// Whether or not anchorable entities should be unanchored when hit. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public bool UnanchorOnHit; + + /// + /// Whether or not the throwing behavior occurs by default. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public bool Enabled = true; +} + +/// +/// Component used to track entities that have been yeeted by +/// +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState] +[Access(typeof(MeleeThrowOnHitSystem))] +public sealed partial class MeleeThrownComponent : Component +{ + /// + /// The velocity of the throw + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public Vector2 Velocity; + + /// + /// How long the throw will last. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public float Lifetime; + + /// + /// How long we wait to start accepting collision. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float MinLifetime; + + /// + /// At what point in time will the throw be complete? + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoNetworkedField] + public TimeSpan ThrownEndTime; + + /// + /// At what point in time will the be exhausted + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoNetworkedField] + public TimeSpan MinLifetimeTime; +} + +/// +/// Event raised before an entity is thrown by to see if a throw is allowed. +/// If not handled, the enabled field on the component will be used instead. +/// +[ByRefEvent] +public record struct AttemptMeleeThrowOnHitEvent(EntityUid Hit, bool Cancelled = false, bool Handled = false); + +[ByRefEvent] +public record struct MeleeThrowOnHitStartEvent(EntityUid User, EntityUid Used); + +[ByRefEvent] +public record struct MeleeThrowOnHitEndEvent(); diff --git a/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs b/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs index 5c7096e117..55c01c1d6f 100644 --- a/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs +++ b/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Shared.Damage; using Content.Shared.FixedPoint; using Robust.Shared.Audio; @@ -50,6 +51,12 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs /// public readonly EntityUid Weapon; + /// + /// The direction of the attack. + /// If null, it was a click-attack. + /// + public readonly Vector2? Direction; + /// /// Check if this is true before attempting to do something during a melee attack other than changing/adding bonus damage.
/// For example, do not spend charges unless equals true. @@ -59,12 +66,13 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs /// public bool IsHit = true; - public MeleeHitEvent(List hitEntities, EntityUid user, EntityUid weapon, DamageSpecifier baseDamage) + public MeleeHitEvent(List hitEntities, EntityUid user, EntityUid weapon, DamageSpecifier baseDamage, Vector2? direction) { HitEntities = hitEntities; User = user; Weapon = weapon; BaseDamage = baseDamage; + Direction = direction; } } diff --git a/Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs b/Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs new file mode 100644 index 0000000000..d755f7a0b4 --- /dev/null +++ b/Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs @@ -0,0 +1,126 @@ +using System.Numerics; +using Content.Shared.Construction.Components; +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; + +namespace Content.Shared.Weapons.Melee; + +/// +/// This handles +/// +public sealed class MeleeThrowOnHitSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnMeleeHit); + SubscribeLocalEvent(OnThrownStartup); + SubscribeLocalEvent(OnThrownShutdown); + SubscribeLocalEvent(OnStartCollide); + } + + private void OnMeleeHit(Entity ent, ref MeleeHitEvent args) + { + var (_, comp) = ent; + 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(hit)) + { + _transform.Unanchor(hit, Transform(hit)); + } + + RemComp(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 ent, ref ComponentStartup args) + { + var (_, comp) = ent; + + if (!TryComp(ent, out var body) || + (body.BodyType & (BodyType.Dynamic | BodyType.KinematicController)) == 0x0) + return; + + comp.ThrownEndTime = _timing.CurTime + TimeSpan.FromSeconds(comp.Lifetime); + comp.MinLifetimeTime = _timing.CurTime + TimeSpan.FromSeconds(comp.MinLifetime); + _physics.SetBodyStatus(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 ent, ref ComponentShutdown args) + { + if (TryComp(ent, out var body)) + _physics.SetBodyStatus(body, BodyStatus.OnGround); + var ev = new MeleeThrowOnHitEndEvent(); + RaiseLocalEvent(ent, ref ev); + } + + private void OnStartCollide(Entity ent, ref StartCollideEvent args) + { + var (_, comp) = ent; + if (!args.OtherFixture.Hard || !args.OtherBody.CanCollide || !args.OurFixture.Hard || !args.OurBody.CanCollide) + return; + + if (_timing.CurTime < comp.MinLifetimeTime) + return; + + RemCompDeferred(ent, ent.Comp); + } + + public bool CanThrowOnHit(Entity ent, EntityUid target) + { + var (uid, comp) = ent; + + var ev = new AttemptMeleeThrowOnHitEvent(target); + RaiseLocalEvent(uid, ref ev); + + if (ev.Handled) + return !ev.Cancelled; + + return comp.Enabled; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (_timing.CurTime > comp.ThrownEndTime) + RemCompDeferred(uid, comp); + } + } +} diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 68f625d949..4824672974 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -467,7 +467,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem AdminLogger.Add(LogType.MeleeHit, LogImpact.Low, $"{ToPrettyString(user):actor} melee attacked (light) using {ToPrettyString(meleeUid):tool} and missed"); } - var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage); + var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage, null); RaiseLocalEvent(meleeUid, missEvent); Audio.PlayPredicted(component.SwingSound, meleeUid, user); return; @@ -476,7 +476,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}"); // Raise event before doing damage so we can cancel damage if the event is handled - var hitEvent = new MeleeHitEvent(new List { target.Value }, user, meleeUid, damage); + var hitEvent = new MeleeHitEvent(new List { target.Value }, user, meleeUid, damage, null); RaiseLocalEvent(meleeUid, hitEvent); if (hitEvent.Handled) @@ -578,7 +578,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem AdminLogger.Add(LogType.MeleeHit, LogImpact.Low, $"{ToPrettyString(user):actor} melee attacked (heavy) using {ToPrettyString(meleeUid):tool} and missed"); } - var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage); + var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage, direction); RaiseLocalEvent(meleeUid, missEvent); Audio.PlayPredicted(component.SwingSound, meleeUid, user); @@ -619,7 +619,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}"); // Raise event before doing damage so we can cancel damage if the event is handled - var hitEvent = new MeleeHitEvent(targets, user, meleeUid, damage); + var hitEvent = new MeleeHitEvent(targets, user, meleeUid, damage, direction); RaiseLocalEvent(meleeUid, hitEvent); if (hitEvent.Handled) diff --git a/Resources/Locale/en-US/anomaly/anomaly.ftl b/Resources/Locale/en-US/anomaly/anomaly.ftl index 8452725e5e..df4cbfe20c 100644 --- a/Resources/Locale/en-US/anomaly/anomaly.ftl +++ b/Resources/Locale/en-US/anomaly/anomaly.ftl @@ -25,6 +25,20 @@ anomaly-scanner-particle-unstable = - [color=plum]Unstable type:[/color] {$type} anomaly-scanner-particle-containment = - [color=goldenrod]Containment type:[/color] {$type} anomaly-scanner-pulse-timer = Time until next pulse: [color=gray]{$time}[/color] +anomaly-gorilla-core-slot-name = Anomaly core +anomaly-gorilla-charge-none = It has no [bold]anomaly core[/bold] inside of it. +anomaly-gorilla-charge-limit = It has [color={$count -> + [3]green + [2]yellow + [1]orange + [0]red + *[other]purple +}]{$count} {$count -> + [one]charge + *[other]charges +}[/color] remaining. +anomaly-gorilla-charge-infinite = It has [color=gold]infinite charges[/color]. [italic]For now...[/italic] + anomaly-sync-connected = Anomaly successfully attached anomaly-sync-disconnected = The connection to the anomaly has been lost! anomaly-sync-no-anomaly = No anomaly in range. diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 3b682d9284..9c95964750 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -50,6 +50,7 @@ research-technology-basic-xenoarcheology = Basic XenoArcheology research-technology-alternative-research = Alternative Research research-technology-magnets-tech = Localized Magnetism research-technology-advanced-parts = Advanced Parts +research-technology-anomaly-harnessing = Anomaly Core Harnessing research-technology-grappling = Grappling research-technology-abnormal-artifact-manipulation = Abnormal Artifact Manipulation research-technology-gravity-manipulation = Gravity Manipulation diff --git a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml index a5a592cf78..048bad8e5b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml @@ -106,3 +106,39 @@ slots: cell_slot: name: power-cell-slot-component-slot-name-default + +- type: entity + name: G.O.R.I.L.L.A. gauntlet + parent: BaseItem + id: WeaponGauntletGorilla + description: A robust piece of research equipment. When powered with an anomaly core, a single blow can launch anomalous objects through the air. + components: + - type: Sprite + sprite: Objects/Weapons/Melee/gorilla.rsi + state: icon + - type: Item + size: Large + - type: MeleeWeapon + attackRate: 0.5 + angle: 0 + animation: WeaponArcFist + wideAnimationRotation: -135 + damage: + types: + Blunt: 20 + - type: CorePoweredThrower + - type: MeleeThrowOnHit + unanchorOnHit: true + enabled: false + - type: ItemSlots + slots: + core_slot: + name: anomaly-gorilla-core-slot-name + insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg + ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg + whitelist: + components: + - AnomalyCore + - type: ContainerContainer + containers: + core_slot: !type:ContainerSlot diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml index a02efc13f5..b7fae7aced 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml @@ -37,3 +37,16 @@ - type: Tag tags: - BaseballBat + +- type: entity + name: knockback stick + parent: BaseBallBat + id: WeaponMeleeKnockbackStick + description: And then he spleefed all over. + suffix: Do not map + components: + - type: MeleeThrowOnHit + - type: MeleeWeapon + damage: + types: + Blunt: 1 diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 20008c6a97..8772c7290e 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -262,6 +262,7 @@ - PowerCellMicroreactor - PowerCellHigh - WeaponPistolCHIMP + - WeaponGauntletGorilla - SynthesizerInstrument - RPED - ClothingShoesBootsMag diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index 234dd52359..8ac5fb03e3 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -321,7 +321,7 @@ types: Slash: 1 - type: EntitySpawnAnomaly - superCriticalSpawns: + superCriticalSpawns: - ReagentSlimeSpawner spawns: - PuddleSparkle diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/cores.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/cores.yml index b5801994c0..06e08beb75 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/cores.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/cores.yml @@ -23,7 +23,7 @@ - type: ItemCooldown - type: EmitSoundOnUse #placeholder for future unical mechanic sound: - collection: RadiationPulse + collection: RadiationPulse - type: UseDelay delay: 2 - type: AnomalyCore @@ -143,8 +143,15 @@ id: BaseAnomalyInertCore abstract: true components: + - type: Sprite + layers: + - state: core + - state: pulse + visible: false + map: ["decay"] - type: AnomalyCore - timeToDecay: 1 #decay very fast + timeToDecay: 0 + isDecayed: true - type: entity parent: BaseAnomalyInertCore @@ -248,4 +255,4 @@ radius: 1.5 energy: 2.0 color: "#ffffaa" - castShadows: false \ No newline at end of file + castShadows: false diff --git a/Resources/Prototypes/Recipes/Lathes/devices.yml b/Resources/Prototypes/Recipes/Lathes/devices.yml index 1593f12597..d541c36353 100644 --- a/Resources/Prototypes/Recipes/Lathes/devices.yml +++ b/Resources/Prototypes/Recipes/Lathes/devices.yml @@ -101,6 +101,17 @@ Steel: 500 Glass: 400 +- type: latheRecipe + id: WeaponGauntletGorilla + result: WeaponGauntletGorilla + completetime: 5 + materials: + Steel: 1500 + Plastic: 300 + Glass: 500 + Plasma: 500 + Silver: 250 + - type: latheRecipe id: ClothingBackpackHolding result: ClothingBackpackHolding diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index a58baf1b78..f504f7fa24 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -70,6 +70,18 @@ recipeUnlocks: - ClothingShoesBootsMag +- type: technology + id: AnomalyCoreHarnessing + name: research-technology-anomaly-harnessing + icon: + sprite: Objects/Weapons/Melee/gorilla.rsi + state: icon + discipline: Experimental + tier: 1 + cost: 10000 + recipeUnlocks: + - WeaponGauntletGorilla + # Tier 2 - type: technology diff --git a/Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/icon.png b/Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a57ce53cc4633590e7be46bbe293c5bd7cace1f4 GIT binary patch literal 753 zcmVPx%t4TybR9J=WmETKKVI0Ok{$M6 zf-a>Sz4B_v)*sMC9P2_D6q%tZMA~d@7dvPotg|0EmGyS9&ajy7mvqyft8-r7@AEwG z^ZoL^P)8mAI~q=tYK1x2YOs!m{fU`CAG{6SLNMvuh{>q%`Jps-ddiK-KL zDJ>FdzKF$QA@wc71`+!1bn|3A3Qic&+(2hZo|k<0;yOCu(olXY;q0LVE zJHjRFt4_e-IFHM9nf-%9WcyiU*-kVX!(y?}>4X>YHzYQ$mE>uvM%*_xDQ@&$2SBzv z2!HuZHoKp1M=FKe?IQQ{kWa*THvja*A*?uofHx?1d-LCt)CNFz&n;Fzz6ZeVb`g!n z(BeC!QmK-B6(!&sn-tPuJHxACcCK8&Y_?)Dwb0lou-P2MqF>2o_Y2~$)aNBHqp}SI zyg?xiwga%)Zs-2@ENdn(nOgoZvG{I@gM)*z#G%X<_{Jv1ZZ8b4hI#Ah0l-g&i8EIG z=_F>e6@ZoHwX%f`l-zuD-~h(=a=gCe1VGca7~jj`Z*5{_d5yr_RK;)0i3NtZF>C<3 zo?+r#2mWmS%YN?@eW)z!tq!UD6ivxR+ky$=L%D&of)IL?H}<0-V|v~VKXNlE-O jD2gJE&O#k^oHl*|^#mz*24CGg00000NkvXXu0mjfwBKXR literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..67c61efb1403646bdc3ffc3d4a9b7d2499e373f1 GIT binary patch literal 1055 zcmV+)1mOFLP)Px&*-1n}RCt{2nqO#BR~*MbnG@P&OR%}5&9EgfWR*UoCL}mv^v_1)RotQ$CZy{6 zl&6;MVT?ZPVNXGPk?pD5L#Ii>Cy9upD>(a9Hf6QlW_657aiZ~(3dt-?jh&mnlh1|)mSVhUUTmt7z*I^YPIpH>?KmCPyT1T zqsKoHd-Zn!FpNBNKQ8cR;Wsjwj4c)Lk{zPY6SgEE7z*HaJE>GkR4OIQyAO~$ebOc z*+Pbl?B&W&z_fRPd~>w|C>H^^>|Ll^fZ-I`kQ;3)_W4tzh0*Fv`CX(H}-9I zvhFxhJuZgOYu87!L(j*LA%#MrP$(1%g+ifFC=?2X;@QTQ_PAZG?0+MXNC?xEF*Y`4 zIllDP*wY{ui;2#zxA}Oq`p0Q9nG{xCDbf@0-ofJ>t_+IyPq){C_xAS64zmHTS7Y+* zlmLhy+3T?COp)#n7;bVgZBrlbaisLL=;Z4LAH8kHAhsgg`1t~o(f}sEt1tM#8~S?fW~(pBqv~hVK+k``nWK&g_}2iul4q2$N0Q;qY>j= zn~RafU+Qk6N=v{Od%hH=H(+_+D&a^sU0q$W}DVX`bq{rv*GM`)LT$ssFC=`lD Z_y-K3e42?F1)%@{002ovPDHLkV1mh7{_Ow& literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..dd19bf46c2938d274e81e2985bc5bd90b9c3c75e GIT binary patch literal 1039 zcmV+q1n~QbP)Px&$w@>(RCt{2nqO#JRUF4Zajq=O>}74tQrSYF3@1g>rKE^2-KMmLQH-^)hop1! zB~KF{+(RFH@TrtODEbt9C^aqkBoUFc!LdAzIk9U{W?=}0mDyd=heTs?E#>%-C8YUN zl6dlGcs@_}Tn_hsf4}p+_a5%=2MUEkp-?Ck3WehTh^gAiU@$10PG@nvwY3F6C={wX zzuO1~gQ9R;h+}qkwlVEvV`HLp?DP3Vt-0MsBU&r6GV|U|@s{_E;#k*{tpA+g&(uRA zk;pUQ0H4n%UeM0s9&ur_Spis$-XgrTP@(_1wyWzhDiRQ0S}?6fZ($f&3?qxCS#XcI zI-#v83ifx8xX?5UhLNqg`T);YJ2|eq2psDLAhop(fYq2LLZmgSCGxH@&E$oB$nHZn zjpjVfQQbwj#{8-Yh}jbC|_i>$FCU92oT4;|dj`~bk;nLQj1JG#D8bAHWzz!`^w zb1$FejKcwt5B}RUAKbao^{c<=xyV%J&AR5+y%Ex!r#Y#eY+6)1`7VBhz_kHZuf5N} zO9O|_t+|>LUjKppbO0|i(LmR)K0w&JT-3fylxO9grBEmo3WY+UP$(1%g+ifFw1I~H zmCen~3CK50bfQ_p=ktk~1`B62I#b=3tN&QZ#PAG zR5E`wzb!6adY#2wTKcu?NnQ)6hGhnhOe? zdu6bqo+y*a3;S53l{L%%N<#@KcR^_b!Y8K&xtw}UrZxbMTaL2%+waASuk3r&wGSw# zH1Li4KjIl3=1M%qW#lJdXlMw4UF$zUzE1(Z5VHCd8cslXX~CrHeqp!wVYZy$y!$-K z^yHW&|^0clm06e3^09=X3 zSi2*y-rw|vCcJ)85cSi&4S<$5A{1VJUDfQ+s?VQ7p-{Aoe*xxKbo>vOwdVi;002ov JPDHLkV1n