Clumsy system refactor (#31147)

* First commit

* Fixes

* Added the noise

* Renames

* Timespan

* Fixed space

* entity -> ent

* This shouldn't work

* opps....

* Datafield name change

* Better comments

* small comment

* Personal skill issue

* Event renames and stuff

* Couple fixes

* Defib ref fixes (Silly me)

* Added clumsy back!

* no hard code clumsy!

* Identity fix

* Event name change

* Comment change

* Function name change

* opp

* Update names

* Damage stuff!

* Fixes!

* Fixes

* opps

* This was hidden away!!

* negative diff feeds me
This commit is contained in:
beck-thompson
2024-11-15 15:46:01 -08:00
committed by GitHub
parent 09d0565413
commit da4fa9bea9
25 changed files with 485 additions and 282 deletions

View File

@@ -11,11 +11,6 @@ public sealed partial class HyposprayComponent : Component
[DataField]
public string SolutionName = "hypospray";
// TODO: This should be on clumsycomponent.
[DataField]
[ViewVariables(VVAccess.ReadWrite)]
public float ClumsyFailChance = 0.5f;
[DataField]
[ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 TransferAmount = FixedPoint2.New(5);

View File

@@ -0,0 +1,38 @@
using Content.Shared.Inventory;
namespace Content.Shared.Chemistry.Hypospray.Events;
public abstract partial class BeforeHyposprayInjectsTargetEvent : CancellableEntityEventArgs, IInventoryRelayEvent
{
public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
public EntityUid EntityUsingHypospray;
public readonly EntityUid Hypospray;
public EntityUid TargetGettingInjected;
public string? InjectMessageOverride;
public BeforeHyposprayInjectsTargetEvent(EntityUid user, EntityUid hypospray, EntityUid target)
{
EntityUsingHypospray = user;
Hypospray = hypospray;
TargetGettingInjected = target;
InjectMessageOverride = null;
}
}
/// <summary>
/// This event is raised on the user using the hypospray before the hypospray is injected.
/// The event is triggered on the user and all their clothing.
/// </summary>
public sealed class SelfBeforeHyposprayInjectsEvent : BeforeHyposprayInjectsTargetEvent
{
public SelfBeforeHyposprayInjectsEvent(EntityUid user, EntityUid hypospray, EntityUid target) : base(user, hypospray, target) { }
}
/// <summary>
/// This event is raised on the target before the hypospray is injected.
/// The event is triggered on the target itself and all its clothing.
/// </summary>
public sealed class TargetBeforeHyposprayInjectsEvent : BeforeHyposprayInjectsTargetEvent
{
public TargetBeforeHyposprayInjectsEvent (EntityUid user, EntityUid hypospray, EntityUid target) : base(user, hypospray, target) { }
}

View File

@@ -1,5 +1,4 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Shared.Climbing.Components;
@@ -8,39 +7,18 @@ namespace Content.Shared.Climbing.Components;
/// Makes entity do damage and stun entities with ClumsyComponent
/// upon DragDrop or Climb interactions.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(Systems.BonkSystem))]
[RegisterComponent, NetworkedComponent]
public sealed partial class BonkableComponent : Component
{
/// <summary>
/// Chance of bonk triggering if the user is clumsy.
/// How long to stun players on bonk, in seconds.
/// </summary>
[DataField("bonkClumsyChance")]
public float BonkClumsyChance = 0.5f;
[DataField]
public TimeSpan BonkTime = TimeSpan.FromSeconds(2);
/// <summary>
/// Sound to play when bonking.
/// How much damage to apply on bonk.
/// </summary>
/// <seealso cref="Bonk"/>
[DataField("bonkSound")]
public SoundSpecifier? BonkSound;
/// <summary>
/// How long to stun players on bonk, in seconds.
/// </summary>
/// <seealso cref="Bonk"/>
[DataField("bonkTime")]
public float BonkTime = 2;
/// <summary>
/// How much damage to apply on bonk.
/// </summary>
/// <seealso cref="Bonk"/>
[DataField("bonkDamage")]
[DataField]
public DamageSpecifier? BonkDamage;
/// <summary>
/// How long it takes to bonk.
/// </summary>
[DataField("bonkDelay")]
public float BonkDelay = 1.5f;
}

View File

@@ -0,0 +1,36 @@
using Content.Shared.Inventory;
using Content.Shared.Climbing.Components;
namespace Content.Shared.Climbing.Events;
public abstract partial class BeforeClimbEvent : CancellableEntityEventArgs
{
public readonly EntityUid GettingPutOnTable;
public readonly EntityUid PuttingOnTable;
public readonly Entity<ClimbableComponent> BeingClimbedOn;
public BeforeClimbEvent(EntityUid gettingPutOntable, EntityUid puttingOnTable, Entity<ClimbableComponent> beingClimbedOn)
{
GettingPutOnTable = gettingPutOntable;
PuttingOnTable = puttingOnTable;
BeingClimbedOn = beingClimbedOn;
}
}
/// <summary>
/// This event is raised on the the person either getting put on or going on the table.
/// The event is also called on their clothing as well.
/// </summary>
public sealed class SelfBeforeClimbEvent : BeforeClimbEvent, IInventoryRelayEvent
{
public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
public SelfBeforeClimbEvent(EntityUid gettingPutOntable, EntityUid puttingOnTable, Entity<ClimbableComponent> beingClimbedOn) : base(gettingPutOntable, puttingOnTable, beingClimbedOn) { }
}
/// <summary>
/// This event is raised on the thing being climbed on.
/// </summary>
public sealed class TargetBeforeClimbEvent : BeforeClimbEvent
{
public TargetBeforeClimbEvent(EntityUid gettingPutOntable, EntityUid puttingOnTable, Entity<ClimbableComponent> beingClimbedOn) : base(gettingPutOntable, puttingOnTable, beingClimbedOn) { }
}

View File

@@ -1,130 +0,0 @@
using Content.Shared.CCVar;
using Content.Shared.Climbing.Components;
using Content.Shared.Climbing.Events;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.DragDrop;
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Components;
using Content.Shared.Popups;
using Content.Shared.Stunnable;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Serialization;
namespace Content.Shared.Climbing.Systems;
public sealed partial class BonkSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BonkableComponent, BonkDoAfterEvent>(OnBonkDoAfter);
SubscribeLocalEvent<BonkableComponent, AttemptClimbEvent>(OnAttemptClimb);
}
private void OnBonkDoAfter(EntityUid uid, BonkableComponent component, BonkDoAfterEvent args)
{
if (args.Handled || args.Cancelled || args.Args.Used == null)
return;
TryBonk(args.Args.Used.Value, uid, component, source: args.Args.User);
args.Handled = true;
}
public bool TryBonk(EntityUid user, EntityUid bonkableUid, BonkableComponent? bonkableComponent = null, EntityUid? source = null)
{
if (!Resolve(bonkableUid, ref bonkableComponent, false))
return false;
// BONK!
var userName = Identity.Entity(user, EntityManager);
var bonkableName = Identity.Entity(bonkableUid, EntityManager);
if (user == source)
{
// Non-local, non-bonking players
var othersMessage = Loc.GetString("bonkable-success-message-others", ("user", userName), ("bonkable", bonkableName));
// Local, bonking player
var selfMessage = Loc.GetString("bonkable-success-message-user", ("user", userName), ("bonkable", bonkableName));
_popupSystem.PopupPredicted(selfMessage, othersMessage, user, user);
}
else if (source != null)
{
// Local, non-bonking player (dragger)
_popupSystem.PopupClient(Loc.GetString("bonkable-success-message-others", ("user", userName), ("bonkable", bonkableName)), user, source.Value);
// Non-local, non-bonking players
_popupSystem.PopupEntity(Loc.GetString("bonkable-success-message-others", ("user", userName), ("bonkable", bonkableName)), user, Filter.Pvs(user).RemoveWhereAttachedEntity(e => e == user || e == source.Value), true);
// Non-local, bonking player
_popupSystem.PopupEntity(Loc.GetString("bonkable-success-message-user", ("user", userName), ("bonkable", bonkableName)), user, user);
}
if (source != null)
_audioSystem.PlayPredicted(bonkableComponent.BonkSound, bonkableUid, source);
else
_audioSystem.PlayPvs(bonkableComponent.BonkSound, bonkableUid);
_stunSystem.TryParalyze(user, TimeSpan.FromSeconds(bonkableComponent.BonkTime), true);
if (bonkableComponent.BonkDamage is { } bonkDmg)
_damageableSystem.TryChangeDamage(user, bonkDmg, true, origin: user);
return true;
}
private bool TryStartBonk(EntityUid uid, EntityUid user, EntityUid climber, BonkableComponent? bonkableComponent = null)
{
if (!Resolve(uid, ref bonkableComponent, false))
return false;
if (!HasComp<ClumsyComponent>(climber) || !HasComp<HandsComponent>(user))
return false;
if (!_cfg.GetCVar(CCVars.GameTableBonk))
{
// Not set to always bonk, try clumsy roll.
if (!_interactionSystem.TryRollClumsy(climber, bonkableComponent.BonkClumsyChance))
return false;
}
var doAfterArgs = new DoAfterArgs(EntityManager, user, bonkableComponent.BonkDelay, new BonkDoAfterEvent(), uid, target: uid, used: climber)
{
BreakOnMove = true,
BreakOnDamage = true,
DuplicateCondition = DuplicateConditions.SameTool | DuplicateConditions.SameTarget
};
return _doAfter.TryStartDoAfter(doAfterArgs);
}
private void OnAttemptClimb(EntityUid uid, BonkableComponent component, ref AttemptClimbEvent args)
{
if (args.Cancelled)
return;
if (TryStartBonk(uid, args.User, args.Climber, component))
args.Cancelled = true;
}
[Serializable, NetSerializable]
private sealed partial class BonkDoAfterEvent : SimpleDoAfterEvent
{
}
}

