]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Anomalies behaviours (#24683)
authorEd <96445749+TheShuEd@users.noreply.github.com>
Mon, 1 Apr 2024 08:29:13 +0000 (11:29 +0300)
committerGitHub <noreply@github.com>
Mon, 1 Apr 2024 08:29:13 +0000 (19:29 +1100)
* Added new anomaly particle

* Add basic anomaly behaviour

* +2 parametres

* add functional to new particle

* add components to behaviours

* big content

* add shuffle, moved thing to server

* clean up

* fixes

* random pick redo

* bonjour behavioUr

* fix AJCM

* fix

* add some new behaviours

* power modifier behaviour

* rmeove timer

* new event for update ui fix

* refactor!

* fixes

* enum

* Fix mapinit

* Minor touches

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
29 files changed:
Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml
Content.Server/Anomaly/AnomalySystem.Scanner.cs
Content.Server/Anomaly/AnomalySystem.cs
Content.Server/Anomaly/Components/AnomalousParticleComponent.cs
Content.Server/Anomaly/Components/SecretDataAnomalyComponent.cs [new file with mode: 0644]
Content.Server/Anomaly/Components/ShuffleParticlesAnomalyComponent.cs [new file with mode: 0644]
Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs
Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs
Content.Server/Anomaly/Effects/EntityAnomalySystem.cs
Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs
Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs
Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs
Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs
Content.Server/Anomaly/Effects/SecretDataAnomalySystem.cs [new file with mode: 0644]
Content.Server/Anomaly/Effects/ShuffleParticlesAnomalySystem.cs [new file with mode: 0644]
Content.Server/Anomaly/Effects/TileAnomalySystem.cs
Content.Shared/Anomaly/Components/AnomalyComponent.cs
Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs
Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs [new file with mode: 0644]
Content.Shared/Anomaly/SharedAnomaly.cs
Content.Shared/Anomaly/SharedAnomalySystem.cs
Resources/Locale/en-US/anomaly/anomaly.ftl
Resources/Locale/en-US/machine-linking/receiver_ports.ftl
Resources/Prototypes/Anomaly/behaviours.yml [new file with mode: 0644]
Resources/Prototypes/DeviceLinking/sink_ports.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml
Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml

index ac4adf7e0e43b8ba35c413ca7df6d48b0fbefd33..36a750d0098be0180c67d81b56c8c338ccc2d478 100644 (file)
@@ -1,9 +1,9 @@
-<controls:FancyWindow
+<controls:FancyWindow
     xmlns="https://spacestation14.io"
     xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
     Title="{Loc 'anomaly-scanner-ui-title'}"
-    MinSize="350 260"
-    SetSize="350 260">
+    MinSize="350 400"
+    SetSize="350 400">
     <BoxContainer Orientation="Vertical" VerticalExpand="True" Margin="10 0 10 10">
         <RichTextLabel Name="TextDisplay"></RichTextLabel>
     </BoxContainer>
index 74af8e67e3c7b76e747f5eebf055674da15e0480..bce508903d0f40b7f1ee8ce7646869bee8c0446c 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Server.Anomaly.Components;
+using Content.Server.Anomaly.Components;
 using Content.Shared.Anomaly;
 using Content.Shared.Anomaly.Components;
 using Content.Shared.DoAfter;
@@ -21,6 +21,7 @@ public sealed partial class AnomalySystem
 
         SubscribeLocalEvent<AnomalySeverityChangedEvent>(OnScannerAnomalySeverityChanged);
         SubscribeLocalEvent<AnomalyHealthChangedEvent>(OnScannerAnomalyHealthChanged);
+        SubscribeLocalEvent<AnomalyBehaviorChangedEvent>(OnScannerAnomalyBehaviorChanged);
     }
 
     private void OnScannerAnomalyShutdown(ref AnomalyShutdownEvent args)
@@ -67,6 +68,17 @@ public sealed partial class AnomalySystem
         }
     }
 
+    private void OnScannerAnomalyBehaviorChanged(ref AnomalyBehaviorChangedEvent args)
+    {
+        var query = EntityQueryEnumerator<AnomalyScannerComponent>();
+        while (query.MoveNext(out var uid, out var component))
+        {
+            if (component.ScannedAnomaly != args.Anomaly)
+                continue;
+            UpdateScannerUi(uid, component);
+        }
+    }
+
     private void OnScannerUiOpened(EntityUid uid, AnomalyScannerComponent component, BoundUIOpenedEvent args)
     {
         UpdateScannerUi(uid, component);
@@ -132,29 +144,95 @@ public sealed partial class AnomalySystem
             return msg;
         }
 
-        msg.AddMarkup(Loc.GetString("anomaly-scanner-severity-percentage", ("percent", anomalyComp.Severity.ToString("P"))));
+        TryComp<SecretDataAnomalyComponent>(anomaly, out var secret);
+
+        //Severity
+        if (secret != null && secret.Secret.Contains(AnomalySecretData.Severity))
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-severity-percentage-unknown"));
+        else
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-severity-percentage", ("percent", anomalyComp.Severity.ToString("P"))));
         msg.PushNewline();
-        string stateLoc;
-        if (anomalyComp.Stability < anomalyComp.DecayThreshold)
-            stateLoc = Loc.GetString("anomaly-scanner-stability-low");
-        else if (anomalyComp.Stability > anomalyComp.GrowthThreshold)
-            stateLoc =  Loc.GetString("anomaly-scanner-stability-high");
+
+        //Stability
+        if (secret != null && secret.Secret.Contains(AnomalySecretData.Stability))
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-stability-unknown"));
         else
-            stateLoc =  Loc.GetString("anomaly-scanner-stability-medium");
-        msg.AddMarkup(stateLoc);
+        {
+            string stateLoc;
+            if (anomalyComp.Stability < anomalyComp.DecayThreshold)
+                stateLoc = Loc.GetString("anomaly-scanner-stability-low");
+            else if (anomalyComp.Stability > anomalyComp.GrowthThreshold)
+                stateLoc = Loc.GetString("anomaly-scanner-stability-high");
+            else
+                stateLoc = Loc.GetString("anomaly-scanner-stability-medium");
+            msg.AddMarkup(stateLoc);
+        }
         msg.PushNewline();
 
-        msg.AddMarkup(Loc.GetString("anomaly-scanner-point-output", ("point", GetAnomalyPointValue(anomaly, anomalyComp))));
+        //Point output
+        if (secret != null && secret.Secret.Contains(AnomalySecretData.OutputPoint))
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-point-output-unknown"));
+        else
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-point-output", ("point", GetAnomalyPointValue(anomaly, anomalyComp))));
         msg.PushNewline();
         msg.PushNewline();
 
+        //Particles title
         msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-readout"));
         msg.PushNewline();
-        msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-danger", ("type", GetParticleLocale(anomalyComp.SeverityParticleType))));
+
+        //Danger
+        if (secret != null && secret.Secret.Contains(AnomalySecretData.ParticleDanger))
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-danger-unknown"));
+        else
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-danger", ("type", GetParticleLocale(anomalyComp.SeverityParticleType))));
+        msg.PushNewline();
+
+        //Unstable
+        if (secret != null && secret.Secret.Contains(AnomalySecretData.ParticleUnstable))
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-unstable-unknown"));
+        else
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-unstable", ("type", GetParticleLocale(anomalyComp.DestabilizingParticleType))));
         msg.PushNewline();
-        msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-unstable", ("type", GetParticleLocale(anomalyComp.DestabilizingParticleType))));
+
+        //Containment
+        if (secret != null && secret.Secret.Contains(AnomalySecretData.ParticleContainment))
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-containment-unknown"));
+        else
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-containment", ("type", GetParticleLocale(anomalyComp.WeakeningParticleType))));
         msg.PushNewline();
-        msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-containment", ("type", GetParticleLocale(anomalyComp.WeakeningParticleType))));
+
+        //Transformation
+        if (secret != null && secret.Secret.Contains(AnomalySecretData.ParticleTransformation))
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-transformation-unknown"));
+        else
+            msg.AddMarkup(Loc.GetString("anomaly-scanner-particle-transformation", ("type", GetParticleLocale(anomalyComp.TransformationParticleType))));
+
+
+        //Behavior
+        msg.PushNewline();
+        msg.PushNewline();
+        msg.AddMarkup(Loc.GetString("anomaly-behavior-title"));
+        msg.PushNewline();
+
+        if (secret != null && secret.Secret.Contains(AnomalySecretData.Behavior))
+            msg.AddMarkup(Loc.GetString("anomaly-behavior-unknown"));
+        else
+        {
+            if (anomalyComp.CurrentBehavior != null)
+            {
+                var behavior = _prototype.Index(anomalyComp.CurrentBehavior.Value);
+
+                msg.AddMarkup("- " + Loc.GetString(behavior.Description));
+                msg.PushNewline();
+                var mod = Math.Floor((behavior.EarnPointModifier) * 100);
+                msg.AddMarkup("- " + Loc.GetString("anomaly-behavior-point", ("mod", mod)));
+            }
+            else
+            {
+                msg.AddMarkup(Loc.GetString("anomaly-behavior-balanced"));
+            }
+        }
 
         //The timer at the end here is actually added in the ui itself.
         return msg;
