From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Mon, 1 Apr 2024 08:29:13 +0000 (+0300) Subject: Anomalies behaviours (#24683) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=a4ec01d4719e9489b5cceb23357b3be0ea4b86e7;p=space-station-14.git Anomalies behaviours (#24683) * 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 --- diff --git a/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml b/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml index ac4adf7e0e..36a750d009 100644 --- a/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml +++ b/Content.Client/Anomaly/Ui/AnomalyScannerMenu.xaml @@ -1,9 +1,9 @@ - + MinSize="350 400" + SetSize="350 400"> diff --git a/Content.Server/Anomaly/AnomalySystem.Scanner.cs b/Content.Server/Anomaly/AnomalySystem.Scanner.cs index 74af8e67e3..bce508903d 100644 --- a/Content.Server/Anomaly/AnomalySystem.Scanner.cs +++ b/Content.Server/Anomaly/AnomalySystem.Scanner.cs @@ -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(OnScannerAnomalySeverityChanged); SubscribeLocalEvent(OnScannerAnomalyHealthChanged); + SubscribeLocalEvent(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(); + 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(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; diff --git a/Content.Server/Anomaly/AnomalySystem.cs b/Content.Server/Anomaly/AnomalySystem.cs index c3f19aa177..bae101de87 100644 --- a/Content.Server/Anomaly/AnomalySystem.cs +++ b/Content.Server/Anomaly/AnomalySystem.cs @@ -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] + const string WeightListProto = "AnomalyBehaviorList"; + /// 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 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.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 anomaly, ref ComponentShutdown args) { - EndAnomaly(uid, component); + EndAnomaly(anomaly); } - private void OnStartCollide(EntityUid uid, AnomalyComponent component, ref StartCollideEvent args) + private void OnStartCollide(Entity anomaly, ref StartCollideEvent args) { if (!TryComp(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(WeightListProto); + return weightList.Pick(_random); + } + + private void SetBehavior(Entity anomaly, ProtoId 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 anomaly, ProtoId behaviorProto) + { + if (anomaly.Comp.CurrentBehavior == null) + return; + + var behavior = _prototype.Index(anomaly.Comp.CurrentBehavior.Value); + + EntityManager.RemoveComponents(anomaly, behavior.Components); + } + #endregion } diff --git a/Content.Server/Anomaly/Components/AnomalousParticleComponent.cs b/Content.Server/Anomaly/Components/AnomalousParticleComponent.cs index 9141ca6529..5b05522bb9 100644 --- a/Content.Server/Anomaly/Components/AnomalousParticleComponent.cs +++ b/Content.Server/Anomaly/Components/AnomalousParticleComponent.cs @@ -13,58 +13,64 @@ public sealed partial class AnomalousParticleComponent : Component /// The type of particle that the projectile /// imbues onto the anomaly on contact. /// - [DataField("particleType", required: true)] + [DataField(required: true)] public AnomalousParticleType ParticleType; /// /// The fixture that's checked on collision. /// - [DataField("fixtureId")] + [DataField] public string FixtureId = "projectile"; /// /// The amount that the increases by when hit /// of an anomalous particle of . /// - [DataField("severityPerSeverityHit")] + [DataField] public float SeverityPerSeverityHit = 0.025f; /// /// The amount that the increases by when hit /// of an anomalous particle of . /// - [DataField("stabilityPerDestabilizingHit")] + [DataField] public float StabilityPerDestabilizingHit = 0.04f; /// /// The amount that the increases by when hit /// of an anomalous particle of . /// - [DataField("healthPerWeakeningeHit")] + [DataField] public float HealthPerWeakeningeHit = -0.05f; /// /// The amount that the increases by when hit /// of an anomalous particle of . /// - [DataField("stabilityPerWeakeningeHit")] + [DataField] public float StabilityPerWeakeningeHit = -0.1f; /// /// If this is true then the particle will always affect the stability of the anomaly. /// - [DataField("destabilzingOverride")] + [DataField] public bool DestabilzingOverride = false; /// /// If this is true then the particle will always affect the weakeness of the anomaly. /// - [DataField("weakeningOverride")] + [DataField] public bool WeakeningOverride = false; /// /// If this is true then the particle will always affect the severity of the anomaly. /// - [DataField("severityOverride")] + [DataField] public bool SeverityOverride = false; + + /// + /// If this is true then the particle will always affect the behaviour. + /// + [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 index 0000000000..80eecaafc7 --- /dev/null +++ b/Content.Server/Anomaly/Components/SecretDataAnomalyComponent.cs @@ -0,0 +1,45 @@ +using Content.Server.Anomaly.Effects; + +namespace Content.Server.Anomaly.Components; + +/// +/// Hides some information about the anomaly when scanning it +/// +[RegisterComponent, Access(typeof(SecretDataAnomalySystem), typeof(AnomalySystem))] +public sealed partial class SecretDataAnomalyComponent : Component +{ + /// + /// Minimum hidden data elements on MapInit + /// + [DataField] + public int RandomStartSecretMin = 0; + + /// + /// Maximum hidden data elements on MapInit + /// + [DataField] + public int RandomStartSecretMax = 0; + + /// + /// Current secret data + /// + [DataField] + public List Secret = new(); +} + +/// +/// Enum with secret data field variants +/// +[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 index 0000000000..00db76d486 --- /dev/null +++ b/Content.Server/Anomaly/Components/ShuffleParticlesAnomalyComponent.cs @@ -0,0 +1,28 @@ +using Content.Server.Anomaly.Effects; + +namespace Content.Server.Anomaly.Components; + +/// +/// Shuffle Particle types in some situations +/// +[RegisterComponent, Access(typeof(ShuffleParticlesAnomalySystem))] +public sealed partial class ShuffleParticlesAnomalyComponent : Component +{ + /// + /// Prob() chance to randomize particle types after Anomaly pulation + /// + [DataField] + public bool ShuffleOnPulse = false; + + /// + /// Prob() chance to randomize particle types after APE or CHIMP projectile + /// + [DataField] + public bool ShuffleOnParticleHit = false; + + /// + /// Chance to random particles + /// + [DataField] + public float Prob = 0.5f; +} diff --git a/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs b/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs index dd2da82c9d..bd8c2f3c7d 100644 --- a/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs @@ -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(); var xform = xformQuery.GetComponent(uid); - var range = component.MaxShuffleRadius * args.Severity; + var range = component.MaxShuffleRadius * args.Severity * args.PowerModifier; var mobs = new HashSet>(); _lookup.GetEntitiesInRange(xform.Coordinates, range, mobs); var allEnts = new ValueList(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>(); _lookup.GetEntitiesInRange(xform.Coordinates, component.MaxShuffleRadius, mobs); diff --git a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs index 8b5a72449f..f2a060d629 100644 --- a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs @@ -28,7 +28,7 @@ public sealed class ElectricityAnomalySystem : EntitySystem private void OnPulse(Entity 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 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); diff --git a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs index 90a655fbba..86d6937af5 100644 --- a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs @@ -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 anomaly, EntitySpawnSettingsEntry entry, float stability, float severity) + private void SpawnEntities(Entity 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; diff --git a/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs b/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs index 2bef32e322..731d853280 100644 --- a/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs @@ -29,12 +29,12 @@ public sealed class InjectionAnomalySystem : EntitySystem private void OnPulse(Entity 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 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 entity, float injectRadius, float maxInject) diff --git a/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs b/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs index 8483c86dbb..23e0e472f0 100644 --- a/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs @@ -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) diff --git a/Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs b/Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs index 90177bae8e..d460e6f4b0 100644 --- a/Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs @@ -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 entity, ref AnomalySupercriticalEvent args) { if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var sol)) diff --git a/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs b/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs index 6bd8be002e..d38bda562b 100644 --- a/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs @@ -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 index 0000000000..cbdc4b04df --- /dev/null +++ b/Content.Server/Anomaly/Effects/SecretDataAnomalySystem.cs @@ -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 _deita = new(); + + public override void Initialize() + { + SubscribeLocalEvent(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()); + 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 index 0000000000..7d4d9a2ee5 --- /dev/null +++ b/Content.Server/Anomaly/Effects/ShuffleParticlesAnomalySystem.cs @@ -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(OnPulse); + SubscribeLocalEvent(OnStartCollide); + } + + private void OnStartCollide(EntityUid uid, ShuffleParticlesAnomalyComponent shuffle, StartCollideEvent args) + { + if (!TryComp(uid, out var anomaly)) + return; + + if (shuffle.ShuffleOnParticleHit && _random.Prob(shuffle.Prob)) + _anomaly.ShuffleParticlesEffect(anomaly); + + if (!TryComp(args.OtherEntity, out var particle)) + return; + } + + private void OnPulse(EntityUid uid, ShuffleParticlesAnomalyComponent shuffle, AnomalyPulseEvent args) + { + if (!TryComp(uid, out var anomaly)) + return; + + if (shuffle.ShuffleOnPulse && _random.Prob(shuffle.Prob)) + { + _anomaly.ShuffleParticlesEffect(anomaly); + } + } +} + diff --git a/Content.Server/Anomaly/Effects/TileAnomalySystem.cs b/Content.Server/Anomaly/Effects/TileAnomalySystem.cs index c1487cfc8c..92f5415252 100644 --- a/Content.Server/Anomaly/Effects/TileAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/TileAnomalySystem.cs @@ -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 anomaly, TileSpawnSettingsEntry entry, float stability, float severity) + private void SpawnTiles(Entity anomaly, TileSpawnSettingsEntry entry, float stability, float severity, float powerMod) { var xform = Transform(anomaly); if (!TryComp(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; diff --git a/Content.Shared/Anomaly/Components/AnomalyComponent.cs b/Content.Shared/Anomaly/Components/AnomalyComponent.cs index bafdf12360..99f7b8373a 100644 --- a/Content.Shared/Anomaly/Components/AnomalyComponent.cs +++ b/Content.Shared/Anomaly/Components/AnomalyComponent.cs @@ -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; + /// + /// The particle type that change anomaly behaviour. + /// + [DataField] + public AnomalousParticleType TransformationParticleType; + #region Points and Vessels /// /// 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. /// [DataField("maxPointsPerSecond")] - public int MaxPointsPerSecond = 80; + public int MaxPointsPerSecond = 70; /// /// 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? CurrentBehavior; + + /// + /// Presumption of anomaly to change behavior. The higher the number, the higher the chance that the anomaly will change its behavior. + /// + [DataField] + public float Continuity = 0f; + + /// + /// Minimum contituty probability chance, that can be selected by anomaly on MapInit + /// + [DataField] + public float MinContituty = 0.1f; + + /// + /// Maximum contituty probability chance, that can be selected by anomaly on MapInit + /// + [DataField] + public float MaxContituty = 1.0f; + + #endregion + #region Floating Animation /// /// 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 /// /// [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); /// /// Event raised on an anomaly when it reaches a supercritical point. /// [ByRefEvent] -public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly); +public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly, float PowerModifier); /// /// Event broadcast after an anomaly goes supercritical @@ -282,3 +314,9 @@ public readonly record struct AnomalyStabilityChangedEvent(EntityUid Anomaly, fl /// The anomaly being changed [ByRefEvent] public readonly record struct AnomalyHealthChangedEvent(EntityUid Anomaly, float Health); + +/// +/// Event broadcast when an anomaly's behavior is changed. +/// +[ByRefEvent] +public readonly record struct AnomalyBehaviorChangedEvent(EntityUid Anomaly, ProtoId? Old, ProtoId? New); diff --git a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs index f4b7cc8bf4..4c3cdb0146 100644 --- a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs +++ b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs @@ -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(); 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(); var physQuery = GetEntityQuery(); diff --git a/Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs b/Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs new file mode 100644 index 0000000000..c498f5c595 --- /dev/null +++ b/Content.Shared/Anomaly/Prototypes/AnomalyBehaviorPrototype.cs @@ -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!; + + /// + /// Description for anomaly scanner + /// + [DataField] + public string Description = string.Empty; + + /// + /// modification of the number of points earned from an anomaly + /// + [DataField] + public float EarnPointModifier = 1f; + + /// + /// deceleration or acceleration of the pulsation frequency of the anomaly + /// + [DataField] + public float PulseFrequencyModifier = 1f; + + /// + /// pulse and supercrit power modifier + /// + [DataField] + public float PulsePowerModifier = 1f; + + /// + /// how much the particles will affect the anomaly + /// + [DataField] + public float ParticleSensivity = 1f; + + /// + /// Components that are added to the anomaly when this behavior is selected, and removed when another behavior is selected. + /// + [DataField(serverOnly: true)] + public ComponentRegistry Components = new(); +} diff --git a/Content.Shared/Anomaly/SharedAnomaly.cs b/Content.Shared/Anomaly/SharedAnomaly.cs index b7585cb5f1..cde61ca336 100644 --- a/Content.Shared/Anomaly/SharedAnomaly.cs +++ b/Content.Shared/Anomaly/SharedAnomaly.cs @@ -33,6 +33,7 @@ public enum AnomalousParticleType : byte Delta, Epsilon, Zeta, + Sigma, Default } diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index c335cd7b85..da1d31c6ff 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -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(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; + } + /// /// Begins the animation for going supercritical /// @@ -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(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; } /// @@ -335,14 +368,14 @@ public abstract class SharedAnomalySystem : EntitySystem /// /// Gets random points around the anomaly based on the given parameters. /// - public List? GetSpawningPoints(EntityUid uid, float stability, float severity, AnomalySpawnSettings settings) + public List? GetSpawningPoints(EntityUid uid, float stability, float severity, AnomalySpawnSettings settings, float powerModifier = 1f) { var xform = Transform(uid); if (!TryComp(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( diff --git a/Resources/Locale/en-US/anomaly/anomaly.ftl b/Resources/Locale/en-US/anomaly/anomaly.ftl index 3a398d482e..da5882fa62 100644 --- a/Resources/Locale/en-US/anomaly/anomaly.ftl +++ b/Resources/Locale/en-US/anomaly/anomaly.ftl @@ -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 diff --git a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl index 4d2dd25af2..dc45677c8d 100644 --- a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl +++ b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl @@ -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 index 0000000000..b46ba97fc2 --- /dev/null +++ b/Resources/Prototypes/Anomaly/behaviours.yml @@ -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 diff --git a/Resources/Prototypes/DeviceLinking/sink_ports.yml b/Resources/Prototypes/DeviceLinking/sink_ports.yml index f05934f3ba..56b10ec4fc 100644 --- a/Resources/Prototypes/DeviceLinking/sink_ports.yml +++ b/Resources/Prototypes/DeviceLinking/sink_ports.yml @@ -107,3 +107,8 @@ 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 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 202604b8bf..c4736ad356 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -643,6 +643,7 @@ - type: Clothing sprite: Objects/Weapons/Guns/Revolvers/chimp.rsi - type: Gun + projectileSpeed: 4 fireRate: 1.5 soundGunshot: path: /Audio/Weapons/Guns/Gunshots/taser2.ogg @@ -657,6 +658,8 @@ fireCost: 100 - proto: AnomalousParticleZetaStrong fireCost: 100 + - proto: AnomalousParticleSigmaStrong + fireCost: 100 - type: Construction graph: UpgradeWeaponPistolCHIMP node: start @@ -679,6 +682,8 @@ fireCost: 100 - proto: AnomalousParticleZetaStrong fireCost: 100 + - proto: AnomalousParticleSigmaStrong + fireCost: 100 - type: entity name: eye of a behonker diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 9445a3cfe1..a28d527535 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -487,6 +487,11 @@ name: delta particles noSpawn: true components: + - type: PointLight + enabled: true + color: "#c2385d" + radius: 2.0 + energy: 7.0 - type: AnomalousParticle particleType: Delta - type: Sprite @@ -513,6 +518,9 @@ Heat: 5 - type: TimedDespawn lifetime: 3 + - type: Reflective + reflective: + - Energy - type: entity @@ -533,6 +541,11 @@ name: epsilon particles noSpawn: true components: + - type: PointLight + enabled: true + color: "#38c296" + radius: 2.0 + energy: 7.0 - type: Sprite layers: - state: magicm_cyan @@ -558,6 +571,11 @@ name: zeta particles noSpawn: true components: + - type: PointLight + enabled: true + color: "#b9c238" + radius: 2.0 + energy: 7.0 - type: Sprite layers: - state: magicm_yellow @@ -583,6 +601,11 @@ 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: @@ -601,6 +624,33 @@ 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 diff --git a/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml b/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml index 36d77a236d..0064e7a4ae 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml @@ -172,10 +172,12 @@ - AnomalousParticleDelta - AnomalousParticleEpsilon - AnomalousParticleZeta + - AnomalousParticleSigma setTypePorts: SetParticleDelta: AnomalousParticleDelta SetParticleEpsilon: AnomalousParticleEpsilon SetParticleZeta: AnomalousParticleZeta + SetParticleSigma: AnomalousParticleSigma fireBurstSize: 1 fireBurstDelayMin: 2 fireBurstDelayMax: 6 @@ -185,6 +187,7 @@ guides: - APE - type: Gun + projectileSpeed: 4 fireRate: 10 #just has to be fast enough to keep up with upgrades showExamineText: false selectedMode: SemiAuto @@ -219,6 +222,7 @@ - SetParticleDelta - SetParticleEpsilon - SetParticleZeta + - SetParticleSigma - type: entity id: MachineAnomalyGenerator diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index 14a9997b80..99574ab7be 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -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 @@ -119,11 +123,11 @@ 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 @@ -598,7 +602,7 @@ color: "#6270bb" - type: Anomaly animationTime: 6 - offset: 0.05, 0 + offset: 0, 0 corePrototype: AnomalyCoreFlora coreInertPrototype: AnomalyCoreFloraInert anomalyContactDamage: