Fixes singularity collision and consume range (#13424)

Co-authored-by: keronshb <keronshb@live.com>
This commit is contained in:
TemporalOroboros
2023-07-19 01:01:27 -07:00
committed by GitHub
parent afef518fc5
commit 6313164368
13 changed files with 290 additions and 277 deletions

View File

@@ -390,11 +390,11 @@ public sealed class ContainmentFieldGeneratorSystem : EntitySystem
/// <param name="uid">The entity the singularity is trying to eat.</param> /// <param name="uid">The entity the singularity is trying to eat.</param>
/// <param name="comp">The containment field generator the singularity is trying to eat.</param> /// <param name="comp">The containment field generator the singularity is trying to eat.</param>
/// <param name="args">The event arguments.</param> /// <param name="args">The event arguments.</param>
private void PreventBreach(EntityUid uid, ContainmentFieldGeneratorComponent comp, EventHorizonAttemptConsumeEntityEvent args) private void PreventBreach(EntityUid uid, ContainmentFieldGeneratorComponent comp, ref EventHorizonAttemptConsumeEntityEvent args)
{ {
if (args.Cancelled) if (args.Cancelled)
return; return;
if (comp.IsConnected && !args.EventHorizon.CanBreachContainment) if (comp.IsConnected && !args.EventHorizon.CanBreachContainment)
args.Cancel(); args.Cancelled = true;
} }
} }

View File

@@ -41,9 +41,9 @@ public sealed class ContainmentFieldSystem : EntitySystem
} }
} }
private void HandleEventHorizon(EntityUid uid, ContainmentFieldComponent component, EventHorizonAttemptConsumeEntityEvent args) private void HandleEventHorizon(EntityUid uid, ContainmentFieldComponent component, ref EventHorizonAttemptConsumeEntityEvent args)
{ {
if(!args.Cancelled && !args.EventHorizon.CanBreachContainment) if(!args.Cancelled && !args.EventHorizon.CanBreachContainment)
args.Cancel(); args.Cancelled = true;
} }
} }

View File

