From f450398df70dffdeee802926d36e34336157d42c Mon Sep 17 00:00:00 2001
From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Date: Mon, 6 Feb 2023 00:03:53 -0500
Subject: [PATCH] More anomalies (#13766)
---
Content.Client/Anomaly/AnomalySystem.cs | 18 +-
.../Anomaly/AnomalySystem.Generator.cs | 27 ++-
Content.Server/Anomaly/AnomalySystem.cs | 7 +-
.../Effects/ElectricityAnomalySystem.cs | 42 ++++-
.../Anomaly/Effects/FleshAnomalySystem.cs | 97 +++++++++++
.../Effects/PyroclasticAnomalySystem.cs | 2 +-
Content.Server/Maps/TileSystem.cs | 23 +++
.../Anomaly/Components/AnomalyComponent.cs | 39 +++--
.../Anomaly/Effects/BluespaceAnomalySystem.cs | 73 ++++++++
.../Components/BluespaceAnomalyComponent.cs | 40 +++++
.../Components/ElectricityAnomalyComponent.cs | 31 +++-
.../Components/FleshAnomalyComponent.cs | 43 +++++
.../Components/GravityAnomalyComponent.cs | 6 +-
.../Components/PyroclasticAnomalyComponent.cs | 4 +-
.../Effects/SharedGravityAnomalySystem.cs | 4 +-
Content.Shared/Anomaly/SharedAnomalySystem.cs | 7 +
Resources/Locale/en-US/tiles/tiles.ftl | 1 +
.../Markers/Spawners/Random/anomaly.yml | 2 +
.../Prototypes/Entities/Mobs/NPCs/flesh.yml | 160 ++++++++++++++++++
.../Entities/Objects/Misc/kudzu.yml | 53 ++++++
.../Structures/Decoration/flesh_blockers.yml | 40 +++++
.../Structures/Specific/anomalies.yml | 81 ++++++++-
Resources/Prototypes/Tiles/floors.yml | 16 ++
Resources/Prototypes/tags.yml | 3 +
.../Textures/Mobs/Aliens/flesh.rsi/clamp.png | Bin 0 -> 613 bytes
.../Textures/Mobs/Aliens/flesh.rsi/dead.png | Bin 0 -> 311 bytes
.../Textures/Mobs/Aliens/flesh.rsi/golem.png | Bin 0 -> 1620 bytes
.../Textures/Mobs/Aliens/flesh.rsi/jared.png | Bin 0 -> 741 bytes
.../Textures/Mobs/Aliens/flesh.rsi/lover.png | Bin 0 -> 723 bytes
.../Textures/Mobs/Aliens/flesh.rsi/meta.json | 27 +++
.../Objects/Misc/fleshkudzu.rsi/base.png | Bin 0 -> 1047 bytes
.../Objects/Misc/fleshkudzu.rsi/meta.json | 14 ++
.../Decoration/flesh_decoration.rsi/ajar.png | Bin 0 -> 508 bytes
.../flesh_decoration.rsi/closed.png | Bin 0 -> 505 bytes
.../Decoration/flesh_decoration.rsi/meta.json | 20 +++
.../Decoration/flesh_decoration.rsi/open.png | Bin 0 -> 436 bytes
.../Specific/anomaly.rsi/anom5-pulse.png | Bin 0 -> 1195 bytes
.../Structures/Specific/anomaly.rsi/anom5.png | Bin 0 -> 429 bytes
.../Structures/Specific/anomaly.rsi/meta.json | 18 +-
Resources/Textures/Tiles/attributions.yml | 4 +
Resources/Textures/Tiles/meat.png | Bin 0 -> 1858 bytes
41 files changed, 855 insertions(+), 47 deletions(-)
create mode 100644 Content.Server/Anomaly/Effects/FleshAnomalySystem.cs
create mode 100644 Content.Shared/Anomaly/Effects/BluespaceAnomalySystem.cs
create mode 100644 Content.Shared/Anomaly/Effects/Components/BluespaceAnomalyComponent.cs
create mode 100644 Content.Shared/Anomaly/Effects/Components/FleshAnomalyComponent.cs
create mode 100644 Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml
create mode 100644 Resources/Prototypes/Entities/Structures/Decoration/flesh_blockers.yml
create mode 100644 Resources/Textures/Mobs/Aliens/flesh.rsi/clamp.png
create mode 100644 Resources/Textures/Mobs/Aliens/flesh.rsi/dead.png
create mode 100644 Resources/Textures/Mobs/Aliens/flesh.rsi/golem.png
create mode 100644 Resources/Textures/Mobs/Aliens/flesh.rsi/jared.png
create mode 100644 Resources/Textures/Mobs/Aliens/flesh.rsi/lover.png
create mode 100644 Resources/Textures/Mobs/Aliens/flesh.rsi/meta.json
create mode 100644 Resources/Textures/Objects/Misc/fleshkudzu.rsi/base.png
create mode 100644 Resources/Textures/Objects/Misc/fleshkudzu.rsi/meta.json
create mode 100644 Resources/Textures/Structures/Decoration/flesh_decoration.rsi/ajar.png
create mode 100644 Resources/Textures/Structures/Decoration/flesh_decoration.rsi/closed.png
create mode 100644 Resources/Textures/Structures/Decoration/flesh_decoration.rsi/meta.json
create mode 100644 Resources/Textures/Structures/Decoration/flesh_decoration.rsi/open.png
create mode 100644 Resources/Textures/Structures/Specific/anomaly.rsi/anom5-pulse.png
create mode 100644 Resources/Textures/Structures/Specific/anomaly.rsi/anom5.png
create mode 100644 Resources/Textures/Tiles/meat.png
diff --git a/Content.Client/Anomaly/AnomalySystem.cs b/Content.Client/Anomaly/AnomalySystem.cs
index 57f1f90f10..b8e3e74393 100644
--- a/Content.Client/Anomaly/AnomalySystem.cs
+++ b/Content.Client/Anomaly/AnomalySystem.cs
@@ -1,4 +1,5 @@
-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;
@@ -8,6 +9,7 @@ namespace Content.Client.Anomaly;
public sealed class AnomalySystem : SharedAnomalySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly FloatingVisualizerSystem _floating = default!;
///
public override void Initialize()
@@ -15,6 +17,20 @@ public sealed class AnomalySystem : SharedAnomalySystem
base.Initialize();
SubscribeLocalEvent(OnAppearanceChanged);
+ SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(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)
diff --git a/Content.Server/Anomaly/AnomalySystem.Generator.cs b/Content.Server/Anomaly/AnomalySystem.Generator.cs
index 2baacf6b52..7a2deb8611 100644
--- a/Content.Server/Anomaly/AnomalySystem.Generator.cs
+++ b/Content.Server/Anomaly/AnomalySystem.Generator.cs
@@ -4,7 +4,10 @@ using Content.Server.Power.EntitySystems;
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;
@@ -91,12 +94,32 @@ public sealed partial class AnomalySystem
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();
+ 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;
}
diff --git a/Content.Server/Anomaly/AnomalySystem.cs b/Content.Server/Anomaly/AnomalySystem.cs
index 8dce08c406..e48fc328c4 100644
--- a/Content.Server/Anomaly/AnomalySystem.cs
+++ b/Content.Server/Anomaly/AnomalySystem.cs
@@ -23,7 +23,6 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
[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;
@@ -100,13 +99,13 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
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;
}
///
diff --git a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs
index 55c7b1bfa5..6c473d3570 100644
--- a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs
+++ b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs
@@ -6,11 +6,13 @@ using Content.Shared.Anomaly.Effects.Components;
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!;
@@ -25,16 +27,12 @@ public sealed class ElectricityAnomalySystem : EntitySystem
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(xform.MapPosition, range))
+ foreach (var comp in _lookup.GetComponentsInRange(xform.MapPosition, range))
{
var ent = comp.Owner;
-
- _electrocution.TryDoElectrocution(ent, uid, damage, duration, true, statusEffects: comp, ignoreInsulation: true);
+ _lightning.ShootLightning(uid, ent);
}
}
@@ -48,7 +46,7 @@ public sealed class ElectricityAnomalySystem : EntitySystem
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);
}
@@ -58,4 +56,32 @@ public sealed class ElectricityAnomalySystem : EntitySystem
_lightning.ShootLightning(uid, ent);
}
}
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ foreach (var (elec, anom, xform) in EntityQuery())
+ {
+ 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(xform.MapPosition, range))
+ {
+ var ent = comp.Owner;
+
+ _electrocution.TryDoElectrocution(ent, owner, damage, duration, true, statusEffects: comp, ignoreInsulation: true);
+ }
+ }
+ }
}
diff --git a/Content.Server/Anomaly/Effects/FleshAnomalySystem.cs b/Content.Server/Anomaly/Effects/FleshAnomalySystem.cs
new file mode 100644
index 0000000000..227721fd7a
--- /dev/null
+++ b/Content.Server/Anomaly/Effects/FleshAnomalySystem.cs
@@ -0,0 +1,97 @@
+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!;
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnPulse);
+ SubscribeLocalEvent(OnSupercritical);
+ SubscribeLocalEvent(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();
+ 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;
+ }
+ }
+}
diff --git a/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs b/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs
index 12f0b31343..8e656a58c8 100644
--- a/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs
+++ b/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs
@@ -29,7 +29,7 @@ public sealed class PyroclasticAnomalySystem : EntitySystem
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);
}
diff --git a/Content.Server/Maps/TileSystem.cs b/Content.Server/Maps/TileSystem.cs
index 0b888cf225..ddf5efc7f1 100644
--- a/Content.Server/Maps/TileSystem.cs
+++ b/Content.Server/Maps/TileSystem.cs
@@ -3,6 +3,7 @@ using Content.Server.Decals;
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;
@@ -54,6 +55,28 @@ public sealed class TileSystem : EntitySystem
return DeconstructTile(tileRef);
}
+ public bool ReplaceTile(TileRef tileref, ContentTileDefinition replacementTile)
+ {
+ if (!TryComp(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;
diff --git a/Content.Shared/Anomaly/Components/AnomalyComponent.cs b/Content.Shared/Anomaly/Components/AnomalyComponent.cs
index cdee844b1f..50f128d145 100644
--- a/Content.Shared/Anomaly/Components/AnomalyComponent.cs
+++ b/Content.Shared/Anomaly/Components/AnomalyComponent.cs
@@ -62,7 +62,7 @@ public sealed class AnomalyComponent : Component
/// The amount of health lost when the stability is below the
///
[DataField("healthChangePerSecond"), ViewVariables(VVAccess.ReadWrite)]
- public float HealthChangePerSecond = -0.05f;
+ public float HealthChangePerSecond = -0.01f;
#endregion
#region Growth
@@ -208,21 +208,14 @@ public sealed class AnomalyComponent : Component
/// This doesn't include the point bonus for being unstable.
///
[DataField("maxPointsPerSecond")]
- public int MaxPointsPerSecond = 65;
+ public int MaxPointsPerSecond = 100;
///
/// The multiplier applied to the point value for the
/// anomaly being above the
///
[DataField("growingPointMultiplier")]
- public float GrowingPointMultiplier = 1.2f;
-
- ///
- /// The multiplier applied to the point value for the
- /// anomaly being below the
- ///
- [DataField("decayingPointMultiplier")]
- public float DecayingPointMultiplier = 0.75f;
+ public float GrowingPointMultiplier = 1.5f;
#endregion
///
@@ -238,6 +231,24 @@ public sealed class AnomalyComponent : Component
///
[DataField("anomalyContactDamageSound")]
public SoundSpecifier AnomalyContactDamageSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
+
+ #region Floating Animation
+ ///
+ /// How long it takes to go from the bottom of the animation to the top.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("animationTime")]
+ public readonly float AnimationTime = 2f;
+
+ ///
+ /// How far it goes in any direction.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("offset")]
+ public readonly Vector2 FloatingOffset = (0, 0.15f);
+
+ public readonly string AnimationKey = "anomalyfloat";
+ #endregion
}
[Serializable, NetSerializable]
@@ -260,14 +271,10 @@ public sealed class AnomalyComponentState : ComponentState
///
/// Event raised at regular intervals on an anomaly to do whatever its effect is.
///
-///
+///
///
[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);
///
/// Event raised on an anomaly when it reaches a supercritical point.
diff --git a/Content.Shared/Anomaly/Effects/BluespaceAnomalySystem.cs b/Content.Shared/Anomaly/Effects/BluespaceAnomalySystem.cs
new file mode 100644
index 0000000000..920cc26e52
--- /dev/null
+++ b/Content.Shared/Anomaly/Effects/BluespaceAnomalySystem.cs
@@ -0,0 +1,73 @@
+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!;
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnPulse);
+ SubscribeLocalEvent(OnSupercritical);
+ SubscribeLocalEvent(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(xform.Coordinates, range)
+ .Select(x => x.Owner).ToList();
+ allEnts.Add(uid);
+
+ var xformQuery = GetEntityQuery();
+ var coords = new List();
+ 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(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(uid, out var portal))
+ return;
+ portal.MaxRandomRadius = (component.MaxPortalRadius - component.MinPortalRadius) * args.Severity + component.MinPortalRadius;
+ }
+}
diff --git a/Content.Shared/Anomaly/Effects/Components/BluespaceAnomalyComponent.cs b/Content.Shared/Anomaly/Effects/Components/BluespaceAnomalyComponent.cs
new file mode 100644
index 0000000000..446194188b
--- /dev/null
+++ b/Content.Shared/Anomaly/Effects/Components/BluespaceAnomalyComponent.cs
@@ -0,0 +1,40 @@
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Anomaly.Effects.Components;
+
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(BluespaceAnomalySystem))]
+public sealed class BluespaceAnomalyComponent : Component
+{
+ ///
+ /// The maximum radius that the shuffle effect will extend for
+ /// scales with stability
+ ///
+ [DataField("maxShuffleRadius"), ViewVariables(VVAccess.ReadWrite)]
+ public float MaxShuffleRadius = 10;
+
+ ///
+ /// The maximum MAX distance the portal this anomaly is tied to can teleport you.
+ ///
+ [DataField("maxPortalRadius"), ViewVariables(VVAccess.ReadWrite)]
+ public float MaxPortalRadius = 25;
+
+ ///
+ /// The minimum MAX distance the portal this anomaly is tied to can teleport you.
+ ///
+ [DataField("minPortalRadius"), ViewVariables(VVAccess.ReadWrite)]
+ public float MinPortalRadius = 10;
+
+ ///
+ /// How far the supercritical event can teleport you
+ ///
+ [DataField("superCriticalTeleportRadius"), ViewVariables(VVAccess.ReadWrite)]
+ public float SupercriticalTeleportRadius = 50f;
+
+ ///
+ /// The sound played after players are shuffled/teleported around
+ ///
+ [DataField("teleportSound"), ViewVariables(VVAccess.ReadWrite)]
+ public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg");
+}
diff --git a/Content.Shared/Anomaly/Effects/Components/ElectricityAnomalyComponent.cs b/Content.Shared/Anomaly/Effects/Components/ElectricityAnomalyComponent.cs
index fc430127ef..9afdd0aaa2 100644
--- a/Content.Shared/Anomaly/Effects/Components/ElectricityAnomalyComponent.cs
+++ b/Content.Shared/Anomaly/Effects/Components/ElectricityAnomalyComponent.cs
@@ -1,14 +1,41 @@
-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
{
+ ///
+ /// The maximum radius of the passive electrocution effect
+ /// scales with stability
+ ///
[DataField("maxElectrocutionRange"), ViewVariables(VVAccess.ReadWrite)]
- public float MaxElectrocuteRange = 6f;
+ public float MaxElectrocuteRange = 7f;
+ ///
+ /// The maximum amount of damage the electrocution can do
+ /// scales with severity
+ ///
[DataField("maxElectrocuteDamage"), ViewVariables(VVAccess.ReadWrite)]
public float MaxElectrocuteDamage = 20f;
+ ///
+ /// The maximum amount of time the electrocution lasts
+ /// scales with severity
+ ///
[DataField("maxElectrocuteDuration"), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan MaxElectrocuteDuration = TimeSpan.FromSeconds(8);
+
+ ///
+ /// The maximum chance that each second, when in range of the anomaly, you will be electrocuted.
+ /// scales with stability
+ ///
+ [DataField("passiveElectrocutionChance"), ViewVariables(VVAccess.ReadWrite)]
+ public float PassiveElectrocutionChance = 0.05f;
+
+ ///
+ /// Used for tracking seconds, so that we can shock people in a non-tick-dependent way.
+ ///
+ [DataField("nextSecond", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan NextSecond = TimeSpan.Zero;
}
diff --git a/Content.Shared/Anomaly/Effects/Components/FleshAnomalyComponent.cs b/Content.Shared/Anomaly/Effects/Components/FleshAnomalyComponent.cs
new file mode 100644
index 0000000000..abd0b01939
--- /dev/null
+++ b/Content.Shared/Anomaly/Effects/Components/FleshAnomalyComponent.cs
@@ -0,0 +1,43 @@
+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
+{
+ ///
+ /// A list of entities that are random picked to be spawned on each pulse
+ ///
+ [DataField("spawns", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public List Spawns = new();
+
+ ///
+ /// The maximum number of entities that spawn per pulse
+ /// scales with severity.
+ ///
+ [DataField("maxSpawnAmount"), ViewVariables(VVAccess.ReadWrite)]
+ public int MaxSpawnAmount = 8;
+
+ ///
+ /// The maximum radius the entities will spawn in.
+ /// Also governs the maximum reach of flesh tiles
+ /// scales with stability
+ ///
+ [DataField("spawnRange"), ViewVariables(VVAccess.ReadWrite)]
+ public float SpawnRange = 4f;
+
+ ///
+ /// The tile that is spawned by the anomaly's effect
+ ///
+ [DataField("fleshTileId", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public string FleshTileId = "FloorFlesh";
+
+ ///
+ /// The entity spawned when the anomaly goes supercritical
+ ///
+ [DataField("superCriticalSpawn", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public string SupercriticalSpawn = "FleshKudzu";
+}
diff --git a/Content.Shared/Anomaly/Effects/Components/GravityAnomalyComponent.cs b/Content.Shared/Anomaly/Effects/Components/GravityAnomalyComponent.cs
index a6f80aeda0..761bc9daec 100644
--- a/Content.Shared/Anomaly/Effects/Components/GravityAnomalyComponent.cs
+++ b/Content.Shared/Anomaly/Effects/Components/GravityAnomalyComponent.cs
@@ -1,6 +1,8 @@
-namespace Content.Shared.Anomaly.Effects.Components;
+using Robust.Shared.GameStates;
-[RegisterComponent]
+namespace Content.Shared.Anomaly.Effects.Components;
+
+[RegisterComponent, NetworkedComponent]
public sealed class GravityAnomalyComponent : Component
{
///
diff --git a/Content.Shared/Anomaly/Effects/Components/PyroclasticAnomalyComponent.cs b/Content.Shared/Anomaly/Effects/Components/PyroclasticAnomalyComponent.cs
index 5f3c1c2595..9cfa56bcc2 100644
--- a/Content.Shared/Anomaly/Effects/Components/PyroclasticAnomalyComponent.cs
+++ b/Content.Shared/Anomaly/Effects/Components/PyroclasticAnomalyComponent.cs
@@ -13,7 +13,7 @@ public sealed class PyroclasticAnomalyComponent : Component
/// I have no clue if this is balanced.
///
[DataField("heatPerSecond")]
- public float HeatPerSecond = 50;
+ public float HeatPerSecond = 25;
///
/// The maximum distance from which you can be ignited by the anomaly.
@@ -50,5 +50,5 @@ public sealed class PyroclasticAnomalyComponent : Component
/// The amount of gas released when the anomaly goes supercritical
///
[DataField("supercriticalMoleAmount")]
- public float SupercriticalMoleAmount = 50f;
+ public float SupercriticalMoleAmount = 75f;
}
diff --git a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs
index d7cf92c17d..09f8ec35ee 100644
--- a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs
+++ b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs
@@ -29,9 +29,8 @@ public abstract class SharedGravityAnomalySystem : EntitySystem
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);
}
}
@@ -54,7 +53,6 @@ public abstract class SharedGravityAnomalySystem : EntitySystem
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);
}
}
diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs
index 6106112a1d..874e28ebcd 100644
--- a/Content.Shared/Anomaly/SharedAnomalySystem.cs
+++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs
@@ -119,6 +119,9 @@ public abstract class SharedAnomalySystem : EntitySystem
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;
@@ -173,6 +176,10 @@ public abstract class SharedAnomalySystem : EntitySystem
{
if (!Resolve(uid, ref component))
return;
+
+ if (!Timing.IsFirstTimePredicted)
+ return;
+
Audio.PlayPvs(component.SupercriticalSound, uid);
var ev = new AnomalySupercriticalEvent();
diff --git a/Resources/Locale/en-US/tiles/tiles.ftl b/Resources/Locale/en-US/tiles/tiles.ftl
index a53fa1988f..974db2ab6e 100644
--- a/Resources/Locale/en-US/tiles/tiles.ftl
+++ b/Resources/Locale/en-US/tiles/tiles.ftl
@@ -82,6 +82,7 @@ tiles-asteroid-ironsand-pebbles = asteroid ironsand pebbles
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
diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml
index a082fae7d6..a21437fd77 100644
--- a/Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml
+++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml
@@ -13,4 +13,6 @@
- AnomalyPyroclastic
- AnomalyGravity
- AnomalyElectricity
+ - AnomalyFlesh
+ - AnomalyBluespace
chance: 1
diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml b/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml
new file mode 100644
index 0000000000..68dde1d1b5
--- /dev/null
+++ b/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml
@@ -0,0 +1,160 @@
+- 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
diff --git a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml
index d2942f45cc..280ce2c7e3 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml
@@ -14,6 +14,7 @@
"/Audio/Weapons/slash.ogg"
- type: Sprite
sprite: Objects/Misc/kudzu.rsi
+ color: "#ff0000"
state: kudzu_11
drawdepth: Overdoors
netsync: false
@@ -77,3 +78,55 @@
- 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
diff --git a/Resources/Prototypes/Entities/Structures/Decoration/flesh_blockers.yml b/Resources/Prototypes/Entities/Structures/Decoration/flesh_blockers.yml
new file mode 100644
index 0000000000..e883177707
--- /dev/null
+++ b/Resources/Prototypes/Entities/Structures/Decoration/flesh_blockers.yml
@@ -0,0 +1,40 @@
+- 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" ]
diff --git a/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml
index 5d57d343a7..1dfeefff36 100644
--- a/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml
+++ b/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml
@@ -20,7 +20,7 @@
- type: Transform
anchored: true
- type: Physics
- bodyType: Static
+ bodyType: Static
- type: Fixtures
fixtures:
- shape:
@@ -33,12 +33,14 @@
- 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
@@ -71,7 +73,6 @@
suffix: Gravity
components:
- type: Sprite
- drawdepth: Effects #it needs to draw over stuff.
layers:
- state: anom2
map: ["enum.AnomalyVisualLayers.Base"]
@@ -105,4 +106,76 @@
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
diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml
index 9043d57afa..a23e9493a4 100644
--- a/Resources/Prototypes/Tiles/floors.yml
+++ b/Resources/Prototypes/Tiles/floors.yml
@@ -1367,6 +1367,22 @@
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
diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml
index 859eff49db..dbb2562846 100644
--- a/Resources/Prototypes/tags.yml
+++ b/Resources/Prototypes/tags.yml
@@ -216,6 +216,9 @@
- type: Tag
id: FireAxe
+- type: Tag
+ id: Flesh
+
- type: Tag
id: WhitelistChameleon
diff --git a/Resources/Textures/Mobs/Aliens/flesh.rsi/clamp.png b/Resources/Textures/Mobs/Aliens/flesh.rsi/clamp.png
new file mode 100644
index 0000000000000000000000000000000000000000..d9085d75b9609deab2fca9c02fb905a185119f9c
GIT binary patch
literal 613
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
XAc?LPld5Ch00000NkvXXu0mjfp4d>`
literal 0
HcmV?d00001
diff --git a/Resources/Textures/Mobs/Aliens/flesh.rsi/lover.png b/Resources/Textures/Mobs/Aliens/flesh.rsi/lover.png
new file mode 100644
index 0000000000000000000000000000000000000000..0b5229c37f85e1d26dbab44f8a8c8cc4298a7b3c
GIT binary patch
literal 723
zcmV;^0xbQBP)Px%jY&j7R9J=WmN97CKoo|5)d7RBX$D2b#aU!;DFh|ZC2NO5>6j^lySP&+ow62n
z^c1pW$WqN+J7)D*cXB61OH7R~hS=B~xQjX{?{Yfd$x0gN;(rm+o!DCE)>`X7
z%X;j9f#WCYv(Mw7M$WwqQo#y&uN0B?I)rM(_q#*DUiLRj^zY9gyPW8PxVb;P}b+$w1tn?ComewmnLn8FOtt_Zzi;9ge?wIvkE|B%K8Xxe!DN_BfFbeh>iyP%
zkVh665(*IV$f8Whh$_lhegFN7=IsnWDYP4sJiCe=cT)t-m{UX<>yn_gA>HJ%Cba8-
zdKSq0k7Zbwjd9>Q88ab|th^)HQU&g|Px&(Md!>R9J3U@q>01_P*FuR+w6~Qnlw==
zDn7u{Hd`QVnT9^HR&Xng>z7iGKBIeAvKrI
zOs%jET0UnIn%JRp=R}rw6v>%_io6o9BD1W=lxt8Z8-+Gx3HMdX-`uJz~jj(_=Oy`X2cmCNg@i$5M{F^W*Zi
zsJm)yfsjZeG6*^Ibv9)XZj|kqf(cR`u|z;#RA2GIxeIE7Ve#-ffm^w3fKqMeiz#1F
zx|wWDndau72q`qNgW0lhDH_TQzzGqYxPY38psUON-2R{g4^8a2@&R={8JCSG=Q!}%
zwfh0dd@%)POLi)glIJQ^OpaxpWhaLwcGml(vRctQ3ZBYFwj`%WinsE>$nSi)Y>*=|
zr1hv@Y^8b^d{PR4*ozLfxE9-k*0wi?S)lB%V=vJbV=B7liCCNTZH}3VV%M78R8{L(QrrhhLbDLAC!yB{+
z;@pJD4;PmfkN5Att&3*eg+k7+F9Iay03C!)WJ1*Bjl>*yWaM)BRYU5#fpj*6WD)
zIG4*QQ!zCKkqn_BSxq-%9Xh&?NTO_qk>A03pRm3w8-s*`k-UDc88td`47KQY4dAp`
z>g7^iK9n&zUhfm@T`&1T^S_Oh&Mu_@xSsR@BOTZ6{}lN3MNsLLZqqAK@gJ%Ko+qZW
Rzv=)0002ovPDHLkV1nH{>MQ^N
literal 0
HcmV?d00001
diff --git a/Resources/Textures/Objects/Misc/fleshkudzu.rsi/meta.json b/Resources/Textures/Objects/Misc/fleshkudzu.rsi/meta.json
new file mode 100644
index 0000000000..fc8bf10c30
--- /dev/null
+++ b/Resources/Textures/Objects/Misc/fleshkudzu.rsi/meta.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "license": "CC0-1.0",
+ "copyright": "Created by EmoGarbage404 (github) for space-station-14",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "base"
+ }
+ ]
+}
diff --git a/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/ajar.png b/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/ajar.png
new file mode 100644
index 0000000000000000000000000000000000000000..c4e0b26773f868de5b8c83c4735288713d350dd4
GIT binary patch
literal 508
zcmVPx$wn;=mR9J=WRlQ2XP#8T*V_KU2Q0~??P!YjFp@XZC-8_I$U%h917P+VLJLOY2O5)8C4)i{*zlB7v<)6&VD>3;X#eCK@Ue)k5*kRd~c4F4Tl>n70U
zmgkTA&ReRSxpf5qfL5!+bR7!4G$OXwA=c%V*Df(Ag&eAq3;<}C*n%q?6952>auN1q
z#^b6arxLNU0IEJrZn_S4W1|9yFlx@%51SQ0FkJ_IW5maea#8pgRuVw=6XD_YF<%S!
zL=sY6L#n7_U}(pdhsS~
zULEmqx7UHrRE%$4@ci+Kp=|~BrfI@hd&quZ?!v%&htAZ%D@R1+l0P<_DgW-#9mVoQ
yF8I%01Vk1!4*;)ID^|qPYZ$IDYAr6#Ac{X(?7M}7!Zo4*0000Px$vq?ljR9J=Wlp#;UKp2J}$VRuU+X$&+G=X3chzS&G8ipkp1d?BX#)9DwU{L7&
z0K|gEfgvH8ThfF!G}$g46hXdAJ2u*~q3Emn^{)56_w>7iT)A@P%Jtv1FzW(sV?B)4
z9rcoF&BCk!09q@nxPg)~%o1X87UQ*UAB=DVg`sy*>a(Ya1H-v+rRUB>*1YoWz?kkwB|xhigk_%(&z9*t=qcZ>rD4f#|Px$Zb?KzR9J=Wl(9;~P!xv0swD-hky`0c8)$LqBKiOwx{7g3f}AgR^g-gNuuk
zDEI(%@6ZS6rkg^8Lm_BO0&SW?9BM+E-lU*|2>09apL@>v@4e>+Boc{4B9TZWe@rSV
zSWjzSRBO52#GGi}qV+3DMXDK95YaqQ(Qs2gFRx_CTFZgnbl&Fjga&k_?Cr1R*+xs=uuM(Y^0T-K^%VLypjpN<4g#jQ}`ME2H%K}$VBXaAY35H
eNVJLM-|+=Qh@R}L-ZlvU0000Px(Wl2OqRCt{2T03qVF%W$l1QMXmW+5nT0vRq`8&Hu8q{;z&iyR}j&;e5A0x1lr
zcA;2++XyNsbplom(|Fb+YDms5*ES6E0S1iaF5lyDH2ll}1qB5K1>JEExYyQ@gAcDQ
z;I&b8{ZN|ui;Vud@=uMy%G%EStv?{*2LQK|YitgCj0Ypk)(d=oeCIh0(Ko;PMW6V*
z8Ug^8^$wTAGXTKW)z4-KXN+4}tLC2?gUZ?@ze?ae;Czdd01^Q}6w-$gXz=sLf%8H0
z{|Q@H<7T~389^Ncxz+jL0UU{;_S?y|w(BJL;C);U7oMm4G0MR&v@nDp-8It&Ud`V&J>LkBv8T4m?M8pO!
zeb*JdADaEP--VDgzy(l!#P^U7#k;>0Ehs1`C@3f>c(ZWEWbH)X
z{Z2630J$^J2X~H!&IY\fA?ha!K_U5yaE6qUfw0K7b3Lc5QyXEx%tM
zz(LQSr9ObjFNbGMPQVARADH)8AHexFXv43n4_H~NCZ`1Ic{Rk!+Uy%6ntfg$km3f=
z@TY>K$^*DHV<~DPmcGk@-%qGW!=EG#f2;Zco)PxTVnTv)>I3}Q=lzDz)(fHrm>Sbu
zJyTMGTR#+tIBqA`ssx1(5*_nCDNz#gf{+n{2H+p?0tKl+$`>OUM!QjQ#yII+J^|GK
z=CD68Ld+wjzJnW~%G$~q0Dy1*{zUf!o$CYm;3W?^uZF+?Ss7yT!nwdt
z8i3Q1oMpYk2K%NwCgy#rW$-3ZrP1cQE(rC*y^rf4T^fE>eZbV1qhO;+Q*T|3sWI6H
zqsrP7ey$-@CPVzaZH0M$J{1^gFWx7*)CXwL=`{E=QXkOvG}eWm4rQ?-X)XVos}G3X
z2XrxfR($~9;jOHF(fqu$S?bf{vV^=OcWCGMZcwhp3knKy@E^obSQm=gLnr_M002ov
JPDHLkV1m63HmU#s
literal 0
HcmV?d00001
diff --git a/Resources/Textures/Structures/Specific/anomaly.rsi/anom5.png b/Resources/Textures/Structures/Specific/anomaly.rsi/anom5.png
new file mode 100644
index 0000000000000000000000000000000000000000..6117776649f15449eccd5d7ab6683ca17d41a968
GIT binary patch
literal 429
zcmV;e0aE^nP)Px$XGugsR9J=WmN9OGFc3vQAp#jG#j*uA$ZE?yz#f8=atIDU$r+FWbRZ)kry69u
zHXcw`Y51kF{ofzP2H@1GQ~#{s=)znGWsVi&xO>0d3&^;eXFRVDG}C}AN>H{H(lFc&
z!k%avr$Gz=sAZ3Pd;tJ-O6~?>0pvm`Gcf@W1OO|D58{IZ0AQ6`H`k2`Bn@IbuMgI~
zUI0p;1~F>cBa0I2JQ~*_x_P!X>n;EQt2AbMecXiORACc9PtTD#k5dJ21@shIl%SUV
zQf8MrC9Ugi`t$*i$dpci+4=gMW*X37SUq!z)dO_a@9||Rvwq}EK#nmRplmDahGg7K
z!%KAtu$Dc_w%Xi(^A!~18012zPRW-BYo=i_aw&@v{CF-S)+;bmPM#W)6O
z7_K_InfwjbDfzcoaoJWY2%kA8J#g@A`2BTrHs)KthAt5Ol6m|8fD1&|kw2nSr%wF>
X39-#Z{0ZNi00000NkvXXu0mjf>4mXC
literal 0
HcmV?d00001
diff --git a/Resources/Textures/Structures/Specific/anomaly.rsi/meta.json b/Resources/Textures/Structures/Specific/anomaly.rsi/meta.json
index f9d4be792f..c318e8116b 100644
--- a/Resources/Textures/Structures/Specific/anomaly.rsi/meta.json
+++ b/Resources/Textures/Structures/Specific/anomaly.rsi/meta.json
@@ -1,7 +1,7 @@
{
"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
@@ -92,6 +92,20 @@
0.15
]
]
+ },
+ {
+ "name": "anom5"
+ },
+ {
+ "name": "anom5-pulse",
+ "delays": [
+ [
+ 0.25,
+ 0.25,
+ 0.25,
+ 0.25
+ ]
+ ]
}
]
-}
\ No newline at end of file
+}
diff --git a/Resources/Textures/Tiles/attributions.yml b/Resources/Textures/Tiles/attributions.yml
index 9fec6d6c0e..9ce6c90cd0 100644
--- a/Resources/Textures/Tiles/attributions.yml
+++ b/Resources/Textures/Tiles/attributions.yml
@@ -56,3 +56,7 @@
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"
diff --git a/Resources/Textures/Tiles/meat.png b/Resources/Textures/Tiles/meat.png
new file mode 100644
index 0000000000000000000000000000000000000000..d593c1bdfcdb6857938739208bd57f3acf876a30
GIT binary patch
literal 1858
zcmV-I2fg@-P)Px*{7FPXRCt{2n@w&cMG%G4R$0bUBfMb4o-H@vL>z__a)enJ$mKOT#KWuU~&J
zA{(}ZWNEr8Gk!87OQ`-^)K#_(VExGQ_FIl@c%S=g(&}r+ZI^&;p*Gj`jNXvCZFYIXxX0zhB=z
zF6^fTu6LxjZkWq2y>)Kq1j?)ClKkg~$4@;-{^1Lk6mVneT`f{Aj&8L6PK%&M)O&Rj
zm*+avCaVqpT;J)x6MvVbd|Y-%X=7Y|cbbXZaci#gD7l^d!(S@%v_Zc4L}ZY9qx019
z>I9vmsrh~5tXN#zmu-IXyws@=Fq}B&rXjNcI&xsj5@RH*ExZv4my;bbChmJ=V5996&7jZ
z-KdV}guC;(3|g>yPQ{Mv(S>_GeNHLIPX(Qhp;Jq?==|LpjreFvtaqvd?hpy=jP4Yi
zoTLNdhqKC=TmC924J#y^S4xNKNKRjMKKv#6R
zS5GG-g-lXR1?as66iIw{ss*GE)k>`{T#ozU{)0oxpUuy8K85
zB%y`3=vTZF2&Fm@M5@2Zh6Ijvhb7stBQNoEA|2fUJcV5$fy9WYLkD*Xv~GS^5$k-a
z?ENIAQ>Y`M_=;?WK_AbiO26xd*9ptXAgFC}^J-9pVRIK@&sUn`nO#Df=
z3GYY+*`zCld|JmTa?-}H#Vw{o<%x&Vu~vWW>B5N86>kEDoKN!J4kjj@5-8*2d~;NO
z$D_uUK8`gg@8ad?U$S4<%-72=%}B3w^QrR4x$mdV_iJ6Ms#Pg1ut;I&4PKBJ0
zA)Q<=C8~&(6aiG5N{`diab-VM^9kbaIw@TT*G1)(ZUiOM@8YYG?NfRxmeNgA8AqYl
zPg^?CDw~wusf1~4>aTYInl3dsa!8v;HHe4*=|htQ^Qtb%uBm(R6m)d$SyGxdCgmh@
zkR-DUrAfz$Qc7|?D0Hj71RA}*eOy7*2~U}D7H+IczA+N2JFbSxdN
z+CUE#ep+trtM#3OH`Im^O~!VMIHfF=Pbq;GjUq@YzNjX0`EH}?(g9JOW0U@_>+{3o
zIwpjxL-q)@+W~)4XHm~~KIG^^au*p01DM8=)A|^iqk2#qOY)P$>htNqknW|wZvI+!
z9Zb1Y``KFfzh3{os1rJru!uJ8zrd?ykdN=?-ztBt{!&HxdanjUoz+h%A5`U!r>DCHn3ZNeNNHmADQ!v`y?J}kpKVy07*qoM6N<$f^B-RVE_OC
literal 0
HcmV?d00001
--
2.52.0