Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Newtonian Singularity #1619

Merged
merged 2 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Content.Server/Singularity/Components/GravityWellComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,17 @@ public sealed partial class GravityWellComponent : Component
[Access(typeof(GravityWellSystem))]
public TimeSpan LastPulseTime { get; internal set; } = default!;

/// <summary>
/// Whether to also apply Newton's third law.
/// </summary>
[DataField]
public bool ApplyCounterforce;

/// <summary>
/// If <see cref="ApplyCounterforce"/> is true, how much to pull self to static objects. Does not pull static objects if null.
/// </summary>
[DataField]
public float? StaticAttraction = null;

#endregion Update Timing
}
16 changes: 14 additions & 2 deletions Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
using Robust.Shared.Containers;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Timing;
using Content.Shared.Abilities.Psionics; //Nyano - Summary: for the telegnostic projection.
using Content.Shared.Abilities.Psionics;

namespace Content.Server.Singularity.EntitySystems;

Expand All @@ -29,6 +31,7 @@ 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 SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
#endregion Dependencies
Expand All @@ -39,7 +42,7 @@ public override void Initialize()

SubscribeLocalEvent<MapGridComponent, EventHorizonAttemptConsumeEntityEvent>(PreventConsume);
SubscribeLocalEvent<GhostComponent, EventHorizonAttemptConsumeEntityEvent>(PreventConsume);
SubscribeLocalEvent<TelegnosticProjectionComponent, EventHorizonAttemptConsumeEntityEvent>(PreventConsume); ///Nyano - Summary: the telegnositic projection has the same trait as ghosts.
SubscribeLocalEvent<TelegnosticProjectionComponent, EventHorizonAttemptConsumeEntityEvent>(PreventConsume); ///Nyano - Summary: the telegnositic projection has the same trait as ghosts.
SubscribeLocalEvent<StationDataComponent, EventHorizonAttemptConsumeEntityEvent>(PreventConsume);
SubscribeLocalEvent<EventHorizonComponent, MapInitEvent>(OnHorizonMapInit);
SubscribeLocalEvent<EventHorizonComponent, StartCollideEvent>(OnStartCollide);
Expand Down Expand Up @@ -127,6 +130,15 @@ public void ConsumeEntity(EntityUid hungry, EntityUid morsel, EventHorizonCompon
}

EntityManager.QueueDeleteEntity(morsel);

if (eventHorizon.InheritMomentum && TryComp<PhysicsComponent>(hungry, out var thisPhysics) && TryComp<PhysicsComponent>(morsel, out var otherPhysics))
{
var impulse = (otherPhysics.LinearVelocity - thisPhysics.LinearVelocity)
* otherPhysics.FixturesMass
* thisPhysics.FixturesMass / (thisPhysics.FixturesMass + otherPhysics.FixturesMass); // Accounts for the expected mass change from consuming the object
_physics.ApplyLinearImpulse(hungry, impulse, body: thisPhysics);
}

var evSelf = new EntityConsumedByEventHorizonEvent(morsel, hungry, eventHorizon, outerContainer);
var evEaten = new EventHorizonConsumedEntityEvent(morsel, hungry, eventHorizon, outerContainer);
RaiseLocalEvent(hungry, ref evSelf);
Expand Down
78 changes: 53 additions & 25 deletions Content.Server/Singularity/EntitySystems/GravityWellSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ private void Update(EntityUid uid, TimeSpan frameTime, GravityWellComponent? gra
return;

var scale = (float)frameTime.TotalSeconds;
GravPulse(uid, gravWell.MaxRange, gravWell.MinRange, gravWell.BaseRadialAcceleration * scale, gravWell.BaseTangentialAcceleration * scale, xform);
GravPulse(uid, out var appliedImpulse, gravWell.MaxRange, gravWell.MinRange, gravWell.BaseRadialAcceleration * scale, gravWell.BaseTangentialAcceleration * scale, xform, gravWell.StaticAttraction);

if (gravWell.ApplyCounterforce && TryComp<PhysicsComponent>(uid, out var physics))
_physics.ApplyLinearImpulse(uid, -appliedImpulse, body: physics);
}

#region GravPulse
Expand All @@ -123,29 +126,36 @@ private bool CanGravPulseAffect(EntityUid entity)
/// Greates a gravitational pulse, shoving around all entities within some distance of an epicenter.
/// </summary>
/// <param name="uid">The entity at the epicenter of the gravity pulse.</param>
/// <param name="gravWell">The state of the gravity well that is pulsing.</param>
/// <param name="maxRange">The maximum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="baseMatrixDeltaV">The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter.</param>
/// <param name="xform">(optional) The transform of the entity at the epicenter of the gravitational pulse.</param>
public void GravPulse(EntityUid uid, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV, TransformComponent? xform = null)
public void GravPulse(EntityUid uid, out Vector2 appliedImpulse, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV, TransformComponent? xform = null, float? staticImpulse = null)
{
if (Resolve(uid, ref xform))
GravPulse(xform.Coordinates, maxRange, minRange, in baseMatrixDeltaV);
GravPulse(uid, out appliedImpulse, xform.Coordinates, maxRange, minRange, in baseMatrixDeltaV, staticImpulse);
else
appliedImpulse = new Vector2(0f, 0f);
}


