EVENT BASED WEIGHTLESSNESS (#37971)

* Init Commit

* Typos

* Commit 2

* Save Interaction Test Mob from failing

* ssss

* Confident I've gotten all the correct prototypes

* Whoops forgot to edit those

* aaaaa

* Better solution

* Test fail fixes

* Yaml fix

* THE FINAL TEST FIX

* Final fix(?)

* whoops

* Added a WeightlessnessChangedEvent

* Check out this diff

* Wait I'm dumb

* Final optimization and don't duplicate code

* Death to IsWeightless

* File scoped namespaces

* REVIEW

* Fix test fails

* FIX TEST FAILS REAL

* A

* Commit of doom

* borgar

* We don't need to specify on map init apparently

* Fuck it

* LOAD BEARING COMMENT

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
Princess Cheeseballs
2025-08-19 11:35:09 -07:00
committed by GitHub
parent 5cd9ba6016
commit 9de76e70c7
31 changed files with 329 additions and 268 deletions

View File

@@ -21,6 +21,7 @@ namespace Content.IntegrationTests.Tests.Doors
components:
- type: Physics
bodyType: Dynamic
- type: GravityAffected
- type: Fixtures
fixtures:
fix1:

View File

@@ -19,6 +19,7 @@ namespace Content.IntegrationTests.Tests.Gravity
- type: Alerts
- type: Physics
bodyType: Dynamic
- type: GravityAffected
- type: entity
name: WeightlessGravityGeneratorDummy

View File

@@ -76,8 +76,8 @@ namespace Content.IntegrationTests.Tests
Assert.Multiple(() =>
{
Assert.That(generatorComponent.GravityActive, Is.True);
Assert.That(!entityMan.GetComponent<GravityComponent>(grid1).EnabledVV);
Assert.That(entityMan.GetComponent<GravityComponent>(grid2).EnabledVV);
Assert.That(!entityMan.GetComponent<GravityComponent>(grid1).Enabled);
Assert.That(entityMan.GetComponent<GravityComponent>(grid2).Enabled);
});
// Re-enable needs power so it turns off again.
@@ -94,7 +94,7 @@ namespace Content.IntegrationTests.Tests
Assert.Multiple(() =>
{
Assert.That(generatorComponent.GravityActive, Is.False);
Assert.That(entityMan.GetComponent<GravityComponent>(grid2).EnabledVV, Is.False);
Assert.That(entityMan.GetComponent<GravityComponent>(grid2).Enabled, Is.False);
});
});

View File

@@ -144,6 +144,7 @@ public abstract partial class InteractionTest
- type: Stripping
- type: Puller
- type: Physics
- type: GravityAffected
- type: Tag
tags:
- CanPilot

View File

@@ -29,6 +29,7 @@ using Content.Shared.Damage;
using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Electrocution;
using Content.Shared.Gravity;
using Content.Shared.Interaction.Components;
using Content.Shared.Inventory;
using Content.Shared.Mobs;
@@ -675,6 +676,11 @@ public sealed partial class AdminVerbSystem
grav.Weightless = true;
Dirty(args.Target, grav);
EnsureComp<GravityAffectedComponent>(args.Target, out var weightless);
weightless.Weightless = true;
Dirty(args.Target, weightless);
},
Impact = LogImpact.Extreme,
Message = string.Join(": ", noGravityName, Loc.GetString("admin-smite-remove-gravity-description"))

View File

