Tech Anomaly (#31764)
* A * B * C * D * Update TechAnomalySystem.cs * idle anim * Update meta.json * new animation
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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
- AnomalyLiquid
|
||||
- AnomalyFlora
|
||||
- AnomalyShadow
|
||||
- AnomalyTech
|
||||
chance: 1
|
||||
offset: 0.15 # not to put it higher. The anomaly sychnronizer looks for anomalies within this radius, and if the radius is higher, the anomaly can be attracted from a neighboring tile.
|
||||
|
||||
|
||||
@@ -875,3 +875,63 @@
|
||||
damage:
|
||||
types:
|
||||
Cold: 10
|
||||
|
||||
- type: entity
|
||||
id: AnomalyTech
|
||||
parent: BaseAnomaly
|
||||
suffix: Tech
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Structures/Specific/Anomalies/tech_anom.rsi
|
||||
layers:
|
||||
- state: bg
|
||||
map: ["enum.AnomalyVisualLayers.Base"]
|
||||
- state: bg_powered
|
||||
map: ["enum.AnomalyVisualLayers.Animated"]
|
||||
visible: false
|
||||
shader: unshaded
|
||||
- state: part1
|
||||
- state: part2
|
||||
- state: part3
|
||||
- state: part4
|
||||
- type: PointLight
|
||||
radius: 6.5
|
||||
energy: 3.5
|
||||
color: "#56c1e8"
|
||||
- type: Anomaly
|
||||
corePrototype: AnomalyCoreTech
|
||||
coreInertPrototype: AnomalyCoreTechInert
|
||||
minPulseLength: 60
|
||||
maxPulseLength: 120
|
||||
- type: TechAnomaly
|
||||
- type: DeviceLinkSource
|
||||
ports:
|
||||
- Pulse
|
||||
- Timer
|
||||
|
||||
- type: entity
|
||||
id: AnomalyTechBeam
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: /Textures/Effects/techanom_beam.rsi
|
||||
drawdepth: Effects
|
||||
layers:
|
||||
- state: beam
|
||||
shader: unshaded
|
||||
color: "#21c4ff"
|
||||
- type: Physics
|
||||
canCollide: false
|
||||
- type: PointLight
|
||||
enabled: true
|
||||
color: "#4080FF"
|
||||
radius: 3.5
|
||||
softness: 1
|
||||
autoRot: true
|
||||
castShadows: false
|
||||
- type: Beam
|
||||
- type: TimedDespawn
|
||||
lifetime: 2.3
|
||||
- type: Tag
|
||||
tags:
|
||||
- HideContextMenu
|
||||
@@ -168,6 +168,19 @@
|
||||
color: "#793a80"
|
||||
castShadows: false
|
||||
|
||||
- type: entity
|
||||
parent: BaseAnomalyCore
|
||||
id: AnomalyCoreTech
|
||||
suffix: Tech
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Structures/Specific/Anomalies/Cores/tech_core.rsi
|
||||
- type: PointLight
|
||||
radius: 1.5
|
||||
energy: 2.0
|
||||
color: "#56c1e8"
|
||||
castShadows: false
|
||||
|
||||
# Inert cores
|
||||
|
||||
- type: entity
|
||||
@@ -314,3 +327,16 @@
|
||||
energy: 2.0
|
||||
color: "#793a80"
|
||||
castShadows: false
|
||||
|
||||
- type: entity
|
||||
parent: BaseAnomalyInertCore
|
||||
id: AnomalyCoreTechInert
|
||||
suffix: Tech, Inert
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Structures/Specific/Anomalies/Cores/tech_core.rsi
|
||||
- type: PointLight
|
||||
radius: 1.5
|
||||
energy: 2.0
|
||||
color: "#56c1e8"
|
||||
castShadows: false
|
||||
|
||||
BIN
Resources/Textures/Effects/techanom_beam.rsi/beam.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
22
Resources/Textures/Effects/techanom_beam.rsi/meta.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC0-1.0",
|
||||
"copyright": "Created by TheShuEd (Github)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "beam",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 271 B |
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC0-1.0",
|
||||
"copyright": "Created by Jaraten (github) for ss14",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "core"
|
||||
},
|
||||
{
|
||||
"name": "pulse",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 201 B |
|
After Width: | Height: | Size: 226 B |
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC0-1.0",
|
||||
"copyright": "Created by Jaraten (github) for ss14",
|
||||
"size": {
|
||||
"x": 48,
|
||||
"y": 48
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "bg"
|
||||
},
|
||||
{
|
||||
"name": "bg_powered",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "part1",
|
||||
"delays": [
|
||||
[
|
||||
1.8,
|
||||
0.2,
|
||||
0.8,
|
||||
0.4,
|
||||
0.2,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "part2",
|
||||
"delays": [
|
||||
[
|
||||
1.1,
|
||||
0.1,
|
||||
0.8,
|
||||
0.5,
|
||||
0.2,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "part3",
|
||||
"delays": [
|
||||
[
|
||||
1.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0.1,
|
||||
0.5,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "part4",
|
||||
"delays": [
|
||||
[
|
||||
1.8,
|
||||
0.1,
|
||||
0.9,
|
||||
0.4,
|
||||
1.0,
|
||||
0.4
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pulse",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 756 B |
|
After Width: | Height: | Size: 700 B |
|
After Width: | Height: | Size: 771 B |
|
After Width: | Height: | Size: 667 B |
|
After Width: | Height: | Size: 2.4 KiB |