diff --git a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs index 6244af156a..db412a8467 100644 --- a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks.Dataflow; using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects; using Robust.Server.GameObjects.Components.Container; @@ -129,6 +130,9 @@ namespace Content.Server.GameObjects { var pass = false; + if (!ActionBlockerSystem.CanEquip(Owner)) + return false; + if (item is ClothingComponent clothing) { if (clothing.SlotFlags != SlotFlags.PREVENTEQUIP && (clothing.SlotFlags & SlotMasks[slot]) != 0) @@ -185,6 +189,9 @@ namespace Content.Server.GameObjects /// public bool CanUnequip(Slots slot) { + if (!ActionBlockerSystem.CanUnequip(Owner)) + return false; + var InventorySlot = SlotContainers[slot]; return InventorySlot.ContainedEntity != null && InventorySlot.CanRemove(InventorySlot.ContainedEntity); } diff --git a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs index 515cf77e60..418afb2ce6 100644 --- a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs +++ b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs @@ -77,6 +77,16 @@ namespace Content.Server.GameObjects { return true; } + + bool IActionBlocker.CanEquip() + { + return true; + } + + bool IActionBlocker.CanUnequip() + { + return true; + } } /// @@ -138,6 +148,16 @@ namespace Content.Server.GameObjects { return false; } + + bool IActionBlocker.CanEquip() + { + return false; + } + + bool IActionBlocker.CanUnequip() + { + return false; + } } /// @@ -219,5 +239,15 @@ namespace Content.Server.GameObjects { return false; } + + bool IActionBlocker.CanEquip() + { + return false; + } + + bool IActionBlocker.CanUnequip() + { + return false; + } } } diff --git a/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs b/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs new file mode 100644 index 0000000000..ed60cdd358 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs @@ -0,0 +1,145 @@ +using System; +using System.Threading; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces.GameObjects; +using Content.Shared.GameObjects.Components.Mobs; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.Timers; +using Robust.Shared.IoC; +using Robust.Shared.ViewVariables; +using Timer = Robust.Shared.Timers.Timer; + +namespace Content.Server.GameObjects.Components.Mobs +{ + [RegisterComponent] + public class StunnableComponent : Component, IActionBlocker + { + [Dependency] private ITimerManager _timerManager; + + private bool _stunned = false; + private bool _knocked = false; + + private int _stunCapMs = 20000; + private int _knockdownCapMs = 20000; + + private Timer _stunTimer; + private Timer _knockdownTimer; + + private CancellationTokenSource _stunTimerCancellation; + private CancellationTokenSource _knockdownTimerCancellation; + + public override string Name => "Stunnable"; + + [ViewVariables] public bool Stunned => _stunned; + [ViewVariables] public bool KnockedDown => _knocked; + + public void Stun(int milliseconds) + { + if (_stunTimer != null) + { + _stunTimerCancellation.Cancel(); + milliseconds += _stunTimer.Time; + } + + milliseconds = Math.Min(milliseconds, _stunCapMs); + + DropItemsInHands(); + + _stunned = true; + _stunTimerCancellation = new CancellationTokenSource(); + _stunTimer = new Timer(milliseconds, false, OnStunTimerFired); + _timerManager.AddTimer(_stunTimer, _stunTimerCancellation.Token); + } + + public override void Initialize() + { + base.Initialize(); + Timer.Spawn(10000, () => Paralyze(5000)); + } + + public void Knockdown(int milliseconds) + { + if (_knockdownTimer != null) + { + _knockdownTimerCancellation.Cancel(); + milliseconds += _knockdownTimer.Time; + } + + if (Owner.TryGetComponent(out AppearanceComponent appearance)) + { + var state = SharedSpeciesComponent.MobState.Down; + appearance.SetData(SharedSpeciesComponent.MobVisuals.RotationState, state); + } + + milliseconds = Math.Min(milliseconds, _knockdownCapMs); + + DropItemsInHands(); + + _knocked = true; + _knockdownTimerCancellation = new CancellationTokenSource(); + _knockdownTimer = new Timer(milliseconds, false, OnKnockdownTimerFired); + _timerManager.AddTimer(_knockdownTimer, _knockdownTimerCancellation.Token); + } + + private void DropItemsInHands() + { + if (!Owner.TryGetComponent(out IHandsComponent hands)) return; + + foreach (var heldItem in hands.GetAllHeldItems()) + { + hands.Drop(heldItem.Owner); + } + } + + private void OnStunTimerFired() + { + _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; + } + + public void Paralyze(int milliseconds) + { + Stun(milliseconds); + Knockdown(milliseconds); + } + + #region ActionBlockers + public bool CanMove() => (!Stunned); + + public bool CanInteract() => (!Stunned); + + public bool CanUse() => (!Stunned); + + public bool CanThrow() => (!Stunned); + + public bool CanSpeak() => true; + + public bool CanDrop() => (!Stunned); + + public bool CanPickup() => (!Stunned); + + public bool CanEmote() => true; + + public bool CanAttack() => (!Stunned); + + public bool CanEquip() => (!Stunned); + + public bool CanUnequip() => (!Stunned); + #endregion + } +} diff --git a/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs b/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs index 36de02bb79..e6ce5d7862 100644 --- a/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/ActionBlockerSystem.cs @@ -22,6 +22,8 @@ namespace Content.Server.GameObjects.EntitySystems bool CanEmote() => true; bool CanAttack() => true; + bool CanEquip() => true; + bool CanUnequip() => true; } public class ActionBlockerSystem : EntitySystem @@ -119,5 +121,29 @@ namespace Content.Server.GameObjects.EntitySystems return canattack; } + + public static bool CanEquip(IEntity entity) + { + bool canequip = true; + + foreach (var actionblockercomponents in entity.GetAllComponents()) + { + canequip &= actionblockercomponents.CanEquip(); + } + + return canequip; + } + + public static bool CanUnequip(IEntity entity) + { + bool canunequip = true; + + foreach (var actionblockercomponents in entity.GetAllComponents()) + { + canunequip &= actionblockercomponents.CanUnequip(); + } + + return canunequip; + } } } diff --git a/Resources/Prototypes/Entities/Mobs/human.yml b/Resources/Prototypes/Entities/Mobs/human.yml index 88e23fd6c0..fdae22bac7 100644 --- a/Resources/Prototypes/Entities/Mobs/human.yml +++ b/Resources/Prototypes/Entities/Mobs/human.yml @@ -127,6 +127,7 @@ - type: FootstepSound - type: HumanoidAppearance - type: HumanInventoryController + - type: Stunnable - type: entity save: false diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 7853887435..cdfdaad665 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -55,4 +55,5 @@ True True True + True True