-using Content.Shared.Anomaly;
+using Content.Client.Gravity;
+using Content.Shared.Anomaly;
using Content.Shared.Anomaly.Components;
using Robust.Client.GameObjects;
using Robust.Shared.Timing;
public sealed class AnomalySystem : SharedAnomalySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly FloatingVisualizerSystem _floating = default!;
/// <inheritdoc/>
public override void Initialize()
base.Initialize();
SubscribeLocalEvent<AnomalyComponent, AppearanceChangeEvent>(OnAppearanceChanged);
+ SubscribeLocalEvent<AnomalyComponent, ComponentStartup>(OnStartup);
+ SubscribeLocalEvent<AnomalyComponent, AnimationCompletedEvent>(OnAnimationComplete);
+ }
+
+ private void OnStartup(EntityUid uid, AnomalyComponent component, ComponentStartup args)
+ {
+ _floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);
+ }
+
+ private void OnAnimationComplete(EntityUid uid, AnomalyComponent component, AnimationCompletedEvent args)
+ {
+ if (args.Key != component.AnimationKey)
+ return;
+ _floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);
}
private void OnAppearanceChanged(EntityUid uid, AnomalyComponent component, ref AppearanceChangeEvent args)
using Content.Shared.Anomaly;
using Content.Shared.CCVar;
using Content.Shared.Materials;
+using Content.Shared.Physics;
using Robust.Shared.Map.Components;
+using Robust.Shared.Physics;
+using Robust.Shared.Physics.Components;
namespace Content.Server.Anomaly;
var randomY = Random.Next((int) gridBounds.Bottom, (int)gridBounds.Top);
var tile = new Vector2i(randomX, randomY);
- if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile,
- mapGridComp: gridComp) || _atmosphere.IsTileAirBlocked(grid, tile, mapGridComp: gridComp))
+
+ // no air-blocked areas.
+ if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile, mapGridComp: gridComp) ||
+ _atmosphere.IsTileAirBlocked(grid, tile, mapGridComp: gridComp))
{
continue;
}
+ // don't spawn inside of solid objects
+ var physQuery = GetEntityQuery<PhysicsComponent>();
+ var valid = true;
+ foreach (var ent in gridComp.GetAnchoredEntities(tile))
+ {
+ if (!physQuery.TryGetComponent(ent, out var body))
+ continue;
+ if (body.BodyType != BodyType.Static ||
+ !body.Hard ||
+ (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
+ continue;
+
+ valid = false;
+ break;
+ }
+ if (!valid)
+ continue;
+
targetCoords = gridComp.GridTileToLocal(tile);
break;
}
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly ExplosionSystem _explosion = default!;
[Dependency] private readonly MaterialStorageSystem _material = default!;
- [Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
public const float MinParticleVariation = 0.8f;
var multiplier = 1f;
if (component.Stability > component.GrowthThreshold)
multiplier = component.GrowingPointMultiplier; //more points for unstable
- else if (component.Stability < component.DecayThreshold)
- multiplier = component.DecayingPointMultiplier; //less points if it's dying
//penalty of up to 50% based on health
multiplier *= MathF.Pow(1.5f, component.Health) - 0.5f;
- return (int) ((component.MaxPointsPerSecond - component.MinPointsPerSecond) * component.Severity * multiplier);
+ var severityValue = 1 / (1 + MathF.Pow(MathF.E, -7 * (component.Severity - 0.5f)));
+
+ return (int) ((component.MaxPointsPerSecond - component.MinPointsPerSecond) * severityValue * multiplier) + component.MinPointsPerSecond;
}
/// <summary>
using Content.Shared.Mobs.Components;
using Content.Shared.StatusEffect;
using Robust.Shared.Random;
+using Robust.Shared.Timing;
namespace Content.Server.Anomaly.Effects;
public sealed class ElectricityAnomalySystem : EntitySystem
{
+ [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly LightningSystem _lightning = default!;
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
private void OnPulse(EntityUid uid, ElectricityAnomalyComponent component, ref AnomalyPulseEvent args)
{
- var range = component.MaxElectrocuteRange * args.Stabiltiy;
- var damage = (int) (component.MaxElectrocuteDamage * args.Severity);
- var duration = component.MaxElectrocuteDuration * args.Severity;
-
+ var range = component.MaxElectrocuteRange * args.Stability;
var xform = Transform(uid);
- foreach (var comp in _lookup.GetComponentsInRange<StatusEffectsComponent>(xform.MapPosition, range))
+ foreach (var comp in _lookup.GetComponentsInRange<MobStateComponent>(xform.MapPosition, range))
{
var ent = comp.Owner;
-
- _electrocution.TryDoElectrocution(ent, uid, damage, duration, true, statusEffects: comp, ignoreInsulation: true);
+ _lightning.ShootLightning(uid, ent);
}
}
if (mobQuery.HasComponent(ent))
validEnts.Add(ent);
- if (_random.Prob(0.1f) && poweredQuery.HasComponent(ent))
+ if (_random.Prob(0.2f) && poweredQuery.HasComponent(ent))
validEnts.Add(ent);
}
_lightning.ShootLightning(uid, ent);
}
}
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ foreach (var (elec, anom, xform) in EntityQuery<ElectricityAnomalyComponent, AnomalyComponent, TransformComponent>())
+ {
+ if (_timing.CurTime < elec.NextSecond)
+ continue;
+ elec.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);
+
+ var owner = xform.Owner;
+
+ if (!_random.Prob(elec.PassiveElectrocutionChance * anom.Stability))
+ continue;
+
+ var range = elec.MaxElectrocuteRange * anom.Stability;
+ var damage = (int) (elec.MaxElectrocuteDamage * anom.Severity);
+ var duration = elec.MaxElectrocuteDuration * anom.Severity;
+
+ foreach (var comp in _lookup.GetComponentsInRange<StatusEffectsComponent>(xform.MapPosition, range))
+ {
+ var ent = comp.Owner;
+
+ _electrocution.TryDoElectrocution(ent, owner, damage, duration, true, statusEffects: comp, ignoreInsulation: true);
+ }
+ }
+ }
}
--- /dev/null
+using System.Linq;
+using Content.Server.Maps;
+using Content.Shared.Anomaly.Components;
+using Content.Shared.Anomaly.Effects.Components;
+using Content.Shared.Maps;
+using Content.Shared.Physics;
+using Robust.Shared.Map;
+using Robust.Shared.Physics;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Random;
+
+namespace Content.Server.Anomaly.Effects;
+
+public sealed class FleshAnomalySystem : EntitySystem
+{
+ [Dependency] private readonly IMapManager _map = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly ITileDefinitionManager _tiledef = default!;
+ [Dependency] private readonly TileSystem _tile = default!;
+
+ /// <inheritdoc/>
+ public override void Initialize()
+ {
+ SubscribeLocalEvent<FleshAnomalyComponent, AnomalyPulseEvent>(OnPulse);
+ SubscribeLocalEvent<FleshAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
+ SubscribeLocalEvent<FleshAnomalyComponent, AnomalyStabilityChangedEvent>(OnSeverityChanged);
+ }
+
+ private void OnPulse(EntityUid uid, FleshAnomalyComponent component, ref AnomalyPulseEvent args)
+ {
+ var range = component.SpawnRange * args.Stability;
+ var amount = (int) (component.MaxSpawnAmount * args.Severity + 0.5f);
+
+ var xform = Transform(uid);
+ SpawnMonstersOnOpenTiles(component, xform, amount, range);
+ }
+
+ private void OnSupercritical(EntityUid uid, FleshAnomalyComponent component, ref AnomalySupercriticalEvent args)
+ {
+ var xform = Transform(uid);
+ SpawnMonstersOnOpenTiles(component, xform, component.MaxSpawnAmount, component.SpawnRange);
+ Spawn(component.SupercriticalSpawn, xform.Coordinates);
+ }
+
+ private void OnSeverityChanged(EntityUid uid, FleshAnomalyComponent component, ref AnomalyStabilityChangedEvent args)
+ {
+ var xform = Transform(uid);
+ if (!_map.TryGetGrid(xform.GridUid, out var grid))
+ return;
+
+ var radius = component.SpawnRange * args.Stability;
+ var fleshTile = (ContentTileDefinition) _tiledef[component.FleshTileId];
+ var localpos = xform.Coordinates.Position;
+ var tilerefs = grid.GetLocalTilesIntersecting(
+ new Box2(localpos + (-radius, -radius), localpos + (radius, radius)));
+ foreach (var tileref in tilerefs)
+ {
+ if (!_random.Prob(0.33f))
+ continue;
+ _tile.ReplaceTile(tileref, fleshTile);
+ }
+ }
+
+ private void SpawnMonstersOnOpenTiles(FleshAnomalyComponent component, TransformComponent xform, int amount, float radius)
+ {
+ if (!_map.TryGetGrid(xform.GridUid, out var grid))
+ return;
+
+ var localpos = xform.Coordinates.Position;
+ var tilerefs = grid.GetLocalTilesIntersecting(
+ new Box2(localpos + (-radius, -radius), localpos + (radius, radius))).ToArray();
+ _random.Shuffle(tilerefs);
+ var physQuery = GetEntityQuery<PhysicsComponent>();
+ var amountCounter = 0;
+ foreach (var tileref in tilerefs)
+ {
+ var valid = true;
+ foreach (var ent in grid.GetAnchoredEntities(tileref.GridIndices))
+ {
+ if (!physQuery.TryGetComponent(ent, out var body))
+ continue;
+ if (body.BodyType != BodyType.Static ||
+ !body.Hard ||
+ (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
+ continue;
+ valid = false;
+ break;
+ }
+ if (!valid)
+ continue;
+ amountCounter++;
+ Spawn(_random.Pick(component.Spawns), tileref.GridIndices.ToEntityCoordinates(xform.GridUid.Value, _map));
+ if (amountCounter >= amount)
+ return;
+ }
+ }
+}
private void OnPulse(EntityUid uid, PyroclasticAnomalyComponent component, ref AnomalyPulseEvent args)
{
var xform = Transform(uid);
- var ignitionRadius = component.MaximumIgnitionRadius * args.Stabiltiy;
+ var ignitionRadius = component.MaximumIgnitionRadius * args.Stability;
IgniteNearby(xform.Coordinates, args.Severity, ignitionRadius);
}
using Content.Shared.Decals;
using Content.Shared.Maps;
using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
using Robust.Shared.Random;
namespace Content.Server.Maps;
return DeconstructTile(tileRef);
}
+ public bool ReplaceTile(TileRef tileref, ContentTileDefinition replacementTile)
+ {
+ if (!TryComp<MapGridComponent>(tileref.GridUid, out var grid))
+ return false;
+ return ReplaceTile(tileref, replacementTile, tileref.GridUid, grid);
+ }
+
+ public bool ReplaceTile(TileRef tileref, ContentTileDefinition replacementTile, EntityUid grid, MapGridComponent? component = null)
+ {
+ if (!Resolve(grid, ref component))
+ return false;
+
+ var variant = _robustRandom.Pick(replacementTile.PlacementVariants);
+ var decals = _decal.GetDecalsInRange(tileref.GridUid, tileref.GridPosition().SnapToGrid(EntityManager, _mapManager).Position, 0.5f);
+ foreach (var (id, _) in decals)
+ {
+ _decal.RemoveDecal(tileref.GridUid, id);
+ }
+ component.SetTile(tileref.GridIndices, new Tile(replacementTile.TileId, 0, variant));
+ return true;
+ }
+
private bool DeconstructTile(TileRef tileRef)
{
var indices = tileRef.GridIndices;
/// The amount of health lost when the stability is below the <see cref="DecayThreshold"/>
/// </summary>
[DataField("healthChangePerSecond"), ViewVariables(VVAccess.ReadWrite)]
- public float HealthChangePerSecond = -0.05f;
+ public float HealthChangePerSecond = -0.01f;
#endregion
#region Growth
/// This doesn't include the point bonus for being unstable.
/// </summary>
[DataField("maxPointsPerSecond")]
- public int MaxPointsPerSecond = 65;
+ public int MaxPointsPerSecond = 100;
/// <summary>
/// The multiplier applied to the point value for the
/// anomaly being above the <see cref="GrowthThreshold"/>
/// </summary>
[DataField("growingPointMultiplier")]
- public float GrowingPointMultiplier = 1.2f;
-
- /// <summary>
- /// The multiplier applied to the point value for the
- /// anomaly being below the <see cref="DecayThreshold"/>
- /// </summary>
- [DataField("decayingPointMultiplier")]
- public float DecayingPointMultiplier = 0.75f;
+ public float GrowingPointMultiplier = 1.5f;
#endregion
/// <summary>
/// </summary>
[DataField("anomalyContactDamageSound")]
public SoundSpecifier AnomalyContactDamageSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
+
+ #region Floating Animation
+ /// <summary>
+ /// How long it takes to go from the bottom of the animation to the top.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("animationTime")]
+ public readonly float AnimationTime = 2f;
+
+ /// <summary>
+ /// How far it goes in any direction.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("offset")]
+ public readonly Vector2 FloatingOffset = (0, 0.15f);
+
+ public readonly string AnimationKey = "anomalyfloat";
+ #endregion
}
[Serializable, NetSerializable]
/// <summary>
/// Event raised at regular intervals on an anomaly to do whatever its effect is.
/// </summary>
-/// <param name="Stabiltiy"></param>
+/// <param name="Stability"></param>
/// <param name="Severity"></param>
[ByRefEvent]
-public readonly record struct AnomalyPulseEvent(float Stabiltiy, float Severity)
-{
- public readonly float Stabiltiy = Stabiltiy;
- public readonly float Severity = Severity;
-}
+public readonly record struct AnomalyPulseEvent(float Stability, float Severity);
/// <summary>
/// Event raised on an anomaly when it reaches a supercritical point.
--- /dev/null
+using System.Linq;
+using Content.Shared.Anomaly.Components;
+using Content.Shared.Anomaly.Effects.Components;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Teleportation.Components;
+using Robust.Shared.Map;
+using Robust.Shared.Random;
+
+namespace Content.Shared.Anomaly.Effects;
+
+public sealed class BluespaceAnomalySystem : EntitySystem
+{
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly EntityLookupSystem _lookup = default!;
+ [Dependency] private readonly SharedTransformSystem _xform = default!;
+
+ /// <inheritdoc/>
+ public override void Initialize()
+ {
+ SubscribeLocalEvent<BluespaceAnomalyComponent, AnomalyPulseEvent>(OnPulse);
+ SubscribeLocalEvent<BluespaceAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
+ SubscribeLocalEvent<BluespaceAnomalyComponent, AnomalySeverityChangedEvent>(OnSeverityChanged);
+ }
+
+ private void OnPulse(EntityUid uid, BluespaceAnomalyComponent component, ref AnomalyPulseEvent args)
+ {
+ var xform = Transform(uid);
+ var range = component.MaxShuffleRadius * args.Severity;
+ var allEnts = _lookup.GetComponentsInRange<MobStateComponent>(xform.Coordinates, range)
+ .Select(x => x.Owner).ToList();
+ allEnts.Add(uid);
+
+ var xformQuery = GetEntityQuery<TransformComponent>();
+ var coords = new List<EntityCoordinates>();
+ foreach (var ent in allEnts)
+ {
+ if (xformQuery.TryGetComponent(ent, out var xf))
+ coords.Add(xf.Coordinates);
+ }
+
+ _random.Shuffle(coords);
+ for (var i = 0; i < allEnts.Count; i++)
+ {
+ _xform.SetCoordinates(allEnts[i], coords[i]);
+ }
+ }
+
+ private void OnSupercritical(EntityUid uid, BluespaceAnomalyComponent component, ref AnomalySupercriticalEvent args)
+ {
+ var xform = Transform(uid);
+ var mapPos = _xform.GetWorldPosition(xform);
+ var radius = component.SupercriticalTeleportRadius;
+ var gridBounds = new Box2(mapPos - (radius, radius), mapPos + (radius, radius));
+ foreach (var comp in _lookup.GetComponentsInRange<MobStateComponent>(xform.Coordinates, component.MaxShuffleRadius))
+ {
+ var ent = comp.Owner;
+ var randomX = _random.NextFloat(gridBounds.Left, gridBounds.Right);
+ var randomY = _random.NextFloat(gridBounds.Bottom, gridBounds.Top);
+
+ var pos = new Vector2(randomX, randomY);
+ _xform.SetWorldPosition(ent, pos);
+ _audio.PlayPvs(component.TeleportSound, ent);
+ }
+ }
+
+ private void OnSeverityChanged(EntityUid uid, BluespaceAnomalyComponent component, ref AnomalySeverityChangedEvent args)
+ {
+ if (!TryComp<PortalComponent>(uid, out var portal))
+ return;
+ portal.MaxRandomRadius = (component.MaxPortalRadius - component.MinPortalRadius) * args.Severity + component.MinPortalRadius;
+ }
+}
--- /dev/null
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Anomaly.Effects.Components;
+
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(BluespaceAnomalySystem))]
+public sealed class BluespaceAnomalyComponent : Component
+{
+ /// <summary>
+ /// The maximum radius that the shuffle effect will extend for
+ /// scales with stability
+ /// </summary>
+ [DataField("maxShuffleRadius"), ViewVariables(VVAccess.ReadWrite)]
+ public float MaxShuffleRadius = 10;
+
+ /// <summary>
+ /// The maximum MAX distance the portal this anomaly is tied to can teleport you.
+ /// </summary>
+ [DataField("maxPortalRadius"), ViewVariables(VVAccess.ReadWrite)]
+ public float MaxPortalRadius = 25;
+
+ /// <summary>
+ /// The minimum MAX distance the portal this anomaly is tied to can teleport you.
+ /// </summary>
+ [DataField("minPortalRadius"), ViewVariables(VVAccess.ReadWrite)]
+ public float MinPortalRadius = 10;
+
+ /// <summary>
+ /// How far the supercritical event can teleport you
+ /// </summary>
+ [DataField("superCriticalTeleportRadius"), ViewVariables(VVAccess.ReadWrite)]
+ public float SupercriticalTeleportRadius = 50f;
+
+ /// <summary>
+ /// The sound played after players are shuffled/teleported around
+ /// </summary>
+ [DataField("teleportSound"), ViewVariables(VVAccess.ReadWrite)]
+ public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg");
+}
-namespace Content.Shared.Anomaly.Effects.Components;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Anomaly.Effects.Components;
[RegisterComponent]
public sealed class ElectricityAnomalyComponent : Component
{
+ /// <summary>
+ /// The maximum radius of the passive electrocution effect
+ /// scales with stability
+ /// </summary>
[DataField("maxElectrocutionRange"), ViewVariables(VVAccess.ReadWrite)]
- public float MaxElectrocuteRange = 6f;
+ public float MaxElectrocuteRange = 7f;
+ /// <summary>
+ /// The maximum amount of damage the electrocution can do
+ /// scales with severity
+ /// </summary>
[DataField("maxElectrocuteDamage"), ViewVariables(VVAccess.ReadWrite)]
public float MaxElectrocuteDamage = 20f;
+ /// <summary>
+ /// The maximum amount of time the electrocution lasts
+ /// scales with severity
+ /// </summary>
[DataField("maxElectrocuteDuration"), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan MaxElectrocuteDuration = TimeSpan.FromSeconds(8);
+
+ /// <summary>
+ /// The maximum chance that each second, when in range of the anomaly, you will be electrocuted.
+ /// scales with stability
+ /// </summary>
+ [DataField("passiveElectrocutionChance"), ViewVariables(VVAccess.ReadWrite)]
+ public float PassiveElectrocutionChance = 0.05f;
+
+ /// <summary>
+ /// Used for tracking seconds, so that we can shock people in a non-tick-dependent way.
+ /// </summary>
+ [DataField("nextSecond", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan NextSecond = TimeSpan.Zero;
}
--- /dev/null
+using Content.Shared.Maps;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+
+namespace Content.Shared.Anomaly.Effects.Components;
+
+[RegisterComponent]
+public sealed class FleshAnomalyComponent : Component
+{
+ /// <summary>
+ /// A list of entities that are random picked to be spawned on each pulse
+ /// </summary>
+ [DataField("spawns", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
+ public List<string> Spawns = new();
+
+ /// <summary>
+ /// The maximum number of entities that spawn per pulse
+ /// scales with severity.
+ /// </summary>
+ [DataField("maxSpawnAmount"), ViewVariables(VVAccess.ReadWrite)]
+ public int MaxSpawnAmount = 8;
+
+ /// <summary>
+ /// The maximum radius the entities will spawn in.
+ /// Also governs the maximum reach of flesh tiles
+ /// scales with stability
+ /// </summary>
+ [DataField("spawnRange"), ViewVariables(VVAccess.ReadWrite)]
+ public float SpawnRange = 4f;
+
+ /// <summary>
+ /// The tile that is spawned by the anomaly's effect
+ /// </summary>
+ [DataField("fleshTileId", customTypeSerializer: typeof(PrototypeIdSerializer<ContentTileDefinition>)), ViewVariables(VVAccess.ReadWrite)]
+ public string FleshTileId = "FloorFlesh";
+
+ /// <summary>
+ /// The entity spawned when the anomaly goes supercritical
+ /// </summary>
+ [DataField("superCriticalSpawn", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
+ public string SupercriticalSpawn = "FleshKudzu";
+}
-namespace Content.Shared.Anomaly.Effects.Components;
+using Robust.Shared.GameStates;
-[RegisterComponent]
+namespace Content.Shared.Anomaly.Effects.Components;
+
+[RegisterComponent, NetworkedComponent]
public sealed class GravityAnomalyComponent : Component
{
/// <summary>
/// I have no clue if this is balanced.
/// </remarks>
[DataField("heatPerSecond")]
- public float HeatPerSecond = 50;
+ public float HeatPerSecond = 25;
/// <summary>
/// The maximum distance from which you can be ignited by the anomaly.
/// The amount of gas released when the anomaly goes supercritical
/// </summary>
[DataField("supercriticalMoleAmount")]
- public float SupercriticalMoleAmount = 50f;
+ public float SupercriticalMoleAmount = 75f;
}
foreach (var ent in lookup)
{
var tempXform = Transform(ent);
-
var foo = tempXform.MapPosition.Position - xform.MapPosition.Position;
- _throwing.TryThrow(ent, foo.Normalized * 10, strength, uid, 0);
+ _throwing.TryThrow(ent, foo * 10, strength, uid, 0);
}
}
var tempXform = Transform(ent);
var foo = tempXform.MapPosition.Position - xform.MapPosition.Position;
- Logger.Debug($"{ToPrettyString(ent)}: {foo}: {foo.Normalized}: {foo.Normalized * 10}");
_throwing.TryThrow(ent, foo * 5, strength, uid, 0);
}
}
if (!Resolve(uid, ref component))
return;
+ if (!Timing.IsFirstTimePredicted)
+ 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;
{
if (!Resolve(uid, ref component))
return;
+
+ if (!Timing.IsFirstTimePredicted)
+ return;
+
Audio.PlayPvs(component.SupercriticalSound, uid);
var ev = new AnomalySupercriticalEvent();
tiles-asteroid-ironsand-rock = asteroid ironsand rock
tiles-cave = cave
tiles-cave-drought = cave drought
+tiles-flesh-floor = flesh floor
tiles-techmaint3-floor = grated maintenance floor
tiles-techmaint2-floor = steel maintenance floor
tiles-wood2 = wood pattern floor
- AnomalyPyroclastic
- AnomalyGravity
- AnomalyElectricity
+ - AnomalyFlesh
+ - AnomalyBluespace
chance: 1
--- /dev/null
+- type: entity
+ parent: SimpleMobBase
+ id: BaseMobFlesh
+ name: aberrant flesh
+ description: A shambling mass of flesh, animated through anomalous energy.
+ abstract: true
+ components:
+ - type: HTN
+ rootTask: SimpleHostileCompound
+ - type: Faction
+ factions:
+ - SimpleHostile
+ - type: Tag
+ tags:
+ - DoorBumpOpener
+ - Flesh
+ - type: Sprite
+ drawdepth: Mobs
+ sprite: Mobs/Aliens/flesh.rsi
+ - type: MovementAlwaysTouching
+ - type: MovementSpeedModifier
+ baseWalkSpeed: 1
+ baseSprintSpeed: 1.5
+ - type: MobState
+ allowedStates:
+ - Alive
+ - Dead
+ - type: MobThresholds
+ thresholds:
+ 0: Alive
+ 75: Dead
+ - type: Stamina
+ excess: 50
+ - type: Appearance
+ - type: Butcherable
+ spawned:
+ - id: FoodMeat
+ amount: 1
+ - type: Bloodstream
+ bloodMaxVolume: 500
+ - type: CombatMode
+ disarmAction:
+ enabled: false
+ autoPopulate: false
+ name: action-name-disarm
+ - type: MeleeWeapon
+ hidden: true
+ soundHit:
+ path: /Audio/Weapons/Xeno/alien_claw_flesh3.ogg
+ angle: 0
+ animation: WeaponArcClaw
+ damage:
+ types:
+ Slash: 3
+ - type: ReplacementAccent
+ accent: genericAggressive
+
+- type: entity
+ parent: BaseMobFlesh
+ id: MobFleshJared
+ components:
+ - type: Sprite
+ layers:
+ - map: [ "enum.DamageStateVisualLayers.Base" ]
+ state: jared
+ - type: DamageStateVisuals
+ states:
+ Alive:
+ Base: jared
+ Critical:
+ Base: dead
+ Dead:
+ Base: dead
+ - type: MeleeWeapon
+ hidden: true
+ soundHit:
+ path: /Audio/Weapons/Xeno/alien_claw_flesh3.ogg
+ angle: 0
+ animation: WeaponArcClaw
+ damage:
+ types:
+ Slash: 5
+
+- type: entity
+ parent: BaseMobFlesh
+ id: MobFleshGolem
+ components:
+ - type: Sprite
+ layers:
+ - map: [ "enum.DamageStateVisualLayers.Base" ]
+ state: golem
+ - type: DamageStateVisuals
+ states:
+ Alive:
+ Base: golem
+ Critical:
+ Base: dead
+ Dead:
+ Base: dead
+ - type: MobThresholds
+ thresholds:
+ 0: Alive
+ 50: Dead
+ - type: MeleeWeapon
+ hidden: true
+ soundHit:
+ path: /Audio/Weapons/Xeno/alien_claw_flesh3.ogg
+ angle: 0
+ animation: WeaponArcClaw
+ damage:
+ types:
+ Slash: 5
+
+- type: entity
+ parent: BaseMobFlesh
+ id: MobFleshClamp
+ components:
+ - type: Sprite
+ layers:
+ - map: [ "enum.DamageStateVisualLayers.Base" ]
+ state: clamp
+ - type: DamageStateVisuals
+ states:
+ Alive:
+ Base: clamp
+ Critical:
+ Base: dead
+ Dead:
+ Base: dead
+ - type: MobThresholds
+ thresholds:
+ 0: Alive
+ 30: Dead
+ - type: MovementSpeedModifier
+ baseWalkSpeed: 2
+ baseSprintSpeed: 2.5
+
+- type: entity
+ parent: BaseMobFlesh
+ id: MobFleshLover
+ components:
+ - type: Sprite
+ layers:
+ - map: [ "enum.DamageStateVisualLayers.Base" ]
+ state: lover
+ - type: DamageStateVisuals
+ states:
+ Alive:
+ Base: lover
+ Critical:
+ Base: dead
+ Dead:
+ Base: dead
+ - type: MobThresholds
+ thresholds:
+ 0: Alive
+ 30: Dead
+ - type: MovementSpeedModifier
+ baseWalkSpeed: 2
+ baseSprintSpeed: 2.5
"/Audio/Weapons/slash.ogg"
- type: Sprite
sprite: Objects/Misc/kudzu.rsi
+ color: "#ff0000"
state: kudzu_11
drawdepth: Overdoors
netsync: false
- type: SlowContacts
walkSpeedModifier: 0.2
sprintSpeedModifier: 0.2
+
+- type: entity
+ id: FleshKudzu
+ name: tendons
+ description: A rapidly growing cluster of meaty tendons. WHY ARE YOU STOPPING TO LOOK AT IT?!
+ placement:
+ mode: SnapgridCenter
+ snap:
+ - Wall
+ components:
+ - type: MeleeSound
+ soundGroups:
+ Brute:
+ path:
+ "/Audio/Weapons/slash.ogg"
+ - type: Sprite
+ sprite: Objects/Misc/fleshkudzu.rsi
+ state: base
+ drawdepth: Overdoors
+ netsync: false
+ - type: Appearance
+ - type: Clickable
+ - type: Transform
+ anchored: true
+ - type: Physics
+ - type: Fixtures
+ fixtures:
+ - hard: false
+ density: 7
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.5,-0.5,0.5,0.5"
+ layer:
+ - MidImpassable
+ - type: Damageable
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 10
+ behaviors:
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - type: Spreader
+ growthResult: FleshKudzu
+ chance: 1
+ - type: SlowContacts
+ walkSpeedModifier: 0.2
+ sprintSpeedModifier: 0.2
+ ignoreWhitelist:
+ tags:
+ - Flesh
--- /dev/null
+- type: entity
+ id: FleshBlocker
+ parent: BaseStructure
+ name: flesh clump
+ description: An annoying clump of flesh.
+ components:
+ - type: InteractionOutline
+ - type: Sprite
+ noRot: true
+ sprite: Structures/Decoration/flesh_decoration.rsi
+ layers:
+ - state: closed
+ map: [ "enum.DamageStateVisualLayers.Base" ]
+ - type: Fixtures
+ fixtures:
+ - shape:
+ !type:PhysShapeCircle
+ radius: 0.3
+ density: 190
+ mask:
+ - MachineMask
+ layer:
+ - Impassable
+ - type: RandomSprite
+ available:
+ - enum.DamageStateVisualLayers.Base:
+ closed: ""
+ - enum.DamageStateVisualLayers.Base:
+ ajar: ""
+ - enum.DamageStateVisualLayers.Base:
+ open: ""
+ - type: Damageable
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 25
+ behaviors:
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
- type: Transform
anchored: true
- type: Physics
- bodyType: Static
+ bodyType: Static
- type: Fixtures
fixtures:
- shape:
- MobLayer
- type: Sprite
netsync: false
- drawdepth: Items
+ noRot: true
+ drawdepth: Effects #it needs to draw over stuff.
sprite: Structures/Specific/anomaly.rsi
- type: InteractionOutline
- type: Clickable
- type: Damageable
- type: Appearance
+ - type: AnimationPlayer
- type: GuideHelp
guides:
- AnomalousResearch
suffix: Gravity
components:
- type: Sprite
- drawdepth: Effects #it needs to draw over stuff.
layers:
- state: anom2
map: ["enum.AnomalyVisualLayers.Base"]
color: "#ffffaa"
castShadows: false
- type: ElectricityAnomaly
- - type: Electrified
\ No newline at end of file
+ - type: Electrified
+
+- type: entity
+ id: AnomalyFlesh
+ parent: BaseAnomaly
+ suffix: Flesh
+ components:
+ - type: Sprite
+ layers:
+ - state: anom5
+ map: ["enum.AnomalyVisualLayers.Base"]
+ - state: anom5-pulse
+ map: ["enum.AnomalyVisualLayers.Animated"]
+ visible: false
+ - type: PointLight
+ radius: 2.0
+ energy: 7.5
+ color: "#cb5b7e"
+ castShadows: false
+ - type: FleshAnomaly
+ spawns:
+ - MobFleshJared
+ - MobFleshGolem
+ - MobFleshClamp
+ - MobFleshLover
+ - FleshBlocker
+
+- type: entity
+ id: AnomalyBluespace
+ parent: BaseAnomaly
+ suffix: Bluespace
+ components:
+ - type: Sprite
+ layers:
+ - state: anom4
+ map: ["enum.AnomalyVisualLayers.Base"]
+ - state: anom4-pulse
+ map: ["enum.AnomalyVisualLayers.Animated"]
+ visible: false
+ - type: PointLight
+ radius: 2.0
+ energy: 7.5
+ color: "#00ccff"
+ castShadows: false
+ - type: BluespaceAnomaly
+ - type: Portal
+ - type: Fixtures
+ fixtures:
+ - shape:
+ !type:PhysShapeCircle
+ radius: 0.35
+ density: 50
+ mask:
+ - MobMask
+ layer:
+ - MobLayer
+ - id: portalFixture
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.25,-0.48,0.25,0.48"
+ mask:
+ - FullTileMask
+ layer:
+ - WallLayer
+ hard: false
+ - type: Anomaly
+ pulseSound:
+ collection: RadiationPulse
+ params:
+ volume: 5
+ anomalyContactDamage:
+ types:
+ Radiation: 10
thermalConductivity: 0.04
heatCapacity: 10000
+- type: tile
+ id: FloorFlesh
+ name: tiles-flesh-floor
+ sprite: /Textures/Tiles/meat.png
+ variants: 4
+ placementVariants: [0, 1, 2, 3]
+ baseTurfs:
+ - Plating
+ isSubfloor: false
+ canCrowbar: true
+ footstepSounds:
+ collection: BarestepCarpet
+ friction: 0.20 #slippy
+ thermalConductivity: 0.04
+ heatCapacity: 10000
+
- type: tile
id: FloorTechMaint2
name: tiles-techmaint2-floor
- type: Tag
id: FireAxe
+- type: Tag
+ id: Flesh
+
- type: Tag
id: WhitelistChameleon
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Created by EmoGarbage404 (github) for space-station-14, credit to Aleksh#7552 (discord) for original concepts and designs",
+ "states": [
+ {
+ "name": "clamp"
+ },
+ {
+ "name": "dead"
+ },
+ {
+ "name": "golem",
+ "directions": 4
+ },
+ {
+ "name": "jared"
+ },
+ {
+ "name": "lover"
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "license": "CC0-1.0",
+ "copyright": "Created by EmoGarbage404 (github) for space-station-14",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "base"
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Created by Aleksh#7552 (discord) for space-station-14",
+ "states": [
+ {
+ "name": "ajar"
+ },
+ {
+ "name": "closed"
+ },
+ {
+ "name": "open"
+ }
+ ]
+}
{
"version": 1,
"license": "CC0-1.0",
- "copyright": "Created by EmoGarbage; anom3, anom3-pulse, anom4, anom4-pulse are CC-BY-SA-3.0 at https://github.com/ParadiseSS13/Paradise/blob/master/icons/effects/effects.dmi",
+ "copyright": "Created by EmoGarbage; anom3, anom3-pulse, anom4, anom4-pulse are CC-BY-SA-3.0 at https://github.com/ParadiseSS13/Paradise/blob/master/icons/effects/effects.dmi; anom5, anom5-pulse are CC-BY-SA-3.0 by Aleksh#7552 (discord) for space-station-14",
"size": {
"x": 32,
"y": 32
0.15
]
]
+ },
+ {
+ "name": "anom5"
+ },
+ {
+ "name": "anom5-pulse",
+ "delays": [
+ [
+ 0.25,
+ 0.25,
+ 0.25,
+ 0.25
+ ]
+ ]
}
]
-}
\ No newline at end of file
+}
copyright: "by brainfood for space-station-14, ."
source: "https://github.com/space-wizards/space-station-14/pull/12193"
+- files: ["meat.png"]
+ license: "CC0-1.0"
+ copyright: "Created by EmoGarbage404 (github) for space-station-14."
+ source: "https://github.com/space-wizards/space-station-14/pull/13766"