From 37d30568099259b7790ce72071c0fd3d755d78f6 Mon Sep 17 00:00:00 2001
From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Date: Wed, 7 Jun 2023 16:26:45 -0400
Subject: [PATCH] Events all over melee (#16997)
---
.../Weapons/Melee/MeleeWindupOverlay.cs | 4 +-
.../Electrocution/ElectrocutionSystem.cs | 2 +-
.../NPC/Systems/NPCCombatSystem.Melee.cs | 2 +-
.../Weapons/Melee/MeleeWeaponSystem.cs | 2 +-
.../BonusMeleeAttackRateComponent.cs | 31 ++++++
.../Components/BonusMeleeDamageComponent.cs | 28 ++++-
.../Weapons/Melee/Events/MeleeHitEvent.cs | 22 +++-
.../Weapons/Melee/MeleeWeaponComponent.cs | 10 +-
.../Weapons/Melee/SharedMeleeWeaponSystem.cs | 103 +++++++++++++++---
9 files changed, 170 insertions(+), 34 deletions(-)
create mode 100644 Content.Shared/Weapons/Melee/Components/BonusMeleeAttackRateComponent.cs
diff --git a/Content.Client/Weapons/Melee/MeleeWindupOverlay.cs b/Content.Client/Weapons/Melee/MeleeWindupOverlay.cs
index 72439de236..9f5193a3ca 100644
--- a/Content.Client/Weapons/Melee/MeleeWindupOverlay.cs
+++ b/Content.Client/Weapons/Melee/MeleeWindupOverlay.cs
@@ -104,7 +104,7 @@ public sealed class MeleeWindupOverlay : Overlay
const float endX = 22f;
// Area marking where to release
- var releaseWidth = 2f * SharedMeleeWeaponSystem.GracePeriod / (float) comp.WindupTime.TotalSeconds * EyeManager.PixelsPerMeter;
+ var releaseWidth = 2f * SharedMeleeWeaponSystem.GracePeriod / (float) _melee.GetWindupTime(meleeUid, owner.Value, comp).TotalSeconds * EyeManager.PixelsPerMeter;
const float releaseMiddle = (endX - startX) / 2f + startX;
var releaseBox = new Box2(new Vector2(releaseMiddle - releaseWidth / 2f, 3f) / EyeManager.PixelsPerMeter,
@@ -114,7 +114,7 @@ public sealed class MeleeWindupOverlay : Overlay
handle.DrawRect(releaseBox, Color.LimeGreen);
// Wraps around back to 0
- var totalDuration = comp.WindupTime.TotalSeconds * 2;
+ var totalDuration = _melee.GetWindupTime(meleeUid, owner.Value, comp).TotalSeconds * 2;
var elapsed = (currentTime - comp.WindUpStart.Value).TotalSeconds % (2 * totalDuration);
var value = elapsed / totalDuration;
diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs
index 1caffd1fd5..b73228a139 100644
--- a/Content.Server/Electrocution/ElectrocutionSystem.cs
+++ b/Content.Server/Electrocution/ElectrocutionSystem.cs
@@ -163,7 +163,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
if (!electrified.OnAttacked)
return;
- if (_meleeWeapon.GetDamage(args.Used).Total == 0)
+ if (_meleeWeapon.GetDamage(args.Used, args.User).Total == 0)
return;
TryDoElectrifiedAct(uid, args.User, 1, electrified);
diff --git a/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs b/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs
index 27b7156ad2..a4ece57beb 100644
--- a/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs
+++ b/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs
@@ -29,7 +29,7 @@ public sealed partial class NPCCombatSystem
var cdRemaining = weapon.NextAttack - _timing.CurTime;
// If CD remaining then backup.
- if (cdRemaining < TimeSpan.FromSeconds(1f / weapon.AttackRate) * 0.5f)
+ if (cdRemaining < TimeSpan.FromSeconds(1f / _melee.GetAttackRate(component.Weapon, uid, weapon)) * 0.5f)
return;
if (!_physics.TryGetNearestPoints(uid, component.Target, out var pointA, out var pointB))
diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
index 9c995d2283..6f93edb13d 100644
--- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
+++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
@@ -62,7 +62,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
if (!args.CanInteract || !args.CanAccess || component.HideFromExamine)
return;
- var damageSpec = GetDamage(uid, component);
+ var damageSpec = GetDamage(uid, args.User, component);
if (damageSpec.Total == FixedPoint2.Zero)
return;
diff --git a/Content.Shared/Weapons/Melee/Components/BonusMeleeAttackRateComponent.cs b/Content.Shared/Weapons/Melee/Components/BonusMeleeAttackRateComponent.cs
new file mode 100644
index 0000000000..a47ed503bf
--- /dev/null
+++ b/Content.Shared/Weapons/Melee/Components/BonusMeleeAttackRateComponent.cs
@@ -0,0 +1,31 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Weapons.Melee.Components;
+
+[RegisterComponent, NetworkedComponent, Access(typeof(SharedMeleeWeaponSystem))]
+public sealed class BonusMeleeAttackRateComponent : Component
+{
+ ///
+ /// The value added onto the attack rate of a melee weapon
+ ///
+ [DataField("flatModifier"), ViewVariables(VVAccess.ReadWrite)]
+ public float FlatModifier;
+
+ ///
+ /// A value that is multiplied by the attack rate of a melee weapon
+ ///
+ [DataField("multiplier"), ViewVariables(VVAccess.ReadWrite)]
+ public float Multiplier = 1;
+
+ ///
+ /// A value that is added on to a weapon's heavy windup time.
+ ///
+ [DataField("heavyWindupFlatModifier"), ViewVariables(VVAccess.ReadWrite)]
+ public float HeavyWindupFlatModifier;
+
+ ///
+ /// A value that is multiplied by a weapon's heavy windup time
+ ///
+ [DataField("heavyWindupMultiplier"), ViewVariables(VVAccess.ReadWrite)]
+ public float HeavyWindupMultiplier = 1;
+}
diff --git a/Content.Shared/Weapons/Melee/Components/BonusMeleeDamageComponent.cs b/Content.Shared/Weapons/Melee/Components/BonusMeleeDamageComponent.cs
index ea937b2582..06762d57ce 100644
--- a/Content.Shared/Weapons/Melee/Components/BonusMeleeDamageComponent.cs
+++ b/Content.Shared/Weapons/Melee/Components/BonusMeleeDamageComponent.cs
@@ -1,4 +1,6 @@
using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
+using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Melee.Components;
@@ -7,12 +9,30 @@ namespace Content.Shared.Weapons.Melee.Components;
/// This is used for adding in bonus damage via
/// This exists only for event relays and doing entity shenanigans.
///
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent, NetworkedComponent, Access(typeof(SharedMeleeWeaponSystem))]
public sealed class BonusMeleeDamageComponent : Component
{
///
- /// The damage that will be applied.
+ /// The damage that will be added.
///
- [DataField("bonusDamage", required: true)]
- public DamageSpecifier BonusDamage = default!;
+ [DataField("bonusDamage")]
+ public DamageSpecifier? BonusDamage;
+
+ ///
+ /// A modifier set for the damage that will be dealt.
+ ///
+ [DataField("damageModifierSet")]
+ public DamageModifierSet? DamageModifierSet;
+
+ ///
+ /// A flat damage increase added to
+ ///
+ [DataField("heavyDamageFlatModifier"), ViewVariables(VVAccess.ReadWrite)]
+ public FixedPoint2 HeavyDamageFlatModifier;
+
+ ///
+ /// A value multiplier by the value of
+ ///
+ [DataField("heavyDamageMultiplier"), ViewVariables(VVAccess.ReadWrite)]
+ public float HeavyDamageMultiplier = 1;
}
diff --git a/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs b/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs
index cb7e28890b..da46446377 100644
--- a/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs
+++ b/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs
@@ -1,4 +1,5 @@
using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
using Robust.Shared.Audio;
namespace Content.Shared.Weapons.Melee.Events;
@@ -70,6 +71,23 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs
///
/// Raised on a melee weapon to calculate potential damage bonuses or decreases.
///
-///
[ByRefEvent]
-public record struct GetMeleeDamageEvent(DamageSpecifier Damage);
+public record struct GetMeleeDamageEvent(EntityUid Weapon, DamageSpecifier Damage, List Modifiers, EntityUid User);
+
+///
+/// Raised on a melee weapon to calculate the attack rate.
+///
+[ByRefEvent]
+public record struct GetMeleeAttackRateEvent(EntityUid Weapon, float Rate, float Multipliers, EntityUid User);
+
+///
+/// Raised on a melee weapon to calculate the heavy damage modifier.
+///
+[ByRefEvent]
+public record struct GetHeavyDamageModifierEvent(EntityUid Weapon, FixedPoint2 DamageModifier, float Multipliers, EntityUid User);
+
+///
+/// Raised on a melee weapon to calculate the heavy windup modifier.
+///
+[ByRefEvent]
+public record struct GetHeavyWindupModifierEvent(EntityUid Weapon, float WindupModifier, float Multipliers, EntityUid User);
diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
index 2ee18740fd..4bb83425e2 100644
--- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
+++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
@@ -1,6 +1,5 @@
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
-using Content.Shared.Interaction;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
@@ -21,7 +20,7 @@ public sealed class MeleeWeaponComponent : Component
///
[ViewVariables(VVAccess.ReadWrite)]
[DataField("hidden")]
- public bool HideFromExamine { get; set; } = false;
+ public bool HideFromExamine;
///
/// Next time this component is allowed to light attack. Heavy attacks are wound up and never have a cooldown.
@@ -53,7 +52,6 @@ public sealed class MeleeWeaponComponent : Component
///
[ViewVariables(VVAccess.ReadWrite)]
public bool Attacking = false;
-
///
/// When did we start a heavy attack.
@@ -62,12 +60,6 @@ public sealed class MeleeWeaponComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField("windUpStart")]
public TimeSpan? WindUpStart;
- ///
- /// How long it takes a heavy attack to windup.
- ///
- [ViewVariables]
- public TimeSpan WindupTime => AttackRate > 0 ? TimeSpan.FromSeconds(1 / AttackRate * HeavyWindupModifier) : TimeSpan.Zero;
-
///
/// Heavy attack windup time gets multiplied by this value and the light attack cooldown.
///
diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
index f1a4ae111c..37b11ada7e 100644
--- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
+++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
@@ -18,7 +18,6 @@ using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Audio;
-using Robust.Shared.Collections;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Physics;
@@ -74,6 +73,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
SubscribeLocalEvent(OnMeleeSelected);
SubscribeLocalEvent(OnMeleeShot);
SubscribeLocalEvent(OnGetBonusMeleeDamage);
+ SubscribeLocalEvent(OnGetBonusHeavyDamageModifier);
+ SubscribeLocalEvent(OnGetBonusMeleeAttackRate);
+ SubscribeLocalEvent(OnGetBonusHeavyWindupModifier);
SubscribeAllEvent(OnHeavyAttack);
SubscribeAllEvent(OnLightAttack);
@@ -112,7 +114,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
private void OnMeleeSelected(EntityUid uid, MeleeWeaponComponent component, HandSelectedEvent args)
{
- if (component.AttackRate.Equals(0f))
+ var attackRate = GetAttackRate(uid, args.User, component);
+ if (attackRate.Equals(0f))
return;
if (!component.ResetOnHandSelected)
@@ -123,7 +126,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
// If someone swaps to this weapon then reset its cd.
var curTime = Timing.CurTime;
- var minimum = curTime + TimeSpan.FromSeconds(1 / component.AttackRate);
+ var minimum = curTime + TimeSpan.FromSeconds(1 / attackRate);
if (minimum < component.NextAttack)
return;
@@ -143,7 +146,28 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
private void OnGetBonusMeleeDamage(EntityUid uid, BonusMeleeDamageComponent component, ref GetMeleeDamageEvent args)
{
- args.Damage += component.BonusDamage;
+ if (component.BonusDamage != null)
+ args.Damage += component.BonusDamage;
+ if (component.DamageModifierSet != null)
+ args.Modifiers.Add(component.DamageModifierSet);
+ }
+
+ private void OnGetBonusHeavyDamageModifier(EntityUid uid, BonusMeleeDamageComponent component, ref GetHeavyDamageModifierEvent args)
+ {
+ args.DamageModifier += component.HeavyDamageFlatModifier;
+ args.Multipliers *= component.HeavyDamageMultiplier;
+ }
+
+ private void OnGetBonusMeleeAttackRate(EntityUid uid, BonusMeleeAttackRateComponent component, ref GetMeleeAttackRateEvent args)
+ {
+ args.Rate += component.FlatModifier;
+ args.Multipliers *= component.Multiplier;
+ }
+
+ private void OnGetBonusHeavyWindupModifier(EntityUid uid, BonusMeleeAttackRateComponent component, ref GetHeavyWindupModifierEvent args)
+ {
+ args.WindupModifier += component.HeavyWindupFlatModifier;
+ args.Multipliers *= component.HeavyWindupMultiplier;
}
private void OnStopAttack(StopAttackEvent msg, EntitySessionEventArgs args)
@@ -276,15 +300,66 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
component.Range = state.Range;
}
- public DamageSpecifier GetDamage(EntityUid uid, MeleeWeaponComponent? component = null)
+ ///
+ /// Gets the total damage a weapon does, including modifiers like wielding and enablind/disabling
+ ///
+ public DamageSpecifier GetDamage(EntityUid uid, EntityUid user, MeleeWeaponComponent? component = null)
{
if (!Resolve(uid, ref component, false))
return new DamageSpecifier();
- var ev = new GetMeleeDamageEvent(new (component.Damage));
+ var ev = new GetMeleeDamageEvent(uid, new (component.Damage), new(), user);
RaiseLocalEvent(uid, ref ev);
- return ev.Damage;
+ return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers);
+ }
+
+ public float GetAttackRate(EntityUid uid, EntityUid user, MeleeWeaponComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return 0;
+
+ var ev = new GetMeleeAttackRateEvent(uid, component.AttackRate, 1, user);
+ RaiseLocalEvent(uid, ref ev);
+
+ return ev.Rate * ev.Multipliers;
+ }
+
+ public FixedPoint2 GetHeavyDamageModifier(EntityUid uid, EntityUid user, MeleeWeaponComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return FixedPoint2.Zero;
+
+ var ev = new GetHeavyDamageModifierEvent(uid, component.HeavyDamageModifier, 1, user);
+ RaiseLocalEvent(uid, ref ev);
+
+ return ev.DamageModifier * ev.Multipliers;
+ }
+
+ public float GetHeavyWindupModifier(EntityUid uid, EntityUid user, MeleeWeaponComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return 0;
+
+ var ev = new GetHeavyWindupModifierEvent(uid, component.HeavyWindupModifier, 1, user);
+ RaiseLocalEvent(uid, ref ev);
+
+ return ev.WindupModifier * ev.Multipliers;
+ }
+
+ ///
+ /// Gets how long it takes a heavy attack to windup.
+ ///
+ public TimeSpan GetWindupTime(EntityUid uid, EntityUid user, MeleeWeaponComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return TimeSpan.Zero;
+
+ var attackRate = GetAttackRate(uid, user, component);
+
+ return attackRate > 0
+ ? TimeSpan.FromSeconds(1 / attackRate * GetHeavyWindupModifier(uid, user, component))
+ : TimeSpan.Zero;
}
public bool TryGetWeapon(EntityUid entity, out EntityUid weaponUid, [NotNullWhen(true)] out MeleeWeaponComponent? melee)
@@ -388,7 +463,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
}
// Windup time checked elsewhere.
- var fireRate = TimeSpan.FromSeconds(1f / weapon.AttackRate);
+ var fireRate = TimeSpan.FromSeconds(1f / GetAttackRate(weaponUid, user, weapon));
var swings = 0;
// TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly.
@@ -451,7 +526,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
///
/// When an attack is released get the actual modifier for damage done.
///
- public float GetModifier(MeleeWeaponComponent component, bool lightAttack)
+ public float GetModifier(EntityUid uid, EntityUid user, MeleeWeaponComponent component, bool lightAttack)
{
if (lightAttack)
return 1f;
@@ -461,7 +536,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return 0f;
var releaseTime = (Timing.CurTime - windup.Value).TotalSeconds;
- var windupTime = component.WindupTime.TotalSeconds;
+ var windupTime = GetWindupTime(uid, user, component).TotalSeconds;
// Wraps around back to 0
releaseTime %= (2 * windupTime);
@@ -479,14 +554,14 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
fraction = 0;
DebugTools.Assert(fraction <= 1);
- return (float) fraction * component.HeavyDamageModifier.Float();
+ return (float) fraction * GetHeavyDamageModifier(uid, user, component).Float();
}
protected abstract bool InRange(EntityUid user, EntityUid target, float range, ICommonSession? session);
protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
{
- var damage = GetDamage(meleeUid, component) * GetModifier(component, true);
+ var damage = GetDamage(meleeUid, user, component) * GetModifier(meleeUid, user, component, true);
// For consistency with wide attacks stuff needs damageable.
if (Deleted(ev.Target) ||
@@ -560,7 +635,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
{
Audio.PlayPredicted(hitEvent.HitSoundOverride, meleeUid, user);
}
- else if (GetDamage(meleeUid, component).Total.Equals(FixedPoint2.Zero) && component.HitSound != null)
+ else if (GetDamage(meleeUid, user, component).Total.Equals(FixedPoint2.Zero) && component.HitSound != null)
{
Audio.PlayPredicted(component.HitSound, meleeUid, user);
}
@@ -593,7 +668,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
var direction = targetMap.Position - userPos;
var distance = Math.Min(component.Range, direction.Length);
- var damage = GetDamage(meleeUid, component) * GetModifier(component, false);
+ var damage = GetDamage(meleeUid, user, component) * GetModifier(meleeUid, user, component, false);
var entities = ev.Entities;
if (entities.Count == 0)