From 6bae52db709bd48ed3d681d45c5a4dc75105ee6b Mon Sep 17 00:00:00 2001 From: Token <56667933+TokenStyle@users.noreply.github.com> Date: Tue, 26 Nov 2024 03:11:51 +0500 Subject: [PATCH] [Port] Laying System (#83) * [Port] Laying System Barebones port of https://github.com/Rxup/space-station-14/pull/745 * aaa * Update GhostSystem.cs * Update GhostSystem.cs * Feature: Back in Round Button * Update CCVars.cs * up1 * up2 * upd * update test --------- Co-authored-by: Zack Backmen Co-authored-by: Trest <144359854+trest100@users.noreply.github.com> * all fixes of laying system at once --------- Co-authored-by: Zack Backmen Co-authored-by: Trest <144359854+trest100@users.noreply.github.com> Co-authored-by: Roudenn <149893554+Roudenn@users.noreply.github.com> * backmen to _CorvaxNext and move to appropriate folders * laying system hotkey on Shift C * let me pass tests --------- Co-authored-by: Trest <144359854+trest100@users.noreply.github.com> Co-authored-by: Zack Backmen Co-authored-by: Roudenn <149893554+Roudenn@users.noreply.github.com> --- Content.Client/Buckle/BuckleSystem.cs | 3 +- Content.Client/Input/ContentContexts.cs | 1 + .../Options/UI/Tabs/KeyRebindTab.xaml.cs | 10 + .../_CorvaxNext/Standing/LayingDownSystem.cs | 155 ++++++++ .../Tests/Buckle/BuckleTest.cs | 16 +- .../NPC/Systems/NPCCombatSystem.Ranged.cs | 2 +- .../EntitySystems/FootprintsSystem.cs | 5 +- .../_CorvaxNext/Standing/LayingDownSystem.cs | 77 ++++ .../Body/Systems/SharedBodySystem.Body.cs | 4 +- Content.Shared/Input/ContentKeyFunctions.cs | 1 + .../Standing/StandingStateComponent.cs | 46 ++- .../Standing/StandingStateSystem.cs | 264 +++++++------ Content.Shared/Stunnable/SharedStunSystem.cs | 25 +- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 8 + Content.Shared/_CorvaxNext/NextVars.cs | 17 + .../Standing/LayingDownComponent.cs | 30 ++ .../Standing/SharedLayingDownSystem.cs | 369 ++++++++++++++++++ .../_corvaxnext/escape-menu/ui/options.ftl | 3 + .../Prototypes/Entities/Mobs/Species/base.yml | 1 + Resources/keybinds.yml | 4 + 20 files changed, 890 insertions(+), 151 deletions(-) create mode 100644 Content.Client/_CorvaxNext/Standing/LayingDownSystem.cs create mode 100644 Content.Server/_CorvaxNext/Standing/LayingDownSystem.cs create mode 100644 Content.Shared/_CorvaxNext/Standing/LayingDownComponent.cs create mode 100644 Content.Shared/_CorvaxNext/Standing/SharedLayingDownSystem.cs diff --git a/Content.Client/Buckle/BuckleSystem.cs b/Content.Client/Buckle/BuckleSystem.cs index 035e1300ca5..a71e4c10a84 100644 --- a/Content.Client/Buckle/BuckleSystem.cs +++ b/Content.Client/Buckle/BuckleSystem.cs @@ -65,7 +65,8 @@ private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref Ap !buckled || args.Sprite == null) { - _rotationVisualizerSystem.SetHorizontalAngle((uid, rotVisuals), rotVisuals.DefaultRotation); + // _CorvaxNext: Laying System + //_rotationVisualizerSystem.SetHorizontalAngle((uid, rotVisuals), rotVisuals.DefaultRotation); return; } diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 0523e70081a..685b79c8cbc 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -91,6 +91,7 @@ public static void SetupContexts(IInputContextContainer contexts) human.AddFunction(ContentKeyFunctions.TargetLeftLeg); // _CorvaxNext: surgery human.AddFunction(ContentKeyFunctions.TargetRightLeg); // _CorvaxNext: surgery human.AddFunction(ContentKeyFunctions.OfferItem); // Corvax-Next-Offer + human.AddFunction(ContentKeyFunctions.ToggleStanding); // CorvaxNext: laying system toggle standing // actions should be common (for ghosts, mobs, etc) common.AddFunction(ContentKeyFunctions.OpenActionsMenu); diff --git a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs index 91ee3a06703..744a8937080 100644 --- a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs @@ -151,6 +151,14 @@ void AddCheckBox(string checkBoxName, bool currentState, Action(OnMovementInput); + SubscribeLocalEvent(OnChangeDraw); + SubscribeLocalEvent(OnChangeStanding); + + _cfg.OnValueChanged(NextVars.AutoGetUp, b => _autoGetUp = b, true); + + //SubscribeNetworkEvent(OnCheckAutoGetUp); + } + + private void OnChangeStanding(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (_animation.HasRunningAnimation(ent, "rotate")) + return; + + if (!TryComp(ent, out var sprite)) + { + return; + } + + if (ent.Comp.Standing) + { + sprite.Rotation = Angle.Zero; + return; + } + + if (sprite.Rotation != Angle.FromDegrees(270) && sprite.Rotation != Angle.FromDegrees(90)) + { + sprite.Rotation = Angle.FromDegrees(270); + } + } + + private void OnChangeDraw(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (!TryComp(ent, out var sprite)) + return; + + if (ent.Comp.DrawDowned) + { + sprite.DrawDepth = (int)Shared.DrawDepth.DrawDepth.SmallMobs; + } + else if (!ent.Comp.DrawDowned && sprite.DrawDepth == (int)Shared.DrawDepth.DrawDepth.SmallMobs) + { + sprite.DrawDepth = (int)Shared.DrawDepth.DrawDepth.Mobs; + } + } + + private bool _autoGetUp; + + protected override bool GetAutoGetUp(Entity ent, ICommonSession session) + { + return _autoGetUp; + } + + private void OnMovementInput(EntityUid uid, LayingDownComponent component, MoveEvent args) + { + if (!_timing.IsFirstTimePredicted) + return; + if (!_standing.IsDown(uid) || _animation.HasRunningAnimation(uid, "rotate") || _buckle.IsBuckled(uid)) + return; + if (TerminatingOrDeleted(uid)) + return; + + if (!TryComp(uid, out var sprite) + || !TryComp(uid, out var rotationVisuals)) + { + return; + } + + ProcessVisuals((uid, Transform(uid), sprite, rotationVisuals)); + } + + private void ProcessVisuals(Entity entity) + { + var rotation = entity.Comp1.LocalRotation + (_eyeManager.CurrentEye.Rotation - (entity.Comp1.LocalRotation - _transform.GetWorldRotation(entity.Comp1))); + + if (rotation.GetDir() is Direction.SouthEast or Direction.East or Direction.NorthEast or Direction.North) + { + _rotationVisuals.SetHorizontalAngle((entity.Owner, entity.Comp3), Angle.FromDegrees(270)); + if (entity.Comp2 != null) + entity.Comp2.Rotation = Angle.FromDegrees(270); + return; + } + + _rotationVisuals.ResetHorizontalAngle((entity.Owner, entity.Comp3)); + if (entity.Comp2 != null) + entity.Comp2.Rotation = entity.Comp3.DefaultRotation; + } + + public override void AutoGetUp(Entity ent) + { + if (!_timing.IsFirstTimePredicted) + return; + + if (TerminatingOrDeleted(ent)) + return; + + var transform = Transform(ent); + + if (!TryComp(ent, out var rotationVisuals)) + return; + + ProcessVisuals((ent.Owner, transform, null, rotationVisuals)); + } + + /* + private void OnCheckAutoGetUp(CheckAutoGetUpEvent ev, EntitySessionEventArgs args) + { + if (!_timing.IsFirstTimePredicted) + return; + + var uid = GetEntity(ev.User); + + if (!TryComp(uid, out var transform) || !TryComp(uid, out var rotationVisuals)) + return; + + var rotation = transform.LocalRotation + (_eyeManager.CurrentEye.Rotation - (transform.LocalRotation - transform.WorldRotation)); + + if (rotation.GetDir() is Direction.SouthEast or Direction.East or Direction.NorthEast or Direction.North) + { + rotationVisuals.HorizontalRotation = Angle.FromDegrees(270); + return; + } + + rotationVisuals.HorizontalRotation = Angle.FromDegrees(90); + }*/ +} diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index 1b31fe38c28..5c717f980b2 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -2,6 +2,7 @@ using Content.Server.Body.Systems; using Content.Shared.Buckle; using Content.Shared.ActionBlocker; +using Content.Shared._CorvaxNext.Standing; using Content.Shared.Body.Components; using Content.Shared.Body.Part; using Content.Shared.Buckle.Components; @@ -34,6 +35,7 @@ public sealed partial class BuckleTest - type: Body prototype: Human - type: StandingState + - type: LayingDown # _CorvaxNext - type: entity name: {StrapDummyId} @@ -297,7 +299,6 @@ await server.WaitAssertion(() => { Assert.That(hand.HeldEntity, Is.Not.Null); } - var bodySystem = entityManager.System(); var legs = bodySystem.GetBodyChildrenOfType(human, BodyPartType.Leg, body); @@ -310,10 +311,11 @@ await server.WaitAssertion(() => await server.WaitRunTicks(10); + // start-_CorvaxNext: Laying System await server.WaitAssertion(() => { - // Still buckled - Assert.That(buckle.Buckled); + // Unbuckled and laydown + Assert.That(buckle.Buckled, Is.True); // Now with no item in any hand foreach (var hand in hands.Hands.Values) @@ -321,9 +323,17 @@ await server.WaitAssertion(() => Assert.That(hand.HeldEntity, Is.Null); } + var comp = entityManager.GetComponentOrNull(human); + Assert.That(comp, Is.Not.Null); + Assert.That(comp.CurrentState, Is.EqualTo(StandingState.Standing)); buckleSystem.Unbuckle(human, human); Assert.That(buckle.Buckled, Is.False); + Assert.That(comp.CurrentState, Is.EqualTo(StandingState.Lying)); + Assert.That(comp.Standing, Is.False); + entityManager.System().Stand(human); + Assert.That(comp.CurrentState, Is.EqualTo(StandingState.Lying)); }); + // end-_CorvaxNext: Laying System await pair.CleanReturnAsync(); } diff --git a/Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs b/Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs index d7196ea73c7..ef46415ceac 100644 --- a/Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs +++ b/Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs @@ -201,7 +201,7 @@ private void UpdateRanged(float frameTime) { return; } - + _gun.SetTarget(gun, comp.Target); // _CorvaxNext: Laying System _gun.AttemptShoot(uid, gunUid, gun, targetCordinates); } } diff --git a/Content.Server/_CorvaxNext/Footprints/EntitySystems/FootprintsSystem.cs b/Content.Server/_CorvaxNext/Footprints/EntitySystems/FootprintsSystem.cs index c4017351366..7a28fbb3bba 100644 --- a/Content.Server/_CorvaxNext/Footprints/EntitySystems/FootprintsSystem.cs +++ b/Content.Server/_CorvaxNext/Footprints/EntitySystems/FootprintsSystem.cs @@ -8,6 +8,7 @@ using Robust.Shared.Random; using Content.Shared._CorvaxNext.Footprints; using Content.Shared._CorvaxNext.Footprints.Components; +using Content.Shared._CorvaxNext.Standing; namespace Content.Server._CorvaxNext.Footprints.EntitySystems; @@ -24,12 +25,14 @@ public sealed class FootprintsSystem : EntitySystem private EntityQuery _transformQuery; private EntityQuery _mobThresholdQuery; private EntityQuery _appearanceQuery; + private EntityQuery _layingQuery; public override void Initialize() { _transformQuery = GetEntityQuery(); _mobThresholdQuery = GetEntityQuery(); _appearanceQuery = GetEntityQuery(); + _layingQuery = GetEntityQuery(); SubscribeLocalEvent(OnStartupComponent); SubscribeLocalEvent(OnMove); @@ -54,7 +57,7 @@ private void OnMove(EntityUid uid, FootprintVisualizerComponent component, ref M if (!_map.TryFindGridAt(_transform.GetMapCoordinates((uid, transform)), out var gridUid, out _)) return; - var dragging = mobThreshHolds.CurrentThresholdState is MobState.Critical or MobState.Dead; + var dragging = mobThreshHolds.CurrentThresholdState is MobState.Critical or MobState.Dead || _layingQuery.TryComp(uid, out var laying) && laying.DrawDowned; var distance = (transform.LocalPosition - component.StepPos).Length(); var stepSize = dragging ? component.DragSize : component.StepSize; diff --git a/Content.Server/_CorvaxNext/Standing/LayingDownSystem.cs b/Content.Server/_CorvaxNext/Standing/LayingDownSystem.cs new file mode 100644 index 00000000000..6155d8819a2 --- /dev/null +++ b/Content.Server/_CorvaxNext/Standing/LayingDownSystem.cs @@ -0,0 +1,77 @@ +using Content.Shared._CorvaxNext.NextVars; +using Content.Shared._CorvaxNext.Standing; +using Content.Shared.Rotation; +using Content.Shared.Standing; +using Robust.Shared.Configuration; +using Robust.Shared.Player; + +namespace Content.Server._CorvaxNext.Standing; + +public sealed class LayingDownSystem : SharedLayingDownSystem // WD EDIT +{ + [Dependency] private readonly INetConfigurationManager _cfg = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedRotationVisualsSystem _rotationVisuals = default!; + + public override void Initialize() + { + base.Initialize(); + + //SubscribeNetworkEvent(OnCheckAutoGetUp); + SubscribeLocalEvent(OnStoodEvent); + SubscribeLocalEvent(OnDownedEvent); + } + private void OnDownedEvent(Entity ent, ref DownedEvent args) + { + // Raising this event will lower the entity's draw depth to the same as a small mob. + if (CrawlUnderTables) + { + ent.Comp.DrawDowned = true; + Dirty(ent, ent.Comp); + } + } + + private void OnStoodEvent(Entity ent, ref StoodEvent args) + { + if (CrawlUnderTables) + { + ent.Comp.DrawDowned = false; + Dirty(ent, ent.Comp); + } + } + + public override void AutoGetUp(Entity ent) + { + if (!TryComp(ent, out var eyeComp) || !TryComp(ent, out var rotationVisualsComp)) + return; + + var xform = Transform(ent); + + var rotation = xform.LocalRotation + (eyeComp.Rotation - (xform.LocalRotation - _transform.GetWorldRotation(xform))); + + if (rotation.GetDir() is Direction.SouthEast or Direction.East or Direction.NorthEast or Direction.North) + { + _rotationVisuals.SetHorizontalAngle((ent, rotationVisualsComp), Angle.FromDegrees(270)); + return; + } + + _rotationVisuals.ResetHorizontalAngle((ent, rotationVisualsComp)); + } + + protected override bool GetAutoGetUp(Entity ent, ICommonSession session) + { + return _cfg.GetClientCVar(session.Channel, NextVars.AutoGetUp); + } + /* + private void OnCheckAutoGetUp(CheckAutoGetUpEvent ev, EntitySessionEventArgs args) + { + var uid = GetEntity(ev.User); + + if (!TryComp(uid, out LayingDownComponent? layingDown)) + return; + + layingDown.AutoGetUp = _cfg.GetClientCVar(args.SenderSession.Channel, CCVars.AutoGetUp); + Dirty(uid, layingDown); + } + */ +} diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 2a23f0c667b..87538fd763f 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -15,6 +15,8 @@ using Content.Shared.Humanoid; using Content.Shared.Humanoid.Events; using Content.Shared.Inventory; +using Content.Shared.Rejuvenate; +using Content.Shared.Silicons.Borgs.Components; using Content.Shared.Standing; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -140,7 +142,7 @@ private void OnBodyCanDrag(Entity ent, ref CanDragEvent args) // start-_CorvaxNext: surgery private void OnStandAttempt(Entity ent, ref StandAttemptEvent args) { - if (ent.Comp.LegEntities.Count == 0) + if (!HasComp(ent) && ent.Comp.LegEntities.Count == 0) args.Cancel(); } // end-_CorvaxNext: surgery diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs index bd98f514470..2188324555f 100644 --- a/Content.Shared/Input/ContentKeyFunctions.cs +++ b/Content.Shared/Input/ContentKeyFunctions.cs @@ -57,6 +57,7 @@ public static class ContentKeyFunctions public static readonly BoundKeyFunction ToggleFullscreen = "ToggleFullscreen"; public static readonly BoundKeyFunction Point = "Point"; public static readonly BoundKeyFunction OfferItem = "OfferItem"; // Corvax-Next-Offer + public static readonly BoundKeyFunction ToggleStanding = "ToggleStanding"; // _CorvaxNext: Laying System standing public static readonly BoundKeyFunction ZoomOut = "ZoomOut"; public static readonly BoundKeyFunction ZoomIn = "ZoomIn"; public static readonly BoundKeyFunction ResetZoom = "ResetZoom"; diff --git a/Content.Shared/Standing/StandingStateComponent.cs b/Content.Shared/Standing/StandingStateComponent.cs index 02708722355..8770b79269e 100644 --- a/Content.Shared/Standing/StandingStateComponent.cs +++ b/Content.Shared/Standing/StandingStateComponent.cs @@ -1,24 +1,38 @@ using Robust.Shared.Audio; using Robust.Shared.GameStates; -namespace Content.Shared.Standing +namespace Content.Shared.Standing; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class StandingStateComponent : Component { - [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] - [Access(typeof(StandingStateSystem))] - public sealed partial class StandingStateComponent : Component - { - [ViewVariables(VVAccess.ReadWrite)] - [DataField] - public SoundSpecifier? DownSound { get; private set; } = new SoundCollectionSpecifier("BodyFall"); + [ViewVariables(VVAccess.ReadWrite)] + [DataField] + public SoundSpecifier DownSound { get; private set; } = new SoundCollectionSpecifier("BodyFall"); - [DataField, AutoNetworkedField] - public bool Standing { get; set; } = true; + // _CorvaxNext EDIT START + [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + public StandingState CurrentState { get; set; } = StandingState.Standing; + // _CorvaxNext EDIT END - /// - /// List of fixtures that had their collision mask changed when the entity was downed. - /// Required for re-adding the collision mask. - /// - [DataField, AutoNetworkedField] - public List ChangedFixtures = new(); + public bool Standing + { + get => CurrentState == StandingState.Standing; + set => CurrentState = value ? StandingState.Standing : StandingState.Lying; } + + /// + /// List of fixtures that had their collision mask changed when the entity was downed. + /// Required for re-adding the collision mask. + /// + [DataField, AutoNetworkedField] + public List ChangedFixtures = new(); +} +// _CorvaxNext EDIT START +public enum StandingState +{ + Lying, + GettingUp, + Standing, } +// _CorvaxNext EDIT END diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index 8d9be9ab776..d50ee64f2e2 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -1,172 +1,182 @@ +using Content.Shared.Buckle; +using Content.Shared.Buckle.Components; using Content.Shared.Hands.Components; +using Content.Shared.Movement.Systems; using Content.Shared.Physics; using Content.Shared.Rotation; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; -namespace Content.Shared.Standing +namespace Content.Shared.Standing; + +public sealed class StandingStateSystem : EntitySystem { - public sealed class StandingStateSystem : EntitySystem + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; // _CorvaxNext EDIT + + // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited. + private const int StandingCollisionLayer = (int)CollisionGroup.MidImpassable; + + public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) { - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; + if (!Resolve(uid, ref standingState, false)) + return false; + + return !standingState.Standing; + } - // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited. - private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable; + public bool Down(EntityUid uid, + bool playSound = true, + bool dropHeldItems = true, + bool force = false, + StandingStateComponent? standingState = null, + AppearanceComponent? appearance = null, + HandsComponent? hands = null) + { + // TODO: This should actually log missing comps... + if (!Resolve(uid, ref standingState, false)) + return false; - public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) - { - if (!Resolve(uid, ref standingState, false)) - return false; + // Optional component. + Resolve(uid, ref appearance, ref hands, false); - return !standingState.Standing; + if (!standingState.Standing) + return true; + + // This is just to avoid most callers doing this manually saving boilerplate + // 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to. + // We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway + // and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent. + if (dropHeldItems && hands != null) + { + RaiseLocalEvent(uid, new DropHandItemsEvent(), false); } - public bool Down(EntityUid uid, - bool playSound = true, - bool dropHeldItems = true, - bool force = false, - StandingStateComponent? standingState = null, - AppearanceComponent? appearance = null, - HandsComponent? hands = null) + //if (TryComp(uid, out BuckleComponent? buckle) && buckle.Buckled && !_buckle.TryUnbuckle(uid, uid, buckleComp: buckle)) // WD EDIT + // return false; + + if (!force) { - // TODO: This should actually log missing comps... - if (!Resolve(uid, ref standingState, false)) + var msg = new DownAttemptEvent(); + RaiseLocalEvent(uid, msg, false); + + if (msg.Cancelled) return false; + } - // Optional component. - Resolve(uid, ref appearance, ref hands, false); + standingState.CurrentState = StandingState.Lying; + Dirty(uid, standingState); + RaiseLocalEvent(uid, new DownedEvent(), false); - if (!standingState.Standing) - return true; + // Seemed like the best place to put it + _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance); - // This is just to avoid most callers doing this manually saving boilerplate - // 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to. - // We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway - // and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent. - if (dropHeldItems && hands != null) - { - RaiseLocalEvent(uid, new DropHandItemsEvent(), false); - } - - if (!force) + // Change collision masks to allow going under certain entities like flaps and tables + if (TryComp(uid, out FixturesComponent? fixtureComponent)) + { + foreach (var (key, fixture) in fixtureComponent.Fixtures) { - var msg = new DownAttemptEvent(); - RaiseLocalEvent(uid, msg, false); + if ((fixture.CollisionMask & StandingCollisionLayer) == 0) + continue; - if (msg.Cancelled) - return false; + standingState.ChangedFixtures.Add(key); + _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent); } + } - standingState.Standing = false; - Dirty(uid, standingState); - RaiseLocalEvent(uid, new DownedEvent(), false); + // check if component was just added or streamed to client + // if true, no need to play sound - mob was down before player could seen that + if (standingState.LifeStage <= ComponentLifeStage.Starting) + return true; - // Seemed like the best place to put it - _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance); + if (playSound) + { + _audio.PlayPredicted(standingState.DownSound, uid, null); + } - // Change collision masks to allow going under certain entities like flaps and tables - if (TryComp(uid, out FixturesComponent? fixtureComponent)) - { - foreach (var (key, fixture) in fixtureComponent.Fixtures) - { - if ((fixture.CollisionMask & StandingCollisionLayer) == 0) - continue; - - standingState.ChangedFixtures.Add(key); - _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent); - } - } + _movement.RefreshMovementSpeedModifiers(uid); // _CorvaxNext EDIT + return true; + } - // check if component was just added or streamed to client - // if true, no need to play sound - mob was down before player could seen that - if (standingState.LifeStage <= ComponentLifeStage.Starting) - return true; + public bool Stand(EntityUid uid, + StandingStateComponent? standingState = null, + AppearanceComponent? appearance = null, + bool force = false) + { + // TODO: This should actually log missing comps... + if (!Resolve(uid, ref standingState, false)) + return false; - if (playSound) - { - _audio.PlayPredicted(standingState.DownSound, uid, uid); - } + // Optional component. + Resolve(uid, ref appearance, false); + if (standingState.Standing) return true; - } - - public bool Stand(EntityUid uid, - StandingStateComponent? standingState = null, - AppearanceComponent? appearance = null, - bool force = false) - { - // TODO: This should actually log missing comps... - if (!Resolve(uid, ref standingState, false)) - return false; - - // Optional component. - Resolve(uid, ref appearance, false); - if (standingState.Standing) - return true; + //if (TryComp(uid, out BuckleComponent? buckle) && buckle.Buckled && !_buckle.TryUnbuckle(uid, uid, buckleComp: buckle)) // WD EDIT + // return false; - if (!force) - { - var msg = new StandAttemptEvent(); - RaiseLocalEvent(uid, msg, false); + if (!force) + { + var msg = new StandAttemptEvent(); + RaiseLocalEvent(uid, msg, false); - if (msg.Cancelled) - return false; - } + if (msg.Cancelled) + return false; + } - standingState.Standing = true; - Dirty(uid, standingState); - RaiseLocalEvent(uid, new StoodEvent(), false); + standingState.CurrentState = StandingState.Standing; + Dirty(uid, standingState); + RaiseLocalEvent(uid, new StoodEvent(), false); - _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance); + _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance); - if (TryComp(uid, out FixturesComponent? fixtureComponent)) + if (TryComp(uid, out FixturesComponent? fixtureComponent)) + { + foreach (var key in standingState.ChangedFixtures) { - foreach (var key in standingState.ChangedFixtures) - { - if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture)) - _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent); - } + if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture)) + _physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent); } - standingState.ChangedFixtures.Clear(); - - return true; } - } + standingState.ChangedFixtures.Clear(); + _movement.RefreshMovementSpeedModifiers(uid); // _CorvaxNext EDIT - public sealed class DropHandItemsEvent : EventArgs - { + return true; } +} - /// - /// Subscribe if you can potentially block a down attempt. - /// - public sealed class DownAttemptEvent : CancellableEntityEventArgs - { - } +public sealed class DropHandItemsEvent : EventArgs +{ +} - /// - /// Subscribe if you can potentially block a stand attempt. - /// - public sealed class StandAttemptEvent : CancellableEntityEventArgs - { - } +/// +/// Subscribe if you can potentially block a down attempt. +/// +public sealed class DownAttemptEvent : CancellableEntityEventArgs +{ +} - /// - /// Raised when an entity becomes standing - /// - public sealed class StoodEvent : EntityEventArgs - { - } +/// +/// Subscribe if you can potentially block a stand attempt. +/// +public sealed class StandAttemptEvent : CancellableEntityEventArgs +{ +} - /// - /// Raised when an entity is not standing - /// - public sealed class DownedEvent : EntityEventArgs - { - } +/// +/// Raised when an entity becomes standing +/// +public sealed class StoodEvent : EntityEventArgs +{ +} + +/// +/// Raised when an entity is not standing +/// +public sealed class DownedEvent : EntityEventArgs +{ } diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index 8f828131f5c..67dd7265bfe 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -19,6 +19,8 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; +using Content.Shared._CorvaxNext.Standing; +using Content.Shared.Movement.Components; namespace Content.Shared.Stunnable; @@ -32,6 +34,7 @@ public abstract class SharedStunSystem : EntitySystem [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; [Dependency] private readonly StandingStateSystem _standingState = default!; [Dependency] private readonly StatusEffectsSystem _statusEffect = default!; + [Dependency] private readonly SharedLayingDownSystem _layingDown = default!; // Ataraxia EDIT /// /// Friction modifier for knocked down players. @@ -137,11 +140,31 @@ private void OnStunOnContactCollide(Entity ent, ref Star private void OnKnockInit(EntityUid uid, KnockedDownComponent component, ComponentInit args) { _standingState.Down(uid); + // start-_CorvaxNext: Laying System + if (TryComp(uid, out var layingDownComponent)) + { + _layingDown.TryProcessAutoGetUp((uid, layingDownComponent)); + _layingDown.TryLieDown(uid, layingDownComponent, null, DropHeldItemsBehavior.DropIfStanding); // Ataraxia EDIT + } + // end-_CorvaxNext: Laying System + } + private void OnKnockShutdown(EntityUid uid, KnockedDownComponent component, ComponentShutdown args) { - _standingState.Stand(uid); + // start-_CorvaxNext: Laying System + if (!TryComp(uid, out StandingStateComponent? standing)) + return; + + if (TryComp(uid, out LayingDownComponent? layingDown)) + { + _layingDown.TryProcessAutoGetUp((uid, layingDown)); + return; + } + + _standingState.Stand(uid, standing); + // end-_CorvaxNext: Laying System } private void OnStandAttempt(EntityUid uid, KnockedDownComponent component, StandAttemptEvent args) diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index c7cc09e618b..0ab3f1e9989 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -224,6 +224,14 @@ public void AttemptShoot(EntityUid gunUid, GunComponent gun) gun.ShotCounter = 0; } + /// + /// Sets the targeted entity of the gun. Should be called before attempting to shoot to avoid shooting over the target. + /// + public void SetTarget(GunComponent gun, EntityUid target) + { + gun.Target = target; + } + private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) { if (gun.FireRateModified <= 0f || diff --git a/Content.Shared/_CorvaxNext/NextVars.cs b/Content.Shared/_CorvaxNext/NextVars.cs index 9b3323e71e7..1e38ee75e96 100644 --- a/Content.Shared/_CorvaxNext/NextVars.cs +++ b/Content.Shared/_CorvaxNext/NextVars.cs @@ -24,4 +24,21 @@ public sealed class NextVars CVarDef.Create("surgery.can_operate_on_self", false, CVar.SERVERONLY); #endregion + + /* + * _CorvaxNext Bind Standing and Laying System + */ + + public static readonly CVarDef AutoGetUp = + CVarDef.Create("laying.auto_get_up", true, CVar.CLIENT | CVar.ARCHIVE | CVar.REPLICATED); + + /// + /// When true, entities that fall to the ground will be able to crawl under tables and + /// plastic flaps, allowing them to take cover from gunshots. + /// + public static readonly CVarDef CrawlUnderTables = + CVarDef.Create("laying.crawlundertables", true, CVar.REPLICATED); + + // public static readonly CVarDef OfferModeIndicatorsPointShow = + // CVarDef.Create("hud.offer_mode_indicators_point_show", true, CVar.ARCHIVE | CVar.CLIENTONLY); } diff --git a/Content.Shared/_CorvaxNext/Standing/LayingDownComponent.cs b/Content.Shared/_CorvaxNext/Standing/LayingDownComponent.cs new file mode 100644 index 00000000000..1fa6572067b --- /dev/null +++ b/Content.Shared/_CorvaxNext/Standing/LayingDownComponent.cs @@ -0,0 +1,30 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared._CorvaxNext.Standing; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class LayingDownComponent : Component +{ + [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + public float StandingUpTime { get; set; } = 1f; + + [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + public float SpeedModify { get; set; } = 0.25f; + + [ViewVariables, AutoNetworkedField] + public bool DrawDowned { get; set; } = false; + + [ViewVariables] + public int? OriginalDrawDepth { get; set; } = (int)DrawDepth.DrawDepth.Mobs; +} +[Serializable, NetSerializable] +public sealed class ChangeLayingDownEvent : CancellableEntityEventArgs; + +/* +[Serializable, NetSerializable] +public sealed class CheckAutoGetUpEvent(NetEntity user) : CancellableEntityEventArgs +{ + public NetEntity User = user; +} +*/ diff --git a/Content.Shared/_CorvaxNext/Standing/SharedLayingDownSystem.cs b/Content.Shared/_CorvaxNext/Standing/SharedLayingDownSystem.cs new file mode 100644 index 00000000000..30c20002f52 --- /dev/null +++ b/Content.Shared/_CorvaxNext/Standing/SharedLayingDownSystem.cs @@ -0,0 +1,369 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared._CorvaxNext.NextVars; +using Content.Shared._CorvaxNext.Targeting; +using Content.Shared.Body.Components; +using Content.Shared.Buckle; +using Content.Shared.Buckle.Components; +using Content.Shared.Damage; +using Content.Shared.DoAfter; +using Content.Shared.Gravity; +using Content.Shared.Input; +using Content.Shared.Interaction; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Movement.Systems; +using Content.Shared.Physics; +using Content.Shared.Popups; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Standing; +using Content.Shared.Stunnable; +using Content.Shared.Tag; +using Content.Shared.Traits.Assorted; +using Content.Shared.UserInterface; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; +using Robust.Shared.Containers; +using Robust.Shared.Input.Binding; +using Robust.Shared.Map.Components; +using Robust.Shared.Network; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; + +namespace Content.Shared._CorvaxNext.Standing; + +public abstract class SharedLayingDownSystem : EntitySystem +{ + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedBuckleSystem _buckle = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly PullingSystem _pulling = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + [Dependency] private readonly IConfigurationManager _config = default!; + + protected bool CrawlUnderTables = false; + + public override void Initialize() + { + CommandBinds.Builder + .Bind(ContentKeyFunctions.ToggleStanding, InputCmdHandler.FromDelegate(ToggleStanding)) + .Register(); + + SubscribeAllEvent(OnChangeState); + + SubscribeLocalEvent(OnStandingUpDoAfter); + SubscribeLocalEvent(OnRefreshMovementSpeed); + SubscribeLocalEvent(OnParentChanged); + SubscribeLocalEvent(OnChangeMobState); + + SubscribeLocalEvent(OnBuckled); + SubscribeLocalEvent(OnUnBuckled); + SubscribeLocalEvent(OnCheckLegs); + SubscribeLocalEvent(OnBoundUserInterface, after: [typeof(SharedInteractionSystem)]); + + Subs.CVar(_config, NextVars.NextVars.CrawlUnderTables, b => CrawlUnderTables = b, true); + } + + private void OnCheckLegs(Entity ent, ref StandAttemptEvent args) + { + if (!TryComp(ent, out var body)) + return; + + if (!HasComp(ent) && (body.LegEntities.Count < body.RequiredLegs || body.LegEntities.Count == 0)) + args.Cancel(); // no legs bro + } + + private void OnBoundUserInterface(BoundUserInterfaceMessageAttempt args) + { + if ( + args.Cancelled || + !TryComp(args.Target, out var uiComp) || + !TryComp(args.Actor, out var standingStateComponent) || + standingStateComponent.CurrentState != StandingState.Lying) + return; + + if (uiComp.RequiresComplex) + args.Cancel(); + } + + private void OnChangeMobState(Entity ent, ref MobStateChangedEvent args) + { + if ( + !TryComp(ent, out var standingStateComponent) || + standingStateComponent.CurrentState != StandingState.Lying) + return; + + if (args.NewMobState == MobState.Alive) + { + AutoGetUp(ent); + TryStandUp(ent, ent, standingStateComponent); + return; + } + + if (CrawlUnderTables) + { + ent.Comp.DrawDowned = false; + Dirty(ent, ent.Comp); + } + } + + + + private void OnUnBuckled(Entity ent, ref UnbuckledEvent args) + { + if (!TryComp(ent, out var standingStateComponent)) + return; + + if (TryComp(ent, out var body) && + ((body.RequiredLegs > 0 && body.LegEntities.Count < body.RequiredLegs) || body.LegEntities.Count == 0) + && standingStateComponent.CurrentState != StandingState.Lying) + { + _standing.Down(ent, true, true, true); + return; + } + + TryProcessAutoGetUp(ent); + + if (CrawlUnderTables && standingStateComponent.CurrentState == StandingState.Lying) + { + ent.Comp.DrawDowned = true; + Dirty(ent, ent.Comp); + } + } + + private void OnBuckled(Entity ent, ref BuckledEvent args) + { + if ( + !TryComp(ent, out var standingStateComponent) || + standingStateComponent.CurrentState != StandingState.Lying) + return; + + if (CrawlUnderTables) + { + ent.Comp.DrawDowned = false; + Dirty(ent, ent.Comp); + } + } + + protected abstract bool GetAutoGetUp(Entity ent, ICommonSession session); + + public void TryProcessAutoGetUp(Entity ent) + { + if (_buckle.IsBuckled(ent)) + return; + + if (_pulling.IsPulled(ent)) + return; + + if (!IsSafeStanUp(ent, out _)) + return; + + var autoUp = !_playerManager.TryGetSessionByEntity(ent, out var player) || + GetAutoGetUp(ent, session: player); + + if (autoUp && !_container.IsEntityInContainer(ent)) + TryStandUp(ent, ent); + } + + public override void Shutdown() + { + base.Shutdown(); + CommandBinds.Unregister(); + } + + private void ToggleStanding(ICommonSession? session) + { + if (session?.AttachedEntity == null || + !HasComp(session.AttachedEntity) || + _gravity.IsWeightless(session.AttachedEntity.Value)) + { + return; + } + + if (!_timing.IsFirstTimePredicted) + return; + + RaisePredictiveEvent(new ChangeLayingDownEvent()); + } + + public virtual void AutoGetUp(Entity ent) + { + + } + + private void OnChangeState(ChangeLayingDownEvent ev, EntitySessionEventArgs args) + { + if (!args.SenderSession.AttachedEntity.HasValue) + return; + + var uid = args.SenderSession.AttachedEntity.Value; + + // TODO: Wizard + //if (HasComp(uid)) + // return; + + if (!TryComp(uid, out StandingStateComponent? standing) || + !TryComp(uid, out LayingDownComponent? layingDown) || + !TryComp(uid, out var inputMover)) + { + return; + } + + if ( + HasComp(uid) || + !_mobState.IsAlive(uid) || + !inputMover.CanMove) + return; + + //RaiseNetworkEvent(new CheckAutoGetUpEvent(GetNetEntity(uid))); + TryProcessAutoGetUp((uid, layingDown)); + + if (_standing.IsDown(uid, standing)) + TryStandUp(uid, layingDown, standing); + else + TryLieDown(uid, layingDown, standing); + } + + private void OnStandingUpDoAfter(EntityUid uid, StandingStateComponent component, StandingUpDoAfterEvent args) + { + if (args.Handled || args.Cancelled || HasComp(uid) || + _mobState.IsIncapacitated(uid) || !IsSafeStanUp(uid, out _) || !_standing.Stand(uid)) + { + component.CurrentState = StandingState.Lying; + Dirty(uid, component); + return; + } + + component.CurrentState = StandingState.Standing; + Dirty(uid, component); + } + + private void OnRefreshMovementSpeed(EntityUid uid, LayingDownComponent component, RefreshMovementSpeedModifiersEvent args) + { + if (_standing.IsDown(uid)) + args.ModifySpeed(component.SpeedModify, component.SpeedModify); + //else + // args.ModifySpeed(1f, 1f); + } + + private void OnParentChanged(EntityUid uid, LayingDownComponent component, EntParentChangedMessage args) + { + // If the entity is not on a grid, try to make it stand up to avoid issues + if (!TryComp(uid, out var standingState) + || standingState.CurrentState is StandingState.Standing + || Transform(uid).GridUid != null) + { + return; + } + + _standing.Stand(uid, standingState); + } + + public bool IsSafeStanUp(EntityUid entity, [NotNullWhen(false)] out EntityUid? obj) + { + var xform = Transform(entity); + if (xform.GridUid != null) + { + foreach (var ent in _map.GetAnchoredEntities(xform.GridUid.Value, Comp(xform.GridUid.Value), xform.Coordinates)) + { + if (!_tag.HasTag(ent, "Structure") || !TryComp(ent, out var phys)) + continue; + + if (!phys.CanCollide || (phys.CollisionMask & (int)CollisionGroup.MidImpassable) == 0x0) + continue; + + obj = ent; + return false; + } + } + obj = null; + return true; + } + + private static SoundSpecifier _bonkSound = new SoundCollectionSpecifier("TrayHit"); + public bool TryStandUp(EntityUid uid, LayingDownComponent? layingDown = null, StandingStateComponent? standingState = null) + { + if (!Resolve(uid, ref standingState, false) || + !Resolve(uid, ref layingDown, false) || + standingState.CurrentState is not StandingState.Lying || + !_mobState.IsAlive(uid) || + _buckle.IsBuckled(uid) || + _pulling.IsPulled(uid) || + HasComp(uid) || + TerminatingOrDeleted(uid)) + { + return false; + } + + if (!IsSafeStanUp(uid, out var obj)) + { + _popup.PopupPredicted( + Loc.GetString("bonkable-success-message-user", ("bonkable", obj.Value)), + Loc.GetString("bonkable-success-message-others", ("bonkable", obj.Value), ("user", uid)), + obj.Value, + uid, + PopupType.MediumCaution); + _damageable.TryChangeDamage(uid, new DamageSpecifier() { DamageDict = { { "Blunt", 5 } } }, ignoreResistances: true, canEvade: true, targetPart: TargetBodyPart.Head); + _stun.TryStun(uid, TimeSpan.FromSeconds(2), true); + _audioSystem.PlayPredicted(_bonkSound, uid, obj.Value); + return false; + } + + var args = new DoAfterArgs(EntityManager, uid, layingDown.StandingUpTime, new StandingUpDoAfterEvent(), uid) + { + BreakOnHandChange = false, + RequireCanInteract = false + }; + + if (!_doAfter.TryStartDoAfter(args)) + return false; + + standingState.CurrentState = StandingState.GettingUp; + Dirty(uid, standingState); + return true; + } + + public bool TryLieDown(EntityUid uid, LayingDownComponent? layingDown = null, StandingStateComponent? standingState = null, DropHeldItemsBehavior behavior = DropHeldItemsBehavior.NoDrop) + { + if (!Resolve(uid, ref standingState, false) || + !Resolve(uid, ref layingDown, false) || + standingState.CurrentState is not StandingState.Standing || + _buckle.IsBuckled(uid)) + { + if (behavior == DropHeldItemsBehavior.AlwaysDrop) + RaiseLocalEvent(uid, new DropHandItemsEvent()); + + return false; + } + + _standing.Down(uid, true, behavior != DropHeldItemsBehavior.NoDrop, standingState: standingState); + return true; + } +} + +[Serializable, NetSerializable] +public sealed partial class StandingUpDoAfterEvent : SimpleDoAfterEvent; + +public enum DropHeldItemsBehavior : byte +{ + NoDrop, + DropIfStanding, + AlwaysDrop +} diff --git a/Resources/Locale/ru-RU/_corvaxnext/escape-menu/ui/options.ftl b/Resources/Locale/ru-RU/_corvaxnext/escape-menu/ui/options.ftl index d7857e07bbf..9ae17779e23 100644 --- a/Resources/Locale/ru-RU/_corvaxnext/escape-menu/ui/options.ftl +++ b/Resources/Locale/ru-RU/_corvaxnext/escape-menu/ui/options.ftl @@ -1 +1,4 @@ ui-options-hud-theme-operative = Оперативник + +ui-options-hotkey-auto-up = Автоматически вставать при падении +ui-options-function-toggle-standing = Встать или лечь diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index c82e206738c..7bcdf52088f 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -215,6 +215,7 @@ - AnomalyHost - type: Targeting # _CorvaxNext: surgery - type: SurgeryTarget # _CorvaxNext: surgery + - type: LayingDown # _CorvaxNext: LayingDown Laying Down - type: entity save: false diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index f6bde4eb498..c3daeffe2f2 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -612,3 +612,7 @@ binds: - function: OfferItem # Corvax-Next-Offer type: State key: F +- function: ToggleStanding # _CorvaxNext-edit Laying System + type: State + key: C + mod1: Shift