-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;
/// </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;
- }
}
-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>
/// The moment of core decay. It is set during entity initialization.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ [AutoNetworkedField]
public TimeSpan DecayMoment;
/// <summary>
/// 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;
}
--- /dev/null
+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);
+}
--- /dev/null
+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);
+ }
+}
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;
[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!;
SubscribeLocalEvent<AnomalyComponent, InteractHandEvent>(OnInteractHand);
SubscribeLocalEvent<AnomalyComponent, AttackedEvent>(OnAttacked);
+ SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitStartEvent>(OnAnomalyThrowStart);
+ SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitEndEvent>(OnAnomalyThrowEnd);
SubscribeLocalEvent<AnomalyComponent, EntityUnpausedEvent>(OnAnomalyUnpause);
SubscribeLocalEvent<AnomalyPulsingComponent, EntityUnpausedEvent>(OnPulsingUnpause);
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);
var pulse = EnsureComp<AnomalyPulsingComponent>(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);
}
--- /dev/null
+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();
+using System.Numerics;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio;
/// </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.
/// </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;
}
}
--- /dev/null
+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);
+ }
+ }
+}
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;
// 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)
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);
// 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)
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.
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
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
- 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
- PowerCellMicroreactor
- PowerCellHigh
- WeaponPistolCHIMP
+ - WeaponGauntletGorilla
- SynthesizerInstrument
- RPED
- ClothingShoesBootsMag
types:
Slash: 1
- type: EntitySpawnAnomaly
- superCriticalSpawns:
+ superCriticalSpawns:
- ReagentSlimeSpawner
spawns:
- PuddleSparkle
- type: ItemCooldown
- type: EmitSoundOnUse #placeholder for future unical mechanic
sound:
- collection: RadiationPulse
+ collection: RadiationPulse
- type: UseDelay
delay: 2
- type: AnomalyCore
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
radius: 1.5
energy: 2.0
color: "#ffffaa"
- castShadows: false
\ No newline at end of file
+ castShadows: false
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
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
--- /dev/null
+{
+ "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
+ }
+ ]
+}