/// <summary>
/// Greates a gravitational pulse, shoving around all entities within some distance of an epicenter.
/// </summary>
/// <param name="uid">The entity at the epicenter of the gravity pulse.</param>
/// <param name="gravWell">The state of the gravity well that is pulsing.</param>
/// <param name="maxRange">The maximum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="baseRadialDeltaV">The base radial velocity that will be added to entities within range towards the center of the gravitational pulse.</param>
/// <param name="baseTangentialDeltaV">The base tangential velocity that will be added to entities within countrclockwise around the center of the gravitational pulse.</param>
/// <param name="xform">(optional) The transform of the entity at the epicenter of the gravitational pulse.</param>
public void GravPulse(EntityUid uid, float maxRange, float minRange, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f, TransformComponent? xform = null)
public void GravPulse(EntityUid uid, out Vector2 appliedImpulse, float maxRange, float minRange, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f, TransformComponent? xform = null, float? staticImpulse = null)
{
if (Resolve(uid, ref xform))
GravPulse(xform.Coordinates, maxRange, minRange, baseRadialDeltaV, baseTangentialDeltaV);
GravPulse(uid, out appliedImpulse, xform.Coordinates, maxRange, minRange, baseRadialDeltaV, baseTangentialDeltaV, staticImpulse);
else
appliedImpulse = new Vector2(0f, 0f);
}

/// <summary>
Expand All @@ -155,8 +165,8 @@ public void GravPulse(EntityUid uid, float maxRange, float minRange, float baseR
/// <param name="maxRange">The maximum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="baseMatrixDeltaV">The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter.</param>
public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV)
=> GravPulse(entityPos.ToMap(EntityManager, _transform), maxRange, minRange, in baseMatrixDeltaV);
public void GravPulse(EntityUid uid, out Vector2 appliedImpulse, EntityCoordinates entityPos, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV, float? staticImpulse = null)
=> GravPulse(uid, out appliedImpulse, _transform.ToMapCoordinates(entityPos), maxRange, minRange, in baseMatrixDeltaV, staticImpulse);

/// <summary>
/// Greates a gravitational pulse, shoving around all entities within some distance of an epicenter.
Expand All @@ -166,18 +176,22 @@ public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRang
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="baseRadialDeltaV">The base radial velocity that will be added to entities within range towards the center of the gravitational pulse.</param>
/// <param name="baseTangentialDeltaV">The base tangential velocity that will be added to entities within countrclockwise around the center of the gravitational pulse.</param>
public void GravPulse(EntityCoordinates entityPos, float maxRange, float minRange, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f)
=> GravPulse(entityPos.ToMap(EntityManager, _transform), maxRange, minRange, baseRadialDeltaV, baseTangentialDeltaV);
public void GravPulse(EntityUid uid, out Vector2 appliedImpulse, EntityCoordinates entityPos, float maxRange, float minRange, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f, float? staticImpulse = null)
=> GravPulse(uid, out appliedImpulse, _transform.ToMapCoordinates(entityPos), maxRange, minRange, baseRadialDeltaV, baseTangentialDeltaV, staticImpulse);

/// <summary>
/// Causes a gravitational pulse, shoving around all entities within some distance of an epicenter.
/// </summary>
/// <param name="uid">The entity at the epicenter of the gravity pulse.</param>
/// <param name="gravWell">The state of the gravity well that is pulsing.</param>
/// <param name="mapPos">The epicenter of the gravity pulse.</param>
/// <param name="maxRange">The maximum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse. Exists to prevent div/0 errors.</param>
/// <param name="baseMatrixDeltaV">The base velocity added to any entities within affected by the gravity pulse scaled by the displacement of those entities from the epicenter.</param>
public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV)
public void GravPulse(EntityUid uid, out Vector2 appliedImpulse, MapCoordinates mapPos, float maxRange, float minRange, in Matrix3x2 baseMatrixDeltaV, float? staticImpulse = null)
{
appliedImpulse = new Vector2(0f, 0f);

if (mapPos == MapCoordinates.Nullspace)
return; // No gravpulses in nullspace please.

Expand All @@ -186,43 +200,57 @@ public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange, in
var bodyQuery = GetEntityQuery<PhysicsComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();

foreach(var entity in _lookup.GetEntitiesInRange(mapPos.MapId, epicenter, maxRange, flags: LookupFlags.Dynamic | LookupFlags.Sundries))
var countStatic = staticImpulse is not null;

var flags = LookupFlags.Dynamic | LookupFlags.Sundries;
if (countStatic)
flags |= LookupFlags.Static;

foreach(var entity in _lookup.GetEntitiesInRange(mapPos.MapId, epicenter, maxRange, flags: flags))
{
if (!bodyQuery.TryGetComponent(entity, out var physics)
|| physics.BodyType == BodyType.Static)
{
if (!bodyQuery.TryGetComponent(entity, out var physics))
continue;
}

if (TryComp<MovedByPressureComponent>(entity, out var movedPressure) && !movedPressure.Enabled) //Ignore magboots users
bool isStatic = physics.BodyType == BodyType.Static;
if (!countStatic && isStatic)
continue;

if(!CanGravPulseAffect(entity))
if (!CanGravPulseAffect(entity))
continue;

isStatic |= TryComp<MovedByPressureComponent>(entity, out var movedPressure) && !movedPressure.Enabled; // Treat magboots users as static

var displacement = epicenter - _transform.GetWorldPosition(entity, xformQuery);
var distance2 = displacement.LengthSquared();
if (distance2 < minRange2)
continue;

var scaling = (1f / distance2) * physics.Mass; // TODO: Variable falloff gradiants.
_physics.ApplyLinearImpulse(entity, Vector2.Transform(displacement, baseMatrixDeltaV) * scaling, body: physics);
var scaling = (1f / distance2) * physics.FixturesMass; // TODO: Variable falloff gradients
if (isStatic)
scaling *= staticImpulse ?? 1f;

var impulse = Vector2.Transform(displacement, baseMatrixDeltaV) * scaling;
if (!isStatic) // Impulse shouldn't be applied to static entities
_physics.ApplyLinearImpulse(entity, impulse, body: physics);

appliedImpulse += impulse;
}
}

/// <summary>
/// Causes a gravitational pulse, shoving around all entities within some distance of an epicenter.
/// </summary>
/// <param name="uid">The entity at the epicenter of the gravity pulse.</param>
/// <param name="gravWell">The state of the gravity well that is pulsing.</param>
/// <param name="mapPos">The epicenter of the gravity pulse.</param>
/// <param name="maxRange">The maximum distance at which entities can be affected by the gravity pulse.</param>
/// <param name="minRange">The minimum distance at which entities can be affected by the gravity pulse. Exists to prevent div/0 errors.</param>
/// <param name="baseRadialDeltaV">The base amount of velocity that will be added to entities in range towards the epicenter of the pulse.</param>
/// <param name="baseTangentialDeltaV">The base amount of velocity that will be added to entities in range counterclockwise relative to the epicenter of the pulse.</param>
public void GravPulse(MapCoordinates mapPos, float maxRange, float minRange = 0.0f, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f)
=> GravPulse(mapPos, maxRange, minRange, new Matrix3x2(
baseRadialDeltaV, -baseTangentialDeltaV, 0.0f,
+baseTangentialDeltaV, baseRadialDeltaV, 0.0f
));
public void GravPulse(EntityUid uid, out Vector2 appliedImpulse, MapCoordinates mapPos, float maxRange, float minRange = 0.0f, float baseRadialDeltaV = 0.0f, float baseTangentialDeltaV = 0.0f, float? staticImpulse = null)
=> GravPulse(uid, out appliedImpulse, mapPos, maxRange, minRange, new Matrix3x2(
baseRadialDeltaV, +baseTangentialDeltaV, 0.0f,
-baseTangentialDeltaV, baseRadialDeltaV, 0.0f
), staticImpulse);

#endregion GravPulse

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,11 @@ public sealed partial class EventHorizonComponent : Component
[AutoPausedField]
public TimeSpan NextConsumeWaveTime;

/// <summary>
/// Whether to inherit the momentum of consumed objects.
/// </summary>
[DataField]
public bool InheritMomentum;

#endregion Update Timing
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
- type: EventHorizon # To make the singularity consume things.
radius: 0.5
canBreachContainment: false
inheritMomentum: true
colliderFixtureId: EventHorizonCollider
consumerFixtureId: EventHorizonConsumer
- type: GravityWell # To make the singularity attract things.
baseRadialAcceleration: 10
maxRange: 4
applyCounterforce: true
staticAttraction: 0.3
- type: Fixtures
fixtures:
EventHorizonCollider:
Expand All @@ -30,7 +33,7 @@
radius: 0.35
hard: true
restitution: 0.8
density: 99999
density: 2000
mask:
- AllMask
layer:
Expand All @@ -52,6 +55,7 @@
- type: RandomWalk # To make the singularity move around.
maxSpeed: 2.5
minSpeed: 1.875
accumulatorRatio: 0.7
- type: SingularityDistortion
falloffPower: 2.529822
intensity: 3645
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
radius: 0.55
hard: true
restitution: 0.8
density: 99999
density: 2000
mask:
- Opaque
layer:
Expand Down
Loading