View File

@@ -251,6 +251,18 @@ public sealed partial class ClimbSystem : VirtualController
if (!Resolve(climbable, ref comp, false))
return;
var selfEvent = new SelfBeforeClimbEvent(uid, user, (climbable, comp));
RaiseLocalEvent(uid, selfEvent);
if (selfEvent.Cancelled)
return;
var targetEvent = new TargetBeforeClimbEvent(uid, user, (climbable, comp));
RaiseLocalEvent(climbable, targetEvent);
if (targetEvent.Cancelled)
return;
if (!ReplaceFixtures(uid, climbing, fixtures))
return;

View File

@@ -0,0 +1,61 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Shared.Clumsy;
/// <summary>
/// A simple clumsy tag-component.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ClumsyComponent : Component
{
// Standard options. Try to fit these in if you can!
/// <summary>
/// Sound to play when clumsy interactions fail.
/// </summary>
[DataField]
public SoundSpecifier ClumsySound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg");
/// <summary>
/// Default chance to fail a clumsy interaction.
/// If a system needs to use something else, add a new variable in the component, do not modify this percentage.
/// </summary>
[DataField, AutoNetworkedField]
public float ClumsyDefaultCheck = 0.5f;
/// <summary>
/// Default stun time.
/// If a system needs to use something else, add a new variable in the component, do not modify this number.
/// </summary>
[DataField, AutoNetworkedField]
public TimeSpan ClumsyDefaultStunTime = TimeSpan.FromSeconds(2.5);
// Specific options
/// <summary>
/// Sound to play after hitting your head on a table. Ouch!
/// </summary>
[DataField]
public SoundCollectionSpecifier TableBonkSound = new SoundCollectionSpecifier("TrayHit");
/// <summary>
/// Stun time after failing to shoot a gun.
/// </summary>
[DataField, AutoNetworkedField]
public TimeSpan GunShootFailStunTime = TimeSpan.FromSeconds(3);
/// <summary>
/// Stun time after failing to shoot a gun.
/// </summary>
[DataField, AutoNetworkedField]
public DamageSpecifier? GunShootFailDamage;
/// <summary>
/// Noise to play after failing to shoot a gun. Boom!
/// </summary>
[DataField]
public SoundSpecifier GunShootFailSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg");
}

