Tech Anomaly (#31764)
* A * B * C * D * Update TechAnomalySystem.cs * idle anim * Update meta.json * new animation
This commit is contained in:
61
Content.Server/Anomaly/Components/TechAnomalyComponent.cs
Normal file
61
Content.Server/Anomaly/Components/TechAnomalyComponent.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Content.Server.Anomaly.Effects;
|
||||
using Content.Shared.Destructible.Thresholds;
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Anomaly.Components;
|
||||
|
||||
[RegisterComponent, AutoGenerateComponentPause, Access(typeof(TechAnomalySystem))]
|
||||
public sealed partial class TechAnomalyComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// the distance at which random ports will bind to the anomaly. Scales with severity.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public MinMax LinkRadius = new(5, 10);
|
||||
|
||||
/// <summary>
|
||||
/// the maximum number of entities with which an anomaly is associated during pulsing. Scales with severity
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public MinMax LinkCountPerPulse = new(2, 8);
|
||||
|
||||
/// <summary>
|
||||
/// Number of linkable pairs. when supercrit, the anomaly will link random devices in the radius to each other in pairs.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int LinkCountSupercritical = 30;
|
||||
|
||||
/// <summary>
|
||||
/// port activated by pulsation of the anomaly
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<SourcePortPrototype> PulsePort = "Pulse";
|
||||
|
||||
/// <summary>
|
||||
/// A port that activates every few seconds of an anomaly's lifetime
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<SourcePortPrototype> TimerPort = "Timer";
|
||||
|
||||
/// <summary>
|
||||
/// Chance of emag the device, when supercrit
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float EmagSupercritProbability = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// A prototype beam shot into devices when pulsed
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntProtoId LinkBeamProto = "AnomalyTechBeam";
|
||||
|
||||
/// <summary>
|
||||
/// time until the next activation of the timer ports
|
||||
/// </summary>
|
||||
[DataField, AutoPausedField]
|
||||
public TimeSpan NextTimer = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public float TimerFrequency = 3f;
|
||||
}
|
||||
125
Content.Server/Anomaly/Effects/TechAnomalySystem.cs
Normal file
125
Content.Server/Anomaly/Effects/TechAnomalySystem.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Content.Server.Anomaly.Components;
|
||||
using Content.Server.Beam;
|
||||
using Content.Server.DeviceLinking.Systems;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Anomaly.Effects;
|
||||
|
||||
public sealed class TechAnomalySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DeviceLinkSystem _signal = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly BeamSystem _beam = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly EmagSystem _emag = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TechAnomalyComponent, AnomalyPulseEvent>(OnPulse);
|
||||
SubscribeLocalEvent<TechAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
|
||||
SubscribeLocalEvent<TechAnomalyComponent, AnomalyStabilityChangedEvent>(OnStabilityChanged);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<TechAnomalyComponent, AnomalyComponent>();
|
||||
while (query.MoveNext(out var uid, out var tech, out var anom))
|
||||
{
|
||||
if (_timing.CurTime < tech.NextTimer)
|
||||
return;
|
||||
|
||||
tech.NextTimer = _timing.CurTime + TimeSpan.FromSeconds(tech.TimerFrequency * anom.Stability);
|
||||
|
||||
_signal.InvokePort(uid, tech.TimerPort);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStabilityChanged(Entity<TechAnomalyComponent> tech, ref AnomalyStabilityChangedEvent args)
|
||||
{
|
||||
var links = MathHelper.Lerp(tech.Comp.LinkCountPerPulse.Min, tech.Comp.LinkCountPerPulse.Max, args.Severity);
|
||||
CreateNewRandomLink(tech, (int)links);
|
||||
}
|
||||
|
||||
private void CreateNewRandomLink(Entity<TechAnomalyComponent> tech, int count)
|
||||
{
|
||||
if (!TryComp<AnomalyComponent>(tech, out var anomaly))
|
||||
return;
|
||||
if (!TryComp<DeviceLinkSourceComponent>(tech, out var sourceComp))
|
||||
return;
|
||||
|
||||
var range = MathHelper.Lerp(tech.Comp.LinkRadius.Min, tech.Comp.LinkRadius.Max, anomaly.Severity);
|
||||
|
||||
var devices = _lookup.GetEntitiesInRange<DeviceLinkSinkComponent>(Transform(tech).Coordinates, range);
|
||||
if (devices.Count < 1)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var device = _random.Pick(devices);
|
||||
CreateNewLink(tech, (tech, sourceComp), device);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateNewLink(Entity<TechAnomalyComponent> tech, Entity<DeviceLinkSourceComponent> source, Entity<DeviceLinkSinkComponent> target)
|
||||
{
|
||||
var sourcePort = _random.Pick(source.Comp.Ports);
|
||||
var sinkPort = _random.Pick(target.Comp.Ports);
|
||||
|
||||
_signal.SaveLinks(null, source, target,new()
|
||||
{
|
||||
(sourcePort, sinkPort),
|
||||
});
|
||||
_beam.TryCreateBeam(source, target, tech.Comp.LinkBeamProto);
|
||||
}
|
||||
|
||||
private void OnSupercritical(Entity<TechAnomalyComponent> tech, ref AnomalySupercriticalEvent args)
|
||||
{
|
||||
// We remove the component so that the anomaly does not bind itself to other devices before self destroy.
|
||||
RemComp<DeviceLinkSourceComponent>(tech);
|
||||
|
||||
var sources =
|
||||
_lookup.GetEntitiesInRange<DeviceLinkSourceComponent>(Transform(tech).Coordinates,
|
||||
tech.Comp.LinkRadius.Max);
|
||||
|
||||
var sinks =
|
||||
_lookup.GetEntitiesInRange<DeviceLinkSinkComponent>(Transform(tech).Coordinates,
|
||||
tech.Comp.LinkRadius.Max);
|
||||
|
||||
for (var i = 0; i < tech.Comp.LinkCountSupercritical; i++)
|
||||
{
|
||||
if (sources.Count < 1)
|
||||
return;
|
||||
|
||||
if (sinks.Count < 1)
|
||||
return;
|
||||
|
||||
var source = _random.Pick(sources);
|
||||
sources.Remove(source);
|
||||
|
||||
var sink = _random.Pick(sinks);
|
||||
sinks.Remove(sink);
|
||||
|
||||
if (_random.Prob(tech.Comp.EmagSupercritProbability))
|
||||
{
|
||||
_emag.DoEmagEffect(tech, source);
|
||||
_emag.DoEmagEffect(tech, sink);
|
||||
}
|
||||
|
||||
CreateNewLink(tech, source, sink);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPulse(Entity<TechAnomalyComponent> tech, ref AnomalyPulseEvent args)
|
||||
{
|
||||
_signal.InvokePort(tech, tech.Comp.PulsePort);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user