@@ -1,21 +1,19 @@
using System.Numerics;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Ghost.Components;
using Content.Server.Mind.Components;
using Content.Server.Station.Components;
using Content.Server.Singularity.Events;
using Content.Shared.Database;
using Content.Shared.Singularity.Components;
using Content.Shared.Singularity.EntitySystems;
using Content.Shared.Tag;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
using System.Numerics;
using Content.Shared.Singularity.Components;
using Content.Shared.Singularity.EntitySystems;
using Content.Server.Ghost.Components;
using Content.Server.Mind.Components;
using Content.Server.Station.Components;
using Content.Server.Singularity.Components;
using Content.Server.Singularity.Events;
using Content.Shared.Database;
using Content.Shared.Tag;
namespace Content.Server.Singularity.EntitySystems; namespace Content.Server.Singularity.EntitySystems;
@@ -31,19 +29,10 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
[Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly IMapManager _mapMan = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!;
#endregion Dependencies #endregion Dependencies
/// <summary>
/// The maximum number of nested containers an event horizon is allowed to eat through in an attempt to get to the map.
/// </summary>
private const int MaxEventHorizonUnnestingIterations = 100;
/// <summary>
/// The maximum number of nested containers an immune entity in a container being consumed by an event horizon is allowed to search through before it gives up and just jumps to the map.
/// </summary>
private const int MaxEventHorizonDumpSearchIterations = 100;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -74,26 +63,20 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Updates the cooldowns of all event horizons. /// Updates the cooldowns of all event horizons.
/// If an event horizon are off cooldown this makes it consume everything within range and resets their cooldown. /// If an event horizon are off cooldown this makes it consume everything within range and resets their cooldown.
/// </summary> /// </summary>
/// <param name="frameTime">The amount of time that has elapsed since the last cooldown update.</param>
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
if(!_timing.IsFirstTimePredicted) var query = EntityQueryEnumerator<EventHorizonComponent, TransformComponent>();
return; while (query.MoveNext(out var uid, out var eventHorizon, out var xform))
foreach(var (eventHorizon, xform) in EntityManager.EntityQuery<EventHorizonComponent, TransformComponent>())
{ {
var curTime = _timing.CurTime; var curTime = _timing.CurTime;
if (eventHorizon.NextConsumeWaveTime <= curTime) if (eventHorizon.NextConsumeWaveTime <= curTime)
Update(eventHorizon.Owner, eventHorizon, xform); Update(uid, eventHorizon, xform);
} }
} }
/// <summary> /// <summary>
/// Makes an event horizon consume everything nearby and resets the cooldown it for the next automated wave. /// Makes an event horizon consume everything nearby and resets the cooldown it for the next automated wave.
/// </summary> /// </summary>
/// <param name="uid">The uid of the event horizon consuming everything nearby.</param>
/// <param name="eventHorizon">The event horizon we want to consume nearby things.</param>
/// <param name="xform">The transform of the event horizon.</param>
public void Update(EntityUid uid, EventHorizonComponent? eventHorizon = null, TransformComponent? xform = null) public void Update(EntityUid uid, EventHorizonComponent? eventHorizon = null, TransformComponent? xform = null)
{ {
if (!Resolve(uid, ref eventHorizon)) if (!Resolve(uid, ref eventHorizon))
@@ -108,14 +91,15 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
// Handle singularities some admin smited into a locker. // Handle singularities some admin smited into a locker.
if (_containerSystem.TryGetContainingContainer(uid, out var container, transform: xform) if (_containerSystem.TryGetContainingContainer(uid, out var container, transform: xform)
&& !AttemptConsumeEntity(container.Owner, eventHorizon)) && !AttemptConsumeEntity(uid, container.Owner, eventHorizon))
{ {
// Locker is indestructible. Consume everything else in the locker instead of magically teleporting out.
ConsumeEntitiesInContainer(uid, container, eventHorizon, container); ConsumeEntitiesInContainer(uid, container, eventHorizon, container);
return; return;
} }
if (eventHorizon.Radius > 0.0f) if (eventHorizon.Radius > 0.0f)
ConsumeEverythingInRange(xform.Owner, eventHorizon.Radius, xform, eventHorizon); ConsumeEverythingInRange(uid, eventHorizon.Radius, xform, eventHorizon);
} }
#region Consume #region Consume
@@ -125,48 +109,42 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// <summary> /// <summary>
/// Makes an event horizon consume a given entity. /// Makes an event horizon consume a given entity.
/// </summary> /// </summary>
/// <param name="uid">The entity to consume.</param> public void ConsumeEntity(EntityUid hungry, EntityUid morsel, EventHorizonComponent eventHorizon, IContainer? outerContainer = null)
/// <param name="eventHorizon">The event horizon consuming the given entity.</param>
/// <param name="outerContainer">The innermost container of the entity to consume that isn't also being consumed by the event horizon.</param>
public void ConsumeEntity(EntityUid uid, EventHorizonComponent eventHorizon, IContainer? outerContainer = null)
{ {
var eventHorizonOwner = eventHorizon.Owner; if (!EntityManager.IsQueuedForDeletion(morsel) // I saw it log twice a few times for some reason?
&& (HasComp<MindContainerComponent>(morsel)
|| _tagSystem.HasTag(morsel, "HighRiskItem")
|| HasComp<ContainmentFieldGeneratorComponent>(morsel)))
{
_adminLogger.Add(LogType.EntityDelete, LogImpact.Extreme, $"{ToPrettyString(morsel)} entered the event horizon of {ToPrettyString(hungry)} and was deleted");
}
if (!EntityManager.IsQueuedForDeletion(uid) && // I saw it log twice a few times for some reason? EntityManager.QueueDeleteEntity(morsel);
(HasComp<MindContainerComponent>(uid) || var evSelf = new EntityConsumedByEventHorizonEvent(morsel, hungry, eventHorizon, outerContainer);
_tagSystem.HasTag(uid, "HighRiskItem") || var evEaten = new EventHorizonConsumedEntityEvent(morsel, hungry, eventHorizon, outerContainer);
HasComp<ContainmentFieldGeneratorComponent>(uid))) RaiseLocalEvent(hungry, ref evSelf);
_adminLogger.Add(LogType.EntityDelete, LogImpact.Extreme, $"{ToPrettyString(uid)} entered the event horizon of {ToPrettyString(eventHorizonOwner)} and was deleted"); RaiseLocalEvent(morsel, ref evEaten);
EntityManager.QueueDeleteEntity(uid);
RaiseLocalEvent(eventHorizonOwner, new EntityConsumedByEventHorizonEvent(uid, eventHorizon, outerContainer));
RaiseLocalEvent(uid, new EventHorizonConsumedEntityEvent(uid, eventHorizon, outerContainer));
} }
/// <summary> /// <summary>
/// Makes an event horizon attempt to consume a given entity. /// Makes an event horizon attempt to consume a given entity.
/// </summary> /// </summary>
/// <param name="uid">The entity to attempt to consume.</param> public bool AttemptConsumeEntity(EntityUid hungry, EntityUid morsel, EventHorizonComponent eventHorizon, IContainer? outerContainer = null)
/// <param name="eventHorizon">The event horizon attempting to consume the given entity.</param>
/// <param name="outerContainer">The innermost container of the entity to consume that isn't also being consumed by the event horizon.</param>
public bool AttemptConsumeEntity(EntityUid uid, EventHorizonComponent eventHorizon, IContainer? outerContainer = null)
{ {
if(!CanConsumeEntity(uid, eventHorizon)) if (!CanConsumeEntity(hungry, morsel, eventHorizon))
return false; return false;
ConsumeEntity(uid, eventHorizon, outerContainer); ConsumeEntity(hungry, morsel, eventHorizon, outerContainer);
return true; return true;
} }
/// <summary> /// <summary>
/// Checks whether an event horizon can consume a given entity. /// Checks whether an event horizon can consume a given entity.
/// </summary> /// </summary>
/// <param name="uid">The entity to check for consumability.</param> public bool CanConsumeEntity(EntityUid hungry, EntityUid uid, EventHorizonComponent eventHorizon)
/// <param name="eventHorizon">The event horizon checking whether it can consume the entity.</param>
public bool CanConsumeEntity(EntityUid uid, EventHorizonComponent eventHorizon)
{ {
var ev = new EventHorizonAttemptConsumeEntityEvent(uid, eventHorizon); var ev = new EventHorizonAttemptConsumeEntityEvent(uid, hungry, eventHorizon);
RaiseLocalEvent(uid, ev); RaiseLocalEvent(uid, ref ev);
return !ev.Cancelled; return !ev.Cancelled;
} }
@@ -174,21 +152,27 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Attempts to consume all entities within a given distance of an entity; /// Attempts to consume all entities within a given distance of an entity;
/// Excludes the center entity. /// Excludes the center entity.
/// </summary> /// </summary>
/// <param name="uid">The entity uid in the center of the region to consume all entities within.</param>
/// <param name="range">The distance of the center entity within which to consume all entities.</param>
/// <param name="xform">The transform component attached to the center entity.</param>
/// <param name="eventHorizon">The event horizon component attached to the center entity.</param>
public void ConsumeEntitiesInRange(EntityUid uid, float range, TransformComponent? xform = null, EventHorizonComponent? eventHorizon = null) public void ConsumeEntitiesInRange(EntityUid uid, float range, TransformComponent? xform = null, EventHorizonComponent? eventHorizon = null)
{ {
if (!Resolve(uid, ref xform, ref eventHorizon)) if (!Resolve(uid, ref xform, ref eventHorizon))
return; return;
var range2 = range * range;
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
var epicenter = _xformSystem.GetWorldPosition(xform, xformQuery);
foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, range, flags: LookupFlags.Uncontained)) foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, range, flags: LookupFlags.Uncontained))
{ {
if (entity == uid) if (entity == uid)
continue; continue;
if (!xformQuery.TryGetComponent(entity, out var entityXform))
continue;
AttemptConsumeEntity(entity, eventHorizon); // GetEntitiesInRange gets everything in a _square_ centered on the given position, but we are a _circle_. If we don't have this check and the station is rotated it is possible for the singularity to reach _outside of the containment field_ and eat the emitters.
var displacement = _xformSystem.GetWorldPosition(entityXform, xformQuery) - epicenter;
if (displacement.LengthSquared() > range2)
continue;
AttemptConsumeEntity(uid, entity, eventHorizon);
} }
} }
@@ -197,22 +181,19 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Excludes the event horizon itself. /// Excludes the event horizon itself.
/// All immune entities within the container will be dumped to a given container or the map/grid if that is impossible. /// All immune entities within the container will be dumped to a given container or the map/grid if that is impossible.
/// </summary> /// </summary>
/// <param name="uid">The uid of the event horizon. The single entity that is immune-by-default.</param> public void ConsumeEntitiesInContainer(EntityUid hungry, IContainer container, EventHorizonComponent eventHorizon, IContainer? outerContainer = null)
/// <param name="container">The container within which to consume all entities.</param> {
/// <param name="eventHorizon">The state of the event horizon.</param>
/// <param name="outerContainer">The location any immune entities within the container should be dumped to.</param>
public void ConsumeEntitiesInContainer(EntityUid uid, IContainer container, EventHorizonComponent eventHorizon, IContainer? outerContainer = null) {
// Removing the immune entities from the container needs to be deferred until after iteration or the iterator raises an error. // Removing the immune entities from the container needs to be deferred until after iteration or the iterator raises an error.
List<EntityUid> immune = new(); List<EntityUid> immune = new();
foreach (var entity in container.ContainedEntities) foreach (var entity in container.ContainedEntities)
{ {
if (entity == uid || !AttemptConsumeEntity(entity, eventHorizon, outerContainer)) if (entity == hungry || !AttemptConsumeEntity(hungry, entity, eventHorizon, outerContainer))
immune.Add(entity); // The first check keeps singularities an admin smited into a locker from consuming themselves. immune.Add(entity); // The first check keeps singularities an admin smited into a locker from consuming themselves.
// The second check keeps things that have been rendered immune to singularities from being deleted by a singularity eating their container. // The second check keeps things that have been rendered immune to singularities from being deleted by a singularity eating their container.
} }
if (outerContainer == container) if (outerContainer == container || immune.Count <= 0)
return; // The container we are intended to drop immune things to is the same container we are consuming everything in return; // The container we are intended to drop immune things to is the same container we are consuming everything in
// it's a safe bet that we aren't consuming the container entity so there's no reason to eject anything from this container. // it's a safe bet that we aren't consuming the container entity so there's no reason to eject anything from this container.
@@ -231,7 +212,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
// If we couldn't or there was no container to insert into just dump them to the map/grid. // If we couldn't or there was no container to insert into just dump them to the map/grid.
if (target_container == null) if (target_container == null)
Transform(entity).AttachToGridOrMap(); _xformSystem.AttachToGridOrMap(entity);
} }
} }
@@ -242,51 +223,47 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// <summary> /// <summary>
/// Makes an event horizon consume a specific tile on a grid. /// Makes an event horizon consume a specific tile on a grid.
/// </summary> /// </summary>
/// <param name="tile">The tile to consume.</param> public void ConsumeTile(EntityUid hungry, TileRef tile, EventHorizonComponent eventHorizon)
/// <param name="eventHorizon">The event horizon which is consuming the tile on the grid.</param> {
public void ConsumeTile(TileRef tile, EventHorizonComponent eventHorizon) ConsumeTiles(hungry, new List<(Vector2i, Tile)>(new[] { (tile.GridIndices, Tile.Empty) }), tile.GridUid, _mapMan.GetGrid(tile.GridUid), eventHorizon);
=> ConsumeTiles(new List<(Vector2i, Tile)>(new []{(tile.GridIndices, Tile.Empty)}), _mapMan.GetGrid(tile.GridUid), eventHorizon); }
/// <summary> /// <summary>
/// Makes an event horizon attempt to consume a specific tile on a grid. /// Makes an event horizon attempt to consume a specific tile on a grid.
/// </summary> /// </summary>
/// <param name="tile">The tile to attempt to consume.</param> public void AttemptConsumeTile(EntityUid hungry, TileRef tile, EventHorizonComponent eventHorizon)
/// <param name="eventHorizon">The event horizon which is attempting to consume the tile on the grid.</param> {
public void AttemptConsumeTile(TileRef tile, EventHorizonComponent eventHorizon) AttemptConsumeTiles(hungry, new TileRef[1] { tile }, tile.GridUid, _mapMan.GetGrid(tile.GridUid), eventHorizon);
=> AttemptConsumeTiles(new TileRef[1]{tile}, _mapMan.GetGrid(tile.GridUid), eventHorizon); }
/// <summary> /// <summary>
/// Makes an event horizon consume a set of tiles on a grid. /// Makes an event horizon consume a set of tiles on a grid.
/// </summary> /// </summary>
/// <param name="tiles">The tiles to consume.</param> public void ConsumeTiles(EntityUid hungry, List<(Vector2i, Tile)> tiles, EntityUid gridId, MapGridComponent grid, EventHorizonComponent eventHorizon)
/// <param name="grid">The grid hosting the tiles to consume.</param>
/// <param name="eventHorizon">The event horizon which is consuming the tiles on the grid.</param>
public void ConsumeTiles(List<(Vector2i, Tile)> tiles, MapGridComponent grid, EventHorizonComponent eventHorizon)
{ {
if (tiles.Count <= 0) if (tiles.Count <= 0)
return; return;
RaiseLocalEvent(eventHorizon.Owner, new TilesConsumedByEventHorizonEvent(tiles, grid, eventHorizon)); var ev = new TilesConsumedByEventHorizonEvent(tiles, gridId, grid, hungry, eventHorizon);
RaiseLocalEvent(hungry, ref ev);
grid.SetTiles(tiles); grid.SetTiles(tiles);
} }
/// <summary> /// <summary>
/// Makes an event horizon attempt to consume a set of tiles on a grid. /// Makes an event horizon attempt to consume a set of tiles on a grid.
/// </summary> /// </summary>
/// <param name="tiles">The tiles to attempt to consume.</param> public int AttemptConsumeTiles(EntityUid hungry, IEnumerable<TileRef> tiles, EntityUid gridId, MapGridComponent grid, EventHorizonComponent eventHorizon)
/// <param name="grid">The grid hosting the tiles to attempt to consume.</param>
/// <param name="eventHorizon">The event horizon which is attempting to consume the tiles on the grid.</param>
public int AttemptConsumeTiles(IEnumerable<TileRef> tiles, MapGridComponent grid, EventHorizonComponent eventHorizon)
{ {
var toConsume = new List<(Vector2i, Tile)>(); var toConsume = new List<(Vector2i, Tile)>();
foreach(var tile in tiles) { foreach (var tile in tiles)
if (CanConsumeTile(tile, grid, eventHorizon)) {
if (CanConsumeTile(hungry, tile, grid, eventHorizon))
toConsume.Add((tile.GridIndices, Tile.Empty)); toConsume.Add((tile.GridIndices, Tile.Empty));
} }
var result = toConsume.Count; var result = toConsume.Count;
if (toConsume.Count > 0) if (toConsume.Count > 0)
ConsumeTiles(toConsume, grid, eventHorizon); ConsumeTiles(hungry, toConsume, gridId, grid, eventHorizon);
return result; return result;
} }
@@ -294,14 +271,11 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Checks whether an event horizon can consume a given tile. /// Checks whether an event horizon can consume a given tile.
/// This is only possible if it can also consume all entities anchored to the tile. /// This is only possible if it can also consume all entities anchored to the tile.
/// </summary> /// </summary>
/// <param name="tile">The tile to check for consumability.</param> public bool CanConsumeTile(EntityUid hungry, TileRef tile, MapGridComponent grid, EventHorizonComponent eventHorizon)
/// <param name="grid">The grid hosting the tile to check.</param>
/// <param name="eventHorizon">The event horizon which is checking to see if it can consume the tile on the grid.</param>
public bool CanConsumeTile(TileRef tile, MapGridComponent grid, EventHorizonComponent eventHorizon)
{ {
foreach (var blockingEntity in grid.GetAnchoredEntities(tile.GridIndices)) foreach (var blockingEntity in grid.GetAnchoredEntities(tile.GridIndices))
{ {
if(!CanConsumeEntity(blockingEntity, eventHorizon)) if (!CanConsumeEntity(hungry, blockingEntity, eventHorizon))
return false; return false;
} }
return true; return true;
@@ -311,10 +285,6 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Consumes all tiles within a given distance of an entity. /// Consumes all tiles within a given distance of an entity.
/// Some entities are immune to consumption. /// Some entities are immune to consumption.
/// </summary> /// </summary>
/// <param name="uid">The entity uid in the center of the region to consume all tiles within.</param>
/// <param name="range">The distance of the center entity within which to consume all tiles.</param>
/// <param name="xform">The transform component attached to the center entity.</param>
/// <param name="eventHorizon">The event horizon component attached to the center entity.</param>
public void ConsumeTilesInRange(EntityUid uid, float range, TransformComponent? xform, EventHorizonComponent? eventHorizon) public void ConsumeTilesInRange(EntityUid uid, float range, TransformComponent? xform, EventHorizonComponent? eventHorizon)
{ {
if (!Resolve(uid, ref xform) || !Resolve(uid, ref eventHorizon)) if (!Resolve(uid, ref xform) || !Resolve(uid, ref eventHorizon))
@@ -324,8 +294,8 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
var box = Box2.CenteredAround(mapPos.Position, new Vector2(range, range)); var box = Box2.CenteredAround(mapPos.Position, new Vector2(range, range));
var circle = new Circle(mapPos.Position, range); var circle = new Circle(mapPos.Position, range);
foreach (var grid in _mapMan.FindGridsIntersecting(mapPos.MapId, box)) foreach (var grid in _mapMan.FindGridsIntersecting(mapPos.MapId, box))
{ { // TODO: Remover grid.Owner when this iterator returns entityuids as well.
AttemptConsumeTiles(grid.GetTilesIntersecting(circle), grid, eventHorizon); AttemptConsumeTiles(uid, grid.GetTilesIntersecting(circle), grid.Owner, grid, eventHorizon);
} }
} }
@@ -335,10 +305,6 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Consumes most entities and tiles within a given distance of an entity. /// Consumes most entities and tiles within a given distance of an entity.
/// Some entities are immune to consumption. /// Some entities are immune to consumption.
/// </summary> /// </summary>
/// <param name="uid">The entity uid in the center of the region to consume everything within.</param>
/// <param name="range">The distance of the center entity within which to consume everything.</param>
/// <param name="xform">The transform component attached to the center entity.</param>
/// <param name="eventHorizon">The event horizon component attached to the center entity.</param>
public void ConsumeEverythingInRange(EntityUid uid, float range, TransformComponent? xform = null, EventHorizonComponent? eventHorizon = null) public void ConsumeEverythingInRange(EntityUid uid, float range, TransformComponent? xform = null, EventHorizonComponent? eventHorizon = null)
{ {
if (!Resolve(uid, ref xform, ref eventHorizon)) if (!Resolve(uid, ref xform, ref eventHorizon))
@@ -357,9 +323,6 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// The value is specifically how long the subsystem should wait between scans. /// The value is specifically how long the subsystem should wait between scans.
/// If the new scanning period would have already prompted a scan given the previous scan time one is prompted immediately. /// If the new scanning period would have already prompted a scan given the previous scan time one is prompted immediately.
/// </summary> /// </summary>
/// <param name="uid">The uid of the event horizon to set the consume wave period for.</param>
/// <param name="value">The amount of time that this subsystem should wait between scans.</param>
/// <param name="eventHorizon">The state of the event horizon to set the consume wave period for.</param>
public void SetConsumePeriod(EntityUid uid, TimeSpan value, EventHorizonComponent? eventHorizon = null) public void SetConsumePeriod(EntityUid uid, TimeSpan value, EventHorizonComponent? eventHorizon = null)
{ {
if (!Resolve(uid, ref eventHorizon)) if (!Resolve(uid, ref eventHorizon))
@@ -383,60 +346,49 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// <summary> /// <summary>
/// Prevents a singularity from colliding with anything it is incapable of consuming. /// Prevents a singularity from colliding with anything it is incapable of consuming.
/// </summary> /// </summary>
/// <param name="uid">The event horizon entity that is trying to collide with something.</param>
/// <param name="comp">The event horizon that is trying to collide with something.</param>
/// <param name="args">The event arguments.</param>
protected override bool PreventCollide(EntityUid uid, EventHorizonComponent comp, ref PreventCollideEvent args) protected override bool PreventCollide(EntityUid uid, EventHorizonComponent comp, ref PreventCollideEvent args)
{ {
if (base.PreventCollide(uid, comp, ref args) || args.Cancelled) if (base.PreventCollide(uid, comp, ref args) || args.Cancelled)
return true; return true;
args.Cancelled = !CanConsumeEntity(args.OtherEntity, comp); // If we can eat it we don't want to bounce off of it. If we can't eat it we want to bounce off of it (containment fields).
args.Cancelled = args.OurFixture.Hard && CanConsumeEntity(uid, args.OtherEntity, comp);
return false; return false;
} }
/// <summary> /// <summary>
/// A generic event handler that prevents singularities from consuming entities with a component of a given type if registered. /// A generic event handler that prevents singularities from consuming entities with a component of a given type if registered.
/// </summary> /// </summary>
/// <param name="uid">The entity the singularity is trying to eat.</param> public static void PreventConsume<TComp>(EntityUid uid, TComp comp, ref EventHorizonAttemptConsumeEntityEvent args)
/// <param name="comp">The component the singularity is trying to eat.</param>
/// <param name="args">The event arguments.</param>
public void PreventConsume<TComp>(EntityUid uid, TComp comp, EventHorizonAttemptConsumeEntityEvent args)
{ {
if (!args.Cancelled) if (!args.Cancelled)
args.Cancel(); args.Cancelled = true;
} }
/// <summary> /// <summary>
/// A generic event handler that prevents singularities from breaching containment. /// A generic event handler that prevents singularities from breaching containment.
/// In this case 'breaching containment' means consuming an entity with a component of the given type unless the event horizon is set to breach containment anyway. /// In this case 'breaching containment' means consuming an entity with a component of the given type unless the event horizon is set to breach containment anyway.
/// </summary> /// </summary>
/// <param name="uid">The entity the singularity is trying to eat.</param> public static void PreventBreach<TComp>(EntityUid uid, TComp comp, ref EventHorizonAttemptConsumeEntityEvent args)
/// <param name="comp">The component the singularity is trying to eat.</param>
/// <param name="args">The event arguments.</param>
public void PreventBreach<TComp>(EntityUid uid, TComp comp, EventHorizonAttemptConsumeEntityEvent args)
{ {
if (args.Cancelled) if (args.Cancelled)
return; return;
if (!args.EventHorizon.CanBreachContainment) if (!args.EventHorizon.CanBreachContainment)
PreventConsume(uid, comp, args); PreventConsume(uid, comp, ref args);
} }
/// <summary> /// <summary>
/// Handles event horizons consuming any entities they bump into. /// Handles event horizons consuming any entities they bump into.
/// The event horizon will not consume any entities if it itself has been consumed by an event horizon. /// The event horizon will not consume any entities if it itself has been consumed by an event horizon.
/// </summary> /// </summary>
/// <param name="uid">The event horizon entity.</param>
/// <param name="comp">The event horizon.</param>
/// <param name="args">The event arguments.</param>
private void OnStartCollide(EntityUid uid, EventHorizonComponent comp, ref StartCollideEvent args) private void OnStartCollide(EntityUid uid, EventHorizonComponent comp, ref StartCollideEvent args)
{ {
if (comp.BeingConsumedByAnotherEventHorizon) if (comp.BeingConsumedByAnotherEventHorizon)
return; return;
if (args.OurFixture.ID != comp.HorizonFixtureId) if (args.OurFixture.ID != comp.ConsumerFixtureId)
return; return;
AttemptConsumeEntity(args.OtherEntity, comp); AttemptConsumeEntity(uid, args.OtherEntity, comp);
} }
/// <summary> /// <summary>
@@ -444,40 +396,31 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Specifically prevents event horizons from consuming themselves. /// Specifically prevents event horizons from consuming themselves.
/// Also ensures that if this event horizon has already been consumed by another event horizon it cannot be consumed again. /// Also ensures that if this event horizon has already been consumed by another event horizon it cannot be consumed again.
/// </summary> /// </summary>
/// <param name="uid">The event horizon entity.</param> private void OnAnotherEventHorizonAttemptConsumeThisEventHorizon(EntityUid uid, EventHorizonComponent comp, ref EventHorizonAttemptConsumeEntityEvent args)
/// <param name="comp">The event horizon.</param>
/// <param name="args">The event arguments.</param>
private void OnAnotherEventHorizonAttemptConsumeThisEventHorizon(EntityUid uid, EventHorizonComponent comp, EventHorizonAttemptConsumeEntityEvent args)
{ {
if (!args.Cancelled && (args.EventHorizon == comp || comp.BeingConsumedByAnotherEventHorizon)) if (!args.Cancelled && (args.EventHorizon == comp || comp.BeingConsumedByAnotherEventHorizon))
args.Cancel(); args.Cancelled = true;
} }
/// <summary> /// <summary>
/// Prevents two singularities from annihilating one another. /// Prevents two singularities from annihilating one another.
/// Specifically ensures if this event horizon is consumed by another event horizon it knows that it has been consumed. /// Specifically ensures if this event horizon is consumed by another event horizon it knows that it has been consumed.
/// </summary> /// </summary>
/// <param name="uid">The event horizon entity.</param> private void OnAnotherEventHorizonConsumedThisEventHorizon(EntityUid uid, EventHorizonComponent comp, ref EventHorizonConsumedEntityEvent args)
/// <param name="comp">The event horizon.</param>
/// <param name="args">The event arguments.</param>
private void OnAnotherEventHorizonConsumedThisEventHorizon(EntityUid uid, EventHorizonComponent comp, EventHorizonConsumedEntityEvent args)
{ {
comp.BeingConsumedByAnotherEventHorizon = true; comp.BeingConsumedByAnotherEventHorizon = true;
} }
/// <summary> /// <summary>
/// Handles event horizons deciding to escape containers they are inserted into. /// Handles event horizons deciding to escape containers they are inserted into.
/// Delegates the actual escape to <see cref="EventHorizonSystem.OnEventHorizonContained(EventHorizonContainedEvent)" /> on a delay. /// Delegates the actual escape to <see cref="OnEventHorizonContained(EventHorizonContainedEvent)" /> on a delay.
/// This ensures that the escape is handled after all other handlers for the insertion event and satisfies the assertion that /// This ensures that the escape is handled after all other handlers for the insertion event and satisfies the assertion that
/// the inserted entity SHALL be inside of the specified container after all handles to the entity event /// the inserted entity SHALL be inside of the specified container after all handles to the entity event
/// <see cref="EntGotInsertedIntoContainerMessage" /> are processed. /// <see cref="EntGotInsertedIntoContainerMessage" /> are processed.
/// </summary> /// </summary>
/// <param name="uid">The uid of the event horizon.</param>] private void OnEventHorizonContained(EntityUid uid, EventHorizonComponent comp, EntGotInsertedIntoContainerMessage args)
/// <param name="comp">The state of the event horizon.</param>] {
/// <param name="args">The arguments of the insertion.</param>]
private void OnEventHorizonContained(EntityUid uid, EventHorizonComponent comp, EntGotInsertedIntoContainerMessage args) {
// Delegates processing an event until all queued events have been processed. // Delegates processing an event until all queued events have been processed.
// As of 1:44 AM, Sunday, Dec. 4, 2022 this is the one use for this in the codebase.
QueueLocalEvent(new EventHorizonContainedEvent(uid, comp, args)); QueueLocalEvent(new EventHorizonContainedEvent(uid, comp, args));
} }
@@ -487,29 +430,29 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// container and drop the the next innermost contaning container. /// container and drop the the next innermost contaning container.
/// This loops until the event horizon has escaped to the map or wound up in an indestructible container. /// This loops until the event horizon has escaped to the map or wound up in an indestructible container.
/// </summary> /// </summary>
/// <param name="args">The arguments for this event.</param>] private void OnEventHorizonContained(EventHorizonContainedEvent args)
private void OnEventHorizonContained(EventHorizonContainedEvent args) { {
var uid = args.Entity; var uid = args.Entity;
var comp = args.EventHorizon;
if (!EntityManager.EntityExists(uid)) if (!EntityManager.EntityExists(uid))
return; return;
var comp = args.EventHorizon;
if (comp.BeingConsumedByAnotherEventHorizon) if (comp.BeingConsumedByAnotherEventHorizon)
return; return;
var containerEntity = args.Args.Container.Owner; var containerEntity = args.Args.Container.Owner;
if(!(EntityManager.EntityExists(containerEntity) && AttemptConsumeEntity(containerEntity, comp))) { if (!EntityManager.EntityExists(containerEntity))
return;
if (AttemptConsumeEntity(uid, containerEntity, comp))
return; // If we consume the entity we also consume everything in the containers it has.
ConsumeEntitiesInContainer(uid, args.Args.Container, comp, args.Args.Container); ConsumeEntitiesInContainer(uid, args.Args.Container, comp, args.Args.Container);
} }
}
/// <summary> /// <summary>
/// Recursively consumes all entities within a container that is consumed by the singularity. /// Recursively consumes all entities within a container that is consumed by the singularity.
/// If an entity within a consumed container cannot be consumed itself it is removed from the container. /// If an entity within a consumed container cannot be consumed itself it is removed from the container.
/// </summary> /// </summary>
/// <param name="uid">The uid of the container being consumed.</param> private void OnContainerConsumed(EntityUid uid, ContainerManagerComponent comp, ref EventHorizonConsumedEntityEvent args)
/// <param name="comp">The state of the container being consumed.</param>
/// <param name="args">The event arguments.</param>
private void OnContainerConsumed(EntityUid uid, ContainerManagerComponent comp, EventHorizonConsumedEntityEvent args)
{ {
var drop_container = args.Container; var drop_container = args.Container;
if (drop_container is null) if (drop_container is null)
@@ -517,7 +460,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
foreach (var container in comp.GetAllContainers()) foreach (var container in comp.GetAllContainers())
{ {
ConsumeEntitiesInContainer(args.EventHorizon.Owner, container, args.EventHorizon, drop_container); ConsumeEntitiesInContainer(args.EventHorizonUid, container, args.EventHorizon, drop_container);
} }
} }
#endregion Event Handlers #endregion Event Handlers

View File

@@ -256,7 +256,7 @@ public sealed class SingularitySystem : SharedSingularitySystem
/// <param name="uid">The entity UID of the singularity that is consuming the entity.</param> /// <param name="uid">The entity UID of the singularity that is consuming the entity.</param>
/// <param name="comp">The component of the singularity that is consuming the entity.</param> /// <param name="comp">The component of the singularity that is consuming the entity.</param>
/// <param name="args">The event arguments.</param> /// <param name="args">The event arguments.</param>
public void OnConsumedEntity(EntityUid uid, SingularityComponent comp, EntityConsumedByEventHorizonEvent args) public void OnConsumedEntity(EntityUid uid, SingularityComponent comp, ref EntityConsumedByEventHorizonEvent args)
{ {
AdjustEnergy(uid, BaseEntityEnergy, singularity: comp); AdjustEnergy(uid, BaseEntityEnergy, singularity: comp);
} }
@@ -267,21 +267,21 @@ public sealed class SingularitySystem : SharedSingularitySystem
/// <param name="uid">The entity UID of the singularity that is consuming the tiles.</param> /// <param name="uid">The entity UID of the singularity that is consuming the tiles.</param>
/// <param name="comp">The component of the singularity that is consuming the tiles.</param> /// <param name="comp">The component of the singularity that is consuming the tiles.</param>
/// <param name="args">The event arguments.</param> /// <param name="args">The event arguments.</param>
public void OnConsumedTiles(EntityUid uid, SingularityComponent comp, TilesConsumedByEventHorizonEvent args) public void OnConsumedTiles(EntityUid uid, SingularityComponent comp, ref TilesConsumedByEventHorizonEvent args)
{ {
AdjustEnergy(uid, args.Tiles.Count * BaseTileEnergy, singularity: comp); AdjustEnergy(uid, args.Tiles.Count * BaseTileEnergy, singularity: comp);
} }
/// <summary> /// <summary>
/// Adds the energy of this singularity to singularities consume it. /// Adds the energy of this singularity to singularities that consume it.
/// </summary> /// </summary>
/// <param name="uid">The entity UID of the singularity that is being consumed.</param> /// <param name="uid">The entity UID of the singularity that is being consumed.</param>
/// <param name="comp">The component of the singularity that is being consumed.</param> /// <param name="comp">The component of the singularity that is being consumed.</param>
/// <param name="args">The event arguments.</param> /// <param name="args">The event arguments.</param>
private void OnConsumed(EntityUid uid, SingularityComponent comp, EventHorizonConsumedEntityEvent args) private void OnConsumed(EntityUid uid, SingularityComponent comp, ref EventHorizonConsumedEntityEvent args)
{ {
// Should be slightly more efficient than checking literally everything we consume for a singularity component and doing the reverse. // Should be slightly more efficient than checking literally everything we consume for a singularity component and doing the reverse.
if (EntityManager.TryGetComponent<SingularityComponent>(args.EventHorizon.Owner, out var singulo)) if (EntityManager.TryGetComponent<SingularityComponent>(args.EventHorizonUid, out var singulo))
{ {
AdjustEnergy(singulo.Owner, comp.Energy, singularity: singulo); AdjustEnergy(singulo.Owner, comp.Energy, singularity: singulo);
SetEnergy(uid, 0.0f, comp); SetEnergy(uid, 0.0f, comp);
@@ -294,10 +294,10 @@ public sealed class SingularitySystem : SharedSingularitySystem
/// <param name="uid">The entity UID of the singularity food that is being consumed.</param> /// <param name="uid">The entity UID of the singularity food that is being consumed.</param>
/// <param name="comp">The component of the singularity food that is being consumed.</param> /// <param name="comp">The component of the singularity food that is being consumed.</param>
/// <param name="args">The event arguments.</param> /// <param name="args">The event arguments.</param>
public void OnConsumed(EntityUid uid, SinguloFoodComponent comp, EventHorizonConsumedEntityEvent args) public void OnConsumed(EntityUid uid, SinguloFoodComponent comp, ref EventHorizonConsumedEntityEvent args)
{ {
if (EntityManager.TryGetComponent<SingularityComponent>(args.EventHorizon.Owner, out var singulo)) if (EntityManager.TryGetComponent<SingularityComponent>(args.EventHorizonUid, out var singulo))
AdjustEnergy(args.EventHorizon.Owner, comp.Energy, singularity: singulo); AdjustEnergy(args.EventHorizonUid, comp.Energy, singularity: singulo);
} }
/// <summary> /// <summary>

View File

@@ -6,28 +6,28 @@ namespace Content.Server.Singularity.Events;
/// <summary> /// <summary>
/// Event raised on the event horizon entity whenever an event horizon consumes an entity. /// Event raised on the event horizon entity whenever an event horizon consumes an entity.
/// </summary> /// </summary>
public sealed class EntityConsumedByEventHorizonEvent : EntityEventArgs [ByRefEvent]
public readonly record struct EntityConsumedByEventHorizonEvent
(EntityUid entity, EntityUid eventHorizonUid, EventHorizonComponent eventHorizon, IContainer? container)
{ {
/// <summary> /// <summary>
/// The entity being consumed by the event horizon. /// The entity being consumed by the event horizon.
/// </summary> /// </summary>
public readonly EntityUid Entity; public readonly EntityUid Entity = entity;
/// <summary>
/// The uid of the event horizon consuming the entity.
/// </summary>
public readonly EntityUid EventHorizonUid = eventHorizonUid;
/// <summary> /// <summary>
/// The event horizon consuming the entity. /// The event horizon consuming the entity.
/// </summary> /// </summary>
public readonly EventHorizonComponent EventHorizon; public readonly EventHorizonComponent EventHorizon = eventHorizon;
/// <summary> /// <summary>
/// The innermost container of the entity being consumed by the event horizon that is not also in the process of being consumed by the event horizon. /// The innermost container of the entity being consumed by the event horizon that is not also in the process of being consumed by the event horizon.
/// Used to correctly dump out the contents containers that are consumed by the event horizon. /// Used to correctly dump out the contents containers that are consumed by the event horizon.
/// </summary> /// </summary>
public readonly IContainer? Container; public readonly IContainer? Container = container;
public EntityConsumedByEventHorizonEvent(EntityUid entity, EventHorizonComponent eventHorizon, IContainer? container = null)
{
Entity = entity;
EventHorizon = eventHorizon;
Container = container;
}
} }

View File

@@ -6,21 +6,27 @@ namespace Content.Server.Singularity.Events;
/// Event raised on the target entity whenever an event horizon attempts to consume an entity. /// Event raised on the target entity whenever an event horizon attempts to consume an entity.
/// Can be cancelled to prevent the target entity from being consumed. /// Can be cancelled to prevent the target entity from being consumed.
/// </summary> /// </summary>
public sealed class EventHorizonAttemptConsumeEntityEvent : CancellableEntityEventArgs [ByRefEvent]
public record struct EventHorizonAttemptConsumeEntityEvent
(EntityUid entity, EntityUid eventHorizonUid, EventHorizonComponent eventHorizon)
{ {
/// <summary> /// <summary>
/// The entity that the event horizon is attempting to consume. /// The entity that the event horizon is attempting to consume.
/// </summary> /// </summary>
public readonly EntityUid Entity; public readonly EntityUid Entity = entity;
/// <summary>
/// The uid of the event horizon consuming the entity.
/// </summary>
public readonly EntityUid EventHorizonUid = eventHorizonUid;
/// <summary> /// <summary>
/// The event horizon consuming the target entity. /// The event horizon consuming the target entity.
/// </summary> /// </summary>
public readonly EventHorizonComponent EventHorizon; public readonly EventHorizonComponent EventHorizon = eventHorizon;
public EventHorizonAttemptConsumeEntityEvent(EntityUid entity, EventHorizonComponent eventHorizon) /// <summary>
{ /// Whether the event horizon has been prevented from consuming the target entity.
Entity = entity; /// </summary>
EventHorizon = eventHorizon; public bool Cancelled = false;
}
} }

View File

@@ -4,30 +4,30 @@ using Robust.Shared.Containers;
namespace Content.Server.Singularity.Events; namespace Content.Server.Singularity.Events;
/// <summary> /// <summary>
/// Event raised on the event horizon entity whenever an event horizon consumes an entity. /// Event raised on the entity being consumed whenever an event horizon consumes an entity.
/// </summary> /// </summary>
public sealed class EventHorizonConsumedEntityEvent : EntityEventArgs [ByRefEvent]
public readonly record struct EventHorizonConsumedEntityEvent
(EntityUid entity, EntityUid eventHorizonUid, EventHorizonComponent eventHorizon, IContainer? container)
{ {
/// <summary> /// <summary>
/// The entity being consumed by the event horizon. /// The entity being consumed by the event horizon.
/// </summary> /// </summary>
public readonly EntityUid Entity; public readonly EntityUid Entity = entity;
/// <summary>
/// The uid of the event horizon consuming the entity.
/// </summary>
public readonly EntityUid EventHorizonUid = eventHorizonUid;
/// <summary> /// <summary>
/// The event horizon consuming the target entity. /// The event horizon consuming the target entity.
/// </summary> /// </summary>
public readonly EventHorizonComponent EventHorizon; public readonly EventHorizonComponent EventHorizon = eventHorizon;
/// <summary> /// <summary>
/// The innermost container of the entity being consumed by the event horizon that is not also in the process of being consumed by the event horizon. /// The innermost container of the entity being consumed by the event horizon that is not also in the process of being consumed by the event horizon.
/// Used to correctly dump out the contents containers that are consumed by the event horizon. /// Used to correctly dump out the contents containers that are consumed by the event horizon.
/// </summary> /// </summary>
public readonly IContainer? Container; public readonly IContainer? Container = container;
public EventHorizonConsumedEntityEvent(EntityUid entity, EventHorizonComponent eventHorizon, IContainer? container = null)
{
Entity = entity;
EventHorizon = eventHorizon;
Container = container;
}
} }

View File

@@ -6,8 +6,10 @@ namespace Content.Shared.Singularity.EntitySystems;
/// <summary> /// <summary>
/// An event queued when an event horizon is contained (put into a container). /// An event queued when an event horizon is contained (put into a container).
/// Exists to delay the event horizon eating its way out of the container until events relating to the insertion have been processed. /// Exists to delay the event horizon eating its way out of the container until events relating to the insertion have been processed.
/// Needs to be a class because ref structs can't be put into the queue.
/// </summary> /// </summary>
public sealed class EventHorizonContainedEvent : EntityEventArgs { public sealed class EventHorizonContainedEvent : EntityEventArgs
{
/// <summary> /// <summary>
/// The uid of the event horizon that has been contained. /// The uid of the event horizon that has been contained.
/// </summary> /// </summary>
@@ -23,7 +25,8 @@ public sealed class EventHorizonContainedEvent : EntityEventArgs {
/// </summary> /// </summary>
public readonly EntGotInsertedIntoContainerMessage Args; public readonly EntGotInsertedIntoContainerMessage Args;
public EventHorizonContainedEvent(EntityUid entity, EventHorizonComponent eventHorizon, EntGotInsertedIntoContainerMessage args) { public EventHorizonContainedEvent(EntityUid entity, EventHorizonComponent eventHorizon, EntGotInsertedIntoContainerMessage args)
{
Entity = entity; Entity = entity;
EventHorizon = eventHorizon; EventHorizon = eventHorizon;
Args = args; Args = args;

View File

@@ -8,28 +8,33 @@ namespace Content.Server.Singularity.Events;
/// <summary> /// <summary>
/// Event raised on the event horizon entity whenever an event horizon consumes an entity. /// Event raised on the event horizon entity whenever an event horizon consumes an entity.
/// </summary> /// </summary>
public sealed class TilesConsumedByEventHorizonEvent : EntityEventArgs [ByRefEvent]
public readonly record struct TilesConsumedByEventHorizonEvent
(IReadOnlyList<(Vector2i, Tile)> tiles, EntityUid mapGridUid, MapGridComponent mapGrid, EntityUid eventHorizonUid, EventHorizonComponent eventHorizon)
{ {
/// <summary> /// <summary>
/// The tiles that the event horizon is consuming. /// The tiles that the event horizon is consuming.
/// Ripped directly from the relevant proc so the second element of each element will be what the tiles are going to be after the grid is updated; usually <see cref="Tile.Empty"/>. /// Ripped directly from the relevant proc so the second element of each element will be what the tiles are going to be after the grid is updated; usually <see cref="Tile.Empty"/>.
/// </summary> /// </summary>
public readonly IReadOnlyList<(Vector2i, Tile)> Tiles; public readonly IReadOnlyList<(Vector2i, Tile)> Tiles = tiles;
/// <summary>
/// The uid of the map grid the event horizon is consuming part of.
/// </summary>
public readonly EntityUid MapGridUid = mapGridUid;
/// <summary> /// <summary>
/// The mapgrid that the event horizon is consuming tiles of. /// The mapgrid that the event horizon is consuming tiles of.
/// </summary> /// </summary>
public readonly MapGridComponent MapGrid; public readonly MapGridComponent MapGrid = mapGrid;
/// <summary>
/// The uid of the event horizon consuming the entity.
/// </summary>
public readonly EntityUid EventHorizonUid = eventHorizonUid;
/// <summary> /// <summary>
/// The event horizon consuming the tiles. /// The event horizon consuming the tiles.
/// </summary> /// </summary>
public readonly EventHorizonComponent EventHorizon; public readonly EventHorizonComponent EventHorizon = eventHorizon;
public TilesConsumedByEventHorizonEvent(IReadOnlyList<(Vector2i, Tile)> tiles, MapGridComponent mapGrid, EventHorizonComponent eventHorizon)
{
Tiles = tiles;
MapGrid = mapGrid;
EventHorizon = eventHorizon;
}
} }

View File

@@ -8,6 +8,7 @@ namespace Content.Shared.Singularity.Components;
/// Also makes the associated entity destroy other entities upon contact. /// Also makes the associated entity destroy other entities upon contact.
/// Primarily managed by <see cref="SharedEventHorizonSystem"/> and its server/client versions. /// Primarily managed by <see cref="SharedEventHorizonSystem"/> and its server/client versions.
/// </summary> /// </summary>
[Access(friends: typeof(SharedEventHorizonSystem))]
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed class EventHorizonComponent : Component public sealed class EventHorizonComponent : Component
{ {
@@ -17,7 +18,6 @@ public sealed class EventHorizonComponent : Component
/// If you want to set this go through <see cref="SharedEventHorizonSystem.SetRadius"/>. /// If you want to set this go through <see cref="SharedEventHorizonSystem.SetRadius"/>.
/// </summary> /// </summary>
[DataField("radius")] [DataField("radius")]
[Access(friends:typeof(SharedEventHorizonSystem))]
public float Radius; public float Radius;
/// <summary> /// <summary>
@@ -25,7 +25,7 @@ public sealed class EventHorizonComponent : Component
/// If you want to set this go through <see cref="SharedEventHorizonSystem.SetCanBreachContainment"/>. /// If you want to set this go through <see cref="SharedEventHorizonSystem.SetCanBreachContainment"/>.
/// </summary> /// </summary>
[DataField("canBreachContainment")] [DataField("canBreachContainment")]
[Access(friends:typeof(SharedEventHorizonSystem))] [ViewVariables(VVAccess.ReadWrite)]
public bool CanBreachContainment = false; public bool CanBreachContainment = false;
/// <summary> /// <summary>
@@ -33,9 +33,18 @@ public sealed class EventHorizonComponent : Component
/// Can be set to null, in which case no such fixture is used. /// Can be set to null, in which case no such fixture is used.
/// If you want to set this go through <see cref="SharedEventHorizonSystem.SetHorizonFixtureId"/>. /// If you want to set this go through <see cref="SharedEventHorizonSystem.SetHorizonFixtureId"/>.
/// </summary> /// </summary>
[DataField("horizonFixtureId")] [DataField("consumerFixtureId")]
[Access(friends:typeof(SharedEventHorizonSystem))] [ViewVariables(VVAccess.ReadWrite)]
public string? HorizonFixtureId = "EventHorizon"; public string? ConsumerFixtureId = "EventHorizonConsumer";
/// <summary>
/// The ID of the fixture used to detect if the event horizon has collided with any physics objects.
/// Can be set to null, in which case no such fixture is used.
/// If you want to set this go through <see cref="SharedEventHorizonSystem.SetHorizonFixtureId"/>.
/// </summary>
[DataField("colliderFixtureId")]
[ViewVariables(VVAccess.ReadWrite)]
public string? ColliderFixtureId = "EventHorizonCollider";
/// <summary> /// <summary>
/// Whether the entity this event horizon is attached to is being consumed by another event horizon. /// Whether the entity this event horizon is attached to is being consumed by another event horizon.
@@ -49,22 +58,19 @@ public sealed class EventHorizonComponent : Component
/// The amount of time that should elapse between this event horizon consuming everything it overlaps with. /// The amount of time that should elapse between this event horizon consuming everything it overlaps with.
/// </summary> /// </summary>
[DataField("consumePeriod")] [DataField("consumePeriod")]
[ViewVariables(VVAccess.ReadOnly)] [ViewVariables(VVAccess.ReadWrite)]
[Access(typeof(SharedEventHorizonSystem))]
public TimeSpan TargetConsumePeriod { get; set; } = TimeSpan.FromSeconds(0.5); public TimeSpan TargetConsumePeriod { get; set; } = TimeSpan.FromSeconds(0.5);
/// <summary> /// <summary>
/// The last time at which this consumed everything it overlapped with. /// The last time at which this consumed everything it overlapped with.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadOnly)] [ViewVariables(VVAccess.ReadOnly)]
[Access(typeof(SharedEventHorizonSystem))]
public TimeSpan LastConsumeWaveTime { get; set; } = default!; public TimeSpan LastConsumeWaveTime { get; set; } = default!;
/// <summary> /// <summary>
/// The next time at which this consumed everything it overlapped with. /// The next time at which this consumed everything it overlapped with.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadOnly)] [ViewVariables(VVAccess.ReadOnly)]
[Access(typeof(SharedEventHorizonSystem))]
public TimeSpan NextConsumeWaveTime { get; set; } = default!; public TimeSpan NextConsumeWaveTime { get; set; } = default!;
#endregion Update Timing #endregion Update Timing

View File

@@ -18,8 +18,8 @@ public sealed class SingularityComponent : Component
/// Used as a scaling factor for things like visual size, event horizon radius, gravity well radius, radiation output, etc. /// Used as a scaling factor for things like visual size, event horizon radius, gravity well radius, radiation output, etc.
/// If you want to set this use <see cref="SharedSingularitySystem.SetLevel"/>(). /// If you want to set this use <see cref="SharedSingularitySystem.SetLevel"/>().
/// </summary> /// </summary>
[DataField("level")]
[Access(friends: typeof(SharedSingularitySystem), Other = AccessPermissions.Read, Self = AccessPermissions.Read)] [Access(friends: typeof(SharedSingularitySystem), Other = AccessPermissions.Read, Self = AccessPermissions.Read)]
[DataField("level")]
public byte Level = 1; public byte Level = 1;
/// <summary> /// <summary>
@@ -27,8 +27,8 @@ public sealed class SingularityComponent : Component
/// Has to be on shared in case someone attaches a RadiationPulseComponent to the singularity. /// Has to be on shared in case someone attaches a RadiationPulseComponent to the singularity.
/// If you want to set this use <see cref="SharedSingularitySystem.SetRadsPerLevel"/>(). /// If you want to set this use <see cref="SharedSingularitySystem.SetRadsPerLevel"/>().
/// </summary> /// </summary>
[DataField("radsPerLevel")]
[Access(friends: typeof(SharedSingularitySystem), Other = AccessPermissions.Read, Self = AccessPermissions.Read)] [Access(friends: typeof(SharedSingularitySystem), Other = AccessPermissions.Read, Self = AccessPermissions.Read)]
[DataField("radsPerLevel")]
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public float RadsPerLevel = 2f; public float RadsPerLevel = 2f;
@@ -88,7 +88,7 @@ public sealed class SingularityComponent : Component
/// The amount of time that should elapse between automated updates to this singularity. /// The amount of time that should elapse between automated updates to this singularity.
/// </summary> /// </summary>
[DataField("updatePeriod")] [DataField("updatePeriod")]
[ViewVariables(VVAccess.ReadOnly)] [ViewVariables(VVAccess.ReadWrite)]
public TimeSpan TargetUpdatePeriod = TimeSpan.FromSeconds(1.0); public TimeSpan TargetUpdatePeriod = TimeSpan.FromSeconds(1.0);
/// <summary> /// <summary>

View File

@@ -31,7 +31,8 @@ public abstract class SharedEventHorizonSystem : EntitySystem
var vvHandle = Vvm.GetTypeHandler<EventHorizonComponent>(); var vvHandle = Vvm.GetTypeHandler<EventHorizonComponent>();
vvHandle.AddPath(nameof(EventHorizonComponent.Radius), (_, comp) => comp.Radius, (uid, value, comp) => SetRadius(uid, value, eventHorizon: comp)); 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.CanBreachContainment), (_, comp) => comp.CanBreachContainment, (uid, value, comp) => SetCanBreachContainment(uid, value, eventHorizon: comp));
vvHandle.AddPath(nameof(EventHorizonComponent.HorizonFixtureId), (_, comp) => comp.HorizonFixtureId, (uid, value, comp) => SetHorizonFixtureId(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() public override void Shutdown()
@@ -39,7 +40,8 @@ public abstract class SharedEventHorizonSystem : EntitySystem
var vvHandle = Vvm.GetTypeHandler<EventHorizonComponent>(); var vvHandle = Vvm.GetTypeHandler<EventHorizonComponent>();
vvHandle.RemovePath(nameof(EventHorizonComponent.Radius)); vvHandle.RemovePath(nameof(EventHorizonComponent.Radius));
vvHandle.RemovePath(nameof(EventHorizonComponent.CanBreachContainment)); vvHandle.RemovePath(nameof(EventHorizonComponent.CanBreachContainment));
vvHandle.RemovePath(nameof(EventHorizonComponent.HorizonFixtureId)); vvHandle.RemovePath(nameof(EventHorizonComponent.ColliderFixtureId));
vvHandle.RemovePath(nameof(EventHorizonComponent.ConsumerFixtureId));
base.Shutdown(); base.Shutdown();
} }
@@ -100,16 +102,39 @@ public abstract class SharedEventHorizonSystem : EntitySystem
/// <param name="value">The new fixture ID to associate the event horizon with.</param> /// <param name="value">The new fixture ID to associate the event horizon with.</param>
/// <param name="updateFixture">Whether to update the associated fixture upon changing whether the event horizon can breach containment.</param> /// <param name="updateFixture">Whether to update the associated fixture upon changing whether the event horizon can breach containment.</param>
/// <param name="eventHorizon">The state of the event horizon with the fixture ID to change.</param> /// <param name="eventHorizon">The state of the event horizon with the fixture ID to change.</param>
public void SetHorizonFixtureId(EntityUid uid, string? value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null) public void SetColliderFixtureId(EntityUid uid, string? value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
{ {
if (!Resolve(uid, ref eventHorizon)) if (!Resolve(uid, ref eventHorizon))
return; return;
var oldValue = eventHorizon.HorizonFixtureId; var oldValue = eventHorizon.ColliderFixtureId;
if (value == oldValue) if (value == oldValue)
return; return;
eventHorizon.HorizonFixtureId = value; eventHorizon.ColliderFixtureId = value;
Dirty(eventHorizon);
if (updateFixture)
UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
}
/// <summary>
/// Setter for <see cref="EventHorizonComponent.HorizonFixtureId"/>
/// May also update the fixture associated with the event horizon.
/// </summary>
/// <param name="uid">The uid of the event horizon with the fixture ID to change.</param>
/// <param name="value">The new fixture ID to associate the event horizon with.</param>
/// <param name="updateFixture">Whether to update the associated fixture upon changing whether the event horizon can breach containment.</param>
/// <param name="eventHorizon">The state of the event horizon with the fixture ID to change.</param>
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(eventHorizon); Dirty(eventHorizon);
if (updateFixture) if (updateFixture)
UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon); UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
@@ -118,27 +143,42 @@ public abstract class SharedEventHorizonSystem : EntitySystem
/// <summary> /// <summary>
/// Updates the state of the fixture associated with the event horizon. /// Updates the state of the fixture associated with the event horizon.
/// </summary> /// </summary>
/// <param name="eventHorizon">The uid of the event horizon associated with the fixture to update.</param> /// <param name="uid">The uid of the event horizon associated with the fixture to update.</param>
/// <param name="physics">The physics component containing the fixture to update.</param> /// <param name="fixtures">The fixture manager component containing the fixture to update.</param>
/// <param name="eventHorizon">The state of the event horizon associated with the fixture to update.</param> /// <param name="eventHorizon">The state of the event horizon associated with the fixture to update.</param>
public void UpdateEventHorizonFixture(EntityUid uid, PhysicsComponent? physics = null, EventHorizonComponent? eventHorizon = null) public void UpdateEventHorizonFixture(EntityUid uid, FixturesComponent? fixtures = null, EventHorizonComponent? eventHorizon = null)
{ {
if (!Resolve(uid, ref eventHorizon)) if (!Resolve(uid, ref eventHorizon))
return; return;
var fixtureId = eventHorizon.HorizonFixtureId; var consumerId = eventHorizon.ConsumerFixtureId;
FixturesComponent? manager = null; var colliderId = eventHorizon.ColliderFixtureId;
if (consumerId == null || colliderId == null
if (fixtureId == null || !Resolve(uid, ref manager, ref physics, logMissing: false)) || !Resolve(uid, ref fixtures, logMissing: false))
return; return;
var fixture = _fixtures.GetFixtureOrNull(uid, fixtureId, manager); // Update both fixtures the event horizon is associated with:
if (fixture == null) if (consumerId != null)
return; {
var consumer = _fixtures.GetFixtureOrNull(uid, consumerId, fixtures);
if (consumer != null)
{
_physics.SetRadius(uid, consumer, consumer.Shape, eventHorizon.Radius, fixtures);
_physics.SetHard(uid, consumer, false, fixtures);
}
}
var shape = (PhysShapeCircle)fixture.Shape; if (colliderId != null)
_physics.SetRadius(uid, fixture, shape, eventHorizon.Radius, manager: manager, body: physics); {
_physics.SetHard(uid, fixture, true, manager); var collider = _fixtures.GetFixtureOrNull(uid, colliderId, fixtures);
if (collider != null)
{
_physics.SetRadius(uid, collider, collider.Shape, eventHorizon.Radius, fixtures);
_physics.SetHard(uid, collider, true, fixtures);
}
}
EntityManager.Dirty(uid, fixtures);
} }
#endregion Getters/Setters #endregion Getters/Setters

View File

@@ -14,20 +14,31 @@
- type: EventHorizon # To make the singularity consume things. - type: EventHorizon # To make the singularity consume things.
radius: 0.5 radius: 0.5
canBreachContainment: false canBreachContainment: false
horizonFixtureId: EventHorizon colliderFixtureId: EventHorizonCollider
consumerFixtureId: EventHorizonConsumer
- type: GravityWell # To make the singularity attract things. - type: GravityWell # To make the singularity attract things.
- type: Fixtures - type: Fixtures
fixtures: fixtures:
EventHorizon: EventHorizonCollider:
shape: shape:
!type:PhysShapeCircle !type:PhysShapeCircle
radius: 0.35 radius: 0.35
hard: true
restitution: 0.8 restitution: 0.8
density: 99999 density: 99999
mask: mask:
- AllMask - AllMask
layer: layer:
- AllMask - AllMask
EventHorizonConsumer:
shape:
!type:PhysShapeCircle
radius: 0.35
hard: false
mask:
- AllMask
layer:
- AllMask
- type: Singularity - type: Singularity
energy: 180 energy: 180
level: 1 level: 1
@@ -80,4 +91,3 @@
sprite: Structures/Power/Generation/Singularity/singularity_6.rsi sprite: Structures/Power/Generation/Singularity/singularity_6.rsi
state: singularity_6 state: singularity_6
scale: .9,.9 scale: .9,.9