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 <comedian_vs_clown@hotmail.com>
This commit is contained in:
@@ -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<EntityUid> _fields = new();
|
||||
private int _sharedEnergyPool;
|
||||
private readonly CancellationTokenSource _powerDecreaseCancellationTokenSource = new();
|
||||
@@ -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<IEntityManager>().TryGetComponent<PointLightComponent>(component.Owner, out var pointLightComponent))
|
||||
{
|
||||
bool hasAnyConnection = (component.Connection1 != null) || (component.Connection2 != null);
|
||||
pointLightComponent.Enabled = hasAnyConnection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Direction, ContainmentFieldConnection>? Connection1;
|
||||
public Tuple<Direction, ContainmentFieldConnection>? Connection2;
|
||||
|
||||
PowerBuffer = newBuffer;
|
||||
}
|
||||
|
||||
private void TryPowerConnection(ref Tuple<Direction, ContainmentFieldConnection>? 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<Direction, ContainmentFieldConnection>? _connection1;
|
||||
private Tuple<Direction, ContainmentFieldConnection>? _connection2;
|
||||
|
||||
public bool CanRepel(SharedSingularityComponent toRepel) => _connection1?.Item2?.CanRepel(toRepel) == true ||
|
||||
_connection2?.Item2?.CanRepel(toRepel) == true;
|
||||
|
||||
public void OnAnchoredChanged()
|
||||
{
|
||||
if(_entMan.TryGetComponent<PhysicsComponent>(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<Direction, ContainmentFieldConnection>? propertyFieldTuple)
|
||||
{
|
||||
if (propertyFieldTuple != null) return false;
|
||||
if(_entMan.TryGetComponent<PhysicsComponent>(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<TransformComponent>(Owner).WorldRotation.RotateVec(direction.ToVec());
|
||||
var ray = new CollisionRay(_entMan.GetComponent<TransformComponent>(Owner).WorldPosition, dirVec, (int) CollisionGroup.MobMask);
|
||||
var rawRayCastResults = EntitySystem.Get<SharedPhysicsSystem>().IntersectRay(_entMan.GetComponent<TransformComponent>(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<ContainmentFieldGeneratorComponent?>(ent, out var fieldGeneratorComponent) ||
|
||||
fieldGeneratorComponent.Owner == Owner ||
|
||||
!fieldGeneratorComponent.HasFreeConnections() ||
|
||||
IsConnectedWith(fieldGeneratorComponent) ||
|
||||
!_entMan.TryGetComponent<PhysicsComponent?>(ent, out var collidableComponent) ||
|
||||
collidableComponent.BodyType != BodyType.Static)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var connection = new ContainmentFieldConnection(this, fieldGeneratorComponent);
|
||||
propertyFieldTuple = new Tuple<Direction, ContainmentFieldConnection>(direction, connection);
|
||||
if (fieldGeneratorComponent._connection1 == null)
|
||||
{
|
||||
fieldGeneratorComponent._connection1 = new Tuple<Direction, ContainmentFieldConnection>(direction.GetOpposite(), connection);
|
||||
}
|
||||
else if (fieldGeneratorComponent._connection2 == null)
|
||||
{
|
||||
fieldGeneratorComponent._connection2 = new Tuple<Direction, ContainmentFieldConnection>(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<PointLightComponent>(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ContainmentFieldGeneratorComponent, PhysicsBodyTypeChangedEvent>(BodyTypeChanged);
|
||||
SubscribeLocalEvent<ContainmentFieldGeneratorComponent, ComponentRemove>(OnComponentRemoved);
|
||||
SubscribeLocalEvent<ContainmentFieldComponent, StartCollideEvent>(HandleFieldCollide);
|
||||
SubscribeLocalEvent<ContainmentFieldGeneratorComponent, StartCollideEvent>(HandleGeneratorCollide);
|
||||
SubscribeLocalEvent<ParticleProjectileComponent, StartCollideEvent>(HandleParticleCollide);
|
||||
SubscribeLocalEvent<ContainmentFieldGeneratorComponent, AnchorStateChangedEvent>(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<PointLightComponent>(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<Direction, ContainmentFieldConnection>? propertyFieldTuple, ContainmentFieldGeneratorComponent component)
|
||||
{
|
||||
if (propertyFieldTuple != null) return false;
|
||||
if (EntityManager.TryGetComponent<TransformComponent>(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<TransformComponent>(component.Owner).WorldRotation.RotateVec(direction.ToVec());
|
||||
var ray = new CollisionRay(EntityManager.GetComponent<TransformComponent>(component.Owner).WorldPosition, dirVec, (int) CollisionGroup.MobMask);
|
||||
var rawRayCastResults = EntitySystem.Get<SharedPhysicsSystem>().IntersectRay(EntityManager.GetComponent<TransformComponent>(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<ContainmentFieldGeneratorComponent?>(ent, out var fieldGeneratorComponent) ||
|
||||
fieldGeneratorComponent.Owner == component.Owner ||
|
||||
!HasFreeConnections(fieldGeneratorComponent) ||
|
||||
IsConnectedWith(component, fieldGeneratorComponent) ||
|
||||
!EntityManager.TryGetComponent<PhysicsComponent?>(ent, out var collidableComponent) ||
|
||||
collidableComponent.BodyType != BodyType.Static)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var connection = new ContainmentFieldConnection(component, fieldGeneratorComponent);
|
||||
propertyFieldTuple = new Tuple<Direction, ContainmentFieldConnection>(direction, connection);
|
||||
if (fieldGeneratorComponent.Connection1 == null)
|
||||
{
|
||||
fieldGeneratorComponent.Connection1 = new Tuple<Direction, ContainmentFieldConnection>(direction.GetOpposite(), connection);
|
||||
}
|
||||
else if (fieldGeneratorComponent.Connection2 == null)
|
||||
{
|
||||
fieldGeneratorComponent.Connection2 = new Tuple<Direction, ContainmentFieldConnection>(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<Direction, ContainmentFieldConnection>? 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!;
|
||||
/// <summary>
|
||||
/// How much energy the singulo gains from destroying a tile.
|
||||
/// </summary>
|
||||
@@ -132,7 +132,7 @@ namespace Content.Server.Singularity.EntitySystems
|
||||
!EntityManager.HasComponent<GhostComponent>(entity) &&
|
||||
(component.Level > 4 ||
|
||||
!EntityManager.HasComponent<ContainmentFieldComponent>(entity) &&
|
||||
!(EntityManager.TryGetComponent<ContainmentFieldGeneratorComponent>(entity, out var containFieldGen) && containFieldGen.CanRepel(component)));
|
||||
!(EntityManager.TryGetComponent<ContainmentFieldGeneratorComponent>(entity, out var containFieldGen) && _fieldGeneratorSystem.CanRepel(component, containFieldGen)));
|
||||
}
|
||||
|
||||
private void HandleDestroy(ServerSingularityComponent component, EntityUid entity)
|
||||
|
||||
Reference in New Issue
Block a user