From 9b54a8a2ae748ca9a17e4f1eaff15586fffa4335 Mon Sep 17 00:00:00 2001 From: ScalyChimp <72841710+ScalyChimp@users.noreply.github.com> Date: Thu, 16 Jun 2022 14:46:21 +0100 Subject: [PATCH] ECS containmentfieldgeneratorcomponent (#8757) * ECS containmentfieldgeneratorcomponent * Fix tests and clean up one line * check for anchored in a better way * Fix dependency exception I'm not really happy with this solution, it's not very good but I'm not sure how to do it better without refactoring way more than I want to. Maybe I'm missing something. * review Co-authored-by: metalgearsloth --- .../Components/ContainmentFieldConnection.cs | 33 +++- .../ContainmentFieldGeneratorComponent.cs | 151 +--------------- .../ContainmentFieldGeneratorSystem.cs | 161 +++++++++++++++++- .../EntitySystems/SingularitySystem.cs | 4 +- 4 files changed, 185 insertions(+), 164 deletions(-) diff --git a/Content.Server/Singularity/Components/ContainmentFieldConnection.cs b/Content.Server/Singularity/Components/ContainmentFieldConnection.cs index 6851a3b84d..ccfc503a08 100644 --- a/Content.Server/Singularity/Components/ContainmentFieldConnection.cs +++ b/Content.Server/Singularity/Components/ContainmentFieldConnection.cs @@ -1,13 +1,15 @@ using System.Threading; +using Content.Server.Singularity.EntitySystems; using Content.Shared.Singularity.Components; +using Robust.Server.GameObjects; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.Singularity.Components { public sealed class ContainmentFieldConnection : IDisposable { - public readonly ContainmentFieldGeneratorComponent Generator1; - public readonly ContainmentFieldGeneratorComponent Generator2; + public ContainmentFieldGeneratorComponent Generator1; + public ContainmentFieldGeneratorComponent Generator2; private readonly List _fields = new(); private int _sharedEnergyPool; private readonly CancellationTokenSource _powerDecreaseCancellationTokenSource = new(); @@ -63,7 +65,7 @@ namespace Content.Server.Singularity.Components } - Timer.SpawnRepeating(1000, () => { SharedEnergyPool--;}, _powerDecreaseCancellationTokenSource.Token); + Timer.SpawnRepeating(1000, () => { SharedEnergyPool--; }, _powerDecreaseCancellationTokenSource.Token); } public bool CanRepel(SharedSingularityComponent toRepel) @@ -82,8 +84,29 @@ namespace Content.Server.Singularity.Components } _fields.Clear(); - Generator1.RemoveConnection(this); - Generator2.RemoveConnection(this); + RemoveConnection(this, Generator1); + RemoveConnection(this, Generator2); + } + + public void RemoveConnection(ContainmentFieldConnection? connection, ContainmentFieldGeneratorComponent component) + { + if (component.Connection1?.Item2 == connection) + { + component.Connection1 = null; + } + else if (component.Connection2?.Item2 == connection) + { + component.Connection2 = null; + } + else if (connection != null) + { + Logger.Error("RemoveConnection called on Containmentfieldgenerator with a connection that can't be found in its connections."); + } + if (IoCManager.Resolve().TryGetComponent(component.Owner, out var pointLightComponent)) + { + bool hasAnyConnection = (component.Connection1 != null) || (component.Connection2 != null); + pointLightComponent.Enabled = hasAnyConnection; + } } } } diff --git a/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs b/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs index b9f3aad25a..0e9631c202 100644 --- a/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs +++ b/Content.Server/Singularity/Components/ContainmentFieldGeneratorComponent.cs @@ -11,8 +11,6 @@ namespace Content.Server.Singularity.Components [ComponentReference(typeof(SharedContainmentFieldGeneratorComponent))] public sealed class ContainmentFieldGeneratorComponent : SharedContainmentFieldGeneratorComponent { - [Dependency] private readonly IEntityManager _entMan = default!; - private int _powerBuffer; [ViewVariables] @@ -22,153 +20,8 @@ namespace Content.Server.Singularity.Components set => _powerBuffer = Math.Clamp(value, 0, 6); } - public void ReceivePower(int power) - { - var totalPower = power + PowerBuffer; - var powerPerConnection = totalPower / 2; - var newBuffer = totalPower % 2; - TryPowerConnection(ref _connection1, ref newBuffer, powerPerConnection); - TryPowerConnection(ref _connection2, ref newBuffer, powerPerConnection); + public Tuple? Connection1; + public Tuple? Connection2; - PowerBuffer = newBuffer; - } - - private void TryPowerConnection(ref Tuple? connectionProperty, ref int powerBuffer, int powerPerConnection) - { - if (connectionProperty != null) - { - connectionProperty.Item2.SharedEnergyPool += powerPerConnection; - } - else - { - if (TryGenerateFieldConnection(ref connectionProperty)) - { - connectionProperty.Item2.SharedEnergyPool += powerPerConnection; - } - else - { - powerBuffer += powerPerConnection; - } - } - } - - private Tuple? _connection1; - private Tuple? _connection2; - - public bool CanRepel(SharedSingularityComponent toRepel) => _connection1?.Item2?.CanRepel(toRepel) == true || - _connection2?.Item2?.CanRepel(toRepel) == true; - - public void OnAnchoredChanged() - { - if(_entMan.TryGetComponent(Owner, out var physicsComponent) && physicsComponent.BodyType != BodyType.Static) - { - _connection1?.Item2.Dispose(); - _connection2?.Item2.Dispose(); - } - } - - private bool IsConnectedWith(ContainmentFieldGeneratorComponent comp) - { - - return comp == this || _connection1?.Item2.Generator1 == comp || _connection1?.Item2.Generator2 == comp || - _connection2?.Item2.Generator1 == comp || _connection2?.Item2.Generator2 == comp; - } - - public bool HasFreeConnections() - { - return _connection1 == null || _connection2 == null; - } - - private bool TryGenerateFieldConnection([NotNullWhen(true)] ref Tuple? propertyFieldTuple) - { - if (propertyFieldTuple != null) return false; - if(_entMan.TryGetComponent(Owner, out var physicsComponent) && physicsComponent.BodyType != BodyType.Static) return false; - - foreach (var direction in new[] {Direction.North, Direction.East, Direction.South, Direction.West}) - { - if (_connection1?.Item1 == direction || _connection2?.Item1 == direction) continue; - - var dirVec = _entMan.GetComponent(Owner).WorldRotation.RotateVec(direction.ToVec()); - var ray = new CollisionRay(_entMan.GetComponent(Owner).WorldPosition, dirVec, (int) CollisionGroup.MobMask); - var rawRayCastResults = EntitySystem.Get().IntersectRay(_entMan.GetComponent(Owner).MapID, ray, 4.5f, Owner, false); - - var rayCastResults = rawRayCastResults as RayCastResults[] ?? rawRayCastResults.ToArray(); - if(!rayCastResults.Any()) continue; - - RayCastResults? closestResult = null; - var smallestDist = 4.5f; - foreach (var res in rayCastResults) - { - if (res.Distance > smallestDist) continue; - - smallestDist = res.Distance; - closestResult = res; - } - if(closestResult == null) continue; - var ent = closestResult.Value.HitEntity; - if (!_entMan.TryGetComponent(ent, out var fieldGeneratorComponent) || - fieldGeneratorComponent.Owner == Owner || - !fieldGeneratorComponent.HasFreeConnections() || - IsConnectedWith(fieldGeneratorComponent) || - !_entMan.TryGetComponent(ent, out var collidableComponent) || - collidableComponent.BodyType != BodyType.Static) - { - continue; - } - - var connection = new ContainmentFieldConnection(this, fieldGeneratorComponent); - propertyFieldTuple = new Tuple(direction, connection); - if (fieldGeneratorComponent._connection1 == null) - { - fieldGeneratorComponent._connection1 = new Tuple(direction.GetOpposite(), connection); - } - else if (fieldGeneratorComponent._connection2 == null) - { - fieldGeneratorComponent._connection2 = new Tuple(direction.GetOpposite(), connection); - } - else - { - Logger.Error("When trying to connect two Containmentfieldgenerators, the second one already had two connection but the check didn't catch it"); - } - UpdateConnectionLights(); - return true; - } - - return false; - } - - public void RemoveConnection(ContainmentFieldConnection? connection) - { - if (_connection1?.Item2 == connection) - { - _connection1 = null; - UpdateConnectionLights(); - } - else if (_connection2?.Item2 == connection) - { - _connection2 = null; - UpdateConnectionLights(); - } - else if(connection != null) - { - Logger.Error("RemoveConnection called on Containmentfieldgenerator with a connection that can't be found in its connections."); - } - } - - public void UpdateConnectionLights() - { - if (_entMan.TryGetComponent(Owner, out var pointLightComponent)) - { - bool hasAnyConnection = (_connection1 != null) || (_connection2 != null); - pointLightComponent.Enabled = hasAnyConnection; - } - } - - protected override void OnRemove() - { - _connection1?.Item2.Dispose(); - _connection2?.Item2.Dispose(); - base.OnRemove(); - } } } diff --git a/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs b/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs index 67e92dc163..0cee86fbc0 100644 --- a/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs @@ -1,8 +1,13 @@ using Content.Server.ParticleAccelerator.Components; using Content.Server.Singularity.Components; +using Content.Shared.Physics; using Content.Shared.Singularity.Components; using Content.Shared.Tag; +using Robust.Server.GameObjects; +using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Content.Server.Singularity.EntitySystems { @@ -14,10 +19,26 @@ namespace Content.Server.Singularity.EntitySystems { base.Initialize(); - SubscribeLocalEvent(BodyTypeChanged); + SubscribeLocalEvent(OnComponentRemoved); SubscribeLocalEvent(HandleFieldCollide); SubscribeLocalEvent(HandleGeneratorCollide); SubscribeLocalEvent(HandleParticleCollide); + SubscribeLocalEvent(OnAnchorChanged); + } + + private void OnComponentRemoved(EntityUid uid, ContainmentFieldGeneratorComponent component, ComponentRemove args) + { + component.Connection1?.Item2.Dispose(); + component.Connection2?.Item2.Dispose(); + } + + private void OnAnchorChanged(EntityUid uid, ContainmentFieldGeneratorComponent component, ref AnchorStateChangedEvent args) + { + if (!args.Anchored) + { + component.Connection1?.Item2.Dispose(); + component.Connection2?.Item2.Dispose(); + } } private void HandleParticleCollide(EntityUid uid, ParticleProjectileComponent component, StartCollideEvent args) @@ -40,8 +61,9 @@ namespace Content.Server.Singularity.EntitySystems private void HandleGeneratorCollide(EntityUid uid, ContainmentFieldGeneratorComponent component, StartCollideEvent args) { - if (_tags.HasTag(args.OtherFixture.Body.Owner, "EmitterBolt")) { - component.ReceivePower(6); + if (_tags.HasTag(args.OtherFixture.Body.Owner, "EmitterBolt")) + { + ReceivePower(6, component); } } @@ -54,12 +76,135 @@ namespace Content.Server.Singularity.EntitySystems } } - private static void BodyTypeChanged( - EntityUid uid, - ContainmentFieldGeneratorComponent component, - ref PhysicsBodyTypeChangedEvent args) + public void ReceivePower(int power, ContainmentFieldGeneratorComponent component) { - component.OnAnchoredChanged(); + var totalPower = power + component.PowerBuffer; + var powerPerConnection = totalPower / 2; + var newBuffer = totalPower % 2; + TryPowerConnection(ref component.Connection1, ref newBuffer, powerPerConnection, component); + TryPowerConnection(ref component.Connection2, ref newBuffer, powerPerConnection, component); + + component.PowerBuffer = newBuffer; } + + public void UpdateConnectionLights(ContainmentFieldGeneratorComponent component) + { + if (EntityManager.TryGetComponent(component.Owner, out var pointLightComponent)) + { + bool hasAnyConnection = (component.Connection1 != null) || (component.Connection2 != null); + pointLightComponent.Enabled = hasAnyConnection; + } + } + + public void RemoveConnection(ContainmentFieldConnection? connection, ContainmentFieldGeneratorComponent component) + { + if (component.Connection1?.Item2 == connection) + { + component.Connection1 = null; + UpdateConnectionLights(component); + } + else if (component.Connection2?.Item2 == connection) + { + component.Connection2 = null; + UpdateConnectionLights(component); + } + else if (connection != null) + { + Logger.Error("RemoveConnection called on Containmentfieldgenerator with a connection that can't be found in its connections."); + } + } + + private bool TryGenerateFieldConnection([NotNullWhen(true)] ref Tuple? propertyFieldTuple, ContainmentFieldGeneratorComponent component) + { + if (propertyFieldTuple != null) return false; + if (EntityManager.TryGetComponent(component.Owner, out var xform) && !xform.Anchored) return false; + + foreach (var direction in new[] { Direction.North, Direction.East, Direction.South, Direction.West }) + { + if (component.Connection1?.Item1 == direction || component.Connection2?.Item1 == direction) continue; + + var dirVec = EntityManager.GetComponent(component.Owner).WorldRotation.RotateVec(direction.ToVec()); + var ray = new CollisionRay(EntityManager.GetComponent(component.Owner).WorldPosition, dirVec, (int) CollisionGroup.MobMask); + var rawRayCastResults = EntitySystem.Get().IntersectRay(EntityManager.GetComponent(component.Owner).MapID, ray, 4.5f, component.Owner, false); + + var rayCastResults = rawRayCastResults as RayCastResults[] ?? rawRayCastResults.ToArray(); + if (!rayCastResults.Any()) continue; + + RayCastResults? closestResult = null; + var smallestDist = 4.5f; + foreach (var res in rayCastResults) + { + if (res.Distance > smallestDist) continue; + + smallestDist = res.Distance; + closestResult = res; + } + if (closestResult == null) continue; + var ent = closestResult.Value.HitEntity; + if (!EntityManager.TryGetComponent(ent, out var fieldGeneratorComponent) || + fieldGeneratorComponent.Owner == component.Owner || + !HasFreeConnections(fieldGeneratorComponent) || + IsConnectedWith(component, fieldGeneratorComponent) || + !EntityManager.TryGetComponent(ent, out var collidableComponent) || + collidableComponent.BodyType != BodyType.Static) + { + continue; + } + + var connection = new ContainmentFieldConnection(component, fieldGeneratorComponent); + propertyFieldTuple = new Tuple(direction, connection); + if (fieldGeneratorComponent.Connection1 == null) + { + fieldGeneratorComponent.Connection1 = new Tuple(direction.GetOpposite(), connection); + } + else if (fieldGeneratorComponent.Connection2 == null) + { + fieldGeneratorComponent.Connection2 = new Tuple(direction.GetOpposite(), connection); + } + else + { + Logger.Error("When trying to connect two Containmentfieldgenerators, the second one already had two connection but the check didn't catch it"); + } + UpdateConnectionLights(component); + return true; + } + + return false; + } + + private bool IsConnectedWith(ContainmentFieldGeneratorComponent comp, ContainmentFieldGeneratorComponent otherComp) + { + return otherComp == comp || comp.Connection1?.Item2.Generator1 == otherComp || comp.Connection1?.Item2.Generator2 == otherComp || + comp.Connection2?.Item2.Generator1 == otherComp || comp.Connection2?.Item2.Generator2 == otherComp; + } + + public void TryPowerConnection(ref Tuple? connectionProperty, ref int powerBuffer, int powerPerConnection, ContainmentFieldGeneratorComponent component) + { + if (connectionProperty != null) + { + connectionProperty.Item2.SharedEnergyPool += powerPerConnection; + } + else + { + if (TryGenerateFieldConnection(ref connectionProperty, component)) + { + connectionProperty.Item2.SharedEnergyPool += powerPerConnection; + } + else + { + powerBuffer += powerPerConnection; + } + } + } + + public bool CanRepel(SharedSingularityComponent toRepel, ContainmentFieldGeneratorComponent component) => component.Connection1?.Item2?.CanRepel(toRepel) == true || + component.Connection2?.Item2?.CanRepel(toRepel) == true; + + public bool HasFreeConnections(ContainmentFieldGeneratorComponent component) + { + return component.Connection1 == null || component.Connection2 == null; + } + + } } diff --git a/Content.Server/Singularity/EntitySystems/SingularitySystem.cs b/Content.Server/Singularity/EntitySystems/SingularitySystem.cs index 671d6d5ef9..eb78038e3f 100644 --- a/Content.Server/Singularity/EntitySystems/SingularitySystem.cs +++ b/Content.Server/Singularity/EntitySystems/SingularitySystem.cs @@ -18,7 +18,7 @@ namespace Content.Server.Singularity.EntitySystems [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly PVSOverrideSystem _pvs = default!; - + [Dependency] private readonly ContainmentFieldGeneratorSystem _fieldGeneratorSystem = default!; /// /// How much energy the singulo gains from destroying a tile. /// @@ -132,7 +132,7 @@ namespace Content.Server.Singularity.EntitySystems !EntityManager.HasComponent(entity) && (component.Level > 4 || !EntityManager.HasComponent(entity) && - !(EntityManager.TryGetComponent(entity, out var containFieldGen) && containFieldGen.CanRepel(component))); + !(EntityManager.TryGetComponent(entity, out var containFieldGen) && _fieldGeneratorSystem.CanRepel(component, containFieldGen))); } private void HandleDestroy(ServerSingularityComponent component, EntityUid entity)