More anomalies (#13766)
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Anomaly;
|
using Content.Client.Gravity;
|
||||||
|
using Content.Shared.Anomaly;
|
||||||
using Content.Shared.Anomaly.Components;
|
using Content.Shared.Anomaly.Components;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
@@ -8,6 +9,7 @@ namespace Content.Client.Anomaly;
|
|||||||
public sealed class AnomalySystem : SharedAnomalySystem
|
public sealed class AnomalySystem : SharedAnomalySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly FloatingVisualizerSystem _floating = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -15,6 +17,20 @@ public sealed class AnomalySystem : SharedAnomalySystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<AnomalyComponent, AppearanceChangeEvent>(OnAppearanceChanged);
|
SubscribeLocalEvent<AnomalyComponent, AppearanceChangeEvent>(OnAppearanceChanged);
|
||||||
|
SubscribeLocalEvent<AnomalyComponent, ComponentStartup>(OnStartup);
|
||||||
|
SubscribeLocalEvent<AnomalyComponent, AnimationCompletedEvent>(OnAnimationComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStartup(EntityUid uid, AnomalyComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
_floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAnimationComplete(EntityUid uid, AnomalyComponent component, AnimationCompletedEvent args)
|
||||||
|
{
|
||||||
|
if (args.Key != component.AnimationKey)
|
||||||
|
return;
|
||||||
|
_floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAppearanceChanged(EntityUid uid, AnomalyComponent component, ref AppearanceChangeEvent args)
|
private void OnAppearanceChanged(EntityUid uid, AnomalyComponent component, ref AppearanceChangeEvent args)
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ using Content.Server.Power.EntitySystems;
|
|||||||
using Content.Shared.Anomaly;
|
using Content.Shared.Anomaly;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Materials;
|
using Content.Shared.Materials;
|
||||||
|
using Content.Shared.Physics;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
|
||||||
namespace Content.Server.Anomaly;
|
namespace Content.Server.Anomaly;
|
||||||
|
|
||||||
@@ -91,12 +94,32 @@ public sealed partial class AnomalySystem
|
|||||||
var randomY = Random.Next((int) gridBounds.Bottom, (int)gridBounds.Top);
|
var randomY = Random.Next((int) gridBounds.Bottom, (int)gridBounds.Top);
|
||||||
|
|
||||||
var tile = new Vector2i(randomX, randomY);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't spawn inside of solid objects
|
||||||
|
var physQuery = GetEntityQuery<PhysicsComponent>();
|
||||||
|
var valid = true;
|
||||||
|
foreach (var ent in gridComp.GetAnchoredEntities(tile))
|
||||||
|
{
|
||||||
|
if (!physQuery.TryGetComponent(ent, out var body))
|
||||||
|
continue;
|
||||||
|
if (body.BodyType != BodyType.Static ||
|
||||||
|
!body.Hard ||
|
||||||
|
(body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
targetCoords = gridComp.GridTileToLocal(tile);
|
targetCoords = gridComp.GridTileToLocal(tile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
|||||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||||
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
||||||
[Dependency] private readonly MaterialStorageSystem _material = default!;
|
[Dependency] private readonly MaterialStorageSystem _material = default!;
|
||||||
[Dependency] private readonly TransformSystem _transform = default!;
|
|
||||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||||
|
|
||||||
public const float MinParticleVariation = 0.8f;
|
public const float MinParticleVariation = 0.8f;
|
||||||
@@ -100,13 +99,13 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
|||||||
var multiplier = 1f;
|
var multiplier = 1f;
|
||||||
if (component.Stability > component.GrowthThreshold)
|
if (component.Stability > component.GrowthThreshold)
|
||||||
multiplier = component.GrowingPointMultiplier; //more points for unstable
|
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
|
//penalty of up to 50% based on health
|
||||||
multiplier *= MathF.Pow(1.5f, component.Health) - 0.5f;
|
multiplier *= MathF.Pow(1.5f, component.Health) - 0.5f;
|
||||||
|
|
||||||
return (int) ((component.MaxPointsPerSecond - component.MinPointsPerSecond) * component.Severity * multiplier);
|
var severityValue = 1 / (1 + MathF.Pow(MathF.E, -7 * (component.Severity - 0.5f)));
|
||||||
|
|
||||||
|
return (int) ((component.MaxPointsPerSecond - component.MinPointsPerSecond) * severityValue * multiplier) + component.MinPointsPerSecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ using Content.Shared.Anomaly.Effects.Components;
|
|||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.StatusEffect;
|
using Content.Shared.StatusEffect;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Anomaly.Effects;
|
namespace Content.Server.Anomaly.Effects;
|
||||||
|
|
||||||
public sealed class ElectricityAnomalySystem : EntitySystem
|
public sealed class ElectricityAnomalySystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly LightningSystem _lightning = default!;
|
[Dependency] private readonly LightningSystem _lightning = default!;
|
||||||
[Dependency] private readonly ElectrocutionSystem _electrocution = 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)
|
private void OnPulse(EntityUid uid, ElectricityAnomalyComponent component, ref AnomalyPulseEvent args)
|
||||||
{
|
{
|
||||||
var range = component.MaxElectrocuteRange * args.Stabiltiy;
|
var range = component.MaxElectrocuteRange * args.Stability;
|
||||||
var damage = (int) (component.MaxElectrocuteDamage * args.Severity);
|
|
||||||
var duration = component.MaxElectrocuteDuration * args.Severity;
|
|
||||||
|
|
||||||
var xform = Transform(uid);
|
var xform = Transform(uid);
|
||||||
foreach (var comp in _lookup.GetComponentsInRange<StatusEffectsComponent>(xform.MapPosition, range))
|
foreach (var comp in _lookup.GetComponentsInRange<MobStateComponent>(xform.MapPosition, range))
|
||||||
{
|
{
|
||||||
var ent = comp.Owner;
|
var ent = comp.Owner;
|
||||||
|
_lightning.ShootLightning(uid, ent);
|
||||||
_electrocution.TryDoElectrocution(ent, uid, damage, duration, true, statusEffects: comp, ignoreInsulation: true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +46,7 @@ public sealed class ElectricityAnomalySystem : EntitySystem
|
|||||||
if (mobQuery.HasComponent(ent))
|
if (mobQuery.HasComponent(ent))
|
||||||
validEnts.Add(ent);
|
validEnts.Add(ent);
|
||||||
|
|
||||||
if (_random.Prob(0.1f) && poweredQuery.HasComponent(ent))
|
if (_random.Prob(0.2f) && poweredQuery.HasComponent(ent))
|
||||||
validEnts.Add(ent);
|
validEnts.Add(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,4 +56,32 @@ public sealed class ElectricityAnomalySystem : EntitySystem
|
|||||||
_lightning.ShootLightning(uid, ent);
|
_lightning.ShootLightning(uid, ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
foreach (var (elec, anom, xform) in EntityQuery<ElectricityAnomalyComponent, AnomalyComponent, TransformComponent>())
|
||||||
|
{
|
||||||
|
if (_timing.CurTime < elec.NextSecond)
|
||||||
|
continue;
|
||||||
|
elec.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
var owner = xform.Owner;
|
||||||
|
|
||||||
|
if (!_random.Prob(elec.PassiveElectrocutionChance * anom.Stability))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var range = elec.MaxElectrocuteRange * anom.Stability;
|
||||||
|
var damage = (int) (elec.MaxElectrocuteDamage * anom.Severity);
|
||||||
|
var duration = elec.MaxElectrocuteDuration * anom.Severity;
|
||||||
|
|
||||||
|
foreach (var comp in _lookup.GetComponentsInRange<StatusEffectsComponent>(xform.MapPosition, range))
|
||||||
|
{
|
||||||
|
var ent = comp.Owner;
|
||||||
|
|
||||||
|
_electrocution.TryDoElectrocution(ent, owner, damage, duration, true, statusEffects: comp, ignoreInsulation: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
97
Content.Server/Anomaly/Effects/FleshAnomalySystem.cs
Normal file
@@ -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!;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<FleshAnomalyComponent, AnomalyPulseEvent>(OnPulse);
|
||||||
|
SubscribeLocalEvent<FleshAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
|
||||||
|
SubscribeLocalEvent<FleshAnomalyComponent, AnomalyStabilityChangedEvent>(OnSeverityChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPulse(EntityUid uid, FleshAnomalyComponent component, ref AnomalyPulseEvent args)
|
||||||
|
{
|
||||||
|
var range = component.SpawnRange * args.Stability;
|
||||||
|
var amount = (int) (component.MaxSpawnAmount * args.Severity + 0.5f);
|
||||||
|
|
||||||
|
var xform = Transform(uid);
|
||||||
|
SpawnMonstersOnOpenTiles(component, xform, amount, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSupercritical(EntityUid uid, FleshAnomalyComponent component, ref AnomalySupercriticalEvent args)
|
||||||
|
{
|
||||||
|
var xform = Transform(uid);
|
||||||
|
SpawnMonstersOnOpenTiles(component, xform, component.MaxSpawnAmount, component.SpawnRange);
|
||||||
|
Spawn(component.SupercriticalSpawn, xform.Coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSeverityChanged(EntityUid uid, FleshAnomalyComponent component, ref AnomalyStabilityChangedEvent args)
|
||||||
|
{
|
||||||
|
var xform = Transform(uid);
|
||||||
|
if (!_map.TryGetGrid(xform.GridUid, out var grid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var radius = component.SpawnRange * args.Stability;
|
||||||
|
var fleshTile = (ContentTileDefinition) _tiledef[component.FleshTileId];
|
||||||
|
var localpos = xform.Coordinates.Position;
|
||||||
|
var tilerefs = grid.GetLocalTilesIntersecting(
|
||||||
|
new Box2(localpos + (-radius, -radius), localpos + (radius, radius)));
|
||||||
|
foreach (var tileref in tilerefs)
|
||||||
|
{
|
||||||
|
if (!_random.Prob(0.33f))
|
||||||
|
continue;
|
||||||
|
_tile.ReplaceTile(tileref, fleshTile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpawnMonstersOnOpenTiles(FleshAnomalyComponent component, TransformComponent xform, int amount, float radius)
|
||||||
|
{
|
||||||
|
if (!_map.TryGetGrid(xform.GridUid, out var grid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var localpos = xform.Coordinates.Position;
|
||||||
|
var tilerefs = grid.GetLocalTilesIntersecting(
|
||||||
|
new Box2(localpos + (-radius, -radius), localpos + (radius, radius))).ToArray();
|
||||||
|
_random.Shuffle(tilerefs);
|
||||||
|
var physQuery = GetEntityQuery<PhysicsComponent>();
|
||||||
|
var amountCounter = 0;
|
||||||
|
foreach (var tileref in tilerefs)
|
||||||
|
{
|
||||||
|
var valid = true;
|
||||||
|
foreach (var ent in grid.GetAnchoredEntities(tileref.GridIndices))
|
||||||
|
{
|
||||||
|
if (!physQuery.TryGetComponent(ent, out var body))
|
||||||
|
continue;
|
||||||
|
if (body.BodyType != BodyType.Static ||
|
||||||
|
!body.Hard ||
|
||||||
|
(body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
|
||||||
|
continue;
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!valid)
|
||||||
|
continue;
|
||||||
|
amountCounter++;
|
||||||
|
Spawn(_random.Pick(component.Spawns), tileref.GridIndices.ToEntityCoordinates(xform.GridUid.Value, _map));
|
||||||
|
if (amountCounter >= amount)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ public sealed class PyroclasticAnomalySystem : EntitySystem
|
|||||||
private void OnPulse(EntityUid uid, PyroclasticAnomalyComponent component, ref AnomalyPulseEvent args)
|
private void OnPulse(EntityUid uid, PyroclasticAnomalyComponent component, ref AnomalyPulseEvent args)
|
||||||
{
|
{
|
||||||
var xform = Transform(uid);
|
var xform = Transform(uid);
|
||||||
var ignitionRadius = component.MaximumIgnitionRadius * args.Stabiltiy;
|
var ignitionRadius = component.MaximumIgnitionRadius * args.Stability;
|
||||||
IgniteNearby(xform.Coordinates, args.Severity, ignitionRadius);
|
IgniteNearby(xform.Coordinates, args.Severity, ignitionRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Server.Decals;
|
|||||||
using Content.Shared.Decals;
|
using Content.Shared.Decals;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
namespace Content.Server.Maps;
|
namespace Content.Server.Maps;
|
||||||
@@ -54,6 +55,28 @@ public sealed class TileSystem : EntitySystem
|
|||||||
return DeconstructTile(tileRef);
|
return DeconstructTile(tileRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ReplaceTile(TileRef tileref, ContentTileDefinition replacementTile)
|
||||||
|
{
|
||||||
|
if (!TryComp<MapGridComponent>(tileref.GridUid, out var grid))
|
||||||
|
return false;
|
||||||
|
return ReplaceTile(tileref, replacementTile, tileref.GridUid, grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ReplaceTile(TileRef tileref, ContentTileDefinition replacementTile, EntityUid grid, MapGridComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(grid, ref component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var variant = _robustRandom.Pick(replacementTile.PlacementVariants);
|
||||||
|
var decals = _decal.GetDecalsInRange(tileref.GridUid, tileref.GridPosition().SnapToGrid(EntityManager, _mapManager).Position, 0.5f);
|
||||||
|
foreach (var (id, _) in decals)
|
||||||
|
{
|
||||||
|
_decal.RemoveDecal(tileref.GridUid, id);
|
||||||
|
}
|
||||||
|
component.SetTile(tileref.GridIndices, new Tile(replacementTile.TileId, 0, variant));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private bool DeconstructTile(TileRef tileRef)
|
private bool DeconstructTile(TileRef tileRef)
|
||||||
{
|
{
|
||||||
var indices = tileRef.GridIndices;
|
var indices = tileRef.GridIndices;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public sealed class AnomalyComponent : Component
|
|||||||
/// The amount of health lost when the stability is below the <see cref="DecayThreshold"/>
|
/// The amount of health lost when the stability is below the <see cref="DecayThreshold"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("healthChangePerSecond"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField("healthChangePerSecond"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float HealthChangePerSecond = -0.05f;
|
public float HealthChangePerSecond = -0.01f;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Growth
|
#region Growth
|
||||||
@@ -208,21 +208,14 @@ public sealed class AnomalyComponent : Component
|
|||||||
/// This doesn't include the point bonus for being unstable.
|
/// This doesn't include the point bonus for being unstable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("maxPointsPerSecond")]
|
[DataField("maxPointsPerSecond")]
|
||||||
public int MaxPointsPerSecond = 65;
|
public int MaxPointsPerSecond = 100;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The multiplier applied to the point value for the
|
/// The multiplier applied to the point value for the
|
||||||
/// anomaly being above the <see cref="GrowthThreshold"/>
|
/// anomaly being above the <see cref="GrowthThreshold"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("growingPointMultiplier")]
|
[DataField("growingPointMultiplier")]
|
||||||
public float GrowingPointMultiplier = 1.2f;
|
public float GrowingPointMultiplier = 1.5f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The multiplier applied to the point value for the
|
|
||||||
/// anomaly being below the <see cref="DecayThreshold"/>
|
|
||||||
/// </summary>
|
|
||||||
[DataField("decayingPointMultiplier")]
|
|
||||||
public float DecayingPointMultiplier = 0.75f;
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -238,6 +231,24 @@ public sealed class AnomalyComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("anomalyContactDamageSound")]
|
[DataField("anomalyContactDamageSound")]
|
||||||
public SoundSpecifier AnomalyContactDamageSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
|
public SoundSpecifier AnomalyContactDamageSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
|
||||||
|
|
||||||
|
#region Floating Animation
|
||||||
|
/// <summary>
|
||||||
|
/// How long it takes to go from the bottom of the animation to the top.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("animationTime")]
|
||||||
|
public readonly float AnimationTime = 2f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How far it goes in any direction.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("offset")]
|
||||||
|
public readonly Vector2 FloatingOffset = (0, 0.15f);
|
||||||
|
|
||||||
|
public readonly string AnimationKey = "anomalyfloat";
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
@@ -260,14 +271,10 @@ public sealed class AnomalyComponentState : ComponentState
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event raised at regular intervals on an anomaly to do whatever its effect is.
|
/// Event raised at regular intervals on an anomaly to do whatever its effect is.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Stabiltiy"></param>
|
/// <param name="Stability"></param>
|
||||||
/// <param name="Severity"></param>
|
/// <param name="Severity"></param>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public readonly record struct AnomalyPulseEvent(float Stabiltiy, float Severity)
|
public readonly record struct AnomalyPulseEvent(float Stability, float Severity);
|
||||||
{
|
|
||||||
public readonly float Stabiltiy = Stabiltiy;
|
|
||||||
public readonly float Severity = Severity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event raised on an anomaly when it reaches a supercritical point.
|
/// Event raised on an anomaly when it reaches a supercritical point.
|
||||||
|
|||||||
73
Content.Shared/Anomaly/Effects/BluespaceAnomalySystem.cs
Normal file
@@ -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!;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<BluespaceAnomalyComponent, AnomalyPulseEvent>(OnPulse);
|
||||||
|
SubscribeLocalEvent<BluespaceAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
|
||||||
|
SubscribeLocalEvent<BluespaceAnomalyComponent, AnomalySeverityChangedEvent>(OnSeverityChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPulse(EntityUid uid, BluespaceAnomalyComponent component, ref AnomalyPulseEvent args)
|
||||||
|
{
|
||||||
|
var xform = Transform(uid);
|
||||||
|
var range = component.MaxShuffleRadius * args.Severity;
|
||||||
|
var allEnts = _lookup.GetComponentsInRange<MobStateComponent>(xform.Coordinates, range)
|
||||||
|
.Select(x => x.Owner).ToList();
|
||||||
|
allEnts.Add(uid);
|
||||||
|
|
||||||
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
|
var coords = new List<EntityCoordinates>();
|
||||||
|
foreach (var ent in allEnts)
|
||||||
|
{
|
||||||
|
if (xformQuery.TryGetComponent(ent, out var xf))
|
||||||
|
coords.Add(xf.Coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
_random.Shuffle(coords);
|
||||||
|
for (var i = 0; i < allEnts.Count; i++)
|
||||||
|
{
|
||||||
|
_xform.SetCoordinates(allEnts[i], coords[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSupercritical(EntityUid uid, BluespaceAnomalyComponent component, ref AnomalySupercriticalEvent args)
|
||||||
|
{
|
||||||
|
var xform = Transform(uid);
|
||||||
|
var mapPos = _xform.GetWorldPosition(xform);
|
||||||
|
var radius = component.SupercriticalTeleportRadius;
|
||||||
|
var gridBounds = new Box2(mapPos - (radius, radius), mapPos + (radius, radius));
|
||||||
|
foreach (var comp in _lookup.GetComponentsInRange<MobStateComponent>(xform.Coordinates, component.MaxShuffleRadius))
|
||||||
|
{
|
||||||
|
var ent = comp.Owner;
|
||||||
|
var randomX = _random.NextFloat(gridBounds.Left, gridBounds.Right);
|
||||||
|
var randomY = _random.NextFloat(gridBounds.Bottom, gridBounds.Top);
|
||||||
|
|
||||||
|
var pos = new Vector2(randomX, randomY);
|
||||||
|
_xform.SetWorldPosition(ent, pos);
|
||||||
|
_audio.PlayPvs(component.TeleportSound, ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSeverityChanged(EntityUid uid, BluespaceAnomalyComponent component, ref AnomalySeverityChangedEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<PortalComponent>(uid, out var portal))
|
||||||
|
return;
|
||||||
|
portal.MaxRandomRadius = (component.MaxPortalRadius - component.MinPortalRadius) * args.Severity + component.MinPortalRadius;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum radius that the shuffle effect will extend for
|
||||||
|
/// scales with stability
|
||||||
|
/// </summary>
|
||||||
|
[DataField("maxShuffleRadius"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MaxShuffleRadius = 10;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum MAX distance the portal this anomaly is tied to can teleport you.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("maxPortalRadius"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MaxPortalRadius = 25;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum MAX distance the portal this anomaly is tied to can teleport you.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("minPortalRadius"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MinPortalRadius = 10;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How far the supercritical event can teleport you
|
||||||
|
/// </summary>
|
||||||
|
[DataField("superCriticalTeleportRadius"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float SupercriticalTeleportRadius = 50f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sound played after players are shuffled/teleported around
|
||||||
|
/// </summary>
|
||||||
|
[DataField("teleportSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg");
|
||||||
|
}
|
||||||
@@ -1,14 +1,41 @@
|
|||||||
namespace Content.Shared.Anomaly.Effects.Components;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Shared.Anomaly.Effects.Components;
|
||||||
|
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class ElectricityAnomalyComponent : Component
|
public sealed class ElectricityAnomalyComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum radius of the passive electrocution effect
|
||||||
|
/// scales with stability
|
||||||
|
/// </summary>
|
||||||
[DataField("maxElectrocutionRange"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField("maxElectrocutionRange"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float MaxElectrocuteRange = 6f;
|
public float MaxElectrocuteRange = 7f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum amount of damage the electrocution can do
|
||||||
|
/// scales with severity
|
||||||
|
/// </summary>
|
||||||
[DataField("maxElectrocuteDamage"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField("maxElectrocuteDamage"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float MaxElectrocuteDamage = 20f;
|
public float MaxElectrocuteDamage = 20f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum amount of time the electrocution lasts
|
||||||
|
/// scales with severity
|
||||||
|
/// </summary>
|
||||||
[DataField("maxElectrocuteDuration"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField("maxElectrocuteDuration"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
public TimeSpan MaxElectrocuteDuration = TimeSpan.FromSeconds(8);
|
public TimeSpan MaxElectrocuteDuration = TimeSpan.FromSeconds(8);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum chance that each second, when in range of the anomaly, you will be electrocuted.
|
||||||
|
/// scales with stability
|
||||||
|
/// </summary>
|
||||||
|
[DataField("passiveElectrocutionChance"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float PassiveElectrocutionChance = 0.05f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for tracking seconds, so that we can shock people in a non-tick-dependent way.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("nextSecond", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan NextSecond = TimeSpan.Zero;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A list of entities that are random picked to be spawned on each pulse
|
||||||
|
/// </summary>
|
||||||
|
[DataField("spawns", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public List<string> Spawns = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of entities that spawn per pulse
|
||||||
|
/// scales with severity.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("maxSpawnAmount"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int MaxSpawnAmount = 8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum radius the entities will spawn in.
|
||||||
|
/// Also governs the maximum reach of flesh tiles
|
||||||
|
/// scales with stability
|
||||||
|
/// </summary>
|
||||||
|
[DataField("spawnRange"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float SpawnRange = 4f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The tile that is spawned by the anomaly's effect
|
||||||
|
/// </summary>
|
||||||
|
[DataField("fleshTileId", customTypeSerializer: typeof(PrototypeIdSerializer<ContentTileDefinition>)), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public string FleshTileId = "FloorFlesh";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The entity spawned when the anomaly goes supercritical
|
||||||
|
/// </summary>
|
||||||
|
[DataField("superCriticalSpawn", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public string SupercriticalSpawn = "FleshKudzu";
|
||||||
|
}
|
||||||
@@ -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
|
public sealed class GravityAnomalyComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public sealed class PyroclasticAnomalyComponent : Component
|
|||||||
/// I have no clue if this is balanced.
|
/// I have no clue if this is balanced.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DataField("heatPerSecond")]
|
[DataField("heatPerSecond")]
|
||||||
public float HeatPerSecond = 50;
|
public float HeatPerSecond = 25;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum distance from which you can be ignited by the anomaly.
|
/// 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
|
/// The amount of gas released when the anomaly goes supercritical
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("supercriticalMoleAmount")]
|
[DataField("supercriticalMoleAmount")]
|
||||||
public float SupercriticalMoleAmount = 50f;
|
public float SupercriticalMoleAmount = 75f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,8 @@ public abstract class SharedGravityAnomalySystem : EntitySystem
|
|||||||
foreach (var ent in lookup)
|
foreach (var ent in lookup)
|
||||||
{
|
{
|
||||||
var tempXform = Transform(ent);
|
var tempXform = Transform(ent);
|
||||||
|
|
||||||
var foo = tempXform.MapPosition.Position - xform.MapPosition.Position;
|
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 tempXform = Transform(ent);
|
||||||
|
|
||||||
var foo = tempXform.MapPosition.Position - xform.MapPosition.Position;
|
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);
|
_throwing.TryThrow(ent, foo * 5, strength, uid, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,9 @@ public abstract class SharedAnomalySystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref component))
|
if (!Resolve(uid, ref component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!Timing.IsFirstTimePredicted)
|
||||||
|
return;
|
||||||
|
|
||||||
DebugTools.Assert(component.MinPulseLength > TimeSpan.FromSeconds(3)); // this is just to prevent lagspikes mispredicting pulses
|
DebugTools.Assert(component.MinPulseLength > TimeSpan.FromSeconds(3)); // this is just to prevent lagspikes mispredicting pulses
|
||||||
var variation = Random.NextFloat(-component.PulseVariation, component.PulseVariation) + 1;
|
var variation = Random.NextFloat(-component.PulseVariation, component.PulseVariation) + 1;
|
||||||
component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * variation;
|
component.NextPulseTime = Timing.CurTime + GetPulseLength(component) * variation;
|
||||||
@@ -173,6 +176,10 @@ public abstract class SharedAnomalySystem : EntitySystem
|
|||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component))
|
if (!Resolve(uid, ref component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!Timing.IsFirstTimePredicted)
|
||||||
|
return;
|
||||||
|
|
||||||
Audio.PlayPvs(component.SupercriticalSound, uid);
|
Audio.PlayPvs(component.SupercriticalSound, uid);
|
||||||
|
|
||||||
var ev = new AnomalySupercriticalEvent();
|
var ev = new AnomalySupercriticalEvent();
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ tiles-asteroid-ironsand-pebbles = asteroid ironsand pebbles
|
|||||||
tiles-asteroid-ironsand-rock = asteroid ironsand rock
|
tiles-asteroid-ironsand-rock = asteroid ironsand rock
|
||||||
tiles-cave = cave
|
tiles-cave = cave
|
||||||
tiles-cave-drought = cave drought
|
tiles-cave-drought = cave drought
|
||||||
|
tiles-flesh-floor = flesh floor
|
||||||
tiles-techmaint3-floor = grated maintenance floor
|
tiles-techmaint3-floor = grated maintenance floor
|
||||||
tiles-techmaint2-floor = steel maintenance floor
|
tiles-techmaint2-floor = steel maintenance floor
|
||||||
tiles-wood2 = wood pattern floor
|
tiles-wood2 = wood pattern floor
|
||||||
|
|||||||
@@ -13,4 +13,6 @@
|
|||||||
- AnomalyPyroclastic
|
- AnomalyPyroclastic
|
||||||
- AnomalyGravity
|
- AnomalyGravity
|
||||||
- AnomalyElectricity
|
- AnomalyElectricity
|
||||||
|
- AnomalyFlesh
|
||||||
|
- AnomalyBluespace
|
||||||
chance: 1
|
chance: 1
|
||||||
|
|||||||
160
Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml
Normal file
@@ -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
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"/Audio/Weapons/slash.ogg"
|
"/Audio/Weapons/slash.ogg"
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Misc/kudzu.rsi
|
sprite: Objects/Misc/kudzu.rsi
|
||||||
|
color: "#ff0000"
|
||||||
state: kudzu_11
|
state: kudzu_11
|
||||||
drawdepth: Overdoors
|
drawdepth: Overdoors
|
||||||
netsync: false
|
netsync: false
|
||||||
@@ -77,3 +78,55 @@
|
|||||||
- type: SlowContacts
|
- type: SlowContacts
|
||||||
walkSpeedModifier: 0.2
|
walkSpeedModifier: 0.2
|
||||||
sprintSpeedModifier: 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
|
||||||
|
|||||||
@@ -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" ]
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
- type: Transform
|
- type: Transform
|
||||||
anchored: true
|
anchored: true
|
||||||
- type: Physics
|
- type: Physics
|
||||||
bodyType: Static
|
bodyType: Static
|
||||||
- type: Fixtures
|
- type: Fixtures
|
||||||
fixtures:
|
fixtures:
|
||||||
- shape:
|
- shape:
|
||||||
@@ -33,12 +33,14 @@
|
|||||||
- MobLayer
|
- MobLayer
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
netsync: false
|
netsync: false
|
||||||
drawdepth: Items
|
noRot: true
|
||||||
|
drawdepth: Effects #it needs to draw over stuff.
|
||||||
sprite: Structures/Specific/anomaly.rsi
|
sprite: Structures/Specific/anomaly.rsi
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
|
- type: AnimationPlayer
|
||||||
- type: GuideHelp
|
- type: GuideHelp
|
||||||
guides:
|
guides:
|
||||||
- AnomalousResearch
|
- AnomalousResearch
|
||||||
@@ -71,7 +73,6 @@
|
|||||||
suffix: Gravity
|
suffix: Gravity
|
||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
drawdepth: Effects #it needs to draw over stuff.
|
|
||||||
layers:
|
layers:
|
||||||
- state: anom2
|
- state: anom2
|
||||||
map: ["enum.AnomalyVisualLayers.Base"]
|
map: ["enum.AnomalyVisualLayers.Base"]
|
||||||
@@ -105,4 +106,76 @@
|
|||||||
color: "#ffffaa"
|
color: "#ffffaa"
|
||||||
castShadows: false
|
castShadows: false
|
||||||
- type: ElectricityAnomaly
|
- type: ElectricityAnomaly
|
||||||
- type: Electrified
|
- 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
|
||||||
|
|||||||
@@ -1367,6 +1367,22 @@
|
|||||||
thermalConductivity: 0.04
|
thermalConductivity: 0.04
|
||||||
heatCapacity: 10000
|
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
|
- type: tile
|
||||||
id: FloorTechMaint2
|
id: FloorTechMaint2
|
||||||
name: tiles-techmaint2-floor
|
name: tiles-techmaint2-floor
|
||||||
|
|||||||
@@ -216,6 +216,9 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
id: FireAxe
|
id: FireAxe
|
||||||
|
|
||||||
|
- type: Tag
|
||||||
|
id: Flesh
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: WhitelistChameleon
|
id: WhitelistChameleon
|
||||||
|
|
||||||
|
|||||||
BIN
Resources/Textures/Mobs/Aliens/flesh.rsi/clamp.png
Normal file
|
After Width: | Height: | Size: 613 B |
BIN
Resources/Textures/Mobs/Aliens/flesh.rsi/dead.png
Normal file
|
After Width: | Height: | Size: 311 B |
BIN
Resources/Textures/Mobs/Aliens/flesh.rsi/golem.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Resources/Textures/Mobs/Aliens/flesh.rsi/jared.png
Normal file
|
After Width: | Height: | Size: 741 B |
BIN
Resources/Textures/Mobs/Aliens/flesh.rsi/lover.png
Normal file
|
After Width: | Height: | Size: 723 B |
27
Resources/Textures/Mobs/Aliens/flesh.rsi/meta.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
Resources/Textures/Objects/Misc/fleshkudzu.rsi/base.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
14
Resources/Textures/Objects/Misc/fleshkudzu.rsi/meta.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 508 B |
|
After Width: | Height: | Size: 505 B |
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 436 B |
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Resources/Textures/Structures/Specific/anomaly.rsi/anom5.png
Normal file
|
After Width: | Height: | Size: 429 B |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"license": "CC0-1.0",
|
"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": {
|
"size": {
|
||||||
"x": 32,
|
"x": 32,
|
||||||
"y": 32
|
"y": 32
|
||||||
@@ -92,6 +92,20 @@
|
|||||||
0.15
|
0.15
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "anom5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "anom5-pulse",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.25,
|
||||||
|
0.25,
|
||||||
|
0.25,
|
||||||
|
0.25
|
||||||
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,3 +56,7 @@
|
|||||||
copyright: "by brainfood for space-station-14, ."
|
copyright: "by brainfood for space-station-14, ."
|
||||||
source: "https://github.com/space-wizards/space-station-14/pull/12193"
|
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"
|
||||||
|
|||||||
BIN
Resources/Textures/Tiles/meat.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |