Bläddra i källkod

Stalker's lay and sprinting modules

Taislin 7 månader sedan
förälder
incheckning
0a13940d67
67 ändrade filer med 1965 tillägg och 611 borttagningar
  1. 1 1
      Content.Client/Input/ContentContexts.cs
  2. 1 0
      Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs
  3. 2 2
      Content.Server/GameTicking/GameTicker.GamePreset.cs
  4. 21 0
      Content.Server/_Stalker/Characteristics/Modifiers/BaseCharacteristicFloatModifierComponent.cs
  5. 41 0
      Content.Server/_Stalker/Characteristics/Modifiers/BaseCharacteristicFloatModifierSystem.cs
  6. 3 0
      Content.Server/_Stalker/Characteristics/Modifiers/BaseCharacteristicModifierComponent.cs
  7. 17 0
      Content.Server/_Stalker/Characteristics/Modifiers/MovementSpeed/CharacteristicModifierMovementSpeedComponent.cs
  8. 22 0
      Content.Server/_Stalker/Characteristics/Modifiers/MovementSpeed/CharacteristicModifierMovementSpeedSystem.cs
  9. 19 0
      Content.Server/_Stalker/Characteristics/Modifiers/Stamina/CritThreshold/CharacteristicModifierStaminaCritThresholdComponent.cs
  10. 6 0
      Content.Server/_Stalker/Characteristics/Modifiers/Stamina/CritThreshold/CharacteristicModifierStaminaCritThresholdSystem.cs
  11. 19 0
      Content.Server/_Stalker/Characteristics/Modifiers/Stamina/Decay/CharacteristicModifierStaminaDecayComponent.cs
  12. 6 0
      Content.Server/_Stalker/Characteristics/Modifiers/Stamina/Decay/CharacteristicModifierStaminaDecaySystem.cs
  13. 19 0
      Content.Server/_Stalker/Characteristics/Modifiers/Weight/CharacteristicModifierWeightMaximumComponent.cs
  14. 7 0
      Content.Server/_Stalker/Characteristics/Modifiers/Weight/CharacteristicModifierWeightMaximumSystem.cs
  15. 10 0
      Content.Server/_Stalker/Lay/Events/STLayStateChangedEvent.cs
  16. 14 0
      Content.Server/_Stalker/Lay/STLaidComponent.cs
  17. 25 0
      Content.Server/_Stalker/Lay/STLayComponent.cs
  18. 28 0
      Content.Server/_Stalker/Lay/STLaySystem.Handle.cs
  19. 62 0
      Content.Server/_Stalker/Lay/STLaySystem.Laid.cs
  20. 77 0
      Content.Server/_Stalker/Lay/STLaySystem.cs
  21. 27 0
      Content.Server/_Stalker/MoveSpeed/StalkerMoveSpeedComponent.cs
  22. 86 0
      Content.Server/_Stalker/MoveSpeed/StalkerMoveSpeedSystem.cs
  23. 77 0
      Content.Server/_Stalker/Stamina/StaminaActiveSystem.cs
  24. 7 0
      Content.Server/_Stalker/Weight/Modifier/STWeightMaximumModifierComponent.cs
  25. 5 0
      Content.Server/_Stalker/Weight/Modifier/STWeightMaximumModifierSystem.cs
  26. 17 0
      Content.Server/_Stalker/Weight/STWeightSystem.Modifier.cs
  27. 18 0
      Content.Server/_Stalker/Weight/STWeightSystem.Throwing.cs
  28. 123 0
      Content.Server/_Stalker/Weight/STWeightSystem.cs
  29. 2 2
      Content.Shared/CCVar/CCVars.Game.cs
  30. 3 3
      Content.Shared/CCVar/CCVars.Shuttle.cs
  31. 11 0
      Content.Shared/Damage/Components/StaminaComponent.cs
  32. 7 6
      Content.Shared/Damage/Systems/StaminaSystem.cs
  33. 2 0
      Content.Shared/Input/ContentKeyFunctions.cs
  34. 24 3
      Content.Shared/Movement/Components/MovementSpeedModifierComponent.cs
  35. 12 4
      Content.Shared/Standing/StandingStateSystem.cs
  36. 43 0
      Content.Shared/_Stalker/Characteristics/Characteristic.cs
  37. 10 0
      Content.Shared/_Stalker/Characteristics/CharacteristicAccess.cs
  38. 35 0
      Content.Shared/_Stalker/Characteristics/CharacteristicPrototype.cs
  39. 42 0
      Content.Shared/_Stalker/Characteristics/CharacteristicSystem.cs
  40. 17 0
      Content.Shared/_Stalker/Characteristics/CharacteristicType.cs
  41. 8 0
      Content.Shared/_Stalker/Characteristics/CharacteristicUpdatedEvent.cs
  42. 3 0
      Content.Shared/_Stalker/Characteristics/SharedCharacteristicContainerSystem.cs
  43. 8 0
      Content.Shared/_Stalker/Characteristics/TrainingCompleteDoAfterEvent.cs
  44. 7 0
      Content.Shared/_Stalker/Damage/Components/Stamina/StaminaCritThresholdModifierComponent.cs
  45. 6 0
      Content.Shared/_Stalker/Damage/Components/Stamina/StaminaDecayModifierComponent.cs
  46. 6 0
      Content.Shared/_Stalker/Damage/Systems/Stamina/StaminaCritThresholdModifierSystem.cs
  47. 6 0
      Content.Shared/_Stalker/Damage/Systems/Stamina/StaminaDecayModifierSystem.cs
  48. 21 0
      Content.Shared/_Stalker/Lay/Events/STLayDoAfterEvent.cs
  49. 10 0
      Content.Shared/_Stalker/Lay/STLayState.cs
  50. 4 0
      Content.Shared/_Stalker/Modifier/BaseFloatModifiableComponent.cs
  51. 3 0
      Content.Shared/_Stalker/Modifier/BaseFloatModifierComponent.cs
  52. 36 0
      Content.Shared/_Stalker/Modifier/BaseFloatModifierSystem.cs
  53. 7 0
      Content.Shared/_Stalker/Modifier/BaseModifierComponent.cs
  54. 11 0
      Content.Shared/_Stalker/Modifier/FloatModifierRefreshEvent.cs
  55. 6 0
      Content.Shared/_Stalker/Modifier/UpdatedFloatModifierEvent.cs
  56. 60 0
      Content.Shared/_Stalker/MoveSpeed/StalkerMoveSpeedSystemShared.cs
  57. 40 0
      Content.Shared/_Stalker/Stamina/StaminaActiveComponent.cs
  58. 15 0
      Content.Shared/_Stalker/Throwing/STBeforeThrowedEvent.cs
  59. 13 0
      Content.Shared/_Stalker/Weight/GetWeightModifiersEvent.cs
  60. 60 0
      Content.Shared/_Stalker/Weight/STWeightComponent.cs
  61. 52 0
      Content.Shared/_Stalker/Weight/SharedWeightExamineInfoSystem.cs
  62. 9 0
      Resources/Changelog/Changelog.yml
  63. 1 1
      Resources/ConfigPresets/Civ/production.toml
  64. 2 2
      Resources/Locale/en-US/game-ticking/game-presets/preset-extended.ftl
  65. 12 0
      Resources/Prototypes/Entities/Mobs/Species/base.yml
  66. 3 3
      Resources/Prototypes/game_presets.yml
  67. 588 584
      Resources/keybinds.yml

+ 1 - 1
Content.Client/Input/ContentContexts.cs

@@ -84,7 +84,7 @@ public static void SetupContexts(IInputContextContainer contexts)
             human.AddFunction(ContentKeyFunctions.Arcade1);
             human.AddFunction(ContentKeyFunctions.Arcade2);
             human.AddFunction(ContentKeyFunctions.Arcade3);
-
+            human.AddFunction(ContentKeyFunctions.Lay); // Stalker-Changes-UI
             // actions should be common (for ghosts, mobs, etc)
             common.AddFunction(ContentKeyFunctions.OpenActionsMenu);
 

+ 1 - 0
Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs

@@ -189,6 +189,7 @@ void AddCheckBox(string checkBoxName, bool currentState, Action<BaseButton.Butto
             AddButton(ContentKeyFunctions.SmartEquipBackpack);
             AddButton(ContentKeyFunctions.SmartEquipBelt);
             AddButton(ContentKeyFunctions.OpenBackpack);
+            AddButton(ContentKeyFunctions.Lay); // Stalker-Changes-UI
             AddButton(ContentKeyFunctions.OpenBelt);
             AddButton(ContentKeyFunctions.ThrowItemInHand);
             AddButton(ContentKeyFunctions.TryPullObject);

+ 2 - 2
Content.Server/GameTicking/GameTicker.GamePreset.cs

@@ -86,7 +86,7 @@ void FailedPresetRestart()
 
         private void InitializeGamePreset()
         {
-            SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "extended");
+            SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "nomads");
         }
 
         public void SetGamePreset(GamePresetPrototype? preset, bool force = false)
@@ -108,7 +108,7 @@ public void SetGamePreset(GamePresetPrototype? preset, bool force = false)
         public void SetGamePreset(string preset, bool force = false)
         {
             var proto = FindGamePreset(preset);
-            if(proto != null)
+            if (proto != null)
                 SetGamePreset(proto, force);
         }
 

+ 21 - 0
Content.Server/_Stalker/Characteristics/Modifiers/BaseCharacteristicFloatModifierComponent.cs

@@ -0,0 +1,21 @@
+using Content.Shared._Stalker.Characteristics;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers;
+
+public abstract partial class BaseCharacteristicFloatModifierComponent : BaseCharacteristicModifierComponent
+{
+    [ViewVariables]
+    public virtual float Value { get; set; } = 0f;
+
+    [DataField]
+    public virtual CharacteristicType AllowedType { get; set; } = CharacteristicType.Dexterity;
+
+    [DataField]
+    public virtual float MinModifier { get; set; } = 0.1f;
+
+    [DataField]
+    public virtual float MaxModifier { get; set; } = 1.7f;
+
+    [DataField]
+    public virtual float Modifier { get; set; } = 0.05f;
+}

+ 41 - 0
Content.Server/_Stalker/Characteristics/Modifiers/BaseCharacteristicFloatModifierSystem.cs

@@ -0,0 +1,41 @@
+using Content.Shared._Stalker.Characteristics;
+using Content.Shared._Stalker.Modifier;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers;
+
+public abstract class
+    BaseCharacteristicFloatModifierSystem<TCharacteristicModifierComponent, TModifierComponent, TModifierSystem> : EntitySystem
+        where TCharacteristicModifierComponent : BaseCharacteristicFloatModifierComponent
+        where TModifierComponent : BaseFloatModifierComponent
+        where TModifierSystem : BaseFloatModifierSystem<TModifierComponent>
+{
+    [Dependency] private readonly TModifierSystem _modifierSystem = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<TCharacteristicModifierComponent, CharacteristicUpdatedEvent>(OnCharacteristicUpdate);
+        SubscribeLocalEvent<TCharacteristicModifierComponent, FloatModifierRefreshEvent<TModifierComponent>>(OnModifierRefresh);
+    }
+
+    private void OnCharacteristicUpdate(Entity<TCharacteristicModifierComponent> ent, ref CharacteristicUpdatedEvent args)
+    {
+        if (args.Characteristic.Type != ent.Comp.AllowedType)
+            return;
+
+        ent.Comp.Value = GetModifier(ent, args.NewLevel);
+
+        _modifierSystem.RefreshModifiers(ent);
+    }
+
+    private void OnModifierRefresh(Entity<TCharacteristicModifierComponent> ent, ref FloatModifierRefreshEvent<TModifierComponent> args)
+    {
+        args.Modify(ent.Comp.Value);
+    }
+
+    protected virtual float GetModifier(Entity<TCharacteristicModifierComponent> ent, int value)
+    {
+        return Math.Clamp(1f + Math.Abs(value) * ent.Comp.Modifier, ent.Comp.MinModifier, ent.Comp.MaxModifier);
+    }
+}

+ 3 - 0
Content.Server/_Stalker/Characteristics/Modifiers/BaseCharacteristicModifierComponent.cs

@@ -0,0 +1,3 @@
+namespace Content.Server._Stalker.Characteristics.Modifiers;
+
+public abstract partial class BaseCharacteristicModifierComponent : Component;

+ 17 - 0
Content.Server/_Stalker/Characteristics/Modifiers/MovementSpeed/CharacteristicModifierMovementSpeedComponent.cs

