2021-03-05 07:23:09 +00:00
using System ;
2020-06-22 05:47:15 +10:00
using System.Collections.Generic ;
using System.Linq ;
2020-08-18 14:39:08 +02:00
using System.Threading.Tasks ;
2021-11-29 02:34:44 +13:00
using Content.Server.Administration.Logs ;
2021-06-09 22:19:39 +02:00
using Content.Server.Camera ;
using Content.Server.Projectiles.Components ;
using Content.Server.Weapon.Ranged.Ammunition.Components ;
2021-09-15 03:07:37 +10:00
using Content.Shared.Damage ;
2021-11-28 14:56:53 +01:00
using Content.Shared.Database ;
2021-06-09 22:19:39 +02:00
using Content.Shared.Examine ;
using Content.Shared.Interaction ;
2021-07-10 17:35:33 +02:00
using Content.Shared.Sound ;
2021-06-09 22:19:39 +02:00
using Content.Shared.Weapons.Ranged.Components ;
2020-06-22 05:47:15 +10:00
using Robust.Shared.Audio ;
2020-09-13 14:23:52 +02:00
using Robust.Shared.GameObjects ;
2020-06-22 05:47:15 +10:00
using Robust.Shared.IoC ;
2020-07-24 14:18:37 +02:00
using Robust.Shared.Localization ;
2020-06-22 05:47:15 +10:00
using Robust.Shared.Log ;
using Robust.Shared.Map ;
using Robust.Shared.Maths ;
2021-02-11 01:13:03 -08:00
using Robust.Shared.Physics ;
2021-03-16 15:50:20 +01:00
using Robust.Shared.Player ;
2020-06-22 05:47:15 +10:00
using Robust.Shared.Prototypes ;
using Robust.Shared.Random ;
using Robust.Shared.Serialization ;
2021-03-05 01:08:38 +01:00
using Robust.Shared.Serialization.Manager.Attributes ;
2021-02-11 01:13:03 -08:00
using Robust.Shared.Timing ;
2020-06-22 05:47:15 +10:00
using Robust.Shared.Utility ;
2021-06-09 22:19:39 +02:00
namespace Content.Server.Weapon.Ranged.Barrels.Components
2020-06-22 05:47:15 +10:00
{
/// <summary>
/// All of the ranged weapon components inherit from this to share mechanics like shooting etc.
/// Only difference between them is how they retrieve a projectile to shoot (battery, magazine, etc.)
/// </summary>
2021-10-27 18:10:40 +02:00
#pragma warning disable 618
2021-12-05 16:23:47 +13:00
public abstract class ServerRangedBarrelComponent : SharedRangedBarrelComponent , IExamine , ISerializationHooks
2021-10-27 18:10:40 +02:00
#pragma warning restore 618
2020-06-22 05:47:15 +10:00
{
// There's still some of py01 and PJB's work left over, especially in underlying shooting logic,
// it's just when I re-organised it changed me as the contributor
2020-08-24 14:10:28 +02:00
[Dependency] private readonly IGameTiming _gameTiming = default ! ;
[Dependency] private readonly IRobustRandom _robustRandom = default ! ;
2021-12-23 15:46:43 +11:00
[Dependency] protected readonly IEntityManager Entities = default ! ;
2020-06-24 20:00:31 +02:00
2020-06-22 05:47:15 +10:00
public override FireRateSelector FireRateSelector = > _fireRateSelector ;
2021-03-05 01:08:38 +01:00
[DataField("currentSelector")]
private FireRateSelector _fireRateSelector = FireRateSelector . Safety ;
2020-06-22 05:47:15 +10:00
public override FireRateSelector AllRateSelectors = > _fireRateSelector ;
2021-03-05 01:08:38 +01:00
2021-05-04 15:37:16 +02:00
[DataField("fireRate")]
2021-03-05 01:08:38 +01:00
public override float FireRate { get ; } = 2f ;
2020-06-24 20:00:31 +02:00
2020-06-22 05:47:15 +10:00
// _lastFire is when we actually fired (so if we hold the button then recoil doesn't build up if we're not firing)
private TimeSpan _lastFire ;
2020-06-24 20:00:31 +02:00
2021-12-06 00:52:58 +01:00
public abstract EntityUid ? PeekAmmo ( ) ;
public abstract EntityUid ? TakeProjectile ( EntityCoordinates spawnAt ) ;
2020-06-22 05:47:15 +10:00
// Recoil / spray control
2021-03-05 01:08:38 +01:00
[DataField("minAngle")]
private float _minAngleDegrees ;
public Angle MinAngle { get ; private set ; }
[DataField("maxAngle")]
private float _maxAngleDegrees = 45 ;
public Angle MaxAngle { get ; private set ; }
2020-06-22 05:47:15 +10:00
private Angle _currentAngle = Angle . Zero ;
2021-03-05 01:08:38 +01:00
[DataField("angleDecay")]
private float _angleDecayDegrees = 20 ;
2020-06-22 05:47:15 +10:00
/// <summary>
/// How slowly the angle's theta decays per second in radians
/// </summary>
2021-03-05 01:08:38 +01:00
public float AngleDecay { get ; private set ; }
[DataField("angleIncrease")]
private float? _angleIncreaseDegrees ;
2020-06-22 05:47:15 +10:00
/// <summary>
/// How quickly the angle's theta builds for every shot fired in radians
/// </summary>
2021-03-05 01:08:38 +01:00
public float AngleIncrease { get ; private set ; }
2020-06-22 05:47:15 +10:00
// Multiplies the ammo spread to get the final spread of each pellet
2021-03-05 01:08:38 +01:00
[DataField("ammoSpreadRatio")]
public float SpreadRatio { get ; private set ; }
2020-06-22 05:47:15 +10:00
2021-05-04 15:37:16 +02:00
[DataField("canMuzzleFlash")]
2021-03-05 01:08:38 +01:00
public bool CanMuzzleFlash { get ; } = true ;
2020-06-22 05:47:15 +10:00
// Sounds
2021-07-31 19:52:33 +02:00
[DataField("soundGunshot", required: true)]
2021-07-10 17:35:33 +02:00
public SoundSpecifier SoundGunshot { get ; set ; } = default ! ;
2021-03-05 01:08:38 +01:00
2021-05-04 15:37:16 +02:00
[DataField("soundEmpty")]
2021-07-10 17:35:33 +02:00
public SoundSpecifier SoundEmpty { get ; } = new SoundPathSpecifier ( "/Audio/Weapons/Guns/Empty/empty.ogg" ) ;
2021-03-05 01:08:38 +01:00
void ISerializationHooks . BeforeSerialization ( )
2020-06-22 05:47:15 +10:00
{
2021-03-05 01:08:38 +01:00
_minAngleDegrees = ( float ) ( MinAngle . Degrees * 2 ) ;
_maxAngleDegrees = ( float ) ( MaxAngle . Degrees * 2 ) ;
_angleIncreaseDegrees = MathF . Round ( AngleIncrease / ( ( float ) Math . PI / 180f ) , 2 ) ;
AngleDecay = MathF . Round ( AngleDecay / ( ( float ) Math . PI / 180f ) , 2 ) ;
2020-06-22 05:47:15 +10:00
}
2021-03-05 01:08:38 +01:00
void ISerializationHooks . AfterDeserialization ( )
2020-06-22 05:47:15 +10:00
{
// This hard-to-read area's dealing with recoil
// Use degrees in yaml as it's easier to read compared to "0.0125f"
2021-03-05 01:08:38 +01:00
MinAngle = Angle . FromDegrees ( _minAngleDegrees / 2f ) ;
2020-07-23 01:46:09 +02:00
// Random doubles it as it's +/- so uhh we'll just half it here for readability
2021-03-05 01:08:38 +01:00
MaxAngle = Angle . FromDegrees ( _maxAngleDegrees / 2f ) ;
2020-07-23 01:46:09 +02:00
2021-03-05 01:08:38 +01:00
_angleIncreaseDegrees ? ? = 40 / FireRate ;
AngleIncrease = _angleIncreaseDegrees . Value * ( float ) Math . PI / 180f ;
2020-07-23 01:46:09 +02:00
2021-03-05 01:08:38 +01:00
AngleDecay = _angleDecayDegrees * ( float ) Math . PI / 180f ;
2020-06-22 05:47:15 +10:00
// For simplicity we'll enforce it this way; ammo determines max spread
2021-03-05 01:08:38 +01:00
if ( SpreadRatio > 1.0f )
2020-06-22 05:47:15 +10:00
{
Logger . Error ( "SpreadRatio must be <= 1.0f for guns" ) ;
throw new InvalidOperationException ( ) ;
}
}
2021-12-05 16:23:47 +13:00
protected override void Initialize ( )
2020-06-22 05:47:15 +10:00
{
2021-12-05 16:23:47 +13:00
base . Initialize ( ) ;
2020-08-24 13:39:00 +02:00
2020-12-04 13:26:54 +01:00
Owner . EnsureComponentWarn ( out ServerRangedWeaponComponent rangedWeaponComponent ) ;
2020-08-22 22:29:20 +02:00
rangedWeaponComponent . Barrel ? ? = this ;
2020-08-11 16:44:15 -07:00
rangedWeaponComponent . FireHandler + = Fire ;
rangedWeaponComponent . WeaponCanFireHandler + = WeaponCanFire ;
2020-06-22 05:47:15 +10:00
}
2021-06-19 19:41:26 -07:00
protected override void OnRemove ( )
2020-06-22 05:47:15 +10:00
{
base . OnRemove ( ) ;
2021-12-23 15:46:43 +11:00
if ( Entities . TryGetComponent ( Owner , out ServerRangedWeaponComponent ? rangedWeaponComponent ) )
2020-06-25 01:31:04 +10:00
{
rangedWeaponComponent . Barrel = null ;
rangedWeaponComponent . FireHandler - = Fire ;
rangedWeaponComponent . WeaponCanFireHandler - = WeaponCanFire ;
}
2020-06-22 05:47:15 +10:00
}
private Angle GetRecoilAngle ( Angle direction )
{
var currentTime = _gameTiming . CurTime ;
var timeSinceLastFire = ( currentTime - _lastFire ) . TotalSeconds ;
2021-03-05 01:08:38 +01:00
var newTheta = MathHelper . Clamp ( _currentAngle . Theta + AngleIncrease - AngleDecay * timeSinceLastFire , MinAngle . Theta , MaxAngle . Theta ) ;
2020-06-22 05:47:15 +10:00
_currentAngle = new Angle ( newTheta ) ;
var random = ( _robustRandom . NextDouble ( ) - 0.5 ) * 2 ;
var angle = Angle . FromDegrees ( direction . Degrees + _currentAngle . Degrees * random ) ;
return angle ;
}
public void ChangeFireSelector ( FireRateSelector rateSelector )
{
if ( ( rateSelector & AllRateSelectors ) ! = 0 )
{
_fireRateSelector = rateSelector ;
return ;
}
throw new InvalidOperationException ( ) ;
}
protected virtual bool WeaponCanFire ( )
{
// If the ServerRangedWeaponComponent gets re-done probably need to add the checks here
return true ;
}
2020-08-11 16:44:15 -07:00
/// <summary>
/// Fires a round of ammo out of the weapon.
/// </summary>
/// <param name="shooter">Entity that is operating the weapon, usually the player.</param>
/// <param name="targetPos">Target position on the map to shoot at.</param>
2021-12-05 18:09:01 +01:00
private void Fire ( EntityUid shooter , Vector2 targetPos )
2020-06-22 05:47:15 +10:00
{
if ( ShotsLeft = = 0 )
{
2021-08-18 23:36:57 +02:00
SoundSystem . Play ( Filter . Broadcast ( ) , SoundEmpty . GetSound ( ) , Owner ) ;
2020-06-22 05:47:15 +10:00
return ;
}
var ammo = PeekAmmo ( ) ;
2021-12-23 15:46:43 +11:00
if ( TakeProjectile ( Entities . GetComponent < TransformComponent > ( shooter ) . Coordinates ) is not { Valid : true } projectile )
2020-06-22 05:47:15 +10:00
{
2021-08-18 23:36:57 +02:00
SoundSystem . Play ( Filter . Broadcast ( ) , SoundEmpty . GetSound ( ) , Owner ) ;
2020-06-22 05:47:15 +10:00
return ;
}
// At this point firing is confirmed
2021-12-23 15:46:43 +11:00
var direction = ( targetPos - Entities . GetComponent < TransformComponent > ( shooter ) . WorldPosition ) . ToAngle ( ) ;
2020-06-22 05:47:15 +10:00
var angle = GetRecoilAngle ( direction ) ;
// This should really be client-side but for now we'll just leave it here
2021-12-23 15:46:43 +11:00
if ( Entities . TryGetComponent ( shooter , out CameraRecoilComponent ? recoilComponent ) )
2020-06-22 05:47:15 +10:00
{
recoilComponent . Kick ( - angle . ToVec ( ) * 0.15f ) ;
}
// This section probably needs tweaking so there can be caseless hitscan etc.
2021-12-23 15:46:43 +11:00
if ( Entities . TryGetComponent ( projectile , out HitscanComponent ? hitscan ) )
2020-06-22 05:47:15 +10:00
{
FireHitscan ( shooter , hitscan , angle ) ;
}
2021-12-23 15:46:43 +11:00
else if ( Entities . HasComponent < ProjectileComponent > ( projectile ) & &
Entities . TryGetComponent ( ammo , out AmmoComponent ? ammoComponent ) )
2020-06-22 05:47:15 +10:00
{
2021-12-06 00:52:58 +01:00
FireProjectiles ( shooter , projectile , ammoComponent . ProjectilesFired , ammoComponent . EvenSpreadAngle , angle , ammoComponent . Velocity , ammo . Value ) ;
2020-06-22 05:47:15 +10:00
if ( CanMuzzleFlash )
{
2021-12-23 15:46:43 +11:00
EntitySystem . Get < GunSystem > ( ) . MuzzleFlash ( Owner , ammoComponent , angle ) ;
2020-06-22 05:47:15 +10:00
}
if ( ammoComponent . Caseless )
{
2021-12-23 15:46:43 +11:00
Entities . DeleteEntity ( ammo . Value ) ;
2020-06-22 05:47:15 +10:00
}
}
else
{
// Invalid types
throw new InvalidOperationException ( ) ;
}
2020-06-24 20:00:31 +02:00
2021-08-18 23:36:57 +02:00
SoundSystem . Play ( Filter . Broadcast ( ) , SoundGunshot . GetSound ( ) , Owner ) ;
2020-06-22 05:47:15 +10:00
2021-03-16 15:50:20 +01:00
_lastFire = _gameTiming . CurTime ;
2020-06-22 05:47:15 +10:00
}
/// <summary>
/// Drops a single cartridge / shell
/// Made as a static function just because multiple places need it
/// </summary>
/// <param name="entity"></param>
/// <param name="playSound"></param>
/// <param name="robustRandom"></param>
/// <param name="prototypeManager"></param>
/// <param name="ejectDirections"></param>
2021-12-05 21:02:04 +01:00
public static void EjectCasing (
2021-12-05 18:09:01 +01:00
EntityUid entity ,
2020-06-22 05:47:15 +10:00
bool playSound = true ,
2021-12-05 21:02:04 +01:00
Direction [ ] ? ejectDirections = null ,
2021-03-16 15:50:20 +01:00
IRobustRandom ? robustRandom = null ,
IPrototypeManager ? prototypeManager = null ,
2021-12-05 21:02:04 +01:00
IEntityManager ? entities = null )
2020-06-22 05:47:15 +10:00
{
2021-12-05 21:02:04 +01:00
IoCManager . Resolve ( ref robustRandom , ref prototypeManager , ref entities ) ;
2021-03-16 15:50:20 +01:00
ejectDirections ? ? = new [ ]
{ Direction . East , Direction . North , Direction . NorthWest , Direction . South , Direction . SouthEast , Direction . West } ;
2020-06-24 20:00:31 +02:00
2021-03-05 07:23:09 +00:00
const float ejectOffset = 1.8f ;
2021-12-05 21:02:04 +01:00
var ammo = entities . GetComponent < AmmoComponent > ( entity ) ;
2021-03-05 07:23:09 +00:00
var offsetPos = ( ( robustRandom . NextFloat ( ) - 0.5f ) * ejectOffset , ( robustRandom . NextFloat ( ) - 0.5f ) * ejectOffset ) ;
2021-12-05 21:02:04 +01:00
entities . GetComponent < TransformComponent > ( entity ) . Coordinates = entities . GetComponent < TransformComponent > ( entity ) . Coordinates . Offset ( offsetPos ) ;
entities . GetComponent < TransformComponent > ( entity ) . LocalRotation = robustRandom . Pick ( ejectDirections ) . ToAngle ( ) ;
2020-06-22 05:47:15 +10:00
2021-12-05 21:02:04 +01:00
var coordinates = entities . GetComponent < TransformComponent > ( entity ) . Coordinates ;
SoundSystem . Play ( Filter . Broadcast ( ) , ammo . SoundCollectionEject . GetSound ( ) , coordinates , AudioParams . Default . WithVolume ( - 1 ) ) ;
2020-06-22 05:47:15 +10:00
}
/// <summary>
/// Drops multiple cartridges / shells on the floor
/// Wraps EjectCasing to make it less toxic for bulk ejections
/// </summary>
/// <param name="entities"></param>
2021-12-05 21:02:04 +01:00
public static void EjectCasings ( IEnumerable < EntityUid > entities )
2020-06-22 05:47:15 +10:00
{
var robustRandom = IoCManager . Resolve < IRobustRandom > ( ) ;
var prototypeManager = IoCManager . Resolve < IPrototypeManager > ( ) ;
2021-03-05 07:23:09 +00:00
var ejectDirections = new [ ] { Direction . East , Direction . North , Direction . NorthWest , Direction . South , Direction . SouthEast , Direction . West } ;
2020-06-22 05:47:15 +10:00
var soundPlayCount = 0 ;
var playSound = true ;
foreach ( var entity in entities )
{
2021-12-05 21:02:04 +01:00
EjectCasing ( entity , playSound , ejectDirections , robustRandom , prototypeManager ) ;
2020-06-22 05:47:15 +10:00
soundPlayCount + + ;
if ( soundPlayCount > 3 )
{
playSound = false ;
}
}
}
#region Firing
/// <summary>
/// Handles firing one or many projectiles
/// </summary>
2021-12-05 18:09:01 +01:00
private void FireProjectiles ( EntityUid shooter , EntityUid baseProjectile , int count , float evenSpreadAngle , Angle angle , float velocity , EntityUid ammo )
2020-06-22 05:47:15 +10:00
{
2021-03-16 15:50:20 +01:00
List < Angle > ? sprayAngleChange = null ;
2020-06-22 05:47:15 +10:00
if ( count > 1 )
{
2021-03-05 01:08:38 +01:00
evenSpreadAngle * = SpreadRatio ;
2020-06-22 05:47:15 +10:00
sprayAngleChange = Linspace ( - evenSpreadAngle / 2 , evenSpreadAngle / 2 , count ) ;
}
2021-12-01 12:40:00 +11:00
var firedProjectiles = new EntityUid [ count ] ;
2020-06-22 05:47:15 +10:00
for ( var i = 0 ; i < count ; i + + )
{
2021-12-05 18:09:01 +01:00
EntityUid projectile ;
2020-06-24 20:00:31 +02:00
2020-06-22 05:47:15 +10:00
if ( i = = 0 )
{
projectile = baseProjectile ;
}
else
{
2021-12-23 15:46:43 +11:00
projectile = Entities . SpawnEntity (
Entities . GetComponent < MetaDataComponent > ( baseProjectile ) . EntityPrototype ? . ID ,
Entities . GetComponent < TransformComponent > ( baseProjectile ) . Coordinates ) ;
2020-06-22 05:47:15 +10:00
}
2021-12-01 12:40:00 +11:00
2021-12-03 15:53:09 +01:00
firedProjectiles [ i ] = projectile ;
2020-06-22 05:47:15 +10:00
Angle projectileAngle ;
if ( sprayAngleChange ! = null )
{
projectileAngle = angle + sprayAngleChange [ i ] ;
}
else
{
projectileAngle = angle ;
}
2021-12-23 15:46:43 +11:00
var physics = Entities . GetComponent < IPhysBody > ( projectile ) ;
2021-03-08 04:09:59 +11:00
physics . BodyStatus = BodyStatus . InAir ;
2020-06-24 20:00:31 +02:00
2021-12-23 15:46:43 +11:00
var projectileComponent = Entities . GetComponent < ProjectileComponent > ( projectile ) ;
2020-06-22 05:47:15 +10:00
projectileComponent . IgnoreEntity ( shooter ) ;
2020-07-23 18:33:37 +02:00
2021-03-19 19:32:22 +01:00
// FIXME: Work around issue where inserting and removing an entity from a container,
// then setting its linear velocity in the same tick resets velocity back to zero.
2021-07-21 21:15:12 +10:00
// See SharedBroadphaseSystem.HandleContainerInsert()... It sets Awake to false, which causes this.
2021-03-19 19:32:22 +01:00
projectile . SpawnTimer ( TimeSpan . FromMilliseconds ( 25 ) , ( ) = >
{
2021-12-23 15:46:43 +11:00
Entities . GetComponent < IPhysBody > ( projectile )
2021-03-19 19:32:22 +01:00
. LinearVelocity = projectileAngle . ToVec ( ) * velocity ;
} ) ;
2020-07-23 18:33:37 +02:00
2021-12-23 15:46:43 +11:00
Entities . GetComponent < TransformComponent > ( projectile ) . WorldRotation = projectileAngle + MathHelper . PiOver2 ;
2020-06-22 05:47:15 +10:00
}
2021-12-01 12:40:00 +11:00
2021-12-23 15:46:43 +11:00
Entities . EventBus . RaiseLocalEvent ( Owner , new GunShotEvent ( firedProjectiles ) ) ;
Entities . EventBus . RaiseLocalEvent ( ammo , new AmmoShotEvent ( firedProjectiles ) ) ;
2020-06-22 05:47:15 +10:00
}
2020-06-24 20:00:31 +02:00
2020-06-22 05:47:15 +10:00
/// <summary>
/// Returns a list of numbers that form a set of equal intervals between the start and end value. Used to calculate shotgun spread angles.
/// </summary>
private List < Angle > Linspace ( double start , double end , int intervals )
{
DebugTools . Assert ( intervals > 1 ) ;
2020-06-24 20:00:31 +02:00
2020-06-22 05:47:15 +10:00
var linspace = new List < Angle > ( intervals ) ;
for ( var i = 0 ; i < = intervals - 1 ; i + + )
{
linspace . Add ( Angle . FromDegrees ( start + ( end - start ) * i / ( intervals - 1 ) ) ) ;
}
return linspace ;
}
/// <summary>
/// Fires hitscan entities and then displays their effects
/// </summary>
2021-12-05 18:09:01 +01:00
private void FireHitscan ( EntityUid shooter , HitscanComponent hitscan , Angle angle )
2020-06-22 05:47:15 +10:00
{
2021-12-23 15:46:43 +11:00
var ray = new CollisionRay ( Entities . GetComponent < TransformComponent > ( Owner ) . Coordinates . ToMapPos ( Entities ) , angle . ToVec ( ) , ( int ) hitscan . CollisionMask ) ;
2021-10-10 14:18:19 +11:00
var physicsManager = EntitySystem . Get < SharedPhysicsSystem > ( ) ;
2021-12-23 15:46:43 +11:00
var rayCastResults = physicsManager . IntersectRay ( Entities . GetComponent < TransformComponent > ( Owner ) . MapID , ray , hitscan . MaxLength , shooter , false ) . ToList ( ) ;
2020-06-22 05:47:15 +10:00
if ( rayCastResults . Count > = 1 )
{
var result = rayCastResults [ 0 ] ;
2020-10-12 23:15:41 +11:00
var distance = result . Distance ;
2020-06-22 05:47:15 +10:00
hitscan . FireEffects ( shooter , distance , angle , result . HitEntity ) ;
2021-12-03 15:53:09 +01:00
var dmg = EntitySystem . Get < DamageableSystem > ( ) . TryChangeDamage ( result . HitEntity , hitscan . Damage ) ;
2021-11-29 02:34:44 +13:00
if ( dmg ! = null )
EntitySystem . Get < AdminLogSystem > ( ) . Add ( LogType . HitScanHit ,
2021-12-23 15:46:43 +11:00
$"{Entities.ToPrettyString(shooter):user} hit {Entities.ToPrettyString(result.HitEntity):target} using {Entities.ToPrettyString(hitscan.Owner):used} and dealt {dmg.Total:damage} damage" ) ;
2020-06-22 05:47:15 +10:00
}
else
{
hitscan . FireEffects ( shooter , hitscan . MaxLength , angle ) ;
}
}
#endregion
2020-07-24 14:18:37 +02:00
2021-12-20 12:42:42 +01:00
public virtual void Examine ( FormattedMessage message , bool inDetailsRange )
2020-07-24 14:18:37 +02:00
{
var fireRateMessage = Loc . GetString ( FireRateSelector switch
{
2021-06-21 02:13:54 +02:00
FireRateSelector . Safety = > "server-ranged-barrel-component-on-examine-fire-rate-safety-description" ,
FireRateSelector . Single = > "server-ranged-barrel-component-on-examine-fire-rate-single-description" ,
FireRateSelector . Automatic = > "server-ranged-barrel-component-on-examine-fire-rate-automatic-description" ,
2020-07-24 14:18:37 +02:00
_ = > throw new IndexOutOfRangeException ( )
} ) ;
message . AddText ( fireRateMessage ) ;
}
2020-06-22 05:47:15 +10:00
}
2020-12-15 07:38:41 -06:00
2021-12-01 12:40:00 +11:00
/// <summary>
/// Raised on a gun when it fires projectiles.
/// </summary>
public sealed class GunShotEvent : EntityEventArgs
2020-12-15 07:38:41 -06:00
{
2021-12-01 12:40:00 +11:00
/// <summary>
/// Uid of the entity that shot.
/// </summary>
public EntityUid Uid ;
public readonly EntityUid [ ] FiredProjectiles ;
public GunShotEvent ( EntityUid [ ] firedProjectiles )
{
FiredProjectiles = firedProjectiles ;
}
}
/// <summary>
/// Raised on ammo when it is fired.
/// </summary>
public sealed class AmmoShotEvent : EntityEventArgs
{
/// <summary>
/// Uid of the entity that shot.
/// </summary>
public EntityUid Uid ;
public readonly EntityUid [ ] FiredProjectiles ;
2020-12-15 07:38:41 -06:00
2021-12-01 12:40:00 +11:00
public AmmoShotEvent ( EntityUid [ ] firedProjectiles )
2020-12-15 07:38:41 -06:00
{
FiredProjectiles = firedProjectiles ;
}
}
2020-06-22 05:47:15 +10:00
}