index c3f19aa1777a717c8f1e1b9b10119838c96d9281..bae101de87819ae9cd0c56429c5c5afba097ea35 100644 (file)
@@ -8,13 +8,18 @@ using Content.Server.Radio.EntitySystems;
 using Content.Server.Station.Systems;
 using Content.Shared.Anomaly;
 using Content.Shared.Anomaly.Components;
+using Content.Shared.Anomaly.Prototypes;
 using Content.Shared.DoAfter;
+using Content.Shared.Random;
+using Content.Shared.Random.Helpers;
 using Robust.Server.GameObjects;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Configuration;
 using Robust.Shared.Physics.Events;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
+using Robust.Shared.Serialization.Manager;
+using System.Linq;
 
 namespace Content.Server.Anomaly;
 
@@ -33,13 +38,20 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
     [Dependency] private readonly SharedPointLightSystem _pointLight = default!;
     [Dependency] private readonly StationSystem _station = default!;
     [Dependency] private readonly RadioSystem _radio = default!;
+    [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly RadiationSystem _radiation = default!;
     [Dependency] private readonly SharedAudioSystem _audio = default!;
     [Dependency] private readonly UserInterfaceSystem _ui = default!;
+    [Dependency] private readonly IComponentFactory _componentFactory = default!;
+    [Dependency] private readonly ISerializationManager _serialization = default!;
+    [Dependency] private readonly IEntityManager _entity = default!;
 
     public const float MinParticleVariation = 0.8f;
     public const float MaxParticleVariation = 1.2f;
 
+    [ValidatePrototypeId<WeightedRandomPrototype>]
+    const string WeightListProto = "AnomalyBehaviorList";
+
     /// <inheritdoc/>
     public override void Initialize()
     {
@@ -54,25 +66,34 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
         InitializeCommands();
     }
 
-    private void OnMapInit(EntityUid uid, AnomalyComponent component, MapInitEvent args)
+    private void OnMapInit(Entity<AnomalyComponent> anomaly, ref MapInitEvent args)
     {
-        component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * 3; // longer the first time
-        ChangeAnomalyStability(uid, Random.NextFloat(component.InitialStabilityRange.Item1 , component.InitialStabilityRange.Item2), component);
-        ChangeAnomalySeverity(uid, Random.NextFloat(component.InitialSeverityRange.Item1, component.InitialSeverityRange.Item2), component);
+        anomaly.Comp.NextPulseTime = Timing.CurTime + GetPulseLength(anomaly.Comp) * 3; // longer the first time
+        ChangeAnomalyStability(anomaly, Random.NextFloat(anomaly.Comp.InitialStabilityRange.Item1 , anomaly.Comp.InitialStabilityRange.Item2), anomaly.Comp);
+        ChangeAnomalySeverity(anomaly, Random.NextFloat(anomaly.Comp.InitialSeverityRange.Item1, anomaly.Comp.InitialSeverityRange.Item2), anomaly.Comp);
+
+        ShuffleParticlesEffect(anomaly.Comp);
+        anomaly.Comp.Continuity = _random.NextFloat(anomaly.Comp.MinContituty, anomaly.Comp.MaxContituty);
+        SetBehavior(anomaly, GetRandomBehavior());
+    }
 
+    public void ShuffleParticlesEffect(AnomalyComponent anomaly)
+    {
         var particles = new List<AnomalousParticleType>
-            { AnomalousParticleType.Delta, AnomalousParticleType.Epsilon, AnomalousParticleType.Zeta };
-        component.SeverityParticleType = Random.PickAndTake(particles);
-        component.DestabilizingParticleType = Random.PickAndTake(particles);
-        component.WeakeningParticleType = Random.PickAndTake(particles);
+            { AnomalousParticleType.Delta, AnomalousParticleType.Epsilon, AnomalousParticleType.Zeta, AnomalousParticleType.Sigma };
+
+        anomaly.SeverityParticleType = Random.PickAndTake(particles);
+        anomaly.DestabilizingParticleType = Random.PickAndTake(particles);
+        anomaly.WeakeningParticleType = Random.PickAndTake(particles);
+        anomaly.TransformationParticleType = Random.PickAndTake(particles);
     }
 
-    private void OnShutdown(EntityUid uid, AnomalyComponent component, ComponentShutdown args)
+    private void OnShutdown(Entity<AnomalyComponent> anomaly, ref ComponentShutdown args)
     {
-        EndAnomaly(uid, component);
+        EndAnomaly(anomaly);
     }
 
-    private void OnStartCollide(EntityUid uid, AnomalyComponent component, ref StartCollideEvent args)
+    private void OnStartCollide(Entity<AnomalyComponent> anomaly, ref StartCollideEvent args)
     {
         if (!TryComp<AnomalousParticleComponent>(args.OtherEntity, out var particle))
             return;
@@ -80,21 +101,33 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
         if (args.OtherFixtureId != particle.FixtureId)
             return;
 
+        var behaviorMod = 1f;
+        if (anomaly.Comp.CurrentBehavior != null)
+        {
+            var b = _prototype.Index(anomaly.Comp.CurrentBehavior.Value);
+            behaviorMod = b.ParticleSensivity;
+        }
         // small function to randomize because it's easier to read like this
-        float VaryValue(float v) => v * Random.NextFloat(MinParticleVariation, MaxParticleVariation);
+        float VaryValue(float v) => v * behaviorMod * Random.NextFloat(MinParticleVariation, MaxParticleVariation);
 
-        if (particle.ParticleType == component.DestabilizingParticleType || particle.DestabilzingOverride)
+        if (particle.ParticleType == anomaly.Comp.DestabilizingParticleType || particle.DestabilzingOverride)
         {
-            ChangeAnomalyStability(uid, VaryValue(particle.StabilityPerDestabilizingHit), component);
+            ChangeAnomalyStability(anomaly, VaryValue(particle.StabilityPerDestabilizingHit), anomaly.Comp);
         }
-        if (particle.ParticleType == component.SeverityParticleType || particle.SeverityOverride)
+        if (particle.ParticleType == anomaly.Comp.SeverityParticleType || particle.SeverityOverride)
         {
-            ChangeAnomalySeverity(uid, VaryValue(particle.SeverityPerSeverityHit), component);
+            ChangeAnomalySeverity(anomaly, VaryValue(particle.SeverityPerSeverityHit), anomaly.Comp);
         }
-        if (particle.ParticleType == component.WeakeningParticleType || particle.WeakeningOverride)
+        if (particle.ParticleType == anomaly.Comp.WeakeningParticleType || particle.WeakeningOverride)
         {
-            ChangeAnomalyHealth(uid, VaryValue(particle.HealthPerWeakeningeHit), component);
-            ChangeAnomalyStability(uid, VaryValue(particle.StabilityPerWeakeningeHit), component);
+            ChangeAnomalyHealth(anomaly, VaryValue(particle.HealthPerWeakeningeHit), anomaly.Comp);
+            ChangeAnomalyStability(anomaly, VaryValue(particle.StabilityPerWeakeningeHit), anomaly.Comp);
+        }
+        if (particle.ParticleType == anomaly.Comp.TransformationParticleType || particle.TransmutationOverride)
+        {
+            ChangeAnomalySeverity(anomaly, VaryValue(particle.SeverityPerSeverityHit), anomaly.Comp);
+            if (_random.Prob(anomaly.Comp.Continuity))
+                SetBehavior(anomaly, GetRandomBehavior());
         }
     }
 
@@ -116,6 +149,13 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
         //penalty of up to 50% based on health
         multiplier *= MathF.Pow(1.5f, component.Health) - 0.5f;
 
+        //Apply behavior modifier
+        if (component.CurrentBehavior != null)
+        {
+            var behavior = _prototype.Index(component.CurrentBehavior.Value);
+            multiplier *= behavior.EarnPointModifier;
+        }
+
         var severityValue = 1 / (1 + MathF.Pow(MathF.E, -7 * (component.Severity - 0.5f)));
 
         return (int) ((component.MaxPointsPerSecond - component.MinPointsPerSecond) * severityValue * multiplier) + component.MinPointsPerSecond;
@@ -133,6 +173,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
             AnomalousParticleType.Delta => Loc.GetString("anomaly-particles-delta"),
             AnomalousParticleType.Epsilon => Loc.GetString("anomaly-particles-epsilon"),
             AnomalousParticleType.Zeta => Loc.GetString("anomaly-particles-zeta"),
+            AnomalousParticleType.Sigma => Loc.GetString("anomaly-particles-sigma"),
             _ => throw new ArgumentOutOfRangeException()
         };
     }
@@ -144,4 +185,40 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
         UpdateGenerator();
         UpdateVessels();
     }
+
+    #region Behavior
+    private string GetRandomBehavior()
+    {
+        var weightList = _prototype.Index<WeightedRandomPrototype>(WeightListProto);
+        return weightList.Pick(_random);
+    }
+
+    private void SetBehavior(Entity<AnomalyComponent> anomaly, ProtoId<AnomalyBehaviorPrototype> behaviorProto)
+    {
+        if (anomaly.Comp.CurrentBehavior == behaviorProto)
+            return;
+
+        if (anomaly.Comp.CurrentBehavior != null)
+            RemoveBehavior(anomaly, anomaly.Comp.CurrentBehavior.Value);
+
+        //event broadcast
+        var ev = new AnomalyBehaviorChangedEvent(anomaly, anomaly.Comp.CurrentBehavior, behaviorProto);
+        anomaly.Comp.CurrentBehavior = behaviorProto;
+        RaiseLocalEvent(anomaly, ref ev, true);
+
+        var behavior = _prototype.Index(behaviorProto);
+
+        EntityManager.AddComponents(anomaly, behavior.Components);
+    }
+
+    private void RemoveBehavior(Entity<AnomalyComponent> anomaly, ProtoId<AnomalyBehaviorPrototype> behaviorProto)
+    {
+        if (anomaly.Comp.CurrentBehavior == null)
+            return;
+
+        var behavior = _prototype.Index(anomaly.Comp.CurrentBehavior.Value);
+
+        EntityManager.RemoveComponents(anomaly, behavior.Components);
+    }
+    #endregion
 }
index 9141ca6529c5cb1a0b914fb0b78e16048b09972d..5b05522bb9abd3ba679e190804ffbcbaa6f4a91b 100644 (file)
@@ -13,58 +13,64 @@ public sealed partial class AnomalousParticleComponent : Component
     /// The type of particle that the projectile
     /// imbues onto the anomaly on contact.
     /// </summary>
-    [DataField("particleType", required: true)]
+    [DataField(required: true)]
     public AnomalousParticleType ParticleType;
 
     /// <summary>
     /// The fixture that's checked on collision.
     /// </summary>
-    [DataField("fixtureId")]
+    [DataField]
     public string FixtureId = "projectile";
 
     /// <summary>
     /// The amount that the <see cref="AnomalyComponent.Severity"/> increases by when hit
     /// of an anomalous particle of <seealso cref="AnomalyComponent.SeverityParticleType"/>.
     /// </summary>
-    [DataField("severityPerSeverityHit")]
+    [DataField]
     public float SeverityPerSeverityHit = 0.025f;
 
     /// <summary>
     /// The amount that the <see cref="AnomalyComponent.Stability"/> increases by when hit
     /// of an anomalous particle of <seealso cref="AnomalyComponent.DestabilizingParticleType"/>.
     /// </summary>
-    [DataField("stabilityPerDestabilizingHit")]
+    [DataField]
     public float StabilityPerDestabilizingHit = 0.04f;
 
     /// <summary>
     /// The amount that the <see cref="AnomalyComponent.Stability"/> increases by when hit
     /// of an anomalous particle of <seealso cref="AnomalyComponent.DestabilizingParticleType"/>.
     /// </summary>
-    [DataField("healthPerWeakeningeHit")]
+    [DataField]
     public float HealthPerWeakeningeHit = -0.05f;
 
     /// <summary>
     /// The amount that the <see cref="AnomalyComponent.Stability"/> increases by when hit
     /// of an anomalous particle of <seealso cref="AnomalyComponent.DestabilizingParticleType"/>.
     /// </summary>
-    [DataField("stabilityPerWeakeningeHit")]
+    [DataField]
     public float StabilityPerWeakeningeHit = -0.1f;
 
     /// <summary>
     /// If this is true then the particle will always affect the stability of the anomaly.
     /// </summary>
-    [DataField("destabilzingOverride")]
+    [DataField]
     public bool DestabilzingOverride = false;
 
     /// <summary>
     /// If this is true then the particle will always affect the weakeness of the anomaly.
     /// </summary>
-    [DataField("weakeningOverride")]
+    [DataField]
     public bool WeakeningOverride = false;
 
     /// <summary>
     /// If this is true then the particle will always affect the severity of the anomaly.
     /// </summary>
-    [DataField("severityOverride")]
+    [DataField]
     public bool SeverityOverride = false;
+
+    /// <summary>
+    /// If this is true then the particle will always affect the behaviour.
+    /// </summary>
+    [DataField]
+    public bool TransmutationOverride = false;
 }
diff --git a/Content.Server/Anomaly/Components/SecretDataAnomalyComponent.cs b/Content.Server/Anomaly/Components/SecretDataAnomalyComponent.cs
new file mode 100644 (file)
index 0000000..80eecaa
--- /dev/null
@@ -0,0 +1,45 @@
+using Content.Server.Anomaly.Effects;
+
+namespace Content.Server.Anomaly.Components;
+
+/// <summary>
+/// Hides some information about the anomaly when scanning it
+/// </summary>
+[RegisterComponent, Access(typeof(SecretDataAnomalySystem), typeof(AnomalySystem))]
+public sealed partial class SecretDataAnomalyComponent : Component
+{
+    /// <summary>
+    /// Minimum hidden data elements on MapInit
+    /// </summary>
+    [DataField]
+    public int RandomStartSecretMin = 0;
+
+    /// <summary>
+    /// Maximum hidden data elements on MapInit
+    /// </summary>
+    [DataField]
+    public int RandomStartSecretMax = 0;
+
+    /// <summary>
+    /// Current secret data
+    /// </summary>
+    [DataField]
+    public List<AnomalySecretData> Secret = new();
+}
+
+/// <summary>
+/// Enum with secret data field variants
+/// </summary>
+[Serializable]
+public enum AnomalySecretData : byte
+{
+    Severity,
+    Stability,
+    OutputPoint,
+    ParticleDanger,
+    ParticleUnstable,
+    ParticleContainment,
+    ParticleTransformation,
+    Behavior,
+    Default
+}
diff --git a/Content.Server/Anomaly/Components/ShuffleParticlesAnomalyComponent.cs b/Content.Server/Anomaly/Components/ShuffleParticlesAnomalyComponent.cs
new file mode 100644 (file)
index 0000000..00db76d
--- /dev/null
@@ -0,0 +1,28 @@
+using Content.Server.Anomaly.Effects;
+
+namespace Content.Server.Anomaly.Components;
+
+/// <summary>
+/// Shuffle Particle types in some situations
+/// </summary>
+[RegisterComponent, Access(typeof(ShuffleParticlesAnomalySystem))]
+public sealed partial class ShuffleParticlesAnomalyComponent : Component
+{
+    /// <summary>
+    /// Prob() chance to randomize particle types after Anomaly pulation
+    /// </summary>
+    [DataField]
+    public bool ShuffleOnPulse = false;
+
+    /// <summary>
+    /// Prob() chance to randomize particle types after APE or CHIMP projectile
+    /// </summary>
+    [DataField]
+    public bool ShuffleOnParticleHit = false;
+
+    /// <summary>
+    /// Chance to random particles
+    /// </summary>
+    [DataField]
+    public float Prob = 0.5f;
+}
index dd2da82c9d6b497d2ba0e128c9fdc1c9cdcd9247..bd8c2f3c7dee7f37b8cee2593b7e6b4301f8a2a4 100644 (file)
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
 using System.Numerics;
 using Content.Server.Anomaly.Components;
 using Content.Shared.Administration.Logs;
