diff --git a/Modules/GoobStation/Content.Goobstation.Shared/Damage/Components/DestroyInteractingsComponent.cs b/Modules/GoobStation/Content.Goobstation.Shared/Damage/Components/DestroyInteractingsComponent.cs new file mode 100644 index 0000000000..79ecba00a9 --- /dev/null +++ b/Modules/GoobStation/Content.Goobstation.Shared/Damage/Components/DestroyInteractingsComponent.cs @@ -0,0 +1,64 @@ +using Content.Shared.Whitelist; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Goobstation.Common.Damage; + +/// +/// Goobstation +/// Make entity destroy other entities on interaction +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class DestroyInteractingsComponent : Component +{ + #region Logic Handlers + /// + /// Whitelist entities that this entity can destroy + /// + [DataField] + public EntityWhitelist? DestroyWhitelist; + + /// + /// Marks entities that can not be destroed by this entity + /// + [DataField] + public EntityWhitelist? DestroyBlacklist; + + /// + /// Should entity count contacts as interaction + /// + [DataField] + public bool RespectContacts = false; + + /// + /// Should entity try interaction with item in hand first + /// + [DataField] + public bool RespectHandInteraction = true; + + /// + /// The colliding fixture + /// + [DataField] + public string FixtureId = "bzzz"; + + #endregion + + #region Visuals and Sounds + /// + /// Sound that should be played on destruction coordinates + /// + [DataField] + public SoundSpecifier? DestructionSound; + + /// + /// Entity that should instead of destroyed entity + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string SpawnOnDestruction = string.Empty; + + //TODO:Particles + #endregion +} \ No newline at end of file diff --git a/Modules/GoobStation/Content.Goobstation.Shared/Damage/Systems/SharedDestroyInteractingsSystem.cs b/Modules/GoobStation/Content.Goobstation.Shared/Damage/Systems/SharedDestroyInteractingsSystem.cs new file mode 100644 index 0000000000..5cc7eb97c8 --- /dev/null +++ b/Modules/GoobStation/Content.Goobstation.Shared/Damage/Systems/SharedDestroyInteractingsSystem.cs @@ -0,0 +1,78 @@ +using Content.Goobstation.Common.Damage; +using Content.Shared.Destructible; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction; +using Content.Shared.Weapons.Melee.Events; +using Content.Shared.Whitelist; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Physics.Events; +using Robust.Shared.Timing; + +namespace Content.Goobstation.Shared.Damage; + +public sealed partial class SharedDestroyInteractinngSystem : EntitySystem +{ + [Dependency] private IGameTiming _timing = default!; + [Dependency] private SharedAudioSystem _audioSystem = default!; + [Dependency] private SharedDestructibleSystem _destructibleSystem = default!; + [Dependency] private SharedHandsSystem _handsSystem = default!; + [Dependency] private EntityWhitelistSystem _whitelistSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnCollide); + SubscribeLocalEvent(OnAttacked); + } + + private bool TryDestroyEntity(EntityUid entity, Entity destructor, out EntityUid? spawned, EntityUid? user = null) + { + spawned = null; + if (!_whitelistSystem.CheckBoth(entity, destructor.Comp.DestroyBlacklist, destructor.Comp.DestroyWhitelist)) + return false; + + var position = Transform(entity).Coordinates; + if (!_destructibleSystem.DestroyEntity(entity)) + return false; + + _audioSystem.PlayPredicted(destructor.Comp.DestructionSound, destructor, user); + + if (destructor.Comp.SpawnOnDestruction != string.Empty) + spawned = PredictedSpawnAtPosition(destructor.Comp.SpawnOnDestruction, position); + + return true; + } + + #region Event Handlers + private void OnInteractUsing(Entity destructor, ref InteractUsingEvent args) + { + var target = destructor.Comp.RespectHandInteraction ? args.Used : args.User; + args.Handled = TryDestroyEntity(target, destructor, out var spawned, args.User); + + if (spawned is not { } spawnedEnt) + return; + + _handsSystem.TryPickup(args.User, spawnedEnt, animate: false); + } + + private void OnInteractHand(Entity destructor, ref InteractHandEvent args) + { + args.Handled = TryDestroyEntity(args.User, destructor, out _, args.User); + } + + private void OnCollide(Entity destructor, ref StartCollideEvent args) + { + if (destructor.Comp.RespectContacts && destructor.Comp.FixtureId == args.OurFixtureId && args.OtherFixture.Hard && args.OurFixture.Hard) + TryDestroyEntity(args.OtherEntity, destructor, out _); + } + + private void OnAttacked(Entity destructor, ref AttackedEvent args) + { + TryDestroyEntity(args.Used, destructor, out _, user: args.User); + } + + #endregion +} \ No newline at end of file