diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 52bf3014a7..860dcc3d8e 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -207,8 +207,6 @@ namespace Content.Client.Entry "GasTank", "BreathMask", "RadiationCollector", - "ContainmentFieldGenerator", - "ContainmentField", "Emitter", "SingularityGenerator", "ParticleProjectile", diff --git a/Content.Client/Singularity/Components/ContainmentFieldComponent.cs b/Content.Client/Singularity/Components/ContainmentFieldComponent.cs index 4e28fb1089..0222b3a226 100644 --- a/Content.Client/Singularity/Components/ContainmentFieldComponent.cs +++ b/Content.Client/Singularity/Components/ContainmentFieldComponent.cs @@ -1,13 +1,16 @@ +using Content.Shared.Singularity.Components; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Log; namespace Content.Client.Singularity.Components { - public class ContainmentFieldComponent : Component + [RegisterComponent] + [ComponentReference(typeof(SharedContainmentFieldComponent))] + public class ContainmentFieldComponent : SharedContainmentFieldComponent { - public override string Name => "ContainmentField"; - + // Jesus what is this code. + // Singulo cleanup WHEEENNN private SpriteComponent? _spriteComponent; protected override void Initialize() diff --git a/Content.Client/Singularity/Components/ContainmentFieldGeneratorComponent.cs b/Content.Client/Singularity/Components/ContainmentFieldGeneratorComponent.cs new file mode 100644 index 0000000000..8039c22ec4 --- /dev/null +++ b/Content.Client/Singularity/Components/ContainmentFieldGeneratorComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.Singularity.Components; +using Robust.Shared.GameObjects; + +namespace Content.Client.Singularity.Components +{ + [RegisterComponent] + [ComponentReference(typeof(SharedContainmentFieldGeneratorComponent))] + public sealed class ContainmentFieldGeneratorComponent : SharedContainmentFieldGeneratorComponent + { + + } +} diff --git a/Content.Client/Singularity/Visualizers/SingularityVisualizer.cs b/Content.Client/Singularity/Visualizers/SingularityVisualizer.cs index cf5acade56..769c433ed6 100644 --- a/Content.Client/Singularity/Visualizers/SingularityVisualizer.cs +++ b/Content.Client/Singularity/Visualizers/SingularityVisualizer.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; namespace Content.Client.Singularity.Visualizers { @@ -33,8 +34,7 @@ namespace Content.Client.Singularity.Visualizers return; } - sprite.LayerSetRSI(Layer, "Structures/Power/Generation/Singularity/singularity_" + level + ".rsi"); - sprite.LayerSetState(Layer, "singularity_" + level); + sprite.LayerSetSprite(Layer, new SpriteSpecifier.Rsi(new ResourcePath("Structures/Power/Generation/Singularity/singularity_" + level + ".rsi"), "singularity_" + level)); } } } diff --git a/Content.Server/Physics/Controllers/SingularityController.cs b/Content.Server/Physics/Controllers/SingularityController.cs index 934bace7ca..90dcc2fa4a 100644 --- a/Content.Server/Physics/Controllers/SingularityController.cs +++ b/Content.Server/Physics/Controllers/SingularityController.cs @@ -1,12 +1,8 @@ -using Content.Server.Ghost.Components; using Content.Server.Singularity.Components; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.IoC; -using Robust.Shared.Map; using Robust.Shared.Maths; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Broadphase; using Robust.Shared.Physics.Controllers; using Robust.Shared.Random; @@ -14,93 +10,48 @@ namespace Content.Server.Physics.Controllers { internal sealed class SingularityController : VirtualController { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; - private float _pullAccumulator; - private float _moveAccumulator; + private const float MaxMoveCooldown = 10f; + private const float MinMoveCooldown = 2f; public override void UpdateBeforeSolve(bool prediction, float frameTime) { base.UpdateBeforeSolve(prediction, frameTime); - _moveAccumulator += frameTime; - _pullAccumulator += frameTime; - - while (_pullAccumulator > 0.5f) + foreach (var (singularity, physics) in ComponentManager.EntityQuery()) { - _pullAccumulator -= 0.5f; + if (singularity.Owner.HasComponent() || + singularity.BeingDeletedByAnotherSingularity) continue; - foreach (var singularity in ComponentManager.EntityQuery()) - { - // TODO: Use colliders instead probably yada yada - PullEntities(singularity); - // Yeah look the collision with station wasn't working and I'm 15k lines in and not debugging this shit - DestroyTiles(singularity); - } - } + singularity.MoveAccumulator -= frameTime; - while (_moveAccumulator > 1.0f) - { - _moveAccumulator -= 1.0f; + if (singularity.MoveAccumulator > 0f) continue; - foreach (var (singularity, physics) in ComponentManager.EntityQuery()) - { - if (singularity.Owner.HasComponent()) continue; + singularity.MoveAccumulator = MinMoveCooldown + (MaxMoveCooldown - MinMoveCooldown) * _robustRandom.NextFloat(); - // TODO: Need to essentially use a push vector in a random direction for us PLUS - // Any entity colliding with our larger circlebox needs to have an impulse applied to itself. - physics.BodyStatus = BodyStatus.InAir; - MoveSingulo(singularity, physics); - } + MoveSingulo(singularity, physics); } } private void MoveSingulo(ServerSingularityComponent singularity, PhysicsComponent physics) { - // To prevent getting stuck, ServerSingularityComponent will zero the velocity of a singularity when it goes to a level <= 1 (see here). - if (singularity.Level <= 1) return; - // TODO: Could try gradual changes instead but for now just try to replicate + // TODO: Need to make this events instead. + if (singularity.Level <= 1) + { + physics.BodyStatus = BodyStatus.OnGround; + return; + } + // TODO: Could try gradual changes instead var pushVector = new Vector2(_robustRandom.Next(-10, 10), _robustRandom.Next(-10, 10)); if (pushVector == Vector2.Zero) return; physics.LinearVelocity = Vector2.Zero; - physics.LinearVelocity = pushVector.Normalized * 2; - } - - private void PullEntities(ServerSingularityComponent component) - { - var singularityCoords = component.Owner.Transform.Coordinates; - // TODO: Maybe if we have named fixtures needs to pull out the outer circle collider (inner will be for deleting). - var entitiesToPull = IoCManager.Resolve().GetEntitiesInRange(singularityCoords, component.Level * 10); - foreach (var entity in entitiesToPull) - { - if (!entity.TryGetComponent(out var collidableComponent) || collidableComponent.BodyType == BodyType.Static) continue; - if (entity.HasComponent()) continue; - if (singularityCoords.EntityId != entity.Transform.Coordinates.EntityId) continue; - var vec = (singularityCoords - entity.Transform.Coordinates).Position; - if (vec == Vector2.Zero) continue; - - var speed = 10 / vec.Length * component.Level; - - collidableComponent.ApplyLinearImpulse(vec.Normalized * speed); - } - } - - private void DestroyTiles(ServerSingularityComponent component) - { - if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) return; - var worldBox = physicsComponent.GetWorldAABB(); - - foreach (var grid in _mapManager.FindGridsIntersecting(component.Owner.Transform.MapID, worldBox)) - { - foreach (var tile in grid.GetTilesIntersecting(worldBox)) - { - grid.SetTile(tile.GridIndices, Tile.Empty); - } - } + physics.BodyStatus = BodyStatus.InAir; + physics.ApplyLinearImpulse(pushVector.Normalized + 1f / singularity.Level * physics.Mass); + // TODO: Speedcap it probably? } } } diff --git a/Content.Server/Radiation/RadiationPulseSystem.cs b/Content.Server/Radiation/RadiationPulseSystem.cs index b00939b334..daed81f417 100644 --- a/Content.Server/Radiation/RadiationPulseSystem.cs +++ b/Content.Server/Radiation/RadiationPulseSystem.cs @@ -11,53 +11,39 @@ namespace Content.Server.Radiation [UsedImplicitly] public sealed class RadiationPulseSystem : EntitySystem { - private const string RadiationPrototype = "RadiationPulse"; + [Dependency] private readonly IEntityLookup _lookup = default!; - public IEntity RadiationPulse( - EntityCoordinates coordinates, - float range, - int dps, - bool decay = true, - float minPulseLifespan = 0.8f, - float maxPulseLifespan = 2.5f, - SoundSpecifier sound = default!) - { - var radiationEntity = EntityManager.SpawnEntity(RadiationPrototype, coordinates); - var radiation = radiationEntity.GetComponent(); - - radiation.Range = range; - radiation.RadsPerSecond = dps; - radiation.Draw = false; - radiation.Decay = decay; - radiation.MinPulseLifespan = minPulseLifespan; - radiation.MaxPulseLifespan = maxPulseLifespan; - radiation.Sound = sound; - - radiation.DoPulse(); - - return radiationEntity; - } + private const float RadiationCooldown = 0.5f; + private float _accumulator; public override void Update(float frameTime) { base.Update(frameTime); - var lookupSystem = IoCManager.Resolve(); + _accumulator += RadiationCooldown; - foreach (var comp in ComponentManager.EntityQuery(true)) + while (_accumulator > RadiationCooldown) { - comp.Update(frameTime); - var ent = comp.Owner; + _accumulator -= RadiationCooldown; - if (ent.Deleted) continue; - - foreach (var entity in lookupSystem.GetEntitiesInRange(ent.Transform.Coordinates, comp.Range, true)) + foreach (var comp in ComponentManager.EntityQuery(true)) { - if (entity.Deleted) continue; + comp.Update(frameTime); + var ent = comp.Owner; - foreach (var radiation in entity.GetAllComponents().ToArray()) + if (ent.Deleted) continue; + + foreach (var entity in _lookup.GetEntitiesInRange(ent.Transform.Coordinates, comp.Range)) { - radiation.RadiationAct(frameTime, comp); + // For now at least still need this because it uses a list internally then returns and this may be deleted before we get to it. + if (entity.Deleted) continue; + + // Note: Radiation is liable for a refactor (stinky Sloth coding a basic version when he did StationEvents) + // so this ToArray doesn't really matter. + foreach (var radiation in entity.GetAllComponents().ToArray()) + { + radiation.RadiationAct(RadiationCooldown, comp); + } } } } diff --git a/Content.Server/Singularity/Components/ContainmentFieldComponent.cs b/Content.Server/Singularity/Components/ContainmentFieldComponent.cs index f40cce21c0..8d6dc682fe 100644 --- a/Content.Server/Singularity/Components/ContainmentFieldComponent.cs +++ b/Content.Server/Singularity/Components/ContainmentFieldComponent.cs @@ -1,11 +1,12 @@ +using Content.Shared.Singularity.Components; using Robust.Shared.GameObjects; namespace Content.Server.Singularity.Components { [RegisterComponent] - public class ContainmentFieldComponent : Component + [ComponentReference(typeof(SharedContainmentFieldComponent))] + public class ContainmentFieldComponent : SharedContainmentFieldComponent { - public override string Name => "ContainmentField"; public ContainmentFieldConnection? Parent; } } diff --git a/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs b/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs index eb5f57d0ee..e8096f5ade 100644 --- a/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs +++ b/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Physics; +using Content.Shared.Singularity.Components; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Log; @@ -13,10 +14,9 @@ using Robust.Shared.ViewVariables; namespace Content.Server.Singularity.Components { [RegisterComponent] - public class ContainmentFieldGeneratorComponent : Component + [ComponentReference(typeof(SharedContainmentFieldGeneratorComponent))] + public class ContainmentFieldGeneratorComponent : SharedContainmentFieldGeneratorComponent { - public override string Name => "ContainmentFieldGenerator"; - private int _powerBuffer; [ViewVariables] diff --git a/Content.Server/Singularity/Components/ServerSingularityComponent.cs b/Content.Server/Singularity/Components/ServerSingularityComponent.cs index 9a75e0673a..ff22db994d 100644 --- a/Content.Server/Singularity/Components/ServerSingularityComponent.cs +++ b/Content.Server/Singularity/Components/ServerSingularityComponent.cs @@ -62,6 +62,8 @@ namespace Content.Server.Singularity.Components _ => 0 }; + public float MoveAccumulator; + // This is an interesting little workaround. // See, two singularities queuing deletion of each other at the same time will annihilate. // This is undesirable behaviour, so this flag allows the imperatively first one processed to take priority. @@ -95,11 +97,6 @@ namespace Content.Server.Singularity.Components _singularitySystem.ChangeSingularityLevel(this, 1); } - public void Update(int seconds) - { - Energy -= EnergyDrain * seconds; - } - protected override void OnRemove() { _playingSound?.Stop(); diff --git a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs index 75950f6866..cb543e10d4 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -168,6 +168,8 @@ namespace Content.Server.Singularity.EntitySystems private void ShotTimerCallback(EmitterComponent component) { + if (component.Deleted) return; + // Any power-off condition should result in the timer for this method being cancelled // and thus not firing DebugTools.Assert(component.IsPowered); diff --git a/Content.Server/Singularity/EntitySystems/SingularitySystem.cs b/Content.Server/Singularity/EntitySystems/SingularitySystem.cs index eafc982c37..b2b2745a73 100644 --- a/Content.Server/Singularity/EntitySystems/SingularitySystem.cs +++ b/Content.Server/Singularity/EntitySystems/SingularitySystem.cs @@ -1,8 +1,14 @@ +using Content.Server.Ghost.Components; using Content.Server.Singularity.Components; using Content.Shared.Singularity; +using Content.Shared.Singularity.Components; using JetBrains.Annotations; using Robust.Shared.Containers; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; namespace Content.Server.Singularity.EntitySystems @@ -10,7 +16,18 @@ namespace Content.Server.Singularity.EntitySystems [UsedImplicitly] public class SingularitySystem : SharedSingularitySystem { - private float _updateInterval = 1.0f; + [Dependency] private readonly IEntityLookup _lookup = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + + /// + /// How much energy the singulo gains from destroying a tile. + /// + private const int TileEnergyGain = 1; + + private const float GravityCooldown = 0.5f; + private float _gravityAccumulator; + + private int _updateInterval = 1; private float _accumulator; public override void Initialize() @@ -21,47 +38,23 @@ namespace Content.Server.Singularity.EntitySystems private void HandleCollide(EntityUid uid, ServerSingularityComponent component, StartCollideEvent args) { + // This handles bouncing off of containment walls. + // If you want the delete behavior we do it under DeleteEntities for reasons (not everything has physics). + // If we're being deleted by another singularity, this call is probably for that singularity. // Even if not, just don't bother. if (component.BeingDeletedByAnotherSingularity) return; - var otherEntity = args.OtherFixture.Body.Owner; - - if (otherEntity.TryGetComponent(out var mapGridComponent)) - { - foreach (var tile in mapGridComponent.Grid.GetTilesIntersecting(args.OurFixture.Body.GetWorldAABB())) - { - mapGridComponent.Grid.SetTile(tile.GridIndices, Robust.Shared.Map.Tile.Empty); - component.Energy++; - } - return; - } - - if (otherEntity.HasComponent() || - (otherEntity.TryGetComponent(out var containmentField) && containmentField.CanRepell(component.Owner))) - { - return; - } - - if (otherEntity.IsInContainer()) - return; - - // Singularity priority management / etc. - if (otherEntity.TryGetComponent(out var otherSingulo)) - otherSingulo.BeingDeletedByAnotherSingularity = true; - - otherEntity.QueueDelete(); - - if (otherEntity.TryGetComponent(out var singuloFood)) - component.Energy += singuloFood.Energy; - else - component.Energy++; + // Using this to also get smooth deletions is hard because we need to be hard for good bounce + // off of containment but also we need to be non-hard so we can freely move through the station. + // For now I've just made it so only the lookup does deletions and collision is just for fields. } public override void Update(float frameTime) { base.Update(frameTime); + _gravityAccumulator += frameTime; _accumulator += frameTime; while (_accumulator > _updateInterval) @@ -70,7 +63,142 @@ namespace Content.Server.Singularity.EntitySystems foreach (var singularity in ComponentManager.EntityQuery()) { - singularity.Update(1); + singularity.Energy -= singularity.EnergyDrain; + } + } + + while (_gravityAccumulator > GravityCooldown) + { + _gravityAccumulator -= GravityCooldown; + + foreach (var singularity in ComponentManager.EntityQuery()) + { + Update(singularity, GravityCooldown); + } + } + } + + private void Update(ServerSingularityComponent component, float frameTime) + { + if (component.BeingDeletedByAnotherSingularity) return; + + var worldPos = component.Owner.Transform.WorldPosition; + DestroyEntities(component, worldPos); + DestroyTiles(component, worldPos); + PullEntities(component, worldPos); + } + + private float PullRange(ServerSingularityComponent component) + { + // Level 6 is normally 15 range but that's yuge. + return 2 + component.Level * 2; + } + + private float DestroyTileRange(ServerSingularityComponent component) + { + return component.Level - 0.5f; + } + + private bool CanDestroy(SharedSingularityComponent component, IEntity entity) + { + return entity == component.Owner || + entity.HasComponent() || + entity.HasComponent() || + entity.HasComponent() || + entity.HasComponent(); + } + + private void HandleDestroy(ServerSingularityComponent component, IEntity entity) + { + // TODO: Need singuloimmune tag + if (CanDestroy(component, entity)) return; + + // Singularity priority management / etc. + if (entity.TryGetComponent(out var otherSingulo)) + { + // MERGE + if (!otherSingulo.BeingDeletedByAnotherSingularity) + { + component.Energy += otherSingulo.Energy; + } + + otherSingulo.BeingDeletedByAnotherSingularity = true; + } + + entity.QueueDelete(); + + if (entity.TryGetComponent(out var singuloFood)) + component.Energy += singuloFood.Energy; + else + component.Energy++; + } + + /// + /// Handle deleting entities and increasing energy + /// + private void DestroyEntities(ServerSingularityComponent component, Vector2 worldPos) + { + // The reason we don't /just/ use collision is because we'll be deleting stuff that may not necessarily have physics (e.g. carpets). + var destroyRange = DestroyTileRange(component); + + foreach (var entity in _lookup.GetEntitiesInRange(component.Owner.Transform.MapID, worldPos, destroyRange)) + { + HandleDestroy(component, entity); + } + } + + private bool CanPull(IEntity entity) + { + return !(entity.HasComponent() || + entity.HasComponent() || + entity.HasComponent() || + entity.IsInContainer()); + } + + private void PullEntities(ServerSingularityComponent component, Vector2 worldPos) + { + // TODO: When we split up dynamic and static trees we might be able to make items always on the broadphase + // in which case we can just query dynamictree directly for brrt + var pullRange = PullRange(component); + var destroyRange = DestroyTileRange(component); + + foreach (var entity in _lookup.GetEntitiesInRange(component.Owner.Transform.MapID, worldPos, pullRange)) + { + // I tried having it so level 6 can de-anchor. BAD IDEA, MASSIVE LAG. + if (entity == component.Owner || + !entity.TryGetComponent(out var collidableComponent) || + collidableComponent.BodyType == BodyType.Static) continue; + + if (!CanPull(entity)) continue; + + var vec = worldPos - entity.Transform.WorldPosition; + + if (vec.Length < destroyRange - 0.01f) continue; + + var speed = vec.Length * component.Level * collidableComponent.Mass; + + // Because tile friction is so high we'll just multiply by mass so stuff like closets can even move. + collidableComponent.ApplyLinearImpulse(vec.Normalized * speed); + } + } + + /// + /// Destroy any grid tiles within the relevant Level range. + /// + private void DestroyTiles(ServerSingularityComponent component, Vector2 worldPos) + { + var radius = DestroyTileRange(component); + + var circle = new Circle(worldPos, radius); + var box = new Box2(worldPos - radius, worldPos + radius); + + foreach (var grid in _mapManager.FindGridsIntersecting(component.Owner.Transform.MapID, box)) + { + foreach (var tile in grid.GetTilesIntersecting(circle)) + { + if (tile.Tile.IsEmpty) continue; + grid.SetTile(tile.GridIndices, Tile.Empty); + component.Energy += TileEnergyGain; } } } diff --git a/Content.Shared/Friction/SharedTileFrictionController.cs b/Content.Shared/Friction/SharedTileFrictionController.cs index 66f237185b..4c78f655dc 100644 --- a/Content.Shared/Friction/SharedTileFrictionController.cs +++ b/Content.Shared/Friction/SharedTileFrictionController.cs @@ -53,7 +53,8 @@ namespace Content.Shared.Friction foreach (var body in map.AwakeBodies) { // Only apply friction when it's not a mob (or the mob doesn't have control) - if (prediction && !body.Predict || + if (body.Deleted || + prediction && !body.Predict || body.BodyStatus == BodyStatus.InAir || Mover.UseMobMovement(body.Owner.Uid)) continue; diff --git a/Content.Shared/Singularity/Components/SharedContainmentFieldComponent.cs b/Content.Shared/Singularity/Components/SharedContainmentFieldComponent.cs new file mode 100644 index 0000000000..42b73c5a7d --- /dev/null +++ b/Content.Shared/Singularity/Components/SharedContainmentFieldComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameObjects; + +namespace Content.Shared.Singularity.Components +{ + public abstract class SharedContainmentFieldComponent : Component + { + public override string Name => "ContainmentField"; + } +} diff --git a/Content.Shared/Singularity/Components/SharedContainmentFieldGeneratorComponent.cs b/Content.Shared/Singularity/Components/SharedContainmentFieldGeneratorComponent.cs new file mode 100644 index 0000000000..8363d1c1c7 --- /dev/null +++ b/Content.Shared/Singularity/Components/SharedContainmentFieldGeneratorComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameObjects; + +namespace Content.Shared.Singularity.Components +{ + public abstract class SharedContainmentFieldGeneratorComponent : Component + { + public override string Name => "ContainmentFieldGenerator"; + } +} diff --git a/Content.Shared/Singularity/Components/SharedSingularityComponent.cs b/Content.Shared/Singularity/Components/SharedSingularityComponent.cs index 687a4d1494..59e5afe7a0 100644 --- a/Content.Shared/Singularity/Components/SharedSingularityComponent.cs +++ b/Content.Shared/Singularity/Components/SharedSingularityComponent.cs @@ -12,8 +12,6 @@ namespace Content.Shared.Singularity.Components { public override string Name => "Singularity"; - [DataField("deleteFixture")] public string? DeleteFixtureId { get; } = default; - /// /// Changed by /// diff --git a/Content.Shared/Singularity/SharedSingularitySystem.cs b/Content.Shared/Singularity/SharedSingularitySystem.cs index 9905ad300b..e7c706ed46 100644 --- a/Content.Shared/Singularity/SharedSingularitySystem.cs +++ b/Content.Shared/Singularity/SharedSingularitySystem.cs @@ -4,11 +4,14 @@ using Content.Shared.Singularity.Components; using Robust.Shared.GameObjects; using Robust.Shared.Maths; using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Dynamics; namespace Content.Shared.Singularity { public abstract class SharedSingularitySystem : EntitySystem { + public const string DeleteFixture = "DeleteCircle"; + private float GetFalloff(int level) { return level switch @@ -72,8 +75,7 @@ namespace Content.Shared.Singularity } if (physics != null && - singularity.DeleteFixtureId != null && - physics.GetFixture(singularity.DeleteFixtureId) is {Shape: PhysShapeCircle circle}) + physics.GetFixture(DeleteFixture) is {Shape: PhysShapeCircle circle}) { circle.Radius = value - 0.5f; } @@ -86,5 +88,24 @@ namespace Content.Shared.Singularity singularity.Dirty(); } + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandleFieldCollision); + } + + private void HandleFieldCollision(EntityUid uid, SharedSingularityComponent component, PreventCollideEvent args) + { + var other = args.BodyB.Owner; + + if ((!other.HasComponent() && + !other.HasComponent()) || + component.Level >= 4) + { + args.Cancel(); + return; + } + } } } diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml index bcc1dd509f..9fe45c9e4b 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml @@ -11,20 +11,21 @@ shape: !type:PhysShapeCircle radius: 0.5 - mass: 5 - # Keep an eye on ParticlesProjectile when adjusting these - layer: - - Impassable + restitution: 0.9 mask: - AllMask + layer: + - AllMask - type: Singularity - type: SingularityDistortion - type: RadiationPulse range: 15 decay: false + dps: 1 - type: Sprite sprite: Structures/Power/Generation/Singularity/singularity_1.rsi state: singularity_1 + netsync: false - type: Icon sprite: Structures/Power/Generation/Singularity/singularity_1.rsi state: singularity_1