[Port] Lying Down System / Система Лежания (#2)

* add: White lay down

* fix: rotation

* fix

* fix: rotation

* fix
This commit is contained in:
Spatison
2024-08-23 16:30:07 +03:00
committed by GitHub
parent b985fc9601
commit 58f3ff4f5c
14 changed files with 362 additions and 129 deletions

View File

@@ -57,7 +57,6 @@ internal sealed class BuckleSystem : SharedBuckleSystem
!buckled ||
args.Sprite == null)
{
_rotationVisualizerSystem.SetHorizontalAngle((uid, rotVisuals), rotVisuals.DefaultRotation);
return;
}

View File

@@ -110,6 +110,12 @@ namespace Content.Client.Options.UI.Tabs
_cfg.SaveToFile();
}
private void HandleToggleAutoGetUp(BaseButton.ButtonToggledEventArgs args) // WD EDIT
{
_cfg.SetCVar(WhiteCVars.AutoGetUp, args.Pressed);
_cfg.SaveToFile();
}
public KeyRebindTab()
{
IoCManager.InjectDependencies(this);
@@ -194,6 +200,7 @@ namespace Content.Client.Options.UI.Tabs
AddButton(ContentKeyFunctions.SaveItemLocation);
AddButton(ContentKeyFunctions.ToggleStanding);
AddButton(ContentKeyFunctions.LookUp); // WD EDIT
AddCheckBox("ui-options-function-auto-get-up", _cfg.GetCVar(WhiteCVars.AutoGetUp), HandleToggleAutoGetUp); // WD EDIT
AddCheckBox("ui-options-function-hold-look-up", _cfg.GetCVar(WhiteCVars.HoldLookUp), HandleHoldLookUp); // WD EDIT
AddHeader("ui-options-header-interaction-adv");

View File

@@ -0,0 +1,82 @@
using Content.Shared._White.Standing;
using Content.Shared.Buckle;
using Content.Shared.Rotation;
using Content.Shared.Standing;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Timing;
namespace Content.Client._White.Standing;
public sealed class LayingDownSystem : SharedLayingDownSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly StandingStateSystem _standing = default!;
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
[Dependency] private readonly SharedBuckleSystem _buckle = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LayingDownComponent, MoveEvent>(OnMovementInput);
SubscribeNetworkEvent<CheckAutoGetUpEvent>(OnCheckAutoGetUp);
}
private void OnMovementInput(EntityUid uid, LayingDownComponent component, MoveEvent args)
{
if (!_timing.IsFirstTimePredicted)
return;
if (!_standing.IsDown(uid))
return;
if (_buckle.IsBuckled(uid))
return;
if (_animation.HasRunningAnimation(uid, "rotate"))
return;
if (!TryComp<TransformComponent>(uid, out var transform)
|| !TryComp<SpriteComponent>(uid, out var sprite)
|| !TryComp<RotationVisualsComponent>(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);
sprite.Rotation = Angle.FromDegrees(270);
return;
}
rotationVisuals.HorizontalRotation = Angle.FromDegrees(90);
sprite.Rotation = Angle.FromDegrees(90);
}
private void OnCheckAutoGetUp(CheckAutoGetUpEvent ev, EntitySessionEventArgs args)
{
if (!_timing.IsFirstTimePredicted)
return;
var uid = GetEntity(ev.User);
if (!TryComp<TransformComponent>(uid, out var transform) || !TryComp<RotationVisualsComponent>(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);
}
}

View File

@@ -1,14 +0,0 @@
namespace Content.Server.Standing;
[RegisterComponent]
public sealed partial class LayingDownComponent : Component
{
[DataField]
public float DownedSpeedMultiplier = 0.15f;
[DataField]
public TimeSpan Cooldown = TimeSpan.FromSeconds(2.5f);
[DataField]
public TimeSpan NextToggleAttempt = TimeSpan.Zero;
}

View File

