Anomaly events & science point gen tweaks (#13590)
This commit is contained in:
@@ -74,7 +74,7 @@ public sealed partial class AnomalySystem
|
|||||||
UpdateGeneratorUi(uid, component);
|
UpdateGeneratorUi(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpawnOnRandomGridLocation(EntityUid grid, string toSpawn)
|
public void SpawnOnRandomGridLocation(EntityUid grid, string toSpawn)
|
||||||
{
|
{
|
||||||
if (!TryComp<MapGridComponent>(grid, out var gridComp))
|
if (!TryComp<MapGridComponent>(grid, out var gridComp))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public sealed partial class AnomalySystem
|
|||||||
component.TokenSource = null;
|
component.TokenSource = null;
|
||||||
|
|
||||||
Audio.PlayPvs(component.CompleteSound, uid);
|
Audio.PlayPvs(component.CompleteSound, uid);
|
||||||
_popup.PopupEntity(Loc.GetString("anomaly-scanner-component-scan-complete"), uid);
|
Popup.PopupEntity(Loc.GetString("anomaly-scanner-component-scan-complete"), uid);
|
||||||
UpdateScannerWithNewAnomaly(uid, args.Anomaly, component);
|
UpdateScannerWithNewAnomaly(uid, args.Anomaly, component);
|
||||||
|
|
||||||
if (TryComp<ActorComponent>(args.User, out var actor))
|
if (TryComp<ActorComponent>(args.User, out var actor))
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public sealed partial class AnomalySystem
|
|||||||
SubscribeLocalEvent<AnomalyVesselComponent, ComponentShutdown>(OnVesselShutdown);
|
SubscribeLocalEvent<AnomalyVesselComponent, ComponentShutdown>(OnVesselShutdown);
|
||||||
SubscribeLocalEvent<AnomalyVesselComponent, MapInitEvent>(OnVesselMapInit);
|
SubscribeLocalEvent<AnomalyVesselComponent, MapInitEvent>(OnVesselMapInit);
|
||||||
SubscribeLocalEvent<AnomalyVesselComponent, RefreshPartsEvent>(OnRefreshParts);
|
SubscribeLocalEvent<AnomalyVesselComponent, RefreshPartsEvent>(OnRefreshParts);
|
||||||
|
SubscribeLocalEvent<AnomalyVesselComponent, UpgradeExamineEvent>(OnUpgradeExamine);
|
||||||
SubscribeLocalEvent<AnomalyVesselComponent, InteractUsingEvent>(OnVesselInteractUsing);
|
SubscribeLocalEvent<AnomalyVesselComponent, InteractUsingEvent>(OnVesselInteractUsing);
|
||||||
SubscribeLocalEvent<AnomalyVesselComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<AnomalyVesselComponent, ExaminedEvent>(OnExamined);
|
||||||
SubscribeLocalEvent<AnomalyVesselComponent, ResearchServerGetPointsPerSecondEvent>(OnVesselGetPointsPerSecond);
|
SubscribeLocalEvent<AnomalyVesselComponent, ResearchServerGetPointsPerSecondEvent>(OnVesselGetPointsPerSecond);
|
||||||
@@ -59,6 +60,11 @@ public sealed partial class AnomalySystem
|
|||||||
component.PointMultiplier = MathF.Pow(component.PartRatingPointModifier, modifierRating);
|
component.PointMultiplier = MathF.Pow(component.PartRatingPointModifier, modifierRating);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnUpgradeExamine(EntityUid uid, AnomalyVesselComponent component, UpgradeExamineEvent args)
|
||||||
|
{
|
||||||
|
args.AddPercentageUpgrade("anomaly-vessel-component-upgrade-output", component.PointMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnVesselInteractUsing(EntityUid uid, AnomalyVesselComponent component, InteractUsingEvent args)
|
private void OnVesselInteractUsing(EntityUid uid, AnomalyVesselComponent component, InteractUsingEvent args)
|
||||||
{
|
{
|
||||||
if (component.Anomaly != null ||
|
if (component.Anomaly != null ||
|
||||||
@@ -74,7 +80,7 @@ public sealed partial class AnomalySystem
|
|||||||
component.Anomaly = scanner.ScannedAnomaly;
|
component.Anomaly = scanner.ScannedAnomaly;
|
||||||
anomalyComponent.ConnectedVessel = uid;
|
anomalyComponent.ConnectedVessel = uid;
|
||||||
UpdateVesselAppearance(uid, component);
|
UpdateVesselAppearance(uid, component);
|
||||||
_popup.PopupEntity(Loc.GetString("anomaly-vessel-component-anomaly-assigned"), uid);
|
Popup.PopupEntity(Loc.GetString("anomaly-vessel-component-anomaly-assigned"), uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnVesselGetPointsPerSecond(EntityUid uid, AnomalyVesselComponent component, ref ResearchServerGetPointsPerSecondEvent args)
|
private void OnVesselGetPointsPerSecond(EntityUid uid, AnomalyVesselComponent component, ref ResearchServerGetPointsPerSecondEvent args)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Content.Server.Audio;
|
|||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
using Content.Server.Explosion.EntitySystems;
|
using Content.Server.Explosion.EntitySystems;
|
||||||
using Content.Server.Materials;
|
using Content.Server.Materials;
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Shared.Anomaly;
|
using Content.Shared.Anomaly;
|
||||||
using Content.Shared.Anomaly.Components;
|
using Content.Shared.Anomaly.Components;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -24,7 +23,6 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
|||||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||||
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
||||||
[Dependency] private readonly MaterialStorageSystem _material = default!;
|
[Dependency] private readonly MaterialStorageSystem _material = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
|
||||||
[Dependency] private readonly TransformSystem _transform = default!;
|
[Dependency] private readonly TransformSystem _transform = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||||
|
|
||||||
@@ -101,9 +99,9 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
|||||||
|
|
||||||
var multiplier = 1f;
|
var multiplier = 1f;
|
||||||
if (component.Stability > component.GrowthThreshold)
|
if (component.Stability > component.GrowthThreshold)
|
||||||
multiplier = 1.25f; //more points for unstable
|
multiplier = component.GrowingPointMultiplier; //more points for unstable
|
||||||
else if (component.Stability < component.DecayThreshold)
|
else if (component.Stability < component.DecayThreshold)
|
||||||
multiplier = 0.75f; //less points if it's dying
|
multiplier = component.DecayingPointMultiplier; //less points if it's dying
|
||||||
|
|
||||||
//penalty of up to 50% based on health
|
//penalty of up to 50% based on health
|
||||||
multiplier *= MathF.Pow(1.5f, component.Health) - 0.5f;
|
multiplier *= MathF.Pow(1.5f, component.Health) - 0.5f;
|
||||||
|
|||||||
51
Content.Server/StationEvents/Events/AnomalySpawn.cs
Normal file
51
Content.Server/StationEvents/Events/AnomalySpawn.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Server.Anomaly;
|
||||||
|
using Content.Server.Station.Components;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.StationEvents.Events;
|
||||||
|
|
||||||
|
public sealed class AnomalySpawn : StationEventSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly AnomalySystem _anomaly = default!;
|
||||||
|
|
||||||
|
public override string Prototype => "AnomalySpawn";
|
||||||
|
|
||||||
|
public readonly string AnomalySpawnerPrototype = "RandomAnomalySpawner";
|
||||||
|
|
||||||
|
public override void Added()
|
||||||
|
{
|
||||||
|
base.Added();
|
||||||
|
|
||||||
|
var str = Loc.GetString("anomaly-spawn-event-announcement",
|
||||||
|
("sighting", Loc.GetString($"anomaly-spawn-sighting-{_random.Next(1, 6)}")));
|
||||||
|
ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Started()
|
||||||
|
{
|
||||||
|
base.Started();
|
||||||
|
|
||||||
|
if (StationSystem.Stations.Count == 0)
|
||||||
|
return; // No stations
|
||||||
|
var chosenStation = RobustRandom.Pick(StationSystem.Stations.ToList());
|
||||||
|
if (!TryComp<StationDataComponent>(chosenStation, out var stationData))
|
||||||
|
return;
|
||||||
|
|
||||||
|
EntityUid? grid = null;
|
||||||
|
foreach (var g in stationData.Grids.Where(HasComp<BecomesStationComponent>))
|
||||||
|
{
|
||||||
|
grid = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid is not { })
|
||||||
|
return;
|
||||||
|
|
||||||
|
var amountToSpawn = Math.Max(1, (int) MathF.Round(GetSeverityModifier() / 2));
|
||||||
|
for (var i = 0; i < amountToSpawn; i++)
|
||||||
|
{
|
||||||
|
_anomaly.SpawnOnRandomGridLocation(grid.Value, AnomalySpawnerPrototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,9 @@ public sealed class BluespaceArtifact : StationEventSystem
|
|||||||
public override void Started()
|
public override void Started()
|
||||||
{
|
{
|
||||||
base.Started();
|
base.Started();
|
||||||
|
var amountToSpawn = Math.Max(1, (int) MathF.Round(GetSeverityModifier() / 1.5f));
|
||||||
|
for (var i = 0; i < amountToSpawn; i++)
|
||||||
|
{
|
||||||
if (!TryFindRandomTile(out _, out _, out _, out var coords))
|
if (!TryFindRandomTile(out _, out _, out _, out var coords))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -43,4 +45,5 @@ public sealed class BluespaceArtifact : StationEventSystem
|
|||||||
|
|
||||||
Sawmill.Info($"Spawning random artifact at {coords}");
|
Sawmill.Info($"Spawning random artifact at {coords}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,25 @@ public sealed class ArtifactComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("lastActivationTime", customTypeSerializer: typeof(TimespanSerializer))]
|
[DataField("lastActivationTime", customTypeSerializer: typeof(TimespanSerializer))]
|
||||||
public TimeSpan LastActivationTime;
|
public TimeSpan LastActivationTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base price of each node for an artifact
|
||||||
|
/// </summary>
|
||||||
|
[DataField("pricePerNode")]
|
||||||
|
public int PricePerNode = 500;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base amount of research points for each artifact node.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("pointsPerNode")]
|
||||||
|
public int PointsPerNode = 5000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A multiplier that is raised to the power of the average depth of a node.
|
||||||
|
/// Used for calculating the research point value of an artifact node.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("pointDangerMultiplier")]
|
||||||
|
public float PointDangerMultiplier = 1.35f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ public sealed partial class ArtifactSystem : EntitySystem
|
|||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
private const int PricePerNode = 500;
|
|
||||||
private const int PointsPerNode = 5000;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -34,7 +31,7 @@ public sealed partial class ArtifactSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnInit(EntityUid uid, ArtifactComponent component, MapInitEvent args)
|
private void OnInit(EntityUid uid, ArtifactComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
RandomizeArtifact(component);
|
RandomizeArtifact(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -52,7 +49,7 @@ public sealed partial class ArtifactSystem : EntitySystem
|
|||||||
if (component.NodeTree == null)
|
if (component.NodeTree == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var price = component.NodeTree.AllNodes.Sum(GetNodePrice);
|
var price = component.NodeTree.AllNodes.Sum(x => GetNodePrice(x, component));
|
||||||
|
|
||||||
// 25% bonus for fully exploring every node.
|
// 25% bonus for fully exploring every node.
|
||||||
var fullyExploredBonus = component.NodeTree.AllNodes.Any(x => !x.Triggered) ? 1 : 1.25f;
|
var fullyExploredBonus = component.NodeTree.AllNodes.Any(x => !x.Triggered) ? 1 : 1.25f;
|
||||||
@@ -60,7 +57,7 @@ public sealed partial class ArtifactSystem : EntitySystem
|
|||||||
args.Price =+ price * fullyExploredBonus;
|
args.Price =+ price * fullyExploredBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetNodePrice(ArtifactNode node)
|
private float GetNodePrice(ArtifactNode node, ArtifactComponent component)
|
||||||
{
|
{
|
||||||
if (!node.Discovered) //no money for undiscovered nodes.
|
if (!node.Discovered) //no money for undiscovered nodes.
|
||||||
return 0;
|
return 0;
|
||||||
@@ -70,7 +67,7 @@ public sealed partial class ArtifactSystem : EntitySystem
|
|||||||
//the danger is the average of node depth, effect danger, and trigger danger.
|
//the danger is the average of node depth, effect danger, and trigger danger.
|
||||||
var nodeDanger = (node.Depth + node.Effect.TargetDepth + node.Trigger.TargetDepth) / 3;
|
var nodeDanger = (node.Depth + node.Effect.TargetDepth + node.Trigger.TargetDepth) / 3;
|
||||||
|
|
||||||
var price = MathF.Pow(2f, nodeDanger) * PricePerNode * priceMultiplier;
|
var price = MathF.Pow(2f, nodeDanger) * component.PricePerNode * priceMultiplier;
|
||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,43 +75,52 @@ public sealed partial class ArtifactSystem : EntitySystem
|
|||||||
/// Calculates how many research points the artifact is worht
|
/// Calculates how many research points the artifact is worht
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Rebalance this shit at some point. Definitely OP.
|
/// General balancing (for fully unlocked artifacts):
|
||||||
|
/// Simple (1-2 Nodes): ~10K
|
||||||
|
/// Medium (5-8 Nodes): ~30-40K
|
||||||
|
/// Complex (7-12 Nodes): ~60-80K
|
||||||
|
///
|
||||||
|
/// Simple artifacts should be enough to unlock a few techs.
|
||||||
|
/// Medium should get you partway through a tree.
|
||||||
|
/// Complex should get you through a full tree and then some.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public int GetResearchPointValue(EntityUid uid, ArtifactComponent? component = null)
|
public int GetResearchPointValue(EntityUid uid, ArtifactComponent? component = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component) || component.NodeTree == null)
|
if (!Resolve(uid, ref component) || component.NodeTree == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var sumValue = component.NodeTree.AllNodes.Sum(GetNodePointValue);
|
var sumValue = component.NodeTree.AllNodes.Sum(n => GetNodePointValue(n, component));
|
||||||
var fullyExploredBonus = component.NodeTree.AllNodes.Any(x => !x.Triggered) ? 1 : 1.25f;
|
var fullyExploredBonus = component.NodeTree.AllNodes.Any(x => !x.Triggered) ? 1 : 1.25f;
|
||||||
|
|
||||||
var pointValue = (int) (sumValue * fullyExploredBonus);
|
var pointValue = (int) (sumValue * fullyExploredBonus);
|
||||||
return pointValue;
|
return pointValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetNodePointValue(ArtifactNode node)
|
/// <summary>
|
||||||
|
/// Gets the point value for an individual node
|
||||||
|
/// </summary>
|
||||||
|
private float GetNodePointValue(ArtifactNode node, ArtifactComponent component)
|
||||||
{
|
{
|
||||||
if (!node.Discovered)
|
if (!node.Discovered)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var valueDeduction = !node.Triggered ? 0.5f : 1;
|
var valueDeduction = !node.Triggered ? 0.25f : 1;
|
||||||
var nodeDanger = (node.Depth + node.Effect.TargetDepth + node.Trigger.TargetDepth) / 3;
|
var nodeDanger = (node.Depth + node.Effect.TargetDepth + node.Trigger.TargetDepth) / 3;
|
||||||
|
return component.PointsPerNode * MathF.Pow(component.PointDangerMultiplier, nodeDanger) * valueDeduction;
|
||||||
return (nodeDanger+1) * PointsPerNode * valueDeduction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Randomize a given artifact.
|
/// Randomize a given artifact.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public void RandomizeArtifact(ArtifactComponent component)
|
public void RandomizeArtifact(EntityUid uid, ArtifactComponent component)
|
||||||
{
|
{
|
||||||
var nodeAmount = _random.Next(component.NodesMin, component.NodesMax);
|
var nodeAmount = _random.Next(component.NodesMin, component.NodesMax);
|
||||||
|
|
||||||
component.NodeTree = new ArtifactTree();
|
component.NodeTree = new ArtifactTree();
|
||||||
|
|
||||||
GenerateArtifactNodeTree(component.Owner, ref component.NodeTree, nodeAmount);
|
GenerateArtifactNodeTree(uid, ref component.NodeTree, nodeAmount);
|
||||||
EnterNode(component.Owner, ref component.NodeTree.StartNode, component);
|
EnterNode(uid, ref component.NodeTree.StartNode, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -166,21 +172,21 @@ public sealed partial class ArtifactSystem : EntitySystem
|
|||||||
component.CurrentNode.Triggered = true;
|
component.CurrentNode.Triggered = true;
|
||||||
if (component.CurrentNode.Edges.Any())
|
if (component.CurrentNode.Edges.Any())
|
||||||
{
|
{
|
||||||
var newNode = GetNewNode(component);
|
var newNode = GetNewNode(uid, component);
|
||||||
if (newNode == null)
|
if (newNode == null)
|
||||||
return;
|
return;
|
||||||
EnterNode(uid, ref newNode, component);
|
EnterNode(uid, ref newNode, component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArtifactNode? GetNewNode(ArtifactComponent component)
|
private ArtifactNode? GetNewNode(EntityUid uid, ArtifactComponent component)
|
||||||
{
|
{
|
||||||
if (component.CurrentNode == null)
|
if (component.CurrentNode == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var allNodes = component.CurrentNode.Edges;
|
var allNodes = component.CurrentNode.Edges;
|
||||||
|
|
||||||
if (TryComp<BiasedArtifactComponent>(component.Owner, out var bias) &&
|
if (TryComp<BiasedArtifactComponent>(uid, out var bias) &&
|
||||||
TryComp<TraversalDistorterComponent>(bias.Provider, out var trav) &&
|
TryComp<TraversalDistorterComponent>(bias.Provider, out var trav) &&
|
||||||
_random.Prob(trav.BiasChance) &&
|
_random.Prob(trav.BiasChance) &&
|
||||||
this.IsPowered(bias.Provider, EntityManager))
|
this.IsPowered(bias.Provider, EntityManager))
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Robust.Shared.Audio;
|
using Content.Shared.Damage;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
@@ -104,6 +105,13 @@ public sealed class AnomalyComponent : Component
|
|||||||
[DataField("pulseVariation")]
|
[DataField("pulseVariation")]
|
||||||
public float PulseVariation = .1f;
|
public float PulseVariation = .1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The largest value by which the anomaly will vary in stability for each pulse.
|
||||||
|
/// In simple terms, every pulse, stability changes from a range of -this_value to this_value
|
||||||
|
/// </summary>
|
||||||
|
[DataField("pulseStabilityVariation")]
|
||||||
|
public float PulseStabilityVariation = 0.05f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sound played when an anomaly pulses
|
/// The sound played when an anomaly pulses
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -200,8 +208,36 @@ public sealed class AnomalyComponent : Component
|
|||||||
/// This doesn't include the point bonus for being unstable.
|
/// This doesn't include the point bonus for being unstable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("maxPointsPerSecond")]
|
[DataField("maxPointsPerSecond")]
|
||||||
public int MaxPointsPerSecond = 100;
|
public int MaxPointsPerSecond = 65;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The multiplier applied to the point value for the
|
||||||
|
/// anomaly being above the <see cref="GrowthThreshold"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("growingPointMultiplier")]
|
||||||
|
public float GrowingPointMultiplier = 1.2f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The multiplier applied to the point value for the
|
||||||
|
/// anomaly being below the <see cref="DecayThreshold"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("decayingPointMultiplier")]
|
||||||
|
public float DecayingPointMultiplier = 0.75f;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of damage dealt when either a player touches the anomaly
|
||||||
|
/// directly or by hitting the anomaly.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("anomalyContactDamage", required: true)]
|
||||||
|
public DamageSpecifier AnomalyContactDamage = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sound effect played when a player
|
||||||
|
/// burns themselves on an anomaly via contact.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("anomalyContactDamageSound")]
|
||||||
|
public SoundSpecifier AnomalyContactDamageSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Anomaly.Components;
|
using Content.Shared.Anomaly.Components;
|
||||||
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
@@ -15,8 +19,10 @@ public abstract class SharedAnomalySystem : EntitySystem
|
|||||||
[Dependency] private readonly INetManager _net = default!;
|
[Dependency] private readonly INetManager _net = default!;
|
||||||
[Dependency] protected readonly IRobustRandom Random = default!;
|
[Dependency] protected readonly IRobustRandom Random = default!;
|
||||||
[Dependency] protected readonly ISharedAdminLogManager Log = default!;
|
[Dependency] protected readonly ISharedAdminLogManager Log = default!;
|
||||||
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
||||||
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
||||||
|
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -26,6 +32,8 @@ public abstract class SharedAnomalySystem : EntitySystem
|
|||||||
SubscribeLocalEvent<AnomalyComponent, ComponentHandleState>(OnAnomalyHandleState);
|
SubscribeLocalEvent<AnomalyComponent, ComponentHandleState>(OnAnomalyHandleState);
|
||||||
SubscribeLocalEvent<AnomalySupercriticalComponent, ComponentGetState>(OnSupercriticalGetState);
|
SubscribeLocalEvent<AnomalySupercriticalComponent, ComponentGetState>(OnSupercriticalGetState);
|
||||||
SubscribeLocalEvent<AnomalySupercriticalComponent, ComponentHandleState>(OnSupercriticalHandleState);
|
SubscribeLocalEvent<AnomalySupercriticalComponent, ComponentHandleState>(OnSupercriticalHandleState);
|
||||||
|
SubscribeLocalEvent<AnomalyComponent, InteractHandEvent>(OnInteractHand);
|
||||||
|
SubscribeLocalEvent<AnomalyComponent, AttackedEvent>(OnAttacked);
|
||||||
|
|
||||||
SubscribeLocalEvent<AnomalyComponent, EntityUnpausedEvent>(OnAnomalyUnpause);
|
SubscribeLocalEvent<AnomalyComponent, EntityUnpausedEvent>(OnAnomalyUnpause);
|
||||||
SubscribeLocalEvent<AnomalyPulsingComponent, EntityUnpausedEvent>(OnPulsingUnpause);
|
SubscribeLocalEvent<AnomalyPulsingComponent, EntityUnpausedEvent>(OnPulsingUnpause);
|
||||||
@@ -69,6 +77,26 @@ public abstract class SharedAnomalySystem : EntitySystem
|
|||||||
component.SupercriticalDuration = state.Duration;
|
component.SupercriticalDuration = state.Duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnInteractHand(EntityUid uid, AnomalyComponent component, InteractHandEvent args)
|
||||||
|
{
|
||||||
|
DoAnomalyBurnDamage(uid, args.User, component);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAttacked(EntityUid uid, AnomalyComponent component, AttackedEvent args)
|
||||||
|
{
|
||||||
|
DoAnomalyBurnDamage(uid, args.User, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoAnomalyBurnDamage(EntityUid source, EntityUid target, AnomalyComponent component)
|
||||||
|
{
|
||||||
|
_damageable.TryChangeDamage(target, component.AnomalyContactDamage, true);
|
||||||
|
if (!Timing.IsFirstTimePredicted || _net.IsServer)
|
||||||
|
return;
|
||||||
|
Audio.PlayPvs(component.AnomalyContactDamageSound, source);
|
||||||
|
Popup.PopupEntity(Loc.GetString("anomaly-component-contact-damage"), target, target);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnAnomalyUnpause(EntityUid uid, AnomalyComponent component, ref EntityUnpausedEvent args)
|
private void OnAnomalyUnpause(EntityUid uid, AnomalyComponent component, ref EntityUnpausedEvent args)
|
||||||
{
|
{
|
||||||
component.NextPulseTime += args.PausedTime;
|
component.NextPulseTime += args.PausedTime;
|
||||||
@@ -100,12 +128,9 @@ public abstract class SharedAnomalySystem : EntitySystem
|
|||||||
{
|
{
|
||||||
ChangeAnomalySeverity(uid, GetSeverityIncreaseFromGrowth(component), component);
|
ChangeAnomalySeverity(uid, GetSeverityIncreaseFromGrowth(component), component);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
var stability = Random.NextFloat(-component.PulseStabilityVariation, component.PulseStabilityVariation);
|
||||||
// just doing this to update the scanner ui
|
ChangeAnomalyStability(uid, stability, component);
|
||||||
// as they hook into these events
|
|
||||||
ChangeAnomalySeverity(uid, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Add(LogType.Anomaly, LogImpact.Medium, $"Anomaly {ToPrettyString(uid)} pulsed with severity {component.Severity}.");
|
Log.Add(LogType.Anomaly, LogImpact.Medium, $"Anomaly {ToPrettyString(uid)} pulsed with severity {component.Severity}.");
|
||||||
if (_net.IsServer)
|
if (_net.IsServer)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
anomaly-component-contact-damage = The anomaly sears off your skin!
|
||||||
|
|
||||||
anomaly-vessel-component-anomaly-assigned = Anomaly assigned to vessel.
|
anomaly-vessel-component-anomaly-assigned = Anomaly assigned to vessel.
|
||||||
anomaly-vessel-component-not-assigned = This vessel is not assigned to any anomaly. Try using a scanner on it.
|
anomaly-vessel-component-not-assigned = This vessel is not assigned to any anomaly. Try using a scanner on it.
|
||||||
anomaly-vessel-component-assigned = This vessel is currently assigned to an anomaly.
|
anomaly-vessel-component-assigned = This vessel is currently assigned to an anomaly.
|
||||||
|
anomaly-vessel-component-upgrade-output = point output
|
||||||
|
|
||||||
anomaly-particles-delta = Delta particles
|
anomaly-particles-delta = Delta particles
|
||||||
anomaly-particles-epsilon = Epsilon particles
|
anomaly-particles-epsilon = Epsilon particles
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
anomaly-spawn-event-announcement = Our readings have detected a dangerous interspacial anomaly. Please inform the research team of { $sighting }.
|
||||||
|
|
||||||
|
anomaly-spawn-sighting-1 = low pulsating sounds heard throughout the station
|
||||||
|
anomaly-spawn-sighting-2 = strange sources of light
|
||||||
|
anomaly-spawn-sighting-3 = inexplicable shapes
|
||||||
|
anomaly-spawn-sighting-4 = forms causing severe mental distress
|
||||||
|
anomaly-spawn-sighting-5 = strange effects on the local environment
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
bluespace-artifact-event-announcement = Our readings have detected an incoming anomalous object. Please inform the research team of { $sighting }.
|
bluespace-artifact-event-announcement = Our readings have detected an incoming object of alien origin. Please inform the research team of { $sighting }.
|
||||||
|
|
||||||
bluespace-artifact-sighting-1 = bright flashes of light
|
bluespace-artifact-sighting-1 = bright flashes of light
|
||||||
bluespace-artifact-sighting-2 = strange sounds coming from maintenance tunnels
|
bluespace-artifact-sighting-2 = strange sounds coming from maintenance tunnels
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
collection: RadiationPulse
|
collection: RadiationPulse
|
||||||
params:
|
params:
|
||||||
volume: 5
|
volume: 5
|
||||||
|
anomalyContactDamage:
|
||||||
|
types:
|
||||||
|
Radiation: 10
|
||||||
- type: AmbientSound
|
- type: AmbientSound
|
||||||
range: 5
|
range: 5
|
||||||
volume: -5
|
volume: -5
|
||||||
@@ -35,8 +38,6 @@
|
|||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Inorganic
|
|
||||||
damageModifierSet: Metallic
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: GuideHelp
|
- type: GuideHelp
|
||||||
guides:
|
guides:
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
- type: gameRule
|
- type: gameRule
|
||||||
|
id: AnomalySpawn
|
||||||
|
config:
|
||||||
|
!type:StationEventRuleConfiguration
|
||||||
|
id: AnomalySpawn
|
||||||
|
weight: 10
|
||||||
|
startAfter: 30
|
||||||
|
endAfter: 35
|
||||||
|
|
||||||
|
- type: gameRule
|
||||||
id: BluespaceArtifact
|
id: BluespaceArtifact
|
||||||
config:
|
config:
|
||||||
!type:StationEventRuleConfiguration
|
!type:StationEventRuleConfiguration
|
||||||
|
|||||||
Reference in New Issue
Block a user