Events all over melee (#16997)

This commit is contained in:
Nemanja
2023-06-07 16:26:45 -04:00
committed by GitHub
parent 937aae14ff
commit 37d3056809
9 changed files with 170 additions and 34 deletions

View File

@@ -104,7 +104,7 @@ public sealed class MeleeWindupOverlay : Overlay
const float endX = 22f; const float endX = 22f;
// Area marking where to release // 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; const float releaseMiddle = (endX - startX) / 2f + startX;
var releaseBox = new Box2(new Vector2(releaseMiddle - releaseWidth / 2f, 3f) / EyeManager.PixelsPerMeter, 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); handle.DrawRect(releaseBox, Color.LimeGreen);
// Wraps around back to 0 // 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 elapsed = (currentTime - comp.WindUpStart.Value).TotalSeconds % (2 * totalDuration);
var value = elapsed / totalDuration; var value = elapsed / totalDuration;

View File

@@ -163,7 +163,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
if (!electrified.OnAttacked) if (!electrified.OnAttacked)
return; return;
if (_meleeWeapon.GetDamage(args.Used).Total == 0) if (_meleeWeapon.GetDamage(args.Used, args.User).Total == 0)
return; return;
TryDoElectrifiedAct(uid, args.User, 1, electrified); TryDoElectrifiedAct(uid, args.User, 1, electrified);

View File

@@ -29,7 +29,7 @@ public sealed partial class NPCCombatSystem
var cdRemaining = weapon.NextAttack - _timing.CurTime; var cdRemaining = weapon.NextAttack - _timing.CurTime;
// If CD remaining then backup. // 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; return;
if (!_physics.TryGetNearestPoints(uid, component.Target, out var pointA, out var pointB)) if (!_physics.TryGetNearestPoints(uid, component.Target, out var pointA, out var pointB))

View File

@@ -62,7 +62,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
if (!args.CanInteract || !args.CanAccess || component.HideFromExamine) if (!args.CanInteract || !args.CanAccess || component.HideFromExamine)
return; return;
var damageSpec = GetDamage(uid, component); var damageSpec = GetDamage(uid, args.User, component);
if (damageSpec.Total == FixedPoint2.Zero) if (damageSpec.Total == FixedPoint2.Zero)
return; return;

View File

@@ -0,0 +1,31 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Melee.Components;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedMeleeWeaponSystem))]
public sealed class BonusMeleeAttackRateComponent : Component
{
/// <summary>
/// The value added onto the attack rate of a melee weapon
/// </summary>
[DataField("flatModifier"), ViewVariables(VVAccess.ReadWrite)]
public float FlatModifier;
/// <summary>
/// A value that is multiplied by the attack rate of a melee weapon
/// </summary>
[DataField("multiplier"), ViewVariables(VVAccess.ReadWrite)]
public float Multiplier = 1;
/// <summary>
/// A value that is added on to a weapon's heavy windup time.
/// </summary>
[DataField("heavyWindupFlatModifier"), ViewVariables(VVAccess.ReadWrite)]
public float HeavyWindupFlatModifier;
/// <summary>
/// A value that is multiplied by a weapon's heavy windup time
/// </summary>
[DataField("heavyWindupMultiplier"), ViewVariables(VVAccess.ReadWrite)]
public float HeavyWindupMultiplier = 1;
}

View File

