-<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>
-using Content.Server.Anomaly.Components;
+using Content.Server.Anomaly.Components;
using Content.Shared.Anomaly;
using Content.Shared.Anomaly.Components;
using Content.Shared.DoAfter;
SubscribeLocalEvent<AnomalySeverityChangedEvent>(OnScannerAnomalySeverityChanged);
SubscribeLocalEvent<AnomalyHealthChangedEvent>(OnScannerAnomalyHealthChanged);
+ SubscribeLocalEvent<AnomalyBehaviorChangedEvent>(OnScannerAnomalyBehaviorChanged);
}
private void OnScannerAnomalyShutdown(ref AnomalyShutdownEvent args)
}
}
+ 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);
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;
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;
[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()
{
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;
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());
}
}
//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;
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()
};
}
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
}
/// 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;
}
--- /dev/null
+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
+}
--- /dev/null
+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;
+}
-using System.Linq;
+using System.Linq;
using System.Numerics;
using Content.Server.Anomaly.Components;
using Content.Shared.Administration.Logs;
{
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 };
{
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);
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));
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);
if (!entry.Settings.SpawnOnPulse)
continue;
- SpawnEntities(component, entry, args.Stability, args.Severity);
+ SpawnEntities(component, entry, args.Stability, args.Severity, args.PowerModifier);
}
}
if (!entry.Settings.SpawnOnSuperCritical)
continue;
- SpawnEntities(component, entry, 1, 1);
+ SpawnEntities(component, entry, 1, 1, args.PowerModifier);
}
}
if (!entry.Settings.SpawnOnShutdown || args.Supercritical)
continue;
- SpawnEntities(component, entry, 1, 1);
+ SpawnEntities(component, entry, 1, 1, 1);
}
}
if (!entry.Settings.SpawnOnStabilityChanged)
continue;
- SpawnEntities(component, entry, args.Stability, args.Severity);
+ SpawnEntities(component, entry, args.Stability, args.Severity, 1);
}
}
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;
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)
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)
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))
-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;
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)
--- /dev/null
+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));
+ }
+ }
+}
+
--- /dev/null
+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);
+ }
+ }
+}
+
if (!entry.Settings.SpawnOnPulse)
continue;
- SpawnTiles(component, entry, args.Stability, args.Severity);
+ SpawnTiles(component, entry, args.Stability, args.Severity, args.PowerModifier);
}
}
if (!entry.Settings.SpawnOnSuperCritical)
continue;
- SpawnTiles(component, entry, 1, 1);
+ SpawnTiles(component, entry, 1, 1, args.PowerModifier);
}
}
if (!entry.Settings.SpawnOnShutdown || args.Supercritical)
continue;
- SpawnTiles(component, entry, 1, 1);
+ SpawnTiles(component, entry, 1, 1, 1);
}
}
if (!entry.Settings.SpawnOnStabilityChanged)
continue;
- SpawnTiles(component, entry, args.Stability, args.Severity);
+ SpawnTiles(component, entry, args.Stability, args.Severity, 1);
}
}
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;
using System.Numerics;
+using Content.Shared.Anomaly.Prototypes;
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
[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
/// 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
[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.
/// <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
/// <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);
-using System.Linq;
+using System.Linq;
using Content.Shared.Anomaly.Components;
using Content.Shared.Anomaly.Effects.Components;
using Content.Shared.Ghost;
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);
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>();
--- /dev/null
+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();
+}
Delta,
Epsilon,
Zeta,
+ Sigma,
Default
}
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;
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;
[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()
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)}");
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>
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);
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>
/// <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(
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
# 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
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.
--- /dev/null
+- 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
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
- 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
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
- 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
anchored: false
- type: Physics
bodyType: Static
+ bodyStatus: InAir
- type: Fixtures
fixtures:
fix1:
- 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: