From b0e3ab339385713aa2d9be89d1ae7f05c0ed0032 Mon Sep 17 00:00:00 2001 From: 4dplanner <3combined@gmail.com> Date: Sat, 22 Feb 2020 23:37:56 +0000 Subject: [PATCH] MovementSpeedModifierComponent (#721) * movement modifier system * update default exposedata values * changing base speed marks as dirty * formatting * movement speed modifier component * Moves slowdown component to the prototype AIController respects slowdown * reset dirty flag --- .../Movement/AiControllerComponent.cs | 41 +++++++++-- .../MovementSpeedModifierComponent.cs | 68 +++++++++++++++++++ .../Movement/PlayerInputMoverComponent.cs | 58 +++++++++++++--- .../Movement/ShuttleControllerComponent.cs | 6 +- .../Components/Nutrition/HungerComponent.cs | 46 +++++++++---- .../Components/Nutrition/ThirstComponent.cs | 46 +++++++++---- .../GameObjects/EntitySystems/MoverSystem.cs | 10 +-- .../Components/Movement/IMoverComponent.cs | 4 +- Resources/Prototypes/Entities/mobs/human.yml | 1 + 9 files changed, 231 insertions(+), 49 deletions(-) create mode 100644 Content.Server/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs diff --git a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs index d48a78b198..755d00dee7 100644 --- a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs @@ -57,16 +57,49 @@ namespace Content.Server.GameObjects.Components.Movement } /// - /// Movement speed (m/s) that the entity walks. + /// Movement speed (m/s) that the entity walks, before modifiers /// [ViewVariables(VVAccess.ReadWrite)] - public float WalkMoveSpeed { get; set; } = 4.0f; + public float BaseWalkSpeed { get; set; } = PlayerInputMoverComponent.DefaultBaseWalkSpeed; /// - /// Movement speed (m/s) that the entity sprints. + /// Movement speed (m/s) that the entity sprints, before modifiers /// [ViewVariables(VVAccess.ReadWrite)] - public float SprintMoveSpeed { get; set; } = 10.0f; + public float BaseSprintSpeed { get; set; } = PlayerInputMoverComponent.DefaultBaseSprintSpeed; + + /// + /// Movement speed (m/s) that the entity walks, after modifiers + /// + [ViewVariables] + public float CurrentWalkSpeed + { + get + { + float speed = BaseWalkSpeed; + if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + { + speed *= component.WalkSpeedModifier; + } + return speed; + } + } + /// + /// Movement speed (m/s) that the entity walks, after modifiers + /// + [ViewVariables] + public float CurrentSprintSpeed + { + get + { + float speed = BaseSprintSpeed; + if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + { + speed *= component.SprintSpeedModifier; + } + return speed; + } + } /// /// Is the entity Sprinting (running)? diff --git a/Content.Server/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs b/Content.Server/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs new file mode 100644 index 0000000000..aaa6a38261 --- /dev/null +++ b/Content.Server/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs @@ -0,0 +1,68 @@ + +using Robust.Shared.GameObjects; + +namespace Content.Server.GameObjects.Components.Movement +{ + [RegisterComponent] + public class MovementSpeedModifierComponent : Component + { + public override string Name => "MovementSpeedModifier"; + + private float _cachedWalkSpeedModifier = 1.0f; + public float WalkSpeedModifier + { + get + { + RecalculateMovementSpeedModifiers(); + return _cachedWalkSpeedModifier; + } + } + private float _cachedSprintSpeedModifier; + public float SprintSpeedModifier + { + get + { + RecalculateMovementSpeedModifiers(); + return _cachedSprintSpeedModifier; + } + } + + /// + /// set to warn us that a component's movespeed modifier has changed + /// + private bool _movespeedModifiersNeedRefresh = true; + + public void RefreshMovementSpeedModifiers() + { + _movespeedModifiersNeedRefresh = true; + } + + /// + /// Recalculate movement speed with current modifiers, or return early if no change + /// + private void RecalculateMovementSpeedModifiers() + { + { + if (!_movespeedModifiersNeedRefresh) + return; + var movespeedModifiers = Owner.GetAllComponents(); + float walkSpeedModifier = 1.0f; + float sprintSpeedModifier = 1.0f; + foreach (var component in movespeedModifiers) + { + walkSpeedModifier *= component.WalkSpeedModifier; + sprintSpeedModifier *= component.SprintSpeedModifier; + } + _cachedWalkSpeedModifier = walkSpeedModifier; + _cachedSprintSpeedModifier = sprintSpeedModifier; + } + _movespeedModifiersNeedRefresh = false; + } + } + + interface IMoveSpeedModifier + { + float WalkSpeedModifier { get; } + float SprintSpeedModifier { get; } + } +} diff --git a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs index 3b4708eec4..26d056ac17 100644 --- a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs @@ -22,6 +22,8 @@ namespace Content.Server.GameObjects.Components.Movement [ComponentReference(typeof(IMoverComponent))] public class PlayerInputMoverComponent : Component, IMoverComponent, ICollideSpecial { + public const float DefaultBaseWalkSpeed = 4.0f; + public const float DefaultBaseSprintSpeed = 7.0f; #pragma warning disable 649 [Dependency] private readonly IConfigurationManager _configurationManager; @@ -35,17 +37,51 @@ namespace Content.Server.GameObjects.Components.Movement /// public override string Name => "PlayerInputMover"; - /// - /// Movement speed (m/s) that the entity walks. - /// - [ViewVariables(VVAccess.ReadWrite)] - public float WalkMoveSpeed { get; set; } = 4.0f; /// - /// Movement speed (m/s) that the entity sprints. + /// Movement speed (m/s) that the entity walks, before modifiers /// [ViewVariables(VVAccess.ReadWrite)] - public float SprintMoveSpeed { get; set; } = 7.0f; + public float BaseWalkSpeed { get; set; } = DefaultBaseWalkSpeed; + + /// + /// Movement speed (m/s) that the entity sprints, before modifiers + /// + [ViewVariables(VVAccess.ReadWrite)] + public float BaseSprintSpeed { get; set; } = DefaultBaseSprintSpeed; + + /// + /// Movement speed (m/s) that the entity walks, after modifiers + /// + [ViewVariables] + public float CurrentWalkSpeed + { + get + { + float speed = BaseWalkSpeed; + if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + { + speed *= component.WalkSpeedModifier; + } + return speed; + } + } + /// + /// Movement speed (m/s) that the entity walks, after modifiers + /// + [ViewVariables] + public float CurrentSprintSpeed + { + get + { + float speed = BaseSprintSpeed; + if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + { + speed *= component.SprintSpeedModifier; + } + return speed; + } + } /// /// Is the entity Sprinting (running)? @@ -89,13 +125,15 @@ namespace Content.Server.GameObjects.Components.Movement { base.ExposeData(serializer); - serializer.DataReadWriteFunction("wspd", 4.0f, value => WalkMoveSpeed = value, () => WalkMoveSpeed); - serializer.DataReadWriteFunction("rspd", 10.0f, value => SprintMoveSpeed = value, () => SprintMoveSpeed); + //only save the base speeds - the current speeds are transient. + serializer.DataReadWriteFunction("wspd", DefaultBaseWalkSpeed, value => BaseWalkSpeed = value, () => BaseWalkSpeed); + serializer.DataReadWriteFunction("rspd", DefaultBaseSprintSpeed, value => BaseSprintSpeed = value, () => BaseSprintSpeed); // The velocity and moving directions is usually set from player or AI input, // so we don't want to save/load these derived fields. } + /// /// Toggles one of the four cardinal directions. Each of the four directions are /// composed into a single direction vector, . Enabling @@ -150,7 +188,7 @@ namespace Content.Server.GameObjects.Components.Movement /// bool ICollideSpecial.PreventCollide(IPhysBody collidedwith) { - // Don't collid with other mobs + // Don't collide with other mobs if (collidedwith.Owner.TryGetComponent(out var collidedSpeciesComponent)) { return true; diff --git a/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs index aa066e4732..82e3a479e4 100644 --- a/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs @@ -32,8 +32,8 @@ namespace Content.Server.GameObjects.Components.Movement public override string Name => "ShuttleController"; [ViewVariables(VVAccess.ReadWrite)] - public float WalkMoveSpeed { get; set; } = 8; - public float SprintMoveSpeed { get; set; } + public float CurrentWalkSpeed { get; set; } = 8; + public float CurrentSprintSpeed { get; set; } public bool Sprinting { get; set; } public Vector2 VelocityDir { get; } = Vector2.Zero; public GridCoordinates LastPosition { get; set; } @@ -61,7 +61,7 @@ namespace Content.Server.GameObjects.Components.Movement collideComp.PhysicsShapes.Add(new PhysShapeGrid(grid)); } - physComp.LinearVelocity = CalcNewVelocity(direction, enabled) * WalkMoveSpeed; + physComp.LinearVelocity = CalcNewVelocity(direction, enabled) * CurrentWalkSpeed; } } diff --git a/Content.Server/GameObjects/Components/Nutrition/HungerComponent.cs b/Content.Server/GameObjects/Components/Nutrition/HungerComponent.cs index 1b3d3cb21f..275384f317 100644 --- a/Content.Server/GameObjects/Components/Nutrition/HungerComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/HungerComponent.cs @@ -14,11 +14,11 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Nutrition { [RegisterComponent] - public sealed class HungerComponent : Component + public sealed class HungerComponent : Component, IMoveSpeedModifier { - #pragma warning disable 649 +#pragma warning disable 649 [Dependency] private readonly IRobustRandom _random; - #pragma warning restore 649 +#pragma warning restore 649 public override string Name => "Hunger"; @@ -33,7 +33,7 @@ namespace Content.Server.GameObjects.Components.Nutrition private HungerThreshold _currentHungerThreshold; private HungerThreshold _lastHungerThreshold; public float CurrentHunger => _currentHunger; - [ViewVariables] private float _currentHunger; + [ViewVariables(VVAccess.ReadWrite)] private float _currentHunger; public Dictionary HungerThresholds => _hungerThresholds; private Dictionary _hungerThresholds = new Dictionary @@ -53,16 +53,15 @@ namespace Content.Server.GameObjects.Components.Nutrition public void HungerThresholdEffect(bool force = false) { - if (_currentHungerThreshold != _lastHungerThreshold || force) { + if (_currentHungerThreshold != _lastHungerThreshold || force) + { Logger.InfoS("hunger", $"Updating hunger state for {Owner.Name}"); // Revert slow speed if required if (_lastHungerThreshold == HungerThreshold.Starving && _currentHungerThreshold != HungerThreshold.Dead && - Owner.TryGetComponent(out PlayerInputMoverComponent playerSpeedupComponent)) + Owner.TryGetComponent(out MovementSpeedModifierComponent movementSlowdownComponent)) { - // TODO shitcode: Come up something better - playerSpeedupComponent.WalkMoveSpeed = playerSpeedupComponent.WalkMoveSpeed * 2; - playerSpeedupComponent.SprintMoveSpeed = playerSpeedupComponent.SprintMoveSpeed * 4; + movementSlowdownComponent.RefreshMovementSpeedModifiers(); } // Update UI @@ -91,9 +90,9 @@ namespace Content.Server.GameObjects.Components.Nutrition case HungerThreshold.Starving: // TODO: If something else bumps this could cause mega-speed. // If some form of speed update system if multiple things are touching it use that. - if (Owner.TryGetComponent(out PlayerInputMoverComponent playerInputMoverComponent)) { - playerInputMoverComponent.WalkMoveSpeed = playerInputMoverComponent.WalkMoveSpeed / 2; - playerInputMoverComponent.SprintMoveSpeed = playerInputMoverComponent.SprintMoveSpeed / 4; + if (Owner.TryGetComponent(out MovementSpeedModifierComponent movementSlowdownComponent1)) + { + movementSlowdownComponent1.RefreshMovementSpeedModifiers(); } _lastHungerThreshold = _currentHungerThreshold; _actualDecayRate = _baseDecayRate * 0.6f; @@ -169,6 +168,29 @@ namespace Content.Server.GameObjects.Components.Nutrition { _currentHunger = HungerThresholds[HungerThreshold.Okay]; } + + float IMoveSpeedModifier.WalkSpeedModifier + { + get + { + if (_currentHungerThreshold == HungerThreshold.Starving) + { + return 0.5f; + } + return 1.0f; + } + } + float IMoveSpeedModifier.SprintSpeedModifier + { + get + { + if (_currentHungerThreshold == HungerThreshold.Starving) + { + return 0.5f; + } + return 1.0f; + } + } } public enum HungerThreshold diff --git a/Content.Server/GameObjects/Components/Nutrition/ThirstComponent.cs b/Content.Server/GameObjects/Components/Nutrition/ThirstComponent.cs index 76b7acf34e..807a52a151 100644 --- a/Content.Server/GameObjects/Components/Nutrition/ThirstComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/ThirstComponent.cs @@ -14,11 +14,11 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Nutrition { [RegisterComponent] - public sealed class ThirstComponent : Component + public sealed class ThirstComponent : Component, IMoveSpeedModifier { - #pragma warning disable 649 +#pragma warning disable 649 [Dependency] private readonly IRobustRandom _random; - #pragma warning restore 649 +#pragma warning restore 649 public override string Name => "Thirst"; @@ -53,16 +53,15 @@ namespace Content.Server.GameObjects.Components.Nutrition public void ThirstThresholdEffect(bool force = false) { - if (_currentThirstThreshold != _lastThirstThreshold || force) { + if (_currentThirstThreshold != _lastThirstThreshold || force) + { Logger.InfoS("thirst", $"Updating Thirst state for {Owner.Name}"); // Revert slow speed if required if (_lastThirstThreshold == ThirstThreshold.Parched && _currentThirstThreshold != ThirstThreshold.Dead && - Owner.TryGetComponent(out PlayerInputMoverComponent playerSpeedupComponent)) + Owner.TryGetComponent(out MovementSpeedModifierComponent movementSlowdownComponent)) { - // TODO shitcode: Come up something better - playerSpeedupComponent.WalkMoveSpeed = playerSpeedupComponent.WalkMoveSpeed * 2; - playerSpeedupComponent.SprintMoveSpeed = playerSpeedupComponent.SprintMoveSpeed * 4; + movementSlowdownComponent.RefreshMovementSpeedModifiers(); } // Update UI @@ -89,11 +88,9 @@ namespace Content.Server.GameObjects.Components.Nutrition return; case ThirstThreshold.Parched: - // TODO: If something else bumps this could cause mega-speed. - // If some form of speed update system if multiple things are touching it use that. - if (Owner.TryGetComponent(out PlayerInputMoverComponent playerInputMoverComponent)) { - playerInputMoverComponent.WalkMoveSpeed = playerInputMoverComponent.WalkMoveSpeed / 2; - playerInputMoverComponent.SprintMoveSpeed = playerInputMoverComponent.SprintMoveSpeed / 4; + if (Owner.TryGetComponent(out MovementSpeedModifierComponent movementSlowdownComponent1)) + { + movementSlowdownComponent1.RefreshMovementSpeedModifiers(); } _lastThirstThreshold = _currentThirstThreshold; _actualDecayRate = _baseDecayRate * 0.6f; @@ -166,6 +163,29 @@ namespace Content.Server.GameObjects.Components.Nutrition } } + float IMoveSpeedModifier.SprintSpeedModifier + { + get + { + if (_currentThirstThreshold == ThirstThreshold.Parched) + { + return 0.25f; + } + return 1.0f; + } + } + float IMoveSpeedModifier.WalkSpeedModifier + { + get + { + if (_currentThirstThreshold == ThirstThreshold.Parched) + { + return 0.5f; + } + return 1.0f; + } + } + public void ResetThirst() { _currentThirst = ThirstThresholds[ThirstThreshold.Okay]; diff --git a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs index 1530112eaf..a2524b6204 100644 --- a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs @@ -90,7 +90,7 @@ namespace Content.Server.GameObjects.EntitySystems private static void PlayerDetached(PlayerDetachedSystemMessage ev) { - if(ev.Entity.HasComponent()) + if (ev.Entity.HasComponent()) { ev.Entity.RemoveComponent(); } @@ -136,7 +136,7 @@ namespace Content.Server.GameObjects.EntitySystems } else { - physics.LinearVelocity = mover.VelocityDir * (mover.Sprinting ? mover.SprintMoveSpeed : mover.WalkMoveSpeed); + physics.LinearVelocity = mover.VelocityDir * (mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed); transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle(); // Handle footsteps. @@ -177,7 +177,7 @@ namespace Content.Server.GameObjects.EntitySystems private static void HandleDirChange(ICommonSession session, Direction dir, bool state) { - if(!TryGetAttachedComponent(session as IPlayerSession, out IMoverComponent moverComp)) + if (!TryGetAttachedComponent(session as IPlayerSession, out IMoverComponent moverComp)) return; moverComp.SetVelocityDirection(dir, state); @@ -185,14 +185,14 @@ namespace Content.Server.GameObjects.EntitySystems private static void HandleRunChange(ICommonSession session, bool running) { - if(!TryGetAttachedComponent(session as IPlayerSession, out PlayerInputMoverComponent moverComp)) + if (!TryGetAttachedComponent(session as IPlayerSession, out PlayerInputMoverComponent moverComp)) return; moverComp.Sprinting = running; } private static bool TryGetAttachedComponent(IPlayerSession session, out T component) - where T: IComponent + where T : IComponent { component = default; diff --git a/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs b/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs index d1f5cb9597..5e3ed05180 100644 --- a/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs +++ b/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs @@ -12,12 +12,12 @@ namespace Content.Server.Interfaces.GameObjects.Components.Movement /// /// Movement speed (m/s) that the entity walks. /// - float WalkMoveSpeed { get; set; } + float CurrentWalkSpeed { get; } /// /// Movement speed (m/s) that the entity sprints. /// - float SprintMoveSpeed { get; set; } + float CurrentSprintSpeed { get; } /// /// Is the entity Sprinting (running)? diff --git a/Resources/Prototypes/Entities/mobs/human.yml b/Resources/Prototypes/Entities/mobs/human.yml index e8c810591b..e7b3c9c2c2 100644 --- a/Resources/Prototypes/Entities/mobs/human.yml +++ b/Resources/Prototypes/Entities/mobs/human.yml @@ -9,6 +9,7 @@ hands: - left - right + - type: MovementSpeedModifier - type: Hunger - type: Thirst # Organs