Implement field-deltas for melee (#33977)

* Implement field-deltas for melee

* Review
This commit is contained in:
metalgearsloth
2025-03-30 16:02:45 +11:00
committed by GitHub
parent b1f542a54c
commit 0ff70fdb40
3 changed files with 55 additions and 29 deletions

View File

@@ -132,6 +132,16 @@ public sealed partial class ZombieSystem
melee.Angle = 0.0f; melee.Angle = 0.0f;
melee.HitSound = zombiecomp.BiteSound; melee.HitSound = zombiecomp.BiteSound;
DirtyFields(target, melee, null, fields:
[
nameof(MeleeWeaponComponent.Animation),
nameof(MeleeWeaponComponent.WideAnimation),
nameof(MeleeWeaponComponent.AltDisarm),
nameof(MeleeWeaponComponent.Range),
nameof(MeleeWeaponComponent.Angle),
nameof(MeleeWeaponComponent.HitSound),
]);
if (mobState.CurrentState == MobState.Alive) if (mobState.CurrentState == MobState.Alive)
{ {
// Groaning when damaged // Groaning when damaged

View File

@@ -10,7 +10,7 @@ namespace Content.Shared.Weapons.Melee;
/// <summary> /// <summary>
/// When given to a mob lets them do unarmed attacks, or when given to an item lets someone wield it to do attacks. /// When given to a mob lets them do unarmed attacks, or when given to an item lets someone wield it to do attacks.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause]
public sealed partial class MeleeWeaponComponent : Component public sealed partial class MeleeWeaponComponent : Component
{ {
// TODO: This is becoming bloated as shit. // TODO: This is becoming bloated as shit.
@@ -18,28 +18,26 @@ public sealed partial class MeleeWeaponComponent : Component
/// <summary> /// <summary>
/// Does this entity do a disarm on alt attack. /// Does this entity do a disarm on alt attack.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] [DataField, AutoNetworkedField]
public bool AltDisarm = true; public bool AltDisarm = true;
/// <summary> /// <summary>
/// Should the melee weapon's damage stats be examinable. /// Should the melee weapon's damage stats be examinable.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField]
[DataField]
public bool Hidden; public bool Hidden;
/// <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.
/// </summary> /// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
[ViewVariables(VVAccess.ReadWrite)]
[AutoPausedField] [AutoPausedField]
public TimeSpan NextAttack; public TimeSpan NextAttack;
/// <summary> /// <summary>
/// Starts attack cooldown when equipped if true. /// Starts attack cooldown when equipped if true.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField] [DataField, AutoNetworkedField]
public bool ResetOnHandSelected = true; public bool ResetOnHandSelected = true;
/* /*
@@ -51,72 +49,70 @@ public sealed partial class MeleeWeaponComponent : Component
/// <summary> /// <summary>
/// How many times we can attack per second. /// How many times we can attack per second.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public float AttackRate = 1f; public float AttackRate = 1f;
/// <summary> /// <summary>
/// Are we currently holding down the mouse for an attack. /// Are we currently holding down the mouse for an attack.
/// Used so we can't just hold the mouse button and attack constantly. /// Used so we can't just hold the mouse button and attack constantly.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] [AutoNetworkedField]
public bool Attacking = false; public bool Attacking = false;
/// <summary> /// <summary>
/// If true, attacks will be repeated automatically without requiring the mouse button to be lifted. /// If true, attacks will be repeated automatically without requiring the mouse button to be lifted.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] [DataField, AutoNetworkedField]
public bool AutoAttack; public bool AutoAttack;
/// <summary> /// <summary>
/// If true, attacks will bypass armor resistances. /// If true, attacks will bypass armor resistances.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] [DataField, AutoNetworkedField]
public bool ResistanceBypass = false; public bool ResistanceBypass = false;
/// <summary> /// <summary>
/// Base damage for this weapon. Can be modified via heavy damage or other means. /// Base damage for this weapon. Can be modified via heavy damage or other means.
/// </summary> /// </summary>
[DataField(required: true)] [DataField(required: true), AutoNetworkedField]
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public DamageSpecifier Damage = default!; public DamageSpecifier Damage = default!;
[DataField] [DataField, AutoNetworkedField]
[ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 BluntStaminaDamageFactor = FixedPoint2.New(0.5f); public FixedPoint2 BluntStaminaDamageFactor = FixedPoint2.New(0.5f);
/// <summary> /// <summary>
/// Multiplies damage by this amount for single-target attacks. /// Multiplies damage by this amount for single-target attacks.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField] [DataField, AutoNetworkedField]
public FixedPoint2 ClickDamageModifier = FixedPoint2.New(1); public FixedPoint2 ClickDamageModifier = FixedPoint2.New(1);
// TODO: Temporarily 1.5 until interactionoutline is adjusted to use melee, then probably drop to 1.2 // TODO: Temporarily 1.5 until interactionoutline is adjusted to use melee, then probably drop to 1.2
/// <summary> /// <summary>
/// Nearest edge range to hit an entity. /// Nearest edge range to hit an entity.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public float Range = 1.5f; public float Range = 1.5f;
/// <summary> /// <summary>
/// Total width of the angle for wide attacks. /// Total width of the angle for wide attacks.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField] [DataField, AutoNetworkedField]
public Angle Angle = Angle.FromDegrees(60); public Angle Angle = Angle.FromDegrees(60);
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public EntProtoId Animation = "WeaponArcPunch"; public EntProtoId Animation = "WeaponArcPunch";
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public EntProtoId WideAnimation = "WeaponArcSlash"; public EntProtoId WideAnimation = "WeaponArcSlash";
/// <summary> /// <summary>
/// Rotation of the animation. /// Rotation of the animation.
/// 0 degrees means the top faces the attacker. /// 0 degrees means the top faces the attacker.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField] [DataField, AutoNetworkedField]
public Angle WideAnimationRotation = Angle.Zero; public Angle WideAnimationRotation = Angle.Zero;
[ViewVariables(VVAccess.ReadWrite), DataField] [DataField, AutoNetworkedField]
public bool SwingLeft; public bool SwingLeft;

View File

@@ -104,7 +104,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
if (gun.NextFire > component.NextAttack) if (gun.NextFire > component.NextAttack)
{ {
component.NextAttack = gun.NextFire; component.NextAttack = gun.NextFire;
Dirty(uid, component); DirtyField(uid, component, nameof(MeleeWeaponComponent.NextAttack));
} }
} }
@@ -128,7 +128,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return; return;
component.NextAttack = minimum; component.NextAttack = minimum;
Dirty(uid, component); DirtyField(uid, component, nameof(MeleeWeaponComponent.NextAttack));
} }
private void OnGetBonusMeleeDamage(EntityUid uid, BonusMeleeDamageComponent component, ref GetMeleeDamageEvent args) private void OnGetBonusMeleeDamage(EntityUid uid, BonusMeleeDamageComponent component, ref GetMeleeDamageEvent args)
@@ -168,7 +168,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return; return;
weapon.Attacking = false; weapon.Attacking = false;
Dirty(weaponUid, weapon); DirtyField(weaponUid, weapon, nameof(MeleeWeaponComponent.Attacking));
} }
private void OnLightAttack(LightAttackEvent msg, EntitySessionEventArgs args) private void OnLightAttack(LightAttackEvent msg, EntitySessionEventArgs args)
@@ -392,7 +392,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
swings++; swings++;
} }
Dirty(weaponUid, weapon); DirtyField(weaponUid, weapon, nameof(MeleeWeaponComponent.NextAttack));
// Do this AFTER attack so it doesn't spam every tick // Do this AFTER attack so it doesn't spam every tick
var ev = new AttemptMeleeEvent(); var ev = new AttemptMeleeEvent();
@@ -442,6 +442,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
RaiseLocalEvent(user, ref attackEv); RaiseLocalEvent(user, ref attackEv);
weapon.Attacking = true; weapon.Attacking = true;
DirtyField(weaponUid, weapon, nameof(MeleeWeaponComponent.Attacking));
return true; return true;
} }
@@ -838,15 +839,21 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
//Setting deactivated damage to the weapon's regular value before changing it. //Setting deactivated damage to the weapon's regular value before changing it.
itemToggleMelee.DeactivatedDamage ??= meleeWeapon.Damage; itemToggleMelee.DeactivatedDamage ??= meleeWeapon.Damage;
meleeWeapon.Damage = itemToggleMelee.ActivatedDamage; meleeWeapon.Damage = itemToggleMelee.ActivatedDamage;
DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.Damage));
} }
meleeWeapon.HitSound = itemToggleMelee.ActivatedSoundOnHit; if (meleeWeapon.HitSound?.Equals(itemToggleMelee.ActivatedSoundOnHit) != true)
{
meleeWeapon.HitSound = itemToggleMelee.ActivatedSoundOnHit;
DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.HitSound));
}
if (itemToggleMelee.ActivatedSoundOnHitNoDamage != null) if (itemToggleMelee.ActivatedSoundOnHitNoDamage != null)
{ {
//Setting the deactivated sound on no damage hit to the weapon's regular value before changing it. //Setting the deactivated sound on no damage hit to the weapon's regular value before changing it.
itemToggleMelee.DeactivatedSoundOnHitNoDamage ??= meleeWeapon.NoDamageSound; itemToggleMelee.DeactivatedSoundOnHitNoDamage ??= meleeWeapon.NoDamageSound;
meleeWeapon.NoDamageSound = itemToggleMelee.ActivatedSoundOnHitNoDamage; meleeWeapon.NoDamageSound = itemToggleMelee.ActivatedSoundOnHitNoDamage;
DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.NoDamageSound));
} }
if (itemToggleMelee.ActivatedSoundOnSwing != null) if (itemToggleMelee.ActivatedSoundOnSwing != null)
@@ -854,28 +861,41 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
//Setting the deactivated sound on no damage hit to the weapon's regular value before changing it. //Setting the deactivated sound on no damage hit to the weapon's regular value before changing it.
itemToggleMelee.DeactivatedSoundOnSwing ??= meleeWeapon.SwingSound; itemToggleMelee.DeactivatedSoundOnSwing ??= meleeWeapon.SwingSound;
meleeWeapon.SwingSound = itemToggleMelee.ActivatedSoundOnSwing; meleeWeapon.SwingSound = itemToggleMelee.ActivatedSoundOnSwing;
DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.SwingSound));
} }
if (itemToggleMelee.DeactivatedSecret) if (itemToggleMelee.DeactivatedSecret)
{
meleeWeapon.Hidden = false; meleeWeapon.Hidden = false;
}
} }
else else
{ {
if (itemToggleMelee.DeactivatedDamage != null) if (itemToggleMelee.DeactivatedDamage != null)
{
meleeWeapon.Damage = itemToggleMelee.DeactivatedDamage; meleeWeapon.Damage = itemToggleMelee.DeactivatedDamage;
DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.Damage));
}
meleeWeapon.HitSound = itemToggleMelee.DeactivatedSoundOnHit; meleeWeapon.HitSound = itemToggleMelee.DeactivatedSoundOnHit;
DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.HitSound));
if (itemToggleMelee.DeactivatedSoundOnHitNoDamage != null) if (itemToggleMelee.DeactivatedSoundOnHitNoDamage != null)
{
meleeWeapon.NoDamageSound = itemToggleMelee.DeactivatedSoundOnHitNoDamage; meleeWeapon.NoDamageSound = itemToggleMelee.DeactivatedSoundOnHitNoDamage;
DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.NoDamageSound));
}
if (itemToggleMelee.DeactivatedSoundOnSwing != null) if (itemToggleMelee.DeactivatedSoundOnSwing != null)
{
meleeWeapon.SwingSound = itemToggleMelee.DeactivatedSoundOnSwing; meleeWeapon.SwingSound = itemToggleMelee.DeactivatedSoundOnSwing;
DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.SwingSound));
}
if (itemToggleMelee.DeactivatedSecret) if (itemToggleMelee.DeactivatedSecret)
{
meleeWeapon.Hidden = true; meleeWeapon.Hidden = true;
}
} }
Dirty(uid, meleeWeapon);
} }
} }