@@ -1,4 +1,6 @@
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Melee.Components; 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 <see cref="GetMeleeWeaponEvent"/> /// This is used for adding in bonus damage via <see cref="GetMeleeWeaponEvent"/>
/// This exists only for event relays and doing entity shenanigans. /// This exists only for event relays and doing entity shenanigans.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent, Access(typeof(SharedMeleeWeaponSystem))]
public sealed class BonusMeleeDamageComponent : Component public sealed class BonusMeleeDamageComponent : Component
{ {
/// <summary> /// <summary>
/// The damage that will be applied. /// The damage that will be added.
/// </summary> /// </summary>
[DataField("bonusDamage", required: true)] [DataField("bonusDamage")]
public DamageSpecifier BonusDamage = default!; public DamageSpecifier? BonusDamage;
/// <summary>
/// A modifier set for the damage that will be dealt.
/// </summary>
[DataField("damageModifierSet")]
public DamageModifierSet? DamageModifierSet;
/// <summary>
/// A flat damage increase added to <see cref="GetHeavyDamageModifierEvent"/>
/// </summary>
[DataField("heavyDamageFlatModifier"), ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 HeavyDamageFlatModifier;
/// <summary>
/// A value multiplier by the value of <see cref="GetHeavyDamageModifierEvent"/>
/// </summary>
[DataField("heavyDamageMultiplier"), ViewVariables(VVAccess.ReadWrite)]
public float HeavyDamageMultiplier = 1;
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio; using Robust.Shared.Audio;
namespace Content.Shared.Weapons.Melee.Events; namespace Content.Shared.Weapons.Melee.Events;
@@ -70,6 +71,23 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs
/// <summary> /// <summary>
/// Raised on a melee weapon to calculate potential damage bonuses or decreases. /// Raised on a melee weapon to calculate potential damage bonuses or decreases.
/// </summary> /// </summary>
/// <param name="Damage"></param>
[ByRefEvent] [ByRefEvent]
public record struct GetMeleeDamageEvent(DamageSpecifier Damage); public record struct GetMeleeDamageEvent(EntityUid Weapon, DamageSpecifier Damage, List<DamageModifierSet> Modifiers, EntityUid User);
/// <summary>
/// Raised on a melee weapon to calculate the attack rate.
/// </summary>
[ByRefEvent]
public record struct GetMeleeAttackRateEvent(EntityUid Weapon, float Rate, float Multipliers, EntityUid User);
/// <summary>
/// Raised on a melee weapon to calculate the heavy damage modifier.
/// </summary>
[ByRefEvent]
public record struct GetHeavyDamageModifierEvent(EntityUid Weapon, FixedPoint2 DamageModifier, float Multipliers, EntityUid User);
/// <summary>
/// Raised on a melee weapon to calculate the heavy windup modifier.
/// </summary>
[ByRefEvent]
public record struct GetHeavyWindupModifierEvent(EntityUid Weapon, float WindupModifier, float Multipliers, EntityUid User);

View File

@@ -1,6 +1,5 @@
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -21,7 +20,7 @@ public sealed class MeleeWeaponComponent : Component
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("hidden")] [DataField("hidden")]
public bool HideFromExamine { get; set; } = false; public bool HideFromExamine;
/// <summary> /// <summary>
/// Next time this component is allowed to light attack. Heavy attacks are wound up and never have a cooldown. /// 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
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public bool Attacking = false; public bool Attacking = false;
/// <summary> /// <summary>
/// When did we start a heavy attack. /// When did we start a heavy attack.
@@ -62,12 +60,6 @@ public sealed class MeleeWeaponComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField("windUpStart")] [ViewVariables(VVAccess.ReadWrite), DataField("windUpStart")]
public TimeSpan? WindUpStart; public TimeSpan? WindUpStart;
/// <summary>
/// How long it takes a heavy attack to windup.
/// </summary>
[ViewVariables]
public TimeSpan WindupTime => AttackRate > 0 ? TimeSpan.FromSeconds(1 / AttackRate * HeavyWindupModifier) : TimeSpan.Zero;
/// <summary> /// <summary>
/// Heavy attack windup time gets multiplied by this value and the light attack cooldown. /// Heavy attack windup time gets multiplied by this value and the light attack cooldown.
/// </summary> /// </summary>

View File

@@ -18,7 +18,6 @@ using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems; using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Collections;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Physics; using Robust.Shared.Physics;
@@ -74,6 +73,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
SubscribeLocalEvent<MeleeWeaponComponent, HandSelectedEvent>(OnMeleeSelected); SubscribeLocalEvent<MeleeWeaponComponent, HandSelectedEvent>(OnMeleeSelected);
SubscribeLocalEvent<MeleeWeaponComponent, GunShotEvent>(OnMeleeShot); SubscribeLocalEvent<MeleeWeaponComponent, GunShotEvent>(OnMeleeShot);
SubscribeLocalEvent<BonusMeleeDamageComponent, GetMeleeDamageEvent>(OnGetBonusMeleeDamage); SubscribeLocalEvent<BonusMeleeDamageComponent, GetMeleeDamageEvent>(OnGetBonusMeleeDamage);
SubscribeLocalEvent<BonusMeleeDamageComponent, GetHeavyDamageModifierEvent>(OnGetBonusHeavyDamageModifier);
SubscribeLocalEvent<BonusMeleeAttackRateComponent, GetMeleeAttackRateEvent>(OnGetBonusMeleeAttackRate);
SubscribeLocalEvent<BonusMeleeAttackRateComponent, GetHeavyWindupModifierEvent>(OnGetBonusHeavyWindupModifier);
SubscribeAllEvent<HeavyAttackEvent>(OnHeavyAttack); SubscribeAllEvent<HeavyAttackEvent>(OnHeavyAttack);
SubscribeAllEvent<LightAttackEvent>(OnLightAttack); SubscribeAllEvent<LightAttackEvent>(OnLightAttack);
@@ -112,7 +114,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
private void OnMeleeSelected(EntityUid uid, MeleeWeaponComponent component, HandSelectedEvent args) 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; return;
if (!component.ResetOnHandSelected) if (!component.ResetOnHandSelected)
@@ -123,7 +126,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
// If someone swaps to this weapon then reset its cd. // If someone swaps to this weapon then reset its cd.
var curTime = Timing.CurTime; var curTime = Timing.CurTime;
var minimum = curTime + TimeSpan.FromSeconds(1 / component.AttackRate); var minimum = curTime + TimeSpan.FromSeconds(1 / attackRate);
if (minimum < component.NextAttack) if (minimum < component.NextAttack)
return; return;
@@ -143,7 +146,28 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
private void OnGetBonusMeleeDamage(EntityUid uid, BonusMeleeDamageComponent component, ref GetMeleeDamageEvent args) 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) private void OnStopAttack(StopAttackEvent msg, EntitySessionEventArgs args)
@@ -276,15 +300,66 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
component.Range = state.Range; component.Range = state.Range;
} }
public DamageSpecifier GetDamage(EntityUid uid, MeleeWeaponComponent? component = null) /// <summary>
/// Gets the total damage a weapon does, including modifiers like wielding and enablind/disabling
/// </summary>
public DamageSpecifier GetDamage(EntityUid uid, EntityUid user, MeleeWeaponComponent? component = null)
{ {
if (!Resolve(uid, ref component, false)) if (!Resolve(uid, ref component, false))
return new DamageSpecifier(); return new DamageSpecifier();
var ev = new GetMeleeDamageEvent(new (component.Damage)); var ev = new GetMeleeDamageEvent(uid, new (component.Damage), new(), user);
RaiseLocalEvent(uid, ref ev); 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;
}
/// <summary>
/// Gets how long it takes a heavy attack to windup.
/// </summary>
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) 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. // Windup time checked elsewhere.
var fireRate = TimeSpan.FromSeconds(1f / weapon.AttackRate); var fireRate = TimeSpan.FromSeconds(1f / GetAttackRate(weaponUid, user, weapon));
var swings = 0; var swings = 0;
// TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly. // 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
/// <summary> /// <summary>
/// When an attack is released get the actual modifier for damage done. /// When an attack is released get the actual modifier for damage done.
/// </summary> /// </summary>
public float GetModifier(MeleeWeaponComponent component, bool lightAttack) public float GetModifier(EntityUid uid, EntityUid user, MeleeWeaponComponent component, bool lightAttack)
{ {
if (lightAttack) if (lightAttack)
return 1f; return 1f;
@@ -461,7 +536,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return 0f; return 0f;
var releaseTime = (Timing.CurTime - windup.Value).TotalSeconds; var releaseTime = (Timing.CurTime - windup.Value).TotalSeconds;
var windupTime = component.WindupTime.TotalSeconds; var windupTime = GetWindupTime(uid, user, component).TotalSeconds;
// Wraps around back to 0 // Wraps around back to 0
releaseTime %= (2 * windupTime); releaseTime %= (2 * windupTime);
@@ -479,14 +554,14 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
fraction = 0; fraction = 0;
DebugTools.Assert(fraction <= 1); 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 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) 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. // For consistency with wide attacks stuff needs damageable.
if (Deleted(ev.Target) || if (Deleted(ev.Target) ||
@@ -560,7 +635,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
{ {
Audio.PlayPredicted(hitEvent.HitSoundOverride, meleeUid, user); 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); Audio.PlayPredicted(component.HitSound, meleeUid, user);
} }
@@ -593,7 +668,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
var direction = targetMap.Position - userPos; var direction = targetMap.Position - userPos;
var distance = Math.Min(component.Range, direction.Length); 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; var entities = ev.Entities;
if (entities.Count == 0) if (entities.Count == 0)