diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.Ballistic.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.Ballistic.cs index 4f956d60b3..202a3fd791 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.Ballistic.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.Ballistic.cs @@ -20,7 +20,7 @@ public sealed partial class GunSystem } } - protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates) + protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates, GunComponent? gunComponent) { if (!Timing.IsFirstTimePredicted) return; diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs index ce8126140d..3963b9b41d 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Ballistic.cs @@ -1,5 +1,5 @@ using Content.Server.Stack; -using Content.Shared.Hands.EntitySystems; // WWDP +using Content.Shared.Hands.EntitySystems; using Content.Shared.Stacks; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; @@ -12,9 +12,11 @@ public sealed partial class GunSystem [Dependency] private readonly StackSystem _stack = default!; // WD EDIT [Dependency] private readonly SharedHandsSystem _handsSystem = default!; // WWDP - protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates) + protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates, GunComponent? gunComponent) { EntityUid? ent = null; + if (!Resolve(uid, ref gunComponent, false)) + return; // TODO: Combine with TakeAmmo if (component.Entities.Count > 0) @@ -24,6 +26,7 @@ public sealed partial class GunSystem Containers.Remove(existing, component.Container); EnsureShootable(existing); + EjectCartridge(existing, gunComp: gunComponent); } else if (component.UnspawnedCount > 0) { @@ -33,7 +36,7 @@ public sealed partial class GunSystem } if (ent != null) - EjectCartridge(ent.Value); + EjectCartridge(ent.Value, gunComp: gunComponent); var cycledEvent = new GunCycledEvent(); RaiseLocalEvent(uid, ref cycledEvent); diff --git a/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs index 82a68514ac..fa709cc469 100644 --- a/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs @@ -66,4 +66,16 @@ public sealed partial class BallisticAmmoProviderComponent : Component /// [DataField] public TimeSpan FillDelay = TimeSpan.FromSeconds(0.5); + + /// + /// Is ammo ejected after each shot, or not. + /// + [DataField] + public bool AutoCycle = true; + + /// + /// Is the gun ready to shoot; if AutoCycle is true then this will always stay true and not need to be manually done. + /// + [DataField, AutoNetworkedField] + public bool Cycled = true; } diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index b5bc29b526..43d2f8798b 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -351,7 +351,10 @@ public sealed partial class GunComponent : Component public float EjectionForce = 0.04f; [DataField] - public float EjectionSpeed = 5f; + public float EjectionSpeed = 20f; + + [DataField] + public float EjectAngleOffset = 3.7f; // WD EDIT START [DataField] diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 42d0c6737a..dd06bab3b7 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -31,6 +31,7 @@ public abstract partial class SharedGunSystem SubscribeLocalEvent(OnBallisticAmmoCount); SubscribeLocalEvent(OnBallisticExamine); + SubscribeLocalEvent>(OnBallisticVerb); SubscribeLocalEvent>(AddInteractionVerb); // WWDP SubscribeLocalEvent>(AddAlternativeVerb); // WWDP SubscribeLocalEvent(OnBallisticInteractUsing); @@ -50,7 +51,9 @@ public abstract partial class SharedGunSystem private void OnBallisticInteractUsing(EntityUid uid, BallisticAmmoProviderComponent component, InteractUsingEvent args) { - if (args.Handled) + if (args.Handled + || _whitelistSystem.IsWhitelistFailOrNull(component.Whitelist, args.Used) + || GetBallisticShots(component) >= component.Capacity) return; if (_whitelistSystem.IsWhitelistFailOrNull(component.Whitelist, args.Used)) @@ -82,23 +85,20 @@ public abstract partial class SharedGunSystem // Not predicted so Audio.PlayPredicted(component.SoundInsert, uid, args.User); args.Handled = true; + component.Cycled = true; + UpdateAmmoCount(uid); UpdateBallisticAppearance(uid, component); Dirty(uid, component); } private void OnBallisticAfterInteract(EntityUid uid, BallisticAmmoProviderComponent component, AfterInteractEvent args) { - if (args.Handled || - !component.MayTransfer || - !Timing.IsFirstTimePredicted || - args.Target == null || - args.Used == args.Target || - Deleted(args.Target) || - !TryComp(args.Target, out var targetComponent) || - targetComponent.Whitelist == null) - { + if (args.Handled || !component.MayTransfer || !Timing.IsFirstTimePredicted + || args.Target is null || args.Used == args.Target + || Deleted(args.Target) + || !TryComp(args.Target, out BallisticAmmoProviderComponent? targetComponent) + || targetComponent.Whitelist is null) return; - } args.Handled = true; @@ -115,9 +115,9 @@ public abstract partial class SharedGunSystem if (args.Handled || args.Cancelled) // WWDP return; - if (Deleted(args.Target) || - !TryComp(args.Target, out var target) || - target.Whitelist == null) + if (Deleted(args.Target) + || !TryComp(args.Target, out BallisticAmmoProviderComponent? target) + || target.Whitelist is null) return; if (target.Entities.Count + target.UnspawnedCount == target.Capacity) @@ -171,6 +171,7 @@ public abstract partial class SharedGunSystem // play sound to be cool Audio.PlayPredicted(component.SoundInsert, uid, args.User); SimulateInsertAmmo(ent.Value, args.Target.Value, Transform(args.Target.Value).Coordinates); + component.Cycled = true; // Make sure when loading shells in shotguns, that the first round is chambered. } if (IsClientSide(ent.Value)) @@ -215,6 +216,19 @@ public abstract partial class SharedGunSystem } // WWDP edit end + private void OnBallisticVerb(EntityUid uid, BallisticAmmoProviderComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || args.Hands == null || !component.Cycleable) + return; + + args.Verbs.Add(new Verb() + { + Text = Loc.GetString("gun-ballistic-cycle"), + Disabled = GetBallisticShots(component) == 0, + Act = () => ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), args.User), + }); + } + private void OnBallisticExamine(EntityUid uid, BallisticAmmoProviderComponent component, ExaminedEvent args) { if (!args.IsInDetailsRange) @@ -297,18 +311,18 @@ public abstract partial class SharedGunSystem return; // Reset shotting for cycling - if (Resolve(uid, ref gunComp, false) && - gunComp is { FireRateModified: > 0f } && - !Paused(uid)) - { + if (Resolve(uid, ref gunComp, false) + && gunComp is { FireRateModified: > 0f } + && !Paused(uid)) gunComp.NextFire = Timing.CurTime + TimeSpan.FromSeconds(1 / gunComp.FireRateModified); - } + Dirty(uid, component); Audio.PlayPredicted(component.SoundRack, uid, user); var shots = GetBallisticShots(component); - Cycle(uid, component, coordinates); + component.Cycled = true; + Cycle(uid, component, coordinates, gunComp); var text = Loc.GetString(shots == 0 ? "gun-ballistic-cycled-empty" : "gun-ballistic-cycled"); @@ -319,7 +333,7 @@ public abstract partial class SharedGunSystem UpdateAmmoCount(uid); } - protected abstract void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates); + protected abstract void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates, GunComponent? gunComponent = null); private void OnBallisticInit(EntityUid uid, BallisticAmmoProviderComponent component, ComponentInit args) { @@ -341,15 +355,15 @@ public abstract partial class SharedGunSystem } } - protected int GetBallisticShots(BallisticAmmoProviderComponent component) - { - return component.Entities.Count + component.UnspawnedCount; - } + protected int GetBallisticShots(BallisticAmmoProviderComponent component) => component.Entities.Count + component.UnspawnedCount; private void OnBallisticTakeAmmo(EntityUid uid, BallisticAmmoProviderComponent component, TakeAmmoEvent args) { for (var i = 0; i < args.Shots; i++) { + if (!component.Cycled) + break; + EntityUid entity; if (component.Entities.Count > 0) @@ -358,24 +372,24 @@ public abstract partial class SharedGunSystem args.Ammo.Add((entity, EnsureShootable(entity))); - if (component.AutoCycle) // WD EDIT - { - component.Entities.RemoveAt(component.Entities.Count - 1); - Containers.Remove(entity, component.Container); - } // WWDP edit; support internal caseless ammo in hand-cycled guns - else if (TryComp(entity, out var cartridge) && cartridge.DeleteOnSpawn) + if (TryComp(entity, out var cartridge) && cartridge.DeleteOnSpawn) { component.Entities.RemoveAt(component.Entities.Count - 1); Containers.Remove(entity, component.Container); component.Racked = false; break; - } // WWDP edit end - else + } + // WWDP edit end + + // if entity in container it can't be ejected, so shell will remain in gun and block next shoot + if (!component.AutoCycle) { component.Racked = false; // WWDP break; } + component.Entities.RemoveAt(component.Entities.Count - 1); + Containers.Remove(entity, component.Container); } else if (component.UnspawnedCount > 0) { @@ -383,19 +397,22 @@ public abstract partial class SharedGunSystem entity = Spawn(component.Proto, args.Coordinates); args.Ammo.Add((entity, EnsureShootable(entity))); - // WD EDIT START - if (!component.AutoCycle && TryComp(entity, out var cartridge)) + // Put it back in if it doesn't auto-cycle + if (Timing.IsFirstTimePredicted && TryComp(entity, out var cartridge) && !component.AutoCycle) // WD EDIT { + // WD EDIT START component.Racked = false; - if (!cartridge.DeleteOnSpawn) - { - component.Entities.Add(entity); - Containers.Insert(entity, component.Container); - } - break; + if (cartridge.DeleteOnSpawn) + break; + // WD EDIT END + + component.Entities.Add(entity); + Containers.Insert(entity, component.Container); } - // WD EDIT END } + + if (!component.AutoCycle) + component.Cycled = false; } UpdateBallisticAppearance(uid, component); @@ -445,6 +462,4 @@ public abstract partial class SharedGunSystem /// DoAfter event for filling one ballistic ammo provider from another. /// [Serializable, NetSerializable] -public sealed partial class AmmoFillDoAfterEvent : SimpleDoAfterEvent -{ -} +public sealed partial class AmmoFillDoAfterEvent : SimpleDoAfterEvent { } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index d3caa46dec..68b8ea31bb 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -540,10 +540,12 @@ public abstract partial class SharedGunSystem : EntitySystem { var throwingForce = 0.01f; var throwingSpeed = 5f; + var ejectAngleOffset = 3.7f; if (gunComp is not null) { throwingForce = gunComp.EjectionForce; throwingSpeed = gunComp.EjectionSpeed; + ejectAngleOffset = gunComp.EjectAngleOffset; } // TODO: Sound limit version. @@ -555,15 +557,14 @@ public abstract partial class SharedGunSystem : EntitySystem TransformSystem.SetLocalRotation(entity, Random.NextAngle(), xform); TransformSystem.SetCoordinates(entity, xform, coordinates); + if (angle is null) + angle = Random.NextAngle(); - // decides direction the casing ejects and only when not cycling - if (angle != null) - { - Angle ejectAngle = angle.Value; - ejectAngle += 3.7f; // 212 degrees; casings should eject slightly to the right and behind of a gun - ThrowingSystem.TryThrow(entity, ejectAngle.ToVec().Normalized() * throwingForce, throwingSpeed); - } - if (playSound && TryComp(entity, out var cartridge)) + Angle ejectAngle = angle.Value; + ejectAngle += ejectAngleOffset; // 212 degrees; casings should eject slightly to the right and behind of a gun + ThrowingSystem.TryThrow(entity, ejectAngle.ToVec().Normalized() * throwingForce, throwingSpeed); + + if (playSound && TryComp(entity, out CartridgeAmmoComponent? cartridge)) { Audio.PlayPvs(cartridge.EjectSound, entity, AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation).WithVolume(-1f)); } @@ -662,25 +663,14 @@ public abstract partial class SharedGunSystem : EntitySystem Dirty(projectile, targeted); } - public void SetFireRate(GunComponent component, float fireRate) // Goobstation - { - component.FireRate = fireRate; - } + public void SetFireRate(GunComponent component, float fireRate) => component.FireRate = fireRate; - public void SetUseKey(GunComponent component, bool useKey) // Goobstation - { - component.UseKey = useKey; - } + public void SetUseKey(GunComponent component, bool useKey) => component.UseKey = useKey; - public void SetSoundGunshot(GunComponent component, SoundSpecifier? sound) // Goobstation - { - component.SoundGunshot = sound; - } + public void SetSoundGunshot(GunComponent component, SoundSpecifier? sound) => component.SoundGunshot = sound; + + public void SetClumsyProof(GunComponent component, bool clumsyProof) => component.ClumsyProof = clumsyProof; - public void SetClumsyProof(GunComponent component, bool clumsyProof) // Goobstation - { - component.ClumsyProof = clumsyProof; - } protected abstract void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null); /// diff --git a/Resources/Audio/_EE/Weapons/Guns/Casings/attributions.yml b/Resources/Audio/_EE/Weapons/Guns/Casings/attributions.yml new file mode 100644 index 0000000000..d2ae18893e --- /dev/null +++ b/Resources/Audio/_EE/Weapons/Guns/Casings/attributions.yml @@ -0,0 +1,7 @@ +- files: + - "shotgun_shell1" + - "shotgun_shell2" + - "shotgun_shell3" + license: "Custom" + copyright: "Valve Software, Non-Commercial Steam Subscriber Agreement" + source: "https://store.steampowered.com/app/220/HalfLife_2/" diff --git a/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell1.ogg b/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell1.ogg new file mode 100644 index 0000000000..242291f262 Binary files /dev/null and b/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell1.ogg differ diff --git a/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell2.ogg b/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell2.ogg new file mode 100644 index 0000000000..61ad21c734 Binary files /dev/null and b/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell2.ogg differ diff --git a/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell3.ogg b/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell3.ogg new file mode 100644 index 0000000000..bc0a139630 Binary files /dev/null and b/Resources/Audio/_EE/Weapons/Guns/Casings/shotgun_shell3.ogg differ diff --git a/Resources/Locale/en-US/weapons/extenddescriptions/descriptions.ftl b/Resources/Locale/en-US/weapons/extenddescriptions/descriptions.ftl index c40439dbe6..d6c87b4858 100644 --- a/Resources/Locale/en-US/weapons/extenddescriptions/descriptions.ftl +++ b/Resources/Locale/en-US/weapons/extenddescriptions/descriptions.ftl @@ -3,6 +3,7 @@ gun-legality-salvage = This weapon is licensed for use in planetary expeditions. # Weapon Modifiers gun-suppressed = This weapon comes with a built-in suppressor. It will be impossible to hear at a distance. +gun-modifier-choke = This shotgun comes with a hunting choke. It has a 50% tighter spread when firing shotshells. # Clothing Modifiers helmet-radio = This item includes a built-in radio, activate it to configure its settings. diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml index 094c05c8ab..48bdbe6721 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml @@ -438,7 +438,7 @@ parent: ClothingBackpackDuffelSyndicateBundle id: ClothingBackpackDuffelSyndicateFilledFPA90 name: FPA-90 bundle - description: "A cheap integrally suppressed SMG. Comes bundled with three magazines." + description: "A cheap integrally suppressed SMG. Magazines are sold separately." components: - type: StorageFill contents: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml index ee228345d3..b2371dacbf 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml @@ -18,6 +18,16 @@ map: [ "enum.AmmoVisualLayers.Base" ] - type: Appearance - type: SpentAmmoVisuals + - type: EmitSoundOnLand + sound: + collection: ShellLand + params: + volume: -5 + - type: EmitSoundOnCollide + sound: + collection: ShellLand + params: + volume: -5 - type: entity id: ShellShotgunBeanbag diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/caseless_rifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/caseless_rifle.yml index d8e2952789..39932fba16 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/caseless_rifle.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/caseless_rifle.yml @@ -7,7 +7,7 @@ - type: Projectile damage: types: - Piercing: 19 + Piercing: 15 - type: entity id: BulletCaselessRiflePractice @@ -30,7 +30,7 @@ damage: types: Blunt: 3 - Heat: 16 + Heat: 12 - type: entity id: BulletCaselessRifleUranium @@ -41,8 +41,8 @@ - type: Projectile damage: types: - Radiation: 9 - Piercing: 10 + Radiation: 7 + Piercing: 8 - type: entity id: BulletCaselessRifleShrapnel @@ -53,7 +53,7 @@ - type: Projectile damage: types: - Piercing: 4.37 + Piercing: 3.75 - type: Sprite scale: 0.5, 0.5 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/sound_collections.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/sound_collections.yml index 71123ddb2c..ad2b86b0ce 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/sound_collections.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/sound_collections.yml @@ -10,8 +10,15 @@ files: - "/Audio/Weapons/Guns/Casings/shotgun_fall.ogg" +- type: soundCollection + id: ShellLand + files: + - "/Audio/_EE/Weapons/Guns/Casings/shotgun_shell1.ogg" + - "/Audio/_EE/Weapons/Guns/Casings/shotgun_shell2.ogg" + - "/Audio/_EE/Weapons/Guns/Casings/shotgun_shell3.ogg" + - type: soundCollection id: ToyFall files: - "/Audio/Items/Toys/ToyFall1.ogg" - - "/Audio/Items/Toys/ToyFall2.ogg" \ No newline at end of file + - "/Audio/Items/Toys/ToyFall2.ogg" diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index c7dcd35046..ca55b76be7 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -69,6 +69,7 @@ tags: - Grenade capacity: 3 + autoCycle: false proto: GrenadeFrag soundInsert: path: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml index 72930c0d67..d4161fedc1 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml @@ -194,6 +194,7 @@ - type: ChamberMagazineAmmoProvider boltClosed: null - type: Gun + damageModifier: 1.25 # "Extra Robust" despite having an underpowered cartridge. fireRate: 4 soundGunshot: path: /Audio/Weapons/Guns/Gunshots/silenced.ogg diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml index 94ee3004bc..5db98784ef 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml @@ -284,7 +284,7 @@ - 0,0,7,0 sprite: Objects/Weapons/Guns/Shotguns/enforcer_inhands_64x.rsi - type: BallisticAmmoProvider - autoCycle: true # WWDP semi-auto + capacity: 7 - type: Wieldable - type: MeleeWeapon attackRate: 1.4 @@ -327,6 +327,12 @@ - type: Wieldable - type: Gun shotgunSpreadMultiplier: 0.5 + - type: ExtendDescription + descriptionList: + - description: "gun-modifier-choke" + fontSize: 12 + color: "#ff4f00" + requireDetailRange: false - type: entity name: sawn-off shotgun