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="comp">The containment field generator the singularity is trying to eat.</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)
return;
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)
args.Cancel();
args.Cancelled = true;
}
}

View File

@@ -1,21 +1,19 @@
using System.Numerics;
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.Timing;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
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;
@@ -31,19 +29,10 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
[Dependency] private readonly IMapManager _mapMan = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
#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()
{
base.Initialize();
@@ -74,26 +63,20 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// 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.
/// </summary>
/// <param name="frameTime">The amount of time that has elapsed since the last cooldown update.</param>
public override void Update(float frameTime)
{
if(!_timing.IsFirstTimePredicted)
return;
foreach(var (eventHorizon, xform) in EntityManager.EntityQuery<EventHorizonComponent, TransformComponent>())
var query = EntityQueryEnumerator<EventHorizonComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var eventHorizon, out var xform))
{
var curTime = _timing.CurTime;
if (eventHorizon.NextConsumeWaveTime <= curTime)
Update(eventHorizon.Owner, eventHorizon, xform);
Update(uid, eventHorizon, xform);
}
}
/// <summary>
/// Makes an event horizon consume everything nearby and resets the cooldown it for the next automated wave.
/// </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)
{
if (!Resolve(uid, ref eventHorizon))
@@ -108,14 +91,15 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
// Handle singularities some admin smited into a locker.
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);
return;
}
if (eventHorizon.Radius > 0.0f)
ConsumeEverythingInRange(xform.Owner, eventHorizon.Radius, xform, eventHorizon);
ConsumeEverythingInRange(uid, eventHorizon.Radius, xform, eventHorizon);
}
#region Consume
@@ -125,48 +109,42 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// <summary>
/// Makes an event horizon consume a given entity.
/// </summary>
/// <param name="uid">The entity to consume.</param>
/// <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)
public void ConsumeEntity(EntityUid hungry, EntityUid morsel, 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?
(HasComp<MindContainerComponent>(uid) ||
_tagSystem.HasTag(uid, "HighRiskItem") ||
HasComp<ContainmentFieldGeneratorComponent>(uid)))
_adminLogger.Add(LogType.EntityDelete, LogImpact.Extreme, $"{ToPrettyString(uid)} entered the event horizon of {ToPrettyString(eventHorizonOwner)} and was deleted");
EntityManager.QueueDeleteEntity(uid);
RaiseLocalEvent(eventHorizonOwner, new EntityConsumedByEventHorizonEvent(uid, eventHorizon, outerContainer));
RaiseLocalEvent(uid, new EventHorizonConsumedEntityEvent(uid, eventHorizon, outerContainer));
EntityManager.QueueDeleteEntity(morsel);
var evSelf = new EntityConsumedByEventHorizonEvent(morsel, hungry, eventHorizon, outerContainer);
var evEaten = new EventHorizonConsumedEntityEvent(morsel, hungry, eventHorizon, outerContainer);
RaiseLocalEvent(hungry, ref evSelf);
RaiseLocalEvent(morsel, ref evEaten);
}
/// <summary>
/// Makes an event horizon attempt to consume a given entity.
/// </summary>
/// <param name="uid">The entity to attempt to consume.</param>
/// <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)
public bool AttemptConsumeEntity(EntityUid hungry, EntityUid morsel, EventHorizonComponent eventHorizon, IContainer? outerContainer = null)
{
if(!CanConsumeEntity(uid, eventHorizon))
if (!CanConsumeEntity(hungry, morsel, eventHorizon))
return false;
ConsumeEntity(uid, eventHorizon, outerContainer);
ConsumeEntity(hungry, morsel, eventHorizon, outerContainer);
return true;
}
/// <summary>
/// Checks whether an event horizon can consume a given entity.
/// </summary>
/// <param name="uid">The entity to check for consumability.</param>
/// <param name="eventHorizon">The event horizon checking whether it can consume the entity.</param>
public bool CanConsumeEntity(EntityUid uid, EventHorizonComponent eventHorizon)
public bool CanConsumeEntity(EntityUid hungry, EntityUid uid, EventHorizonComponent eventHorizon)
{
var ev = new EventHorizonAttemptConsumeEntityEvent(uid, eventHorizon);
RaiseLocalEvent(uid, ev);
var ev = new EventHorizonAttemptConsumeEntityEvent(uid, hungry, eventHorizon);
RaiseLocalEvent(uid, ref ev);
return !ev.Cancelled;
}
@@ -174,21 +152,27 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Attempts to consume all entities within a given distance of an entity;
/// Excludes the center entity.
/// </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)
{
if (!Resolve(uid, ref xform, ref eventHorizon))
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))
{
if (entity == uid)
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.
/// All immune entities within the container will be dumped to a given container or the map/grid if that is impossible.
/// </summary>
/// <param name="uid">The uid of the event horizon. The single entity that is immune-by-default.</param>
/// <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) {
public void ConsumeEntitiesInContainer(EntityUid hungry, 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.
List<EntityUid> immune = new();
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.
// 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
// 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 (target_container == null)
Transform(entity).AttachToGridOrMap();
_xformSystem.AttachToGridOrMap(entity);
}
}
@@ -242,51 +223,47 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// <summary>
/// Makes an event horizon consume a specific tile on a grid.
/// </summary>
/// <param name="tile">The tile to consume.</param>
/// <param name="eventHorizon">The event horizon which is consuming the tile on the grid.</param>
public void ConsumeTile(TileRef tile, EventHorizonComponent eventHorizon)
=> ConsumeTiles(new List<(Vector2i, Tile)>(new []{(tile.GridIndices, Tile.Empty)}), _mapMan.GetGrid(tile.GridUid), eventHorizon);
public void ConsumeTile(EntityUid hungry, TileRef tile, EventHorizonComponent eventHorizon)
{
ConsumeTiles(hungry, new List<(Vector2i, Tile)>(new[] { (tile.GridIndices, Tile.Empty) }), tile.GridUid, _mapMan.GetGrid(tile.GridUid), eventHorizon);
}
/// <summary>
/// Makes an event horizon attempt to consume a specific tile on a grid.
/// </summary>
/// <param name="tile">The tile to attempt to consume.</param>
/// <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(new TileRef[1]{tile}, _mapMan.GetGrid(tile.GridUid), eventHorizon);
public void AttemptConsumeTile(EntityUid hungry, TileRef tile, EventHorizonComponent eventHorizon)
{
AttemptConsumeTiles(hungry, new TileRef[1] { tile }, tile.GridUid, _mapMan.GetGrid(tile.GridUid), eventHorizon);
}
/// <summary>
/// Makes an event horizon consume a set of tiles on a grid.
/// </summary>
/// <param name="tiles">The tiles to consume.</param>
/// <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)
public void ConsumeTiles(EntityUid hungry, List<(Vector2i, Tile)> tiles, EntityUid gridId, MapGridComponent grid, EventHorizonComponent eventHorizon)
{
if (tiles.Count <= 0)
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);
}
/// <summary>
/// Makes an event horizon attempt to consume a set of tiles on a grid.
/// </summary>
/// <param name="tiles">The tiles to attempt to consume.</param>
/// <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)
public int AttemptConsumeTiles(EntityUid hungry, IEnumerable<TileRef> tiles, EntityUid gridId, MapGridComponent grid, EventHorizonComponent eventHorizon)
{
var toConsume = new List<(Vector2i, Tile)>();
foreach(var tile in tiles) {
if (CanConsumeTile(tile, grid, eventHorizon))
foreach (var tile in tiles)
{
if (CanConsumeTile(hungry, tile, grid, eventHorizon))
toConsume.Add((tile.GridIndices, Tile.Empty));
}
var result = toConsume.Count;
if (toConsume.Count > 0)
ConsumeTiles(toConsume, grid, eventHorizon);
ConsumeTiles(hungry, toConsume, gridId, grid, eventHorizon);
return result;
}
@@ -294,14 +271,11 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// 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.
/// </summary>
/// <param name="tile">The tile to check for consumability.</param>
/// <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)
public bool CanConsumeTile(EntityUid hungry, TileRef tile, MapGridComponent grid, EventHorizonComponent eventHorizon)
{
foreach (var blockingEntity in grid.GetAnchoredEntities(tile.GridIndices))
{
if(!CanConsumeEntity(blockingEntity, eventHorizon))
if (!CanConsumeEntity(hungry, blockingEntity, eventHorizon))
return false;
}
return true;
@@ -311,10 +285,6 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// Consumes all tiles within a given distance of an entity.
/// Some entities are immune to consumption.
/// </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)
{
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 circle = new Circle(mapPos.Position, range);
foreach (var grid in _mapMan.FindGridsIntersecting(mapPos.MapId, box))
{
AttemptConsumeTiles(grid.GetTilesIntersecting(circle), grid, eventHorizon);
{ // TODO: Remover grid.Owner when this iterator returns entityuids as well.
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.
/// Some entities are immune to consumption.
/// </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)
{
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.
/// If the new scanning period would have already prompted a scan given the previous scan time one is prompted immediately.
/// </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)
{
if (!Resolve(uid, ref eventHorizon))
@@ -383,60 +346,49 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// <summary>
/// Prevents a singularity from colliding with anything it is incapable of consuming.
/// </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)
{
if (base.PreventCollide(uid, comp, ref args) || args.Cancelled)
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;
}
/// <summary>
/// A generic event handler that prevents singularities from consuming entities with a component of a given type if registered.
/// </summary>
/// <param name="uid">The entity the singularity is trying to eat.</param>
/// <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)
public static void PreventConsume<TComp>(EntityUid uid, TComp comp, ref EventHorizonAttemptConsumeEntityEvent args)
{
if (!args.Cancelled)
args.Cancel();
args.Cancelled = true;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="uid">The entity the singularity is trying to eat.</param>
/// <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)
public static void PreventBreach<TComp>(EntityUid uid, TComp comp, ref EventHorizonAttemptConsumeEntityEvent args)
{
if (args.Cancelled)
return;
if (!args.EventHorizon.CanBreachContainment)
PreventConsume(uid, comp, args);
PreventConsume(uid, comp, ref args);
}
/// <summary>
/// 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.
/// </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)
{
if (comp.BeingConsumedByAnotherEventHorizon)
return;
if (args.OurFixture.ID != comp.HorizonFixtureId)
if (args.OurFixture.ID != comp.ConsumerFixtureId)
return;
AttemptConsumeEntity(args.OtherEntity, comp);
AttemptConsumeEntity(uid, args.OtherEntity, comp);
}
/// <summary>
@@ -444,40 +396,31 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// 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.
/// </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 OnAnotherEventHorizonAttemptConsumeThisEventHorizon(EntityUid uid, EventHorizonComponent comp, EventHorizonAttemptConsumeEntityEvent args)
private void OnAnotherEventHorizonAttemptConsumeThisEventHorizon(EntityUid uid, EventHorizonComponent comp, ref EventHorizonAttemptConsumeEntityEvent args)
{
if (!args.Cancelled && (args.EventHorizon == comp || comp.BeingConsumedByAnotherEventHorizon))
args.Cancel();
args.Cancelled = true;
}
/// <summary>
/// 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.
/// </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 OnAnotherEventHorizonConsumedThisEventHorizon(EntityUid uid, EventHorizonComponent comp, EventHorizonConsumedEntityEvent args)
private void OnAnotherEventHorizonConsumedThisEventHorizon(EntityUid uid, EventHorizonComponent comp, ref EventHorizonConsumedEntityEvent args)
{
comp.BeingConsumedByAnotherEventHorizon = true;
}
/// <summary>
/// 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
/// the inserted entity SHALL be inside of the specified container after all handles to the entity event
/// <see cref="EntGotInsertedIntoContainerMessage" /> are processed.
/// </summary>
/// <param name="uid">The uid of the event horizon.</param>]
/// <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) {
private void OnEventHorizonContained(EntityUid uid, EventHorizonComponent comp, EntGotInsertedIntoContainerMessage args)
{
// 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));
}
@@ -487,29 +430,29 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
/// 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.
/// </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 comp = args.EventHorizon;
if (!EntityManager.EntityExists(uid))
return;
var comp = args.EventHorizon;
if (comp.BeingConsumedByAnotherEventHorizon)
return;
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);
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="uid">The uid of the container being consumed.</param>
/// <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)
private void OnContainerConsumed(EntityUid uid, ContainerManagerComponent comp, ref EventHorizonConsumedEntityEvent args)
{
var drop_container = args.Container;
if (drop_container is null)
@@ -517,7 +460,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
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

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="comp">The component of the singularity that is consuming the entity.</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);
}
@@ -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="comp">The component of the singularity that is consuming the tiles.</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);
}
/// <summary>
/// Adds the energy of this singularity to singularities consume it.
/// Adds the energy of this singularity to singularities that consume it.
/// </summary>
/// <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="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.
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);
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="comp">The component of the singularity food that is being consumed.</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))
AdjustEnergy(args.EventHorizon.Owner, comp.Energy, singularity: singulo);
if (EntityManager.TryGetComponent<SingularityComponent>(args.EventHorizonUid, out var singulo))
AdjustEnergy(args.EventHorizonUid, comp.Energy, singularity: singulo);
}
/// <summary>