@@ -0,0 +1,17 @@
+namespace Content.Server._Stalker.Characteristics.Modifiers.MovementSpeed;
+
+[RegisterComponent]
+public sealed partial class CharacteristicModifierMovementSpeedComponent : Component
+{
+    [DataField]
+    public float MaxBonus = 2f;
+
+    [DataField]
+    public float MinBonus = 0.1f;
+
+    [DataField]
+    public float PositiveModifier = 0.02f;
+
+    [DataField]
+    public float NegativeModifier = -0.02f;
+}

+ 22 - 0
Content.Server/_Stalker/Characteristics/Modifiers/MovementSpeed/CharacteristicModifierMovementSpeedSystem.cs

@@ -0,0 +1,22 @@
+using Content.Shared._Stalker.Characteristics;
+using Content.Shared.Movement.Systems;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers.MovementSpeed;
+
+public sealed class CharacteristicModifierMovementSpeedSystem : EntitySystem
+{
+    [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<CharacteristicModifierMovementSpeedComponent, CharacteristicUpdatedEvent>(OnUpdate);
+    }
+
+    private void OnUpdate(Entity<CharacteristicModifierMovementSpeedComponent> modifier, ref CharacteristicUpdatedEvent args)
+    {
+        _movementSpeedModifier.RefreshMovementSpeedModifiers(modifier);
+    }
+
+}

+ 19 - 0
Content.Server/_Stalker/Characteristics/Modifiers/Stamina/CritThreshold/CharacteristicModifierStaminaCritThresholdComponent.cs

@@ -0,0 +1,19 @@
+using Content.Shared._Stalker.Characteristics;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers.Stamina.CritThreshold;
+
+[RegisterComponent]
+public sealed partial class CharacteristicModifierStaminaCritThresholdComponent : BaseCharacteristicFloatModifierComponent
+{
+    [DataField]
+    public override CharacteristicType AllowedType { get; set; } = CharacteristicType.Endurance;
+
+    [DataField]
+    public override float MinModifier { get; set; } = 0.1f;
+
+    [DataField]
+    public override float MaxModifier { get; set; } = 1.5f;
+
+    [DataField]
+    public override float Modifier { get; set; } = 0.05f;
+}

+ 6 - 0
Content.Server/_Stalker/Characteristics/Modifiers/Stamina/CritThreshold/CharacteristicModifierStaminaCritThresholdSystem.cs

@@ -0,0 +1,6 @@
+using Content.Shared._Stalker.Damage.Components.Stamina;
+using Content.Shared._Stalker.Damage.Systems.Stamina;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers.Stamina.CritThreshold;
+
+public sealed class CharacteristicModifierStaminaCritThresholdSystem: BaseCharacteristicFloatModifierSystem<CharacteristicModifierStaminaCritThresholdComponent, StaminaCritThresholdModifierComponent, StaminaCritThresholdModifierSystem>;

+ 19 - 0
Content.Server/_Stalker/Characteristics/Modifiers/Stamina/Decay/CharacteristicModifierStaminaDecayComponent.cs

@@ -0,0 +1,19 @@
+using Content.Shared._Stalker.Characteristics;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers.Stamina.Decay;
+
+[RegisterComponent]
+public sealed partial class CharacteristicModifierStaminaDecayComponent : BaseCharacteristicFloatModifierComponent
+{
+    [DataField]
+    public override CharacteristicType AllowedType { get; set; } = CharacteristicType.Endurance;
+
+    [DataField]
+    public override float MinModifier { get; set; } = 0.1f;
+
+    [DataField]
+    public override float MaxModifier { get; set; } = 2f;
+
+    [DataField]
+    public override float Modifier { get; set; } = 0.02f;
+}

+ 6 - 0
Content.Server/_Stalker/Characteristics/Modifiers/Stamina/Decay/CharacteristicModifierStaminaDecaySystem.cs

@@ -0,0 +1,6 @@
+using Content.Shared._Stalker.Damage.Components.Stamina;
+using Content.Shared._Stalker.Damage.Systems.Stamina;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers.Stamina.Decay;
+
+public sealed class CharacteristicModifierStaminaDecaySystem : BaseCharacteristicFloatModifierSystem<CharacteristicModifierStaminaDecayComponent, StaminaDecayModifierComponent, StaminaDecayModifierSystem>;

+ 19 - 0
Content.Server/_Stalker/Characteristics/Modifiers/Weight/CharacteristicModifierWeightMaximumComponent.cs

@@ -0,0 +1,19 @@
+using Content.Shared._Stalker.Characteristics;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers.Weight;
+
+[RegisterComponent]
+public sealed partial class CharacteristicModifierWeightMaximumComponent : BaseCharacteristicFloatModifierComponent
+{
+    [DataField]
+    public override CharacteristicType AllowedType { get; set; } = CharacteristicType.Strength;
+
+    [DataField]
+    public override float MinModifier { get; set; } = 0.1f;
+
+    [DataField]
+    public override float MaxModifier { get; set; } = 3f;
+
+    [DataField]
+    public override float Modifier { get; set; } = 0.05f;
+}

+ 7 - 0
Content.Server/_Stalker/Characteristics/Modifiers/Weight/CharacteristicModifierWeightMaximumSystem.cs

@@ -0,0 +1,7 @@
+using Content.Server._Stalker.Weight;
+using Content.Server._Stalker.Weight.Modifier;
+using STWeightMaximumModifierComponent = Content.Server._Stalker.Weight.Modifier.STWeightMaximumModifierComponent;
+
+namespace Content.Server._Stalker.Characteristics.Modifiers.Weight;
+
+public sealed class CharacteristicModifierWeightMaximumSystem : BaseCharacteristicFloatModifierSystem<CharacteristicModifierWeightMaximumComponent, STWeightMaximumModifierComponent, STWeightMaximumModifierSystem>;

+ 10 - 0
Content.Server/_Stalker/Lay/Events/STLayStateChangedEvent.cs

@@ -0,0 +1,10 @@
+using Content.Shared._Stalker.Lay;
+using Robust.Shared.Serialization;
+
+namespace Content.Server._Stalker.Lay.Events;
+
+[Serializable]
+public sealed class STLayStateChangedEvent(STLayState state) : EntityEventArgs
+{
+    public readonly STLayState State = state;
+}

+ 14 - 0
Content.Server/_Stalker/Lay/STLaidComponent.cs

@@ -0,0 +1,14 @@
+namespace Content.Server._Stalker.Lay;
+
+[RegisterComponent]
+public sealed partial class STLaidComponent : Component
+{
+    [DataField]
+    public bool Standing;
+
+    [DataField]
+    public float TileFrictionModifier = 0.4f;
+
+    [DataField]
+    public float MovementSpeedModifier = 0.3f;
+}

+ 25 - 0
Content.Server/_Stalker/Lay/STLayComponent.cs

@@ -0,0 +1,25 @@
+using Content.Shared._Stalker.Lay;
+
+namespace Content.Server._Stalker.Lay;
+
+[RegisterComponent]
+public sealed partial class STLayComponent : Component
+{
+    [DataField]
+    public STLayState State;
+
+    [DataField]
+    public Dictionary<STLayState, TimeSpan> ChangeStateDelay = new()
+    {
+        { STLayState.Stand, TimeSpan.FromSeconds(2.5f) },
+        { STLayState.Laid, TimeSpan.Zero },
+    };
+
+    [DataField]
+    public Dictionary<STLayState, STLayState> StateTransitions = new()
+    {
+        { STLayState.Stand, STLayState.Laid },
+        { STLayState.Laid, STLayState.Stand },
+    };
+}
+

+ 28 - 0
Content.Server/_Stalker/Lay/STLaySystem.Handle.cs

@@ -0,0 +1,28 @@
+using Content.Shared.Input;
+using Robust.Shared.Input.Binding;
+using Robust.Shared.Player;
+
+namespace Content.Server._Stalker.Lay;
+
+public sealed partial class STLaySystem
+{
+    private void InitializeHandle()
+    {
+        CommandBinds.Builder
+            .Bind(ContentKeyFunctions.Lay, InputCmdHandler.FromDelegate(HandleLay, handle: false, outsidePrediction: false))
+            .Register<STLaySystem>();
+    }
+
+    private void HandleLay(ICommonSession? session)
+    {
+        var entity = session?.AttachedEntity;
+        if (entity is null)
+            return;
+
+        if (!TryComp<STLayComponent>(entity, out var comp))
+            return;
+
+        var nextState = comp.StateTransitions[comp.State];
+        StartSetState((entity.Value, comp), nextState);
+    }
+}

+ 62 - 0
Content.Server/_Stalker/Lay/STLaySystem.Laid.cs

@@ -0,0 +1,62 @@
+using Content.Shared.Movement.Events;
+using Content.Shared.Movement.Systems;
+using Content.Shared.Standing;
+
+namespace Content.Server._Stalker.Lay;
+
+public sealed partial class STLaySystem
+{
+    [Dependency] private readonly StandingStateSystem _standingState = default!;
+    [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
+
+    private void InitializeLaid()
+    {
+        SubscribeLocalEvent<STLaidComponent, ComponentInit>(OnLaidInit);
+        SubscribeLocalEvent<STLaidComponent, ComponentShutdown>(OnLaidShutdown);
+        SubscribeLocalEvent<STLaidComponent, StandAttemptEvent>(OnLaidStandAttempt);
+
+        SubscribeLocalEvent<STLaidComponent, TileFrictionEvent>(OnLaidTileFriction);
+        SubscribeLocalEvent<STLaidComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
+    }
+
+    private void OnLaidInit(Entity<STLaidComponent> laid, ref ComponentInit args)
+    {
+        _standingState.Down(laid, dropHeldItems: false, fixtureAttempt: false);
+        _movementSpeedModifier.RefreshMovementSpeedModifiers(laid);
+    }
+
+    private void OnLaidShutdown(Entity<STLaidComponent> laid, ref ComponentShutdown args)
+    {
+        laid.Comp.Standing = true;
+        _standingState.Stand(laid);
+        laid.Comp.Standing = true;
+        _movementSpeedModifier.RefreshMovementSpeedModifiers(laid);
+    }
+
+    private void OnLaidStandAttempt(Entity<STLaidComponent> laid, ref StandAttemptEvent args)
+    {
+        var standing = laid.Comp.Standing;
+        laid.Comp.Standing = false;
+
+        if (args.Cancelled)
+            return;
+
+        if (standing)
+            return;
+
+        args.Cancel();
+    }
+
+    private void OnLaidTileFriction(Entity<STLaidComponent> laid, ref TileFrictionEvent args)
+    {
+        args.Modifier *= laid.Comp.TileFrictionModifier;
+    }
+
+    private void OnRefreshMovementSpeedModifiers(Entity<STLaidComponent> laid, ref RefreshMovementSpeedModifiersEvent args)
+    {
+        if (laid.Comp.Standing)
+            return;
+
+        args.ModifySpeed(laid.Comp.MovementSpeedModifier, laid.Comp.MovementSpeedModifier);
+    }
+}

+ 77 - 0
Content.Server/_Stalker/Lay/STLaySystem.cs

@@ -0,0 +1,77 @@
+using Content.Server._Stalker.Lay.Events;
+using Content.Server.DoAfter;
+using Content.Shared._Stalker.Lay;
+using Content.Shared._Stalker.Lay.Events;
+using Content.Shared.DoAfter;
+
+namespace Content.Server._Stalker.Lay;
+
+public sealed partial class STLaySystem : EntitySystem
+{
+    [Dependency] private readonly DoAfterSystem _doAfter = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        InitializeLaid();
+        InitializeHandle();
+
+        SubscribeLocalEvent<STLayComponent, STLayDoAfterEvent>(OnStateDoAfter);
+        SubscribeLocalEvent<STLayComponent, STLayStateChangedEvent>(OnStateChanged);
+    }
+
+    private void OnStateDoAfter(Entity<STLayComponent> lay, ref STLayDoAfterEvent args)
+    {
+        if (args.Cancelled)
+            return;
+
+        SetState(lay, args.NextState);
+    }
+
+    private void OnStateChanged(Entity<STLayComponent> lay, ref STLayStateChangedEvent args)
+    {
+        switch (args.State)
+        {
+            case STLayState.Stand:
+                RemComp<STLaidComponent>(lay);
+                break;
+
+            case STLayState.Laid:
+                AddComp<STLaidComponent>(lay);
+                break;
+
+            default:
+                throw new ArgumentOutOfRangeException();
+        }
+    }
+
+    private void StartSetState(Entity<STLayComponent> lay, STLayState state)
+    {
+        var delay = lay.Comp.ChangeStateDelay[state];
+
+        if (delay == TimeSpan.Zero)
+        {
+            SetState(lay, state);
+            return;
+        }
+
+        var args = new DoAfterArgs(EntityManager, lay, delay, new STLayDoAfterEvent(state), lay)
+        {
+            NeedHand = true,
+            BreakOnHandChange = false,
+            BreakOnMove = true,
+            CancelDuplicate = false,
+        };
+
+        _doAfter.TryStartDoAfter(args);
+    }
+
+    private void SetState(Entity<STLayComponent> lay, STLayState state)
+    {
+        lay.Comp.State = state;
+
+        var ev = new STLayStateChangedEvent(state);
+        RaiseLocalEvent(lay, ev);
+    }
+}