@@ -166,7 +166,7 @@ public sealed class SpraySystem : EntitySystem
if (TryComp<PhysicsComponent>(user, out var body))
{
if (_gravity.IsWeightless(user, body))
if (_gravity.IsWeightless(user))
{
// push back the player
_physics.ApplyLinearImpulse(user, -impulseDirection * entity.Comp.PushbackAmount, body: body);

View File

@@ -18,7 +18,7 @@ namespace Content.Server.Gravity
/// </summary>
public void RefreshGravity(EntityUid uid, GravityComponent? gravity = null)
{
if (!Resolve(uid, ref gravity))
if (!GravityQuery.Resolve(uid, ref gravity))
return;
if (gravity.Inherent)
@@ -61,7 +61,7 @@ namespace Content.Server.Gravity
/// </summary>
public void EnableGravity(EntityUid uid, GravityComponent? gravity = null)
{
if (!Resolve(uid, ref gravity))
if (!GravityQuery.Resolve(uid, ref gravity))
return;
if (gravity.Enabled || gravity.Inherent)

View File

@@ -6,10 +6,13 @@ namespace Content.Shared.Clothing.EntitySystems;
public sealed class AntiGravityClothingSystem : EntitySystem
{
[Dependency] SharedGravitySystem _gravity = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<AntiGravityClothingComponent, InventoryRelayedEvent<IsWeightlessEvent>>(OnIsWeightless);
SubscribeLocalEvent<AntiGravityClothingComponent, ClothingGotEquippedEvent>(OnEquipped);
SubscribeLocalEvent<AntiGravityClothingComponent, ClothingGotUnequippedEvent>(OnUnequipped);
}
private void OnIsWeightless(Entity<AntiGravityClothingComponent> ent, ref InventoryRelayedEvent<IsWeightlessEvent> args)
@@ -20,4 +23,14 @@ public sealed class AntiGravityClothingSystem : EntitySystem
args.Args.Handled = true;
args.Args.IsWeightless = true;
}
private void OnEquipped(Entity<AntiGravityClothingComponent> entity, ref ClothingGotEquippedEvent args)
{
_gravity.RefreshWeightless(args.Wearer, true);
}
private void OnUnequipped(Entity<AntiGravityClothingComponent> entity, ref ClothingGotUnequippedEvent args)
{
_gravity.RefreshWeightless(args.Wearer, false);
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Alert;
using Content.Shared.Inventory;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
@@ -16,10 +17,4 @@ public sealed partial class MagbootsComponent : Component
/// </summary>
[DataField]
public bool RequiresGrid = true;
/// <summary>
/// Slot the clothing has to be worn in to work.
/// </summary>
[DataField]
public string Slot = "shoes";
}

View File

@@ -32,15 +32,9 @@ public sealed class SharedMagbootsSystem : EntitySystem
private void OnToggled(Entity<MagbootsComponent> ent, ref ItemToggledEvent args)
{
var (uid, comp) = ent;
// only stick to the floor if being worn in the correct slot
if (_container.TryGetContainingContainer((uid, null, null), out var container) &&
_inventory.TryGetSlotEntity(container.Owner, comp.Slot, out var worn)
&& uid == worn)
{
if (_container.TryGetContainingContainer((ent.Owner, null, null), out var container))
UpdateMagbootEffects(container.Owner, ent, args.Activated);
}
}
private void OnGotUnequipped(Entity<MagbootsComponent> ent, ref ClothingGotUnequippedEvent args)
{
@@ -58,6 +52,8 @@ public sealed class SharedMagbootsSystem : EntitySystem
if (TryComp<MovedByPressureComponent>(user, out var moved))
moved.Enabled = !state;
_gravity.RefreshWeightless(user, !state);
if (state)
_alerts.ShowAlert(user, ent.Comp.MagbootsAlert);
else

View File

@@ -164,12 +164,11 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
if (args.Target is { } target && !xformQuery.TryGetComponent(target, out targetXform))
return true;
TransformComponent? usedXform = null;
if (args.Used is { } @using && !xformQuery.TryGetComponent(@using, out usedXform))
if (args.Used is { } @using && !xformQuery.HasComp(@using))
return true;
// TODO: Re-use existing xform query for these calculations.
if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User, xform: userXform)))
if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User)))
{
// Whether the user has moved too much from their original position.
if (!_transform.InRange(userXform.Coordinates, doAfter.UserPosition, args.MovementThreshold))

View File

@@ -73,7 +73,7 @@ namespace Content.Shared.Friction
// If we're not touching the ground, don't use tileFriction.
// TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events
if (body.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid, body, xform) || !xform.Coordinates.IsValid(EntityManager))
if (body.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid) || !xform.Coordinates.IsValid(EntityManager))
friction = xform.GridUid == null || !_gridQuery.HasComp(xform.GridUid) ? _offGridDamping : _airDamping;
else
friction = _frictionModifier * GetTileFriction(uid, body, xform);