@@ -33,7 +33,7 @@ public sealed class BluespaceAnomalySystem : EntitySystem
     {
         var xformQuery = GetEntityQuery<TransformComponent>();
         var xform = xformQuery.GetComponent(uid);
-        var range = component.MaxShuffleRadius * args.Severity;
+        var range = component.MaxShuffleRadius * args.Severity * args.PowerModifier;
         var mobs = new HashSet<Entity<MobStateComponent>>();
         _lookup.GetEntitiesInRange(xform.Coordinates, range, mobs);
         var allEnts = new ValueList<EntityUid>(mobs.Select(m => m.Owner)) { uid };
@@ -56,7 +56,7 @@ public sealed class BluespaceAnomalySystem : EntitySystem
     {
         var xform = Transform(uid);
         var mapPos = _xform.GetWorldPosition(xform);
-        var radius = component.SupercriticalTeleportRadius;
+        var radius = component.SupercriticalTeleportRadius * args.PowerModifier;
         var gridBounds = new Box2(mapPos - new Vector2(radius, radius), mapPos + new Vector2(radius, radius));
         var mobs = new HashSet<Entity<MobStateComponent>>();
         _lookup.GetEntitiesInRange(xform.Coordinates, component.MaxShuffleRadius, mobs);
index 8b5a72449f348d2027ab86bb8ed7fb91ac748ac7..f2a060d6295d39ea359642f6cfa75f7a2c6ca9c3 100644 (file)
@@ -28,7 +28,7 @@ public sealed class ElectricityAnomalySystem : EntitySystem
 
     private void OnPulse(Entity<ElectricityAnomalyComponent> anomaly, ref AnomalyPulseEvent args)
     {
-        var range = anomaly.Comp.MaxElectrocuteRange * args.Stability;
+        var range = anomaly.Comp.MaxElectrocuteRange * args.Stability * args.PowerModifier;
 
         int boltCount = (int)MathF.Floor(MathHelper.Lerp((float)anomaly.Comp.MinBoltCount, (float)anomaly.Comp.MaxBoltCount, args.Severity));
 
@@ -37,7 +37,7 @@ public sealed class ElectricityAnomalySystem : EntitySystem
 
     private void OnSupercritical(Entity<ElectricityAnomalyComponent> anomaly, ref AnomalySupercriticalEvent args)
     {
-        var range = anomaly.Comp.MaxElectrocuteRange * 3;
+        var range = anomaly.Comp.MaxElectrocuteRange * 3 * args.PowerModifier;
 
         _emp.EmpPulse(_transform.GetMapCoordinates(anomaly), range, anomaly.Comp.EmpEnergyConsumption, anomaly.Comp.EmpDisabledDuration);
         _lightning.ShootRandomLightnings(anomaly, range, anomaly.Comp.MaxBoltCount * 3, arcDepth: 3);
index 90a655fbba764adef8d69d72825f275a5b2164d9..86d6937af5e81a8a0b148acb24ae87bdc9c9f913 100644 (file)
@@ -35,7 +35,7 @@ public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
             if (!entry.Settings.SpawnOnPulse)
                 continue;
 
-            SpawnEntities(component, entry, args.Stability, args.Severity);
+            SpawnEntities(component, entry, args.Stability, args.Severity, args.PowerModifier);
         }
     }
 
@@ -46,7 +46,7 @@ public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
             if (!entry.Settings.SpawnOnSuperCritical)
                 continue;
 
-            SpawnEntities(component, entry, 1, 1);
+            SpawnEntities(component, entry, 1, 1, args.PowerModifier);
         }
     }
 
@@ -57,7 +57,7 @@ public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
             if (!entry.Settings.SpawnOnShutdown || args.Supercritical)
                 continue;
 
-            SpawnEntities(component, entry, 1, 1);
+            SpawnEntities(component, entry, 1, 1, 1);
         }
     }
 
@@ -68,7 +68,7 @@ public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
             if (!entry.Settings.SpawnOnStabilityChanged)
                 continue;
 
-            SpawnEntities(component, entry, args.Stability, args.Severity);
+            SpawnEntities(component, entry, args.Stability, args.Severity, 1);
         }
     }
 
@@ -79,17 +79,17 @@ public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
             if (!entry.Settings.SpawnOnSeverityChanged)
                 continue;
 
-            SpawnEntities(component, entry, args.Stability, args.Severity);
+            SpawnEntities(component, entry, args.Stability, args.Severity, 1);
         }
     }
 
-    private void SpawnEntities(Entity<EntitySpawnAnomalyComponent> anomaly, EntitySpawnSettingsEntry entry, float stability, float severity)
+    private void SpawnEntities(Entity<EntitySpawnAnomalyComponent> anomaly, EntitySpawnSettingsEntry entry, float stability, float severity, float powerMod)
     {
         var xform = Transform(anomaly);
         if (!TryComp(xform.GridUid, out MapGridComponent? grid))
             return;
 
-        var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings);
+        var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings, powerMod);
         if (tiles == null)
             return;
 
index 2bef32e322816733c67427feb91bc25e1f2f70dc..731d853280c938d41a436d5b77c170f4ee3b397d 100644 (file)
@@ -29,12 +29,12 @@ public sealed class InjectionAnomalySystem : EntitySystem
 
     private void OnPulse(Entity<InjectionAnomalyComponent> entity, ref AnomalyPulseEvent args)
     {
-        PulseScalableEffect(entity, entity.Comp.InjectRadius, entity.Comp.MaxSolutionInjection * args.Severity);
+        PulseScalableEffect(entity, entity.Comp.InjectRadius * args.PowerModifier, entity.Comp.MaxSolutionInjection * args.Severity * args.PowerModifier);
     }
 
     private void OnSupercritical(Entity<InjectionAnomalyComponent> entity, ref AnomalySupercriticalEvent args)
     {
-        PulseScalableEffect(entity, entity.Comp.SuperCriticalInjectRadius, entity.Comp.SuperCriticalSolutionInjection);
+        PulseScalableEffect(entity, entity.Comp.SuperCriticalInjectRadius * args.PowerModifier, entity.Comp.SuperCriticalSolutionInjection * args.PowerModifier);
     }
 
     private void PulseScalableEffect(Entity<InjectionAnomalyComponent> entity, float injectRadius, float maxInject)
index 8483c86dbbfcd9dc301485595733e307bd858ed7..23e0e472f0cf8b114300f03a9c64dcba567ae349 100644 (file)
@@ -31,12 +31,12 @@ public sealed class ProjectileAnomalySystem : EntitySystem
 
     private void OnPulse(EntityUid uid, ProjectileAnomalyComponent component, ref AnomalyPulseEvent args)
     {
-        ShootProjectilesAtEntities(uid, component, args.Severity);
+        ShootProjectilesAtEntities(uid, component, args.Severity * args.PowerModifier);
     }
 
     private void OnSupercritical(EntityUid uid, ProjectileAnomalyComponent component, ref AnomalySupercriticalEvent args)
     {
-        ShootProjectilesAtEntities(uid, component, 1.0f);
+        ShootProjectilesAtEntities(uid, component, args.PowerModifier);
     }
 
     private void ShootProjectilesAtEntities(EntityUid uid, ProjectileAnomalyComponent component, float severity)
index 90177bae8e3e4e8e84358356f60929f1e25d5edd..d460e6f4b08b5f4959a18eece523f8ceb4998885 100644 (file)
@@ -25,9 +25,10 @@ public sealed class PuddleCreateAnomalySystem : EntitySystem
             return;
 
         var xform = Transform(entity.Owner);
-        var puddleSol = _solutionContainer.SplitSolution(sol.Value, entity.Comp.MaxPuddleSize * args.Severity);
+        var puddleSol = _solutionContainer.SplitSolution(sol.Value, entity.Comp.MaxPuddleSize * args.Severity * args.PowerModifier);
         _puddle.TrySplashSpillAt(entity.Owner, xform.Coordinates, puddleSol, out _);
     }
+
     private void OnSupercritical(Entity<PuddleCreateAnomalyComponent> entity, ref AnomalySupercriticalEvent args)
     {
         if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var sol))
index 6bd8be002e0adcc23c49536500b660f5beca36ae..d38bda562bc0ff37cf043ddbd33b666b0401044e 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Server.Atmos.Components;
+using Content.Server.Atmos.Components;
 using Content.Server.Atmos.EntitySystems;
 using Content.Shared.Anomaly.Components;
 using Content.Shared.Anomaly.Effects.Components;
@@ -24,14 +24,14 @@ public sealed class PyroclasticAnomalySystem : EntitySystem
     private void OnPulse(EntityUid uid, PyroclasticAnomalyComponent component, ref AnomalyPulseEvent args)
     {
         var xform = Transform(uid);
-        var ignitionRadius = component.MaximumIgnitionRadius * args.Stability;
+        var ignitionRadius = component.MaximumIgnitionRadius * args.Stability * args.PowerModifier;
         IgniteNearby(uid, xform.Coordinates, args.Severity, ignitionRadius);
     }
 
     private void OnSupercritical(EntityUid uid, PyroclasticAnomalyComponent component, ref AnomalySupercriticalEvent args)
     {
         var xform = Transform(uid);
-        IgniteNearby(uid, xform.Coordinates, 1, component.MaximumIgnitionRadius * 2);
+        IgniteNearby(uid, xform.Coordinates, 1, component.MaximumIgnitionRadius * 2 * args.PowerModifier);
     }
 
     public void IgniteNearby(EntityUid uid, EntityCoordinates coordinates, float severity, float radius)
