diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index a01a324013..ef4b161477 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -23,6 +23,7 @@ using Content.Shared.Tag; using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Random; @@ -33,16 +34,17 @@ namespace Content.Server.Weapons.Melee; public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem { - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly BloodstreamSystem _bloodstream = default!; - [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly DamageExamineSystem _damageExamine = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly LagCompensationSystem _lag = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; - [Dependency] private readonly SolutionContainerSystem _solutions = default!; - [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly BloodstreamSystem _bloodstream = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly DamageExamineSystem _damageExamine = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly LagCompensationSystem _lag = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; + [Dependency] private readonly SolutionContainerSystem _solutions = default!; + [Dependency] private readonly TagSystem _tag = default!; public override void Initialize() { @@ -158,7 +160,8 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem return false; } - Audio.PlayPvs(combatMode.DisarmSuccessSound, user, AudioParams.Default.WithVariation(0.025f).WithVolume(5f)); + _audio.PlayPvs(combatMode.DisarmSuccessSound, user, AudioParams.Default.WithVariation(0.025f).WithVolume(5f)); + AdminLogger.Add(LogType.DisarmedAction, $"{ToPrettyString(user):user} used disarm on {ToPrettyString(target):target}"); var targetEnt = Identity.Entity(target, EntityManager); var userEnt = Identity.Entity(user, EntityManager); @@ -175,7 +178,6 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem PopupSystem.PopupEntity(msgOther, user, filterOther, true); PopupSystem.PopupEntity(msgUser, target, user); - if (eventArgs.IsStunned) { diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index e64657743d..f5f4e3f199 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -38,7 +38,7 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StunSystem _stun = default!; - public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation; + private const float DamagePitchVariation = 0.05f; public const float GunClumsyChance = 0.5f; public override void Initialize() diff --git a/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs b/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs new file mode 100644 index 0000000000..5bf7480202 --- /dev/null +++ b/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs @@ -0,0 +1,108 @@ +using Content.Shared.Weapons.Melee.Components; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; + +namespace Content.Shared.Weapons.Melee; + +/// +/// This handles +/// +public sealed class MeleeSoundSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + + public const float DamagePitchVariation = 0.05f; + + /// + /// Plays the SwingSound from a weapon component + /// for immediate feedback, misses and such + /// (Swinging a weapon goes "whoosh" whether it hits or not) + /// + public void PlaySwingSound(EntityUid userUid, EntityUid weaponUid, MeleeWeaponComponent weaponComponent) + { + _audio.PlayPredicted(weaponComponent.SwingSound, weaponUid, userUid); + } + + /// + /// Takes a "damageType" string as an argument and uses it to + /// search one of the various Dictionaries in the MeleeSoundComponent + /// for a sound to play, and falls back if that fails + /// + /// Serves as a lookup key for a hit sound + /// A sound can be supplied by the itself to override everything else + public void PlayHitSound(EntityUid targetUid, EntityUid? userUid, string? damageType, SoundSpecifier? hitSoundOverride, MeleeWeaponComponent weaponComponent) + { + var hitSound = weaponComponent.HitSound; + var noDamageSound = weaponComponent.NoDamageSound; + + var playedSound = false; + + if (Deleted(targetUid)) + return; + + // hitting can obv destroy an entity so we play at coords and not following them + var coords = Transform(targetUid).Coordinates; + // Play sound based off of highest damage type. + if (TryComp(targetUid, out var damageSoundComp)) + { + if (damageType == null && damageSoundComp.NoDamageSound != null) + { + _audio.PlayPredicted(damageSoundComp.NoDamageSound, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + else if (damageType != null && damageSoundComp.SoundTypes?.TryGetValue(damageType, out var damageSoundType) == true) + { + _audio.PlayPredicted(damageSoundType, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + else if (damageType != null && damageSoundComp.SoundGroups?.TryGetValue(damageType, out var damageSoundGroup) == true) + { + _audio.PlayPredicted(damageSoundGroup, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + } + + // Use weapon sounds if the thing being hit doesn't specify its own sounds. + if (!playedSound) + { + if (hitSoundOverride != null) + { + _audio.PlayPredicted(hitSoundOverride, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + else if (hitSound != null) + { + _audio.PlayPredicted(hitSound, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + else + { + _audio.PlayPredicted(noDamageSound, coords, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + playedSound = true; + } + } + + // Fallback to generic sounds. + if (!playedSound) + { + switch (damageType) + { + // Unfortunately heat returns caustic group so can't just use the damagegroup in that instance. + case "Burn": + case "Heat": + case "Radiation": + case "Cold": + _audio.PlayPredicted(new SoundPathSpecifier("/Audio/Items/welder.ogg"), targetUid, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + break; + // No damage, fallback to tappies + case null: + _audio.PlayPredicted(new SoundCollectionSpecifier("WeakHit"), targetUid, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + break; + case "Brute": + _audio.PlayPredicted(new SoundCollectionSpecifier("MetalThud"), targetUid, userUid, AudioParams.Default.WithVariation(DamagePitchVariation)); + break; + } + } + } + +} diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 6a5127f2c9..e59b4a13fe 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -21,8 +21,6 @@ using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; @@ -36,22 +34,21 @@ namespace Content.Shared.Weapons.Melee; public abstract class SharedMeleeWeaponSystem : EntitySystem { - [Dependency] protected readonly IGameTiming Timing = default!; - [Dependency] protected readonly IMapManager MapManager = default!; - [Dependency] private readonly IPrototypeManager _protoManager = default!; - [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; - [Dependency] protected readonly ActionBlockerSystem Blocker = default!; - [Dependency] protected readonly DamageableSystem Damageable = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] protected readonly SharedAudioSystem Audio = default!; - [Dependency] protected readonly SharedCombatModeSystem CombatMode = default!; - [Dependency] protected readonly SharedInteractionSystem Interaction = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] protected readonly SharedPopupSystem PopupSystem = default!; - [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; - [Dependency] private readonly StaminaSystem _stamina = default!; + [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; + [Dependency] protected readonly ActionBlockerSystem Blocker = default!; + [Dependency] protected readonly SharedCombatModeSystem CombatMode = default!; + [Dependency] protected readonly DamageableSystem Damageable = default!; + [Dependency] protected readonly SharedInteractionSystem Interaction = default!; + [Dependency] protected readonly IMapManager MapManager = default!; + [Dependency] protected readonly SharedPopupSystem PopupSystem = default!; + [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly MeleeSoundSystem _meleeSound = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly StaminaSystem _stamina = default!; - public const float DamagePitchVariation = 0.05f; private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque); /// @@ -83,7 +80,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem SubscribeAllEvent(OnStopAttack); #if DEBUG - SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent (OnMapInit); } private void OnMapInit(EntityUid uid, MeleeWeaponComponent component, MapInitEvent args) @@ -465,7 +463,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem } var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage, null); RaiseLocalEvent(meleeUid, missEvent); - Audio.PlayPredicted(component.SwingSound, meleeUid, user); + _meleeSound.PlaySwingSound(user, meleeUid, component); return; } @@ -520,7 +518,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem } - PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component.HitSound, component.NoDamageSound); + _meleeSound.PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component); if (damageResult?.GetTotal() > FixedPoint2.Zero) { @@ -563,7 +561,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage, direction); RaiseLocalEvent(meleeUid, missEvent); - Audio.PlayPredicted(component.SwingSound, meleeUid, user); + // immediate audio feedback + _meleeSound.PlaySwingSound(user, meleeUid, component); + return true; } @@ -658,7 +658,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem if (entities.Count != 0) { var target = entities.First(); - PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component.HitSound, component.NoDamageSound); + _meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component); } if (appliedDamage.GetTotal() > FixedPoint2.Zero) @@ -702,77 +702,6 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem return true; } - public void PlayHitSound(EntityUid target, EntityUid? user, string? type, SoundSpecifier? hitSoundOverride, SoundSpecifier? hitSound, SoundSpecifier? noDamageSound) - { - var playedSound = false; - - if (Deleted(target)) - return; - - // hitting can obv destroy an entity so we play at coords and not following them - var coords = Transform(target).Coordinates; - // Play sound based off of highest damage type. - if (TryComp(target, out var damageSoundComp)) - { - if (type == null && damageSoundComp.NoDamageSound != null) - { - Audio.PlayPredicted(damageSoundComp.NoDamageSound, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - else if (type != null && damageSoundComp.SoundTypes?.TryGetValue(type, out var damageSoundType) == true) - { - Audio.PlayPredicted(damageSoundType, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - else if (type != null && damageSoundComp.SoundGroups?.TryGetValue(type, out var damageSoundGroup) == true) - { - Audio.PlayPredicted(damageSoundGroup, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - } - - // Use weapon sounds if the thing being hit doesn't specify its own sounds. - if (!playedSound) - { - if (hitSoundOverride != null) - { - Audio.PlayPredicted(hitSoundOverride, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - else if (hitSound != null) - { - Audio.PlayPredicted(hitSound, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - else if (noDamageSound != null) - { - Audio.PlayPredicted(noDamageSound, coords, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - playedSound = true; - } - } - - // Fallback to generic sounds. - if (!playedSound) - { - switch (type) - { - // Unfortunately heat returns caustic group so can't just use the damagegroup in that instance. - case "Burn": - case "Heat": - case "Radiation": - case "Cold": - Audio.PlayPredicted(new SoundPathSpecifier("/Audio/Items/welder.ogg"), target, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - break; - // No damage, fallback to tappies - case null: - Audio.PlayPredicted(new SoundCollectionSpecifier("WeakHit"), target, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - break; - case "Brute": - Audio.PlayPredicted(new SoundCollectionSpecifier("MetalThud"), target, user, AudioParams.Default.WithVariation(DamagePitchVariation)); - break; - } - } - } public static string? GetHighestDamageSound(DamageSpecifier modifiedDamage, IPrototypeManager protoManager) { @@ -809,7 +738,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem } // Play a sound to give instant feedback; same with playing the animations - Audio.PlayPredicted(component.SwingSound, meleeUid, user); + _meleeSound.PlaySwingSound(user, meleeUid, component); return true; }