View File

@@ -0,0 +1,146 @@
using Content.Shared.CCVar;
using Content.Shared.Chemistry.Hypospray.Events;
using Content.Shared.Climbing.Components;
using Content.Shared.Climbing.Events;
using Content.Shared.Damage;
using Content.Shared.IdentityManagement;
using Content.Shared.Medical;
using Content.Shared.Popups;
using Content.Shared.Stunnable;
using Content.Shared.Weapons.Ranged.Events;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Shared.Clumsy;
public sealed class ClumsySystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedStunSystem _stun = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
public override void Initialize()
{
SubscribeLocalEvent<ClumsyComponent, SelfBeforeHyposprayInjectsEvent>(BeforeHyposprayEvent);
SubscribeLocalEvent<ClumsyComponent, SelfBeforeDefibrillatorZapsEvent>(BeforeDefibrillatorZapsEvent);
SubscribeLocalEvent<ClumsyComponent, SelfBeforeGunShotEvent>(BeforeGunShotEvent);
SubscribeLocalEvent<ClumsyComponent, SelfBeforeClimbEvent>(OnBeforeClimbEvent);
}
// If you add more clumsy interactions add them in this section!
#region Clumsy interaction events
private void BeforeHyposprayEvent(Entity<ClumsyComponent> ent, ref SelfBeforeHyposprayInjectsEvent args)
{
// Clumsy people sometimes inject themselves! Apparently syringes are clumsy proof...
if (!_random.Prob(ent.Comp.ClumsyDefaultCheck))
return;
args.TargetGettingInjected = args.EntityUsingHypospray;
args.InjectMessageOverride = "hypospray-component-inject-self-clumsy-message";
_audio.PlayPvs(ent.Comp.ClumsySound, ent);
}
private void BeforeDefibrillatorZapsEvent(Entity<ClumsyComponent> ent, ref SelfBeforeDefibrillatorZapsEvent args)
{
// Clumsy people sometimes defib themselves!
if (!_random.Prob(ent.Comp.ClumsyDefaultCheck))
return;
args.DefibTarget = args.EntityUsingDefib;
_audio.PlayPvs(ent.Comp.ClumsySound, ent);
}
private void BeforeGunShotEvent(Entity<ClumsyComponent> ent, ref SelfBeforeGunShotEvent args)
{
// Clumsy people sometimes can't shoot :(
if (args.Gun.Comp.ClumsyProof)
return;
if (!_random.Prob(ent.Comp.ClumsyDefaultCheck))
return;
if (ent.Comp.GunShootFailDamage != null)
_damageable.TryChangeDamage(ent, ent.Comp.GunShootFailDamage, origin: ent);
_stun.TryParalyze(ent, ent.Comp.GunShootFailStunTime, true);
// Apply salt to the wound ("Honk!") (No idea what this comment means)
_audio.PlayPvs(ent.Comp.GunShootFailSound, ent);
_audio.PlayPvs(ent.Comp.ClumsySound, ent);
_popup.PopupEntity(Loc.GetString("gun-clumsy"), ent, ent);
args.Cancel();
}
private void OnBeforeClimbEvent(Entity<ClumsyComponent> ent, ref SelfBeforeClimbEvent args)
{
// This event is called in shared, thats why it has all the extra prediction stuff.
var rand = new System.Random((int)_timing.CurTick.Value);
// If someone is putting you on the table, always get past the guard.
if (!_cfg.GetCVar(CCVars.GameTableBonk) && args.PuttingOnTable == ent.Owner && !rand.Prob(ent.Comp.ClumsyDefaultCheck))
return;
HitHeadClumsy(ent, args.BeingClimbedOn);
_audio.PlayPredicted(ent.Comp.ClumsySound, ent, ent);
_audio.PlayPredicted(ent.Comp.TableBonkSound, ent, ent);
var gettingPutOnTableName = Identity.Entity(args.GettingPutOnTable, EntityManager);
var puttingOnTableName = Identity.Entity(args.PuttingOnTable, EntityManager);
if (args.PuttingOnTable == ent.Owner)
{
// You are slamming yourself onto the table.
_popup.PopupPredicted(
Loc.GetString("bonkable-success-message-user", ("bonkable", args.BeingClimbedOn)),
Loc.GetString("bonkable-success-message-others", ("victim", gettingPutOnTableName), ("bonkable", args.BeingClimbedOn)),
ent,
ent);
}
else
{
// Someone else slamed you onto the table.
// This is only run in server so you need to use popup entity.
_popup.PopupPredicted(
Loc.GetString("forced-bonkable-success-message",
("bonker", puttingOnTableName),
("victim", gettingPutOnTableName),
("bonkable", args.BeingClimbedOn)),
ent,
null);
}
args.Cancel();
}
#endregion
#region Helper functions
/// <summary>
/// "Hits" an entites head against the given table.
/// </summary>
// Oh this fucntion is public le- NO!! This is only public for the one admin command if you use this anywhere else I will cry.
public void HitHeadClumsy(Entity<ClumsyComponent> target, EntityUid table)
{
var stunTime = target.Comp.ClumsyDefaultStunTime;
if (TryComp<BonkableComponent>(table, out var bonkComp))
{
stunTime = bonkComp.BonkTime;
if (bonkComp.BonkDamage != null)
_damageable.TryChangeDamage(target, bonkComp.BonkDamage, true);
}
_stun.TryParalyze(target, stunTime, true);
}
#endregion
}

