diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index bb96d478e3..6fb559b515 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -76,7 +76,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem } } - if (weapon.Attacking || weapon.NextAttack > Timing.CurTime) + if (weapon.Attacking) // WD EDIT { return; } @@ -124,10 +124,31 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem target = screen.GetDamageableClickedEntity(mousePos); // WD EDIT } + // WD START + if (target == null) + { + if (weapon.NextAttack > Timing.CurTime || weapon.NextMobAttack > Timing.CurTime) + return; + } + else if (IsMob(target.Value)) + { + if (weapon.NextMobAttack > Timing.CurTime) + return; + } + else + { + if (weapon.NextAttack > Timing.CurTime) + return; + } + // WD END + EntityManager.RaisePredictiveEvent(new DisarmAttackEvent(GetNetEntity(target), GetNetCoordinates(coordinates))); return; } + if (weapon.NextAttack > Timing.CurTime || weapon.NextMobAttack > Timing.CurTime) + return; + // WD START if (HasComp(weaponUid)) { @@ -176,6 +197,24 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem if (Interaction.CombatModeCanHandInteract(entity, target)) return; + // WD START + if (target == null) + { + if (weapon.NextAttack > Timing.CurTime || weapon.NextMobAttack > Timing.CurTime) + return; + } + else if (IsMob(target.Value)) + { + if (weapon.NextMobAttack > Timing.CurTime) + return; + } + else + { + if (weapon.NextAttack > Timing.CurTime) + return; + } + // WD END + RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates))); } } diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index 84f7616675..3156b44113 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Stunnable; using Content.Server.Temperature.Components; using Content.Server.Temperature.Systems; using Content.Server.Damage.Components; +using Content.Shared._White.Blocking; using Content.Shared.ActionBlocker; using Content.Shared.Alert; using Content.Shared.Atmos; diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs index dfbe45c035..e5ba3c7860 100644 --- a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -17,6 +17,7 @@ using Content.Server.Chemistry.Containers.EntitySystems; using Robust.Shared.GameStates; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Content.Shared._White.Blocking; using Robust.Server.Audio; namespace Content.Server.Chemistry.EntitySystems; @@ -32,7 +33,8 @@ public sealed class HypospraySystem : SharedHypospraySystem base.Initialize(); SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent(OnAttack); + SubscribeLocalEvent(OnAttack, + after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT SubscribeLocalEvent(OnUseInHand); } @@ -68,6 +70,9 @@ public sealed class HypospraySystem : SharedHypospraySystem public void OnAttack(Entity entity, ref MeleeHitEvent args) { + if (args.Handled) // WD + return; + if (!args.HitEntities.Any()) return; diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs index 3c57cc31af..183dda05d1 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Shared._White.Blocking; using Content.Shared.Inventory; using Content.Shared.Popups; using Content.Shared.Projectiles; @@ -28,7 +29,8 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(HandleProjectileHit); SubscribeLocalEvent(HandleEmbed); - SubscribeLocalEvent(HandleMeleeHit); + SubscribeLocalEvent(HandleMeleeHit, + after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT } private void HandleProjectileHit(Entity entity, ref ProjectileHitEvent args) @@ -43,6 +45,8 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem private void HandleMeleeHit(Entity entity, ref MeleeHitEvent args) { + if (args.Handled) // WD + return; // MeleeHitEvent is weird, so we have to filter to make sure we actually // hit something and aren't just examining the weapon. if (args.IsHit) diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index 7780e5d467..747e3205a7 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -1,5 +1,6 @@ using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Fluids.Components; +using Content.Shared._White.Blocking; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; @@ -28,7 +29,8 @@ public sealed partial class PuddleSystem SubscribeLocalEvent(SpillOnLand); // Openable handles the event if it's closed - SubscribeLocalEvent(SplashOnMeleeHit, after: [typeof(OpenableSystem)]); + SubscribeLocalEvent( + SplashOnMeleeHit, before: new[] {typeof(MeleeBlockSystem)}, after:[typeof(OpenableSystem)]); // WD EDIT SubscribeLocalEvent(OnGotEquipped); SubscribeLocalEvent(OnGotUnequipped); SubscribeLocalEvent(OnOverflow); diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index 1445e253f3..1cee053c8f 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -3,6 +3,7 @@ using Content.Server.DoAfter; using Content.Server.Fluids.EntitySystems; using Content.Server.Forensics.Components; using Content.Server.Popups; +using Content.Shared._White.Blocking; using Content.Shared.Chemistry.Components; using Content.Shared.DoAfter; using Content.Shared.Forensics; @@ -27,7 +28,8 @@ namespace Content.Server.Forensics SubscribeLocalEvent(OnDNAInit); SubscribeLocalEvent(OnBeingGibbed); - SubscribeLocalEvent(OnMeleeHit); + SubscribeLocalEvent(OnMeleeHit, + after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT SubscribeLocalEvent(OnRehydrated); SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) }); SubscribeLocalEvent(OnCleanForensicsDoAfter); @@ -61,6 +63,8 @@ namespace Content.Server.Forensics private void OnMeleeHit(EntityUid uid, ForensicsComponent component, MeleeHitEvent args) { + if (args.Handled) // WD EDIT + return; if((args.BaseDamage.DamageDict.TryGetValue("Blunt", out var bluntDamage) && bluntDamage.Value > 0) || (args.BaseDamage.DamageDict.TryGetValue("Slash", out var slashDamage) && slashDamage.Value > 0) || (args.BaseDamage.DamageDict.TryGetValue("Piercing", out var pierceDamage) && pierceDamage.Value > 0)) diff --git a/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs b/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs index 2ae20817e2..56ae632eeb 100644 --- a/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs +++ b/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs @@ -101,7 +101,7 @@ public sealed partial class NPCCombatSystem return; } - if (weapon.NextAttack > curTime || !Enabled) + if (weapon.NextAttack > curTime || weapon.NextMobAttack > curTime || !Enabled) // WD EDIT return; if (_random.Prob(component.MissChance) && diff --git a/Content.Server/NPC/Systems/NPCJukeSystem.cs b/Content.Server/NPC/Systems/NPCJukeSystem.cs index da9fa1f761..84a780635b 100644 --- a/Content.Server/NPC/Systems/NPCJukeSystem.cs +++ b/Content.Server/NPC/Systems/NPCJukeSystem.cs @@ -143,7 +143,7 @@ public sealed class NPCJukeSystem : EntitySystem if (!_melee.TryGetWeapon(uid, out var weaponUid, out var weapon)) return; - var cdRemaining = weapon.NextAttack - _timing.CurTime; + var cdRemaining = weapon.NextMobAttack - _timing.CurTime; // WD EDIT var attackCooldown = TimeSpan.FromSeconds(1f / _melee.GetAttackRate(weaponUid, uid, weapon)); // Might as well get in range. diff --git a/Content.Server/Weapons/Melee/WeaponRandom/WeaponRandomSystem.cs b/Content.Server/Weapons/Melee/WeaponRandom/WeaponRandomSystem.cs index 7b246b8d09..15eca43bd9 100644 --- a/Content.Server/Weapons/Melee/WeaponRandom/WeaponRandomSystem.cs +++ b/Content.Server/Weapons/Melee/WeaponRandom/WeaponRandomSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared._White.Blocking; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Random; using Robust.Shared.Audio.Systems; diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index 914f969bc8..0bc9aad9c8 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Cloning; using Content.Server.Drone.Components; using Content.Server.Emoting.Systems; using Content.Server.Speech.EntitySystems; +using Content.Shared._White.Blocking; using Content.Shared.Bed.Sleep; using Content.Shared.Cloning; using Content.Shared.Damage; @@ -56,7 +57,8 @@ namespace Content.Server.Zombies SubscribeLocalEvent(OnEmote, before: new[] { typeof(VocalSystem), typeof(BodyEmotesSystem) }); - SubscribeLocalEvent(OnMeleeHit); + SubscribeLocalEvent(OnMeleeHit, + after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT SubscribeLocalEvent(OnMobState); SubscribeLocalEvent(OnZombieCloning); SubscribeLocalEvent(OnSleepAttempt); @@ -207,6 +209,9 @@ namespace Content.Server.Zombies private void OnMeleeHit(EntityUid uid, ZombieComponent component, MeleeHitEvent args) { + if (args.Handled) // WD EDIT + return; + if (!TryComp(args.User, out _)) return; diff --git a/Content.Server/_White/Cult/Items/Systems/ReturnItemOnThrowSystem.cs b/Content.Server/_White/Cult/Items/Systems/ReturnItemOnThrowSystem.cs index a7126f67d2..0b10b9d173 100644 --- a/Content.Server/_White/Cult/Items/Systems/ReturnItemOnThrowSystem.cs +++ b/Content.Server/_White/Cult/Items/Systems/ReturnItemOnThrowSystem.cs @@ -31,7 +31,7 @@ public sealed class ReturnItemOnThrowSystem : EntitySystem if (!HasComp(args.Target)) return; - if (!_stun.IsParalyzed(args.Target) && !isCultist && !_holyWeapon.IsHoldingHolyWeapon(args.Target)) + if (!isCultist && !_holyWeapon.IsHoldingHolyWeapon(args.Target)) { _stun.TryParalyze(args.Target, TimeSpan.FromSeconds(component.StunTime), true); } diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Constructs.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Constructs.cs index 1cf9f55d3f..21e45787c2 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Constructs.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Constructs.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared._White.Blocking; using Content.Shared._White.Cult.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Mind.Components; @@ -22,7 +23,7 @@ public partial class CultSystem SubscribeLocalEvent(OnShellInit); SubscribeLocalEvent(OnShellRemove); SubscribeLocalEvent(OnShellSelected); - SubscribeLocalEvent(OnMeleeHit); + SubscribeLocalEvent(OnMeleeHit, before: new []{typeof(MeleeBlockSystem)}); } private void OnMeleeHit(Entity ent, ref MeleeHitEvent args) diff --git a/Content.Server/_White/Other/CritSystem/CritSystem.cs b/Content.Server/_White/Other/CritSystem/CritSystem.cs index 0e8c3ae079..a7f197a220 100644 --- a/Content.Server/_White/Other/CritSystem/CritSystem.cs +++ b/Content.Server/_White/Other/CritSystem/CritSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.Popups; +using Content.Shared._White.Blocking; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Examine; @@ -25,7 +26,7 @@ public sealed class CritSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent(HandleHit); + SubscribeLocalEvent(HandleHit, before: new [] {typeof(MeleeBlockSystem)}); SubscribeLocalEvent(GetMeleeAttackRate); SubscribeLocalEvent(OnRefreshMoveSpeed); } diff --git a/Content.Server/_White/Other/MeleeBlockSystem/MeleeBlockComponent.cs b/Content.Server/_White/Other/MeleeBlockSystem/MeleeBlockComponent.cs deleted file mode 100644 index c5c63c33a6..0000000000 --- a/Content.Server/_White/Other/MeleeBlockSystem/MeleeBlockComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Content.Server._White.Other.MeleeBlockSystem; - -[RegisterComponent] -public sealed partial class MeleeBlockComponent : Component -{ - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float BlockChance = 0.4f; -} diff --git a/Content.Server/_White/Other/MeleeBlockSystem/MeleeBlockSystem.cs b/Content.Server/_White/Other/MeleeBlockSystem/MeleeBlockSystem.cs deleted file mode 100644 index 3f072fa2ef..0000000000 --- a/Content.Server/_White/Other/MeleeBlockSystem/MeleeBlockSystem.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Content.Shared.Hands.Components; -using Content.Shared.Popups; -using Content.Shared.Weapons.Melee.Events; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Random; - -namespace Content.Server._White.Other.MeleeBlockSystem; - -public sealed class MeleeBlockSystem : EntitySystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnBlockAttempt); - } - - private void OnBlockAttempt(Entity ent, ref MeleeBlockAttemptEvent args) - { - if (ent.Owner == args.Attacker || - !TryComp(ent.Comp.ActiveHandEntity, out MeleeBlockComponent? blockComponent) || - !_random.Prob(blockComponent.BlockChance)) - return; - - args.Blocked = true; - - _popupSystem.PopupEntity("заблокировал!", ent); - - _audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/block_metal1.ogg"), ent, - AudioParams.Default.WithVariation(0.25f)); - } -} diff --git a/Content.Server/_White/Other/RandomDamageSystem/RandomDamageSystem.cs b/Content.Server/_White/Other/RandomDamageSystem/RandomDamageSystem.cs index 8a7c46e010..dd9274a109 100644 --- a/Content.Server/_White/Other/RandomDamageSystem/RandomDamageSystem.cs +++ b/Content.Server/_White/Other/RandomDamageSystem/RandomDamageSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared._White.Blocking; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Weapons.Melee.Events; @@ -15,7 +16,7 @@ public sealed class RandomDamageSystem : EntitySystem { base.Initialize(); - SubscribeLocalEvent(HandleHit); + SubscribeLocalEvent(HandleHit, before: new [] {typeof(MeleeBlockSystem)}); } private void HandleHit(Entity ent, ref MeleeHitEvent args) diff --git a/Content.Server/_White/Stunprod/StunprodComponent.cs b/Content.Server/_White/Stunprod/StunprodComponent.cs index e737d61abc..788571303f 100644 --- a/Content.Server/_White/Stunprod/StunprodComponent.cs +++ b/Content.Server/_White/Stunprod/StunprodComponent.cs @@ -4,7 +4,7 @@ namespace Content.Server._White.Stunprod; public sealed partial class StunprodComponent : Component { [DataField, ViewVariables(VVAccess.ReadWrite)] - public float EnergyPerUse { get; set; } = 72; + public float EnergyPerUse { get; set; } = 144; [DataField] public bool HasHeldPrefix = true; diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index c163efb405..bd362e9038 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -40,6 +40,7 @@ namespace Content.Shared.Alert MoodDead, CultBuffed, Knockdown, + RecentlyBlocked, //WD end PilotingShuttle, Peckish, diff --git a/Content.Shared/Blocking/BlockingSystem.User.cs b/Content.Shared/Blocking/BlockingSystem.User.cs index 42fc119751..f168c36d5d 100644 --- a/Content.Shared/Blocking/BlockingSystem.User.cs +++ b/Content.Shared/Blocking/BlockingSystem.User.cs @@ -1,5 +1,6 @@ using Content.Shared.Damage; using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -19,8 +20,33 @@ public sealed partial class BlockingSystem SubscribeLocalEvent(OnInsertAttempt); SubscribeLocalEvent(OnAnchorChanged); SubscribeLocalEvent(OnEntityTerminating); + + SubscribeLocalEvent(OnMeleeBlockAttempt); // WD } + // WD START + private void OnMeleeBlockAttempt(Entity ent, ref MeleeBlockAttemptEvent args) + { + if (args.Handled) + return; + + var uid = ent.Comp.BlockingItem; + if (!TryComp(uid, out BlockingComponent? blocking) || !blocking.IsBlocking) + return; + + if (TryComp(uid.Value, out ItemToggleComponent? toggle) && !toggle.Activated) + return; + + if (!TryComp(uid.Value, out DamageableComponent? damageable)) + return; + + _audio.PlayPredicted(blocking.BlockSound, ent, args.Attacker); + _popupSystem.PopupPredicted(Loc.GetString("melee-block-event-blocked"), ent, args.Attacker); + _damageable.TryChangeDamage(uid.Value, args.Damage, damageable: damageable); + args.Handled = true; + } + // WD END + private void OnParentChanged(EntityUid uid, BlockingUserComponent component, ref EntParentChangedMessage args) { UserStopBlocking(uid, component); diff --git a/Content.Shared/Damage/Systems/StaminaSystem.cs b/Content.Shared/Damage/Systems/StaminaSystem.cs index 31c8689bac..ec9936c423 100644 --- a/Content.Shared/Damage/Systems/StaminaSystem.cs +++ b/Content.Shared/Damage/Systems/StaminaSystem.cs @@ -135,6 +135,9 @@ public sealed partial class StaminaSystem : EntitySystem private void OnMeleeHit(EntityUid uid, StaminaDamageOnHitComponent component, MeleeHitEvent args) { + if (args.Handled) // WD + return; + if (!args.IsHit || !args.HitEntities.Any() || component.Damage <= 0f) diff --git a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs index 523f67bac3..8db31715fc 100644 --- a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Wieldable; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; +using Robust.Shared.Timing; namespace Content.Shared.Item.ItemToggle; /// @@ -22,6 +23,7 @@ public abstract class SharedItemToggleSystem : EntitySystem [Dependency] private readonly SharedPointLightSystem _light = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { @@ -240,6 +242,9 @@ public abstract class SharedItemToggleSystem : EntitySystem /// private void UpdateActiveSound(EntityUid uid, ItemToggleActiveSoundComponent activeSound, ref ItemToggledEvent args) { + if (!_timing.IsFirstTimePredicted) // WD + return; + if (args.Activated) { if (activeSound.ActiveSound != null && activeSound.PlayingStream == null) diff --git a/Content.Shared/Jittering/JitteringComponent.cs b/Content.Shared/Jittering/JitteringComponent.cs index 417b42e895..344b194d70 100644 --- a/Content.Shared/Jittering/JitteringComponent.cs +++ b/Content.Shared/Jittering/JitteringComponent.cs @@ -9,11 +9,11 @@ public sealed partial class JitteringComponent : Component { [AutoNetworkedField] [ViewVariables(VVAccess.ReadWrite)] - public float Amplitude { get; set; } + public float Amplitude { get; set; } = 10f; [AutoNetworkedField] [ViewVariables(VVAccess.ReadWrite)] - public float Frequency { get; set; } + public float Frequency { get; set; } = 4f; [ViewVariables(VVAccess.ReadWrite)] public Vector2 LastJitter { get; set; } diff --git a/Content.Shared/Standing/Systems/SharedStandingStateSystem.cs b/Content.Shared/Standing/Systems/SharedStandingStateSystem.cs index 17cb0bdb5a..c502bff8a1 100644 --- a/Content.Shared/Standing/Systems/SharedStandingStateSystem.cs +++ b/Content.Shared/Standing/Systems/SharedStandingStateSystem.cs @@ -78,7 +78,7 @@ public abstract partial class SharedStandingStateSystem : EntitySystem RaiseNetworkEvent(new CheckAutoGetUpEvent()); // WD EDIT - if (_stun.IsParalyzed(uid)) + if (HasComp(uid)) { return; } diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index 4f4df8bc61..f97be06252 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -265,13 +265,6 @@ public abstract class SharedStunSystem : EntitySystem args.Modifier *= KnockDownModifier; }*/ - //WD EDIT START - public bool IsParalyzed(EntityUid uid) - { - return HasComp(uid) || HasComp(uid); - } - //WD EDIT END - #region Attempt Event Handling private void OnMoveAttempt(EntityUid uid, StunnedComponent stunned, UpdateCanMoveEvent args) diff --git a/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs b/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs index ee42fc2553..5770fd8d10 100644 --- a/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs +++ b/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs @@ -97,6 +97,10 @@ public record struct GetMeleeAttackRateEvent(EntityUid Weapon, float Rate, float public record struct GetHeavyDamageModifierEvent(EntityUid Weapon, FixedPoint2 DamageModifier, float Multipliers, EntityUid User); // WD START -[ByRefEvent] -public record struct MeleeBlockAttemptEvent(EntityUid Attacker, bool Blocked = false); +public sealed class MeleeBlockAttemptEvent(EntityUid attacker, DamageSpecifier damage) : HandledEntityEventArgs +{ + public EntityUid Attacker = attacker; + + public DamageSpecifier Damage = damage; +} // WD END diff --git a/Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs b/Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs index 8b76d14a02..65693dc506 100644 --- a/Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs +++ b/Content.Shared/Weapons/Melee/MeleeThrowOnHitSystem.cs @@ -64,6 +64,9 @@ public sealed class MeleeThrowOnHitSystem : EntitySystem private void OnMeleeHit(Entity ent, ref MeleeHitEvent args) { + if (args.Handled) // WD + return; + var (_, comp) = ent; if (!args.IsHit) return; diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index c19cc5edfa..79ef8319e5 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -70,7 +70,10 @@ public sealed partial class MeleeWeaponComponent : Component public bool CanAttackSelf = true; [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] - public bool CanMiss = true; + public bool CanBeBlocked = true; + + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + public bool CanMiss; [DataField] public EntityWhitelist? AttackWhitelist; @@ -78,6 +81,14 @@ public sealed partial class MeleeWeaponComponent : Component [DataField] public EntityWhitelist? AttackBlacklist; + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] + [ViewVariables(VVAccess.ReadWrite)] + [AutoPausedField] + public TimeSpan NextMobAttack; + + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + public float? EquipCooldown; + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public bool IgnoreResistances; @@ -114,12 +125,11 @@ public sealed partial class MeleeWeaponComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField] public FixedPoint2 ClickDamageModifier = FixedPoint2.New(1); - // TODO: Temporarily 1.5 until interactionoutline is adjusted to use melee, then probably drop to 1.2 /// /// Nearest edge range to hit an entity. /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] - public float Range = 1.5f; + public float Range = 1.2f; /// /// Total width of the angle for wide attacks. diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 85e6edfa49..e43e991fe3 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -22,6 +22,7 @@ using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; using Content.Shared._White; using Content.Shared._White.Implants.NeuroControl; +using Content.Shared.Movement.Components; using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Physics; @@ -94,14 +95,14 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem private void OnMapInit(EntityUid uid, MeleeWeaponComponent component, MapInitEvent args) { - if (component.NextAttack > Timing.CurTime) + if (component.NextAttack > Timing.CurTime || component.NextMobAttack > Timing.CurTime) // WD EDIT Log.Warning($"Initializing a map that contains an entity that is on cooldown. Entity: {ToPrettyString(uid)}"); #endif } private void OnMeleeShotAttempted(EntityUid uid, MeleeWeaponComponent comp, ref ShotAttemptedEvent args) { - if (comp.NextAttack > Timing.CurTime) + if (comp.NextAttack > Timing.CurTime || comp.NextMobAttack > Timing.CurTime) // WD EDIT args.Cancel(); } @@ -110,16 +111,28 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem if (!TryComp(uid, out var gun)) return; + // WD EDIT START + var dirty = false; if (gun.NextFire > component.NextAttack) { component.NextAttack = gun.NextFire; - Dirty(uid, component); + dirty = true; } + + if (gun.NextFire > component.NextMobAttack) + { + component.NextMobAttack = gun.NextFire; + dirty = true; + } + + if (dirty) + Dirty(uid, component); + // WD EDIT END } private void OnMeleeSelected(EntityUid uid, MeleeWeaponComponent component, HandSelectedEvent args) { - var attackRate = GetAttackRate(uid, args.User, component); + var attackRate = component.EquipCooldown ?? GetAttackRate(uid, args.User, component); // WD EDIT if (attackRate.Equals(0f)) return; @@ -133,11 +146,21 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem var curTime = Timing.CurTime; var minimum = curTime + TimeSpan.FromSeconds(1 / attackRate); - if (minimum < component.NextAttack) - return; - - component.NextAttack = minimum; - Dirty(uid, component); + // WD EDIT START + var dirty = false; + if (minimum > component.NextAttack) + { + component.NextAttack = minimum; + dirty = true; + } + if (minimum > component.NextMobAttack) + { + component.NextMobAttack = minimum; + dirty = true; + } + if (dirty) + Dirty(uid, component); + // WD EDIT END } private void OnGetBonusMeleeDamage(EntityUid uid, BonusMeleeDamageComponent component, ref GetMeleeDamageEvent args) @@ -339,6 +362,13 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem return AttemptAttack(user, weaponUid, weapon, new DisarmAttackEvent(GetNetEntity(target), GetNetCoordinates(targetXform.Coordinates)), null); } + private enum UpdateNextAttack : byte // WD + { + Mob, + NonMob, + Both + } + /// /// Called when a windup is finished and an attack is tried. /// @@ -347,16 +377,16 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem { var curTime = Timing.CurTime; - if (weapon.NextAttack > curTime) - return false; - if (!CombatMode.IsInCombatMode(user)) return false; + var update = UpdateNextAttack.Both; // WD switch (attack) { case LightAttackEvent light: var lightTarget = GetEntity(light.Target); + update = lightTarget == null ? UpdateNextAttack.Both : + IsMob(lightTarget.Value) ? UpdateNextAttack.Mob : UpdateNextAttack.NonMob; // WD if (!Blocker.CanAttack(user, lightTarget, (weaponUid, weapon))) return false; @@ -392,6 +422,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem break; case DisarmAttackEvent disarm: var disarmTarget = GetEntity(disarm.Target); + update = disarmTarget == null ? UpdateNextAttack.Both : + IsMob(disarmTarget.Value) ? UpdateNextAttack.Mob : UpdateNextAttack.NonMob; // WD if (!Blocker.CanAttack(user, disarmTarget, (weaponUid, weapon), true)) return false; @@ -405,23 +437,52 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // Windup time checked elsewhere. var fireRate = TimeSpan.FromSeconds(1f / GetAttackRate(weaponUid, user, weapon)); - if (attack is LightAttackEvent _) - { - fireRate *= 0.8f; - } - var swings = 0; - // TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly. - if (weapon.NextAttack < curTime) - weapon.NextAttack = curTime; - - while (weapon.NextAttack <= curTime) + // WD EDIT START + switch(update) { - weapon.NextAttack += fireRate; - swings++; + case UpdateNextAttack.Mob: + if (weapon.NextMobAttack > curTime) + return false; + break; + case UpdateNextAttack.NonMob: + if (weapon.NextAttack > curTime) + return false; + break; + default: + if (weapon.NextAttack > curTime || weapon.NextMobAttack > curTime) + return false; + break; } + if (update != UpdateNextAttack.Mob) + { + // TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly. + if (weapon.NextAttack < curTime) + weapon.NextAttack = curTime; + + while (weapon.NextAttack <= curTime) + { + weapon.NextAttack += fireRate; + swings++; + } + } + + if (update != UpdateNextAttack.NonMob) + { + if (weapon.NextMobAttack < curTime) + weapon.NextMobAttack = curTime; + + while (weapon.NextMobAttack <= curTime) + { + weapon.NextMobAttack += fireRate; + if (update == UpdateNextAttack.Mob) + swings++; + } + } + // WD EDIT END + Dirty(weaponUid, weapon); // Do this AFTER attack so it doesn't spam every tick @@ -452,7 +513,10 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem ? weapon.MissAnimation : weapon.Animation; if (miss) + { weapon.NextAttack -= fireRate / 2f; + weapon.NextMobAttack -= fireRate / 2f; + } // WD EDIT END break; case DisarmAttackEvent disarm: @@ -520,13 +584,6 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}"); - // WD START - var blockEvent = new MeleeBlockAttemptEvent(user); - RaiseLocalEvent(target.Value, ref blockEvent); - if (blockEvent.Blocked) - return; - // WD END - // Raise event before doing damage so we can cancel damage if the event is handled var hitEvent = new MeleeHitEvent(new List { target.Value }, user, meleeUid, damage, null); RaiseLocalEvent(meleeUid, hitEvent); @@ -663,19 +720,6 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}"); - // WD START - foreach (var target in new List(targets)) - { - var blockEvent = new MeleeBlockAttemptEvent(user); - RaiseLocalEvent(target, ref blockEvent); - if (blockEvent.Blocked) - targets.Remove(target); - } - - if (targets.Count == 0) - return true; - // WD END - // Raise event before doing damage so we can cancel damage if the event is handled var hitEvent = new MeleeHitEvent(targets, user, meleeUid, damage, direction); RaiseLocalEvent(meleeUid, hitEvent); @@ -899,4 +943,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem Dirty(uid, meleeWeapon); } + + protected bool IsMob(EntityUid uid) + { + return HasComp(uid) || HasComp(uid) || HasComp(uid); + } } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 944aba9dd2..9e5ee2d8c3 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -120,11 +120,23 @@ public abstract partial class SharedGunSystem : EntitySystem if (!TryComp(uid, out var melee)) return; + // WD EDIT START + var dirty = false; if (melee.NextAttack > component.NextFire) { component.NextFire = melee.NextAttack; - Dirty(uid, component); + dirty = true; } + + if (melee.NextMobAttack > component.NextFire) + { + component.NextFire = melee.NextMobAttack; + dirty = true; + } + + if (dirty) + Dirty(uid, component); + // WD EDIT END } private void OnShootRequest(RequestShootEvent msg, EntitySessionEventArgs args) diff --git a/Content.Shared/_White/Blocking/BlockBlockerComponent.cs b/Content.Shared/_White/Blocking/BlockBlockerComponent.cs new file mode 100644 index 0000000000..7cb9e9c858 --- /dev/null +++ b/Content.Shared/_White/Blocking/BlockBlockerComponent.cs @@ -0,0 +1,8 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._White.Blocking; + +[RegisterComponent, NetworkedComponent] +public sealed partial class BlockBlockerComponent : Component +{ +} diff --git a/Content.Shared/_White/Blocking/MeleeBlockComponent.cs b/Content.Shared/_White/Blocking/MeleeBlockComponent.cs new file mode 100644 index 0000000000..0a3fc9f42f --- /dev/null +++ b/Content.Shared/_White/Blocking/MeleeBlockComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.Audio; + +namespace Content.Shared._White.Blocking; + +[RegisterComponent] +public sealed partial class MeleeBlockComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Delay = TimeSpan.FromSeconds(3.1); + + [DataField] + public SoundSpecifier BlockSound = new SoundPathSpecifier("/Audio/Weapons/block_metal1.ogg") + { + Params = AudioParams.Default.WithVariation(0.25f) + }; +} diff --git a/Content.Shared/_White/Blocking/MeleeBlockSystem.cs b/Content.Shared/_White/Blocking/MeleeBlockSystem.cs new file mode 100644 index 0000000000..d06048641c --- /dev/null +++ b/Content.Shared/_White/Blocking/MeleeBlockSystem.cs @@ -0,0 +1,97 @@ +using Content.Shared._White.BetrayalDagger; +using Content.Shared.Blocking; +using Content.Shared.Damage; +using Content.Shared.Damage.Systems; +using Content.Shared.Examine; +using Content.Shared.Hands.Components; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Popups; +using Content.Shared.StatusEffect; +using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Melee.Events; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Timing; + +namespace Content.Shared._White.Blocking; + +public sealed class MeleeBlockSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffect = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBlockAttempt, + after: new[] {typeof(BlockingSystem)}); + SubscribeLocalEvent(OnHit, + before: new[] {typeof(StaminaSystem), typeof(MeleeThrowOnHitSystem)}, + after: new[] {typeof(BackstabSystem)}); + SubscribeLocalEvent(OnExamine); + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + args.PushMarkup(Loc.GetString("melee-block-component-delay", ("delay", ent.Comp.Delay.TotalSeconds))); + } + + private void OnHit(Entity ent, ref MeleeHitEvent args) + { + if (!_timing.IsFirstTimePredicted) + return; + + if (!ent.Comp.CanBeBlocked || !args.IsHit || args.Handled) + return; + + if (args.Direction != null || args.HitEntities.Count != 1) // Heavy attacks are unblockable + return; + + var hitEntity = args.HitEntities[0]; + + if (hitEntity == args.User) + return; + + var ev = new MeleeBlockAttemptEvent(args.User, args.BaseDamage + args.BonusDamage); + RaiseLocalEvent(hitEntity, ev); + if (ev.Handled) + args.Handled = true; + } + + private void OnBlockAttempt(Entity ent, ref MeleeBlockAttemptEvent args) + { + if (args.Handled || HasComp(ent)) + return; + + if (!TryComp(ent, out StatusEffectsComponent? statusEffects)) + return; + + var uid = ent.Comp.ActiveHandEntity; + if (!TryComp(uid, out MeleeBlockComponent? block)) + return; + + if (TryComp(uid.Value, out ItemToggleComponent? toggle) && !toggle.Activated) + return; + + _audio.PlayPredicted(block.BlockSound, ent, args.Attacker); + _popupSystem.PopupPredicted(Loc.GetString("melee-block-event-blocked"), ent, args.Attacker); + _damageable.TryChangeDamage(uid.Value, args.Damage); + TryBlockBlocking(ent, block.Delay, true, statusEffects); + args.Handled = true; + } + + public bool TryBlockBlocking(EntityUid uid, TimeSpan time, bool refresh, + StatusEffectsComponent? status = null) + { + if (time <= TimeSpan.Zero) + return false; + + if (!Resolve(uid, ref status, false)) + return false; + + return _statusEffect.TryAddStatusEffect(uid, "RecentlyBlocked", time, refresh); + } +} diff --git a/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownComponent.cs b/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownComponent.cs new file mode 100644 index 0000000000..7f208f0f14 --- /dev/null +++ b/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._White.Item.DelayedKnockdown; + +[RegisterComponent, NetworkedComponent] +public sealed partial class DelayedKnockdownComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan KnockdownTime = TimeSpan.FromSeconds(5); + + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan KnockdownMoment = TimeSpan.MaxValue; +} diff --git a/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownOnHitComponent.cs b/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownOnHitComponent.cs new file mode 100644 index 0000000000..b7a32c5acb --- /dev/null +++ b/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownOnHitComponent.cs @@ -0,0 +1,17 @@ +namespace Content.Shared._White.Item.DelayedKnockdown; + +[RegisterComponent] +public sealed partial class DelayedKnockdownOnHitComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Delay = TimeSpan.FromSeconds(2); + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan KnockdownTime = TimeSpan.FromSeconds(5); + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan StutterTime = TimeSpan.FromSeconds(16); + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan JitterTime = TimeSpan.FromSeconds(40); +} diff --git a/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownOnHitSystem.cs b/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownOnHitSystem.cs new file mode 100644 index 0000000000..bee3baa40e --- /dev/null +++ b/Content.Shared/_White/Item/DelayedKnockdown/DelayedKnockdownOnHitSystem.cs @@ -0,0 +1,66 @@ +using Content.Shared.Damage.Events; +using Content.Shared.Jittering; +using Content.Shared.Speech.EntitySystems; +using Content.Shared.Standing; +using Content.Shared.StatusEffect; +using Content.Shared.Stunnable; +using Robust.Shared.Timing; + +namespace Content.Shared._White.Item.DelayedKnockdown; + +public sealed class DelayedKnockdownOnHitSystem : EntitySystem +{ + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly SharedJitteringSystem _jitter = default!; + [Dependency] private readonly SharedStutteringSystem _stutter = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHit); + } + + private void OnHit(Entity ent, ref StaminaMeleeHitEvent args) + { + var jitterTime = ent.Comp.JitterTime; + var stutterTime = ent.Comp.StutterTime; + foreach (var (uid, _) in args.HitList) + { + if (!TryComp(uid, out StatusEffectsComponent? statusEffects)) + continue; + + if (!TryComp(uid, out StandingStateComponent? standingState) || !standingState.CanLieDown) + continue; + + if (jitterTime > TimeSpan.Zero) + _jitter.DoJitter(uid, jitterTime, true, status: statusEffects); + if (stutterTime > TimeSpan.Zero) + _stutter.DoStutter(uid, stutterTime, true, statusEffects); + if (HasComp(uid)) + continue; + var delayedKnockdown = EnsureComp(uid); + delayedKnockdown.KnockdownTime = TimeSpan.FromSeconds(Math.Max(ent.Comp.KnockdownTime.TotalSeconds, + delayedKnockdown.KnockdownTime.TotalSeconds)); + var knockdownMoment = _timing.CurTime + ent.Comp.Delay; + if (knockdownMoment < delayedKnockdown.KnockdownMoment) + delayedKnockdown.KnockdownMoment = knockdownMoment; + } + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var delayedKnockdown)) + { + if (delayedKnockdown.KnockdownMoment > _timing.CurTime) + continue; + + _stun.TryKnockdown(uid, delayedKnockdown.KnockdownTime, true); + RemCompDeferred(uid); + } + } +} diff --git a/Content.Shared/_White/Item/Telebaton/TelebatonComponent.cs b/Content.Shared/_White/Item/Telebaton/TelebatonComponent.cs new file mode 100644 index 0000000000..75051e4cfe --- /dev/null +++ b/Content.Shared/_White/Item/Telebaton/TelebatonComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._White.Item.Telebaton; + +[RegisterComponent] +public sealed partial class TelebatonComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan KnockdownTime = TimeSpan.FromSeconds(1.5f); +} diff --git a/Content.Shared/_White/Item/Telebaton/TelebatonSystem.cs b/Content.Shared/_White/Item/Telebaton/TelebatonSystem.cs new file mode 100644 index 0000000000..f5b88e2bca --- /dev/null +++ b/Content.Shared/_White/Item/Telebaton/TelebatonSystem.cs @@ -0,0 +1,56 @@ +using Content.Shared.Damage.Events; +using Content.Shared.Examine; +using Content.Shared.Item; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Stunnable; + +namespace Content.Shared._White.Item.Telebaton; + +public sealed class TelebatonSystem : EntitySystem +{ + [Dependency] private readonly SharedItemSystem _item = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnStaminaHitAttempt); + SubscribeLocalEvent(ToggleDone); + SubscribeLocalEvent(OnHit); + } + + private void OnHit(Entity ent, ref StaminaMeleeHitEvent args) + { + var time = ent.Comp.KnockdownTime; + if (time <= TimeSpan.Zero) + return; + + foreach (var (uid, _) in args.HitList) + { + _stun.TryKnockdown(uid, time, true); + } + } + + private void OnStaminaHitAttempt(Entity entity, ref StaminaDamageOnHitAttemptEvent args) + { + if (!_itemToggle.IsActivated(entity.Owner)) + args.Cancelled = true; + } + + private void OnExamined(Entity entity, ref ExaminedEvent args) + { + var onMsg = _itemToggle.IsActivated(entity.Owner) + ? Loc.GetString("comp-telebaton-examined-on") + : Loc.GetString("comp-telebaton-examined-off"); + args.PushMarkup(onMsg); + } + + private void ToggleDone(Entity entity, ref ItemToggledEvent args) + { + _item.SetHeldPrefix(entity.Owner, args.Activated ? "on" : "off"); + } +} diff --git a/Resources/Audio/White/Weapons/woodhit.ogg b/Resources/Audio/White/Weapons/woodhit.ogg new file mode 100644 index 0000000000..04ff37f3c7 Binary files /dev/null and b/Resources/Audio/White/Weapons/woodhit.ogg differ diff --git a/Resources/Locale/ru-RU/_white/object/telebaton.ftl b/Resources/Locale/ru-RU/_white/object/telebaton.ftl new file mode 100644 index 0000000000..f753a2c6ba --- /dev/null +++ b/Resources/Locale/ru-RU/_white/object/telebaton.ftl @@ -0,0 +1,5 @@ +ent-Telebaton = телескопическая дубинка + .desc = Компактное, но надежное оружие личной обороны. В сложенном состоянии может быть скрыто. + +comp-telebaton-examined-on = Дубинка в боевом положении. +comp-telebaton-examined-off = Дубинка сложена. diff --git a/Resources/Locale/ru-RU/_white/white-shit.ftl b/Resources/Locale/ru-RU/_white/white-shit.ftl index 2ff64f81d9..5ca1b1e9be 100644 --- a/Resources/Locale/ru-RU/_white/white-shit.ftl +++ b/Resources/Locale/ru-RU/_white/white-shit.ftl @@ -17,3 +17,8 @@ carry-start = { $carrier } пытается взять вас на руки! alerts-knockdown-name = Лежу alerts-knockdown-desc = Не могу встать. + +melee-block-event-blocked = заблокировал! +alerts-blocked-name = Атака заблокирована +alerts-blocked-desc = Невозможно блокировать некоторое время. +melee-block-component-delay = Может блокировать атаку ближнего боя каждые {$delay} секунд. diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index 85789a002f..d9b6d780fd 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -20,6 +20,7 @@ - alertType: Corporeal - alertType: Stun - alertType: Knockdown + - alertType: RecentlyBlocked - category: Breathing # Vox gang not calling this oxygen - category: Pressure - alertType: Bleed @@ -494,6 +495,13 @@ name: alerts-knockdown-name description: alerts-knockdown-desc +# WD-EDIT +- type: alert + id: RecentlyBlocked + icons: [ /Textures/Objects/Weapons/Melee/shields.rsi/buckler-icon.png ] + name: alerts-blocked-name + description: alerts-blocked-desc + # WD-EDIT - type: alert id: Bleeding diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml index bb1018313b..4fdd5f9fd0 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml @@ -66,6 +66,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- name: StationCharter #- name: TelescopicBaton - type: entity @@ -77,6 +78,7 @@ contents: - id: BoxSurvivalEngineering - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -88,6 +90,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -99,6 +102,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -110,6 +114,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -121,6 +126,7 @@ contents: - id: BoxSurvivalMedical - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -132,6 +138,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml index b6fe8cbaa4..0b7bd825a2 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/duffelbag.yml @@ -75,6 +75,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- name: StationCharter #- name: TelescopicBaton - type: entity @@ -86,6 +87,7 @@ contents: - id: BoxSurvivalEngineering - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -97,6 +99,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -108,6 +111,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -119,6 +123,7 @@ contents: - id: BoxSurvivalMedical - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -130,6 +135,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml index b32003bf8a..ecc79c5060 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/satchel.yml @@ -103,6 +103,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- name: StationCharter #- name: TelescopicBaton - type: entity @@ -114,6 +115,7 @@ contents: - id: BoxSurvivalEngineering - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -125,6 +127,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -136,6 +139,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -147,6 +151,7 @@ contents: - id: BoxSurvivalMedical - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -158,6 +163,7 @@ contents: - id: BoxSurvival - id: Flash + - id: Telebaton #- id: TelescopicBaton - type: entity @@ -180,6 +186,7 @@ contents: - id: BoxSurvivalSecurity - id: Flash + - id: Telebaton - type: entity noSpawn: true diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml index aef0c5a8f5..83d03da727 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml @@ -23,6 +23,7 @@ - type: Item size: Small - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml index bd822396e7..a341a84443 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml @@ -16,6 +16,7 @@ - type: Sprite state: icon - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml index 1379a25c89..bf7d8a0a1d 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml @@ -48,6 +48,7 @@ - type: Spillable solution: drink - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml index 6dcf556d04..aa041cb99e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml @@ -62,6 +62,7 @@ - type: Spillable solution: drink - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml index a3f5bf1903..9b110b1def 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml @@ -20,6 +20,7 @@ maxTransferAmount: 5 - type: Drink - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml index 86cf0afa8e..634f347c6f 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml @@ -62,6 +62,7 @@ acts: [ "Destruction" ] # packet contents can be splashed when open - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml index 6b96f3bcb3..8601dfbad5 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml @@ -31,6 +31,7 @@ solution: food # soup weapon! - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index c8d5c37c43..8c05823005 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -29,6 +29,7 @@ - type: UseDelay delay: 1.0 - type: MeleeWeapon + canBeBlocked: false wideAnimationRotation: 180 soundHit: collection: ToySqueak @@ -992,6 +993,7 @@ sprite: Objects/Fun/toys.rsi state: foamblade - type: MeleeWeapon + canBeBlocked: false attackRate: 1.5 angle: 0 animation: WeaponArcThrust @@ -1325,6 +1327,7 @@ sprite: Objects/Weapons/Melee/cutlass.rsi state: foam_icon - type: MeleeWeapon + canBeBlocked: false attackRate: 1.5 range: 2.0 angle: 0 @@ -1383,6 +1386,7 @@ - type: DisarmMalus malus: 0 - type: MeleeWeapon + canBeBlocked: false soundHit: collection: RubberHammer params: @@ -1424,6 +1428,7 @@ params: variation: 0.125 - type: MeleeWeapon + canBeBlocked: false soundHit: collection: Parp params: diff --git a/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml b/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml index 08f7ae71e3..e5ed353cd6 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml @@ -17,6 +17,7 @@ tags: - Handcuffs - type: MeleeWeapon + canBeBlocked: false wideAnimationRotation: 90 resetOnHandSelected: false animation: WeaponArcDisarm diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index ed85e4500e..3648e41db6 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -30,6 +30,7 @@ Slash: 1 Piercing: 1 Heat: 1 + - type: MeleeBlock - type: Damageable damageContainer: Shield - type: Destructible @@ -412,6 +413,7 @@ description: Exotic energy shield, when folded, can even fit in your pocket. components: - type: ItemToggle + predictable: false # WD EDIT soundActivate: path: /Audio/Weapons/ebladeon.ogg soundDeactivate: @@ -420,10 +422,9 @@ path: /Audio/Machines/button.ogg params: variation: 0.250 - # Causes sound issues in combination with Rechargeable - #- type: ItemToggleActiveSound - # activeSound: - # path: /Audio/Weapons/ebladehum.ogg + - type: ItemToggleActiveSound + activeSound: + path: /Audio/Weapons/ebladehum.ogg - type: ItemToggleSize activatedSize: Huge - type: ItemToggleDisarmMalus @@ -480,6 +481,7 @@ flatReductions: Heat: 1 Piercing: 1 + - type: MeleeBlock - type: Appearance - type: Damageable damageContainer: Shield diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index 3239a001da..02ded6b856 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -250,6 +250,7 @@ - type: Spillable solution: absorbed - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml b/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml index 2656e80943..040ceb6b30 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml @@ -78,6 +78,7 @@ - type: Label originalName: jug - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml index acfb65aa54..3c98d38ff0 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml @@ -51,6 +51,7 @@ - type: Spillable solution: drink - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml index c5de88d690..20ca7e584f 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml @@ -57,6 +57,7 @@ shape: - 0,0,0,0 - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml index 4d50c7389c..a70017cccd 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml @@ -16,6 +16,7 @@ - type: Item sprite: Objects/Specific/Chemistry/beaker.rsi - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: @@ -108,6 +109,7 @@ - type: Item sprite: Objects/Specific/Chemistry/beaker.rsi - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Tools/bucket.yml b/Resources/Prototypes/Entities/Objects/Tools/bucket.yml index 496fd231b8..308ec1f986 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/bucket.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/bucket.yml @@ -36,6 +36,7 @@ - key: enum.TransferAmountUiKey.Key type: TransferAmountBoundUserInterface - type: MeleeWeapon + canBeBlocked: false soundNoDamage: path: "/Audio/Effects/Fluids/splat.ogg" damage: diff --git a/Resources/Prototypes/Entities/Objects/Tools/lighters.yml b/Resources/Prototypes/Entities/Objects/Tools/lighters.yml index 8ffd46f30a..a786e56849 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/lighters.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/lighters.yml @@ -74,6 +74,7 @@ Quantity: 8 maxVol: 8 #uses less fuel than a welder, so this isnt as bad as it looks - type: MeleeWeapon + canBeBlocked: false wideAnimationRotation: 180 damage: types: @@ -201,6 +202,7 @@ - state: zippo-inhand-right-flame shader: unshaded - type: MeleeWeapon + canBeBlocked: false wideAnimationRotation: 180 damage: types: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/daggers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/daggers.yml index 366b79cd15..9828b66e0a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/daggers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/daggers.yml @@ -27,6 +27,8 @@ critChance: 50 critMultiplier: 2 isBloodDagger: true + - type: MeleeBlock + delay: 12.1 - type: entity name: предательский нож @@ -61,3 +63,5 @@ - type: Backstab - type: Blink blinkRate: 0.33 + - type: MeleeBlock + delay: 12.1 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index 11d907f168..8630d9df36 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -84,6 +84,12 @@ - Energy - type: IgnitionSource temperature: 700 + - type: MeleeBlock + delay: 6.1 + blockSound: + path: /Audio/Weapons/eblade1.ogg + params: + variation: 0.250 - type: entity name: energy sword @@ -199,6 +205,8 @@ - id: EnergyDagger sound: path: /Audio/Effects/unwrap.ogg + - type: MeleeBlock + delay: 12.1 - type: entity name: energy cutlass @@ -267,6 +275,8 @@ deconstructionTarget: null graph: EnergyDoubleSwordGraph node: desword + - type: MeleeBlock + delay: 3.1 - type: entity parent: EnergySwordBase diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml index d982243dfe..958cd0c854 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml @@ -42,7 +42,7 @@ animation: WeaponArcThrust soundHit: path: /Audio/Weapons/bladeslice.ogg - range: 2 # Spears are long + range: 1.8 # Spears are long - type: DamageOtherOnHit damage: types: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml index cf3bf84414..1e7e366049 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml @@ -1,14 +1,11 @@ - type: entity name: stun prod parent: BaseItem - id: Stunprod + id: StunprodBase description: A stun prod for illegal incapacitation. + abstract: true + noSpawn: true components: - - type: Sprite - sprite: Objects/Weapons/Melee/stunprod.rsi - layers: - - state: stunprod_nocell - map: [ "enum.ToggleVisuals.Layer" ] - type: ItemToggle soundActivate: collection: sparks @@ -27,41 +24,36 @@ types: Blunt: 0 - type: Stunprod + - type: DelayedKnockdownOnHit + delay: 4 - type: MeleeWeapon + attackRate: 0.4 canHeavyAttack: false + equipCooldown: 1 wideAnimationRotation: -135 damage: types: - Blunt: 9 + Blunt: 12 angle: 0 animation: WeaponArcThrust - type: StaminaDamageOnHit - damage: 40 + damage: 60 sound: /Audio/Weapons/egloves.ogg - type: StaminaDamageOnCollide - damage: 40 + damage: 30 sound: /Audio/Weapons/egloves.ogg - type: UseDelay - type: Item heldPrefix: off size: Normal - - type: Clothing - sprite: Objects/Weapons/Melee/stunprod.rsi - quickEquip: false - slots: - - back + shape: + - 0,0,2,0 + storedRotation: 44 - type: DisarmMalus malus: 0.225 - type: Appearance - - type: GenericVisualizer - visuals: - enum.ToggleVisuals.Toggled: - enum.ToggleVisuals.Layer: - nocell: {state: stunprod_nocell} - True: {state: stunprod_on} - False: {state: stunprod_off} - type: StaticPrice - price: 100 + price: 40 - type: PowerCellSlot cellSlotId: cell_slot - type: ItemSlots @@ -71,6 +63,32 @@ - type: ContainerContainer containers: cell_slot: !type:ContainerSlot {} + +- type: entity + name: stun prod + parent: StunprodBase + id: Stunprod + description: A stun prod for illegal incapacitation. + components: + - type: Sprite + sprite: Objects/Weapons/Melee/stunprod.rsi + layers: + - state: stunprod_nocell + map: [ "enum.ToggleVisuals.Layer" ] + - type: Item + sprite: Objects/Weapons/Melee/stunprod.rsi + - type: Clothing + sprite: Objects/Weapons/Melee/stunprod.rsi + quickEquip: false + slots: + - back + - type: GenericVisualizer + visuals: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + nocell: {state: stunprod_nocell} + True: {state: stunprod_on} + False: {state: stunprod_off} - type: Construction deconstructionTarget: cuffs graph: StunprodGraph diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 46ba8b7b20..9ec4e93469 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -31,6 +31,8 @@ tags: - CaptainSabre - type: DisarmMalus + - type: MeleeBlock + delay: 6.1 - type: entity name: katana @@ -94,6 +96,8 @@ - Belt - SuitStorage - type: Reflect + - type: MeleeBlock + delay: 6.1 - type: entity name: machete diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index 5dcf2c872e..319feafb76 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -10,7 +10,7 @@ - state: stunbaton_off map: [ "enum.ToggleVisuals.Layer" ] - type: Stunbaton - energyPerUse: 50 + energyPerUse: 100 - type: ItemToggle predictable: false soundActivate: @@ -30,19 +30,20 @@ types: Blunt: 0 - type: MeleeWeapon - attackRate: 1.25 + attackRate: 0.4 canHeavyAttack: false + equipCooldown: 1 wideAnimationRotation: -135 damage: types: - Blunt: 7 + Blunt: 12 bluntStaminaDamageFactor: 2.0 angle: 60 - type: StaminaDamageOnHit - damage: 40 + damage: 60 sound: /Audio/Weapons/egloves.ogg - type: StaminaDamageOnCollide - damage: 40 + damage: 30 sound: /Audio/Weapons/egloves.ogg - type: ExaminableBattery - type: Battery @@ -87,7 +88,8 @@ - type: GuideHelp guides: - Security - - type: StunLock # Wd EDIT + - type: StunLock # WD EDIT + - type: DelayedKnockdownOnHit # WD EDIT - type: entity name: truncheon @@ -142,6 +144,7 @@ maxCharges: 7 charges: 7 - type: MeleeWeapon + canBeBlocked: false canHeavyAttack: false wideAnimationRotation: 180 damage: diff --git a/Resources/Prototypes/_White/Entities/Cult/Items/hands.yml b/Resources/Prototypes/_White/Entities/Cult/Items/hands.yml index d21ac1879d..c98cd99bcf 100644 --- a/Resources/Prototypes/_White/Entities/Cult/Items/hands.yml +++ b/Resources/Prototypes/_White/Entities/Cult/Items/hands.yml @@ -9,6 +9,7 @@ - type: Item size: Ginormous - type: MeleeWeapon + canBeBlocked: false canHeavyAttack: false canMiss: false attackRate: 2 diff --git a/Resources/Prototypes/_White/Entities/Cult/Weapons/blood_spear.yml b/Resources/Prototypes/_White/Entities/Cult/Weapons/blood_spear.yml index d617be4e60..64acfccdf8 100644 --- a/Resources/Prototypes/_White/Entities/Cult/Weapons/blood_spear.yml +++ b/Resources/Prototypes/_White/Entities/Cult/Weapons/blood_spear.yml @@ -35,14 +35,17 @@ animation: WeaponArcThrust soundHit: path: /Audio/Weapons/bladeslice.ogg - range: 2 + range: 1.8 - type: DamageOtherOnHit damage: types: Piercing: 40 - type: Item sprite: White/Cult/Entities/blood_spear.rsi - size: Ginormous + storedRotation: 44 + size: Huge + shape: + - 0,0,5,0 - type: Clothing slots: - back diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/chaplain_weapons.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/chaplain_weapons.yml index 8242662c74..ab805ea879 100644 --- a/Resources/Prototypes/_White/Entities/Objects/Weapons/chaplain_weapons.yml +++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/chaplain_weapons.yml @@ -265,7 +265,7 @@ - type: MeleeWeapon soundHit: path: /Audio/White/Items/hit/chainhit.ogg - range: 2.5 + range: 2.2 damage: types: Blunt: 18 @@ -309,6 +309,7 @@ - type: UseDelay - type: DisarmMalus - type: MeleeBlock + delay: 6.1 - type: HolyWeapon - type: entity @@ -348,7 +349,6 @@ animation: WeaponArcThrust soundHit: path: /Audio/Weapons/bladeslice.ogg - range: 2 - type: DamageOtherOnHit damage: types: diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/experimental_stunbaton.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/experimental_stunbaton.yml index d078220213..aeb94eb17a 100644 --- a/Resources/Prototypes/_White/Entities/Objects/Weapons/experimental_stunbaton.yml +++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/experimental_stunbaton.yml @@ -9,16 +9,6 @@ - type: Sprite sprite: White/Objects/Weapons/experimental_stunbaton.rsi - type: Stunbaton - - type: MeleeWeapon - damage: - types: - Blunt: 10 - bluntStaminaDamageFactor: 2.0 - angle: 70 - - type: StaminaDamageOnHit - damage: 45 - - type: StaminaDamageOnCollide - damage: 45 - type: Battery maxCharge: 2000 startingCharge: 2000 diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/snatcherprod.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/snatcherprod.yml index a9781e0310..8198801bdc 100644 --- a/Resources/Prototypes/_White/Entities/Objects/Weapons/snatcherprod.yml +++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/snatcherprod.yml @@ -1,5 +1,5 @@ - type: entity - parent: BaseItem + parent: StunprodBase id: Snatcherprod name: хваталка description: Искрится жаждой воровства и коварства. @@ -9,48 +9,8 @@ layers: - state: snatcherprod_nocell map: [ "enum.ToggleVisuals.Layer" ] - - type: ItemToggle - soundActivate: - collection: sparks - params: - variation: 0.250 - soundDeactivate: - collection: sparks - params: - variation: 0.250 - soundFailToActivate: - path: /Audio/Machines/button.ogg - params: - variation: 0.250 - - type: ItemToggleMeleeWeapon - activatedDamage: - types: - Blunt: 0 - - type: MeleeWeapon - canHeavyAttack: false - wideAnimationRotation: -135 - damage: - types: - Blunt: 9 - angle: 0 - animation: WeaponArcThrust - - type: StaminaDamageOnHit - damage: 40 - sound: /Audio/Weapons/egloves.ogg - - type: StaminaDamageOnCollide - damage: 40 - sound: /Audio/Weapons/egloves.ogg - - type: UseDelay - type: Item - heldPrefix: off - size: Normal - - type: Clothing - quickEquip: false - slots: - - back - - type: DisarmMalus - malus: 0.225 - - type: Appearance + sprite: White/Objects/Weapons/snatcherprod.rsi - type: GenericVisualizer visuals: enum.ToggleVisuals.Toggled: @@ -58,19 +18,7 @@ nocell: {state: snatcherprod_nocell} True: {state: snatcherprod_on} False: {state: snatcherprod_off} - - type: StaticPrice - price: 100 - - type: PowerCellSlot - cellSlotId: cell_slot - - type: ItemSlots - slots: - cell_slot: - name: power-cell-slot-component-slot-name-default - - type: ContainerContainer - containers: - cell_slot: !type:ContainerSlot {} - type: Snatcherprod - - type: Stunprod - type: Construction deconstructionTarget: cuffs graph: StunprodGraph @@ -86,7 +34,10 @@ sprite: White/Objects/Weapons/prod.rsi state: prod_unfinished - type: Item - size: Normal + size: Small + shape: + - 0,0,1,0 + storedRotation: 44 - type: Construction deconstructionTarget: cuffs graph: StunprodGraph diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/telebaton.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/telebaton.yml new file mode 100644 index 0000000000..c6ac4558f4 --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/telebaton.yml @@ -0,0 +1,61 @@ +- type: entity + id: Telebaton + name: telescopic baton + parent: BaseItem + description: A compact yet robust personal defense weapon. Can be concealed when folded. + components: + - type: ItemToggle + soundActivate: + path: /Audio/Weapons/telescopicon.ogg + params: + volume: -5 + soundDeactivate: + path: /Audio/Weapons/telescopicoff.ogg + params: + volume: -5 + - type: ItemToggleSize + activatedSize: Large + activatedShape: + - 0, 0, 3, 0 + - type: DisarmMalus + malus: 0.225 + - type: Sprite + sprite: White/Objects/Weapons/telebaton.rsi + layers: + - state: telebaton_off + map: [ "enum.ToggleVisuals.Layer" ] + - type: ItemToggleMeleeWeapon + activatedDamage: + types: + Blunt: 12 + deactivatedSecret: true + - type: MeleeWeapon + canHeavyAttack: false + equipCooldown: 1 + attackRate: 0.25 + wideAnimationRotation: -135 + damage: + types: + Blunt: 0 + bluntStaminaDamageFactor: 0.0 # so blunt doesn't deal stamina damage at all + - type: StaminaDamageOnHit + damage: 55 + sound: /Audio/White/Weapons/woodhit.ogg + - type: UseDelay + - type: Item + heldPrefix: off + sprite: White/Objects/Weapons/telebaton.rsi + size: Small + storedRotation: 44 + shape: + - 0, 0, 1, 0 + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + True: {state: telebaton_on} + False: {state: telebaton_off} + - type: StaticPrice + price: 150 + - type: Telebaton diff --git a/Resources/Prototypes/_White/Fluff/fluff.yml b/Resources/Prototypes/_White/Fluff/fluff.yml index afe2d7ea9e..a9ff9f4fea 100644 --- a/Resources/Prototypes/_White/Fluff/fluff.yml +++ b/Resources/Prototypes/_White/Fluff/fluff.yml @@ -395,6 +395,7 @@ - type: UseDelay delay: 2 - type: MeleeWeapon + canBeBlocked: false soundHit: path: /Audio/White/Fluff/Omntns/gavel.ogg damage: @@ -726,6 +727,7 @@ Quantity: 1 maxVol: 1 - type: MeleeWeapon + canBeBlocked: false damage: types: Blunt: 0 #this feels hacky, but is needed for burn damage while active (i think) diff --git a/Resources/Prototypes/_White/Fluff/fluff1.yml b/Resources/Prototypes/_White/Fluff/fluff1.yml index e9e45dece2..1a489f503b 100644 --- a/Resources/Prototypes/_White/Fluff/fluff1.yml +++ b/Resources/Prototypes/_White/Fluff/fluff1.yml @@ -20,5 +20,6 @@ sound: path: /Textures/White/Fluff/Forg/ryan.ogg - type: MeleeWeapon + canBeBlocked: false soundHit: path: /Audio/Items/Toys/weh.ogg diff --git a/Resources/Prototypes/_White/Wizard/magic_items.yml b/Resources/Prototypes/_White/Wizard/magic_items.yml index 52953f32ef..5b6b029837 100644 --- a/Resources/Prototypes/_White/Wizard/magic_items.yml +++ b/Resources/Prototypes/_White/Wizard/magic_items.yml @@ -24,6 +24,8 @@ storedRotation: 44 shape: - 0, 0, 3, 0 + - type: MeleeBlock + delay: 6.1 - type: entity name: клинок заклинаний @@ -55,6 +57,8 @@ inHandsOnly: true closeOnHandDeselect: true - type: SpellBlade + - type: MeleeBlock + delay: 6.1 - type: entity name: мьёльнир diff --git a/Resources/Prototypes/_White/Wizard/spellbook_catalog.yml b/Resources/Prototypes/_White/Wizard/spellbook_catalog.yml index facb4cd742..590c75e38d 100644 --- a/Resources/Prototypes/_White/Wizard/spellbook_catalog.yml +++ b/Resources/Prototypes/_White/Wizard/spellbook_catalog.yml @@ -289,7 +289,7 @@ description: spellbook-hfrequency-desc productEntity: HighFrequencyBlade cost: - SpellPoint: 1 + SpellPoint: 2 categories: - MagicItems conditions: @@ -302,7 +302,7 @@ description: spellbook-spellblade-desc productEntity: SpellBlade cost: - SpellPoint: 1 + SpellPoint: 2 categories: - MagicItems conditions: diff --git a/Resources/Prototypes/status_effects.yml b/Resources/Prototypes/status_effects.yml index 710c82a69d..746912c122 100644 --- a/Resources/Prototypes/status_effects.yml +++ b/Resources/Prototypes/status_effects.yml @@ -82,5 +82,10 @@ id: Incorporeal alwaysAllowed: true +- type: statusEffect + id: RecentlyBlocked + alwaysAllowed: true + alert: RecentlyBlocked + - type: statusEffect id: BloodLoss diff --git a/Resources/Textures/White/Objects/Weapons/telebaton.rsi/meta.json b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/meta.json new file mode 100644 index 0000000000..01119966d6 --- /dev/null +++ b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4397d63a55dac7d0536eb9bcc0a0f68634858c50", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "telebaton_off" + }, + { + "name": "telebaton_on" + }, + { + "name": "on-inhand-left", + "directions": 4 + }, + { + "name": "on-inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/White/Objects/Weapons/telebaton.rsi/on-inhand-left.png b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/on-inhand-left.png new file mode 100644 index 0000000000..6f31eafd0f Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/on-inhand-left.png differ diff --git a/Resources/Textures/White/Objects/Weapons/telebaton.rsi/on-inhand-right.png b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/on-inhand-right.png new file mode 100644 index 0000000000..3ff76ef4da Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/on-inhand-right.png differ diff --git a/Resources/Textures/White/Objects/Weapons/telebaton.rsi/telebaton_off.png b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/telebaton_off.png new file mode 100644 index 0000000000..94fa196825 Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/telebaton_off.png differ diff --git a/Resources/Textures/White/Objects/Weapons/telebaton.rsi/telebaton_on.png b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/telebaton_on.png new file mode 100644 index 0000000000..979d70e28a Binary files /dev/null and b/Resources/Textures/White/Objects/Weapons/telebaton.rsi/telebaton_on.png differ