diff --git a/Content.Server/Anomaly/Effects/SecretDataAnomalySystem.cs b/Content.Server/Anomaly/Effects/SecretDataAnomalySystem.cs
new file mode 100644 (file)
index 0000000..cbdc4b0
--- /dev/null
@@ -0,0 +1,40 @@
+using Content.Server.Anomaly.Components;
+using Robust.Shared.Random;
+
+namespace Content.Server.Anomaly.Effects;
+
+public sealed class SecretDataAnomalySystem : EntitySystem
+{
+    [Dependency] private readonly IRobustRandom _random = default!;
+
+    private readonly List<AnomalySecretData> _deita = new();
+
+    public override void Initialize()
+    {
+        SubscribeLocalEvent<SecretDataAnomalyComponent, MapInitEvent>(OnMapInit);
+    }
+
+    private void OnMapInit(EntityUid uid, SecretDataAnomalyComponent anomaly, MapInitEvent args)
+    {
+        RandomizeSecret(uid,_random.Next(anomaly.RandomStartSecretMin, anomaly.RandomStartSecretMax), anomaly);
+    }
+
+    public void RandomizeSecret(EntityUid uid, int count, SecretDataAnomalyComponent? component = null)
+    {
+        if (!Resolve(uid, ref component))
+            return;
+
+        component.Secret.Clear();
+
+        // I also considered just adding all the enum values and pruning but that seems more wasteful.
+        _deita.Clear();
+        _deita.AddRange(Enum.GetValues<AnomalySecretData>());
+        var actualCount = Math.Min(count, _deita.Count);
+
+        for (int i = 0; i < actualCount; i++)
+        {
+            component.Secret.Add(_random.PickAndTake(_deita));
+        }
+    }
+}
+
diff --git a/Content.Server/Anomaly/Effects/ShuffleParticlesAnomalySystem.cs b/Content.Server/Anomaly/Effects/ShuffleParticlesAnomalySystem.cs
new file mode 100644 (file)
index 0000000..7d4d9a2
--- /dev/null
@@ -0,0 +1,41 @@
+using Content.Server.Anomaly.Components;
+using Content.Shared.Anomaly.Components;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Random;
+
+namespace Content.Server.Anomaly.Effects;
+public sealed class ShuffleParticlesAnomalySystem : EntitySystem
+{
+    [Dependency] private readonly AnomalySystem _anomaly = default!;
+    [Dependency] private readonly IRobustRandom _random = default!;
+
+    public override void Initialize()
+    {
+        SubscribeLocalEvent<ShuffleParticlesAnomalyComponent, AnomalyPulseEvent>(OnPulse);
+        SubscribeLocalEvent<ShuffleParticlesAnomalyComponent, StartCollideEvent>(OnStartCollide);
+    }
+
+    private void OnStartCollide(EntityUid uid, ShuffleParticlesAnomalyComponent shuffle, StartCollideEvent args)
+    {
+        if (!TryComp<AnomalyComponent>(uid, out var anomaly))
+            return;
+
+        if (shuffle.ShuffleOnParticleHit && _random.Prob(shuffle.Prob))
+            _anomaly.ShuffleParticlesEffect(anomaly);
+
+        if (!TryComp<AnomalousParticleComponent>(args.OtherEntity, out var particle))
+            return;
+    }
+
+    private void OnPulse(EntityUid uid, ShuffleParticlesAnomalyComponent shuffle, AnomalyPulseEvent args)
+    {
+        if (!TryComp<AnomalyComponent>(uid, out var anomaly))
+            return;
+
+        if (shuffle.ShuffleOnPulse && _random.Prob(shuffle.Prob))
+        {
+            _anomaly.ShuffleParticlesEffect(anomaly);
+        }
+    }
+}
+
index c1487cfc8c64a46d6b4d6677dab27a10135ad154..92f5415252675a132a0e52bc48cc3655ffa53988 100644 (file)
@@ -34,7 +34,7 @@ public sealed class TileAnomalySystem : SharedTileAnomalySystem
             if (!entry.Settings.SpawnOnPulse)
                 continue;
 
-            SpawnTiles(component, entry, args.Stability, args.Severity);
+            SpawnTiles(component, entry, args.Stability, args.Severity, args.PowerModifier);
         }
     }
 
@@ -45,7 +45,7 @@ public sealed class TileAnomalySystem : SharedTileAnomalySystem
             if (!entry.Settings.SpawnOnSuperCritical)
                 continue;
 
-            SpawnTiles(component, entry, 1, 1);
+            SpawnTiles(component, entry, 1, 1, args.PowerModifier);
         }
     }
 
@@ -56,7 +56,7 @@ public sealed class TileAnomalySystem : SharedTileAnomalySystem
             if (!entry.Settings.SpawnOnShutdown || args.Supercritical)
                 continue;
 
-            SpawnTiles(component, entry, 1, 1);
+            SpawnTiles(component, entry, 1, 1, 1);
         }
     }
 
@@ -67,7 +67,7 @@ public sealed class TileAnomalySystem : SharedTileAnomalySystem
             if (!entry.Settings.SpawnOnStabilityChanged)
                 continue;
 
-            SpawnTiles(component, entry, args.Stability, args.Severity);
+            SpawnTiles(component, entry, args.Stability, args.Severity, 1);
         }
     }
 
@@ -78,17 +78,17 @@ public sealed class TileAnomalySystem : SharedTileAnomalySystem
             if (!entry.Settings.SpawnOnSeverityChanged)
                 continue;
 
-            SpawnTiles(component, entry, args.Stability, args.Severity);
+            SpawnTiles(component, entry, args.Stability, args.Severity, 1);
         }
     }
 
-    private void SpawnTiles(Entity<TileSpawnAnomalyComponent> anomaly, TileSpawnSettingsEntry entry, float stability, float severity)
+    private void SpawnTiles(Entity<TileSpawnAnomalyComponent> anomaly, TileSpawnSettingsEntry entry, float stability, float severity, float powerMod)
     {
         var xform = Transform(anomaly);
         if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
             return;
 
-        var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings);
+        var tiles = _anomaly.GetSpawningPoints(anomaly, stability, severity, entry.Settings, powerMod);
         if (tiles == null)
             return;
 
index bafdf12360040e4d06944344267ccd0d90bb5d0b..99f7b8373a7fd75892ad180c7d656e449887cd3f 100644 (file)
@@ -1,4 +1,5 @@
 using System.Numerics;
+using Content.Shared.Anomaly.Prototypes;
 using Content.Shared.Damage;
 using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
@@ -166,6 +167,12 @@ public sealed partial class AnomalyComponent : Component
     [DataField]
     public AnomalousParticleType WeakeningParticleType;
 
+    /// <summary>
+    /// The particle type that change anomaly behaviour.
+    /// </summary>
+    [DataField]
+    public AnomalousParticleType TransformationParticleType;
+
     #region Points and Vessels
     /// <summary>
     /// The vessel that the anomaly is connceted to. Stored so that multiple
@@ -185,7 +192,7 @@ public sealed partial class AnomalyComponent : Component
     /// This doesn't include the point bonus for being unstable.
     /// </summary>
     [DataField("maxPointsPerSecond")]
-    public int MaxPointsPerSecond = 80;
+    public int MaxPointsPerSecond = 70;
 
     /// <summary>
     /// The multiplier applied to the point value for the
@@ -221,6 +228,31 @@ public sealed partial class AnomalyComponent : Component
     [DataField, ViewVariables(VVAccess.ReadWrite)]
     public EntProtoId? CoreInertPrototype;
 
+    #region Behavior Deviations
+
+    [DataField]
+    public ProtoId<AnomalyBehaviorPrototype>? CurrentBehavior;
+
+    /// <summary>
+    /// Presumption of anomaly to change behavior. The higher the number, the higher the chance that the anomaly will change its behavior.
+    /// </summary>
+    [DataField]
+    public float Continuity = 0f;
+
+    /// <summary>
+    /// Minimum contituty probability chance, that can be selected by anomaly on MapInit
+    /// </summary>
+    [DataField]
+    public float MinContituty = 0.1f;
+
+    /// <summary>
+    /// Maximum contituty probability chance, that can be selected by anomaly on MapInit
+    /// </summary>
+    [DataField]
+    public float MaxContituty = 1.0f;
+
+    #endregion
+
     #region Floating Animation
     /// <summary>
     /// How long it takes to go from the bottom of the animation to the top.
@@ -247,13 +279,13 @@ public sealed partial class AnomalyComponent : Component
 /// <param name="Stability"></param>
 /// <param name="Severity"></param>
 [ByRefEvent]
-public readonly record struct AnomalyPulseEvent(EntityUid Anomaly, float Stability, float Severity);
+public readonly record struct AnomalyPulseEvent(EntityUid Anomaly, float Stability, float Severity, float PowerModifier);
 
 /// <summary>
 /// Event raised on an anomaly when it reaches a supercritical point.
 /// </summary>
 [ByRefEvent]
-public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly);
+public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly, float PowerModifier);
 
 /// <summary>
 /// Event broadcast after an anomaly goes supercritical