View File

@@ -1,24 +0,0 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Shared.Interaction.Components;
/// <summary>
/// A simple clumsy tag-component.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ClumsyComponent : Component
{
/// <summary>
/// Damage dealt to a clumsy character when they try to fire a gun.
/// </summary>
[DataField(required: true), AutoNetworkedField]
public DamageSpecifier ClumsyDamage = default!;
/// <summary>
/// Sound to play when clumsy interactions fail.
/// </summary>
[DataField]
public SoundSpecifier ClumsySound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg");
}

View File

@@ -1,26 +0,0 @@
using Content.Shared.Interaction.Components;
using Robust.Shared.Random;
namespace Content.Shared.Interaction
{
public partial class SharedInteractionSystem
{
public bool RollClumsy(ClumsyComponent component, float chance)
{
return component.Running && _random.Prob(chance);
}
/// <summary>
/// Rolls a probability chance for a "bad action" if the target entity is clumsy.
/// </summary>
/// <param name="entity">The entity that the clumsy check is happening for.</param>
/// <param name="chance">
/// The chance that a "bad action" happens if the user is clumsy, between 0 and 1 inclusive.
/// </param>
/// <returns>True if a "bad action" happened, false if the normal action should happen.</returns>
public bool TryRollClumsy(EntityUid entity, float chance, ClumsyComponent? component = null)
{
return Resolve(entity, ref component, false) && RollClumsy(component, chance);
}
}
}