View File

@@ -10,20 +10,17 @@ public sealed partial class FloatingVisualsComponent : Component
/// <summary>
/// How long it takes to go from the bottom of the animation to the top.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField, AutoNetworkedField]
public float AnimationTime = 2f;
/// <summary>
/// How far it goes in any direction.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField, AutoNetworkedField]
public Vector2 Offset = new(0, 0.2f);
[ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
public bool CanFloat = false;
[DataField, AutoNetworkedField]
public bool CanFloat;
public readonly string AnimationKey = "gravity";
}

View File

@@ -0,0 +1,17 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Gravity;
/// <summary>
/// This Component allows a target to be considered "weightless" when Weightless is true. Without this component, the
/// target will never be weightless.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class GravityAffectedComponent : Component
{
/// <summary>
/// If true, this entity will be considered "weightless"
/// </summary>
[DataField, AutoNetworkedField]
public bool Weightless = true;
}

View File

@@ -5,34 +5,20 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Gravity
{
[RegisterComponent]
[AutoGenerateComponentState]
[NetworkedComponent]
public sealed partial class GravityComponent : Component
{
[DataField("gravityShakeSound")]
[DataField, AutoNetworkedField]
public SoundSpecifier GravityShakeSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/alert.ogg");
[ViewVariables(VVAccess.ReadWrite)]
public bool EnabledVV
{
get => Enabled;
set
{
if (Enabled == value) return;
Enabled = value;
var ev = new GravityChangedEvent(Owner, value);
IoCManager.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(Owner, ref ev);
Dirty();
}
}
[DataField("enabled")]
[DataField, AutoNetworkedField]
public bool Enabled;
/// <summary>
/// Inherent gravity ensures GravitySystem won't change Enabled according to the gravity generators attached to this entity.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("inherent")]
[DataField, AutoNetworkedField]
public bool Inherent;
}
}

View File

@@ -8,15 +8,14 @@ namespace Content.Shared.Gravity;
/// </summary>
public abstract class SharedFloatingVisualizerSystem : EntitySystem
{
[Dependency] private readonly SharedGravitySystem GravitySystem = default!;
[Dependency] private readonly SharedGravitySystem _gravity = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FloatingVisualsComponent, ComponentStartup>(OnComponentStartup);
SubscribeLocalEvent<GravityChangedEvent>(OnGravityChanged);
SubscribeLocalEvent<FloatingVisualsComponent, EntParentChangedMessage>(OnEntParentChanged);
SubscribeLocalEvent<FloatingVisualsComponent, WeightlessnessChangedEvent>(OnWeightlessnessChanged);
}
/// <summary>
@@ -24,48 +23,28 @@ public abstract class SharedFloatingVisualizerSystem : EntitySystem
/// </summary>
public virtual void FloatAnimation(EntityUid uid, Vector2 offset, string animationKey, float animationTime, bool stop = false) { }
protected bool CanFloat(EntityUid uid, FloatingVisualsComponent component, TransformComponent? transform = null)
protected bool CanFloat(Entity<FloatingVisualsComponent> entity)
{
if (!Resolve(uid, ref transform))
return false;
if (transform.MapID == MapId.Nullspace)
return false;
component.CanFloat = GravitySystem.IsWeightless(uid, xform: transform);
Dirty(uid, component);
return component.CanFloat;
entity.Comp.CanFloat = _gravity.IsWeightless(entity.Owner);
Dirty(entity);
return entity.Comp.CanFloat;
}
private void OnComponentStartup(EntityUid uid, FloatingVisualsComponent component, ComponentStartup args)
private void OnComponentStartup(Entity<FloatingVisualsComponent> entity, ref ComponentStartup args)
{
if (CanFloat(uid, component))
FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
if (CanFloat(entity))
FloatAnimation(entity, entity.Comp.Offset, entity.Comp.AnimationKey, entity.Comp.AnimationTime);
}
private void OnGravityChanged(ref GravityChangedEvent args)
private void OnWeightlessnessChanged(Entity<FloatingVisualsComponent> entity, ref WeightlessnessChangedEvent args)
{
var query = EntityQueryEnumerator<FloatingVisualsComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var floating, out var transform))
{
if (transform.MapID == MapId.Nullspace)
continue;
if (entity.Comp.CanFloat == args.Weightless)
return;
if (transform.GridUid != args.ChangedGridIndex)
continue;
entity.Comp.CanFloat = CanFloat(entity);
Dirty(entity);
floating.CanFloat = !args.HasGravity;
Dirty(uid, floating);
if (!args.HasGravity)
FloatAnimation(uid, floating.Offset, floating.AnimationKey, floating.AnimationTime);
}
}
private void OnEntParentChanged(EntityUid uid, FloatingVisualsComponent component, ref EntParentChangedMessage args)
{
var transform = args.Transform;
if (CanFloat(uid, component, transform))
FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
if (args.Weightless)
FloatAnimation(entity, entity.Comp.Offset, entity.Comp.AnimationKey, entity.Comp.AnimationTime);
}
}

View File

@@ -1,15 +1,17 @@
using Content.Shared.Alert;
using Content.Shared.Inventory;
using Content.Shared.Movement.Components;
using Robust.Shared.GameStates;
using Content.Shared.Throwing;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
namespace Content.Shared.Gravity
{
namespace Content.Shared.Gravity;
public abstract partial class SharedGravitySystem : EntitySystem
{
[Dependency] protected readonly IGameTiming Timing = default!;
@@ -17,31 +19,139 @@ namespace Content.Shared.Gravity
public static readonly ProtoId<AlertPrototype> WeightlessAlert = "Weightless";
private EntityQuery<GravityComponent> _gravityQuery;
protected EntityQuery<GravityComponent> GravityQuery;
private EntityQuery<GravityAffectedComponent> _weightlessQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null)
public override void Initialize()
{
Resolve(uid, ref body, false);
base.Initialize();
// Grid Gravity
SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
SubscribeLocalEvent<GravityChangedEvent>(OnGravityChange);
if ((body?.BodyType & (BodyType.Static | BodyType.Kinematic)) != 0)
// Weightlessness
SubscribeLocalEvent<GravityAffectedComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<GravityAffectedComponent, EntParentChangedMessage>(OnEntParentChanged);
SubscribeLocalEvent<GravityAffectedComponent, PhysicsBodyTypeChangedEvent>(OnBodyTypeChanged);
// Alerts
SubscribeLocalEvent<AlertSyncEvent>(OnAlertsSync);
SubscribeLocalEvent<AlertsComponent, WeightlessnessChangedEvent>(OnWeightlessnessChanged);
SubscribeLocalEvent<AlertsComponent, EntParentChangedMessage>(OnAlertsParentChange);
// Impulse
SubscribeLocalEvent<GravityAffectedComponent, ShooterImpulseEvent>(OnShooterImpulse);
SubscribeLocalEvent<GravityAffectedComponent, ThrowerImpulseEvent>(OnThrowerImpulse);
GravityQuery = GetEntityQuery<GravityComponent>();
_weightlessQuery = GetEntityQuery<GravityAffectedComponent>();
_physicsQuery = GetEntityQuery<PhysicsComponent>();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateShake();
}
public bool IsWeightless(Entity<GravityAffectedComponent?> entity)
{
// If we can be weightless and are weightless, return true, otherwise return false
return _weightlessQuery.Resolve(entity, ref entity.Comp, false) && entity.Comp.Weightless;
}
private bool GetWeightless(Entity<GravityAffectedComponent, PhysicsComponent?> entity)
{
if (!_physicsQuery.Resolve(entity, ref entity.Comp2, false))
return false;
if (TryComp<MovementIgnoreGravityComponent>(uid, out var ignoreGravityComponent))
return ignoreGravityComponent.Weightless;
if (entity.Comp2.BodyType is BodyType.Static or BodyType.Kinematic)
return false;
var ev = new IsWeightlessEvent(uid);
RaiseLocalEvent(uid, ref ev);
// Check if something other than the grid or map is overriding our gravity
var ev = new IsWeightlessEvent();
RaiseLocalEvent(entity, ref ev);
if (ev.Handled)
return ev.IsWeightless;
if (!Resolve(uid, ref xform))
return true;
return !EntityGridOrMapHaveGravity(entity.Owner);
}
// If grid / map has gravity
if (EntityGridOrMapHaveGravity((uid, xform)))
return false;
/// <summary>
/// Refreshes weightlessness status, needs to be called anytime it would change.
/// </summary>
/// <param name="entity">The entity we are updating the weightless status of</param>
public void RefreshWeightless(Entity<GravityAffectedComponent?> entity)
{
if (!_weightlessQuery.Resolve(entity, ref entity.Comp))
return;
return true;
UpdateWeightless(entity!);
}
/// <summary>
/// Overload of <see cref="RefreshWeightless(Entity{GravityAffectedComponent?})"/> which also takes a bool for the weightlessness value we want to change to.
/// This method is LOAD BEARING for UninitializedSaveTest. DO NOT REMOVE IT.
/// </summary>
/// <param name="entity">The entity we are updating the weightless status of</param>
/// <param name="weightless">The weightless value we are trying to change to, helps avoid needless networking</param>
public void RefreshWeightless(Entity<GravityAffectedComponent?> entity, bool weightless)
{
if (!_weightlessQuery.Resolve(entity, ref entity.Comp))
return;
// Only update if we're changing our weightless status
if (entity.Comp.Weightless == weightless)
return;
UpdateWeightless(entity!);
}
private void UpdateWeightless(Entity<GravityAffectedComponent> entity)
{
var newWeightless = GetWeightless(entity);
// Don't network or raise events if it's not changing
if (newWeightless == entity.Comp.Weightless)
return;
entity.Comp.Weightless = newWeightless;
Dirty(entity);
var ev = new WeightlessnessChangedEvent(entity.Comp.Weightless);
RaiseLocalEvent(entity, ref ev);
}
private void OnMapInit(Entity<GravityAffectedComponent> entity, ref MapInitEvent args)
{
RefreshWeightless((entity.Owner, entity.Comp));
}
private void OnWeightlessnessChanged(Entity<AlertsComponent> entity, ref WeightlessnessChangedEvent args)
{
if (args.Weightless)
_alerts.ShowAlert(entity, WeightlessAlert);
else
_alerts.ClearAlert(entity, WeightlessAlert);
}
private void OnEntParentChanged(Entity<GravityAffectedComponent> entity, ref EntParentChangedMessage args)
{
// If we've moved but are still on the same grid, then don't do anything.
if (args.OldParent == args.Transform.GridUid)
return;
RefreshWeightless((entity.Owner, entity.Comp), !EntityGridOrMapHaveGravity((entity, args.Transform)));
}
private void OnBodyTypeChanged(Entity<GravityAffectedComponent> entity, ref PhysicsBodyTypeChangedEvent args)
{
// No need to update weightlessness if we're not weightless and we're a body type that can't be weightless
if (args.New is BodyType.Static or BodyType.Kinematic && entity.Comp.Weightless == false)
return;
RefreshWeightless((entity.Owner, entity.Comp));
}
/// <summary>
@@ -51,11 +161,10 @@ namespace Content.Shared.Gravity
{
entity.Comp ??= Transform(entity);
return _gravityQuery.HasComp(entity.Comp.GridUid) ||
_gravityQuery.HasComp(entity.Comp.MapUid);
return GravityQuery.HasComp(entity.Comp.GridUid) ||
GravityQuery.HasComp(entity.Comp.MapUid);
}
/// <summary>
/// Checks if a given entity is currently standing on a grid or map that has gravity of some kind.
/// </summary>
@@ -63,88 +172,42 @@ namespace Content.Shared.Gravity
{
entity.Comp ??= Transform(entity);
return _gravityQuery.TryComp(entity.Comp.GridUid, out var gravity) && gravity.Enabled ||
_gravityQuery.TryComp(entity.Comp.MapUid, out var mapGravity) && mapGravity.Enabled;
// DO NOT SET TO WEIGHTLESS IF THEY'RE IN NULL-SPACE
// TODO: If entities actually properly pause when leaving PVS rather than entering null-space this can probably go.
if (entity.Comp.MapID == MapId.Nullspace)
return true;
return GravityQuery.TryComp(entity.Comp.GridUid, out var gravity) && gravity.Enabled ||
GravityQuery.TryComp(entity.Comp.MapUid, out var mapGravity) && mapGravity.Enabled;
}
public override void Initialize()
private void OnGravityChange(ref GravityChangedEvent args)
{
base.Initialize();
SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
SubscribeLocalEvent<AlertSyncEvent>(OnAlertsSync);
SubscribeLocalEvent<AlertsComponent, EntParentChangedMessage>(OnAlertsParentChange);
SubscribeLocalEvent<GravityChangedEvent>(OnGravityChange);
SubscribeLocalEvent<GravityComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<GravityComponent, ComponentHandleState>(OnHandleState);
_gravityQuery = GetEntityQuery<GravityComponent>();
}
public override void Update(float frameTime)
var gravity = AllEntityQuery<GravityAffectedComponent, TransformComponent>();
while(gravity.MoveNext(out var uid, out var weightless, out var xform))
{
base.Update(frameTime);
UpdateShake();
}
private void OnHandleState(EntityUid uid, GravityComponent component, ref ComponentHandleState args)
{
if (args.Current is not GravityComponentState state)
return;
if (component.EnabledVV == state.Enabled)
return;
component.EnabledVV = state.Enabled;
var ev = new GravityChangedEvent(uid, component.EnabledVV);
RaiseLocalEvent(uid, ref ev, true);
}
private void OnGetState(EntityUid uid, GravityComponent component, ref ComponentGetState args)
{
args.State = new GravityComponentState(component.EnabledVV);
}
private void OnGravityChange(ref GravityChangedEvent ev)
{
var alerts = AllEntityQuery<AlertsComponent, TransformComponent>();
while(alerts.MoveNext(out var uid, out _, out var xform))
{
if (xform.GridUid != ev.ChangedGridIndex)
if (xform.GridUid != args.ChangedGridIndex)
continue;
if (!ev.HasGravity)
{
_alerts.ShowAlert(uid, WeightlessAlert);
}
else
{
_alerts.ClearAlert(uid, WeightlessAlert);
}
RefreshWeightless((uid, weightless), !args.HasGravity);
}
}
private void OnAlertsSync(AlertSyncEvent ev)
{
if (IsWeightless(ev.Euid))
{
_alerts.ShowAlert(ev.Euid, WeightlessAlert);
}
else
{
_alerts.ClearAlert(ev.Euid, WeightlessAlert);
}
}
private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref EntParentChangedMessage args)
{
if (IsWeightless(uid))
{
_alerts.ShowAlert(uid, WeightlessAlert);
}
else
{
_alerts.ClearAlert(uid, WeightlessAlert);
}
}
private void OnGridInit(GridInitializeEvent ev)
{
@@ -161,11 +224,31 @@ namespace Content.Shared.Gravity
Enabled = enabled;
}
}
private void OnThrowerImpulse(Entity<GravityAffectedComponent> entity, ref ThrowerImpulseEvent args)
{
args.Push = true;
}
private void OnShooterImpulse(Entity<GravityAffectedComponent> entity, ref ShooterImpulseEvent args)
{
args.Push = true;
}
}
/// <summary>
/// Raised to determine if an entity's weightlessness is being overwritten by a component or item with a component.
/// </summary>
/// <param name="IsWeightless">Whether we should be weightless</param>
/// <param name="Handled">Whether something is trying to override our weightlessness</param>
[ByRefEvent]
public record struct IsWeightlessEvent(EntityUid Entity, bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent
public record struct IsWeightlessEvent(bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent
{
SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET;
}
}
/// <summary>
/// Raised on an entity when their weightless status changes.
/// </summary>
[ByRefEvent]
public readonly record struct WeightlessnessChangedEvent(bool Weightless);

View File

@@ -1,34 +1,17 @@
using Content.Shared.Clothing;
using Content.Shared.Gravity;
using Content.Shared.Inventory;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Serialization;
namespace Content.Shared.Movement.Components
{
/// <summary>
/// Ignores gravity entirely.
/// </summary>
[RegisterComponent, NetworkedComponent]
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class MovementIgnoreGravityComponent : Component
{
/// <summary>
/// Whether or not gravity is on or off for this object.
/// Whether gravity is on or off for this object. This will always override the current Gravity State.
/// </summary>
[DataField("gravityState")] public bool Weightless = false;
}
[NetSerializable, Serializable]
public sealed class MovementIgnoreGravityComponentState : ComponentState
{
[DataField, AutoNetworkedField]
public bool Weightless;
public MovementIgnoreGravityComponentState(MovementIgnoreGravityComponent component)
{
Weightless = component.Weightless;
}
}
}

View File

@@ -84,7 +84,7 @@ public sealed class FrictionContactsSystem : EntitySystem
var frictionNoInput = 0.0f;
var acceleration = 0.0f;
var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(entity, physicsComponent);
var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(entity.Owner);
var remove = true;
var entries = 0;

View File

@@ -1,33 +1,35 @@
using Content.Shared.Movement.Components;
using Content.Shared.Gravity;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Events;
using Robust.Shared.GameStates;
namespace Content.Shared.Movement.Systems;
public sealed class MovementIgnoreGravitySystem : EntitySystem
{
[Dependency] SharedGravitySystem _gravity = default!;
public override void Initialize()
{
SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentGetState>(GetState);
SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentHandleState>(HandleState);
SubscribeLocalEvent<MovementAlwaysTouchingComponent, CanWeightlessMoveEvent>(OnWeightless);
SubscribeLocalEvent<MovementIgnoreGravityComponent, IsWeightlessEvent>(OnIsWeightless);
SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentStartup>(OnComponentStartup);
}
private void OnWeightless(EntityUid uid, MovementAlwaysTouchingComponent component, ref CanWeightlessMoveEvent args)
private void OnWeightless(Entity<MovementAlwaysTouchingComponent> entity, ref CanWeightlessMoveEvent args)
{
args.CanMove = true;
}
private void HandleState(EntityUid uid, MovementIgnoreGravityComponent component, ref ComponentHandleState args)
private void OnIsWeightless(Entity<MovementIgnoreGravityComponent> entity, ref IsWeightlessEvent args)
{
if (args.Next is null)
return;
// We don't check if the event has been handled as this component takes precedent over other things.
component.Weightless = ((MovementIgnoreGravityComponentState) args.Next).Weightless;
args.IsWeightless = entity.Comp.Weightless;
args.Handled = true;
}
private void GetState(EntityUid uid, MovementIgnoreGravityComponent component, ref ComponentGetState args)
private void OnComponentStartup(Entity<MovementIgnoreGravityComponent> entity, ref ComponentStartup args)
{
args.State = new MovementIgnoreGravityComponentState(component);
EnsureComp<GravityAffectedComponent>(entity);
_gravity.RefreshWeightless(entity.Owner, entity.Comp.Weightless);
}
}

View File

@@ -195,8 +195,7 @@ public abstract partial class SharedMoverController : VirtualController
}
// If the body is in air but isn't weightless then it can't move
// TODO: MAKE ISWEIGHTLESS EVENT BASED
var weightless = _gravity.IsWeightless(uid, physicsComponent, xform);
var weightless = _gravity.IsWeightless(uid);
var inAirHelpless = false;
if (physicsComponent.BodyStatus != BodyStatus.OnGround && !CanMoveInAirQuery.HasComponent(uid))
@@ -624,8 +623,7 @@ public abstract partial class SharedMoverController : VirtualController
if (!TryComp<PhysicsComponent>(ent, out var physicsComponent) || !XformQuery.TryComp(ent, out var xform))
return;
// TODO: Make IsWeightless event based!!!
if (physicsComponent.BodyStatus != BodyStatus.OnGround || _gravity.IsWeightless(ent, physicsComponent, xform))
if (physicsComponent.BodyStatus != BodyStatus.OnGround || _gravity.IsWeightless(ent.Owner))
args.Modifier *= ent.Comp.BaseWeightlessFriction;
else
args.Modifier *= ent.Comp.BaseFriction;

