using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Content.Shared.Ghost;
using Content.Shared.Singularity.Components;
using Robust.Shared.Physics;
namespace Content.Shared.Singularity.EntitySystems;
///
/// The entity system primarily responsible for managing s.
///
public abstract class SharedEventHorizonSystem : EntitySystem
{
[Dependency] private readonly FixtureSystem _fixtures = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] protected readonly IViewVariablesManager Vvm = default!;
public override void Initialize()
{
base.Initialize();
// Allows for predicted collisions with singularities.
SubscribeLocalEvent(OnEventHorizonStartup);
SubscribeLocalEvent(OnPreventCollide);
var vvHandle = Vvm.GetTypeHandler();
vvHandle.AddPath(nameof(EventHorizonComponent.Radius), (_, comp) => comp.Radius, (uid, value, comp) => SetRadius(uid, value, eventHorizon: comp));
vvHandle.AddPath(nameof(EventHorizonComponent.CanBreachContainment), (_, comp) => comp.CanBreachContainment, (uid, value, comp) => SetCanBreachContainment(uid, value, eventHorizon: comp));
vvHandle.AddPath(nameof(EventHorizonComponent.ColliderFixtureId), (_, comp) => comp.ColliderFixtureId, (uid, value, comp) => SetColliderFixtureId(uid, value, eventHorizon: comp));
vvHandle.AddPath(nameof(EventHorizonComponent.ConsumerFixtureId), (_, comp) => comp.ConsumerFixtureId, (uid, value, comp) => SetConsumerFixtureId(uid, value, eventHorizon: comp));
}
public override void Shutdown()
{
var vvHandle = Vvm.GetTypeHandler();
vvHandle.RemovePath(nameof(EventHorizonComponent.Radius));
vvHandle.RemovePath(nameof(EventHorizonComponent.CanBreachContainment));
vvHandle.RemovePath(nameof(EventHorizonComponent.ColliderFixtureId));
vvHandle.RemovePath(nameof(EventHorizonComponent.ConsumerFixtureId));
base.Shutdown();
}
#region Getters/Setters
///
/// Setter for
/// May also update the fixture associated with the event horizon.
///
/// The uid of the event horizon to change the radius of.
/// The new radius of the event horizon.
/// Whether to update the associated fixture upon changing the radius of the event horizon.
/// The state of the event horizon to change the radius of.
public void SetRadius(EntityUid uid, float value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
{
if (!Resolve(uid, ref eventHorizon))
return;
var oldValue = eventHorizon.Radius;
if (value == oldValue)
return;
eventHorizon.Radius = value;
Dirty(uid, eventHorizon);
if (updateFixture)
UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
}
///
/// Setter for
/// May also update the fixture associated with the event horizon.
///
/// The uid of the event horizon to make (in)capable of breaching containment.
/// Whether the event horizon should be able to breach containment.
/// Whether to update the associated fixture upon changing whether the event horizon can breach containment.
/// The state of the event horizon to make (in)capable of breaching containment.
public void SetCanBreachContainment(EntityUid uid, bool value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
{
if (!Resolve(uid, ref eventHorizon))
return;
var oldValue = eventHorizon.CanBreachContainment;
if (value == oldValue)
return;
eventHorizon.CanBreachContainment = value;
Dirty(uid, eventHorizon);
if (updateFixture)
UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
}
///
/// Setter for
/// May also update the fixture associated with the event horizon.
///
/// The uid of the event horizon with the fixture ID to change.
/// The new fixture ID to associate the event horizon with.
/// Whether to update the associated fixture upon changing whether the event horizon can breach containment.
/// The state of the event horizon with the fixture ID to change.
public void SetColliderFixtureId(EntityUid uid, string? value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
{
if (!Resolve(uid, ref eventHorizon))
return;
var oldValue = eventHorizon.ColliderFixtureId;
if (value == oldValue)
return;
eventHorizon.ColliderFixtureId = value;
Dirty(uid, eventHorizon);
if (updateFixture)
UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
}
///
/// Setter for
/// May also update the fixture associated with the event horizon.
///
/// The uid of the event horizon with the fixture ID to change.
/// The new fixture ID to associate the event horizon with.
/// Whether to update the associated fixture upon changing whether the event horizon can breach containment.
/// The state of the event horizon with the fixture ID to change.
public void SetConsumerFixtureId(EntityUid uid, string? value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
{
if (!Resolve(uid, ref eventHorizon))
return;
var oldValue = eventHorizon.ConsumerFixtureId;
if (value == oldValue)
return;
eventHorizon.ConsumerFixtureId = value;
Dirty(uid, eventHorizon);
if (updateFixture)
UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
}
///
/// Updates the state of the fixture associated with the event horizon.
///
/// The uid of the event horizon associated with the fixture to update.
/// The fixture manager component containing the fixture to update.
/// The state of the event horizon associated with the fixture to update.
public void UpdateEventHorizonFixture(EntityUid uid, FixturesComponent? fixtures = null, EventHorizonComponent? eventHorizon = null)
{
if (!Resolve(uid, ref eventHorizon))
return;
var consumerId = eventHorizon.ConsumerFixtureId;
var colliderId = eventHorizon.ColliderFixtureId;
if (consumerId == null || colliderId == null
|| !Resolve(uid, ref fixtures, logMissing: false))
return;
// Update both fixtures the event horizon is associated with:
var consumer = _fixtures.GetFixtureOrNull(uid, consumerId, fixtures);
if (consumer != null)
{
_physics.SetRadius(uid, consumerId, consumer, consumer.Shape, eventHorizon.Radius, fixtures);
_physics.SetHard(uid, consumer, false, fixtures);
}
var collider = _fixtures.GetFixtureOrNull(uid, colliderId, fixtures);
if (collider != null)
{
_physics.SetRadius(uid, colliderId, collider, collider.Shape, eventHorizon.Radius, fixtures);
_physics.SetHard(uid, collider, true, fixtures);
}
Dirty(uid, fixtures);
}
#endregion Getters/Setters
#region EventHandlers
///
/// Syncs the state of the fixture associated with the event horizon upon startup.
///
/// The entity that has just gained an event horizon component.
/// The event horizon component that is starting up.
/// The event arguments.
private void OnEventHorizonStartup(EntityUid uid, EventHorizonComponent comp, ComponentStartup args)
{
UpdateEventHorizonFixture(uid, eventHorizon: comp);
}
///
/// Prevents the event horizon from colliding with anything it cannot consume.
/// Most notably map grids and ghosts.
/// Also makes event horizons phase through containment if it can breach.
///
/// The entity that is trying to collide with another entity.
/// The event horizon of the former.
/// The event arguments.
private void OnPreventCollide(EntityUid uid, EventHorizonComponent comp, ref PreventCollideEvent args)
{
if (!args.Cancelled)
PreventCollide(uid, comp, ref args);
}
///
/// The actual, functional part of SharedEventHorizonSystem.OnPreventCollide.
/// The return value allows for overrides to early return if the base successfully handles collision prevention.
///
/// The entity that is trying to collide with another entity.
/// The event horizon of the former.
/// The event arguments.
/// A bool indicating whether the collision prevention has been handled.
protected virtual bool PreventCollide(EntityUid uid, EventHorizonComponent comp, ref PreventCollideEvent args)
{
var otherUid = args.OtherEntity;
// For prediction reasons always want the client to ignore these.
if (HasComp(otherUid) ||
HasComp(otherUid))
{
args.Cancelled = true;
return true;
}
// If we can, breach containment
// otherwise, check if it's containment and just keep the collision
if (HasComp(otherUid) ||
HasComp(otherUid))
{
if (comp.CanBreachContainment)
args.Cancelled = true;
return true;
}
return false;
}
#endregion EventHandlers
}