diff --git a/Content.Client/_DV/Abilities/CrawlUnderObjectsSystem.cs b/Content.Client/_DV/Abilities/CrawlUnderObjectsSystem.cs deleted file mode 100644 index 77c51a45dc3..00000000000 --- a/Content.Client/_DV/Abilities/CrawlUnderObjectsSystem.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Content.Shared._DV.Abilities; -using Content.Shared.Popups; -using Robust.Client.GameObjects; -using DrawDepth = Content.Shared.DrawDepth.DrawDepth; - -namespace Content.Client._DV.Abilities; - -public sealed partial class HideUnderTableAbilitySystem : SharedCrawlUnderObjectsSystem -{ - [Dependency] private readonly AppearanceSystem _appearance = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAppearanceChange); - } - - private void OnAppearanceChange(EntityUid uid, - CrawlUnderObjectsComponent component, - AppearanceChangeEvent args) - { - if (!TryComp(uid, out var sprite)) - return; - - _appearance.TryGetData(uid, SneakMode.Enabled, out bool enabled); - if (enabled) - { - if (component.OriginalDrawDepth != null) - return; - - component.OriginalDrawDepth = sprite.DrawDepth; - sprite.DrawDepth = (int) DrawDepth.SmallMobs; - } - else - { - if (component.OriginalDrawDepth == null) - return; - - sprite.DrawDepth = (int) component.OriginalDrawDepth; - component.OriginalDrawDepth = null; - } - } -} diff --git a/Content.Client/_DV/Abilities/DrawDepthVisualizerComponent.cs b/Content.Client/_DV/Abilities/DrawDepthVisualizerComponent.cs new file mode 100644 index 00000000000..0e73ea0233c --- /dev/null +++ b/Content.Client/_DV/Abilities/DrawDepthVisualizerComponent.cs @@ -0,0 +1,25 @@ +using DrawDepth = Content.Shared.DrawDepth.DrawDepth; + +namespace Content.Client._DV.Abilities; + +/// +/// Changes the sprite's draw depth when some appearance data becomes true, reverting it when false. +/// +[RegisterComponent] +public sealed partial class DrawDepthVisualizerComponent : Component +{ + /// + /// Appearance key to check. + /// + [DataField(required: true)] + public Enum Key; + + /// + /// The draw depth to set the sprite to when the appearance data is true. + /// + [DataField(required: true)] + public DrawDepth Depth; + + [DataField] + public int? OriginalDrawDepth; +} diff --git a/Content.Client/_DV/Abilities/DrawDepthVisualizerSystem.cs b/Content.Client/_DV/Abilities/DrawDepthVisualizerSystem.cs new file mode 100644 index 00000000000..fb277b20757 --- /dev/null +++ b/Content.Client/_DV/Abilities/DrawDepthVisualizerSystem.cs @@ -0,0 +1,40 @@ +using Content.Shared.DrawDepth; +using Robust.Client.GameObjects; + +namespace Content.Client._DV.Abilities; + +/// +/// Changes a sprite's draw depth when some appearance data becomes true. +/// +public sealed class DrawDepthVisualizerSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAppearanceChange); + } + + private void OnAppearanceChange(Entity ent, ref AppearanceChangeEvent args) + { + if (args.Sprite is not {} sprite || !args.AppearanceData.TryGetValue(ent.Comp.Key, out var value)) + return; + + if (value is true) + { + if (ent.Comp.OriginalDrawDepth != null) + return; + + ent.Comp.OriginalDrawDepth = sprite.DrawDepth; + sprite.DrawDepth = (int) ent.Comp.Depth; + } + else + { + if (ent.Comp.OriginalDrawDepth is not {} original) + return; + + sprite.DrawDepth = original; + ent.Comp.OriginalDrawDepth = null; + } + } +} diff --git a/Content.Server/Entry/IgnoredComponents.cs b/Content.Server/Entry/IgnoredComponents.cs index 04c45662272..ad2ba46b5fa 100644 --- a/Content.Server/Entry/IgnoredComponents.cs +++ b/Content.Server/Entry/IgnoredComponents.cs @@ -19,7 +19,8 @@ public static class IgnoredComponents "InventorySlots", "LightFade", "HolidayRsiSwap", - "OptionsVisualizer" + "OptionsVisualizer", + "DrawDepthVisualizer" // DeltaV }; } } diff --git a/Content.Server/_DV/Abilities/CrawlUnderObjectsSystem.cs b/Content.Server/_DV/Abilities/CrawlUnderObjectsSystem.cs deleted file mode 100644 index 983d97de9bb..00000000000 --- a/Content.Server/_DV/Abilities/CrawlUnderObjectsSystem.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Climbing.Components; -using Content.Shared.Climbing.Events; -using Content.Shared._DV.Abilities; -using Content.Shared.Maps; -using Content.Shared.Movement.Systems; -using Content.Shared.Physics; -using Robust.Server.GameObjects; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Systems; - -namespace Content.Server._DV.Abilities; - -public sealed partial class CrawlUnderObjectsSystem : SharedCrawlUnderObjectsSystem -{ - [Dependency] private readonly AppearanceSystem _appearance = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly MovementSpeedModifierSystem _movespeed = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly TurfSystem _turf = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnAbilityToggle); - SubscribeLocalEvent(OnAttemptClimb); - SubscribeLocalEvent(OnRefreshMovespeed); - } - - private bool IsOnCollidingTile(EntityUid uid) - { - var xform = Transform(uid); - var tile = xform.Coordinates.GetTileRef(); - if (tile == null) - return false; - - return _turf.IsTileBlocked(tile.Value, CollisionGroup.MobMask); - } - - private void OnInit(EntityUid uid, CrawlUnderObjectsComponent component, ComponentInit args) - { - if (component.ToggleHideAction != null) - return; - - _actionsSystem.AddAction(uid, ref component.ToggleHideAction, component.ActionProto); - } - - private bool EnableSneakMode(EntityUid uid, CrawlUnderObjectsComponent component) - { - if (component.Enabled - || (TryComp(uid, out var climbing) - && climbing.IsClimbing == true)) - return false; - - component.Enabled = true; - Dirty(uid, component); - RaiseLocalEvent(uid, new CrawlingUpdatedEvent(component.Enabled)); - - if (TryComp(uid, out FixturesComponent? fixtureComponent)) - { - foreach (var (key, fixture) in fixtureComponent.Fixtures) - { - var newMask = (fixture.CollisionMask - & (int)~CollisionGroup.HighImpassable - & (int)~CollisionGroup.MidImpassable) - | (int)CollisionGroup.InteractImpassable; - if (fixture.CollisionMask == newMask) - continue; - - component.ChangedFixtures.Add((key, fixture.CollisionMask)); - _physics.SetCollisionMask(uid, - key, - fixture, - newMask, - manager: fixtureComponent); - } - } - return true; - } - - private bool DisableSneakMode(EntityUid uid, CrawlUnderObjectsComponent component) - { - if (!component.Enabled - || IsOnCollidingTile(uid) - || (TryComp(uid, out var climbing) - && climbing.IsClimbing == true)) - return false; - - component.Enabled = false; - Dirty(uid, component); - RaiseLocalEvent(uid, new CrawlingUpdatedEvent(component.Enabled)); - - // Restore normal collision masks - if (TryComp(uid, out FixturesComponent? fixtureComponent)) - foreach (var (key, originalMask) in component.ChangedFixtures) - if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture)) - _physics.SetCollisionMask(uid, key, fixture, originalMask, fixtureComponent); - - component.ChangedFixtures.Clear(); - return true; - } - - private void OnAbilityToggle(EntityUid uid, - CrawlUnderObjectsComponent component, - ToggleCrawlingStateEvent args) - { - if (args.Handled) - return; - - bool result; - - if (component.Enabled) - result = DisableSneakMode(uid, component); - else - result = EnableSneakMode(uid, component); - - if (TryComp(uid, out var app)) - _appearance.SetData(uid, SneakMode.Enabled, component.Enabled, app); - - _movespeed.RefreshMovementSpeedModifiers(uid); - - args.Handled = result; - } - - private void OnAttemptClimb(EntityUid uid, - CrawlUnderObjectsComponent component, - AttemptClimbEvent args) - { - if (component.Enabled == true) - args.Cancelled = true; - } - - private void OnRefreshMovespeed(EntityUid uid, CrawlUnderObjectsComponent component, RefreshMovementSpeedModifiersEvent args) - { - if (component.Enabled) - args.ModifySpeed(component.SneakSpeedModifier, component.SneakSpeedModifier); - } -} diff --git a/Content.Shared/_DV/Abilities/CrawlUnderObjectsComponent.cs b/Content.Shared/_DV/Abilities/CrawlUnderObjectsComponent.cs index 90a2aedd405..779f7759157 100644 --- a/Content.Shared/_DV/Abilities/CrawlUnderObjectsComponent.cs +++ b/Content.Shared/_DV/Abilities/CrawlUnderObjectsComponent.cs @@ -1,47 +1,43 @@ using Content.Shared.Actions; -using DrawDepth = Content.Shared.DrawDepth.DrawDepth; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared._DV.Abilities; +/// +/// Gives the player an action to sneak under tables at a slower move speed. +/// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class CrawlUnderObjectsComponent : Component { [DataField] public EntityUid? ToggleHideAction; - [DataField] - public EntProtoId? ActionProto; + [DataField(required: true)] + public EntProtoId ActionProto; - [DataField] - public bool Enabled = false; + [DataField, AutoNetworkedField] + public bool Enabled; /// /// List of fixtures that had their collision mask changed. /// Required for re-adding the collision mask. /// - [DataField, AutoNetworkedField] - public List<(string key, int originalMask)> ChangedFixtures = new(); - [DataField] - public int? OriginalDrawDepth; + public List<(string key, int originalMask)> ChangedFixtures = new(); [DataField] public float SneakSpeedModifier = 0.7f; } [Serializable, NetSerializable] -public enum SneakMode : byte +public enum SneakingVisuals : byte { - Enabled + Sneaking } -public sealed partial class ToggleCrawlingStateEvent : InstantActionEvent { } +public sealed partial class ToggleCrawlingStateEvent : InstantActionEvent; -[Serializable, NetSerializable] -public sealed partial class CrawlingUpdatedEvent(bool enabled = false) : EventArgs -{ - public readonly bool Enabled = enabled; -} +[ByRefEvent] +public readonly record struct CrawlingUpdatedEvent(bool Enabled, CrawlUnderObjectsComponent Comp); diff --git a/Content.Shared/_DV/Abilities/CrawlUnderObjectsSystem.cs b/Content.Shared/_DV/Abilities/CrawlUnderObjectsSystem.cs new file mode 100644 index 00000000000..881418f695d --- /dev/null +++ b/Content.Shared/_DV/Abilities/CrawlUnderObjectsSystem.cs @@ -0,0 +1,167 @@ +using Content.Shared.Actions; +using Content.Shared.Climbing.Components; +using Content.Shared.Climbing.Events; +using Content.Shared.Maps; +using Content.Shared.Mobs; +using Content.Shared.Movement.Systems; +using Content.Shared.Physics; +using Content.Shared.Popups; +using Content.Shared.Standing; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Systems; + +namespace Content.Shared._DV.Abilities; + +/// +/// Not to be confused with laying down, lets you move under tables. +/// +public sealed class CrawlUnderObjectsSystem : EntitySystem +{ + [Dependency] private readonly MovementSpeedModifierSystem _moveSpeed = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; + [Dependency] private readonly TurfSystem _turf = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnToggleCrawling); + SubscribeLocalEvent(OnAttemptClimb); + SubscribeLocalEvent(CancelWhenSneaking); + SubscribeLocalEvent(CancelWhenSneaking); + SubscribeLocalEvent(OnRefreshMoveSpeed); + SubscribeLocalEvent(OnMobStateChanged); + + SubscribeLocalEvent(OnCrawlingUpdated); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (ent.Comp.ToggleHideAction != null) + return; + + _actions.AddAction(ent, ref ent.Comp.ToggleHideAction, ent.Comp.ActionProto); + } + + private void OnToggleCrawling(Entity ent, ref ToggleCrawlingStateEvent args) + { + if (args.Handled) + return; + + args.Handled = TryToggle(ent); + } + + private void OnAttemptClimb(Entity ent, ref AttemptClimbEvent args) + { + if (ent.Comp.Enabled) + args.Cancelled = true; + } + + private void CancelWhenSneaking(Entity ent, ref TEvent args) where TEvent : CancellableEntityEventArgs + { + if (ent.Comp.Enabled) + args.Cancel(); + } + + private void OnRefreshMoveSpeed(Entity ent, ref RefreshMovementSpeedModifiersEvent args) + { + if (ent.Comp.Enabled) + args.ModifySpeed(ent.Comp.SneakSpeedModifier, ent.Comp.SneakSpeedModifier); + } + + private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + { + if (args.OldMobState != MobState.Alive || !ent.Comp.Enabled) + return; + + // crawling prevents downing, so when you go crit/die stop crawling and force downing + SetEnabled(ent, false); + _standing.Down(ent); + } + + private void OnCrawlingUpdated(Entity ent, ref CrawlingUpdatedEvent args) + { + if (args.Enabled) + { + foreach (var (key, fixture) in ent.Comp.Fixtures) + { + var newMask = (fixture.CollisionMask + & (int)~CollisionGroup.HighImpassable + & (int)~CollisionGroup.MidImpassable) + | (int)CollisionGroup.InteractImpassable; + if (fixture.CollisionMask == newMask) + continue; + + args.Comp.ChangedFixtures.Add((key, fixture.CollisionMask)); + _physics.SetCollisionMask(ent, + key, + fixture, + newMask, + manager: ent.Comp); + } + } + else + { + foreach (var (key, originalMask) in args.Comp.ChangedFixtures) + { + if (ent.Comp.Fixtures.TryGetValue(key, out var fixture)) + _physics.SetCollisionMask(ent, key, fixture, originalMask, ent.Comp); + } + + args.Comp.ChangedFixtures.Clear(); + } + } + + /// + /// Tries to enable or disable sneaking + /// + public bool TrySetEnabled(Entity ent, bool enabled) + { + if (ent.Comp.Enabled == enabled || IsOnCollidingTile(ent) || _standing.IsDown(ent)) + return false; + + if (TryComp(ent, out var climbing) && climbing.IsClimbing) + return false; + + SetEnabled(ent, enabled); + + var msg = Loc.GetString("crawl-under-objects-toggle-" + (enabled ? "on" : "off")); + _popup.PopupPredicted(msg, ent, ent); + + return true; + } + + private void SetEnabled(Entity ent, bool enabled) + { + ent.Comp.Enabled = enabled; + Dirty(ent); + + _appearance.SetData(ent, SneakingVisuals.Sneaking, enabled); + + _moveSpeed.RefreshMovementSpeedModifiers(ent); + + var ev = new CrawlingUpdatedEvent(enabled, ent.Comp); + RaiseLocalEvent(ent, ref ev); + } + + /// + /// Tries to toggle sneaking + /// + public bool TryToggle(Entity ent) + { + return TrySetEnabled(ent, !ent.Comp.Enabled); + } + + private bool IsOnCollidingTile(EntityUid uid) + { + if (Transform(uid).Coordinates.GetTileRef() is not {} tile) + return false; + + return _turf.IsTileBlocked(tile, CollisionGroup.MobMask); + } +} diff --git a/Content.Shared/_DV/Abilities/SharedCrawlUnderObjectsSystem.cs b/Content.Shared/_DV/Abilities/SharedCrawlUnderObjectsSystem.cs deleted file mode 100644 index 43facaad7b7..00000000000 --- a/Content.Shared/_DV/Abilities/SharedCrawlUnderObjectsSystem.cs +++ /dev/null @@ -1,25 +0,0 @@ - -using Content.Shared.Popups; - -namespace Content.Shared._DV.Abilities; -public abstract class SharedCrawlUnderObjectsSystem : EntitySystem -{ - [Dependency] private readonly SharedPopupSystem _popup = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnCrawlingUpdated); - } - - private void OnCrawlingUpdated(EntityUid uid, - CrawlUnderObjectsComponent component, - CrawlingUpdatedEvent args) - { - if (args.Enabled) - _popup.PopupEntity(Loc.GetString("crawl-under-objects-toggle-on"), uid); - else - _popup.PopupEntity(Loc.GetString("crawl-under-objects-toggle-off"), uid); - } -} diff --git a/Resources/Prototypes/_DV/Entities/Mobs/Species/rodentia.yml b/Resources/Prototypes/_DV/Entities/Mobs/Species/rodentia.yml index 1fdd2cc0179..831aec13a97 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/Species/rodentia.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/Species/rodentia.yml @@ -114,6 +114,9 @@ spitDamageThreshold: 3 - type: CrawlUnderObjects actionProto: ActionToggleSneakMode + - type: DrawDepthVisualizer + key: enum.SneakingVisuals.Sneaking + depth: SmallMobs - type: entity save: false