@@ -282,3 +314,9 @@ public readonly record struct AnomalyStabilityChangedEvent(EntityUid Anomaly, fl
 /// <param name="Anomaly">The anomaly being changed</param>
 [ByRefEvent]
 public readonly record struct AnomalyHealthChangedEvent(EntityUid Anomaly, float Health);
+
+/// <summary>
+/// Event broadcast when an anomaly's behavior is changed.
+/// </summary>
+[ByRefEvent]
+public readonly record struct AnomalyBehaviorChangedEvent(EntityUid Anomaly, ProtoId<AnomalyBehaviorPrototype>? Old, ProtoId<AnomalyBehaviorPrototype>? New);
index f4b7cc8bf4ff839d87baaf12a33b2314daf92a53..4c3cdb014654da5ff07fa7cf0e9f24a1c0d1555c 100644 (file)
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
 using Content.Shared.Anomaly.Components;
 using Content.Shared.Anomaly.Effects.Components;
 using Content.Shared.Ghost;
@@ -27,8 +27,8 @@ public abstract class SharedGravityAnomalySystem : EntitySystem
     private void OnAnomalyPulse(EntityUid uid, GravityAnomalyComponent component, ref AnomalyPulseEvent args)
     {
         var xform = Transform(uid);
-        var range = component.MaxThrowRange * args.Severity;
-        var strength = component.MaxThrowStrength * args.Severity;
+        var range = component.MaxThrowRange * args.Severity * args.PowerModifier;
+        var strength = component.MaxThrowStrength * args.Severity * args.PowerModifier;
         var lookup = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic | LookupFlags.Sundries);
         var xformQuery = GetEntityQuery<TransformComponent>();
         var worldPos = _xform.GetWorldPosition(xform, xformQuery);
@@ -61,8 +61,8 @@ public abstract class SharedGravityAnomalySystem : EntitySystem
         var tiles = tileref.Select(t => (t.GridIndices, Tile.Empty)).ToList();
         _mapSystem.SetTiles(xform.GridUid.Value, grid, tiles);
 
-        var range = component.MaxThrowRange * 2;
-        var strength = component.MaxThrowStrength * 2;
+        var range = component.MaxThrowRange * 2 * args.PowerModifier;
+        var strength = component.MaxThrowStrength * 2 * args.PowerModifier;
         var lookup = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic | LookupFlags.Sundries);
         var xformQuery = GetEntityQuery<TransformComponent>();
         var physQuery = GetEntityQuery<PhysicsComponent>();
diff --git a/Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs b/Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs
new file mode 100644 (file)
index 0000000..c498f5c
--- /dev/null
@@ -0,0 +1,45 @@
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Anomaly.Prototypes;
+
+[Prototype]
+public sealed partial class AnomalyBehaviorPrototype : IPrototype
+{
+    [IdDataField] public string ID { get; private set; } = default!;
+
+    /// <summary>
+    /// Description for anomaly scanner
+    /// </summary>
+    [DataField]
+    public string Description = string.Empty;
+
+    /// <summary>
+    /// modification of the number of points earned from an anomaly
+    /// </summary>
+    [DataField]
+    public float EarnPointModifier = 1f;
+
+    /// <summary>
+    /// deceleration or acceleration of the pulsation frequency of the anomaly
+    /// </summary>
+    [DataField]
+    public float PulseFrequencyModifier = 1f;
+
+    /// <summary>
+    /// pulse and supercrit power modifier
+    /// </summary>
+    [DataField]
+    public float PulsePowerModifier = 1f;
+
+    /// <summary>
+    /// how much the particles will affect the anomaly
+    /// </summary>
+    [DataField]
+    public float ParticleSensivity = 1f;
+
+    /// <summary>
+    /// Components that are added to the anomaly when this behavior is selected, and removed when another behavior is selected.
+    /// </summary>
+    [DataField(serverOnly: true)]
+    public ComponentRegistry Components = new();
+}
index b7585cb5f1465da012299e89f18b1bb84aa0a35e..cde61ca336689c7b6f914f43b704c80f0b34d599 100644 (file)
@@ -33,6 +33,7 @@ public enum AnomalousParticleType : byte
     Delta,
     Epsilon,
     Zeta,
+    Sigma,
     Default
 }
 
index c335cd7b858f94cf27a89d123bff583655d875de..da1d31c6ff656d6a5a432fd63116d47b9727417f 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.Administration.Logs;
 using Content.Shared.Anomaly.Components;
+using Content.Shared.Anomaly.Prototypes;
 using Content.Shared.Damage;
 using Content.Shared.Database;
 using Content.Shared.Interaction;
@@ -14,6 +15,7 @@ using Robust.Shared.Network;
 using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Physics.Systems;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Timing;
 using Robust.Shared.Utility;
@@ -33,6 +35,7 @@ public abstract class SharedAnomalySystem : EntitySystem
     [Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
     [Dependency] private readonly SharedPhysicsSystem _physics = default!;
     [Dependency] protected readonly SharedPopupSystem Popup = default!;
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
 
     public override void Initialize()
@@ -90,8 +93,7 @@ public abstract class SharedAnomalySystem : EntitySystem
             return;
 
         DebugTools.Assert(component.MinPulseLength > TimeSpan.FromSeconds(3)); // this is just to prevent lagspikes mispredicting pulses
-        var variation = Random.NextFloat(-component.PulseVariation, component.PulseVariation) + 1;
-        component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * variation;
+        RefreshPulseTimer(uid, component);
 
         if (_net.IsServer)
             Log.Info($"Performing anomaly pulse. Entity: {ToPrettyString(uid)}");
@@ -115,10 +117,25 @@ public abstract class SharedAnomalySystem : EntitySystem
         pulse.EndTime  = Timing.CurTime + pulse.PulseDuration;
         Appearance.SetData(uid, AnomalyVisuals.IsPulsing, true);
 
-        var ev = new AnomalyPulseEvent(uid, component.Stability, component.Severity);
+        var powerMod = 1f;
+        if (component.CurrentBehavior != null)
+        {
+            var beh = _prototype.Index<AnomalyBehaviorPrototype>(component.CurrentBehavior);
+            powerMod = beh.PulsePowerModifier;
+        }
+        var ev = new AnomalyPulseEvent(uid, component.Stability, component.Severity, powerMod);
         RaiseLocalEvent(uid, ref ev, true);
     }
 
+    public void RefreshPulseTimer(EntityUid uid, AnomalyComponent? component = null)
+    {
+        if (!Resolve(uid, ref component))
+            return;
+
+        var variation = Random.NextFloat(-component.PulseVariation, component.PulseVariation) + 1;
+        component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * variation;
+    }
+
     /// <summary>
     /// Begins the animation for going supercritical
     /// </summary>
@@ -159,7 +176,14 @@ public abstract class SharedAnomalySystem : EntitySystem
         if (_net.IsServer)
             Log.Info($"Raising supercritical event. Entity: {ToPrettyString(uid)}");
 
-        var ev = new AnomalySupercriticalEvent(uid);
+        var powerMod = 1f;
+        if (component.CurrentBehavior != null)
+        {
+            var beh = _prototype.Index<AnomalyBehaviorPrototype>(component.CurrentBehavior);
+            powerMod = beh.PulsePowerModifier;
+        }
+
+        var ev = new AnomalySupercriticalEvent(uid, powerMod);
         RaiseLocalEvent(uid, ref ev, true);
 
         EndAnomaly(uid, component, true);
@@ -276,8 +300,17 @@ public abstract class SharedAnomalySystem : EntitySystem
     public TimeSpan GetPulseLength(AnomalyComponent component)
     {
         DebugTools.Assert(component.MaxPulseLength > component.MinPulseLength);
-        var modifier = Math.Clamp((component.Stability - component.GrowthThreshold) /  component.GrowthThreshold, 0, 1);
-        return (component.MaxPulseLength - component.MinPulseLength) * modifier + component.MinPulseLength;
+        var modifier = Math.Clamp((component.Stability - component.GrowthThreshold) / component.GrowthThreshold, 0, 1);
+
+        var lenght = (component.MaxPulseLength - component.MinPulseLength) * modifier + component.MinPulseLength;
+
+        //Apply behavior modifier
+        if (component.CurrentBehavior != null)
+        {
+            var behavior = _prototype.Index(component.CurrentBehavior.Value);
+            lenght *= behavior.PulseFrequencyModifier;
+        }
+        return lenght;
     }
 
     /// <summary>
@@ -335,14 +368,14 @@ public abstract class SharedAnomalySystem : EntitySystem
     /// <summary>
     /// Gets random points around the anomaly based on the given parameters.
     /// </summary>