View File

@@ -85,7 +85,7 @@ public sealed class SpeedModifierContactsSystem : EntitySystem
var sprintSpeed = 0.0f;
// Cache the result of the airborne check, as it's expensive and independent of contacting entities, hence need only be done once.
var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid, physicsComponent);
var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid);
bool remove = true;
var entries = 0;

View File

@@ -216,7 +216,7 @@ public abstract class SharedConveyorController : VirtualController
return true;
if (physics.BodyStatus == BodyStatus.InAir ||
_gravity.IsWeightless(entity, physics, xform))
_gravity.IsWeightless(entity.Owner))
{
return true;
}

View File

@@ -139,7 +139,7 @@ public sealed class StepTriggerSystem : EntitySystem
// and the entity is flying or currently weightless
// Makes sense simulation wise to have this be part of steptrigger directly IMO
if (!component.IgnoreWeightless && TryComp<PhysicsComponent>(otherUid, out var physics) &&
(physics.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(otherUid, physics)))
(physics.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(otherUid)))
return false;
var msg = new StepTriggerAttemptEvent { Source = uid, Tripper = otherUid };

View File

@@ -242,7 +242,7 @@ public sealed class ThrowingSystem : EntitySystem
RaiseLocalEvent(user.Value, ref pushEv);
const float massLimit = 5f;
if (pushEv.Push || _gravity.IsWeightless(user.Value))
if (pushEv.Push)
_physics.ApplyLinearImpulse(user.Value, -impulseVector / physics.Mass * pushbackRatio * MathF.Min(massLimit, physics.Mass), body: userPhysics);
}
}

View File

@@ -391,7 +391,7 @@ public abstract partial class SharedGunSystem : EntitySystem
var shooterEv = new ShooterImpulseEvent();
RaiseLocalEvent(user, ref shooterEv);
if (shooterEv.Push || _gravity.IsWeightless(user, userPhysics))
if (shooterEv.Push)
CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics);
}

View File

@@ -9,6 +9,7 @@
noRot: true
drawdepth: Mobs
- type: MobCollision
- type: GravityAffected
- type: Physics
bodyType: KinematicController
- type: Fixtures

View File

@@ -6,11 +6,13 @@
- type: Clickable
- type: InteractionOutline
- type: MovementIgnoreGravity
weightless: true
- type: Sprite
sprite: Objects/Fun/immovable_rod.rsi
state: icon
noRot: false
- type: ImmovableRod
- type: GravityAffected
- type: Physics
bodyType: Dynamic
linearDamping: 0
@@ -78,8 +80,6 @@
damage:
types:
Blunt: 190
- type: MovementIgnoreGravity
gravityState: true
- type: InputMover
- type: MovementSpeedModifier
baseWeightlessAcceleration: 5

View File

@@ -64,6 +64,7 @@
- type: Pullable
- type: Physics
bodyType: KinematicController
- type: GravityAffected
- type: Clickable
- type: WiresPanel
- type: Fixtures

View File

@@ -24,6 +24,7 @@
soundHit:
collection: MetalThud
- type: CollisionWake
- type: GravityAffected
- type: Physics
bodyType: Dynamic
fixedRotation: false

View File

@@ -7,6 +7,7 @@
- type: Transform
anchored: true
- type: Clickable
- type: GravityAffected
- type: Physics
bodyType: Static
- type: Fixtures