@@ -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;
|
||||
/// </summary>
|
||||
public sealed class AnomalyCoreSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<AnomalyCoreComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<AnomalyCoreComponent, PriceCalculationEvent>(OnGetPrice);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<AnomalyCoreComponent> 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<AnomalyCoreComponent>();
|
||||
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<AnomalyCoreComponent> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This component exists for a limited time, and after it expires it modifies the entity, greatly reducing its value and changing its visuals
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(AnomalyCoreSystem))]
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedAnomalyCoreSystem))]
|
||||
[AutoGenerateComponentState]
|
||||
public sealed partial class AnomalyCoreComponent : Component
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Amount of time required for the core to decompose into an inert core
|
||||
/// </summary>
|
||||
@@ -20,6 +20,7 @@ public sealed partial class AnomalyCoreComponent : Component
|
||||
/// The moment of core decay. It is set during entity initialization.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public TimeSpan DecayMoment;
|
||||
|
||||
/// <summary>
|
||||
@@ -38,5 +39,14 @@ public sealed partial class AnomalyCoreComponent : Component
|
||||
/// Has the core decayed?
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public bool IsDecayed;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of GORILLA charges the core has.
|
||||
/// Not used when <see cref="IsDecayed"/> is false.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public int Charge = 5;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Weapons.Melee.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Anomaly.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for an entity with <see cref="MeleeThrowOnHitComponent"/> that is governed by an anomaly core inside of it.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(SharedAnomalyCoreSystem))]
|
||||
public sealed partial class CorePoweredThrowerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the item slot containing the core.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public string CoreSlotId = "core_slot";
|
||||
|
||||
/// <summary>
|
||||
/// A range for how much the stability variable on the anomaly will increase with each throw.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 StabilityPerThrow = new(0.1f, 0.2f);
|
||||
}
|
||||
112
Content.Shared/Anomaly/SharedAnomalyCoreSystem.cs
Normal file
112
Content.Shared/Anomaly/SharedAnomalyCoreSystem.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This component reduces the value of the entity during decay
|
||||
/// </summary>
|
||||
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<AnomalyCoreComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<CorePoweredThrowerComponent, AttemptMeleeThrowOnHitEvent>(OnAttemptMeleeThrowOnHit);
|
||||
SubscribeLocalEvent<CorePoweredThrowerComponent, ExaminedEvent>(OnCorePoweredExamined);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<AnomalyCoreComponent> core, ref MapInitEvent args)
|
||||
{
|
||||
core.Comp.DecayMoment = _gameTiming.CurTime + TimeSpan.FromSeconds(core.Comp.TimeToDecay);
|
||||
Dirty(core, core.Comp);
|
||||
}
|
||||
|
||||
private void OnAttemptMeleeThrowOnHit(Entity<CorePoweredThrowerComponent> ent, ref AttemptMeleeThrowOnHitEvent args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
|
||||
// don't waste charges on non-anchorable non-anomalous static bodies.
|
||||
if (!HasComp<AnomalyComponent>(args.Hit)
|
||||
&& !HasComp<AnchorableComponent>(args.Hit)
|
||||
&& TryComp<PhysicsComponent>(args.Hit, out var body)
|
||||
&& body.BodyType == BodyType.Static)
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
args.Handled = true;
|
||||
|
||||
if (!_itemSlots.TryGetSlot(uid, comp.CoreSlotId, out var slot))
|
||||
return;
|
||||
|
||||
if (!TryComp<AnomalyCoreComponent>(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<CorePoweredThrowerComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
if (!_itemSlots.TryGetSlot(uid, comp.CoreSlotId, out var slot) ||
|
||||
!TryComp<AnomalyCoreComponent>(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<AnomalyCoreComponent>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<AnomalyComponent, InteractHandEvent>(OnInteractHand);
|
||||
SubscribeLocalEvent<AnomalyComponent, AttackedEvent>(OnAttacked);
|
||||
SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitStartEvent>(OnAnomalyThrowStart);
|
||||
SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitEndEvent>(OnAnomalyThrowEnd);
|
||||
|
||||
SubscribeLocalEvent<AnomalyComponent, EntityUnpausedEvent>(OnAnomalyUnpause);
|
||||
SubscribeLocalEvent<AnomalyPulsingComponent, EntityUnpausedEvent>(OnPulsingUnpause);
|
||||
@@ -49,9 +55,25 @@ public abstract class SharedAnomalySystem : EntitySystem
|
||||
|
||||
private void OnAttacked(EntityUid uid, AnomalyComponent component, AttackedEvent args)
|
||||
{
|
||||
if (HasComp<CorePoweredThrowerComponent>(args.Used))
|
||||
return;
|
||||
|
||||
DoAnomalyBurnDamage(uid, args.User, component);
|
||||
}
|
||||
|
||||
private void OnAnomalyThrowStart(Entity<AnomalyComponent> ent, ref MeleeThrowOnHitStartEvent args)
|
||||
{
|
||||
if (!TryComp<CorePoweredThrowerComponent>(args.Used, out var corePowered) || !TryComp<PhysicsComponent>(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<AnomalyComponent> ent, ref MeleeThrowOnHitEndEvent args)
|
||||
{
|
||||
_physics.SetBodyType(ent, BodyType.Static);
|
||||
}
|
||||
|
||||
public void DoAnomalyBurnDamage(EntityUid source, EntityUid target, AnomalyComponent component)
|
||||
{
|
||||
_damageable.TryChangeDamage(target, component.AnomalyContactDamage, true);
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Weapons.Melee.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for a melee weapon that throws whatever gets hit by it in a line
|
||||
/// until it hits a wall or a time limit is exhausted.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(MeleeThrowOnHitSystem))]
|
||||
[AutoGenerateComponentState]
|
||||
public sealed partial class MeleeThrowOnHitComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The speed at which hit entities should be thrown.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public float Speed = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// How long hit entities remain thrown, max.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public float Lifetime = 3f;
|
||||
|
||||
/// <summary>
|
||||
/// How long we wait to start accepting collision.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MinLifetime = 0.05f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not anchorable entities should be unanchored when hit.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public bool UnanchorOnHit;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the throwing behavior occurs by default.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public bool Enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Component used to track entities that have been yeeted by <see cref="MeleeThrowOnHitComponent"/>
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[AutoGenerateComponentState]
|
||||
[Access(typeof(MeleeThrowOnHitSystem))]
|
||||
public sealed partial class MeleeThrownComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The velocity of the throw
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public Vector2 Velocity;
|
||||
|
||||
/// <summary>
|
||||
/// How long the throw will last.
|
||||
/// </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>
|
||||
/// 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>
|
||||
[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();
|
||||
@@ -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
|
||||
/// </summary>
|
||||
public readonly EntityUid Weapon;
|
||||
|
||||
/// <summary>
|
||||
/// The direction of the attack.
|
||||
/// If null, it was a click-attack.
|
||||
/// </summary>
|
||||
public readonly Vector2? Direction;
|
||||
|
||||
/// <summary>
|
||||
/// Check if this is true before attempting to do something during a melee attack other than changing/adding bonus damage. <br/>
|
||||
/// For example, do not spend charges unless <see cref="IsHit"/> equals true.
|
||||
@@ -59,12 +66,13 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs
|
||||
/// </remarks>
|
||||
public bool IsHit = true;
|
||||
|
||||
public MeleeHitEvent(List<EntityUid> hitEntities, EntityUid user, EntityUid weapon, DamageSpecifier baseDamage)
|
||||
public MeleeHitEvent(List<EntityUid> hitEntities, EntityUid user, EntityUid weapon, DamageSpecifier baseDamage, Vector2? direction)
|
||||
{
|
||||
HitEntities = hitEntities;
|
||||
User = user;
|
||||
Weapon = weapon;
|
||||
BaseDamage = baseDamage;
|
||||
Direction = direction;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
126
Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs
Normal file
126
Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This handles <see cref="MeleeThrowOnHitComponent"/>
|
||||
/// </summary>
|
||||
public sealed class MeleeThrowOnHitSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<MeleeThrowOnHitComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<MeleeThrownComponent, ComponentStartup>(OnThrownStartup);
|
||||
SubscribeLocalEvent<MeleeThrownComponent, ComponentShutdown>(OnThrownShutdown);
|
||||
SubscribeLocalEvent<MeleeThrownComponent, StartCollideEvent>(OnStartCollide);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
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<MeleeThrownComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (TryComp<PhysicsComponent>(ent, out var body))
|
||||
_physics.SetBodyStatus(body, BodyStatus.OnGround);
|
||||
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;
|
||||
|
||||
if (_timing.CurTime < comp.MinLifetimeTime)
|
||||
return;
|
||||
|
||||
RemCompDeferred(ent, ent.Comp);
|
||||
}
|
||||
|
||||
public bool CanThrowOnHit(Entity<MeleeThrowOnHitComponent> 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<MeleeThrownComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (_timing.CurTime > comp.ThrownEndTime)
|
||||
RemCompDeferred(uid, comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<EntityUid>(), user, meleeUid, damage);
|
||||
var missEvent = new MeleeHitEvent(new List<EntityUid>(), 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<EntityUid> { target.Value }, user, meleeUid, damage);
|
||||
var hitEvent = new MeleeHitEvent(new List<EntityUid> { 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<EntityUid>(), user, meleeUid, damage);
|
||||
var missEvent = new MeleeHitEvent(new List<EntityUid>(), 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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -262,6 +262,7 @@
|
||||
- PowerCellMicroreactor
|
||||
- PowerCellHigh
|
||||
- WeaponPistolCHIMP
|
||||
- WeaponGauntletGorilla
|
||||
- SynthesizerInstrument
|
||||
- RPED
|
||||
- ClothingShoesBootsMag
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
BIN
Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/icon.png
Normal file
BIN
Resources/Textures/Objects/Weapons/Melee/gorilla.rsi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 753 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC0-1.0",
|
||||
"copyright": "Design and inhands by ricemar (discord) and icon by EmoGarbage404 (github)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user