Better melee combat (#542)
* - add: NextMobAttack, EquipCooldown. * - fix: Some combat fixes. * - add: Telebaton. * - add: Stun baton rework. * - tweak: Reduce melee range. * - add: Rework melee block system. * - add: ExaminedEvent.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
|
||||
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack,
|
||||
after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT
|
||||
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
|
||||
}
|
||||
|
||||
@@ -68,6 +70,9 @@ public sealed class HypospraySystem : SharedHypospraySystem
|
||||
|
||||
public void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
|
||||
{
|
||||
if (args.Handled) // WD
|
||||
return;
|
||||
|
||||
if (!args.HitEntities.Any())
|
||||
return;
|
||||
|
||||
|
||||
@@ -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<SolutionInjectOnProjectileHitComponent, ProjectileHitEvent>(HandleProjectileHit);
|
||||
SubscribeLocalEvent<SolutionInjectOnEmbedComponent, EmbedEvent>(HandleEmbed);
|
||||
SubscribeLocalEvent<MeleeChemicalInjectorComponent, MeleeHitEvent>(HandleMeleeHit);
|
||||
SubscribeLocalEvent<MeleeChemicalInjectorComponent, MeleeHitEvent>(HandleMeleeHit,
|
||||
after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT
|
||||
}
|
||||
|
||||
private void HandleProjectileHit(Entity<SolutionInjectOnProjectileHitComponent> entity, ref ProjectileHitEvent args)
|
||||
@@ -43,6 +45,8 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
|
||||
|
||||
private void HandleMeleeHit(Entity<MeleeChemicalInjectorComponent> 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)
|
||||
|
||||
@@ -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<SpillableComponent, LandEvent>(SpillOnLand);
|
||||
// Openable handles the event if it's closed
|
||||
SubscribeLocalEvent<SpillableComponent, MeleeHitEvent>(SplashOnMeleeHit, after: [typeof(OpenableSystem)]);
|
||||
SubscribeLocalEvent<SpillableComponent, MeleeHitEvent>(
|
||||
SplashOnMeleeHit, before: new[] {typeof(MeleeBlockSystem)}, after:[typeof(OpenableSystem)]); // WD EDIT
|
||||
SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<SpillableComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
SubscribeLocalEvent<SpillableComponent, SolutionContainerOverflowEvent>(OnOverflow);
|
||||
|
||||
@@ -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<DnaComponent, MapInitEvent>(OnDNAInit);
|
||||
|
||||
SubscribeLocalEvent<DnaComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit,
|
||||
after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT
|
||||
SubscribeLocalEvent<ForensicsComponent, GotRehydratedEvent>(OnRehydrated);
|
||||
SubscribeLocalEvent<CleansForensicsComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) });
|
||||
SubscribeLocalEvent<ForensicsComponent, CleanForensicsDoAfterEvent>(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))
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared._White.Blocking;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
@@ -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<ZombieComponent, EmoteEvent>(OnEmote, before:
|
||||
new[] { typeof(VocalSystem), typeof(BodyEmotesSystem) });
|
||||
|
||||
SubscribeLocalEvent<ZombieComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<ZombieComponent, MeleeHitEvent>(OnMeleeHit,
|
||||
after: new[] {typeof(MeleeBlockSystem)}); // WD EDIT
|
||||
SubscribeLocalEvent<ZombieComponent, MobStateChangedEvent>(OnMobState);
|
||||
SubscribeLocalEvent<ZombieComponent, CloningEvent>(OnZombieCloning);
|
||||
SubscribeLocalEvent<ZombieComponent, TryingToSleepEvent>(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<ZombieComponent>(args.User, out _))
|
||||
return;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public sealed class ReturnItemOnThrowSystem : EntitySystem
|
||||
if (!HasComp<MobStateComponent>(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);
|
||||
}
|
||||
|
||||
@@ -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<ConstructShellComponent, ComponentInit>(OnShellInit);
|
||||
SubscribeLocalEvent<ConstructShellComponent, ComponentRemove>(OnShellRemove);
|
||||
SubscribeLocalEvent<ConstructShellComponent, ConstructFormSelectedEvent>(OnShellSelected);
|
||||
SubscribeLocalEvent<ConstructComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<ConstructComponent, MeleeHitEvent>(OnMeleeHit, before: new []{typeof(MeleeBlockSystem)});
|
||||
}
|
||||
|
||||
private void OnMeleeHit(Entity<ConstructComponent> ent, ref MeleeHitEvent args)
|
||||
|
||||
@@ -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<CritComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<CritComponent, MeleeHitEvent>(HandleHit);
|
||||
SubscribeLocalEvent<CritComponent, MeleeHitEvent>(HandleHit, before: new [] {typeof(MeleeBlockSystem)});
|
||||
SubscribeLocalEvent<CritComponent, GetMeleeAttackRateEvent>(GetMeleeAttackRate);
|
||||
SubscribeLocalEvent<BloodLustComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMoveSpeed);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<HandsComponent, MeleeBlockAttemptEvent>(OnBlockAttempt);
|
||||
}
|
||||
|
||||
private void OnBlockAttempt(Entity<HandsComponent> 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));
|
||||
}
|
||||
}
|
||||
@@ -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<RandomDamageComponent, MeleeHitEvent>(HandleHit);
|
||||
SubscribeLocalEvent<RandomDamageComponent, MeleeHitEvent>(HandleHit, before: new [] {typeof(MeleeBlockSystem)});
|
||||
}
|
||||
|
||||
private void HandleHit(Entity<RandomDamageComponent> ent, ref MeleeHitEvent args)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user