View File

@@ -6,28 +6,28 @@ namespace Content.Server.Singularity.Events;
/// <summary>
/// Event raised on the event horizon entity whenever an event horizon consumes an entity.
/// </summary>
public sealed class EntityConsumedByEventHorizonEvent : EntityEventArgs
[ByRefEvent]
public readonly record struct EntityConsumedByEventHorizonEvent
(EntityUid entity, EntityUid eventHorizonUid, EventHorizonComponent eventHorizon, IContainer? container)
{
/// <summary>
/// The entity being consumed by the event horizon.
/// </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>
/// The event horizon consuming the entity.
/// </summary>
public readonly EventHorizonComponent EventHorizon;
public readonly EventHorizonComponent EventHorizon = eventHorizon;
/// <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.
/// Used to correctly dump out the contents containers that are consumed by the event horizon.
/// </summary>
public readonly IContainer? Container;
public EntityConsumedByEventHorizonEvent(EntityUid entity, EventHorizonComponent eventHorizon, IContainer? container = null)
{
Entity = entity;
EventHorizon = eventHorizon;
Container = container;
}
public readonly IContainer? 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.
/// Can be cancelled to prevent the target entity from being consumed.
/// </summary>
public sealed class EventHorizonAttemptConsumeEntityEvent : CancellableEntityEventArgs
[ByRefEvent]
public record struct EventHorizonAttemptConsumeEntityEvent
(EntityUid entity, EntityUid eventHorizonUid, EventHorizonComponent eventHorizon)
{
/// <summary>
/// The entity that the event horizon is attempting to consume.
/// </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>
/// The event horizon consuming the target entity.
/// </summary>
public readonly EventHorizonComponent EventHorizon;
public readonly EventHorizonComponent EventHorizon = eventHorizon;
public EventHorizonAttemptConsumeEntityEvent(EntityUid entity, EventHorizonComponent eventHorizon)
{
Entity = entity;
EventHorizon = eventHorizon;
}
/// <summary>
/// Whether the event horizon has been prevented from consuming the target entity.
/// </summary>
public bool Cancelled = false;
}

View File

@@ -4,30 +4,30 @@ using Robust.Shared.Containers;
namespace Content.Server.Singularity.Events;
/// <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>
public sealed class EventHorizonConsumedEntityEvent : EntityEventArgs
[ByRefEvent]
public readonly record struct EventHorizonConsumedEntityEvent
(EntityUid entity, EntityUid eventHorizonUid, EventHorizonComponent eventHorizon, IContainer? container)
{
/// <summary>
/// The entity being consumed by the event horizon.
/// </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>
/// The event horizon consuming the target entity.
/// </summary>
public readonly EventHorizonComponent EventHorizon;
public readonly EventHorizonComponent EventHorizon = eventHorizon;
/// <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.
/// Used to correctly dump out the contents containers that are consumed by the event horizon.
/// </summary>
public readonly IContainer? Container;
public EventHorizonConsumedEntityEvent(EntityUid entity, EventHorizonComponent eventHorizon, IContainer? container = null)
{
Entity = entity;
EventHorizon = eventHorizon;
Container = container;
}
public readonly IContainer? Container = container;
}

View File

@@ -6,8 +6,10 @@ namespace Content.Shared.Singularity.EntitySystems;
/// <summary>
/// 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.
/// Needs to be a class because ref structs can't be put into the queue.
/// </summary>
public sealed class EventHorizonContainedEvent : EntityEventArgs {
public sealed class EventHorizonContainedEvent : EntityEventArgs
{
/// <summary>
/// The uid of the event horizon that has been contained.
/// </summary>
@@ -23,7 +25,8 @@ public sealed class EventHorizonContainedEvent : EntityEventArgs {
/// </summary>
public readonly EntGotInsertedIntoContainerMessage Args;
public EventHorizonContainedEvent(EntityUid entity, EventHorizonComponent eventHorizon, EntGotInsertedIntoContainerMessage args) {
public EventHorizonContainedEvent(EntityUid entity, EventHorizonComponent eventHorizon, EntGotInsertedIntoContainerMessage args)
{
Entity = entity;
EventHorizon = eventHorizon;
Args = args;

View File

@@ -8,28 +8,33 @@ namespace Content.Server.Singularity.Events;
/// <summary>
/// Event raised on the event horizon entity whenever an event horizon consumes an entity.
/// </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>
/// 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"/>.
/// </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>
/// The mapgrid that the event horizon is consuming tiles of.
/// </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>
/// The event horizon consuming the tiles.
/// </summary>
public readonly EventHorizonComponent EventHorizon;
public TilesConsumedByEventHorizonEvent(IReadOnlyList<(Vector2i, Tile)> tiles, MapGridComponent mapGrid, EventHorizonComponent eventHorizon)
{
Tiles = tiles;
MapGrid = mapGrid;
EventHorizon = eventHorizon;
}
public readonly EventHorizonComponent EventHorizon = eventHorizon;
}

View File

@@ -8,6 +8,7 @@ namespace Content.Shared.Singularity.Components;
/// Also makes the associated entity destroy other entities upon contact.
/// Primarily managed by <see cref="SharedEventHorizonSystem"/> and its server/client versions.
/// </summary>
[Access(friends: typeof(SharedEventHorizonSystem))]
[RegisterComponent, NetworkedComponent]
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"/>.
/// </summary>
[DataField("radius")]
[Access(friends:typeof(SharedEventHorizonSystem))]
public float Radius;
/// <summary>
@@ -25,7 +25,7 @@ public sealed class EventHorizonComponent : Component
/// If you want to set this go through <see cref="SharedEventHorizonSystem.SetCanBreachContainment"/>.
/// </summary>
[DataField("canBreachContainment")]
[Access(friends:typeof(SharedEventHorizonSystem))]
[ViewVariables(VVAccess.ReadWrite)]
public bool CanBreachContainment = false;
/// <summary>
@@ -33,9 +33,18 @@ public sealed class EventHorizonComponent : Component
/// 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("horizonFixtureId")]
[Access(friends:typeof(SharedEventHorizonSystem))]
public string? HorizonFixtureId = "EventHorizon";
[DataField("consumerFixtureId")]
[ViewVariables(VVAccess.ReadWrite)]
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>
/// 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.
/// </summary>
[DataField("consumePeriod")]
[ViewVariables(VVAccess.ReadOnly)]
[Access(typeof(SharedEventHorizonSystem))]
[ViewVariables(VVAccess.ReadWrite)]
public TimeSpan TargetConsumePeriod { get; set; } = TimeSpan.FromSeconds(0.5);
/// <summary>
/// The last time at which this consumed everything it overlapped with.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[Access(typeof(SharedEventHorizonSystem))]
public TimeSpan LastConsumeWaveTime { get; set; } = default!;
/// <summary>
/// The next time at which this consumed everything it overlapped with.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[Access(typeof(SharedEventHorizonSystem))]
public TimeSpan NextConsumeWaveTime { get; set; } = default!;
#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.
/// If you want to set this use <see cref="SharedSingularitySystem.SetLevel"/>().
/// </summary>
[DataField("level")]
[Access(friends: typeof(SharedSingularitySystem), Other = AccessPermissions.Read, Self = AccessPermissions.Read)]
[DataField("level")]
public byte Level = 1;
/// <summary>
@@ -27,8 +27,8 @@ public sealed class SingularityComponent : Component
/// 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"/>().
/// </summary>
[DataField("radsPerLevel")]
[Access(friends: typeof(SharedSingularitySystem), Other = AccessPermissions.Read, Self = AccessPermissions.Read)]
[DataField("radsPerLevel")]
[ViewVariables(VVAccess.ReadWrite)]
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.
/// </summary>
[DataField("updatePeriod")]
[ViewVariables(VVAccess.ReadOnly)]
[ViewVariables(VVAccess.ReadWrite)]
public TimeSpan TargetUpdatePeriod = TimeSpan.FromSeconds(1.0);
/// <summary>

View File

@@ -31,7 +31,8 @@ public abstract class SharedEventHorizonSystem : EntitySystem
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.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()
@@ -39,7 +40,8 @@ public abstract class SharedEventHorizonSystem : EntitySystem
var vvHandle = Vvm.GetTypeHandler<EventHorizonComponent>();
vvHandle.RemovePath(nameof(EventHorizonComponent.Radius));
vvHandle.RemovePath(nameof(EventHorizonComponent.CanBreachContainment));
vvHandle.RemovePath(nameof(EventHorizonComponent.HorizonFixtureId));
vvHandle.RemovePath(nameof(EventHorizonComponent.ColliderFixtureId));
vvHandle.RemovePath(nameof(EventHorizonComponent.ConsumerFixtureId));
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="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 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))
return;
var oldValue = eventHorizon.HorizonFixtureId;
var oldValue = eventHorizon.ColliderFixtureId;
if (value == oldValue)
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);
if (updateFixture)
UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
@@ -118,27 +143,42 @@ public abstract class SharedEventHorizonSystem : EntitySystem
/// <summary>
/// Updates the state of the fixture associated with the event horizon.
/// </summary>
/// <param name="eventHorizon">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="uid">The uid of the event horizon associated with 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>
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))
return;
var fixtureId = eventHorizon.HorizonFixtureId;
FixturesComponent? manager = null;
if (fixtureId == null || !Resolve(uid, ref manager, ref physics, logMissing: false))
var consumerId = eventHorizon.ConsumerFixtureId;
var colliderId = eventHorizon.ColliderFixtureId;
if (consumerId == null || colliderId == null
|| !Resolve(uid, ref fixtures, logMissing: false))
return;
var fixture = _fixtures.GetFixtureOrNull(uid, fixtureId, manager);
if (fixture == null)
return;
// Update both fixtures the event horizon is associated with:
if (consumerId != null)
{
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;
_physics.SetRadius(uid, fixture, shape, eventHorizon.Radius, manager: manager, body: physics);
_physics.SetHard(uid, fixture, true, manager);
if (colliderId != null)
{
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

View File

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