View File

@@ -1,4 +1,7 @@
using Content.Shared.Chat;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Hypospray.Events;
using Content.Shared.Climbing.Events;
using Content.Shared.Damage;
using Content.Shared.Electrocution;
using Content.Shared.Explosion;
@@ -15,7 +18,7 @@ using Content.Shared.Slippery;
using Content.Shared.Strip.Components;
using Content.Shared.Temperature;
using Content.Shared.Verbs;
using Content.Shared.Chat;
using Content.Shared.Weapons.Ranged.Events;
namespace Content.Shared.Inventory;
@@ -33,6 +36,10 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshNameModifiersEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, TransformSpeakerNameEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, SelfBeforeHyposprayInjectsEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, TargetBeforeHyposprayInjectsEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, SelfBeforeGunShotEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, SelfBeforeClimbEvent>(RelayInventoryEvent);
// by-ref events
SubscribeLocalEvent<InventoryComponent, GetExplosionResistanceEvent>(RefRelayInventoryEvent);

View File

@@ -0,0 +1,39 @@
using Content.Shared.Inventory;
namespace Content.Shared.Medical;
[ByRefEvent]
public readonly record struct TargetDefibrillatedEvent(EntityUid User, Entity<DefibrillatorComponent> Defibrillator);
public abstract class BeforeDefibrillatorZapsEvent : CancellableEntityEventArgs, IInventoryRelayEvent
{
public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
public EntityUid EntityUsingDefib;
public readonly EntityUid Defib;
public EntityUid DefibTarget;
public BeforeDefibrillatorZapsEvent(EntityUid entityUsingDefib, EntityUid defib, EntityUid defibTarget)
{
EntityUsingDefib = entityUsingDefib;
Defib = defib;
DefibTarget = defibTarget;
}
}
/// <summary>
/// This event is raised on the user using the defibrillator before is actually zaps someone.
/// The event is triggered on the user and all their clothing.
/// </summary>
public sealed class SelfBeforeDefibrillatorZapsEvent : BeforeDefibrillatorZapsEvent
{
public SelfBeforeDefibrillatorZapsEvent(EntityUid entityUsingDefib, EntityUid defib, EntityUid defibtarget) : base(entityUsingDefib, defib, defibtarget) { }
}
/// <summary>
/// This event is raised on the target before it gets zapped with the defibrillator.
/// The event is triggered on the target itself and all its clothing.
/// </summary>
public sealed class TargetBeforeDefibrillatorZapsEvent : BeforeDefibrillatorZapsEvent
{
public TargetBeforeDefibrillatorZapsEvent(EntityUid entityUsingDefib, EntityUid defib, EntityUid defibtarget) : base(entityUsingDefib, defib, defibtarget) { }
}

View File

@@ -1,4 +0,0 @@
namespace Content.Shared.Medical;
[ByRefEvent]
public readonly record struct TargetDefibrillatedEvent(EntityUid User, Entity<DefibrillatorComponent> Defibrillator);

View File

@@ -0,0 +1,20 @@
using Content.Shared.Inventory;
using Content.Shared.Weapons.Ranged.Components;
namespace Content.Shared.Weapons.Ranged.Events;
/// <summary>
/// This event is triggered on an entity right before they shoot a gun.
/// </summary>
public sealed partial class SelfBeforeGunShotEvent : CancellableEntityEventArgs, IInventoryRelayEvent
{
public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
public readonly EntityUid Shooter;
public readonly Entity<GunComponent> Gun;
public readonly List<(EntityUid? Entity, IShootable Shootable)> Ammo;
public SelfBeforeGunShotEvent(EntityUid shooter, Entity<GunComponent> gun, List<(EntityUid? Entity, IShootable Shootable)> ammo)
{
Shooter = shooter;
Gun = gun;
Ammo = ammo;
}
}