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