@@ -1,101 +1,28 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Input;
using Content.Shared.Movement.Systems;
using Content.Shared.Popups;
using Content.Shared.Standing;
using Robust.Shared.Input.Binding;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using Content.Shared._White;
using Content.Shared._White.Standing;
using Robust.Shared.Configuration;
namespace Content.Server.Standing;
/// <remarks>Unfortunately cannot be shared because some standing conditions are server-side only</remarks>
public sealed class LayingDownSystem : EntitySystem
public sealed class LayingDownSystem : SharedLayingDownSystem // WD EDIT
{
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedPopupSystem _popups = default!;
[Dependency] private readonly Shared.Standing.StandingStateSystem _standing = default!; // WHY IS THERE TWO DIFFERENT STANDING SYSTEMS?!
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetConfigurationManager _cfg = default!;
public override void Initialize()
{
CommandBinds.Builder
.Bind(ContentKeyFunctions.ToggleStanding, InputCmdHandler.FromDelegate(ToggleStanding, handle: false, outsidePrediction: false))
.Register<LayingDownSystem>();
base.Initialize();
SubscribeLocalEvent<LayingDownComponent, StoodEvent>(DoRefreshMovementSpeed);
SubscribeLocalEvent<LayingDownComponent, DownedEvent>(DoRefreshMovementSpeed);
SubscribeLocalEvent<LayingDownComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeed);
SubscribeLocalEvent<LayingDownComponent, EntParentChangedMessage>(OnParentChanged);
SubscribeNetworkEvent<CheckAutoGetUpEvent>(OnCheckAutoGetUp);
}
public override void Shutdown()
private void OnCheckAutoGetUp(CheckAutoGetUpEvent ev, EntitySessionEventArgs args)
{
base.Shutdown();
var uid = GetEntity(ev.User);
CommandBinds.Unregister<LayingDownSystem>();
}
private void DoRefreshMovementSpeed(EntityUid uid, LayingDownComponent component, object args)
{
_movement.RefreshMovementSpeedModifiers(uid);
}
private void OnRefreshMovementSpeed(EntityUid uid, LayingDownComponent component, RefreshMovementSpeedModifiersEvent args)
{
if (TryComp<StandingStateComponent>(uid, out var standingState) && standingState.Standing)
if (!TryComp(uid, out LayingDownComponent? layingDown))
return;
args.ModifySpeed(component.DownedSpeedMultiplier, component.DownedSpeedMultiplier, bypassImmunity: true);
}
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<StandingStateComponent>(uid, out var standingState)
|| standingState.Standing
|| Transform(uid).GridUid != null)
return;
_standing.Stand(uid, standingState);
}
private void ToggleStanding(ICommonSession? session)
{
if (session is not { AttachedEntity: { Valid: true } uid } playerSession
|| !Exists(uid)
|| !TryComp<StandingStateComponent>(uid, out var standingState)
|| !TryComp<LayingDownComponent>(uid, out var layingDown))
return;
// If successful, show popup to self and others. Otherwise, only to self.
if (ToggleStandingImpl(uid, standingState, layingDown, out var popupBranch))
{
_popups.PopupEntity(Loc.GetString($"laying-comp-{popupBranch}-other", ("entity", uid)), uid, Filter.PvsExcept(uid), true);
layingDown.NextToggleAttempt = _timing.CurTime + layingDown.Cooldown;
}
_popups.PopupEntity(Loc.GetString($"laying-comp-{popupBranch}-self", ("entity", uid)), uid, uid);
}
private bool ToggleStandingImpl(EntityUid uid, StandingStateComponent standingState, LayingDownComponent layingDown, out string popupBranch)
{
var success = layingDown.NextToggleAttempt <= _timing.CurTime;
if (_standing.IsDown(uid, standingState))
{
success = success && _standing.Stand(uid, standingState, force: false);
popupBranch = success ? "stand-success" : "stand-fail";
}
else
{
success = success && Transform(uid).GridUid != null; // Do not allow laying down when not on a surface.
success = success && _standing.Down(uid, standingState: standingState, playSound: true, dropHeldItems: false);
popupBranch = success ? "lay-success" : "lay-fail";
}
return success;
layingDown.AutoGetUp = _cfg.GetClientCVar(args.SenderSession.Channel, WhiteCVars.AutoGetUp);
Dirty(uid, layingDown);
}
}

View File

@@ -1,5 +1,5 @@
using Content.Server.Traits.Assorted;
using Content.Server.Standing;
using Content.Shared._White.Standing;
namespace Content.Shared.Traits.Assorted.Systems;
@@ -16,7 +16,7 @@ public sealed class LayingDownModifierSystem : EntitySystem
if (!TryComp<LayingDownComponent>(uid, out var layingDown))
return;
layingDown.Cooldown *= component.LayingDownCooldownMultiplier;
layingDown.DownedSpeedMultiplier *= component.DownedSpeedMultiplierMultiplier;
layingDown.StandingUpTime *= component.LayingDownCooldownMultiplier;
layingDown.SpeedModify *= component.DownedSpeedMultiplierMultiplier;
}
}

View File

@@ -1,24 +1,35 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Shared.Standing
namespace Content.Shared.Standing;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
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;
// WD EDIT START
[DataField, AutoNetworkedField]
public StandingState CurrentState { get; set; } = StandingState.Standing;
// WD EDIT END
/// <summary>
/// List of fixtures that had their collision mask changed when the entity was downed.
/// Required for re-adding the collision mask.
/// </summary>
[DataField, AutoNetworkedField]
public List<string> ChangedFixtures = new();
}
[DataField, AutoNetworkedField]
public bool Standing { get; set; } = true;
/// <summary>
/// List of fixtures that had their collision mask changed when the entity was downed.
/// Required for re-adding the collision mask.
/// </summary>
[DataField, AutoNetworkedField]
public List<string> ChangedFixtures = new();
}
// WD EDIT START
public enum StandingState
{
Lying,
GettingUp,
Standing,
}
// WD EDIT END

View File

@@ -1,7 +1,9 @@
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;
@@ -13,6 +15,8 @@ namespace Content.Shared.Standing
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!; // WD EDIT
[Dependency] private readonly SharedBuckleSystem _buckle = default!; // WD 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;
@@ -22,7 +26,7 @@ namespace Content.Shared.Standing
if (!Resolve(uid, ref standingState, false))
return false;
return !standingState.Standing;
return standingState.CurrentState is StandingState.Lying or StandingState.GettingUp;
}
public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true,
@@ -37,7 +41,7 @@ namespace Content.Shared.Standing
// Optional component.
Resolve(uid, ref appearance, ref hands, false);
if (!standingState.Standing)
if (standingState.CurrentState is StandingState.Lying or StandingState.GettingUp)
return true;
// This is just to avoid most callers doing this manually saving boilerplate
@@ -49,13 +53,16 @@ namespace Content.Shared.Standing
RaiseLocalEvent(uid, new DropHandItemsEvent(), false);
}
if (TryComp(uid, out BuckleComponent? buckle) && buckle.Buckled && !_buckle.TryUnbuckle(uid, uid, buckleComp: buckle)) // WD EDIT
return false;
var msg = new DownAttemptEvent();
RaiseLocalEvent(uid, msg, false);
if (msg.Cancelled)
return false;
standingState.Standing = false;
standingState.CurrentState = StandingState.Lying;
Dirty(standingState);
RaiseLocalEvent(uid, new DownedEvent(), false);
@@ -82,9 +89,10 @@ namespace Content.Shared.Standing
if (playSound)
{
_audio.PlayPredicted(standingState.DownSound, uid, uid);
_audio.PlayPredicted(standingState.DownSound, uid, null);
}
_movement.RefreshMovementSpeedModifiers(uid); // WD EDIT
return true;
}
@@ -100,9 +108,12 @@ namespace Content.Shared.Standing
// Optional component.
Resolve(uid, ref appearance, false);
if (standingState.Standing)
if (standingState.CurrentState is 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();
@@ -112,7 +123,7 @@ namespace Content.Shared.Standing
return false;
}
standingState.Standing = true;
standingState.CurrentState = StandingState.Standing;
Dirty(uid, standingState);
RaiseLocalEvent(uid, new StoodEvent(), false);
@@ -127,6 +138,7 @@ namespace Content.Shared.Standing
}
}
standingState.ChangedFixtures.Clear();
_movement.RefreshMovementSpeedModifiers(uid); // WD EDIT
return true;
}

View File

@@ -1,3 +1,4 @@
using Content.Shared._White.Standing;
using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs;
using Content.Shared.Audio;
@@ -19,6 +20,7 @@ using Content.Shared.StatusEffect;
using Content.Shared.Throwing;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Player;
@@ -32,6 +34,8 @@ public abstract class SharedStunSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly StandingStateSystem _standingState = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffect = default!;
[Dependency] private readonly SharedLayingDownSystem _layingDown = default!; // WD EDIT
[Dependency] private readonly SharedContainerSystem _container = default!; // WD EDIT
/// <summary>
/// Friction modifier for knocked down players.
@@ -109,12 +113,25 @@ public abstract class SharedStunSystem : EntitySystem
private void OnKnockInit(EntityUid uid, KnockedDownComponent component, ComponentInit args)
{
_standingState.Down(uid);
RaiseNetworkEvent(new CheckAutoGetUpEvent(GetNetEntity(uid))); // WD EDIT
_layingDown.TryLieDown(uid, null, null, DropHeldItemsBehavior.DropIfStanding); // WD EDIT
}
private void OnKnockShutdown(EntityUid uid, KnockedDownComponent component, ComponentShutdown args)
{
_standingState.Stand(uid);
// WD EDIT START
if (!TryComp(uid, out StandingStateComponent? standing))
return;
if (TryComp(uid, out LayingDownComponent? layingDown))
{
if (layingDown.AutoGetUp && !_container.IsEntityInContainer(uid))
_layingDown.TryStandUp(uid, layingDown);
return;
}
_standingState.Stand(uid, standing);
// WD EDIT END
}
private void OnStandAttempt(EntityUid uid, KnockedDownComponent component, StandAttemptEvent args)

