Tech Anomaly (#31764)

* A

* B

* C

* D

* Update TechAnomalySystem.cs

* idle anim

* Update meta.json

* new animation
This commit is contained in:
Ed
2024-09-06 10:24:17 +03:00
committed by GitHub
parent 4bc8d8e7b8
commit 1a4d58816f
18 changed files with 410 additions and 0 deletions

View 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;
}

View 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);
}
}

View File

@@ -20,6 +20,7 @@
- AnomalyLiquid - AnomalyLiquid
- AnomalyFlora - AnomalyFlora
- AnomalyShadow - AnomalyShadow
- AnomalyTech
chance: 1 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. 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.

View File

@@ -875,3 +875,63 @@
damage: damage:
types: types:
Cold: 10 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

View File

@@ -168,6 +168,19 @@
color: "#793a80" color: "#793a80"
castShadows: false 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 # Inert cores
- type: entity - type: entity
@@ -314,3 +327,16 @@
energy: 2.0 energy: 2.0
color: "#793a80" color: "#793a80"
castShadows: false 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View 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
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

View File

@@ -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
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -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
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB