components:
- type: Physics
bodyType: Dynamic
+ - type: GravityAffected
- type: Fixtures
fixtures:
fix1:
- type: Alerts
- type: Physics
bodyType: Dynamic
+ - type: GravityAffected
- type: entity
name: WeightlessGravityGeneratorDummy
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.
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);
});
});
- type: Stripping
- type: Puller
- type: Physics
+ - type: GravityAffected
- type: Tag
tags:
- CanPilot
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;
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"))
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);
/// </summary>
public void RefreshGravity(EntityUid uid, GravityComponent? gravity = null)
{
- if (!Resolve(uid, ref gravity))
+ if (!GravityQuery.Resolve(uid, ref gravity))
return;
if (gravity.Inherent)
/// </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)
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)
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);
+ }
}
using Content.Shared.Alert;
+using Content.Shared.Inventory;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
/// </summary>
[DataField]
public bool RequiresGrid = true;
-
- /// <summary>
- /// Slot the clothing has to be worn in to work.
- /// </summary>
- [DataField]
- public string Slot = "shoes";
}
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)
if (TryComp<MovedByPressureComponent>(user, out var moved))
moved.Enabled = !state;
+ _gravity.RefreshWeightless(user, !state);
+
if (state)
_alerts.ShowAlert(user, ent.Comp.MagbootsAlert);
else
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))
// 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);
/// <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";
}
--- /dev/null
+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;
+}
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;
}
}
/// </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>
/// </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 (transform.GridUid != args.ChangedGridIndex)
- continue;
+ if (entity.Comp.CanFloat == args.Weightless)
+ return;
- floating.CanFloat = !args.HasGravity;
- Dirty(uid, floating);
+ entity.Comp.CanFloat = CanFloat(entity);
+ Dirty(entity);
- 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);
}
}
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
{
- public abstract partial class SharedGravitySystem : EntitySystem
+ [Dependency] protected readonly IGameTiming Timing = default!;
+ [Dependency] private readonly AlertsSystem _alerts = default!;
+
+ public static readonly ProtoId<AlertPrototype> WeightlessAlert = "Weightless";
+
+ protected EntityQuery<GravityComponent> GravityQuery;
+ private EntityQuery<GravityAffectedComponent> _weightlessQuery;
+ private EntityQuery<PhysicsComponent> _physicsQuery;
+
+ public override void Initialize()
{
- [Dependency] protected readonly IGameTiming Timing = default!;
- [Dependency] private readonly AlertsSystem _alerts = default!;
+ base.Initialize();
+ // Grid Gravity
+ SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
+ SubscribeLocalEvent<GravityChangedEvent>(OnGravityChange);
+
+ // 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 static readonly ProtoId<AlertPrototype> WeightlessAlert = "Weightless";
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+ UpdateShake();
+ }
- private EntityQuery<GravityComponent> _gravityQuery;
+ 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;
+ }
- public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null)
- {
- Resolve(uid, ref body, false);
+ private bool GetWeightless(Entity<GravityAffectedComponent, PhysicsComponent?> entity)
+ {
+ if (!_physicsQuery.Resolve(entity, ref entity.Comp2, false))
+ return false;
- if ((body?.BodyType & (BodyType.Static | BodyType.Kinematic)) != 0)
- return false;
+ if (entity.Comp2.BodyType is BodyType.Static or BodyType.Kinematic)
+ return false;
- if (TryComp<MovementIgnoreGravityComponent>(uid, out var ignoreGravityComponent))
- return ignoreGravityComponent.Weightless;
+ // 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;
- var ev = new IsWeightlessEvent(uid);
- RaiseLocalEvent(uid, ref ev);
- if (ev.Handled)
- return ev.IsWeightless;
+ return !EntityGridOrMapHaveGravity(entity.Owner);
+ }
- if (!Resolve(uid, ref xform))
- return true;
+ /// <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;
- // If grid / map has gravity
- if (EntityGridOrMapHaveGravity((uid, xform)))
- return false;
+ UpdateWeightless(entity!);
+ }
- return true;
- }
+ /// <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;
- /// <summary>
- /// Checks if a given entity is currently standing on a grid or map that supports having gravity at all.
- /// </summary>
- public bool EntityOnGravitySupportingGridOrMap(Entity<TransformComponent?> entity)
- {
- entity.Comp ??= Transform(entity);
+ // Only update if we're changing our weightless status
+ if (entity.Comp.Weightless == weightless)
+ return;
- return _gravityQuery.HasComp(entity.Comp.GridUid) ||
- _gravityQuery.HasComp(entity.Comp.MapUid);
- }
+ UpdateWeightless(entity!);
+ }
+ private void UpdateWeightless(Entity<GravityAffectedComponent> entity)
+ {
+ var newWeightless = GetWeightless(entity);
- /// <summary>
- /// Checks if a given entity is currently standing on a grid or map that has gravity of some kind.
- /// </summary>
- public bool EntityGridOrMapHaveGravity(Entity<TransformComponent?> entity)
- {
- entity.Comp ??= Transform(entity);
+ // Don't network or raise events if it's not changing
+ if (newWeightless == entity.Comp.Weightless)
+ return;
- return _gravityQuery.TryComp(entity.Comp.GridUid, out var gravity) && gravity.Enabled ||
- _gravityQuery.TryComp(entity.Comp.MapUid, out var mapGravity) && mapGravity.Enabled;
- }
+ entity.Comp.Weightless = newWeightless;
+ Dirty(entity);
- public override void Initialize()
- {
- 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>();
- }
+ var ev = new WeightlessnessChangedEvent(entity.Comp.Weightless);
+ RaiseLocalEvent(entity, ref ev);
+ }
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
- UpdateShake();
- }
+ private void OnMapInit(Entity<GravityAffectedComponent> entity, ref MapInitEvent args)
+ {
+ RefreshWeightless((entity.Owner, entity.Comp));
+ }
- 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 OnWeightlessnessChanged(Entity<AlertsComponent> entity, ref WeightlessnessChangedEvent args)
+ {
+ if (args.Weightless)
+ _alerts.ShowAlert(entity, WeightlessAlert);
+ else
+ _alerts.ClearAlert(entity, WeightlessAlert);
+ }
- private void OnGetState(EntityUid uid, GravityComponent component, ref ComponentGetState args)
- {
- args.State = new GravityComponentState(component.EnabledVV);
- }
+ 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;
- 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)
- continue;
-
- if (!ev.HasGravity)
- {
- _alerts.ShowAlert(uid, WeightlessAlert);
- }
- else
- {
- _alerts.ClearAlert(uid, WeightlessAlert);
- }
- }
- }
+ RefreshWeightless((entity.Owner, entity.Comp), !EntityGridOrMapHaveGravity((entity, args.Transform)));
+ }
- private void OnAlertsSync(AlertSyncEvent ev)
- {
- if (IsWeightless(ev.Euid))
- {
- _alerts.ShowAlert(ev.Euid, WeightlessAlert);
- }
- else
- {
- _alerts.ClearAlert(ev.Euid, WeightlessAlert);
- }
- }
+ 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;
- private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref EntParentChangedMessage args)
- {
- if (IsWeightless(uid))
- {
- _alerts.ShowAlert(uid, WeightlessAlert);
- }
- else
- {
- _alerts.ClearAlert(uid, WeightlessAlert);
- }
- }
+ RefreshWeightless((entity.Owner, entity.Comp));
+ }
+
+ /// <summary>
+ /// Checks if a given entity is currently standing on a grid or map that supports having gravity at all.
+ /// </summary>
+ public bool EntityOnGravitySupportingGridOrMap(Entity<TransformComponent?> entity)
+ {
+ entity.Comp ??= Transform(entity);
+
+ 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>
+ public bool EntityGridOrMapHaveGravity(Entity<TransformComponent?> entity)
+ {
+ entity.Comp ??= Transform(entity);
+
+ // 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;
- private void OnGridInit(GridInitializeEvent ev)
+ return GravityQuery.TryComp(entity.Comp.GridUid, out var gravity) && gravity.Enabled ||
+ GravityQuery.TryComp(entity.Comp.MapUid, out var mapGravity) && mapGravity.Enabled;
+ }
+
+ private void OnGravityChange(ref GravityChangedEvent args)
+ {
+ var gravity = AllEntityQuery<GravityAffectedComponent, TransformComponent>();
+ while(gravity.MoveNext(out var uid, out var weightless, out var xform))
{
- EnsureComp<GravityComponent>(ev.EntityUid);
+ if (xform.GridUid != args.ChangedGridIndex)
+ continue;
+
+ RefreshWeightless((uid, weightless), !args.HasGravity);
}
+ }
- [Serializable, NetSerializable]
- private sealed class GravityComponentState : ComponentState
- {
- public bool Enabled { get; }
+ 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);
+ }
- public GravityComponentState(bool enabled)
- {
- Enabled = enabled;
- }
+ private void OnGridInit(GridInitializeEvent ev)
+ {
+ EnsureComp<GravityComponent>(ev.EntityUid);
+ }
+
+ [Serializable, NetSerializable]
+ private sealed class GravityComponentState : ComponentState
+ {
+ public bool Enabled { get; }
+
+ public GravityComponentState(bool enabled)
+ {
+ Enabled = enabled;
}
}
- [ByRefEvent]
- public record struct IsWeightlessEvent(EntityUid Entity, bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent
+ private void OnThrowerImpulse(Entity<GravityAffectedComponent> entity, ref ThrowerImpulseEvent args)
+ {
+ args.Push = true;
+ }
+
+ private void OnShooterImpulse(Entity<GravityAffectedComponent> entity, ref ShooterImpulseEvent args)
{
- SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET;
+ 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(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);
-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;
- }
}
}
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;
-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);
}
}
}
// 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))
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;
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;
return true;
if (physics.BodyStatus == BodyStatus.InAir ||
- _gravity.IsWeightless(entity, physics, xform))
+ _gravity.IsWeightless(entity.Owner))
{
return true;
}
// 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 };
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);
}
}
var shooterEv = new ShooterImpulseEvent();
RaiseLocalEvent(user, ref shooterEv);
- if (shooterEv.Push || _gravity.IsWeightless(user, userPhysics))
+ if (shooterEv.Push)
CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics);
}
noRot: true
drawdepth: Mobs
- type: MobCollision
+ - type: GravityAffected
- type: Physics
bodyType: KinematicController
- type: Fixtures
- 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
damage:
types:
Blunt: 190
- - type: MovementIgnoreGravity
- gravityState: true
- type: InputMover
- type: MovementSpeedModifier
baseWeightlessAcceleration: 5
- type: Pullable
- type: Physics
bodyType: KinematicController
+ - type: GravityAffected
- type: Clickable
- type: WiresPanel
- type: Fixtures
soundHit:
collection: MetalThud
- type: CollisionWake
+ - type: GravityAffected
- type: Physics
bodyType: Dynamic
fixedRotation: false
- type: Transform
anchored: true
- type: Clickable
+ - type: GravityAffected
- type: Physics
bodyType: Static
- type: Fixtures