+ 27 - 0
Content.Server/_Stalker/MoveSpeed/StalkerMoveSpeedComponent.cs

@@ -0,0 +1,27 @@
+namespace Content.Server._Stalker.MoveSpeed;
+
+/// <summary>
+/// This is used for...
+/// </summary>
+[RegisterComponent]
+public sealed partial class StalkerMoveSpeedComponent : Component
+{
+    public bool SyncedWalkSpeed = true;
+
+    public bool SyncedSprintSpeed = true;
+
+    [ViewVariables(VVAccess.ReadWrite)]
+    public float StartWalkSpeed = 0f;
+
+    [ViewVariables(VVAccess.ReadWrite)]
+    public float StartSprintSpeed = 0f;
+
+    public Dictionary<string, float> BonusSpeedWalkProcent = new(0);
+    public Dictionary<string, float> BonusSpeedSprintProcent = new(0);
+
+    [ViewVariables(VVAccess.ReadWrite)]
+    public float SumBonusSpeedWalk = 0f;
+
+    [ViewVariables(VVAccess.ReadWrite)]
+    public float SumBonusSpeedSprint = 0f;
+}

+ 86 - 0
Content.Server/_Stalker/MoveSpeed/StalkerMoveSpeedSystem.cs

@@ -0,0 +1,86 @@
+using Content.Shared._Stalker.MoveSpeed;
+using Content.Shared.Mobs;
+using Content.Shared.Movement.Components;
+
+namespace Content.Server._Stalker.MoveSpeed;
+
+public sealed class StalkerMoveSpeedSystem : StalkerMoveSpeedSystemShared
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<MovementSpeedModifierComponent, ComponentInit>(InstallComponentSpeed);
+        SubscribeAllEvent<StalkerMSSetBonusWalkEvent>(OnStalkerMSSetBonusWalkEvent);
+        SubscribeAllEvent<StalkerMSSetBonusSprintEvent>(OnStalkerMSSetBonusSprintEvent);
+    }
+
+    private void OnStalkerMSSetBonusSprintEvent(StalkerMSSetBonusSprintEvent msg, EntitySessionEventArgs args)
+    {
+        if (!TryComp(msg.Entity, out StalkerMoveSpeedComponent? moveSpeedComponent) ||
+            args.SenderSession.AttachedEntity == null)
+            return;
+
+        SetBonusSpeedSprint(args.SenderSession.AttachedEntity.Value, msg.NameBonus, msg.ValueBonus, moveSpeedComponent);
+    }
+
+    private void OnStalkerMSSetBonusWalkEvent(StalkerMSSetBonusWalkEvent msg, EntitySessionEventArgs args)
+    {
+        if (!TryComp(msg.Entity, out StalkerMoveSpeedComponent? moveSpeedComponent) ||
+            args.SenderSession.AttachedEntity == null)
+            return;
+
+        SetBonusSpeedWalk(args.SenderSession.AttachedEntity.Value, msg.NameBonus, msg.ValueBonus, moveSpeedComponent);
+    }
+
+
+    private void InstallComponentSpeed(EntityUid uid, MovementSpeedModifierComponent component, ComponentInit args)
+    {
+        var stalkerSpeedComp = AddComp<StalkerMoveSpeedComponent>(uid);
+        stalkerSpeedComp.StartWalkSpeed = component._baseWalkSpeedVVpublic;
+        stalkerSpeedComp.StartSprintSpeed = component._baseSprintSpeedVVpublic;
+    }
+
+    public void SetBonusSpeedWalk(EntityUid uid, string nameBonus, float valueBonus, StalkerMoveSpeedComponent stalkerSpeedComp)
+    {
+        stalkerSpeedComp.BonusSpeedWalkProcent[nameBonus] = valueBonus;
+        CalculateSpeedByFormula(stalkerSpeedComp);
+        SyncWalkSpeed((uid, stalkerSpeedComp));
+    }
+
+    public void SetBonusSpeedSprint(EntityUid uid, string nameBonus, float valueBonus, StalkerMoveSpeedComponent stalkerSpeedComp)
+    {
+        stalkerSpeedComp.BonusSpeedSprintProcent[nameBonus] = valueBonus;
+        CalculateSpeedByFormula(stalkerSpeedComp);
+        SyncSprintSpeed((uid, stalkerSpeedComp));
+    }
+
+    public void SyncWalkSpeed(Entity<StalkerMoveSpeedComponent> stalkerSpeedComp)
+    {
+        if (!TryComp<MovementSpeedModifierComponent>(stalkerSpeedComp.Owner, out var movementSpeed))
+            return;
+        movementSpeed._baseWalkSpeedVVpublic = stalkerSpeedComp.Comp.SumBonusSpeedWalk;
+    }
+
+    public void SyncSprintSpeed(Entity<StalkerMoveSpeedComponent> stalkerSpeedComp)
+    {
+        if (!TryComp<MovementSpeedModifierComponent>(stalkerSpeedComp.Owner, out var movementSpeed))
+            return;
+        movementSpeed._baseSprintSpeedVVpublic = stalkerSpeedComp.Comp.SumBonusSpeedSprint;
+    }
+    public void CalculateSpeedByFormula(StalkerMoveSpeedComponent stalkerSpeedComp)
+    {
+        var sumProcentWalk = 0f;
+        foreach (var keyValuePair in stalkerSpeedComp.BonusSpeedWalkProcent)
+        {
+            sumProcentWalk += keyValuePair.Value;
+        }
+        stalkerSpeedComp.SumBonusSpeedWalk = stalkerSpeedComp.StartWalkSpeed + stalkerSpeedComp.StartWalkSpeed / 100f * sumProcentWalk;
+
+        var sumProcentSprint = 0f;
+        foreach (var keyValuePair in stalkerSpeedComp.BonusSpeedSprintProcent)
+        {
+            sumProcentSprint += keyValuePair.Value;
+        }
+        stalkerSpeedComp.SumBonusSpeedSprint = stalkerSpeedComp.StartSprintSpeed + stalkerSpeedComp.StartSprintSpeed / 100f * sumProcentSprint;
+    }
+}

+ 77 - 0
Content.Server/_Stalker/Stamina/StaminaActiveSystem.cs

@@ -0,0 +1,77 @@
+using Content.Shared._Stalker.Stamina;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
+using Content.Shared.Movement.Components;
+using Content.Shared.Movement.Systems;
+using Robust.Shared.Physics.Components;
+
+namespace Content.Server._Stalker.Stamina;
+
+public sealed class StaminaActiveSystem : EntitySystem
+{
+    [Dependency] private readonly StaminaSystem _stamina = default!;
+    [Dependency] private readonly MovementSpeedModifierSystem _speed = default!;
+    private ISawmill _sawmill = default!;
+
+    public override void Initialize()
+    {
+        SubscribeLocalEvent<StaminaActiveComponent, RefreshMovementSpeedModifiersEvent>(OnRefresh);
+    }
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<StaminaComponent, MovementSpeedModifierComponent, StaminaActiveComponent, InputMoverComponent>();
+        while (query.MoveNext(out var uid, out var stamina, out var modifier, out var active, out var input))
+        {
+            // If our entity is slowed, we can't apply new speed/speed modifiers
+            // Because CurrentSprintSpeed will change
+            if (!active.Slowed)
+            {
+                active.SprintModifier = modifier.BaseWalkSpeed / modifier.BaseSprintSpeed;
+            }
+
+            if (!TryComp<PhysicsComponent>(uid, out var phys))
+                return;
+
+            // If Walk button pressed we will apply stamina damage.
+            if (input.HeldMoveButtons.HasFlag(MoveButtons.Walk) && !active.Slowed && phys.LinearVelocity.Length() != 0)
+            {
+                _stamina.TakeStaminaDamage(uid, active.RunStaminaDamage, stamina, visual: false);
+            }
+
+            // If our entity gets through SlowThreshold, we will apply slowing.
+            // If our entity is slowed already, we don't need to multiply SprintModifier.
+            if (stamina.StaminaDamage >= active.SlowThreshold && active.Slowed == false)
+            {
+                active.Slowed = true;
+                active.Change = true;
+                _speed.RefreshMovementSpeedModifiers(uid);
+                return;
+            }
+
+            // If our entity revives until ReviveStaminaLevel we will remove same SprintModifier.
+            // If our entity is already revived, we _don't need to remove SprintModifier.
+            if (stamina.StaminaDamage <= active.ReviveStaminaLevel && active.Slowed)
+            {
+                active.Slowed = false;
+                active.Change = true;
+                _speed.RefreshMovementSpeedModifiers(uid);
+                return;
+            }
+        }
+    }
+
+    private void OnRefresh(EntityUid uid, StaminaActiveComponent component, RefreshMovementSpeedModifiersEvent args)
+    {
+        if (!component.Change)
+            return;
+
+        var sprint = component.Slowed
+            ? component.SprintModifier
+            : args.SprintSpeedModifier;
+
+        args.ModifySpeed(args.WalkSpeedModifier, sprint);
+    }
+}

+ 7 - 0
Content.Server/_Stalker/Weight/Modifier/STWeightMaximumModifierComponent.cs

@@ -0,0 +1,7 @@
+using Content.Shared._Stalker.Modifier;
+using Robust.Shared.GameStates;
+
+namespace Content.Server._Stalker.Weight.Modifier;
+
+[RegisterComponent]
+public sealed partial class STWeightMaximumModifierComponent : BaseFloatModifierComponent;

+ 5 - 0
Content.Server/_Stalker/Weight/Modifier/STWeightMaximumModifierSystem.cs

@@ -0,0 +1,5 @@
+using Content.Shared._Stalker.Modifier;
+
+namespace Content.Server._Stalker.Weight.Modifier;
+
+public sealed class STWeightMaximumModifierSystem : BaseFloatModifierSystem<STWeightMaximumModifierComponent>;

+ 17 - 0
Content.Server/_Stalker/Weight/STWeightSystem.Modifier.cs

@@ -0,0 +1,17 @@
+using Content.Shared._Stalker.Modifier;
+using Content.Shared._Stalker.Weight;
+
+namespace Content.Server._Stalker.Weight;
+
+public sealed partial class STWeightSystem
+{
+    private void InitializeModifier()
+    {
+        SubscribeLocalEvent<STWeightComponent, UpdatedFloatModifierEvent<Modifier.STWeightMaximumModifierComponent>>(OnUpdatedMaximum);
+    }
+
+    private void OnUpdatedMaximum(Entity<STWeightComponent> weight, ref UpdatedFloatModifierEvent<Modifier.STWeightMaximumModifierComponent> args)
+    {
+        weight.Comp.MaximumModifier = args.Modifier;
+    }
+}

+ 18 - 0
Content.Server/_Stalker/Weight/STWeightSystem.Throwing.cs

@@ -0,0 +1,18 @@
+using Content.Shared._Stalker.Throwing;
+using Content.Shared._Stalker.Weight;
+
+namespace Content.Server._Stalker.Weight;
+
+public sealed partial class STWeightSystem
+{
+    private void InitializeThrowing()
+    {
+        SubscribeLocalEvent<STWeightComponent, STBeforeThrowedEvent>(OnBeforeThrow);
+    }
+
+    private void OnBeforeThrow(Entity<STWeightComponent> entity, ref STBeforeThrowedEvent args)
+    {
+        var modifier = Math.Max(entity.Comp.WeightThrowMinStrengthModifier, entity.Comp.Total * entity.Comp.WeightThrowModifier);
+        args.Strength /= modifier;
+    }
+}

+ 123 - 0
Content.Server/_Stalker/Weight/STWeightSystem.cs

