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 0000000000..d9085d75b9 Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/flesh.rsi/clamp.png differ diff --git a/Resources/Textures/Mobs/Aliens/flesh.rsi/dead.png b/Resources/Textures/Mobs/Aliens/flesh.rsi/dead.png new file mode 100644 index 0000000000..826d4e0acd Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/flesh.rsi/dead.png differ diff --git a/Resources/Textures/Mobs/Aliens/flesh.rsi/golem.png b/Resources/Textures/Mobs/Aliens/flesh.rsi/golem.png new file mode 100644 index 0000000000..e7744ad985 Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/flesh.rsi/golem.png differ diff --git a/Resources/Textures/Mobs/Aliens/flesh.rsi/jared.png b/Resources/Textures/Mobs/Aliens/flesh.rsi/jared.png new file mode 100644 index 0000000000..da20b436ae Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/flesh.rsi/jared.png differ 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 0000000000..0b5229c37f Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/flesh.rsi/lover.png differ diff --git a/Resources/Textures/Mobs/Aliens/flesh.rsi/meta.json b/Resources/Textures/Mobs/Aliens/flesh.rsi/meta.json new file mode 100644 index 0000000000..6c81c52037 --- /dev/null +++ b/Resources/Textures/Mobs/Aliens/flesh.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "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" + } + ] +} diff --git a/Resources/Textures/Objects/Misc/fleshkudzu.rsi/base.png b/Resources/Textures/Objects/Misc/fleshkudzu.rsi/base.png new file mode 100644 index 0000000000..659b2ea979 Binary files /dev/null and b/Resources/Textures/Objects/Misc/fleshkudzu.rsi/base.png differ 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 0000000000..c4e0b26773 Binary files /dev/null and b/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/ajar.png differ diff --git a/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/closed.png b/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/closed.png new file mode 100644 index 0000000000..dec70d0bb5 Binary files /dev/null and b/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/closed.png differ diff --git a/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/meta.json b/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/meta.json new file mode 100644 index 0000000000..8b58bd6a12 --- /dev/null +++ b/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "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" + } + ] +} diff --git a/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/open.png b/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/open.png new file mode 100644 index 0000000000..5f7bfd3f13 Binary files /dev/null and b/Resources/Textures/Structures/Decoration/flesh_decoration.rsi/open.png differ diff --git a/Resources/Textures/Structures/Specific/anomaly.rsi/anom5-pulse.png b/Resources/Textures/Structures/Specific/anomaly.rsi/anom5-pulse.png new file mode 100644 index 0000000000..c0e547f8dd Binary files /dev/null and b/Resources/Textures/Structures/Specific/anomaly.rsi/anom5-pulse.png differ 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 0000000000..6117776649 Binary files /dev/null and b/Resources/Textures/Structures/Specific/anomaly.rsi/anom5.png differ 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 0000000000..d593c1bdfc Binary files /dev/null and b/Resources/Textures/Tiles/meat.png differ