View File

@@ -7,6 +7,9 @@ public sealed class WhiteCVars
{
#region Keybind
public static readonly CVarDef<bool> AutoGetUp =
CVarDef.Create("white.auto_get_up", true, CVar.CLIENT | CVar.ARCHIVE | CVar.REPLICATED);
public static readonly CVarDef<bool> HoldLookUp =
CVarDef.Create("white.hold_look_up", false, CVar.CLIENT | CVar.ARCHIVE);

View File

@@ -0,0 +1,25 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared._White.Standing;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
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.4f;
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
public bool AutoGetUp;
}
[Serializable, NetSerializable]
public sealed class ChangeLayingDownEvent : CancellableEntityEventArgs;
[Serializable, NetSerializable]
public sealed class CheckAutoGetUpEvent(NetEntity user) : CancellableEntityEventArgs
{
public NetEntity User = user;
}

View File

@@ -0,0 +1,162 @@
using Content.Shared.DoAfter;
using Content.Shared.Gravity;
using Content.Shared.Input;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Systems;
using Content.Shared.Standing;
using Content.Shared.Stunnable;
using Robust.Shared.Input.Binding;
using Robust.Shared.Player;
using Robust.Shared.Serialization;
namespace Content.Shared._White.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!;
public override void Initialize()
{
CommandBinds.Builder
.Bind(ContentKeyFunctions.ToggleStanding, InputCmdHandler.FromDelegate(ToggleStanding))
.Register<SharedLayingDownSystem>();
SubscribeNetworkEvent<ChangeLayingDownEvent>(OnChangeState);
SubscribeLocalEvent<StandingStateComponent, StandingUpDoAfterEvent>(OnStandingUpDoAfter);
SubscribeLocalEvent<LayingDownComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeed);
SubscribeLocalEvent<LayingDownComponent, EntParentChangedMessage>(OnParentChanged);
}
public override void Shutdown()
{
base.Shutdown();
CommandBinds.Unregister<SharedLayingDownSystem>();
}
private void ToggleStanding(ICommonSession? session)
{
if (session?.AttachedEntity == null ||
!HasComp<LayingDownComponent>(session.AttachedEntity) ||
_gravity.IsWeightless(session.AttachedEntity.Value))
{
return;
}
RaiseNetworkEvent(new ChangeLayingDownEvent());
}
private void OnChangeState(ChangeLayingDownEvent ev, EntitySessionEventArgs args)
{
if (!args.SenderSession.AttachedEntity.HasValue)
return;
var uid = args.SenderSession.AttachedEntity.Value;
// TODO: Wizard
//if (HasComp<FrozenComponent>(uid))
// return;
if (!TryComp(uid, out StandingStateComponent? standing) ||
!TryComp(uid, out LayingDownComponent? layingDown))
{
return;
}
RaiseNetworkEvent(new CheckAutoGetUpEvent(GetNetEntity(uid)));
if (HasComp<KnockedDownComponent>(uid) || !_mobState.IsAlive(uid))
return;
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<KnockedDownComponent>(uid) ||
_mobState.IsIncapacitated(uid) || !_standing.Stand(uid))
{
component.CurrentState = StandingState.Lying;
}
component.CurrentState = StandingState.Standing;
}
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<StandingStateComponent>(uid, out var standingState)
|| standingState.CurrentState is StandingState.Standing
|| Transform(uid).GridUid != null)
{
return;
}
_standing.Stand(uid, standingState);
}
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) ||
TerminatingOrDeleted(uid))
{
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;
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)
{
if (behavior == DropHeldItemsBehavior.AlwaysDrop)
RaiseLocalEvent(uid, new DropHandItemsEvent());
return false;
}
_standing.Down(uid, true, behavior != DropHeldItemsBehavior.NoDrop, standingState);
return true;
}
}
[Serializable, NetSerializable]
public sealed partial class StandingUpDoAfterEvent : SimpleDoAfterEvent;
public enum DropHeldItemsBehavior : byte
{
NoDrop,
DropIfStanding,
AlwaysDrop
}

View File

@@ -1,2 +1,3 @@
ui-options-function-look-up = Look up/Take aim
ui-options-function-auto-get-up = Automatically get up after falling
ui-options-function-hold-look-up = Hold down the key to aim

View File

@@ -1,2 +1,3 @@
ui-options-function-look-up = Присмотреться/Прицелиться
ui-options-function-auto-get-up = Автоматически вставать при падении
ui-options-function-hold-look-up = Удерживать клавишу для прицеливания