@@ -0,0 +1,123 @@
+using Content.Shared._Stalker.Weight;
+using Content.Shared.Movement.Systems;
+
+namespace Content.Server._Stalker.Weight;
+
+public sealed partial class STWeightSystem : EntitySystem
+{
+    [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        InitializeModifier();
+        InitializeThrowing();
+
+        SubscribeLocalEvent<STWeightComponent, EntParentChangedMessage>(OnParentChanged);
+        SubscribeLocalEvent<STWeightComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
+    }
+
+    private void OnParentChanged(Entity<STWeightComponent> weight, ref EntParentChangedMessage args)
+    {
+        UpdateWeight(weight);
+        TryUpdateWeight(args.OldParent);
+    }
+
+    private void OnRefreshMovementSpeedModifiers(Entity<STWeightComponent> weight, ref RefreshMovementSpeedModifiersEvent args)
+    {
+        args.ModifySpeed(weight.Comp.MovementSpeedModifier, weight.Comp.MovementSpeedModifier);
+    }
+
+    private void SetMovementSpeedModifiers(Entity<STWeightComponent> weight, float modifier)
+    {
+        weight.Comp.MovementSpeedModifier = modifier;
+
+        _movementSpeedModifier.RefreshMovementSpeedModifiers(weight);
+    }
+
+    private void SetInsideWeight(Entity<STWeightComponent> weight, float inside)
+    {
+        var ev = new GetWeightModifiersEvent(inside, weight.Comp.Self);
+        RaiseLocalEvent(weight, ev);
+
+        weight.Comp.InsideWeight = ev.Inside;
+
+        // Update movement speed
+        var speedModifier = Math.Clamp(1 - (weight.Comp.Total - weight.Comp.Overload) / (weight.Comp.TotalMaximum - weight.Comp.TotalOverload), 0f, 1f);
+        SetMovementSpeedModifiers(weight, speedModifier);
+    }
+
+    private void UpdateWeight(Entity<STWeightComponent> weight)
+    {
+        if (!TryComp<TransformComponent>(weight, out var transform))
+            return;
+
+        var newInside = 0f;
+        var enumerator = transform.ChildEnumerator;
+        while (enumerator.MoveNext(out var uid))
+        {
+            if (!TryGetWeight(uid, out var childWeight))
+                continue;
+
+            newInside += childWeight;
+        }
+
+        SetInsideWeight(weight, newInside);
+
+        // Call update the weight of the parent component if there is one
+        TryUpdateParent(weight);
+    }
+
+    public void SetWeightLimits(Entity<STWeightComponent> weight, float newOverload, float newMaximum)
+    {
+        weight.Comp.Maximum = newMaximum;
+        weight.Comp.Overload = newOverload;
+
+        UpdateWeight(weight);
+    }
+
+    public bool TrySetWeightLimits(EntityUid uid, float newOverload, float newMaximum, STWeightComponent? weight = null)
+    {
+        if (!Resolve(uid, ref weight))
+            return false;
+
+        if (newMaximum == weight.Maximum && newOverload == weight.Overload)
+            return false;
+
+        SetWeightLimits((uid, weight), newOverload, newMaximum);
+        return true;
+    }
+
+    public bool TryUpdateWeight(EntityUid? uid)
+    {
+        if (uid is not { } target)
+            return false;
+
+        if (!TryComp<STWeightComponent>(uid, out var comp))
+            return false;
+
+        UpdateWeight((target, comp));
+        return true;
+    }
+
+    private bool TryGetWeight(EntityUid uid, out float weight)
+    {
+        weight = 0f;
+        if (!TryComp<STWeightComponent>(uid, out var comp))
+            return false;
+
+        weight = comp.Total;
+        return true;
+    }
+
+    private bool TryUpdateParent(Entity<STWeightComponent> weight)
+    {
+        var parent = Transform(weight).ParentUid;
+        if (!TryComp<STWeightComponent>(parent, out var comp))
+            return false;
+
+        UpdateWeight((parent, comp));
+        return true;
+    }
+}

+ 2 - 2
Content.Shared/CCVar/CCVars.Game.cs

@@ -33,7 +33,7 @@ public sealed partial class CCVars
     ///     Controls the default game preset.
     /// </summary>
     public static readonly CVarDef<string>
-        GameLobbyDefaultPreset = CVarDef.Create("game.defaultpreset", "extended", CVar.ARCHIVE);
+        GameLobbyDefaultPreset = CVarDef.Create("game.defaultpreset", "nomads", CVar.ARCHIVE);
 
     /// <summary>
     ///     Controls if the game can force a different preset if the current preset's criteria are not met.
@@ -45,7 +45,7 @@ public sealed partial class CCVars
     ///     The preset for the game to fall back to if the selected preset could not be used, and fallback is enabled.
     /// </summary>
     public static readonly CVarDef<string>
-        GameLobbyFallbackPreset = CVarDef.Create("game.fallbackpreset", "Traitor,Extended", CVar.ARCHIVE);
+        GameLobbyFallbackPreset = CVarDef.Create("game.fallbackpreset", "Nomads", CVar.ARCHIVE);
 
     /// <summary>
     ///     Controls if people can win the game in Suspicion or Deathmatch.

+ 3 - 3
Content.Shared/CCVar/CCVars.Shuttle.cs

@@ -31,13 +31,13 @@ public sealed partial class CCVars
     ///     Whether the arrivals shuttle is enabled.
     /// </summary>
     public static readonly CVarDef<bool> ArrivalsShuttles =
-        CVarDef.Create("shuttle.arrivals", true, CVar.SERVERONLY);
+        CVarDef.Create("shuttle.arrivals", false, CVar.SERVERONLY);
 
     /// <summary>
     ///     The map to use for the arrivals station.
     /// </summary>
     public static readonly CVarDef<string> ArrivalsMap =
-        CVarDef.Create("shuttle.arrivals_map", "/Maps/Misc/terminal.yml", CVar.SERVERONLY);
+        CVarDef.Create("shuttle.arrivals_map", "/Maps/civ/nomads_classic.yml", CVar.SERVERONLY);
 
     /// <summary>
     ///     Cooldown between arrivals departures. This should be longer than the FTL time or it will double cycle.
@@ -174,7 +174,7 @@ public sealed partial class CCVars
     /// </summary>
     [CVarControl(AdminFlags.Server | AdminFlags.Mapping, min: 0, max: int.MaxValue)]
     public static readonly CVarDef<int> EmergencyShuttleAutoCallTime =
-        CVarDef.Create("shuttle.auto_call_time", 90, CVar.SERVERONLY);
+        CVarDef.Create("shuttle.auto_call_time", 0, CVar.SERVERONLY);
 
     /// <summary>
     ///     Time in minutes after the round was extended (by recalling the shuttle) to call

+ 11 - 0
Content.Shared/Damage/Components/StaminaComponent.cs

@@ -23,6 +23,8 @@ public sealed partial class StaminaComponent : Component
     [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
     public float Decay = 3f;
 
+    [DataField, AutoNetworkedField]
+    public float DecayModifier = 1f; // stalker-changes
     /// <summary>
     /// How much time after receiving damage until stamina starts decreasing.
     /// </summary>
@@ -41,6 +43,8 @@ public sealed partial class StaminaComponent : Component
     [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
     public float CritThreshold = 100f;
 
+    [DataField, AutoNetworkedField]
+    public float CritThresholdModifier = 1f; // stalker-changes
     /// <summary>
     /// How long will this mob be stunned for?
     /// </summary>
@@ -56,4 +60,11 @@ public sealed partial class StaminaComponent : Component
 
     [DataField]
     public ProtoId<AlertPrototype> StaminaAlert = "Stamina";
+    // stalker-changes-start
+    /// <summary>
+    /// How much stamina damage is required to entire stam crit.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
+    public float SlowdownThreshold = 50f; // CritThreshold / 2
+    // stalker-changes-end
 }

+ 7 - 6
Content.Shared/Damage/Systems/StaminaSystem.cs

@@ -95,7 +95,7 @@ public float GetStaminaDamage(EntityUid uid, StaminaComponent? component = null)
 
         var curTime = _timing.CurTime;
         var pauseTime = _metadata.GetPauseTime(uid);
-        return MathF.Max(0f, component.StaminaDamage - MathF.Max(0f, (float) (curTime - (component.NextUpdate + pauseTime)).TotalSeconds * component.Decay));
+        return MathF.Max(0f, component.StaminaDamage - MathF.Max(0f, (float)(curTime - (component.NextUpdate + pauseTime)).TotalSeconds * component.Decay));
     }
 
     private void OnRejuvenate(EntityUid uid, StaminaComponent component, RejuvenateEvent args)
@@ -211,7 +211,7 @@ private void SetStaminaAlert(EntityUid uid, StaminaComponent? component = null)
             return;
 
         var severity = ContentHelpers.RoundToLevels(MathF.Max(0f, component.CritThreshold - component.StaminaDamage), component.CritThreshold, 7);
-        _alerts.ShowAlert(uid, component.StaminaAlert, (short) severity);
+        _alerts.ShowAlert(uid, component.StaminaAlert, (short)severity);
     }
 
     /// <summary>