-    public List<TileRef>? GetSpawningPoints(EntityUid uid, float stability, float severity, AnomalySpawnSettings settings)
+    public List<TileRef>? GetSpawningPoints(EntityUid uid, float stability, float severity, AnomalySpawnSettings settings, float powerModifier = 1f)
     {
         var xform = Transform(uid);
 
         if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
             return null;
 
-        var amount = (int) (MathHelper.Lerp(settings.MinAmount, settings.MaxAmount, severity * stability) + 0.5f);
+        var amount = (int) (MathHelper.Lerp(settings.MinAmount, settings.MaxAmount, severity * stability * powerModifier) + 0.5f);
 
         var localpos = xform.Coordinates.Position;
         var tilerefs = grid.GetLocalTilesIntersecting(
index 3a398d482e789dbd136573d80c0b0892e86b0f81..da5882fa62f04bd91c98abdd0b883f3624344b33 100644 (file)
@@ -8,20 +8,29 @@ anomaly-particles-delta = Delta particles
 anomaly-particles-epsilon = Epsilon particles
 anomaly-particles-zeta = Zeta particles
 anomaly-particles-omega = Omega particles
+anomaly-particles-sigma = Sigma particles
 
 anomaly-scanner-component-scan-complete = Scan complete!
 
 anomaly-scanner-ui-title = anomaly scanner
 anomaly-scanner-no-anomaly = No anomaly currently scanned.
 anomaly-scanner-severity-percentage = Current severity: [color=gray]{$percent}[/color]
+anomaly-scanner-severity-percentage-unknown = Current severity: [color=red]ERROR[/color]
 anomaly-scanner-stability-low = Current anomaly state: [color=gold]Decaying[/color]
 anomaly-scanner-stability-medium = Current anomaly state: [color=forestgreen]Stable[/color]
 anomaly-scanner-stability-high = Current anomaly state: [color=crimson]Growing[/color]
+anomaly-scanner-stability-unknown = Current anomaly state: [color=red]ERROR[/color]
 anomaly-scanner-point-output = Point output: [color=gray]{$point}[/color]
+anomaly-scanner-point-output-unknown = Point output: [color=red]ERROR[/color]
 anomaly-scanner-particle-readout = Particle Reaction Analysis:
 anomaly-scanner-particle-danger = - [color=crimson]Danger type:[/color] {$type}
 anomaly-scanner-particle-unstable = - [color=plum]Unstable type:[/color] {$type}
 anomaly-scanner-particle-containment = - [color=goldenrod]Containment type:[/color] {$type}
+anomaly-scanner-particle-transformation = - [color=#6b75fa]Transformation type:[/color] {$type}
+anomaly-scanner-particle-danger-unknown = - [color=crimson]Danger type:[/color] [color=red]ERROR[/color]
+anomaly-scanner-particle-unstable-unknown = - [color=plum]Unstable type:[/color] [color=red]ERROR[/color]
+anomaly-scanner-particle-containment-unknown = - [color=goldenrod]Containment type:[/color] [color=red]ERROR[/color]
+anomaly-scanner-particle-transformation-unknown = - [color=#6b75fa]Transformation type:[/color] [color=red]ERROR[/color]
 anomaly-scanner-pulse-timer = Time until next pulse: [color=gray]{$time}[/color]
 
 anomaly-gorilla-core-slot-name = Anomaly core
@@ -65,3 +74,23 @@ anomaly-command-supercritical = Makes a target anomaly go supercritical
 # Flavor text on the footer
 anomaly-generator-flavor-left = Anomaly may spawn inside the operator.
 anomaly-generator-flavor-right = v1.1
+
+anomaly-behavior-unknown = [color=red]ERROR. Cannot be read.[/color]
+
+anomaly-behavior-title = behavior deviation analysis:
+anomaly-behavior-point =[color=gold]Anomaly produces {$mod}% of the points[/color] 
+
+anomaly-behavior-safe = [color=forestgreen]The anomaly is extremely stable. Extremely rare pulsations.[/color]
+anomaly-behavior-slow = [color=forestgreen]The frequency of pulsations is much less frequent.[/color]
+anomaly-behavior-light = [color=forestgreen]Pulsation power is significantly reduced.[/color]
+anomaly-behavior-balanced = No behavior deviations detected.
+anomaly-behavior-delayed-force = The frequency of pulsations is greatly reduced, but their power is increased.
+anomaly-behavior-rapid = The frequency of the pulsation is much higher, but its strength is attenuated.
+anomaly-behavior-reflect = A protective coating was detected.
+anomaly-behavior-nonsensivity = A weak reaction to particles was detected.
+anomaly-behavior-sensivity = Amplified reaction to particles was detected.
+anomaly-behavior-secret = Interference detected. Some data cannot be read
+anomaly-behavior-inconstancy = [color=crimson]Impermanence has been detected. Particle types can change over time.[/color]
+anomaly-behavior-fast = [color=crimson]The pulsation frequency is strongly increased.[/color]
+anomaly-behavior-strenght = [color=crimson]The pulsation power is significantly increased.[/color]
+anomaly-behavior-moving = [color=crimson]Coordinate instability was detected.[/color]
\ No newline at end of file
index 4d2dd25af2c4d5e400567e4d8afdc9ce3e006a74..dc45677c8d2347d2b6fb05955645d060867caf91 100644 (file)
@@ -70,6 +70,9 @@ signal-port-description-set-particle-epsilon = Sets the type of particle this de
 signal-port-name-set-particle-zeta = Set particle type: zeta
 signal-port-description-set-particle-zeta = Sets the type of particle this device emits to zeta.
 
+signal-port-name-set-particle-sigma = Set particle type: sigma
+signal-port-description-set-particle-sigma = Sets the type of particle this device emits to sigma.
+
 signal-port-name-logic-input-a = Input A
 signal-port-description-logic-input-a = First input of a logic gate.
 
diff --git a/Resources/Prototypes/Anomaly/behaviours.yml b/Resources/Prototypes/Anomaly/behaviours.yml
new file mode 100644 (file)
index 0000000..b46ba97
--- /dev/null
@@ -0,0 +1,208 @@
+- type: weightedRandom
+  id: AnomalyBehaviorList
+  weights:
+  #safe
+    Slow: 0.5
+    Light: 0.5
+    FullSafe: 0.1
+  #balanced
+    Balanced: 3
+    DelayedForce: 1
+    Rapid: 1
+    BalancedSecret: 1
+    Reflect: 1
+    NonSensivity: 1
+    Sensivity: 1
+  #Hard
+    Fast: 0.5
+    Strenght: 0.5
+    Inconstancy: 0.5
+    InconstancyParticle: 0.5
+    FullUnknown: 0.5
+    Jumping: 0.3
+    Moving: 0.1
+  #Complex
+    FastUnknown: 0.2
+    JumpingUnknown: 0.1
+    InconstancyParticleUnknown: 0.1
+
+
+# Easy x0.5 point production
+
+- type: anomalyBehavior
+  id: FullSafe
+  pulseFrequencyModifier: 3
+  pulsePowerModifier: 0.5
+  earnPointModifier: 0.05
+  description: anomaly-behavior-safe
+
+- type: anomalyBehavior
+  id: Slow
+  pulseFrequencyModifier: 2
+  earnPointModifier: 0.5
+  description: anomaly-behavior-slow
+
+- type: anomalyBehavior
+  id: Light
+  pulsePowerModifier: 0.5
+  earnPointModifier: 0.5
+  description: anomaly-behavior-light
+
+# Balanced x1 point production
+
+- type: anomalyBehavior
+  id: Balanced
+  earnPointModifier: 1
+  description: anomaly-behavior-balanced
+
+- type: anomalyBehavior
+  id: DelayedForce
+  earnPointModifier: 1.15
+  description: anomaly-behavior-delayed-force
+  pulseFrequencyModifier: 0.5
+  pulsePowerModifier: 2
+  
+- type: anomalyBehavior
+  id: Rapid
+  earnPointModifier: 1.15
+  description: anomaly-behavior-rapid
+  pulseFrequencyModifier: 2
+  pulsePowerModifier: 0.5
+
+- type: anomalyBehavior
+  id: BalancedSecret
+  earnPointModifier: 1.2
+  description: anomaly-behavior-secret
+  components:
+  - type: SecretDataAnomaly
+    randomStartSecretMin: 2
+    randomStartSecretMax: 3
+
+- type: anomalyBehavior
+  id: Reflect
+  earnPointModifier: 1.1
+  particleSensivity: 0.5
+  description: anomaly-behavior-reflect
+  components:
+  - type: Reflect
+    reflectProb: 0.5
+    reflects:
+      - Energy
+
+- type: anomalyBehavior
+  id: NonSensivity
+  earnPointModifier: 0.8
+  particleSensivity: 0.5
+  description: anomaly-behavior-nonsensivity
+  
+- type: anomalyBehavior
+  id: Sensivity
+  earnPointModifier: 1.2
+  particleSensivity: 1.5
+  description: anomaly-behavior-sensivity
+
+# Hard x2 point production
+
+- type: anomalyBehavior
+  id: Fast
+  earnPointModifier: 1.9
+  pulseFrequencyModifier: 0.5
+  description: anomaly-behavior-fast
+
+- type: anomalyBehavior
+  id: Strenght
+  pulsePowerModifier: 1.5
+  earnPointModifier: 1.4
+  description: anomaly-behavior-strenght
+
+- type: anomalyBehavior
+  id: Inconstancy
+  earnPointModifier: 1.7
+  description: anomaly-behavior-inconstancy
+  components:
+  - type: ShuffleParticlesAnomaly
+    shuffleOnPulse: true
+    prob: 1
+
+- type: anomalyBehavior
+  id: InconstancyParticle
+  earnPointModifier: 1.8
+  description: anomaly-behavior-inconstancy
+  components:
+  - type: ShuffleParticlesAnomaly
+    shuffleOnParticleHit: true
+    prob: 0.8
+
+- type: anomalyBehavior
+  id: Moving
+  earnPointModifier: 2.2
+  description: anomaly-behavior-moving
+  components:
+  - type: RandomWalk
+    minSpeed: 0
+    maxSpeed: 0.3
+  - type: CanMoveInAir
+  - type: Physics
+    bodyType: Dynamic
+    bodyStatus: InAir
+
+- type: anomalyBehavior
+  id: Jumping
+  earnPointModifier: 1.8
+  description: anomaly-behavior-moving
+  components:
+  - type: ChaoticJump
+    jumpMinInterval: 15
+    jumpMaxInterval: 25
+    rangeMin: 1
+    rangeMax: 4
+    effect: PuddleSparkle
+
+- type: anomalyBehavior
+  id: FullUnknown
+  earnPointModifier: 1.9
+  description: anomaly-behavior-secret
+  components:
+  - type: SecretDataAnomaly
+    randomStartSecretMin: 4
+    randomStartSecretMax: 6
+
+# Complex Effects
+
+- type: anomalyBehavior
+  id: JumpingUnknown
+  earnPointModifier: 1.9
+  description: anomaly-behavior-moving
+  components:
+  - type: ChaoticJump
+    jumpMinInterval: 15
+    jumpMaxInterval: 25
+    rangeMin: 1
+    rangeMax: 1
+    effect: PuddleSparkle
+  - type: SecretDataAnomaly
+    randomStartSecretMin: 3
+    randomStartSecretMax: 5
+
+
+- type: anomalyBehavior
+  id: FastUnknown
+  earnPointModifier: 1.9
+  pulseFrequencyModifier: 0.5
+  description: anomaly-behavior-fast
+  components:
+  - type: SecretDataAnomaly
+    randomStartSecretMin: 3
+    randomStartSecretMax: 5
+
+- type: anomalyBehavior
+  id: InconstancyParticleUnknown
+  earnPointModifier: 1.95
+  description: anomaly-behavior-inconstancy
+  components:
+  - type: ShuffleParticlesAnomaly
+    shuffleOnParticleHit: true
+    prob: 0.5
+  - type: SecretDataAnomaly
+    randomStartSecretMin: 3
+    randomStartSecretMax: 5
\ No newline at end of file
index f05934f3baa21890bd37b9872e900008802b2463..56b10ec4fcc311a1a79a11c60b9520115d91f429 100644 (file)
   id: SetParticleZeta
   name: signal-port-name-set-particle-zeta
   description: signal-port-description-set-particle-zeta
+
+- type: sinkPort
+  id: SetParticleSigma
+  name: signal-port-name-set-particle-sigma
+  description: signal-port-description-set-particle-sigma
index 202604b8bf038e9f1da8bef60b6384505a590af7..c4736ad356f2d11c04fab0f5b1a17fa4ec1147be 100644 (file)
   - type: Clothing
     sprite: Objects/Weapons/Guns/Revolvers/chimp.rsi
   - type: Gun
+    projectileSpeed: 4
     fireRate: 1.5
     soundGunshot:
       path: /Audio/Weapons/Guns/Gunshots/taser2.ogg
       fireCost: 100
     - proto: AnomalousParticleZetaStrong
       fireCost: 100
+    - proto: AnomalousParticleSigmaStrong
+      fireCost: 100
   - type: Construction
     graph: UpgradeWeaponPistolCHIMP
     node: start
       fireCost: 100
     - proto: AnomalousParticleZetaStrong
       fireCost: 100
+    - proto: AnomalousParticleSigmaStrong
+      fireCost: 100
 
 - type: entity
   name: eye of a behonker
index 9445a3cfe12fd54ac50aeef644401dcfef6c199e..a28d527535f6da5c5fc7ce070665447a2bd0755b 100644 (file)
   name: delta particles
   noSpawn: true
   components:
+  - type: PointLight
+    enabled: true
+    color: "#c2385d"
+    radius: 2.0
+    energy: 7.0
   - type: AnomalousParticle
     particleType: Delta
   - type: Sprite
         Heat: 5
   - type: TimedDespawn
     lifetime: 3
+  - type: Reflective
+    reflective:
+    - Energy
 
 
 - type: entity
   name: epsilon particles
   noSpawn: true
   components:
+  - type: PointLight
+    enabled: true
+    color: "#38c296"
+    radius: 2.0
+    energy: 7.0
   - type: Sprite
     layers:
       - state: magicm_cyan
   name: zeta particles
   noSpawn: true
   components:
+  - type: PointLight
+    enabled: true
+    color: "#b9c238"
+    radius: 2.0
+    energy: 7.0
   - type: Sprite
     layers:
       - state: magicm_yellow
   name: omega particles
   noSpawn: true
   components:
+  - type: PointLight
+    enabled: true
+    color: "#38c24f"
+    radius: 2.0
+    energy: 7.0
   - type: Sprite
     sprite: Objects/Weapons/Guns/Projectiles/magic.rsi
     layers:
       types:
         Heat: 20
 
+- type: entity
+  parent: AnomalousParticleDelta
+  id: AnomalousParticleSigma
+  name: sigma particles
+  noSpawn: true
+  components:
+  - type: PointLight
+    enabled: true
+    color: "#8d38c2"
+    radius: 2.0
+    energy: 7.0
+  - type: Sprite
+    layers:
+      - state: magicm
+        shader: unshaded
+  - type: AnomalousParticle
+    particleType: Sigma
+    
+- type: entity
+  parent: AnomalousParticleSigma
+  id: AnomalousParticleSigmaStrong
+  name: sigma particles
+  noSpawn: true
+  components:
+  - type: AnomalousParticle
+    particleType: Sigma
+    
 # Launcher projectiles (grenade / rocket)
 - type: entity
   id: BulletRocket
index 36d77a236d07c905535627297f1562e77e761220..0064e7a4ae95d5cf3765b6c4c22a2cc9f67d2bab 100644 (file)
     - AnomalousParticleDelta
     - AnomalousParticleEpsilon
     - AnomalousParticleZeta
+    - AnomalousParticleSigma
     setTypePorts:
       SetParticleDelta: AnomalousParticleDelta
       SetParticleEpsilon: AnomalousParticleEpsilon
       SetParticleZeta: AnomalousParticleZeta
+      SetParticleSigma: AnomalousParticleSigma
     fireBurstSize: 1
     fireBurstDelayMin: 2
     fireBurstDelayMax: 6
     guides:
     - APE
   - type: Gun
+    projectileSpeed: 4
     fireRate: 10 #just has to be fast enough to keep up with upgrades
     showExamineText: false
     selectedMode: SemiAuto
       - SetParticleDelta
       - SetParticleEpsilon
       - SetParticleZeta
+      - SetParticleSigma
 
 - type: entity
   id: MachineAnomalyGenerator
index 14a9997b8032e33c78841c6bb508134bbbad6f19..99574ab7be18d06195cb2a46d2edc5e004719f4b 100644 (file)
@@ -21,6 +21,7 @@
     anchored: false
   - type: Physics
     bodyType: Static
+    bodyStatus: InAir
   - type: Fixtures
     fixtures:
       fix1:
@@ -47,6 +48,9 @@
   - type: EmitSoundOnSpawn
     sound:
       path: /Audio/Effects/teleport_arrival.ogg
+  - type: SecretDataAnomaly
+    randomStartSecretMin: 0
+    randomStartSecretMax: 2
 
 - type: entity
   id: AnomalyPyroclastic
     castShadows: false
   - type: GravityAnomaly
   - type: GravityWell
-  - type: RadiationSource
+  - type: RadiationSource  
   - type: Physics
     bodyType: Dynamic
     bodyStatus: InAir
-  - type: CanMoveInAir
+  - type: CanMoveInAir  
   - type: RandomWalk
   - type: SingularityDistortion
     intensity: 1000
     color: "#6270bb"
   - type: Anomaly
     animationTime: 6
-    offset: 0.05, 0
+    offset: 0, 0
     corePrototype: AnomalyCoreFlora
     coreInertPrototype: AnomalyCoreFloraInert
     anomalyContactDamage: