diff --git a/Content.Client/GameObjects/Components/Mobs/SpeciesVisualizer2D.cs b/Content.Client/GameObjects/Components/Mobs/SpeciesVisualizer2D.cs index 9c4400588d..d49f93f920 100644 --- a/Content.Client/GameObjects/Components/Mobs/SpeciesVisualizer2D.cs +++ b/Content.Client/GameObjects/Components/Mobs/SpeciesVisualizer2D.cs @@ -16,7 +16,7 @@ namespace Content.Client.GameObjects.Components.Mobs { switch (state) { - case SharedSpeciesComponent.MobState.Stand: + case SharedSpeciesComponent.MobState.Standing: sprite.Rotation = 0; break; case SharedSpeciesComponent.MobState.Down: diff --git a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs index 418afb2ce6..73f9433436 100644 --- a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs +++ b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs @@ -1,8 +1,14 @@ -using Content.Server.GameObjects.EntitySystems; +using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Mobs; +using Content.Shared.Audio; using Content.Shared.GameObjects.Components.Mobs; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects.Components; using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; namespace Content.Server.GameObjects { @@ -96,10 +102,15 @@ namespace Content.Server.GameObjects { public void EnterState(IEntity entity) { + if(entity.TryGetComponent(out StunnableComponent stun)) + stun.CancelAll(); + + StandingStateHelper.Down(entity); } public void ExitState(IEntity entity) { + StandingStateHelper.Standing(entity); } public bool IsConscious => false; @@ -167,11 +178,10 @@ namespace Content.Server.GameObjects { public void EnterState(IEntity entity) { - if (entity.TryGetComponent(out AppearanceComponent appearance)) - { - var newState = SharedSpeciesComponent.MobState.Down; - appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, newState); - } + if(entity.TryGetComponent(out StunnableComponent stun)) + stun.CancelAll(); + + StandingStateHelper.Down(entity); if (entity.TryGetComponent(out CollidableComponent collidable)) { @@ -181,11 +191,7 @@ namespace Content.Server.GameObjects public void ExitState(IEntity entity) { - if (entity.TryGetComponent(out AppearanceComponent appearance)) - { - var newState = SharedSpeciesComponent.MobState.Stand; - appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, newState); - } + StandingStateHelper.Standing(entity); if (entity.TryGetComponent(out CollidableComponent collidable)) { diff --git a/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs b/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs index ed60cdd358..01ada4e049 100644 --- a/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs @@ -2,9 +2,14 @@ using System; using System.Threading; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces.GameObjects; +using Content.Server.Mobs; +using Content.Shared.Audio; using Content.Shared.GameObjects.Components.Mobs; using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.Audio; using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Timers; using Robust.Shared.IoC; using Robust.Shared.ViewVariables; @@ -13,109 +18,111 @@ using Timer = Robust.Shared.Timers.Timer; namespace Content.Server.GameObjects.Components.Mobs { [RegisterComponent] - public class StunnableComponent : Component, IActionBlocker + public class StunnableComponent : Component, IActionBlocker, IAttackHand { + [Dependency] private IEntitySystemManager _entitySystemManager; [Dependency] private ITimerManager _timerManager; private bool _stunned = false; private bool _knocked = false; + private bool _canHelp = true; - private int _stunCapMs = 20000; - private int _knockdownCapMs = 20000; + private float _stunCap = 20f; + private float _knockdownCap = 20f; + private float _helpKnockdownRemove = 1f; + private float _helpInterval = 1f; - private Timer _stunTimer; - private Timer _knockdownTimer; - - private CancellationTokenSource _stunTimerCancellation; - private CancellationTokenSource _knockdownTimerCancellation; + private float _stunnedTimer = 0f; + private float _knockdownTimer = 0f; public override string Name => "Stunnable"; [ViewVariables] public bool Stunned => _stunned; [ViewVariables] public bool KnockedDown => _knocked; - public void Stun(int milliseconds) + public void Stun(float seconds) { - if (_stunTimer != null) - { - _stunTimerCancellation.Cancel(); - milliseconds += _stunTimer.Time; - } + seconds = Math.Min(seconds + _stunnedTimer, _stunCap); - milliseconds = Math.Min(milliseconds, _stunCapMs); - - DropItemsInHands(); + StandingStateHelper.DropAllItemsInHands(Owner); _stunned = true; - _stunTimerCancellation = new CancellationTokenSource(); - _stunTimer = new Timer(milliseconds, false, OnStunTimerFired); - _timerManager.AddTimer(_stunTimer, _stunTimerCancellation.Token); + _stunnedTimer = seconds; } public override void Initialize() { base.Initialize(); - Timer.Spawn(10000, () => Paralyze(5000)); + Timer.Spawn(10000, () => Paralyze(15f)); } - public void Knockdown(int milliseconds) + public void Knockdown(float seconds) { - if (_knockdownTimer != null) - { - _knockdownTimerCancellation.Cancel(); - milliseconds += _knockdownTimer.Time; - } + seconds = MathF.Min(_knockdownTimer + seconds, _knockdownCap); - if (Owner.TryGetComponent(out AppearanceComponent appearance)) - { - var state = SharedSpeciesComponent.MobState.Down; - appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, state); - } - - milliseconds = Math.Min(milliseconds, _knockdownCapMs); - - DropItemsInHands(); + StandingStateHelper.Down(Owner); _knocked = true; - _knockdownTimerCancellation = new CancellationTokenSource(); - _knockdownTimer = new Timer(milliseconds, false, OnKnockdownTimerFired); - _timerManager.AddTimer(_knockdownTimer, _knockdownTimerCancellation.Token); + _knockdownTimer = seconds; } - private void DropItemsInHands() + public void Paralyze(float seconds) { - if (!Owner.TryGetComponent(out IHandsComponent hands)) return; - - foreach (var heldItem in hands.GetAllHeldItems()) - { - hands.Drop(heldItem.Owner); - } + Stun(seconds); + Knockdown(seconds); } - private void OnStunTimerFired() + /// + /// Used when + /// + public void CancelAll() { - _stunned = false; - _stunTimer = null; - _stunTimerCancellation = null; - } - - private void OnKnockdownTimerFired() - { - if (Owner.TryGetComponent(out AppearanceComponent appearance)) - { - var state = SharedSpeciesComponent.MobState.Stand; - appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, state); - } - _knocked = false; - _knockdownTimer = null; - _knockdownTimerCancellation = null; + _stunned = false; + + _knockdownTimer = 0f; + _stunnedTimer = 0f; } - public void Paralyze(int milliseconds) + public bool AttackHand(AttackHandEventArgs eventArgs) { - Stun(milliseconds); - Knockdown(milliseconds); + if (!_canHelp || KnockedDown) + return false; + + _canHelp = false; + Timer.Spawn(((int)_helpInterval*1000), () => _canHelp = true); + + IoCManager.Resolve().GetEntitySystem() + .Play("/Audio/effects/thudswoosh.ogg", Owner, AudioHelpers.WithVariation(0.5f)); + + _knockdownTimer -= _helpKnockdownRemove; + + return true; + } + + public void Update(float delta) + { + if (_knocked) + { + _knockdownTimer -= delta; + + if (_knockdownTimer <= 0f) + { + StandingStateHelper.Standing(Owner); + + _knocked = false; + } + } + + if (_stunned) + { + _stunnedTimer -= delta; + + if (_stunnedTimer <= 0) + { + _stunned = false; + } + } } #region ActionBlockers diff --git a/Content.Server/GameObjects/EntitySystems/StunSystem.cs b/Content.Server/GameObjects/EntitySystems/StunSystem.cs new file mode 100644 index 0000000000..8f742b8092 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/StunSystem.cs @@ -0,0 +1,27 @@ +using Content.Server.GameObjects.Components.Mobs; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.Server.GameObjects.EntitySystems +{ + public class StunSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + EntityQuery = new TypeEntityQuery(typeof(StunnableComponent)); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var entity in RelevantEntities) + { + entity.GetComponent().Update(frameTime); + } + } + } +} diff --git a/Content.Server/Mobs/StandingStateHelper.cs b/Content.Server/Mobs/StandingStateHelper.cs new file mode 100644 index 0000000000..e4b0c09e52 --- /dev/null +++ b/Content.Server/Mobs/StandingStateHelper.cs @@ -0,0 +1,81 @@ +using Content.Server.Interfaces.GameObjects; +using Content.Shared.Audio; +using Content.Shared.GameObjects.Components.Mobs; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Shared.Audio; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.Mobs +{ + public static class StandingStateHelper + { + /// + /// Set's the mob standing state to down. + /// + /// The mob in question + /// Whether to play a sound when falling down or not + /// Whether to make the mob drop all the items on his hands + /// False if the mob was already downed or couldn't set the state + public static bool Down(IEntity entity, bool playSound = true, bool dropItems = true) + { + if (!entity.TryGetComponent(out AppearanceComponent appearance)) return false; + + appearance.TryGetData(SharedSpeciesComponent.MobVisuals.RotationState, out var oldState); + + var newState = SharedSpeciesComponent.MobState.Down; + if (newState == oldState) + return false; + + appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, newState); + + if (playSound) + PlaySoundCollection("bodyfall", AudioHelpers.WithVariation(0.5f)); + + if(dropItems) + DropAllItemsInHands(entity); + + return true; + } + + /// + /// Sets the mob's standing state to standing. + /// + /// The mob in question. + /// False if the mob was already standing or couldn't set the state + public static bool Standing(IEntity entity) + { + if (!entity.TryGetComponent(out AppearanceComponent appearance)) return false; + appearance.TryGetData(SharedSpeciesComponent.MobVisuals.RotationState, out var oldState); + var newState = SharedSpeciesComponent.MobState.Standing; + if (newState == oldState) + return false; + + appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, newState); + + return true; + } + + public static void DropAllItemsInHands(IEntity entity) + { + if (!entity.TryGetComponent(out IHandsComponent hands)) return; + + foreach (var heldItem in hands.GetAllHeldItems()) + { + hands.Drop(heldItem.Owner); + } + } + + private static void PlaySoundCollection(string name, AudioParams parameters = default) + { + var soundCollection = IoCManager.Resolve().Index(name); + var file = IoCManager.Resolve().Pick(soundCollection.PickFiles); + IoCManager.Resolve().GetEntitySystem() + .Play(file, parameters); + } + } +} diff --git a/Content.Shared/GameObjects/Components/Mobs/SharedSpeciesComponent.cs b/Content.Shared/GameObjects/Components/Mobs/SharedSpeciesComponent.cs index 0396410c9a..eed7165fbb 100644 --- a/Content.Shared/GameObjects/Components/Mobs/SharedSpeciesComponent.cs +++ b/Content.Shared/GameObjects/Components/Mobs/SharedSpeciesComponent.cs @@ -20,7 +20,7 @@ namespace Content.Shared.GameObjects.Components.Mobs /// /// Mob is standing up /// - Stand, + Standing, /// /// Mob is laying down