@@ -233,7 +233,8 @@ public bool TryTakeStamina(EntityUid uid, float value, StaminaComponent? compone
     }
 
     public void TakeStaminaDamage(EntityUid uid, float value, StaminaComponent? component = null,
-        EntityUid? source = null, EntityUid? with = null, bool visual = true, SoundSpecifier? sound = null)
+        EntityUid? source = null, EntityUid? with = null, bool visual = true, SoundSpecifier? sound = null,
+        bool shouldLog = true) // stalker-changes
     {
         if (!Resolve(uid, ref component, false))
             return;
@@ -259,7 +260,7 @@ public bool TryTakeStamina(EntityUid uid, float value, StaminaComponent? compone
                 component.NextUpdate = nextUpdate;
         }
 
-        var slowdownThreshold = component.CritThreshold / 2f;
+        var slowdownThreshold = component.SlowdownThreshold; // stalker-changes
 
         // If we go above n% then apply slowdown
         if (oldDamage < slowdownThreshold &&
@@ -290,11 +291,11 @@ public bool TryTakeStamina(EntityUid uid, float value, StaminaComponent? compone
 
         if (value <= 0)
             return;
-        if (source != null)
+        if (source != null && shouldLog) // stalker-changes
         {
             _adminLogger.Add(LogType.Stamina, $"{ToPrettyString(source.Value):user} caused {value} stamina damage to {ToPrettyString(uid):target}{(with != null ? $" using {ToPrettyString(with.Value):using}" : "")}");
         }
-        else
+        else if (shouldLog)  // stalker-changes
         {
             _adminLogger.Add(LogType.Stamina, $"{ToPrettyString(uid):target} took {value} stamina damage");
         }

+ 2 - 0
Content.Shared/Input/ContentKeyFunctions.cs

@@ -32,6 +32,8 @@ public static class ContentKeyFunctions
         public static readonly BoundKeyFunction SmartEquipBackpack = "SmartEquipBackpack";
         public static readonly BoundKeyFunction SmartEquipBelt = "SmartEquipBelt";
         public static readonly BoundKeyFunction OpenBackpack = "OpenBackpack";
+
+        public static readonly BoundKeyFunction Lay = "Lay"; // Stalker-Changes-UI
         public static readonly BoundKeyFunction OpenBelt = "OpenBelt";
         public static readonly BoundKeyFunction OpenAHelp = "OpenAHelp";
         public static readonly BoundKeyFunction SwapHands = "SwapHands";

+ 24 - 3
Content.Shared/Movement/Components/MovementSpeedModifierComponent.cs

@@ -1,5 +1,6 @@
 using Content.Shared.Movement.Systems;
 using Robust.Shared.GameStates;
+using Content.Shared._Stalker.MoveSpeed;
 
 namespace Content.Shared.Movement.Components
 {
@@ -8,7 +9,7 @@ namespace Content.Shared.Movement.Components
     /// If this is not present on the entity then they will use defaults for movement.
     /// </summary>
     [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
-    [Access(typeof(MovementSpeedModifierSystem))]
+    [Access([typeof(MovementSpeedModifierSystem), typeof(StalkerMoveSpeedSystemShared)])] // stalker-changes
     public sealed partial class MovementSpeedModifierComponent : Component
     {
         // Weightless
@@ -23,8 +24,8 @@ public sealed partial class MovementSpeedModifierComponent : Component
         public const float DefaultFriction = 20f;
         public const float DefaultFrictionNoInput = 20f;
 
-        public const float DefaultBaseWalkSpeed = 2.5f;
-        public const float DefaultBaseSprintSpeed = 4.5f;
+        public const float DefaultBaseWalkSpeed = 5f;
+        public const float DefaultBaseSprintSpeed = 4f;
 
         [AutoNetworkedField, ViewVariables]
         public float WalkSpeedModifier = 1.0f;
@@ -54,6 +55,26 @@ private float _baseSprintSpeedVV
             }
         }
 
+        // stalker-changes-start
+        public float _baseWalkSpeedVVpublic
+        {
+            get => _baseWalkSpeedVV;
+            set
+            {
+                _baseWalkSpeedVV = value;
+            }
+        }
+
+        public float _baseSprintSpeedVVpublic
+        {
+            get => BaseSprintSpeed;
+            set
+            {
+                BaseSprintSpeed = value;
+            }
+        }
+        // stalker-changes-end
+
         /// <summary>
         /// Minimum speed a mob has to be moving before applying movement friction.
         /// </summary>

+ 12 - 4
Content.Shared/Standing/StandingStateSystem.cs

@@ -15,7 +15,7 @@ public sealed class StandingStateSystem : EntitySystem
         [Dependency] private readonly SharedPhysicsSystem _physics = default!;
 
         // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited.
-        private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
+        private const int StandingCollisionLayer = (int)CollisionGroup.MidImpassable;
 
         public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
         {
@@ -31,7 +31,8 @@ public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
             bool force = false,
             StandingStateComponent? standingState = null,
             AppearanceComponent? appearance = null,
-            HandsComponent? hands = null)
+            HandsComponent? hands = null,
+            bool fixtureAttempt = true) //stalker changes
         {
             // TODO: This should actually log missing comps...
             if (!Resolve(uid, ref standingState, false))
@@ -63,13 +64,19 @@ public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
 
             standingState.Standing = false;
             Dirty(uid, standingState);
-            RaiseLocalEvent(uid, new DownedEvent(), false);
+
+            // stalker-changes-start
+            DownedEvent downedEvent = new DownedEvent();
+            downedEvent.IgnoreLayingBulletPass = fixtureAttempt;
+
+            RaiseLocalEvent(uid, downedEvent, false);
+            //stalker-changes-end
 
             // Seemed like the best place to put it
             _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
 
             // Change collision masks to allow going under certain entities like flaps and tables
-            if (TryComp(uid, out FixturesComponent? fixtureComponent))
+            if (TryComp(uid, out FixturesComponent? fixtureComponent) && fixtureAttempt)
             {
                 foreach (var (key, fixture) in fixtureComponent.Fixtures)
                 {
@@ -168,5 +175,6 @@ public sealed class StoodEvent : EntityEventArgs
     /// </summary>
     public sealed class DownedEvent : EntityEventArgs
     {
+        public bool IgnoreLayingBulletPass = true; // stalker-changes
     }
 }

+ 43 - 0
Content.Shared/_Stalker/Characteristics/Characteristic.cs

@@ -0,0 +1,43 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._Stalker.Characteristics;
+
+[Serializable, NetSerializable]
+public struct Characteristic
+{
+    public readonly CharacteristicPrototype Proto;
+
+    public string ProtoId => Proto.ID;
+
+    public string Name => Proto.Name;
+    public string Description => Proto.Description;
+
+    public CharacteristicAccess Access => Proto.Access;
+    public CharacteristicType Type => Proto.Type;
+
+    public int MinLevel => Proto.MinLevel;
+    public int MaxLevel => Proto.MaxLevel;
+    public int BaseLevel => Proto.BaseLevel;
+    public int DefaultLevel => Proto.DefaultLevel;
+
+    public int Value => Level - BaseLevel;
+
+    public int Level { get; private set; }
+
+    public Characteristic(CharacteristicPrototype proto)
+    {
+        Proto = proto;
+        Level = DefaultLevel;
+    }
+
+    public Characteristic(Characteristic characteristic)
+    {
+        Proto = characteristic.Proto;
+        Level = characteristic.Level;
+    }
+
+    public Characteristic WithLevel(int level)
+    {
+        return new(this) { Level = level };
+    }
+}

+ 10 - 0
Content.Shared/_Stalker/Characteristics/CharacteristicAccess.cs

@@ -0,0 +1,10 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._Stalker.Characteristics;
+
+[Serializable, NetSerializable]
+public enum CharacteristicAccess : short
+{
+    Replicated,
+    ServerOnly,
+}

+ 35 - 0
Content.Shared/_Stalker/Characteristics/CharacteristicPrototype.cs

@@ -0,0 +1,35 @@
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._Stalker.Characteristics;
+
+[Prototype("characteristic"), Serializable, NetSerializable]
+public sealed partial class CharacteristicPrototype : IPrototype
+{
+    [IdDataField]
+    public string ID { get; } = string.Empty;
+
+    [DataField]
+    public string Name = string.Empty;
+
+    [DataField]
+    public string Description = string.Empty;
+
+    [DataField("group", required: true)]
+    public CharacteristicType Type;
+
+    [DataField]
+    public int MinLevel = 0;
+
+    [DataField]
+    public int MaxLevel = 20;
+
+    [DataField]
+    public int BaseLevel = 10;
+
+    [DataField]
+    public int DefaultLevel = 10;
+
+    [DataField]
+    public CharacteristicAccess Access = CharacteristicAccess.Replicated;
+}

+ 42 - 0
Content.Shared/_Stalker/Characteristics/CharacteristicSystem.cs

@@ -0,0 +1,42 @@
+using System.Runtime.CompilerServices;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._Stalker.Characteristics;
+
+public sealed class CharacteristicSystem : EntitySystem
+{
+    [Robust.Shared.IoC.Dependency] private readonly IPrototypeManager _prototype = default!;
+
+    public IReadOnlyDictionary<CharacteristicType, Characteristic> Characteristics => _characteristics;
+
+    private readonly Dictionary<CharacteristicType, Characteristic> _characteristics = new();
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        EnumerateCharacteristics();
+
+        _prototype.PrototypesReloaded += OnProtoReload;
+    }
+
+    // TODO: It'd be better to check influence of this attribute here on perf :)
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    private void EnumerateCharacteristics()
+    {
+        var prototypes = _prototype.EnumeratePrototypes<CharacteristicPrototype>();
+        foreach (var prototype in prototypes)
+        {
+            var characteristic = new Characteristic(prototype);
+            _characteristics.Add(prototype.Type, characteristic);
+        }
+    }
+
+    private void OnProtoReload(PrototypesReloadedEventArgs args)
+    {
+        if (!args.WasModified<CharacteristicPrototype>())
+            return;
+
+        EnumerateCharacteristics();
+    }
+}

+ 17 - 0
Content.Shared/_Stalker/Characteristics/CharacteristicType.cs

@@ -0,0 +1,17 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._Stalker.Characteristics;
+
+[Serializable, NetSerializable]
+public enum CharacteristicType
+{
+    Strength,
+    Dexterity,
+    Endurance,
+    Knowledge,
+    Attention,
+
+    // Hidden
+    Psionics,
+    Karma,
+}

+ 8 - 0
Content.Shared/_Stalker/Characteristics/CharacteristicUpdatedEvent.cs

@@ -0,0 +1,8 @@
+namespace Content.Shared._Stalker.Characteristics;
+
+public sealed partial class CharacteristicUpdatedEvent(Characteristic characteristic, int oldLevel, int newLevel) : EntityEventArgs
+{
+    public readonly Characteristic Characteristic = characteristic;
+    public readonly int OldLevel = oldLevel;
+    public readonly int NewLevel = newLevel;
+}

+ 3 - 0
Content.Shared/_Stalker/Characteristics/SharedCharacteristicContainerSystem.cs

@@ -0,0 +1,3 @@
+namespace Content.Shared._Stalker.Characteristics;
+
+public abstract class SharedCharacteristicContainerSystem : EntitySystem;

+ 8 - 0
Content.Shared/_Stalker/Characteristics/TrainingCompleteDoAfterEvent.cs

@@ -0,0 +1,8 @@
+using Content.Shared.DoAfter;
+using Robust.Shared.Serialization;
+namespace Content.Shared._Stalker.Characteristics;
+
+[Serializable, NetSerializable]
+public sealed partial class TrainingCompleteDoAfterEvent : SimpleDoAfterEvent
+{
+}

+ 7 - 0
Content.Shared/_Stalker/Damage/Components/Stamina/StaminaCritThresholdModifierComponent.cs

@@ -0,0 +1,7 @@
+using Content.Shared._Stalker.Modifier;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._Stalker.Damage.Components.Stamina;
+
+[RegisterComponent]
+public sealed partial class StaminaCritThresholdModifierComponent : BaseFloatModifierComponent;

+ 6 - 0
Content.Shared/_Stalker/Damage/Components/Stamina/StaminaDecayModifierComponent.cs

@@ -0,0 +1,6 @@
+using Content.Shared._Stalker.Modifier;
+
+namespace Content.Shared._Stalker.Damage.Components.Stamina;
+
+[RegisterComponent]
+public sealed partial class StaminaDecayModifierComponent : BaseFloatModifierComponent;

+ 6 - 0
Content.Shared/_Stalker/Damage/Systems/Stamina/StaminaCritThresholdModifierSystem.cs

@@ -0,0 +1,6 @@
+using Content.Shared._Stalker.Modifier;
+using Content.Shared._Stalker.Damage.Components.Stamina;
+
+namespace Content.Shared._Stalker.Damage.Systems.Stamina;
+
+public sealed class StaminaCritThresholdModifierSystem : BaseFloatModifierSystem<StaminaCritThresholdModifierComponent>;

+ 6 - 0
Content.Shared/_Stalker/Damage/Systems/Stamina/StaminaDecayModifierSystem.cs

@@ -0,0 +1,6 @@
+using Content.Shared._Stalker.Modifier;
+using Content.Shared._Stalker.Damage.Components.Stamina;
+
+namespace Content.Shared._Stalker.Damage.Systems.Stamina;
+
+public sealed class StaminaDecayModifierSystem : BaseFloatModifierSystem<StaminaDecayModifierComponent>;

+ 21 - 0
Content.Shared/_Stalker/Lay/Events/STLayDoAfterEvent.cs

@@ -0,0 +1,21 @@
+using Content.Shared.DoAfter;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._Stalker.Lay.Events;
+
+[Serializable, NetSerializable]
+public sealed partial class STLayDoAfterEvent : DoAfterEvent
+{
+    [DataField]
+    public STLayState NextState;
+
+    public STLayDoAfterEvent(STLayState nextState)
+    {
+        NextState = nextState;
+    }
+
+    public override DoAfterEvent Clone()
+    {
+        return this;
+    }
+}

+ 10 - 0
Content.Shared/_Stalker/Lay/STLayState.cs

@@ -0,0 +1,10 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._Stalker.Lay;
+
+[Serializable, NetSerializable]
+public enum STLayState
+{
+    Stand,
+    Laid,
+}

+ 4 - 0
Content.Shared/_Stalker/Modifier/BaseFloatModifiableComponent.cs

@@ -0,0 +1,4 @@
+namespace Content.Shared._Stalker.Modifier;
+
+public abstract partial class BaseFloatModifiableComponent<TComponent> : Component
+    where TComponent : BaseFloatModifierComponent;

+ 3 - 0
Content.Shared/_Stalker/Modifier/BaseFloatModifierComponent.cs

@@ -0,0 +1,3 @@
+namespace Content.Shared._Stalker.Modifier;
+
+public abstract partial class BaseFloatModifierComponent : BaseModifierComponent<float>;

+ 36 - 0
Content.Shared/_Stalker/Modifier/BaseFloatModifierSystem.cs

@@ -0,0 +1,36 @@
+using Robust.Shared.Timing;
+
+namespace Content.Shared._Stalker.Modifier;
+
+public abstract class BaseFloatModifierSystem<TComponent> : EntitySystem where TComponent : BaseFloatModifierComponent
+{
+    [Dependency] private readonly IGameTiming _timing = default!;
+
+    public void RefreshModifiers(EntityUid uid)
+    {
+        if (!TryComp<TComponent>(uid, out var component))
+            return;
+
+        RefreshModifiers((uid, component));
+    }
+
+    public void RefreshModifiers(Entity<TComponent?> modifier)
+    {
+        if (_timing.ApplyingState)
+            return;
+
+        if (!Resolve(modifier, ref modifier.Comp, false))
+            return;
+
+        var refreshEvent = new FloatModifierRefreshEvent<TComponent>();
+        RaiseLocalEvent(modifier, refreshEvent);
+
+        if (MathHelper.CloseTo(refreshEvent.Modifier, modifier.Comp.Modifier))
+            return;
+
+        modifier.Comp.Modifier = refreshEvent.Modifier;
+
+        var updatedEvent = new UpdatedFloatModifierEvent<TComponent>(modifier.Comp.Modifier);
+        RaiseLocalEvent(modifier, updatedEvent);
+    }
+}

+ 7 - 0
Content.Shared/_Stalker/Modifier/BaseModifierComponent.cs

@@ -0,0 +1,7 @@
+namespace Content.Shared._Stalker.Modifier;
+
+public abstract partial class BaseModifierComponent<T> : Component
+{
+    [DataField, ViewVariables]
+    public T Modifier { get; set; }
+}

+ 11 - 0
Content.Shared/_Stalker/Modifier/FloatModifierRefreshEvent.cs

@@ -0,0 +1,11 @@
+namespace Content.Shared._Stalker.Modifier;
+
+public sealed partial class FloatModifierRefreshEvent<TComponent> : EntityEventArgs where TComponent : BaseFloatModifierComponent
+{
+    public float Modifier { get; private set; } = 1f;
+
+    public void Modify(float modifier)
+    {
+        Modifier *= modifier;
+    }
+}

+ 6 - 0
Content.Shared/_Stalker/Modifier/UpdatedFloatModifierEvent.cs

@@ -0,0 +1,6 @@
+namespace Content.Shared._Stalker.Modifier;
+
+public sealed class UpdatedFloatModifierEvent<TComponent>(float modifier) : EntityEventArgs where TComponent : BaseFloatModifierComponent
+{
+    public readonly float Modifier = modifier;
+}

+ 60 - 0
Content.Shared/_Stalker/MoveSpeed/StalkerMoveSpeedSystemShared.cs

@@ -0,0 +1,60 @@
+using JetBrains.Annotations;
+
+namespace Content.Shared._Stalker.MoveSpeed;
+
+/// <summary>
+/// This handles...
+/// </summary>
+///
+[UsedImplicitly]
+public abstract class StalkerMoveSpeedSystemShared : EntitySystem
+{
+
+    /// <inheritdoc/>
+    public override void Initialize()
+    {
+        base.Initialize();
+    }
+
+    public void SetBonusSpeedWalk(string NameBonus,float ValueBonus,EntityUid InputEntity)
+    {
+        var ev = new StalkerMSSetBonusWalkEvent(NameBonus,ValueBonus,InputEntity);
+        EntityManager.EventBus.RaiseEvent(EventSource.Local, ev);
+    }
+
+    public void SetBonusSpeedSprint(string NameBonus,float ValueBonus,EntityUid InputEntity)
+    {
+        var ev = new StalkerMSSetBonusSprintEvent(NameBonus,ValueBonus,InputEntity);
+        EntityManager.EventBus.RaiseEvent(EventSource.Local, ev);
+    }
+
+}
+
+
+public sealed class StalkerMSSetBonusWalkEvent : EntityEventArgs
+{
+    public string NameBonus="";
+    public float ValueBonus=0f;
+    public EntityUid Entity;
+
+    public StalkerMSSetBonusWalkEvent(string nameBonus, float valueBonus, EntityUid entity)
+    {
+        NameBonus = nameBonus;
+        ValueBonus = valueBonus;
+        Entity = entity;
+    }
+}
+
+public sealed class StalkerMSSetBonusSprintEvent : EntityEventArgs
+{
+    public string NameBonus="";
+    public float ValueBonus=0f;
+    public EntityUid Entity;
+
+    public StalkerMSSetBonusSprintEvent(string nameBonus, float valueBonus, EntityUid entity)
+    {
+        NameBonus = nameBonus;
+        ValueBonus = valueBonus;
+        Entity = entity;
+    }
+}

+ 40 - 0
Content.Shared/_Stalker/Stamina/StaminaActiveComponent.cs

@@ -0,0 +1,40 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._Stalker.Stamina;
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class StaminaActiveComponent : Component
+{
+
+    /// <summary>
+    /// Float on which our entity will be "stunned"
+    /// </summary>
+    [DataField("slowThreshold"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+    public float SlowThreshold = 180f;
+
+    /// <summary>
+    /// Value to compare with StaminaDamage and set default sprint speed back.
+    /// If Stamina damage will be less than this value - default sprint will be set.
+    /// </summary>
+    [DataField("reviveStaminaLevel"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+    public float ReviveStaminaLevel = 80f;
+
+    /// <summary>
+    /// Stamina damage to apply when entity is running
+    /// </summary>
+    [DataField("runStaminaDamage"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+    public float RunStaminaDamage = 0.2f;
+
+    /// <summary>
+    /// Modifier to set entity sprint speed to a walking speed. Counts himself.
+    /// Nothing will happen if you'll set it manually
+    /// </summary>
+    public float SprintModifier = 0.5f;
+
+    public bool Change;
+
+    /// <summary>
+    /// If our entity is slowed already.
+    /// Nothing will happen if you'll set it manually.
+    /// </summary>
+    public bool Slowed = false;
+}

+ 15 - 0
Content.Shared/_Stalker/Throwing/STBeforeThrowedEvent.cs

@@ -0,0 +1,15 @@
+using System.Numerics;
+
+namespace Content.Shared._Stalker.Throwing;
+
+[ByRefEvent]
+public struct STBeforeThrowedEvent(EntityUid target, EntityUid user, Vector2 direction, float strength)
+{
+    public readonly EntityUid Target = target;
+    public readonly EntityUid User = user;
+
+    public Vector2 Direction = direction;
+    public float Strength = strength;
+
+    public bool Cancelled;
+}

+ 13 - 0
Content.Shared/_Stalker/Weight/GetWeightModifiersEvent.cs

@@ -0,0 +1,13 @@
+namespace Content.Shared._Stalker.Weight;
+
+public sealed class GetWeightModifiersEvent : EventArgs
+{
+    public float Inside;
+    public float Self;
+
+    public GetWeightModifiersEvent(float inside, float self)
+    {
+        Inside = inside;
+        Self = self;
+    }
+}

+ 60 - 0
Content.Shared/_Stalker/Weight/STWeightComponent.cs

@@ -0,0 +1,60 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._Stalker.Weight;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class STWeightComponent : Component
+{
+    /// <summary>
+    /// The total weight of the entity, which is calculated
+    /// by recursive passes over all children with this component
+    /// </summary>
+    [ViewVariables]
+    public float Total => Self + InsideWeight;
+
+    [ViewVariables]
+    public float TotalMaximum => Maximum * MaximumModifier;
+
+    [DataField, ViewVariables]
+    public float InsideWeight;
+
+    [DataField, ViewVariables]
+    public float WeightThrowModifier = 0.1f;
+
+    /// <summary>
+    /// This allows you to adjust the strength of
+    /// the throw so that small objects are not thrown harder,
+    /// but large objects are thrown weaker
+    /// </summary>
+    [DataField, ViewVariables]
+    public float WeightThrowMinStrengthModifier = 1f;
+
+    [DataField, ViewVariables]
+    public float MovementSpeedModifier = 1f;
+
+    [DataField, ViewVariables]
+    public float MaximumModifier = 1f;
+
+    /// <summary>
+    /// <see cref="STWeightComponent.Total"/> weight at which the entity stops completely,
+    /// yes this code has a linear deceleration schedule,
+    /// possible improvements in the future
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float Maximum = 200f;
+
+    /// <summary>
+    /// <see cref="STWeightComponent.Total"/> weight at which the entity begins to slow down.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float Overload = 100f;
+
+    [ViewVariables]
+    public float TotalOverload => Overload * MaximumModifier;
+
+    /// <summary>
+    /// Entity's own weight
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public float Self = 0.05f;
+}

+ 52 - 0
Content.Shared/_Stalker/Weight/SharedWeightExamineInfoSystem.cs

@@ -0,0 +1,52 @@
+using Content.Shared.Examine;
+
+namespace Content.Shared._Stalker.Weight;
+
+public sealed class SharedWeightExamineInfoSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<STWeightComponent, ExaminedEvent>(OnWeightExamine);
+    }
+
+    private void OnWeightExamine(EntityUid uid, STWeightComponent component, ExaminedEvent args)
+    {
+        if (!args.IsInDetailsRange)
+            return;
+
+        var r = HexFromId(255);
+        var g = HexFromId(255 - 255 / 30 * ((int)component.Total - 50));
+
+        if (component.Total < 50f)
+        {
+             r = HexFromId(255 / 50 * (int)component.Total);
+             g = HexFromId(255);
+        }
+
+        var colorString = $"#{r}{g}00";
+        var str = $"Weighs [color={colorString}]{component.Total:0.00}[/color] kg";
+
+        args.PushMarkup(str);
+    }
+
+    private string HexFromId(int id)
+    {
+        switch (id)
+        {
+            case < 0:
+                return "00";
+
+            case < 16:
+                return  "0" + id.ToString("X");
+
+            case > 255:
+                id = 255;
+                return id.ToString("X");
+
+            default:
+                return id.ToString("X");
+        }
+    }
+}

+ 9 - 0
Resources/Changelog/Changelog.yml

@@ -104,3 +104,12 @@ Entries:
     id: 8
     time: "2025-04-18T00:00:00.0000000+00:00"
     url: https://github.com/Civ13/Civ14/pull/123
+  - author: Taislin
+    changes:
+      - message: Adds stalker's stamina and sprinting mechanics.
+        type: Add
+      - message: Adds stalker's lay down mechanics.
+        type: Add
+    id: 9
+    time: "2025-04-19T00:00:00.0000000+00:00"
+    url: https://github.com/Civ13/Civ14/pull/124

+ 1 - 1
Resources/ConfigPresets/Civ/production.toml

@@ -24,7 +24,7 @@ desc = "An unofficial Civilization 14 server"
 maxplayers = 256
 lobbyduration = 60
 disallowlatejoins = false
-defaultpreset = "extended"
+defaultpreset = "nomads"
 
 [console]
 loginlocal = true

+ 2 - 2
Resources/Locale/en-US/game-ticking/game-presets/preset-extended.ftl

@@ -1,2 +1,2 @@
-extended-title = Extended
-extended-description = A calm experience. Admin intervention required.
+extended-title = Nomads
+extended-description = No preset factions or objectives.

+ 12 - 0
Resources/Prototypes/Entities/Mobs/Species/base.yml

@@ -210,6 +210,18 @@
         - FootstepSound
         - DoorBumpOpener
         - AnomalyHost
+    ## Stalker stuff
+    - type: STLay
+    - type: StaminaActive
+    - type: Stamina
+      decay: 14
+      cooldown: 5
+      critThreshold: 200
+      slowdownThreshold: 190
+    - type: StaminaDecayModifier
+    - type: StaminaCritThresholdModifier
+    - type: STWeight
+      self: 70
 
 - type: entity
   save: false

+ 3 - 3
Resources/Prototypes/game_presets.yml

@@ -1,10 +1,10 @@
 - type: gamePreset
-  id: Extended
+  id: Nomads
   alias:
     - extended
-    - shittersafari
+    - nomads
   name: extended-title
-  showInVote: false #2boring2vote
+  showInVote: true #2boring2vote
   description: extended-description
   rules:
     - RespawnDeadRule

+ 588 - 584
Resources/keybinds.yml

@@ -1,586 +1,590 @@
 version: 1 # Not used right now, whatever.
 binds:
-- function: UIClick
-  type: State
-  key: MouseLeft
-  canFocus: true
-- function: UIRightClick
-  type: State
-  key: MouseRight
-  canFocus: true
-  priority: 10
-- function: CloseModals
-  type: State
-  key: Escape
-  priority: 10
-- function: Use
-  type: State
-  key: MouseLeft
-  canFocus: true
-- function: UseSecondary
-  type: State
-  key: MouseRight
-  canFocus: true
-  priority: -1 # UIRightClick & EditorCancelPlace should fire first.
-- function: ShowDebugMonitors
-  type: Toggle
-  key: F3
-- function: HideUI
-  type: Toggle
-  mod1: Shift
-  key: F4
-- function: MoveUp
-  type: State
-  key: W
-- function: MoveLeft
-  type: State
-  key: A
-- function: MoveRight
-  type: State
-  key: D
-- function: MoveDown
-  type: State
-  key: S
-- function: Walk
-  type: State
-  key: Shift
-# Shuttle
-- function: ShuttleStrafeUp
-  type: State
-  key: W
-- function: ShuttleStrafeLeft
-  type: State
-  key: A
-- function: ShuttleStrafeRight
-  type: State
-  key: D
-- function: ShuttleStrafeDown
-  type: State
-  key: S
-- function: ShuttleRotateLeft
-  type: State
-  key: Q
-- function: ShuttleRotateRight
-  type: State
-  key: E
-- function: ShuttleBrake
-  type: State
-  key: Space
-# Camera
-- function: CameraRotateLeft
-  type: State
-  key: NumpadNum7
-- function: CameraRotateRight
-  type: State
-  key: NumpadNum9
-- function: CameraReset
-  type: State
-  key: NumpadNum8
-- function: ZoomOut
-  type: State
-  key: NumpadNum4
-- function: ZoomIn
-  type: State
-  key: NumpadNum6
-- function: ResetZoom
-  type: State
-  key: NumpadNum5
-# Misc
-- function: ShowEscapeMenu
-  type: State
-  key: F10
-- function: ToggleFullscreen
-  type: State
-  key: F11
-- function: CycleChatChannelForward
-  type: State
-  key: Tab
-  priority: 1 # Before tab complete
-- function: CycleChatChannelBackward
-  type: State
-  key: Tab
-  mod1: Control
-- function: FocusChatInputWindow
-  type: State
-  key: T
-- function: FocusLocalChatWindow
-  type: State
-  key: Period
-- function: FocusEmote
-  type: State
-  mod1: Shift
-  key: Apostrophe
-- function: FocusWhisperChatWindow
-  type: State
-  key: Comma
-- function: FocusRadioWindow
-  type: State
-  key: SemiColon
-- function: FocusLOOCWindow
-  type: State
-  mod1: Shift
-  key: Num9
-- function: FocusOOCWindow
-  type: State
-  key: LBracket
-- function: FocusAdminChatWindow
-  type: State
-  key: RBracket
-- function: FocusDeadChatWindow
-  type: State
-  key: Backslash
-- function: FocusConsoleChatWindow
-  type: State
-  key: Slash
-- function: EditorLinePlace
-  type: State
-  key: MouseLeft
-  canFocus: true
-  mod1: Shift
-- function: EditorGridPlace
-  type: State
-  key: MouseLeft
-  canFocus: true
-  mod1: Control
-- function: EditorPlaceObject
-  type: State
-  key: MouseLeft
-  canFocus: true
-- function: EditorCancelPlace
-  type: State
-  key: MouseRight
-  canFocus: true
-- function: EditorRotateObject
-  type: State
-  key: MouseMiddle
-- function: EditorFlipObject
-  type: State
-  key: MouseMiddle
-  mod1: Control
-- function: EditorCopyObject
-  type: State
-  key: P
-- function: SwapHands
-  type: State
-  key: X
-- function: MoveStoredItem
-  type: State
-  key: MouseLeft
-  canFocus: true
-  priority: 10
-- function: RotateStoredItem
-  type: State
-  key: MouseRight
-- function: SaveItemLocation
-  type: State
-  key: MouseMiddle
-- function: Drop
-  type: State
-  key: Q
-- function: ActivateItemInHand
-  type: State
-  key: Z
-- function: AltActivateItemInHand
-  type: State
-  key: Z
-  mod1: Alt
-- function: OpenCharacterMenu
-  type: State
-  key: C
-- function: OpenEmotesMenu
-  type: State
-  key: Y
-- function: TextCursorSelect
-  # TextCursorSelect HAS to be above ExamineEntity
-  # So that LineEdit receives it correctly.
-  # TODO: Make it so that UI keybinds are somehow prioritized so this ordering stuff isn't necessary.
-  type: State
-  key: MouseLeft
-  mod1: Shift
-  canFocus: true
-- function: ExamineEntity
-  type: State
-  key: MouseLeft
-  canFocus: true
-  mod1: Shift
-- function: ActivateItemInWorld
-  type: State
-  key: E
-- function: AltActivateItemInWorld
-  type: State
-  key: MouseLeft
-  canFocus: true
-  mod1: Alt
-- function: AltActivateItemInWorld # secondary binding
-  type: State
-  key: E
-  mod1: Alt
-- function: ThrowItemInHand
-  type: State
-  key: Q
-  mod1: Control
-- function: TryPullObject
-  type: State
-  canFocus: true
-  key: MouseLeft
-  mod1: Control
-- function: MovePulledObject
-  type: State
-  key: MouseRight
-  mod1: Control
-- function: ReleasePulledObject
-  type: State
-  key: H
-- function: OpenCraftingMenu
-  type: State
-  key: G
-- function: OpenGuidebook
-  type: State
-  key: NumpadNum0
-- function: OpenAHelp
-  type: State
-  key: F1
-- function: OpenInventoryMenu
-  type: State
-  key: I
-- function: SmartEquipBackpack
-  type: State
-  key: B
-  mod1: Shift
-- function: SmartEquipBelt
-  type: State
-  key: E
-  mod1: Shift
-- function: OpenBackpack
-  type: State
-  key: V
-- function: OpenBelt
-  type: State
-  key: V
-  mod1: Shift
-- function: ShowDebugConsole
-  type: State
-  key: Tilde
-- function: InspectEntity
-  type: State
-  key: v
-  mod1: Alt
-- function: MouseMiddle
-  type: State
-  key: MouseMiddle
-  canFocus: true
-- function: RotateObjectClockwise
-  type: State
-  key: R
-- function: RotateObjectCounterclockwise
-  type: State
-  key: R
-  mod1: Shift
-- function: FlipObject
-  type: State
-  key: F
-- function: TextCursorLeft
-  type: State
-  key: Left
-  canRepeat: true
-- function: TextCursorRight
-  type: State
-  key: Right
-  canRepeat: true
-- function: TextCursorUp
-  type: State
-  key: Up
-  canRepeat: true
-  priority: 2
-- function: TextCursorDown
-  type: State
-  key: Down
-  canRepeat: true
-  priority: 2
-- function: TextCursorWordLeft
-  type: State
-  key: Left
-  mod1: Control
-  canRepeat: true
-  allowSubCombs: true
-- function: TextCursorWordRight
-  type: State
-  key: Right
-  mod1: Control
-  canRepeat: true
-  allowSubCombs: true
-- function: TextCursorBegin
-  type: State
-  key: Home
-- function: TextCursorEnd
-  type: State
-  key: End
-  canRepeat: true
-- function: TextCursorSelectLeft
-  type: State
-  key: Left
-  mod1: Shift
-  canRepeat: true
-  allowSubCombs: true
-- function: TextCursorSelectRight
-  type: State
-  key: Right
-  mod1: Shift
-  canRepeat: true
-  allowSubCombs: true
-- function: TextCursorSelectUp
-  type: State
-  key: Up
-  mod1: Shift
-  canRepeat: true
-  allowSubCombs: true
-- function: TextCursorSelectDown
-  type: State
-  key: Down
-  mod1: Shift
-  canRepeat: true
-  allowSubCombs: true
-- function: TextCursorSelectWordLeft
-  type: State
-  key: Left
-  mod1: Shift
-  mod2: Control
-  canRepeat: true
-  allowSubCombs: true
-- function: TextCursorSelectWordRight
-  type: State
-  key: Right
-  mod1: Shift
-  mod2: Control
-  canRepeat: true
-  allowSubCombs: true
-- function: TextCursorSelectBegin
-  type: State
-  mod1: Shift
-  key: Home
-  allowSubCombs: true
-- function: TextCursorSelectEnd
-  type: State
-  mod1: Shift
-  key: End
-  canRepeat: true
-  allowSubCombs: true
-- function: TextBackspace
-  type: State
-  key: BackSpace
-  canRepeat: true
-- function: TextDelete
-  type: State
-  key: Delete
-  canRepeat: true
-- function: TextWordBackspace
-  type: State
-  key: BackSpace
-  mod1: Control
-  canRepeat: true
-  allowSubCombs: true
-- function: TextWordDelete
-  type: State
-  key: Delete
-  mod1: Control
-  canRepeat: true
-  allowSubCombs: true
-- function: TextNewline
-  type: State
-  key: Return
-  canRepeat: true
-- function: TextNewline
-  type: State
-  key: NumpadEnter
-  canRepeat: true
-- function: TextSubmit
-  type: State
-  key: Return
-- function: TextSubmit
-  type: State
-  key: NumpadEnter
-- function: MultilineTextSubmit
-  type: State
-  key: Return
-  mod1: Control
-- function: MultilineTextSubmit
-  type: State
-  key: NumpadEnter
-  mod1: Control
-- function: TextSelectAll
-  type: State
-  key: A
-  mod1: Control
-  allowSubCombs: true
-- function: TextCopy
-  type: State
-  key: C
-  mod1: Control
-  allowSubCombs: true
-- function: TextCut
-  type: State
-  key: X
-  mod1: Control
-  allowSubCombs: true
-- function: TextPaste
-  type: State
-  key: V
-  mod1: Control
-  allowSubCombs: true
-- function: TextHistoryPrev
-  type: State
-  key: Up
-- function: TextHistoryNext
-  type: State
-  key: Down
-- function: TextCompleteNext
-  type: State
-  key: Down
-  priority: 1
-  canRepeat: true
-- function: TextCompletePrev
-  type: State
-  key: Up
-  priority: 1
-  canRepeat: true
-- function: TextReleaseFocus
-  type: State
-  key: Escape
-  priority: 15
-- function: TextScrollToBottom
-  type: State
-  key: PageDown
-- function: TextTabComplete
-  type: State
-  key: Tab
-- function: OpenEntitySpawnWindow
-  type: State
-  key: F5
-- function: OpenTileSpawnWindow
-  type: State
-  key: F6
-- function: OpenAdminMenu
-  type: State
-  key: F7
-- function: OpenDecalSpawnWindow
-  type: State
-  key: F8
-- function: ToggleRoundEndSummaryWindow
-  type: Toggle
-  key: F9
-- function: OpenSandboxWindow
-  type: State
-  key: B
-- function: TakeScreenshot
-  type: State
-  key: F2
-- function: TakeScreenshotNoUI
-  type: State
-  key: F2
-  mod1: Shift
-- function: GuiTabNavigateNext
-  type: State
-  key: Tab
-- function: GuiTabNavigatePrev
-  type: State
-  key: Tab
-  mod1: Shift
-- function: EscapeContext
-  type: State
-  key: Escape
-- function: WindowCloseAll
-  type: State
-  key: Escape
-  mod1: Shift
-- function: Point
-  type: State
-  key: MouseMiddle
-  mod1: Shift
-- function: ArcadeUp
-  type: State
-  key: Up
-  priority: -1
-- function: ArcadeDown
-  type: State
-  key: Down
-  priority: -1
-- function: ArcadeLeft
-  type: State
-  key: Left
-  priority: -1
-- function: ArcadeRight
-  type: State
-  key: Right
-  priority: -1
-- function: Arcade1
-  type: State
-  key: Space
-- function: Arcade2
-  type: State
-  key: C
-- function: Arcade3
-  type: State
-  key: Z
-- function: OpenAbilitiesMenu
-  type: State
-  key: K
-- function: Hotbar0
-  type: State
-  key: Num0
-- function: Hotbar1
-  type: State
-  key: Num1
-- function: Hotbar2
-  type: State
-  key: Num2
-- function: Hotbar3
-  type: State
-  key: Num3
-- function: Hotbar4
-  type: State
-  key: Num4
-- function: Hotbar5
-  type: State
-  key: Num5
-- function: Hotbar6
-  type: State
-  key: Num6
-- function: Hotbar7
-  type: State
-  key: Num7
-- function: Hotbar8
-  type: State
-  key: Num8
-- function: Hotbar9
-  type: State
-  key: Num9
-- function: MappingUnselect
-  type: State
-  key: MouseRight
-  canFocus: true
-- function: SaveMap
-  type: State
-  key: S
-  mod1: Control
-- function: MappingEnablePick
-  type: State
-  key: Num5
-- function: MappingEnableDelete
-  type: State
-  key: Num6
-- function: MappingPick
-  type: State
-  key: MouseLeft
-  canFocus: true
-- function: MappingRemoveDecal
-  type: State
-  key: MouseLeft
-  canFocus: true
-- function: MappingCancelEraseDecal
-  type: State
-  key: MouseRight
-  canFocus: true
-- function: MappingOpenContextMenu
-  type: State
-  key: MouseRight
-  canFocus: true
+  - function: UIClick
+    type: State
+    key: MouseLeft
+    canFocus: true
+  - function: UIRightClick
+    type: State
+    key: MouseRight
+    canFocus: true
+    priority: 10
+  - function: CloseModals
+    type: State
+    key: Escape
+    priority: 10
+  - function: Use
+    type: State
+    key: MouseLeft
+    canFocus: true
+  - function: UseSecondary
+    type: State
+    key: MouseRight
+    canFocus: true
+    priority: -1 # UIRightClick & EditorCancelPlace should fire first.
+  - function: ShowDebugMonitors
+    type: Toggle
+    key: F3
+  - function: HideUI
+    type: Toggle
+    mod1: Shift
+    key: F4
+  - function: MoveUp
+    type: State
+    key: W
+  - function: MoveLeft
+    type: State
+    key: A
+  - function: MoveRight
+    type: State
+    key: D
+  - function: MoveDown
+    type: State
+    key: S
+  - function: Walk
+    type: State
+    key: Shift
+  # Shuttle
+  - function: ShuttleStrafeUp
+    type: State
+    key: W
+  - function: ShuttleStrafeLeft
+    type: State
+    key: A
+  - function: ShuttleStrafeRight
+    type: State
+    key: D
+  - function: ShuttleStrafeDown
+    type: State
+    key: S
+  - function: ShuttleRotateLeft
+    type: State
+    key: Q
+  - function: ShuttleRotateRight
+    type: State
+    key: E
+  - function: ShuttleBrake
+    type: State
+    key: Space
+  # Camera
+  - function: CameraRotateLeft
+    type: State
+    key: NumpadNum7
+  - function: CameraRotateRight
+    type: State
+    key: NumpadNum9
+  - function: CameraReset
+    type: State
+    key: NumpadNum8
+  - function: ZoomOut
+    type: State
+    key: NumpadNum4
+  - function: ZoomIn
+    type: State
+    key: NumpadNum6
+  - function: ResetZoom
+    type: State
+    key: NumpadNum5
+  # Misc
+  - function: ShowEscapeMenu
+    type: State
+    key: F10
+  - function: ToggleFullscreen
+    type: State
+    key: F11
+  - function: CycleChatChannelForward
+    type: State
+    key: Tab
+    priority: 1 # Before tab complete
+  - function: CycleChatChannelBackward
+    type: State
+    key: Tab
+    mod1: Control
+  - function: FocusChatInputWindow
+    type: State
+    key: T
+  - function: FocusLocalChatWindow
+    type: State
+    key: Period
+  - function: FocusEmote
+    type: State
+    mod1: Shift
+    key: Apostrophe
+  - function: FocusWhisperChatWindow
+    type: State
+    key: Comma
+  - function: FocusRadioWindow
+    type: State
+    key: SemiColon
+  - function: FocusLOOCWindow
+    type: State
+    mod1: Shift
+    key: Num9
+  - function: FocusOOCWindow
+    type: State
+    key: LBracket
+  - function: FocusAdminChatWindow
+    type: State
+    key: RBracket
+  - function: FocusDeadChatWindow
+    type: State
+    key: Backslash
+  - function: FocusConsoleChatWindow
+    type: State
+    key: Slash
+  - function: EditorLinePlace
+    type: State
+    key: MouseLeft
+    canFocus: true
+    mod1: Shift
+  - function: EditorGridPlace
+    type: State
+    key: MouseLeft
+    canFocus: true
+    mod1: Control
+  - function: EditorPlaceObject
+    type: State
+    key: MouseLeft
+    canFocus: true
+  - function: EditorCancelPlace
+    type: State
+    key: MouseRight
+    canFocus: true
+  - function: EditorRotateObject
+    type: State
+    key: MouseMiddle
+  - function: EditorFlipObject
+    type: State
+    key: MouseMiddle
+    mod1: Control
+  - function: EditorCopyObject
+    type: State
+    key: P
+  - function: SwapHands
+    type: State
+    key: X
+  - function: MoveStoredItem
+    type: State
+    key: MouseLeft
+    canFocus: true
+    priority: 10
+  - function: RotateStoredItem
+    type: State
+    key: MouseRight
+  - function: SaveItemLocation
+    type: State
+    key: MouseMiddle
+  - function: Drop
+    type: State
+    key: Q
+  - function: ActivateItemInHand
+    type: State
+    key: Z
+  - function: AltActivateItemInHand
+    type: State
+    key: Z
+    mod1: Alt
+  - function: OpenCharacterMenu
+    type: State
+    key: C
+  - function: OpenEmotesMenu
+    type: State
+    key: Y
+  - function: TextCursorSelect
+    # TextCursorSelect HAS to be above ExamineEntity
+    # So that LineEdit receives it correctly.
+    # TODO: Make it so that UI keybinds are somehow prioritized so this ordering stuff isn't necessary.
+    type: State
+    key: MouseLeft
+    mod1: Shift
+    canFocus: true
+  - function: ExamineEntity
+    type: State
+    key: MouseLeft
+    canFocus: true
+    mod1: Shift
+  - function: ActivateItemInWorld
+    type: State
+    key: E
+  - function: AltActivateItemInWorld
+    type: State
+    key: MouseLeft
+    canFocus: true
+    mod1: Alt
+  - function: AltActivateItemInWorld # secondary binding
+    type: State
+    key: E
+    mod1: Alt
+  - function: ThrowItemInHand
+    type: State
+    key: Q
+    mod1: Control
+  - function: TryPullObject
+    type: State
+    canFocus: true
+    key: MouseLeft
+    mod1: Control
+  - function: MovePulledObject
+    type: State
+    key: MouseRight
+    mod1: Control
+  - function: ReleasePulledObject
+    type: State
+    key: H
+  - function: OpenCraftingMenu
+    type: State
+    key: G
+  - function: OpenGuidebook
+    type: State
+    key: NumpadNum0
+  - function: OpenAHelp
+    type: State
+    key: F1
+  - function: OpenInventoryMenu
+    type: State
+    key: I
+  - function: SmartEquipBackpack
+    type: State
+    key: B
+    mod1: Shift
+  - function: SmartEquipBelt
+    type: State
+    key: E
+    mod1: Shift
+  - function: OpenBackpack
+    type: State
+    key: V
+  - function: OpenBelt
+    type: State
+    key: V
+    mod1: Shift
+  - function: ShowDebugConsole
+    type: State
+    key: Tilde
+  - function: InspectEntity
+    type: State
+    key: v
+    mod1: Alt
+  - function: MouseMiddle
+    type: State
+    key: MouseMiddle
+    canFocus: true
+  - function: RotateObjectClockwise
+    type: State
+    key: R
+  - function: RotateObjectCounterclockwise
+    type: State
+    key: R
+    mod1: Shift
+    # stalker-changes
+  - function: Lay
+    type: State
+    key: F
+  - function: FlipObject
+    type: State
+    key: F
+  - function: TextCursorLeft
+    type: State
+    key: Left
+    canRepeat: true
+  - function: TextCursorRight
+    type: State
+    key: Right
+    canRepeat: true
+  - function: TextCursorUp
+    type: State
+    key: Up
+    canRepeat: true
+    priority: 2
+  - function: TextCursorDown
+    type: State
+    key: Down
+    canRepeat: true
+    priority: 2
+  - function: TextCursorWordLeft
+    type: State
+    key: Left
+    mod1: Control
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextCursorWordRight
+    type: State
+    key: Right
+    mod1: Control
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextCursorBegin
+    type: State
+    key: Home
+  - function: TextCursorEnd
+    type: State
+    key: End
+    canRepeat: true
+  - function: TextCursorSelectLeft
+    type: State
+    key: Left
+    mod1: Shift
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextCursorSelectRight
+    type: State
+    key: Right
+    mod1: Shift
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextCursorSelectUp
+    type: State
+    key: Up
+    mod1: Shift
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextCursorSelectDown
+    type: State
+    key: Down
+    mod1: Shift
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextCursorSelectWordLeft
+    type: State
+    key: Left
+    mod1: Shift
+    mod2: Control
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextCursorSelectWordRight
+    type: State
+    key: Right
+    mod1: Shift
+    mod2: Control
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextCursorSelectBegin
+    type: State
+    mod1: Shift
+    key: Home
+    allowSubCombs: true
+  - function: TextCursorSelectEnd
+    type: State
+    mod1: Shift
+    key: End
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextBackspace
+    type: State
+    key: BackSpace
+    canRepeat: true
+  - function: TextDelete
+    type: State
+    key: Delete
+    canRepeat: true
+  - function: TextWordBackspace
+    type: State
+    key: BackSpace
+    mod1: Control
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextWordDelete
+    type: State
+    key: Delete
+    mod1: Control
+    canRepeat: true
+    allowSubCombs: true
+  - function: TextNewline
+    type: State
+    key: Return
+    canRepeat: true
+  - function: TextNewline
+    type: State
+    key: NumpadEnter
+    canRepeat: true
+  - function: TextSubmit
+    type: State
+    key: Return
+  - function: TextSubmit
+    type: State
+    key: NumpadEnter
+  - function: MultilineTextSubmit
+    type: State
+    key: Return
+    mod1: Control
+  - function: MultilineTextSubmit
+    type: State
+    key: NumpadEnter
+    mod1: Control
+  - function: TextSelectAll
+    type: State
+    key: A
+    mod1: Control
+    allowSubCombs: true
+  - function: TextCopy
+    type: State
+    key: C
+    mod1: Control
+    allowSubCombs: true
+  - function: TextCut
+    type: State
+    key: X
+    mod1: Control
+    allowSubCombs: true
+  - function: TextPaste
+    type: State
+    key: V
+    mod1: Control
+    allowSubCombs: true
+  - function: TextHistoryPrev
+    type: State
+    key: Up
+  - function: TextHistoryNext
+    type: State
+    key: Down
+  - function: TextCompleteNext
+    type: State
+    key: Down
+    priority: 1
+    canRepeat: true
+  - function: TextCompletePrev
+    type: State
+    key: Up
+    priority: 1
+    canRepeat: true
+  - function: TextReleaseFocus
+    type: State
+    key: Escape
+    priority: 15
+  - function: TextScrollToBottom
+    type: State
+    key: PageDown
+  - function: TextTabComplete
+    type: State
+    key: Tab
+  - function: OpenEntitySpawnWindow
+    type: State
+    key: F5
+  - function: OpenTileSpawnWindow
+    type: State
+    key: F6
+  - function: OpenAdminMenu
+    type: State
+    key: F7
+  - function: OpenDecalSpawnWindow
+    type: State
+    key: F8
+  - function: ToggleRoundEndSummaryWindow
+    type: Toggle
+    key: F9
+  - function: OpenSandboxWindow
+    type: State
+    key: B
+  - function: TakeScreenshot
+    type: State
+    key: F2
+  - function: TakeScreenshotNoUI
+    type: State
+    key: F2
+    mod1: Shift
+  - function: GuiTabNavigateNext
+    type: State
+    key: Tab
+  - function: GuiTabNavigatePrev
+    type: State
+    key: Tab
+    mod1: Shift
+  - function: EscapeContext
+    type: State
+    key: Escape
+  - function: WindowCloseAll
+    type: State
+    key: Escape
+    mod1: Shift
+  - function: Point
+    type: State
+    key: MouseMiddle
+    mod1: Shift
+  - function: ArcadeUp
+    type: State
+    key: Up
+    priority: -1
+  - function: ArcadeDown
+    type: State
+    key: Down
+    priority: -1
+  - function: ArcadeLeft
+    type: State
+    key: Left
+    priority: -1
+  - function: ArcadeRight
+    type: State
+    key: Right
+    priority: -1
+  - function: Arcade1
+    type: State
+    key: Space
+  - function: Arcade2
+    type: State
+    key: C
+  - function: Arcade3
+    type: State
+    key: Z
+  - function: OpenAbilitiesMenu
+    type: State
+    key: K
+  - function: Hotbar0
+    type: State
+    key: Num0
+  - function: Hotbar1
+    type: State
+    key: Num1
+  - function: Hotbar2
+    type: State
+    key: Num2
+  - function: Hotbar3
+    type: State
+    key: Num3
+  - function: Hotbar4
+    type: State
+    key: Num4
+  - function: Hotbar5
+    type: State
+    key: Num5
+  - function: Hotbar6
+    type: State
+    key: Num6
+  - function: Hotbar7
+    type: State
+    key: Num7
+  - function: Hotbar8
+    type: State
+    key: Num8
+  - function: Hotbar9
+    type: State
+    key: Num9
+  - function: MappingUnselect
+    type: State
+    key: MouseRight
+    canFocus: true
+  - function: SaveMap
+    type: State
+    key: S
+    mod1: Control
+  - function: MappingEnablePick
+    type: State
+    key: Num5
+  - function: MappingEnableDelete
+    type: State
+    key: Num6
+  - function: MappingPick
+    type: State
+    key: MouseLeft
+    canFocus: true
+  - function: MappingRemoveDecal
+    type: State
+    key: MouseLeft
+    canFocus: true
+  - function: MappingCancelEraseDecal
+    type: State
+    key: MouseRight
+    canFocus: true
+  - function: MappingOpenContextMenu
+    type: State
+    key: MouseRight
+    canFocus: true