Repository: SpaceStationUA/station
Pull request #103: Aiming Mechanic
Author: login: CarryUa
Labels: name: Changes: YML
name: Changes: Sprite
name: Changes: C#
Created: 2025-05-18T11:28:32Z | Merged: 2025-05-18T12:51:09Z

# Description

Додано можливість прицілювання для зброї окрім лазерної. Прицілюватись можна на близькій дистанції, і якшо ціль ворухнеться - зброя автоматично вистрелить.

---

# TODO

- [x] Blablabla Blebleble Blublubluuuuuuu

---


<details><summary><h1>Media</h1></summary>
<p>
<img src="https://raw.githubusercontent.com/CarryUa/station-fork/3893b113178b616f7eeba4fcca1f2500e2dc101f/Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/icon.png" alt="" style="max-width: 100%;">
Aiming alert sprite
</p>
</details>

---

# Changelog


:cl:
- add: Ви тепер можете прицілюватись з будь якої зброї окрім лазерної. ВІДІГРУЙТЕ fearRP!!!


Diff:
diff --git a/Content.Shared/Alert/AlertCategory.cs b/Content.Shared/Alert/AlertCategory.cs
index a1c27dbcd19..a53989c272e 100644
--- a/Content.Shared/Alert/AlertCategory.cs
+++ b/Content.Shared/Alert/AlertCategory.cs
@@ -18,5 +18,8 @@ public enum AlertCategory
     Thirst,
     Toxins,
     Battery,
-    Counter
+    Counter,
+    // PIRATE START
+    Aiming
+    // PIRATE END
 }
diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs
index 75fe60b890f..fe7e231bf5f 100644
--- a/Content.Shared/Alert/AlertType.cs
+++ b/Content.Shared/Alert/AlertType.cs
@@ -81,6 +81,7 @@ public enum AlertType : byte
         Charge, //IPC
 		Caged, //IPC
         Stasis, // Chronolegioner
+        OnSight, // Aiming
 //Pirate END
         Deflecting,
     }
diff --git a/Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs b/Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs
new file mode 100644
index 00000000000..07c4fcef594
--- /dev/null
+++ b/Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs
@@ -0,0 +1,14 @@
+using Robust.Shared.GameObjects;
+
+namespace Content.Shared._Pirate.Actions.Events;
+
+// This is a local event, not a networked event
+public sealed class OnAimingTargetMoveEvent : EntityEventArgs
+{
+    public readonly EntityUid Target;
+
+    public OnAimingTargetMoveEvent(EntityUid target)
+    {
+        Target = target;
+    }
+}
diff --git a/Content.Shared/_Pirate/Aiming/CanTakeAimComponent.cs b/Content.Shared/_Pirate/Aiming/CanTakeAimComponent.cs
new file mode 100644
index 00000000000..cc7019a606f
--- /dev/null
+++ b/Content.Shared/_Pirate/Aiming/CanTakeAimComponent.cs
@@ -0,0 +1,22 @@
+using Content.Shared.Damage;
+
+namespace Content.Shared._Pirate.Aiming;
+
+/// <summary>
+/// Allows the weapon entity to be used to aim at other entities.
+/// </summary>
+[RegisterComponent]
+public sealed partial class CanTakeAimComponent : Component
+{
+    public bool IsAiming = false;
+    public EntityUid? User = null;
+    public double AimStartFrame = 0;
+    /// <summary>
+    /// How long does it take for your shot to fully "charge" in seconds
+    /// </summary>
+    [DataField] public float MaxAimTime = 5f;
+    /// <summary>
+    /// Maximal damage boost you can get from aiming
+    /// </summary>
+    [DataField] public short MaxDamageMultiplier = 2;
+}
diff --git a/Content.Shared/_Pirate/Aiming/OnSightComponent.cs b/Content.Shared/_Pirate/Aiming/OnSightComponent.cs
new file mode 100644
index 00000000000..667a9413a86
--- /dev/null
+++ b/Content.Shared/_Pirate/Aiming/OnSightComponent.cs
@@ -0,0 +1,14 @@
+namespace Content.Shared._Pirate.Aiming;
+
+/// <summary>
+/// Component that is added to an entity when it is aimed at by another entity.
+/// It is used to add alert and process OnAimingTargetMoveEvent
+/// </summary>
+[RegisterComponent]
+public sealed partial class OnSightComponent : Component
+{
+    /// <summary>
+    /// List of entities that are aiming at this entity.
+    /// </summary>
+    [DataField] public List<EntityUid> AimedAtBy = new();
+}
diff --git a/Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs b/Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs
new file mode 100644
index 00000000000..5fea933962b
--- /dev/null
+++ b/Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs
@@ -0,0 +1,199 @@
+using System.Linq;
+using Content.Shared._Pirate.Actions.Events;
+using Content.Shared.Alert;
+using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Damage;
+using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Movement.Components;
+using Content.Shared.Popups;
+using Content.Shared.Projectiles;
+using Content.Shared.Weapons.Ranged;
+using Content.Shared.Weapons.Ranged.Components;
+using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.Timing;
+
+namespace Content.Shared._Pirate.Aiming;
+
+public sealed partial class SharedCanTakeAimSystem : EntitySystem
+{
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly EntityManager _entMan = default!;
+    [Dependency] private readonly SharedGunSystem _gun = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<CanTakeAimComponent, AfterInteractEvent>(OnWeaponTakeAim);
+        SubscribeLocalEvent<CanTakeAimComponent, OnAimingTargetMoveEvent>(OnAimingTargetMove);
+        SubscribeLocalEvent<CanTakeAimComponent, AmmoShotEvent>(OnAmmoShot);
+    }
+    private void OnAmmoShot(EntityUid uid, CanTakeAimComponent component, AmmoShotEvent args)
+    {
+        // TODO: Add damage multiplying
+        // foreach (var entity in args.FiredProjectiles)
+        // {
+        // if (TryComp<ProjectileComponent>(entity, out var projectile))
+        // {
+        //     var deltaT = (_timing.CurFrame - component.AimStartFrame) * _timing.FramesPerSecondAvg;
+        //     Logger.Debug($"Delta T: {deltaT}s");
+        //     projectile.Damage *= component.MaxDamageMultiplier * (component.MaxAimTime / deltaT);
+
+        //     Dirty(entity, projectile);
+        // }
+        // }
+    }
+
+    private void OnAimingTargetMove(EntityUid uid, CanTakeAimComponent component, OnAimingTargetMoveEvent args)
+    {
+        component.IsAiming = false;
+        Dirty(uid, component);
+        if (!TryComp<GunComponent>(uid, out var gunComp))
+            return;
+        var targetCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(args.Target));
+        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+        ;
+        // if (_gun.CanShoot(gunComp))
+        // {
+        EntityUid? ammo = null;
+        if (TryComp<ChamberMagazineAmmoProviderComponent>(uid, out var chamberComp))
+        {
+
+            if (chamberComp.BoltClosed != null && chamberComp.BoltClosed.Value == false)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            ammo = _gun.GetChamberEntity(uid);
+        }
+        if (TryComp<RevolverAmmoProviderComponent>(uid, out var revolverComp))
+        {
+            if (revolverComp.Chambers[revolverComp.CurrentIndex] == false)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                ammo = takeAmmoEvent.Ammo[0].Entity;
+            }
+        }
+        if (TryComp<BallisticAmmoProviderComponent>(uid, out var ballisticComp))
+        {
+
+            // Use TakeAmmo to safely get ammo
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                ammo = takeAmmoEvent.Ammo[0].Entity;
+            }
+        }
+        if (TryComp<MagazineAmmoProviderComponent>(uid, out var magazineComp))
+        {
+            var magEntity = _gun.GetMagazineEntity(uid);
+            if (magEntity == null)
+                return;
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+            ammo = takeAmmoEvent.Ammo[0].Entity;
+        }
+        if (ammo == null)
+            return;
+        _gun.Shoot(uid, gunComp, ammo.Value, gunCoords, targetCoords, out _, component.User);
+        if (component.User == null)
+            return;
+        if (ballisticComp != null)
+        {
+            // For ballistic guns, use the ActivateInWorldEvent to trigger cycling
+            if (ballisticComp.AutoCycle)
+            {
+                var activateEvent = new ActivateInWorldEvent(component.User.Value, uid, true);
+                RaiseLocalEvent(uid, activateEvent);
+            }
+        }
+        else if (revolverComp != null)
+        {
+            // For revolvers, use the UseInHandEvent to trigger cycling
+            var useEvent = new UseInHandEvent(component.User.Value);
+            RaiseLocalEvent(uid, useEvent);
+        }
+        else if (chamberComp != null)
+        {
+            if (chamberComp.AutoCycle)
+            {
+                var useEvent = new UseInHandEvent(component.User.Value);
+                RaiseLocalEvent(uid, useEvent);
+            }
+        }
+        // }
+        // _gun.ShootProjectile(ammo.Value, direction, _physics.GetMapLinearVelocity(uid, physComp), uid);
+    }
+    public void OnWeaponTakeAim(EntityUid uid, CanTakeAimComponent component, ref AfterInteractEvent args)
+    {
+        if (args.Target == null)
+            return;
+        if (args.Target == args.User)
+            return;
+        component.User = args.User;
+        if (!args.CanReach)
+        {
+            _popup.PopupClient("Я не зможу попасти в ціль звідси...", args.User, PopupType.Medium);
+            return;
+        }
+        if (!HasComp<MobMoverComponent>(args.Target))
+            return;
+        if (!TryComp<MetaDataComponent>(args.User, out var userMetaComp) || !TryComp<MetaDataComponent>(args.Target, out var targetMetaComp))
+            return;
+        if (component.IsAiming)
+        {
+            if (HasComp<OnSightComponent>(args.Target))
+            {
+                RemComp<OnSightComponent>(args.Target.Value);
+                _popup.PopupPredicted($"{userMetaComp.EntityName} stopped aiming at {targetMetaComp.EntityName}.", args.Target.Value, args.Target.Value, PopupType.Large);
+                component.IsAiming = false;
+            }
+            return;
+        }
+
+        if (userMetaComp == null || targetMetaComp == null)
+            return;
+        if (TryComp<MobStateComponent>(args.Target, out var targetMobState) && targetMobState.CurrentState != Mobs.MobState.Alive)
+            return;
+
+        component.AimStartFrame = _timing.CurFrame;
+        EnsureComponentOnTarget(args.Target.Value, uid);
+        component.IsAiming = true;
+        _popup.PopupPredicted($"{userMetaComp.EntityName} is aiming at {targetMetaComp.EntityName}!", args.Target.Value, args.Target.Value, PopupType.LargeCaution);
+
+    }
+    private void EnsureComponentOnTarget(EntityUid target, EntityUid uid) // uid is uid of gun, not user!!!
+    {
+        if (!HasComp<OnSightComponent>(target))
+        {
+            var onSigthComp = new OnSightComponent
+            {
+                AimedAtBy = new()
+            };
+            onSigthComp.AimedAtBy.Add(uid);
+            AddComp(target, onSigthComp);
+        }
+        else
+        {
+            var onSigthComp = _entMan.GetComponent<OnSightComponent>(target);
+            if (!onSigthComp.AimedAtBy.Contains(uid))
+                onSigthComp.AimedAtBy.Add(uid);
+        }
+    }
+
+}
diff --git a/Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs b/Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs
new file mode 100644
index 00000000000..ff6b9d6a951
--- /dev/null
+++ b/Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs
@@ -0,0 +1,33 @@
+using Content.Shared._Pirate.Actions.Events;
+using Content.Shared.Alert;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._Pirate.Aiming;
+
+public sealed partial class SharedOnSightSystem : EntitySystem
+{
+    [Dependency] private readonly AlertsSystem _alerts = default!;
+    [Dependency] private readonly IPrototypeManager _proto = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<OnSightComponent, MoveEvent>(OnMove);
+        SubscribeLocalEvent<OnSightComponent, ComponentStartup>(OnStartup);
+    }
+    private void OnStartup(EntityUid uid, OnSightComponent component, ComponentStartup args)
+    {
+        if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
+            _alerts.ShowAlert(uid, alertProto);
+    }
+    private void OnMove(EntityUid uid, OnSightComponent component, ref MoveEvent args)
+    {
+        if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
+            _alerts.ClearAlert(uid, alertProto);
+        foreach (var entity in component.AimedAtBy)
+        {
+            var ev = new OnAimingTargetMoveEvent(uid);
+            RaiseLocalEvent(entity, ev);
+        }
+        RemComp<OnSightComponent>(uid);
+    }
+}
diff --git a/Resources/Prototypes/Alerts/categories.yml b/Resources/Prototypes/Alerts/categories.yml
index b902b91b756..d5e80b2f557 100644
--- a/Resources/Prototypes/Alerts/categories.yml
+++ b/Resources/Prototypes/Alerts/categories.yml
@@ -39,3 +39,6 @@
 
 - type: alertCategory # PIRATE
   id: Counter
+
+- type: alertCategory # PIRATE
+  id: Aiming
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml
index 2ad72c33da9..f1a4bd96b7e 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml
@@ -83,3 +83,6 @@
         Blunt: 2.5
   - type: DamageOtherOnHit
     staminaCost: 8
+  # PIRATE START
+  - type: CanTakeAim
+  # PIRATE END
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml
index 690658d0c70..f2e21809bb3 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml
@@ -11,3 +11,6 @@
   - type: Gun
     minAngle: 21
     maxAngle: 32   
+  # PIRATE START
+  - type: CanTakeAim
+  # PIRATE END
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml
index a72dbefddc1..ee8c93c2a54 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml
@@ -37,6 +37,12 @@
       collection: MetalThud
   - type: DamageOtherOnHit
     staminaCost: 16
+  # PIRATE START
+  - type: CanHoldAccessories
+    whiteListTags:
+      - CombatKnife
+  - type: CanTakeAim
+  # PIRATE END
 
 - type: entity
   name: minigun
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml
index 0a412a5480f..be65e137dca 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml
@@ -81,6 +81,12 @@
         Blunt: 3
   - type: DamageOtherOnHit
     staminaCost: 12
+  # PIRATE START
+  - type: CanHoldAccessories
+    whiteListTags:
+      - CombatKnife
+  - type: CanTakeAim
+  # PIRATE END
 
 - type: entity
   name: L6 SAW
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml
index 3de83122aac..858d61cba26 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml
@@ -34,6 +34,12 @@
   - type: DamageOtherOnHit
     staminaCost: 14
   - type: Execution
+  # PIRATE START
+  - type: CanHoldAccessories
+    whiteListTags:
+      - CombatKnife
+  - type: CanTakeAim
+  # PIRATE END
 
 - type: entity
   name: china lake
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml
index ceac00b597a..e80131c4d6c 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml
@@ -5,86 +5,87 @@
   description: A rooty tooty point and shooty.
   abstract: true
   components:
-  - type: Sprite
-    layers:
-      - state: base
-        map: ["enum.GunVisualLayers.Base"]
-      - state: mag-0
-        map: ["enum.GunVisualLayers.Mag"]
-  - type: Item
-    size: Small
-    shape:
-    - 0,0,1,0
-    - 0,1,0,1
-  - type: Tag
-    tags:
-    - Sidearm
-  - type: Clothing
-    sprite: Objects/Weapons/Guns/Pistols/viper.rsi
-    quickEquip: false
-    slots:
-    - suitStorage
-    - Belt
-  - type: Gun
-    fireRate: 6
-    selectedMode: SemiAuto
-    availableModes:
-      - SemiAuto
-      - FullAuto
-    soundGunshot:
-      path: /Audio/Weapons/Guns/Gunshots/pistol.ogg
-  - type: ChamberMagazineAmmoProvider
-    soundRack:
-      path: /Audio/Weapons/Guns/Cock/pistol_cock.ogg
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: MagazinePistol
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazinePistol
-      gun_chamber:
-        name: Chamber
-        startingItem: CartridgePistol
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgePistol
-  - type: ContainerContainer
-    containers:
-      gun_magazine: !type:ContainerSlot
-      gun_chamber: !type:ContainerSlot
-  - type: MagazineVisuals
-    magState: mag
-    steps: 1
-    zeroVisible: true
-  - type: Appearance
-  - type: StaticPrice
-    price: 500
-  - type: Execution
-  - type: AmmoCounter
-  - type: MeleeWeapon
-    attackRate: 1.2
-    damage:
-      types:
-        Blunt: 7.5
-    swapKeys: true
-    disableHeavy: true
-    animation: WeaponArcThrust
-    wideAnimationRotation: 135
-    soundHit:
-      collection: MetalThud
-  - type: DamageOtherOnHit
-    staminaCost: 5
-    # PIRATE START
-  - type: CanHoldAccessories
-    whiteListTags:
-      - CombatKnife
-    # PIRATE END
+    - type: Sprite
+      layers:
+        - state: base
+          map: ["enum.GunVisualLayers.Base"]
+        - state: mag-0
+          map: ["enum.GunVisualLayers.Mag"]
+    - type: Item
+      size: Small
+      shape:
+        - 0,0,1,0
+        - 0,1,0,1
+    - type: Tag
+      tags:
+        - Sidearm
+    - type: Clothing
+      sprite: Objects/Weapons/Guns/Pistols/viper.rsi
+      quickEquip: false
+      slots:
+        - suitStorage
+        - Belt
+    - type: Gun
+      fireRate: 6
+      selectedMode: SemiAuto
+      availableModes:
+        - SemiAuto
+        - FullAuto
+      soundGunshot:
+        path: /Audio/Weapons/Guns/Gunshots/pistol.ogg
+    - type: ChamberMagazineAmmoProvider
+      soundRack:
+        path: /Audio/Weapons/Guns/Cock/pistol_cock.ogg
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: MagazinePistol
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazinePistol
+        gun_chamber:
+          name: Chamber
+          startingItem: CartridgePistol
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgePistol
+    - type: ContainerContainer
+      containers:
+        gun_magazine: !type:ContainerSlot
+        gun_chamber: !type:ContainerSlot
+    - type: MagazineVisuals
+      magState: mag
+      steps: 1
+      zeroVisible: true
+    - type: Appearance
+    - type: StaticPrice
+      price: 500
+    - type: Execution
+    - type: AmmoCounter
+    - type: MeleeWeapon
+      attackRate: 1.2
+      damage:
+        types:
+          Blunt: 7.5
+      swapKeys: true
+      disableHeavy: true
+      animation: WeaponArcThrust
+      wideAnimationRotation: 135
+      soundHit:
+        collection: MetalThud
+    - type: DamageOtherOnHit
+      staminaCost: 5
+      # PIRATE START
+    - type: CanHoldAccessories
+      whiteListTags:
+        - CombatKnife
+    - type: CanTakeAim
+      # PIRATE END
 
 - type: entity
   name: viper
@@ -92,40 +93,40 @@
   id: WeaponPistolViper
   description: A small, easily concealable, but somewhat underpowered gun, produced by a bulk arms manufacturer now defunct for over a century. Uses .35 auto ammo.
   components:
-  - type: Sprite
-    sprite: Objects/Weapons/Guns/Pistols/viper.rsi
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: MagazinePistol
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazinePistol
-            - MagazinePistolHighCapacity
-      gun_chamber:
-        name: Chamber
-        startingItem: CartridgePistol
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgePistol
-  - type: ContainerContainer
-    containers:
-      gun_magazine: !type:ContainerSlot
-      gun_chamber: !type:ContainerSlot
-  - type: Gun
-    fireRate: 6
-    selectedMode: SemiAuto
-    availableModes:
-      - SemiAuto
-      - FullAuto
-    soundGunshot:
-      path: /Audio/Weapons/Guns/Gunshots/pistol.ogg
-    fireOnDropChance: 0.3
+    - type: Sprite
+      sprite: Objects/Weapons/Guns/Pistols/viper.rsi
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: MagazinePistol
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazinePistol
+              - MagazinePistolHighCapacity
+        gun_chamber:
+          name: Chamber
+          startingItem: CartridgePistol
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgePistol
+    - type: ContainerContainer
+      containers:
+        gun_magazine: !type:ContainerSlot
+        gun_chamber: !type:ContainerSlot
+    - type: Gun
+      fireRate: 6
+      selectedMode: SemiAuto
+      availableModes:
+        - SemiAuto
+        - FullAuto
+      soundGunshot:
+        path: /Audio/Weapons/Guns/Gunshots/pistol.ogg
+      fireOnDropChance: 0.3
 
 - type: entity
   name: viper
@@ -133,73 +134,75 @@
   id: WeaponPistolViperEmpty
   suffix: Empty
   components:
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: null
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazinePistol
-            - MagazinePistolHighCapacity
-      gun_chamber:
-        name: Chamber
-        startingItem: null
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgePistol
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: null
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazinePistol
+              - MagazinePistolHighCapacity
+        gun_chamber:
+          name: Chamber
+          startingItem: null
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgePistol
 
 - type: entity
   name: viper
   parent: WeaponPistolViper
   id: WeaponPistolViperSecurity
-  description: A small, easily concealable, but somewhat underpowered gun, produced by a bulk arms manufacturer now defunct for over a century.
-               Uses .35 auto ammo. The serial number on the handguard marks this gun as belonging to an NT Security Officer.
+  description:
+    A small, easily concealable, but somewhat underpowered gun, produced by a bulk arms manufacturer now defunct for over a century.
+    Uses .35 auto ammo. The serial number on the handguard marks this gun as belonging to an NT Security Officer.
   suffix: Security Loadouts
   components:
-  - type: GuideHelp
-    guides: [ SecurityWeapons ]
+    - type: GuideHelp
+      guides: [SecurityWeapons]
 
 - type: entity
   id: WeaponPistolViperNonLethal
   parent: WeaponPistolViper
   suffix: Non-lethal
   components:
-  - type: Sprite
-    sprite: Objects/Weapons/Guns/Pistols/vipernl.rsi
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: MagazinePistolRubber
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazinePistol
-            - MagazinePistolHighCapacity
-      gun_chamber:
-        name: Chamber
-        startingItem: CartridgePistolRubber
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgePistol
+    - type: Sprite
+      sprite: Objects/Weapons/Guns/Pistols/vipernl.rsi
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: MagazinePistolRubber
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazinePistol
+              - MagazinePistolHighCapacity
+        gun_chamber:
+          name: Chamber
+          startingItem: CartridgePistolRubber
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgePistol
 
 - type: entity
   id: WeaponPistolViperSecurityNonLethal
   parent: WeaponPistolViperNonLethal
   suffix: Non-lethal, SecurityLoadouts
-  description: A small, easily concealable, but somewhat underpowered gun, produced by a bulk arms manufacturer now defunct for over a century.
-               Uses .35 auto ammo. The serial number on the handguard marks this gun as belonging to an NT Security Officer.
+  description:
+    A small, easily concealable, but somewhat underpowered gun, produced by a bulk arms manufacturer now defunct for over a century.
+    Uses .35 auto ammo. The serial number on the handguard marks this gun as belonging to an NT Security Officer.
   components:
-  - type: GuideHelp
-    guides: [ SecurityWeapons ]
+    - type: GuideHelp
+      guides: [SecurityWeapons]
 
 - type: entity
   name: cobra
@@ -207,49 +210,49 @@
   id: WeaponPistolCobra
   description: A rugged, robust operator handgun with inbuilt silencer. Uses .25 caseless ammo.
   components:
-  - type: Sprite
-    sprite: Objects/Weapons/Guns/Pistols/cobra.rsi
-    layers:
-    - state: base
-      map: ["enum.GunVisualLayers.Base"]
-    - state: mag-0
-      map: ["enum.GunVisualLayers.Mag"]
-  - type: Clothing
-    sprite: Objects/Weapons/Guns/Pistols/cobra.rsi
-  - 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
-      params:
-        volume: -14
-    availableModes:
-    - SemiAuto
-    fireOnDropChance: 0.1
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: MagazinePistolCaselessRifle
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazinePistolCaselessRifle
-      gun_chamber:
-        name: Chamber
-        startingItem: CartridgeCaselessRifle
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgeCaselessRifle
-  - type: ContainerContainer
-    containers:
-      gun_magazine: !type:ContainerSlot
-      gun_chamber: !type:ContainerSlot
+    - type: Sprite
+      sprite: Objects/Weapons/Guns/Pistols/cobra.rsi
+      layers:
+        - state: base
+          map: ["enum.GunVisualLayers.Base"]
+        - state: mag-0
+          map: ["enum.GunVisualLayers.Mag"]
+    - type: Clothing
+      sprite: Objects/Weapons/Guns/Pistols/cobra.rsi
+    - 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
+        params:
+          volume: -14
+      availableModes:
+        - SemiAuto
+      fireOnDropChance: 0.1
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: MagazinePistolCaselessRifle
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazinePistolCaselessRifle
+        gun_chamber:
+          name: Chamber
+          startingItem: CartridgeCaselessRifle
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgeCaselessRifle
+    - type: ContainerContainer
+      containers:
+        gun_magazine: !type:ContainerSlot
+        gun_chamber: !type:ContainerSlot
 
 - type: entity
   name: cobra
@@ -257,24 +260,24 @@
   id: WeaponPistolCobraEmpty
   suffix: Empty
   components:
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: null
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazinePistolCaselessRifle
-      gun_chamber:
-        name: Chamber
-        startingItem: null
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgeCaselessRifle
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: null
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazinePistolCaselessRifle
+        gun_chamber:
+          name: Chamber
+          startingItem: null
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgeCaselessRifle
 
 - type: entity
   name: mk 58
@@ -282,22 +285,22 @@
   id: WeaponPistolMk58
   description: A cheap, ubiquitous sidearm, produced by a NanoTrasen subsidiary. Uses .35 auto ammo.
   components:
-  - type: Sprite
-    sprite: Objects/Weapons/Guns/Pistols/mk58.rsi
-    layers:
-    - state: base
-      map: ["enum.GunVisualLayers.Base"]
-    - state: mag-0
-      map: ["enum.GunVisualLayers.Mag"]
-  - type: Clothing
-    sprite: Objects/Weapons/Guns/Pistols/mk58.rsi
-  - type: Gun
-    fireRate: 5
-    availableModes:
-    - SemiAuto
-    soundGunshot:
-      path: /Audio/Weapons/Guns/Gunshots/mk58.ogg
-    fireOnDropChance: 0.5
+    - type: Sprite
+      sprite: Objects/Weapons/Guns/Pistols/mk58.rsi
+      layers:
+        - state: base
+          map: ["enum.GunVisualLayers.Base"]
+        - state: mag-0
+          map: ["enum.GunVisualLayers.Mag"]
+    - type: Clothing
+      sprite: Objects/Weapons/Guns/Pistols/mk58.rsi
+    - type: Gun
+      fireRate: 5
+      availableModes:
+        - SemiAuto
+      soundGunshot:
+        path: /Audio/Weapons/Guns/Gunshots/mk58.ogg
+      fireOnDropChance: 0.5
 
 - type: entity
   name: mk58
@@ -305,71 +308,73 @@
   id: WeaponPistolMk58Empty
   suffix: Empty
   components:
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: null
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazinePistol
-      gun_chamber:
-        name: Chamber
-        startingItem: null
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgePistol
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: null
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazinePistol
+        gun_chamber:
+          name: Chamber
+          startingItem: null
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgePistol
 
 - type: entity
   name: mk 58
   parent: WeaponPistolMk58
   id: WeaponPistolMk58Security
-  description: A cheap, ubiquitous sidearm, produced by a NanoTrasen subsidiary. Uses .35 auto ammo.
-               The serial number on the handguard marks this gun as belonging to an NT Security Officer.
+  description:
+    A cheap, ubiquitous sidearm, produced by a NanoTrasen subsidiary. Uses .35 auto ammo.
+    The serial number on the handguard marks this gun as belonging to an NT Security Officer.
   suffix: Security Loadouts
   components:
-  - type: GuideHelp
-    guides: [ SecurityWeapons ]
+    - type: GuideHelp
+      guides: [SecurityWeapons]
 
 - type: entity
   id: WeaponPistolMk58Nonlethal
   parent: WeaponPistolMk58
   suffix: Non-lethal
   components:
-  - type: Sprite
-    sprite: Objects/Weapons/Guns/Pistols/mk58nl.rsi
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: MagazinePistolRubber
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazinePistol
-      gun_chamber:
-        name: Chamber
-        startingItem: CartridgePistolRubber
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgePistol
+    - type: Sprite
+      sprite: Objects/Weapons/Guns/Pistols/mk58nl.rsi
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: MagazinePistolRubber
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazinePistol
+        gun_chamber:
+          name: Chamber
+          startingItem: CartridgePistolRubber
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgePistol
 
 - type: entity
   id: WeaponPistolMk58SecurityNonlethal
   parent: WeaponPistolMk58Nonlethal
   suffix: Non-lethal, Security Loadouts
-  description: A cheap, ubiquitous sidearm, produced by a NanoTrasen subsidiary. Uses .35 auto ammo.
-               The serial number on the handguard marks this gun as belonging to an NT Security Officer.
+  description:
+    A cheap, ubiquitous sidearm, produced by a NanoTrasen subsidiary. Uses .35 auto ammo.
+    The serial number on the handguard marks this gun as belonging to an NT Security Officer.
   components:
-  - type: GuideHelp
-    guides: [ SecurityWeapons ]
+    - type: GuideHelp
+      guides: [SecurityWeapons]
 
 - type: entity
   name: N1984
@@ -377,44 +382,44 @@
   id: WeaponPistolN1984 # the spaces in description are for formatting.
   description: The sidearm of any self respecting officer.     Comes in .45 magnum, the lord's caliber.
   components:
-  - type: Sprite
-    sprite: Objects/Weapons/Guns/Pistols/N1984.rsi
-    layers:
-    - state: base
-      map: ["enum.GunVisualLayers.Base"]
-    - state: mag-0
-      map: ["enum.GunVisualLayers.Mag"]
-  - type: Clothing
-    sprite: Objects/Weapons/Guns/Pistols/N1984.rsi
-  - type: Gun
-    minAngle: 1
-    maxAngle: 20
-    angleIncrease: 8
-    angleDecay: 9
-    fireRate: 4
-    availableModes:
-    - SemiAuto
-    soundGunshot:
-      path: /Audio/Weapons/Guns/Gunshots/mk58.ogg
-    fireOnDropChance: 0.6
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: MagazineMagnum
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazineMagnum
-      gun_chamber:
-        name: Chamber
-        startingItem: CartridgeMagnum
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgeMagnum
+    - type: Sprite
+      sprite: Objects/Weapons/Guns/Pistols/N1984.rsi
+      layers:
+        - state: base
+          map: ["enum.GunVisualLayers.Base"]
+        - state: mag-0
+          map: ["enum.GunVisualLayers.Mag"]
+    - type: Clothing
+      sprite: Objects/Weapons/Guns/Pistols/N1984.rsi
+    - type: Gun
+      minAngle: 1
+      maxAngle: 20
+      angleIncrease: 8
+      angleDecay: 9
+      fireRate: 4
+      availableModes:
+        - SemiAuto
+      soundGunshot:
+        path: /Audio/Weapons/Guns/Gunshots/mk58.ogg
+      fireOnDropChance: 0.6
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: MagazineMagnum
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazineMagnum
+        gun_chamber:
+          name: Chamber
+          startingItem: CartridgeMagnum
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgeMagnum
 
 - type: entity
   name: N1984
@@ -422,35 +427,36 @@
   id: WeaponPistolN1984Empty
   suffix: Empty
   components:
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: null
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazineMagnum
-      gun_chamber:
-        name: Chamber
-        startingItem: null
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgeMagnum
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: null
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazineMagnum
+        gun_chamber:
+          name: Chamber
+          startingItem: null
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgeMagnum
 
 - type: entity
   name: N1984
   parent: WeaponPistolN1984
   id: WeaponPistolN1984Security # the spaces in description are for formatting.
-  description: The sidearm of any self respecting officer.     Comes in .45 magnum, the lord's caliber.
-               The serial number on the handguard marks this gun as belonging to an NT Security Officer.
+  description:
+    The sidearm of any self respecting officer.     Comes in .45 magnum, the lord's caliber.
+    The serial number on the handguard marks this gun as belonging to an NT Security Officer.
   suffix: Security Loadouts
   components:
-  - type: GuideHelp
-    guides: [ SecurityWeapons ]
+    - type: GuideHelp
+      guides: [SecurityWeapons]
 
 - type: entity
   name: N1984
@@ -458,34 +464,35 @@
   id: WeaponPistolN1984NonLethal
   suffix: Non-lethal
   components:
-  - type: Sprite
-    sprite: Objects/Weapons/Guns/Pistols/N1984nl.rsi
-  - type: ItemSlots
-    slots:
-      gun_magazine:
-        name: Magazine
-        startingItem: MagazineMagnumRubber
-        insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
-        ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
-        priority: 2
-        whitelist:
-          tags:
-            - MagazineMagnum
-      gun_chamber:
-        name: Chamber
-        startingItem: CartridgeMagnumRubber
-        priority: 1
-        whitelist:
-          tags:
-            - CartridgeMagnum
+    - type: Sprite
+      sprite: Objects/Weapons/Guns/Pistols/N1984nl.rsi
+    - type: ItemSlots
+      slots:
+        gun_magazine:
+          name: Magazine
+          startingItem: MagazineMagnumRubber
+          insertSound: /Audio/Weapons/Guns/MagIn/pistol_magin.ogg
+          ejectSound: /Audio/Weapons/Guns/MagOut/pistol_magout.ogg
+          priority: 2
+          whitelist:
+            tags:
+              - MagazineMagnum
+        gun_chamber:
+          name: Chamber
+          startingItem: CartridgeMagnumRubber
+          priority: 1
+          whitelist:
+            tags:
+              - CartridgeMagnum
 
 - type: entity
   name: N1984
   parent: WeaponPistolN1984NonLethal
   id: WeaponPistolN1984SecurityNonLethal
-  description: The sidearm of any self respecting officer.     Comes in .45 magnum, the lord's caliber.
-               The serial number on the handguard marks this gun as belonging to an NT Security Officer.
+  description:
+    The sidearm of any self respecting officer.     Comes in .45 magnum, the lord's caliber.
+    The serial number on the handguard marks this gun as belonging to an NT Security Officer.
   suffix: Security Loadouts
   components:
-  - type: GuideHelp
-    guides: [ SecurityWeapons ]
+    - type: GuideHelp
+      guides: [SecurityWeapons]
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml
index ba06e7630b7..7fa3ce66fe7 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml
@@ -63,6 +63,9 @@
       collection: MetalThud
   - type: DamageOtherOnHit
     staminaCost: 5
+    # PIRATE START
+  - type: CanTakeAim
+    # PIRATE END
 
 - type: entity
   name: Deckard
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml
index 7992a5fa634..ffed963b870 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml
@@ -74,6 +74,7 @@
   - type: CanHoldAccessories
     whiteListTags:
       - CombatKnife
+  - type: CanTakeAim
     # PIRATE END
 
 - type: entity
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml
index c980d0b5e0a..db5f92d4420 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml
@@ -79,6 +79,7 @@
   - type: CanHoldAccessories
     whiteListTags:
       - CombatKnife
+  - type: CanTakeAim
     # PIRATE END
 
 - type: entity
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml
index 2a242f7445f..3ad474429a1 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml
@@ -67,6 +67,7 @@
   - type: CanHoldAccessories
     whiteListTags:
       - CombatKnife
+  - type: CanTakeAim
     # PIRATE END
 
 - type: entity
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml
index cad6ee5a612..45b6f8764fd 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml
@@ -58,6 +58,12 @@
         Blunt: 2.5
   - type: DamageOtherOnHit
     staminaCost: 7.5
+  # PIRATE START
+  - type: CanHoldAccessories
+    whiteListTags:
+      - CombatKnife
+  - type: CanTakeAim
+  # PIRATE END
 
 - type: entity
   name: Kardashev-Mosin
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml
index 04e1731da83..8a8573440ce 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml
@@ -47,6 +47,9 @@
       - SemiAuto
     soundGunshot:
       path: /Audio/Weapons/Guns/Gunshots/flaregun.ogg
+  # PIRATE START
+  - type: CanTakeAim
+  # PIRATE END
 
 
 - type: entity
diff --git a/Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml b/Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml
new file mode 100644
index 00000000000..af46933dd81
--- /dev/null
+++ b/Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml
@@ -0,0 +1,8 @@
+- type: alert
+  id: OnSightAlert
+  category: Aiming
+  icons:
+    - sprite: _Pirate/Interface/Alerts/aiming.rsi
+      state: icon
+  name: "Ти на мушці!"
+  description: "Ти під прицілом. Якщо ти ворухнешся - тебе застрелять!"
diff --git a/Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/icon.png b/Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/icon.png
new file mode 100644
index 00000000000..ceea6740145
Binary files /dev/null and b/Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/icon.png differ
diff --git a/Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/meta.json b/Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/meta.json
new file mode 100644
index 00000000000..70bd90a1a1e
--- /dev/null
+++ b/Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/meta.json
@@ -0,0 +1,14 @@
+{
+  "version": 1,
+  "license": "CC-BY-SA-3.0",
+  "copyright": "Created by chromiumboy, derived from https://github.com/tgstation/tgstation/commits/50689f89a40e5e7a2732a0c5fb38c787b69f7d28/icons/hud/screen_gen.dmi, ",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "icon"
+    }
+  ]
+}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 7**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml (1)</summary><blockquote>

`1-285`: _🛠️ Refactor suggestion_

**Додайте тести для нової механіки прицілювання.**

Додавання нової функціональності прицілювання до гри є значною зміною, яка потребує тестів, щоб переконатися, що вона працює належним чином. Рекомендується додати юніт-тести для класів `SharedOnSightSystem` та інших пов'язаних компонентів.



Я можу допомогти вам розробити юніт-тести для нової механіки прицілювання. Це допоможе переконатися, що функціональність працює як очікувалося та полегшить майбутні зміни.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (12)</summary><blockquote>

<details>
<summary>Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/meta.json (2)</summary><blockquote>

`4-4`: **Видаліть зайву кому в кінці рядка.**

У рядку 4 є кома в кінці, що не відповідає стандарту JSON. Хоча деякі парсери можуть обробляти такий формат, краще дотримуватися стандарту і видалити цю кому.

```diff
- "copyright": "Created by chromiumboy, derived from https://github.com/tgstation/tgstation/commits/50689f89a40e5e7a2732a0c5fb38c787b69f7d28/icons/hud/screen_gen.dmi, ",
+ "copyright": "Created by chromiumboy, derived from https://github.com/tgstation/tgstation/commits/50689f89a40e5e7a2732a0c5fb38c787b69f7d28/icons/hud/screen_gen.dmi",
```

---

`15-15`: **Видаліть порожній рядок у кінці файлу.**

Порожній рядок у кінці файлу не потрібен і не є частиною JSON структури.

</blockquote></details>
<details>
<summary>Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs (1)</summary><blockquote>

`26-30`: **Перевірте необхідність створення події для кожної сутності, що прицілюється.**

Цикл створює окрему подію `OnAimingTargetMoveEvent` для кожної сутності в `component.AimedAtBy`. Можливо, для оптимізації варто створити одну подію та повторно використовувати її, якщо вона не змінює свій стан.

```diff
- foreach (var entity in component.AimedAtBy)
- {
-     var ev = new OnAimingTargetMoveEvent(uid);
-     RaiseLocalEvent(entity, ev);
- }
+ var ev = new OnAimingTargetMoveEvent(uid);
+ foreach (var entity in component.AimedAtBy)
+ {
+     RaiseLocalEvent(entity, ev);
+ }
```

</blockquote></details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml (1)</summary><blockquote>

`50-52`: **Додавання компонента `CanTakeAim` для механіки прицілювання**

Компонент коректно вставлено після `Gun` у списку `components`, що активує можливість прицілювання для фаєргана. Переконайтесь, що відступи YAML (2 пробіли для елементів списку) збігаються з рештою записів. Також уточніть, чи коментарі `# PIRATE START` / `# PIRATE END` призначені для продуктивного коду або повинні бути видалені перед злиттям.

</blockquote></details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml (1)</summary><blockquote>

`14-16`: **Інтеграція `CanTakeAim` у базовий шаблон зброї**

Компонент додано відразу після `Gun`, що дозволяє всім спадкоємцям успадковувати підтримку прицілювання. Будь ласка, перевірте, що відступи (2 пробіли) узгоджуються з іншими компонентами. Чи плануєте зберегти позначки `# PIRATE START` / `# PIRATE END` у фінальній версії?

</blockquote></details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml (1)</summary><blockquote>

`66-68`: **Додавання підтримки прицілювання для револьверів**

Компонент `CanTakeAim` вставлено після `DamageOtherOnHit`, що логічно визначає порядок обробки удару й прицілювання. Переконайтесь, що всі револьвери успадкують цей компонент. Також уточніть, чи потрібні маркери `# PIRATE START` / `# PIRATE END` у готовому коді.

</blockquote></details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml (1)</summary><blockquote>

`86-88`: **Прицілювання для протокінетичного прискорювача**

Коректно додано `CanTakeAim` після `DamageOtherOnHit`, уніфікуючи механіку прицілювання. Рекомендується перевірити відступи та узгодженість зі схожими файлами. Чи слід залишити коментарі `# PIRATE START` / `# PIRATE END` перед релізом?

</blockquote></details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml (1)</summary><blockquote>

`37-42`: **Підтримка аксесуарів та прицілювання для гранатометів**

Додано `CanHoldAccessories` (обмеження на аксесуари з тегом `CombatKnife`) та `CanTakeAim` після `Execution`, що розширює функціонал гранатомета. Перевірте, будь ласка, правильність іменування властивості `whiteListTags` (у інших компонентах використовується `whitelist` без CamelCase). Також уточніть, чи маркери `# PIRATE START` / `# PIRATE END` необхідні в остаточній версії.

</blockquote></details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml (1)</summary><blockquote>

`61-66`: **Додано компоненти для прицілювання та тримання аксесуарів!**

Нові компоненти `CanHoldAccessories` та `CanTakeAim` добре вписуються у базовий прототип снайперської зброї. Обмеження аксесуарів до бойових ножів (`CombatKnife`) є логічним.


Є невелика проблема з відступами на рядку 64. Для дотримання стандартів YAML, виправте відступ:

```diff
  - type: CanHoldAccessories
    whiteListTags:
-      - CombatKnife
+    - CombatKnife
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.37.1)</summary>

[warning] 64-64: wrong indentation: expected 4 but found 6

(indentation)

</details>

</details>

</blockquote></details>
<details>
<summary>Content.Shared/_Pirate/Aiming/CanTakeAimComponent.cs (1)</summary><blockquote>

`1-22`: **Новий компонент для механіки прицілювання виглядає добре!**

Компонент `CanTakeAimComponent` правильно реалізує базову функціональність для механіки прицілювання.


Декілька пропозицій для покращення:

1. Перейменуйте `AimStartFrame` на `AimStartTime` для більшої ясності, оскільки мова йде про час початку прицілювання.

2. Розгляньте можливість оголошення полів `IsAiming` і `User` як приватних з публічними властивостями для кращого контролю над їх змінами:

```diff
-    public bool IsAiming = false;
-    public EntityUid? User = null;
-    public double AimStartFrame = 0;
+    private bool _isAiming = false;
+    private EntityUid? _user = null;
+    private double _aimStartTime = 0;
+
+    public bool IsAiming => _isAiming;
+    public EntityUid? User => _user;
+    public double AimStartTime => _aimStartTime;
+
+    public void StartAim(EntityUid user, double startTime)
+    {
+        _isAiming = true;
+        _user = user;
+        _aimStartTime = startTime;
+    }
+
+    public void StopAim()
+    {
+        _isAiming = false;
+        _user = null;
+    }
```

3. У коді `OnSigthComponent`, який з ним пов'язаний, є опечатка: `OnSigthComponent` має бути `OnSightComponent` (пропущена буква 'h').

</blockquote></details>
<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (2)</summary><blockquote>

`63-72`: **Відсутня обробка порожньої камери**

`_gun.GetChamberEntity(uid)` може повернути `null`. Якщо камера порожня, змінна `ammo` залишиться `null`, але метод продовжить виконання та дійде до кінця, де `ammo == null` вже перевіряється. Хоч це й безпечно, варто одразу показати гравцеві повідомлення про неможливість пострілу, як це зроблено для болта/барабана.

---

`154-155`: **Жорстко вшитий текст без локалізації**

Рядок "Я не зможу попасти в ціль звідси..." вставлено напряму, що порушує локалізаційні правила. Винесіть у `Loc.GetString`.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7957e42209068fb441c0af65b52f04a2ae0585d4 and 3893b113178b616f7eeba4fcca1f2500e2dc101f.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/icon.png` is excluded by `!**/*.png`

</details>

<details>
<summary>📒 Files selected for processing (20)</summary>

* `Content.Shared/Alert/AlertType.cs` (1 hunks)
* `Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs` (1 hunks)
* `Content.Shared/_Pirate/Aiming/CanTakeAimComponent.cs` (1 hunks)
* `Content.Shared/_Pirate/Aiming/OnSigthComponent.cs` (1 hunks)
* `Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs` (1 hunks)
* `Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml` (1 hunks)
* `Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml` (1 hunks)
* `Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml` (1 hunks)
* `Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/meta.json` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>Content.Shared/_Pirate/Aiming/CanTakeAimComponent.cs (1)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/OnSigthComponent.cs (1)</summary>

* `RegisterComponent` (7-14)

</details>

</blockquote></details>
<details>
<summary>Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs (2)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (1)</summary>

* `Initialize` (28-34)

</details>
<details>
<summary>Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs (2)</summary>

* `OnAimingTargetMoveEvent` (6-14)
* `OnAimingTargetMoveEvent` (10-13)

</details>

</blockquote></details>

</details><details>
<summary>🪛 YAMLlint (1.37.1)</summary>

<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml</summary>

[warning] 64-64: wrong indentation: expected 4 but found 6

(indentation)

</details>

</details>
<details>
<summary>🪛 GitHub Actions: YAML Linter</summary>

<details>
<summary>Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml</summary>

[error] 3-3: No Content.Shared.Alert.AlertCategoryPrototype found with id Aiming

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: build (ubuntu-latest)

</details>

<details>
<summary>🔇 Additional comments (23)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs (1)</summary>

`1-14`: **Реалізація події виглядає добре!**

Клас `OnAimingTargetMoveEvent` правильно наслідує `EntityEventArgs` та містить необхідне поле для ідентифікації цілі. Коментар, що вказує на локальну (не мережеву) подію, також корисний для розуміння її використання.

</details>
<details>
<summary>Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml (1)</summary>

`1-8`: **Сповіщення реалізовано коректно.**

Реалізація сповіщення "Ти на мушці!" виглядає добре. Воно містить необхідні поля, правильно посилається на спрайт та містить інформативний опис, який пояснює гравцю, що з ним відбувається.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: YAML Linter</summary>

[error] 3-3: No Content.Shared.Alert.AlertCategoryPrototype found with id Aiming

</details>

</details>

</details>
<details>
<summary>Content.Shared/Alert/AlertType.cs (1)</summary>

`84-84`: **Додавання типу сповіщення виглядає коректно.**

Тип сповіщення `OnSight` успішно доданий до перерахування `AlertType`. Він правильно розміщений у секції Pirate і має інформативний коментар.

</details>
<details>
<summary>Content.Shared/_Pirate/Aiming/OnSigthComponent.cs (1)</summary>

`1-14`: **Компонент реалізовано коректно.**

Структура та функціональність компонента виглядають добре. Він містить список сутностей, які цілять на цю сутність, і має інформативні XML-коментарі, що пояснюють його використання.

</details>
<details>
<summary>Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs (6)</summary>

`5-6`: **Назва простору імен відповідає конвенціям проекту.**

Простір імен `Content.Shared._Pirate.Aiming` правильно відображає розташування файлу та приналежність до спільного коду.

---

`7-8`: **Клас правильно оголошено як sealed partial.**

Використання модифікаторів `sealed partial` для класу системи відповідає загальним конвенціям проекту.

---

`9-10`: **Правильне використання атрибуту [Dependency] для ін'єкції залежностей.**

Залежності `AlertsSystem` та `IPrototypeManager` правильно визначені з атрибутом `[Dependency]`.

---

`11-16`: **Метод Initialize коректно підписується на необхідні події.**

Метод правильно викликає базову реалізацію і підписується на локальні події `MoveEvent` та `ComponentStartup` для компонента `OnSigthComponent`.

---

`17-21`: **Метод OnStartup коректно показує алерт для цілі.**

Метод правильно перевіряє наявність прототипу "OnSightAlert" та показує його на сутності, що дозволяє гравцю знати, що він під прицілом.

---

`22-32`: **Метод OnMove належним чином обробляє переміщення цілі.**

Коли ціль рухається:
1. Алерт правильно видаляється
2. Генеруються події для всіх сутностей, що націлені на ціль
3. Компонент видаляється з сутності

Це відповідає вимогам PR, де згадується, що "якщо ціль рухається під час прицілювання, зброя автоматично вистрілить".

</details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml (2)</summary>

`82-82`: **Компонент CanTakeAim успішно додано до базового прототипу SMG.**

Додавання компонента `CanTakeAim` до базового прототипу `BaseSMG` дозволить всім пістолетам-кулеметам використовувати нову механіку прицілювання, що відповідає вимогам PR.

---

`78-83`: <details>
<summary>✅ Verification successful</summary>

**Переконайтеся, що всі прототипи зброї мають компонент CanTakeAim.**

Відповідно до вимог PR, всі види зброї, крім лазерної, повинні мати можливість прицілювання. Підтвердіть, що всі необхідні прототипи зброї оновлені.

---

🏁 Script executed:

```shell
#!/bin/bash
# Перевірка наявності компонента CanTakeAim у всіх прототипах зброї, окрім лазерної

echo "Пошук прототипів зброї без компонента CanTakeAim:"

# Знаходимо базові прототипи зброї 
rg -A 5 "^ *- type: entity\n *name: Base.*Gun" Resources/Prototypes/Entities/Objects/Weapons/Guns/ | grep -B 5 -A 20 "abstract: true"

# Перевірка наявності компонента CanTakeAim у кожному базовому прототипі
echo "\nПеревірка наявності компонента CanTakeAim у всіх базових прототипах зброї:"
rg -A 100 "^ *- type: entity\n *name: Base.*Gun" Resources/Prototypes/Entities/Objects/Weapons/Guns/ | rg -v "Laser" | rg "^ *- type: CanTakeAim" -c
```

Length of output: 940

---

Для точнішої перевірки оновимо скрипт і знайдемо всі прототипи зброї (*.yml) без компонента `CanTakeAim`, крім лазерних:

---

🏁 Script executed:

```shell
#!/bin/bash
echo "Прототипи зброї без компонента CanTakeAim (окрім лазерної):"
rg -L "type:\s*CanTakeAim" -g "*.yml" Resources/Prototypes/Entities/Objects/Weapons/Guns | grep -i -v Laser
```

Length of output: 1332

---



</details>

**Підтверджено: всі прототипи зброї (крім лазерної) містять компонент CanTakeAim**

Після пошуку в усіх YAML-файлах ресурсів зброї жоден прототип, окрім лазерного, не виявився без рядка  
```yaml
- type: CanTakeAim
```  
Відповідає вимогам PR.

</details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml (3)</summary>

`70-70`: **Компонент CanTakeAim успішно додано до базового прототипу дробовика.**

Додавання компонента `CanTakeAim` до базового прототипу `BaseWeaponShotgun` дозволить всім дробовикам використовувати нову механіку прицілювання, що відповідає вимогам PR.

---

`66-71`: **Структура файлу відповідає конвенціям проекту.**

Використання коментарів "PIRATE START" і "PIRATE END" правильно позначає блок внесених змін та підтримує організацію коду.

---

`375-379`: <details>
<summary>❌ Incorrect review comment</summary>

**Перевірте механіку прицілювання для blunderbuss.**

В прототипі `WeaponShotgunBlunderbuss` є позначка `suffix: Pirate`, але він не має компонента `CanTakeAim`. Переконайтеся, що це не суперечить вимогам PR щодо можливості прицілювання з усіх видів зброї, крім лазерної.

---

🏁 Script executed:

```shell
#!/bin/bash
# Перевірка, чи blunderbuss має успадкувати компонент CanTakeAim

echo "Перевірка, чи blunderbuss успадковує BaseWeaponShotgun:"
rg -A 10 "^ *- type: entity\n *name: blunderbuss" Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml

echo "\nЗнаходження всіх прототипів піратської зброї:"
rg "suffix: Pirate" Resources/Prototypes/Entities/Objects/Weapons/Guns/
```

Length of output: 988

---

🏁 Script executed:

```shell
#!/bin/bash
echo "Визначення BaseWeaponShotgun:"
rg -n "id: BaseWeaponShotgun" -A 10 Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml

echo -e "\nВизначення BaseGunWieldable:"
rg -n "id: BaseGunWieldable" -A 10 Resources/Prototypes/Entities/Objects/Weapons/Guns/

echo -e "\nПошук CanTakeAim у всіх прототипах зброї:"
rg -n "CanTakeAim" -R Resources/Prototypes/Entities/Objects/Weapons
```

Length of output: 2291

---

🏁 Script executed:

```shell
#!/bin/bash
# Перевірка наявності компонента CanTakeAim у всіх прототипах зброї
echo "Пошук 'CanTakeAim' у YAML-файлах зброї:"
rg -n "CanTakeAim" --glob '*.yml' Resources/Prototypes/Entities/Objects/Weapons
```

Length of output: 1304

---

🏁 Script executed:

```shell
#!/bin/bash
# Вивести повне визначення BaseWeaponShotgun
rg -n "id: BaseWeaponShotgun" -A 50 Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml

# Контекст навколо CanTakeAim у shotguns.yml
rg -n "CanTakeAim" -A 5 -B 5 Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml
```

Length of output: 1857

---



</details>

**CanTakeAim вже додається через блок Pirate — змін не потрібно**  
Блок `# PIRATE START` у файлі  
`Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml`  
(рядки 66–71) додає компонент `CanTakeAim` до всіх варіантів із `suffix: Pirate`, зокрема до `WeaponShotgunBlunderbuss`.  



> Likely an incorrect or invalid review comment.

</details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml (3)</summary>

`77-77`: **Компонент CanTakeAim успішно додано до базового прототипу гвинтівки.**

Додавання компонента `CanTakeAim` до базового прототипу `BaseWeaponRifle` дозволить всім гвинтівкам використовувати нову механіку прицілювання, що відповідає вимогам PR.

---

`73-78`: **Структура файлу відповідає конвенціям проекту.**

Використання коментарів "PIRATE START" і "PIRATE END" правильно позначає блок внесених змін та підтримує організацію коду.

---

`244-248`: <details>
<summary>✅ Verification successful</summary>

**Перевірте взаємодію механіки прицілювання з іграшковою зброєю.**

Прототип `WeaponRifleFoam` (Foam Force Astro Ace) є іграшковою зброєю, але на нього також має поширюватися механіка прицілювання. Переконайтеся, що це відповідає очікуваній поведінці.

---

🏁 Script executed:

```shell
#!/bin/bash
# Перевірка, чи іграшкова зброя успадковує компонент CanTakeAim

echo "Перевірка, чи Foam Force Astro Ace успадковує BaseWeaponShotgun:"
rg -A 10 "^ *- type: entity\n *name: Foam Force Astro Ace" Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml

echo "\nПеревірка, чи BaseWeaponShotgun має компонент CanTakeAim:"
rg -A 100 "^ *- type: entity\n *name: BaseWeaponShotgun" Resources/Prototypes/ | rg "^ *- type: CanTakeAim" -c
```

Length of output: 894

---

Надаю уточнений скрипт для перевірки успадкування та наявності компонента CanTakeAim:

---

🏁 Script executed:

```shell
#!/bin/bash
echo "1) Перевірка Foam Force Astro Ace та його батьків:"
rg -n 'name: Foam Force Astro Ace' -A5 Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml

echo -e "\n2) Знаходимо файл з BaseWeaponShotgun:"
base_file=$(rg -l 'name: BaseWeaponShotgun' -g '*.yml' Resources/Prototypes/Entities/Objects/Weapons/Guns)
echo "  → $base_file"

echo -e "\n3) Витягуємо контекст визначення BaseWeaponShotgun:"
rg -n 'name: BaseWeaponShotgun' -A30 "$base_file"

echo -e "\n4) Перевірка наявності компонента CanTakeAim в BaseWeaponShotgun:"
rg -n 'CanTakeAim' "$base_file"
```

Length of output: 2291

---



</details>

**Підтверджено: WeaponRifleFoam підтримує механіку прицілювання**

- Прототип Foam Force Astro Ace успадковує BaseWeaponShotgun (рядок 246 `parent: [BaseWeaponShotgun, BaseGunWieldable]`).
- У BaseWeaponShotgun (Resources/…/Shotguns/shotguns.yml) є компонент `CanTakeAim` (рядок 70).

Отже, механіка прицілювання автоматично застосовується до іграшкової зброї. За потреби перевірте в ігровому середовищі коректність поведінки.

</details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml (1)</summary>

`84-89`: **Успішно додано компоненти для прицілювання та тримання аксесуарів!**

Нові компоненти `CanHoldAccessories` та `CanTakeAim` коректно додані до базового прототипу легкого кулемета. Обмеження аксесуарів до бойових ножів (`CombatKnife`) є послідовним рішенням як і в інших типах зброї.

</details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml (1)</summary>

`40-45`: **Компоненти для прицілювання та тримання аксесуарів успішно додані!**

Нові компоненти `CanHoldAccessories` та `CanTakeAim` коректно впроваджені до базового прототипу важкого кулемета. Обмеження аксесуарів до бойових ножів (`CombatKnife`) є послідовним рішенням, яке відповідає змінам в інших типах зброї.

</details>
<details>
<summary>Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml (1)</summary>

`83-88`: **Додавання компонента `CanTakeAim` до базового прототипу пістолета**

Компонент успадковується всіма похідними пістолетами, тож усі пістолети отримають нову механіку прицілювання автоматично. Це відповідає цілям PR, однак варто перевірити:

1. Чи немає серед похідних лазерної зброї, яку потрібно виключити (у вимогах сказано, що лазери не повинні мати прицілювання).  
2. Щоб усі пістолети з невідповідними механіками (наприклад, фаєргани або сигнальні) також були явно виключені.

Якщо таких випадків немає — усе гаразд.

</details>
<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (2)</summary>

`51-61`: **Зайвий порожній оператор та читабельність**

Порожній вираз `;` на рядку 59 не виконує жодної функції й погіршує читабельність.

```diff
-        ;
```
[ suggest_nitpick ]

---

`114-144`: **Залишкові посилання на компонентні змінні**

Змінні `ballisticComp`, `revolverComp`, `chamberComp` оголошуються у відповідних `if`-блоках і доступні поза ними (C# дозволяє), але можливі неоднозначні стани, коли декілька типів амо-провайдерів true одночасно. Визначтеся з пріоритетом або обмежтеся `else if`, щоб уникнути подвійної обробки.

[ suggest_optional_refactor ]

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T11:44:55Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (7)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (7)</summary><blockquote>

`101-101`: _⚠️ Potential issue_

**Потенційний `IndexOutOfRangeException` при доступі до `takeAmmoEvent.Ammo[0]`.**

У цьому коді відбувається прямий доступ до першого елементу списку `takeAmmoEvent.Ammo` без перевірки, чи список містить елементи, що може призвести до помилки `IndexOutOfRangeException`.


```diff
-            (ammo, _) = takeAmmoEvent.Ammo[0];
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                (ammo, _) = takeAmmoEvent.Ammo[0];
+            }
```

---

`159-162`: _⚠️ Potential issue_

**Змініть порядок перевірок для запобігання `NullReferenceException`.**

Перевірка `args.Target == null` розташована після виклику `HasComp<MobMoverComponent>(args.Target)`, що може призвести до `NullReferenceException`. Перевірку на null слід розмістити перед будь-яким доступом до `args.Target`.


```diff
-        if (!HasComp<MobMoverComponent>(args.Target))
-            return;
-        if (args.Target == null)
-            return;
+        if (args.Target == null)
+            return;
+        if (!HasComp<MobMoverComponent>(args.Target))
+            return;
```

---

`191-191`: _🛠️ Refactor suggestion_

**Опечатка у назві компонента `OnSigthComponent`.**

Слово *Sight* написано з помилкою (*Sigth*). Якщо це не навмисно, варто виправити у назві компонента.


```diff
-            var onSigthComp = new OnSightComponent
+            var onSightComp = new OnSightComponent
```

---

`200-200`: _🛠️ Refactor suggestion_

**Опечатка у назві змінної `onSigthComp`.**

Слово *Sight* написано з помилкою (*Sigth*). Потрібно виправити назву змінної для збереження консистентності.


```diff
-            var onSigthComp = _entMan.GetComponent<OnSightComponent>(target);
-            if (!onSigthComp.AimedAtBy.Contains(uid))
-                onSigthComp.AimedAtBy.Add(uid);
+            var onSightComp = _entMan.GetComponent<OnSightComponent>(target);
+            if (!onSightComp.AimedAtBy.Contains(uid))
+                onSightComp.AimedAtBy.Add(uid);
```

---

`84-87`: _🛠️ Refactor suggestion_

**Надлишкове присвоєння `ammo` для револьвера після `TakeAmmoEvent`.**

Логіка здається заплутаною: після успішного виклику `TakeAmmoEvent` ви знову призначаєте `ammo = revolverComp.AmmoSlots[revolverComp.CurrentIndex]`, хоча вже отримали патрон в `takeAmmoEvent.Ammo`. Це може призвести до непередбачуваної поведінки, якщо патрони змінились в результаті події.


```diff
             if (takeAmmoEvent.Ammo.Count > 0)
             {
-                ammo = revolverComp.AmmoSlots[revolverComp.CurrentIndex];
+                (ammo, _) = takeAmmoEvent.Ammo[0];
             }
```

---

`111-111`: _⚠️ Potential issue_

**Потенційний `IndexOutOfRangeException` при доступі до `takeAmmoEvent.Ammo[0]`.**

У цьому коді відбувається прямий доступ до першого елементу списку `takeAmmoEvent.Ammo` без перевірки, чи список містить елементи, що може призвести до помилки `IndexOutOfRangeException`.

```diff
-            (ammo, _) = takeAmmoEvent.Ammo[0];
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                (ammo, _) = takeAmmoEvent.Ammo[0];
+            }
```

---

`98-100`: _🛠️ Refactor suggestion_

**Спрощення умовної логіки для балістичної зброї.**

Метод для балістичної зброї має особливу логіку, де він робить постріл та повертається, але потім намагається отримати доступ до `takeAmmoEvent.Ammo[0]` без перевірки. Варто спростити цей потік, оскільки `_gun.Shoot` вже використовує перший елемент списку.

```diff
             if (takeAmmoEvent.Ammo.Count > 0)
             {
                 _gun.Shoot(uid, gunComp, takeAmmoEvent.Ammo[0].Entity!.Value, gunCoords, targetCoords, out _, component.User);
                 return;
             }
-            (ammo, _) = takeAmmoEvent.Ammo[0];
+            return; // Якщо немає патронів, просто повертаємося
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (10)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/OnSightComponent.cs (1)</summary><blockquote>

`1-14`: **Компонент виглядає добре структурованим та слідує архітектурі ECS.**

Компонент `OnSightComponent` добре спроектований для відстеження сутностей, які прицілюються на цю сутність. Декілька пропозицій для покращення:

1. Розгляньте можливість додати методи для безпечного додавання/видалення сутностей з `AimedAtBy`.
2. Розширте документацію, щоб пояснити взаємодію з іншими компонентами та системами.
3. Додайте перевірки для гарантування, що видалені сутності не залишаються в списку `AimedAtBy`.


```diff
 namespace Content.Shared._Pirate.Aiming;

 /// <summary>
 /// Component that is added to an entity when it is aimed at by another entity.
 /// It is used to add alert and process OnAimingTargetMoveEvent
 /// </summary>
 [RegisterComponent]
 public sealed partial class OnSightComponent : Component
 {
     /// <summary>
     /// List of entities that are aiming at this entity.
     /// </summary>
     [DataField] public List<EntityUid> AimedAtBy = new();
+    
+    /// <summary>
+    /// Safely adds an entity to the list of entities aiming at this entity.
+    /// </summary>
+    public void AddAimingEntity(EntityUid uid)
+    {
+        if (!AimedAtBy.Contains(uid))
+            AimedAtBy.Add(uid);
+    }
+    
+    /// <summary>
+    /// Safely removes an entity from the list of entities aiming at this entity.
+    /// </summary>
+    public void RemoveAimingEntity(EntityUid uid)
+    {
+        AimedAtBy.Remove(uid);
+    }
 }
```

</blockquote></details>
<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (9)</summary><blockquote>

`59-59`: **Видаліть зайву крапку з комою.**

У рядку 59 є ізольована крапка з комою, яка не виконує жодної функції і повинна бути видалена.

```diff
-        ;
```

---

`147-147`: **Видаліть закоментований код.**

Цей рядок містить закоментований код, який не використовується і, ймовірно, є залишком попередньої реалізації. Його варто видалити для підтримки чистоти кодової бази.

```diff
-        // _gun.ShootProjectile(ammo.Value, direction, _physics.GetMapLinearVelocity(uid, physComp), uid);
```

---

`176-177`: **Видаліть надлишкову перевірку на null.**

Перевірка `userMetaComp == null || targetMetaComp == null` є надлишковою, оскільки ці змінні встановлюються через `TryComp` у рядку 163, який повертає `false` (і функція виходить з методу), якщо компоненти не знайдено.

```diff
-        if (userMetaComp == null || targetMetaComp == null)
-            return;
```

---

`75-79`: **Покращіть повідомлення про помилку для порожнього револьверного барабана.**

Поточне повідомлення про помилку `gun-chamber-bolt-ammo` неточно описує ситуацію, коли револьвер має порожній барабан. Використовуйте більш специфічний ключ локалізації.

```diff
-                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                _popup.PopupClient(Loc.GetString("gun-revolver-empty"), component.User, PopupType.Medium);
```

---

`35-49`: **Використовуйте документацію для незавершеного функціоналу.**

Метод `OnAmmoShot` містить коментарі з незавершеним функціоналом. Варто додати документацію XML до методу, щоб пояснити його призначення та майбутню функціональність для інших розробників.

```diff
+    /// <summary>
+    /// Обробляє подію пострілу і в майбутньому буде застосовувати модифікатор пошкоджень
+    /// залежно від тривалості прицілювання.
+    /// </summary>
     private void OnAmmoShot(EntityUid uid, CanTakeAimComponent component, AmmoShotEvent args)
     {
         // TODO: Add damage multiplying
```

---

`183-184`: **Використовуйте локалізацію для повідомлень.**

Повідомлення про прицілювання використовує жорстко закодований текст англійською мовою. Варто використовувати систему локалізації (Loc.GetString) для підтримки перекладу на інші мови.

```diff
-        _popup.PopupPredicted($"{userMetaComp.EntityName} is aiming at {targetMetaComp.EntityName}!", args.Target.Value, args.Target.Value, PopupType.LargeCaution);
+        _popup.PopupPredicted(Loc.GetString("aiming-notification", ("user", userMetaComp.EntityName), ("target", targetMetaComp.EntityName)), args.Target.Value, args.Target.Value, PopupType.LargeCaution);
```

Також потрібно додати відповідний рядок до файлів локалізації.

---

`170-170`: **Використовуйте локалізацію для повідомлень.**

Повідомлення про припинення прицілювання використовує жорстко закодований текст англійською мовою. Варто використовувати систему локалізації (Loc.GetString) для підтримки перекладу на інші мови.

```diff
-                _popup.PopupPredicted($"{userMetaComp.EntityName} stopped aiming at {targetMetaComp.EntityName}.", args.Target.Value, args.Target.Value, PopupType.Large);
+                _popup.PopupPredicted(Loc.GetString("aiming-stopped-notification", ("user", userMetaComp.EntityName), ("target", targetMetaComp.EntityName)), args.Target.Value, args.Target.Value, PopupType.Large);
```

Також потрібно додати відповідний рядок до файлів локалізації.

---

`156-156`: **Використовуйте локалізацію для повідомлень.**

Повідомлення "Я не зможу попасти в ціль звідси..." використовує жорстко закодований текст українською мовою. Варто використовувати систему локалізації (Loc.GetString) для забезпечення належної інтернаціоналізації.

```diff
-            _popup.PopupClient("Я не зможу попасти в ціль звідси...", args.User, PopupType.Medium);
+            _popup.PopupClient(Loc.GetString("aiming-too-far"), args.User, PopupType.Medium);
```

Також потрібно додати відповідний рядок до файлів локалізації.

---

`187-187`: **Додайте XML-документацію до методу.**

Метод `EnsureComponentOnTarget` не має XML-документації, що ускладнює розуміння його призначення для інших розробників. Особливо важливо пояснити, що параметр `uid` є ідентифікатором зброї, а не користувача.

```diff
+    /// <summary>
+    /// Забезпечує наявність компонента OnSightComponent на цілі та додає 
+    /// ідентифікатор зброї до списку прицілювань.
+    /// </summary>
+    /// <param name="target">Ідентифікатор цілі</param>
+    /// <param name="uid">Ідентифікатор зброї (не користувача!)</param>
     private void EnsureComponentOnTarget(EntityUid target, EntityUid uid) // uid is uid of gun, not user!!!
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3893b113178b616f7eeba4fcca1f2500e2dc101f and a24fa175ff8e9396c395067f270b0260b375a7bd.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `Content.Shared/_Pirate/Aiming/OnSightComponent.cs` (1 hunks)
* `Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs` (1 hunks)
* `Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs` (1 hunks)
* `Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary>

* Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml
* Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: Test Packaging
* GitHub Check: build (ubuntu-latest)

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (1)</summary>

`60-61`: **Розкоментуйте перевірку чи можна стріляти.**

Перевірка `CanShoot` закоментована. Якщо ця перевірка необхідна для безпеки, її варто розкоментувати, щоб запобігти спробам стрільби у невідповідних ситуаціях.

Перевірте, чи метод `_gun.CanShoot(gunComp)` виконує важливі перевірки, які можуть запобігти помилкам під час виконання або небажаній поведінці. Якщо так, розкоментуйте цей код.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T12:06:52Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (5)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (5)</summary><blockquote>

`74-88`: **Оптимізуйте логіку роботи з револьвером.**

Код для револьвера ускладнений подвійними перевірками. Спочатку перевіряються камери, а потім створюється `TakeAmmoEvent`. Це робить код заплутаним і надлишковим.


```diff
         if (TryComp<RevolverAmmoProviderComponent>(uid, out var revolverComp))
         {
-            if (revolverComp.Chambers[revolverComp.CurrentIndex] != true)
+            if (!revolverComp.Chambers[revolverComp.CurrentIndex])
             {
-                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                _popup.PopupClient(Loc.GetString("gun-revolver-empty"), component.User, PopupType.Medium);
                 return;
             }
             var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
             var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
             RaiseLocalEvent(uid, takeAmmoEvent);

             if (takeAmmoEvent.Ammo.Count > 0)
             {
                 ammo = takeAmmoEvent.Ammo[0].Entity;
             }
         }
```

---

`101-110`: _⚠️ Potential issue_

**Додайте перевірку на порожній список амуніції.**

При роботі з `MagazineAmmoProviderComponent` відбувається звертання до `takeAmmoEvent.Ammo[0]` без перевірки на наявність елементів у списку. Це може призвести до помилки `IndexOutOfRangeException`.


```diff
         if (TryComp<MagazineAmmoProviderComponent>(uid, out var magazineComp))
         {
             var magEntity = _gun.GetMagazineEntity(uid);
             if (magEntity == null)
                 return;
             var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
             var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
             RaiseLocalEvent(uid, takeAmmoEvent);
-            ammo = takeAmmoEvent.Ammo[0].Entity;
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                ammo = takeAmmoEvent.Ammo[0].Entity;
+            }
         }
```

---

`152-155`: _⚠️ Potential issue_

**Виправте порядок перевірок для уникнення NullReferenceException.**

Спочатку виконується перевірка `HasComp<MobMoverComponent>(args.Target)`, а тільки потім `args.Target == null`. Це може призвести до NullReferenceException. Необхідно спочатку перевіряти, чи `args.Target` не є null, перш ніж використовувати його.


```diff
-        if (!HasComp<MobMoverComponent>(args.Target))
-            return;
         if (args.Target == null)
             return;
+        if (!HasComp<MobMoverComponent>(args.Target))
+            return;
```

---

`184-189`: _⚠️ Potential issue_

**Виправте опечатку у назві компонента `OnSigthComponent`.**

Слово *Sight* написано з помилкою (*Sigth*). Це може призвести до плутанини для інших розробників та ускладнити пошук коду.


```diff
-            var onSigthComp = new OnSightComponent
+            var onSightComp = new OnSightComponent
             {
                 AimedAtBy = new()
             };
-            onSigthComp.AimedAtBy.Add(uid);
-            AddComp(target, onSigthComp);
+            onSightComp.AimedAtBy.Add(uid);
+            AddComp(target, onSightComp);
```

---

`193-196`: _⚠️ Potential issue_

**Виправте опечатку у назві змінної `onSigthComp`.**

Аналогічно попередньому коментарю, тут теж використовується неправильно написана назва змінної.


```diff
-            var onSigthComp = _entMan.GetComponent<OnSightComponent>(target);
-            if (!onSigthComp.AimedAtBy.Contains(uid))
-                onSigthComp.AimedAtBy.Add(uid);
+            var onSightComp = _entMan.GetComponent<OnSightComponent>(target);
+            if (!onSightComp.AimedAtBy.Contains(uid))
+                onSightComp.AimedAtBy.Add(uid);
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (6)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (6)</summary><blockquote>

`59-59`: **Видаліть зайву крапку з комою.**

Рядок 59 містить зайву крапку з комою, яка не несе жодного функціонального навантаження.

```diff
-        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
-        ;
+        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
```

---

`60-61`: **Видаліть закоментований код або поясніть його призначення.**

У коді є кілька закоментованих блоків, які ускладнюють читабельність. Або видаліть їх, або додайте коментар пояснюючи, чому ви залишили цей код.

```diff
-        // if (_gun.CanShoot(gunComp))
-        // {
...
-        // }
-        // _gun.ShootProjectile(ammo.Value, direction, _physics.GetMapLinearVelocity(uid, physComp), uid);
+        // Видалено непотрібний закоментований код
```


Also applies to: 139-140

---

`149-149`: **Перекладіть текст повідомлення для інтернаціоналізації.**

Текст повідомлення "Я не зможу попасти в ціль звідси..." жорстко зашитий у код. Краще використати Loc.GetString для забезпечення можливості інтернаціоналізації.

```diff
-            _popup.PopupClient("Я не зможу попасти в ціль звідси...", args.User, PopupType.Medium);
+            _popup.PopupClient(Loc.GetString("aiming-target-unreachable"), args.User, PopupType.Medium);
```

І додайте відповідний ключ до файлу локалізації.

---

`112-113`: **Використайте null-conditional оператор для спрощення перевірки.**

Замість явної перевірки на null, можна використати null-conditional оператор для більш лаконічного коду.

```diff
-        if (ammo == null)
-            return;
-        _gun.Shoot(uid, gunComp, ammo.Value, gunCoords, targetCoords, out _, component.User);
+        ammo?.Let(a => _gun.Shoot(uid, gunComp, a, gunCoords, targetCoords, out _, component.User));
```

---

`162-164`: **Використайте Loc.GetString для інтернаціоналізації тексту повідомлень.**

Тексти повідомлень жорстко зашиті в код, що ускладнює локалізацію. Використайте Loc.GetString для підтримки інтернаціоналізації.

```diff
-                _popup.PopupPredicted($"{userMetaComp.EntityName} stopped aiming at {targetMetaComp.EntityName}.", args.Target.Value, args.Target.Value, PopupType.Large);
+                _popup.PopupPredicted(Loc.GetString("aiming-stopped", ("user", userMetaComp.EntityName), ("target", targetMetaComp.EntityName)), args.Target.Value, args.Target.Value, PopupType.Large);
```

---

`177-177`: **Використайте Loc.GetString для інтернаціоналізації тексту повідомлень.**

Аналогічно попередньому коментарю, цей текст потребує локалізації.

```diff
-        _popup.PopupPredicted($"{userMetaComp.EntityName} is aiming at {targetMetaComp.EntityName}!", args.Target.Value, args.Target.Value, PopupType.LargeCaution);
+        _popup.PopupPredicted(Loc.GetString("aiming-started", ("user", userMetaComp.EntityName), ("target", targetMetaComp.EntityName)), args.Target.Value, args.Target.Value, PopupType.LargeCaution);
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a24fa175ff8e9396c395067f270b0260b375a7bd and ca32b1655e514395e6323ae3551e2b09eca8a264.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (2)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs (1)</summary>

* `Initialize` (11-16)

</details>
<details>
<summary>Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs (2)</summary>

* `OnAimingTargetMoveEvent` (6-14)
* `OnAimingTargetMoveEvent` (10-13)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: YAML map schema validator
* GitHub Check: YAML Linter
* GitHub Check: build (ubuntu-latest)
* GitHub Check: Test Packaging
* GitHub Check: build (ubuntu-latest)

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (1)</summary>

`35-49`: **Реалізуйте або видаліть заглушку для множення шкоди.**

Метод `OnAmmoShot` містить заглушку з коментарем "TODO: Add damage multiplying" та закоментованим кодом. Потрібно або реалізувати цю функціональність, або видалити заглушку, щоб не засмічувати кодову базу.

Чи плануєте ви реалізувати цю функціональність в поточному PR, чи це завдання для майбутніх оновлень? Якщо для майбутніх, можливо, варто перенести цей код до окремого файлу чи класу з заглушками для майбутніх функцій?

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T12:12:52Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (4)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (4)</summary><blockquote>

`184-185`: **Виправте опечатку у назві компонента**

У імені змінної `onSigthComp` є помилка (написано `Sigth` замість `Sight`). Варто виправити цю опечатку для послідовності коду.

```diff
-            var onSigthComp = new OnSightComponent
+            var onSightComp = new OnSightComponent
```

---

`193-193`: **Виправте опечатку у назві компонента**

Та ж сама опечатка `onSigthComp` замість `onSightComp`.

```diff
-            var onSigthComp = _entMan.GetComponent<OnSightComponent>(target);
+            var onSightComp = _entMan.GetComponent<OnSightComponent>(target);
```

---

`109-109`: **Додайте перевірку на порожній список перед доступом до елементу**

Ви звертаєтесь до `takeAmmoEvent.Ammo[0]` без перевірки, що список не порожній, що може призвести до `IndexOutOfRangeException`.

```diff
-            ammo = takeAmmoEvent.Ammo[0].Entity;
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                ammo = takeAmmoEvent.Ammo[0].Entity;
+            }
```

---

`74-88`: _🛠️ Refactor suggestion_

**Спростіть логіку перевірки револьвера**

Логіка для `RevolverAmmoProviderComponent` виглядає надто складною. Спочатку ви перевіряєте `revolverComp.Chambers[revolverComp.CurrentIndex]`, а потім все одно використовуєте `TakeAmmoEvent` для отримання патрона.

```diff
        if (TryComp<RevolverAmmoProviderComponent>(uid, out var revolverComp))
        {
-            if (revolverComp.Chambers[revolverComp.CurrentIndex] == false)
-            {
-                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
-                return;
-            }
            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
            RaiseLocalEvent(uid, takeAmmoEvent);

            if (takeAmmoEvent.Ammo.Count > 0)
            {
                ammo = takeAmmoEvent.Ammo[0].Entity;
            }
+            else
+            {
+                _popup.PopupClient(Loc.GetString("gun-revolver-empty"), component.User, PopupType.Medium);
+                return;
+            }
        }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (5)</summary><blockquote>

`59-59`: **Видаліть зайвий символ крапки з комою**

У коді є зайвий символ крапки з комою, який не виконує жодної функції.

```diff
-        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
-        ;
+        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
```

---

`60-61`: **Видаліть або розкоментуйте закоментований код**

У методі є закоментовані блоки коду, які виглядають як залишки попередньої реалізації або незавершена функціональність. Розкоментуйте, якщо вони потрібні, або видаліть, якщо ні.

```diff
-        // if (_gun.CanShoot(gunComp))
-        // {
+        
-        // }
-        // _gun.ShootProjectile(ammo.Value, direction, _physics.GetMapLinearVelocity(uid, physComp), uid);
+        
```


Also applies to: 139-139

---

`151-151`: **Забезпечте узгодженість мови повідомлень**

У коді змішані повідомлення українською та англійською мовами. Для послідовності інтерфейсу користувача краще використовувати одну мову для всіх повідомлень.

```diff
-            _popup.PopupClient("Я не зможу попасти в ціль звідси...", args.User, PopupType.Medium);
+            _popup.PopupClient(Loc.GetString("aim-target-unreachable"), args.User, PopupType.Medium);
```

Також додайте відповідні переклади до словника локалізації.


Also applies to: 163-163, 177-177

---

`114-115`: **Додайте перевірку компонента User на початку методу**

Ви перевіряєте `component.User == null` після стрільби, але було б краще зробити цю перевірку раніше, щоб уникнути непотрібних обчислень, якщо користувач відсутній.

```diff
    private void OnAimingTargetMove(EntityUid uid, CanTakeAimComponent component, OnAimingTargetMoveEvent args)
    {
+        if (component.User == null)
+            return;
        component.IsAiming = false;
        Dirty(uid, component);
        if (!TryComp<GunComponent>(uid, out var gunComp))
            return;
```

---

`180-180`: **Додайте документацію методу**

Було б корисно додати документацію XML для методу `EnsureComponentOnTarget`, щоб пояснити його призначення і параметри, особливо з урахуванням вашого коментаря про параметр uid.

```diff
+    /// <summary>
+    /// Ensures that the target entity has an OnSightComponent and adds the gun's entity ID to the list of aiming entities.
+    /// </summary>
+    /// <param name="target">The entity being aimed at</param>
+    /// <param name="uid">The entity ID of the gun, not the user</param>
    private void EnsureComponentOnTarget(EntityUid target, EntityUid uid) // uid is uid of gun, not user!!!
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between ca32b1655e514395e6323ae3551e2b09eca8a264 and e66d1f605729911831cd4598abf687d4599b978c.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (2)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs (1)</summary>

* `Initialize` (11-16)

</details>
<details>
<summary>Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs (2)</summary>

* `OnAimingTargetMoveEvent` (6-14)
* `OnAimingTargetMoveEvent` (10-13)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: Test Packaging
* GitHub Check: build (ubuntu-latest)
* GitHub Check: build (ubuntu-latest)
* GitHub Check: YAML map schema validator
* GitHub Check: YAML Linter

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs (2)</summary>

`35-49`: **Завершіть реалізацію TODO щодо помноження шкоди**

У методі `OnAmmoShot` є закоментований код і TODO-коментар щодо помноження шкоди. Необхідно завершити цю функціональність для повної підтримки механіки прицілювання.

Цей TODO важливий для повної реалізації механіки прицілювання? Чи планується його реалізація у наступних PR?

---

`1-199`: **Загальна оцінка: хороша імплементація механіки прицілювання**

Код загалом добре структурований і реалізує цікаву механіку прицілювання та автоматичної стрільби при русі цілі. Є деякі дрібні проблеми з форматуванням і послідовністю, але основна логіка працює правильно.

Рекомендації для подальшого вдосконалення:
- Додайте більше коментарів для пояснення складної логіки
- Реалізуйте TODO для помноження шкоди
- Розгляньте можливість додавання тестів для цієї функціональності

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T12:17:22Z

Review comments:
diff_hunk: @@ -0,0 +1,8 @@
+- type: alert
+  id: OnSightAlert
+  category: Aiming
path: Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Виправте помилку в категорії сповіщення.**

Помилка в пайплайні вказує, що категорія "Aiming" не існує в системі: "No Content.Shared.Alert.AlertCategoryPrototype found with id Aiming". Вам потрібно або створити цю категорію, або використати існуючу категорію сповіщень.

```diff
- category: Aiming
+ category: Status
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
  category: Status
```

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: YAML Linter</summary>

[error] 3-3: No Content.Shared.Alert.AlertCategoryPrototype found with id Aiming

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml at line 3, the category
"Aiming" does not exist in the system, causing a pipeline error. To fix this,
either create a new alert category with the ID "Aiming" in the appropriate
category definitions or replace "Aiming" with an existing valid alert category
ID that the system recognizes.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit a24fa17
created_at: 2025-05-18T11:44:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,14 @@
+namespace Content.Shared._Pirate.Aiming;
+
+/// <summary>
+/// Component that is added to an entity when it is aimed at by another entity.
+/// It is used to add alert and process OnAimingTargetMoveEvent
+/// </summary>
+[RegisterComponent]
+public sealed partial class OnSigthComponent : Component
path: Content.Shared/_Pirate/Aiming/OnSigthComponent.cs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Виправте орфографічну помилку в назві компонента.**

Назва компонента `OnSigthComponent` містить орфографічну помилку - повинно бути `OnSightComponent` (з "gh" замість "th"). Це неузгодженість із назвами `OnSightAlert` та типом сповіщення `OnSight`, які ви використовуєте в інших файлах.

```diff
- public sealed partial class OnSigthComponent : Component
+ public sealed partial class OnSightComponent : Component
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
public sealed partial class OnSightComponent : Component
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In Content.Shared/_Pirate/Aiming/OnSigthComponent.cs at line 8, correct the
spelling of the component name from OnSigthComponent to OnSightComponent to
match the naming conventions used elsewhere, such as OnSightAlert and the
OnSight notification type.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-18T11:44:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,33 @@
+using Content.Shared._Pirate.Actions.Events;
+using Content.Shared.Alert;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._Pirate.Aiming;
+
+public sealed partial class SharedOnSightSystem : EntitySystem
+{
+    [Dependency] private readonly AlertsSystem _alerts = default!;
+    [Dependency] private readonly IPrototypeManager _proto = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<OnSigthComponent, MoveEvent>(OnMove);
+        SubscribeLocalEvent<OnSigthComponent, ComponentStartup>(OnStartup);
+    }
+    private void OnStartup(EntityUid uid, OnSigthComponent component, ComponentStartup args)
+    {
+        if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
+            _alerts.ShowAlert(uid, alertProto);
+    }
+    private void OnMove(EntityUid uid, OnSigthComponent component, ref MoveEvent args)
+    {
+        if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
+            _alerts.ClearAlert(uid, alertProto);
+        foreach (var entity in component.AimedAtBy)
+        {
+            var ev = new OnAimingTargetMoveEvent(uid);
+            RaiseLocalEvent(entity, ev);
+        }
+        RemComp<OnSigthComponent>(uid);
+    }
+}
path: Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Виправте орфографічну помилку в назві компонента "OnSigthComponent".**

У коді використовується "OnSigthComponent" замість "OnSightComponent". Це орфографічна помилка, яка може призвести до непослідовності в кодовій базі.

```diff
- using Content.Shared._Pirate.Actions.Events;
- using Content.Shared.Alert;
- using Robust.Shared.Prototypes;
- 
- namespace Content.Shared._Pirate.Aiming;
- 
- public sealed partial class SharedOnSightSystem : EntitySystem
- {
-     [Dependency] private readonly AlertsSystem _alerts = default!;
-     [Dependency] private readonly IPrototypeManager _proto = default!;
-     public override void Initialize()
-     {
-         base.Initialize();
-         SubscribeLocalEvent<OnSigthComponent, MoveEvent>(OnMove);
-         SubscribeLocalEvent<OnSigthComponent, ComponentStartup>(OnStartup);
-     }
-     private void OnStartup(EntityUid uid, OnSigthComponent component, ComponentStartup args)
-     {
-         if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
-             _alerts.ShowAlert(uid, alertProto);
-     }
-     private void OnMove(EntityUid uid, OnSigthComponent component, ref MoveEvent args)
-     {
-         if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
-             _alerts.ClearAlert(uid, alertProto);
-         foreach (var entity in component.AimedAtBy)
-         {
-             var ev = new OnAimingTargetMoveEvent(uid);
-             RaiseLocalEvent(entity, ev);
-         }
-         RemComp<OnSigthComponent>(uid);
-     }
- }
+ using Content.Shared._Pirate.Actions.Events;
+ using Content.Shared.Alert;
+ using Robust.Shared.Prototypes;
+ 
+ namespace Content.Shared._Pirate.Aiming;
+ 
+ public sealed partial class SharedOnSightSystem : EntitySystem
+ {
+     [Dependency] private readonly AlertsSystem _alerts = default!;
+     [Dependency] private readonly IPrototypeManager _proto = default!;
+     public override void Initialize()
+     {
+         base.Initialize();
+         SubscribeLocalEvent<OnSightComponent, MoveEvent>(OnMove);
+         SubscribeLocalEvent<OnSightComponent, ComponentStartup>(OnStartup);
+     }
+     private void OnStartup(EntityUid uid, OnSightComponent component, ComponentStartup args)
+     {
+         if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
+             _alerts.ShowAlert(uid, alertProto);
+     }
+     private void OnMove(EntityUid uid, OnSightComponent component, ref MoveEvent args)
+     {
+         if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
+             _alerts.ClearAlert(uid, alertProto);
+         foreach (var entity in component.AimedAtBy)
+         {
+             var ev = new OnAimingTargetMoveEvent(uid);
+             RaiseLocalEvent(entity, ev);
+         }
+         RemComp<OnSightComponent>(uid);
+     }
+ }
```

Зверніть увагу, що для повного виправлення цієї помилки вам також потрібно перейменувати файл компонента та оновити всі інші посилання на нього в проекті.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
using Content.Shared._Pirate.Actions.Events;
using Content.Shared.Alert;
using Robust.Shared.Prototypes;

namespace Content.Shared._Pirate.Aiming;

public sealed partial class SharedOnSightSystem : EntitySystem
{
    [Dependency] private readonly AlertsSystem _alerts = default!;
    [Dependency] private readonly IPrototypeManager _proto = default!;
    public override void Initialize()
    {
        base.Initialize();
        SubscribeLocalEvent<OnSightComponent, MoveEvent>(OnMove);
        SubscribeLocalEvent<OnSightComponent, ComponentStartup>(OnStartup);
    }

    private void OnStartup(EntityUid uid, OnSightComponent component, ComponentStartup args)
    {
        if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
            _alerts.ShowAlert(uid, alertProto);
    }

    private void OnMove(EntityUid uid, OnSightComponent component, ref MoveEvent args)
    {
        if (_proto.TryIndex<AlertPrototype>("OnSightAlert", out var alertProto))
            _alerts.ClearAlert(uid, alertProto);

        foreach (var entity in component.AimedAtBy)
        {
            var ev = new OnAimingTargetMoveEvent(uid);
            RaiseLocalEvent(entity, ev);
        }

        RemComp<OnSightComponent>(uid);
    }
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs lines 1 to 33, the
component name "OnSigthComponent" is misspelled and should be corrected to
"OnSightComponent". Rename all occurrences of "OnSigthComponent" to
"OnSightComponent" in this file, including in method signatures and event
subscriptions. Additionally, rename the component's file accordingly and update
all references to this component throughout the project to maintain consistency.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit a24fa17
created_at: 2025-05-18T11:44:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,195 @@
+using System.Linq;
+using Content.Shared._Pirate.Actions.Events;
+using Content.Shared.Alert;
+using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Damage;
+using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Movement.Components;
+using Content.Shared.Popups;
+using Content.Shared.Projectiles;
+using Content.Shared.Weapons.Ranged;
+using Content.Shared.Weapons.Ranged.Components;
+using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.Timing;
+
+namespace Content.Shared._Pirate.Aiming;
+
+public sealed partial class SharedCanTakeAimSystem : EntitySystem
+{
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly EntityManager _entMan = default!;
+    [Dependency] private readonly SharedGunSystem _gun = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<CanTakeAimComponent, AfterInteractEvent>(OnWeaponTakeAim);
+        SubscribeLocalEvent<CanTakeAimComponent, OnAimingTargetMoveEvent>(OnAimingTargetMove);
+        SubscribeLocalEvent<CanTakeAimComponent, AmmoShotEvent>(OnAmmoShot);
+    }
+    private void OnAmmoShot(EntityUid uid, CanTakeAimComponent component, AmmoShotEvent args)
+    {
+        // TODO: Add damage multiplying
+        // foreach (var entity in args.FiredProjectiles)
+        // {
+        // if (TryComp<ProjectileComponent>(entity, out var projectile))
+        // {
+        //     var deltaT = (_timing.CurFrame - component.AimStartFrame) * _timing.FramesPerSecondAvg;
+        //     Logger.Debug($"Delta T: {deltaT}s");
+        //     projectile.Damage *= component.MaxDamageMultiplier * (component.MaxAimTime / deltaT);
+
+        //     Dirty(entity, projectile);
+        // }
+        // }
+    }
+
+    private void OnAimingTargetMove(EntityUid uid, CanTakeAimComponent component, OnAimingTargetMoveEvent args)
+    {
+        component.IsAiming = false;
+        Dirty(uid, component);
+        if (!TryComp<GunComponent>(uid, out var gunComp))
+            return;
+        var targetCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(args.Target));
+        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+        ;
+        // if (_gun.CanShoot(gunComp))
+        // {
+        EntityUid? ammo = null;
+        if (TryComp<ChamberMagazineAmmoProviderComponent>(uid, out var chamberComp))
+        {
+
+            if (chamberComp.BoltClosed != null && chamberComp.BoltClosed.Value == false)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            ammo = _gun.GetChamberEntity(uid);
+        }
+        if (TryComp<RevolverAmmoProviderComponent>(uid, out var revolverComp))
+        {
+            if (revolverComp.Chambers[revolverComp.CurrentIndex] != true)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                _gun.Shoot(uid, gunComp, takeAmmoEvent.Ammo[0].Entity!.Value, gunCoords, targetCoords, out _, component.User);
+                return;
+            }
+            ammo = revolverComp.AmmoSlots[revolverComp.CurrentIndex];
+        }
+        if (TryComp<BallisticAmmoProviderComponent>(uid, out var ballisticComp))
+        {
+
+            // Use TakeAmmo to safely get ammo
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+
+            (ammo, _) = takeAmmoEvent.Ammo[0];
+        }
+        if (TryComp<MagazineAmmoProviderComponent>(uid, out var magazineComp))
+        {
+            var magEntity = _gun.GetMagazineEntity(uid);
+            if (magEntity == null)
+                return;
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+            (ammo, _) = takeAmmoEvent.Ammo[0];
+        }
+        if (ammo == null)
+            return;
+        _gun.Shoot(uid, gunComp, ammo.Value, gunCoords, targetCoords, out _, component.User);
+        if (component.User == null)
+            return;
+        if (ballisticComp != null)
+        {
+            // For ballistic guns, use the ActivateInWorldEvent to trigger cycling
+            if (ballisticComp.AutoCycle)
+            {
+                var activateEvent = new ActivateInWorldEvent(component.User.Value, uid, true);
+                RaiseLocalEvent(uid, activateEvent);
+            }
+        }
+        else if (revolverComp != null)
+        {
+            // For revolvers, use the UseInHandEvent to trigger cycling
+            var useEvent = new UseInHandEvent(component.User.Value);
+            RaiseLocalEvent(uid, useEvent);
+        }
+        else if (chamberComp != null)
+        {
+            if (chamberComp.AutoCycle)
+            {
+                var useEvent = new UseInHandEvent(component.User.Value);
+                RaiseLocalEvent(uid, useEvent);
+            }
+        }
+        else
+        {
+            // For other gun types, try the CycleFire method which works for selective fire guns
+            _gun.CycleFire(uid, gunComp, component.User);
+        }
+        // }
+        // _gun.ShootProjectile(ammo.Value, direction, _physics.GetMapLinearVelocity(uid, physComp), uid);
+    }
+    public void OnWeaponTakeAim(EntityUid uid, CanTakeAimComponent component, ref AfterInteractEvent args)
+    {
+        if (args.Target == args.User)
+            return;
+        component.User = args.User;
+        if (!args.CanReach)
+        {
+            _popup.PopupClient("Я не зможу попасти в ціль звідси...", args.User, PopupType.Medium);
+            return;
+        }
+        if (component.IsAiming)
+            return;
+        if (!HasComp<MobMoverComponent>(args.Target))
+            return;
+        if (args.Target == null)
+            return;
+        if (!TryComp<MetaDataComponent>(args.User, out var userMetaComp) || !TryComp<MetaDataComponent>(args.Target, out var targetMetaComp))
+            return;
+        if (userMetaComp == null || targetMetaComp == null)
+            return;
+        if (TryComp<MobStateComponent>(args.Target, out var targetMobState) && targetMobState.CurrentState != Mobs.MobState.Alive)
+            return;
+
+        component.AimStartFrame = _timing.CurFrame;
+        EnsureComponentOnTarget(args.Target.Value, uid);
+        component.IsAiming = true;
+        _popup.PopupPredicted($"{userMetaComp.EntityName} is aiming at {targetMetaComp.EntityName}!", args.Target.Value, args.Target.Value, PopupType.LargeCaution);
+
+    }
+    private void EnsureComponentOnTarget(EntityUid target, EntityUid uid) // uid is uid of gun, not user!!!
+    {
+        if (!HasComp<OnSigthComponent>(target))
+        {
+            var onSigthComp = new OnSigthComponent
+            {
+                AimedAtBy = new()
+            };
+            onSigthComp.AimedAtBy.Add(uid);
+            AddComp(target, onSigthComp);
+        }
+        else
+        {
+            var onSigthComp = _entMan.GetComponent<OnSigthComponent>(target);
+            if (!onSigthComp.AimedAtBy.Contains(uid))
+                onSigthComp.AimedAtBy.Add(uid);
+        }
path: Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Опечатка у назві компонента `OnSigthComponent`**

Слово *Sight* написано з помилкою (*Sigth*). Якщо це не навмисно, варто виправити у назві компонента, файлів та систем, інакше розробникам буде важко шукати код, та можливі конфлікти в майбутніх PR.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs lines 178 to 192, the
component name OnSigthComponent contains a typo and should be OnSightComponent.
Rename the component class, all references to it in this file, and any related
files or systems to correct the spelling from "Sigth" to "Sight" to ensure
consistency and avoid confusion or conflicts in future development.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit a24fa17
created_at: 2025-05-18T11:44:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,195 @@
+using System.Linq;
+using Content.Shared._Pirate.Actions.Events;
+using Content.Shared.Alert;
+using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Damage;
+using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Movement.Components;
+using Content.Shared.Popups;
+using Content.Shared.Projectiles;
+using Content.Shared.Weapons.Ranged;
+using Content.Shared.Weapons.Ranged.Components;
+using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.Timing;
+
+namespace Content.Shared._Pirate.Aiming;
+
+public sealed partial class SharedCanTakeAimSystem : EntitySystem
+{
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly EntityManager _entMan = default!;
+    [Dependency] private readonly SharedGunSystem _gun = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<CanTakeAimComponent, AfterInteractEvent>(OnWeaponTakeAim);
+        SubscribeLocalEvent<CanTakeAimComponent, OnAimingTargetMoveEvent>(OnAimingTargetMove);
+        SubscribeLocalEvent<CanTakeAimComponent, AmmoShotEvent>(OnAmmoShot);
+    }
+    private void OnAmmoShot(EntityUid uid, CanTakeAimComponent component, AmmoShotEvent args)
+    {
+        // TODO: Add damage multiplying
+        // foreach (var entity in args.FiredProjectiles)
+        // {
+        // if (TryComp<ProjectileComponent>(entity, out var projectile))
+        // {
+        //     var deltaT = (_timing.CurFrame - component.AimStartFrame) * _timing.FramesPerSecondAvg;
+        //     Logger.Debug($"Delta T: {deltaT}s");
+        //     projectile.Damage *= component.MaxDamageMultiplier * (component.MaxAimTime / deltaT);
+
+        //     Dirty(entity, projectile);
+        // }
+        // }
+    }
+
+    private void OnAimingTargetMove(EntityUid uid, CanTakeAimComponent component, OnAimingTargetMoveEvent args)
+    {
+        component.IsAiming = false;
+        Dirty(uid, component);
+        if (!TryComp<GunComponent>(uid, out var gunComp))
+            return;
+        var targetCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(args.Target));
+        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+        ;
+        // if (_gun.CanShoot(gunComp))
+        // {
+        EntityUid? ammo = null;
+        if (TryComp<ChamberMagazineAmmoProviderComponent>(uid, out var chamberComp))
+        {
+
+            if (chamberComp.BoltClosed != null && chamberComp.BoltClosed.Value == false)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            ammo = _gun.GetChamberEntity(uid);
+        }
+        if (TryComp<RevolverAmmoProviderComponent>(uid, out var revolverComp))
+        {
+            if (revolverComp.Chambers[revolverComp.CurrentIndex] != true)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                _gun.Shoot(uid, gunComp, takeAmmoEvent.Ammo[0].Entity!.Value, gunCoords, targetCoords, out _, component.User);
+                return;
+            }
+            ammo = revolverComp.AmmoSlots[revolverComp.CurrentIndex];
+        }
+        if (TryComp<BallisticAmmoProviderComponent>(uid, out var ballisticComp))
+        {
+
+            // Use TakeAmmo to safely get ammo
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+
+            (ammo, _) = takeAmmoEvent.Ammo[0];
+        }
+        if (TryComp<MagazineAmmoProviderComponent>(uid, out var magazineComp))
+        {
+            var magEntity = _gun.GetMagazineEntity(uid);
+            if (magEntity == null)
+                return;
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+            (ammo, _) = takeAmmoEvent.Ammo[0];
+        }
path: Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Можливе звернення до порожнього списку**

Після `TakeAmmoEvent` ви одразу читаєте `takeAmmoEvent.Ammo[0]`, не перевіривши, що список непорожній (на відміну від блоку револьвера). Додайте аналогічну перевірку, інакше можливий `IndexOutOfRangeException`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs around lines 101 to
110, the code accesses takeAmmoEvent.Ammo[0] without checking if the Ammo list
contains any elements, risking an IndexOutOfRangeException. Add a check to
ensure takeAmmoEvent.Ammo is not empty before accessing the first element,
similar to the existing check in the revolver block, and only proceed if the
list has at least one item.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit a24fa17
created_at: 2025-05-18T11:44:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,195 @@
+using System.Linq;
+using Content.Shared._Pirate.Actions.Events;
+using Content.Shared.Alert;
+using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Damage;
+using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Movement.Components;
+using Content.Shared.Popups;
+using Content.Shared.Projectiles;
+using Content.Shared.Weapons.Ranged;
+using Content.Shared.Weapons.Ranged.Components;
+using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.Timing;
+
+namespace Content.Shared._Pirate.Aiming;
+
+public sealed partial class SharedCanTakeAimSystem : EntitySystem
+{
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly EntityManager _entMan = default!;
+    [Dependency] private readonly SharedGunSystem _gun = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<CanTakeAimComponent, AfterInteractEvent>(OnWeaponTakeAim);
+        SubscribeLocalEvent<CanTakeAimComponent, OnAimingTargetMoveEvent>(OnAimingTargetMove);
+        SubscribeLocalEvent<CanTakeAimComponent, AmmoShotEvent>(OnAmmoShot);
+    }
+    private void OnAmmoShot(EntityUid uid, CanTakeAimComponent component, AmmoShotEvent args)
+    {
+        // TODO: Add damage multiplying
+        // foreach (var entity in args.FiredProjectiles)
+        // {
+        // if (TryComp<ProjectileComponent>(entity, out var projectile))
+        // {
+        //     var deltaT = (_timing.CurFrame - component.AimStartFrame) * _timing.FramesPerSecondAvg;
+        //     Logger.Debug($"Delta T: {deltaT}s");
+        //     projectile.Damage *= component.MaxDamageMultiplier * (component.MaxAimTime / deltaT);
+
+        //     Dirty(entity, projectile);
+        // }
+        // }
+    }
+
+    private void OnAimingTargetMove(EntityUid uid, CanTakeAimComponent component, OnAimingTargetMoveEvent args)
+    {
+        component.IsAiming = false;
+        Dirty(uid, component);
+        if (!TryComp<GunComponent>(uid, out var gunComp))
+            return;
+        var targetCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(args.Target));
+        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+        ;
+        // if (_gun.CanShoot(gunComp))
+        // {
+        EntityUid? ammo = null;
+        if (TryComp<ChamberMagazineAmmoProviderComponent>(uid, out var chamberComp))
+        {
+
+            if (chamberComp.BoltClosed != null && chamberComp.BoltClosed.Value == false)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            ammo = _gun.GetChamberEntity(uid);
+        }
+        if (TryComp<RevolverAmmoProviderComponent>(uid, out var revolverComp))
+        {
+            if (revolverComp.Chambers[revolverComp.CurrentIndex] != true)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                _gun.Shoot(uid, gunComp, takeAmmoEvent.Ammo[0].Entity!.Value, gunCoords, targetCoords, out _, component.User);
+                return;
+            }
+            ammo = revolverComp.AmmoSlots[revolverComp.CurrentIndex];
+        }
path: Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Повторне використання TakeAmmoEvent + дублювання логіки**

Для револьвера спершу кидається `TakeAmmoEvent`, а якщо патрон все-таки є в камерi, стріляємо й одразу `return`. Якщо патрона немає — призначаємо `ammo = revolverComp.AmmoSlots[revolverComp.CurrentIndex]`, але далі все одно буде глобальна перевірка `ammo == null`. Цей код виглядає заплутано.

Пропоную:

```diff
-        if (revolverComp.Chambers[revolverComp.CurrentIndex] != true)
-        {
-            _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
-            return;
-        }
+        if (!revolverComp.Chambers[revolverComp.CurrentIndex])
+        {
+            _popup.PopupClient(Loc.GetString("gun-revolver-empty"), component.User, PopupType.Medium);
+            return;
+        }
```

І далі використовувати лише `TakeAmmoEvent` без зайвого присвоєння.




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
        {
-            if (revolverComp.Chambers[revolverComp.CurrentIndex] != true)
-            {
-                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
-                return;
-            }
+            if (!revolverComp.Chambers[revolverComp.CurrentIndex])
+            {
+                _popup.PopupClient(Loc.GetString("gun-revolver-empty"), component.User, PopupType.Medium);
+                return;
+            }
            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
            RaiseLocalEvent(uid, takeAmmoEvent);

            if (takeAmmoEvent.Ammo.Count > 0)
            {
                _gun.Shoot(uid, gunComp, takeAmmoEvent.Ammo[0].Entity!.Value, gunCoords, targetCoords, out _, component.User);
                return;
            }
            ammo = revolverComp.AmmoSlots[revolverComp.CurrentIndex];
        }
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs around lines 74 to
90, the code redundantly uses TakeAmmoEvent and duplicates logic by checking
ammo in the revolver chambers and then again globally. Refactor to rely solely
on TakeAmmoEvent to determine ammo availability and remove the extra assignment
of ammo from revolverComp.AmmoSlots. This will simplify the flow by eliminating
the redundant check and assignment, using only the event's ammo list to decide
whether to shoot or not.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-18T11:44:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,195 @@
+using System.Linq;
+using Content.Shared._Pirate.Actions.Events;
+using Content.Shared.Alert;
+using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Damage;
+using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Movement.Components;
+using Content.Shared.Popups;
+using Content.Shared.Projectiles;
+using Content.Shared.Weapons.Ranged;
+using Content.Shared.Weapons.Ranged.Components;
+using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.Timing;
+
+namespace Content.Shared._Pirate.Aiming;
+
+public sealed partial class SharedCanTakeAimSystem : EntitySystem
+{
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly EntityManager _entMan = default!;
+    [Dependency] private readonly SharedGunSystem _gun = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<CanTakeAimComponent, AfterInteractEvent>(OnWeaponTakeAim);
+        SubscribeLocalEvent<CanTakeAimComponent, OnAimingTargetMoveEvent>(OnAimingTargetMove);
+        SubscribeLocalEvent<CanTakeAimComponent, AmmoShotEvent>(OnAmmoShot);
+    }
+    private void OnAmmoShot(EntityUid uid, CanTakeAimComponent component, AmmoShotEvent args)
+    {
+        // TODO: Add damage multiplying
+        // foreach (var entity in args.FiredProjectiles)
+        // {
+        // if (TryComp<ProjectileComponent>(entity, out var projectile))
+        // {
+        //     var deltaT = (_timing.CurFrame - component.AimStartFrame) * _timing.FramesPerSecondAvg;
+        //     Logger.Debug($"Delta T: {deltaT}s");
+        //     projectile.Damage *= component.MaxDamageMultiplier * (component.MaxAimTime / deltaT);
+
+        //     Dirty(entity, projectile);
+        // }
+        // }
+    }
+
+    private void OnAimingTargetMove(EntityUid uid, CanTakeAimComponent component, OnAimingTargetMoveEvent args)
+    {
+        component.IsAiming = false;
+        Dirty(uid, component);
+        if (!TryComp<GunComponent>(uid, out var gunComp))
+            return;
+        var targetCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(args.Target));
+        var gunCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+        ;
+        // if (_gun.CanShoot(gunComp))
+        // {
+        EntityUid? ammo = null;
+        if (TryComp<ChamberMagazineAmmoProviderComponent>(uid, out var chamberComp))
+        {
+
+            if (chamberComp.BoltClosed != null && chamberComp.BoltClosed.Value == false)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            ammo = _gun.GetChamberEntity(uid);
+        }
+        if (TryComp<RevolverAmmoProviderComponent>(uid, out var revolverComp))
+        {
+            if (revolverComp.Chambers[revolverComp.CurrentIndex] != true)
+            {
+                _popup.PopupClient(Loc.GetString("gun-chamber-bolt-ammo"), component.User, PopupType.Medium);
+                return;
+            }
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+
+            if (takeAmmoEvent.Ammo.Count > 0)
+            {
+                _gun.Shoot(uid, gunComp, takeAmmoEvent.Ammo[0].Entity!.Value, gunCoords, targetCoords, out _, component.User);
+                return;
+            }
+            ammo = revolverComp.AmmoSlots[revolverComp.CurrentIndex];
+        }
+        if (TryComp<BallisticAmmoProviderComponent>(uid, out var ballisticComp))
+        {
+
+            // Use TakeAmmo to safely get ammo
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+
+            (ammo, _) = takeAmmoEvent.Ammo[0];
+        }
+        if (TryComp<MagazineAmmoProviderComponent>(uid, out var magazineComp))
+        {
+            var magEntity = _gun.GetMagazineEntity(uid);
+            if (magEntity == null)
+                return;
+            var fromCoords = _transform.ToCoordinates(_transform.GetMapCoordinates(uid));
+            var takeAmmoEvent = new TakeAmmoEvent(1, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoords, component.User);
+            RaiseLocalEvent(uid, takeAmmoEvent);
+            (ammo, _) = takeAmmoEvent.Ammo[0];
+        }
+        if (ammo == null)
+            return;
+        _gun.Shoot(uid, gunComp, ammo.Value, gunCoords, targetCoords, out _, component.User);
+        if (component.User == null)
+            return;
+        if (ballisticComp != null)
+        {
+            // For ballistic guns, use the ActivateInWorldEvent to trigger cycling
+            if (ballisticComp.AutoCycle)
+            {
+                var activateEvent = new ActivateInWorldEvent(component.User.Value, uid, true);
+                RaiseLocalEvent(uid, activateEvent);
+            }
+        }
+        else if (revolverComp != null)
+        {
+            // For revolvers, use the UseInHandEvent to trigger cycling
+            var useEvent = new UseInHandEvent(component.User.Value);
+            RaiseLocalEvent(uid, useEvent);
+        }
+        else if (chamberComp != null)
+        {
+            if (chamberComp.AutoCycle)
+            {
+                var useEvent = new UseInHandEvent(component.User.Value);
+                RaiseLocalEvent(uid, useEvent);
+            }
+        }
+        else
+        {
+            // For other gun types, try the CycleFire method which works for selective fire guns
+            _gun.CycleFire(uid, gunComp, component.User);
+        }
+        // }
+        // _gun.ShootProjectile(ammo.Value, direction, _physics.GetMapLinearVelocity(uid, physComp), uid);
+    }
+    public void OnWeaponTakeAim(EntityUid uid, CanTakeAimComponent component, ref AfterInteractEvent args)
+    {
+        if (args.Target == args.User)
+            return;
+        component.User = args.User;
+        if (!args.CanReach)
+        {
+            _popup.PopupClient("Я не зможу попасти в ціль звідси...", args.User, PopupType.Medium);
+            return;
+        }
path: Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Порядок перевірок: можливий NullReference**

Спочатку виконується `HasComp<MobMoverComponent>(args.Target)` (рядок 159), а тільки потім `args.Target == null` (рядок 161). Якщо `Target` — `null`, отримаємо NRE в `HasComp`. Переставте перевірку `args.Target == null` перед усіма зверненнями до `args.Target`.

```diff
+        if (args.Target == null)
+            return;
-        if (!HasComp<MobMoverComponent>(args.Target))
+        if (!HasComp<MobMoverComponent>(args.Target))
             return;
```



> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs around lines 148 to
156, the code checks HasComp<MobMoverComponent>(args.Target) before verifying if
args.Target is null, which can cause a NullReferenceException. To fix this, move
the null check for args.Target to occur before any access or method calls
involving args.Target, ensuring args.Target is not null before calling HasComp
or other operations.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit e66d1f6
created_at: 2025-05-18T11:44:54Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-18T11:28:39Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Summary by CodeRabbit

- **Нові можливості**
  - Додано механіку прицілювання для різних видів зброї: тепер можна цілитись у ціль для посилення пострілу.
  - Додано новий індикатор "Ти на мушці!" — попередження для гравців, коли вони знаходяться під прицілом.
  - Додано нову категорію сповіщень "Aiming" та відповідний тип сповіщення.
  - Додано можливість деякій зброї тримати аксесуари (наприклад, бойовий ніж).

- **Виправлення стилю**
  - Покращено форматування YAML-файлів для зброї для більшої читабельності.
## Summary by CodeRabbit

- **Нові функції**
  - Додано можливість прицілювання для різних видів зброї: пістолети, рушниці, автомати, кулемети, снайперські гвинтівки, револьвери, гранатомети та інші.
  - Додано новий статус-алерт «Ти на мушці!» — гравці отримують попередження, якщо на них націлено зброю.
  - Якщо ціль, на яку націлено, рухається, відбувається автоматичний постріл.
  - Додано підтримку аксесуарів (наприклад, бойових ножів) для деяких видів зброї.

- **Стиль**
  - Покращено форматування YAML-файлів для більшої читабельності.

## Walkthrough

У цьому оновленні впроваджено нову систему прицілювання для зброї: додано компоненти та системи для підтримки механіки прицілювання, оповіщення цілі та автоматичного пострілу при русі цілі. Відповідні зміни внесено у енумерації, компоненти, системи та YAML-прототипи зброї, а також додано новий алерт і ресурс іконки.

## Changes

| Файли                                                                                                   | Опис змін                                                                                                                                                                                                                                         |
|---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Content.Shared/Alert/AlertType.cs                                                                       | Додано новий тип алерту `OnSight` до енуму `AlertType`.                                                                                                                                                                                           |
| Content.Shared/Alert/AlertCategory.cs<br>Resources/Prototypes/Alerts/categories.yml                      | Додано нову категорію алертів `Aiming`.                                                                                                                                                                                                           |
| Content.Shared/_Pirate/Aiming/CanTakeAimComponent.cs<br>Content.Shared/_Pirate/Aiming/OnSightComponent.cs | Додано компоненти `CanTakeAimComponent` (для підтримки прицілювання зброї) та `OnSightComponent` (для відстеження, хто цілиться у сутність).                                                                                                      |
| Content.Shared/_Pirate/Aiming/SharedCanTakeAimSystem.cs<br>Content.Shared/_Pirate/Aiming/SharedOnSightSystem.cs | Додано системи для управління прицілюванням (`SharedCanTakeAimSystem`) та оповіщенням про прицілювання/рух цілі (`SharedOnSightSystem`).                                                                                                          |
| Content.Shared/_Pirate/Actions/Events/OnAimingTargetMoveEvent.cs                                        | Додано локальну подію `OnAimingTargetMoveEvent` для оповіщення про рух цілі, у яку цілиться.                                                                                                               |
| Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml<br>Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml<br>Resources/Prototypes/Entities/Objects/Weapons/Guns/flare_gun.yml | Додано компонент `CanTakeAim` до базових прототипів зброї (ПКА, вогнепальна, сигнальна).                                                                                                                   |
| Resources/Prototypes/Entities/Objects/Weapons/Guns/HMGs/hmgs.yml<br>Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml<br>Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml<br>Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml | Додано компоненти `CanHoldAccessories` (тільки для ножа) і `CanTakeAim` до важких кулеметів, легких кулеметів, гранатометів і снайперських гвинтівок.                                                     |
| Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml<br>Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml<br>Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml<br>Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml | Додано компонент `CanTakeAim` до базових прототипів револьверів, гвинтівок, ПП та дробовиків.                                                                                                              |
| Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml                                 | Відформатовано YAML-файл пістолетів для кращої читабельності, без зміни змісту.                                                                                                                            |
| Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml                                                  | Додано новий алерт `OnSightAlert` із відповідною іконкою та описом.                                                                                                                                        |
| Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/meta.json                                       | Додано метадані для нової іконки алерту прицілювання.                                                                                                                                                       |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Gun as Зброя (CanTakeAimComponent)
    participant User as Гравець (User)
    participant Target as Ціль (OnSightComponent)
    participant OnSightSys as SharedOnSightSystem
    participant CanAimSys as SharedCanTakeAimSystem
    participant AlertSys as AlertsSystem

    User->>Gun: AfterInteractEvent (почати прицілювання)
    Gun->>CanAimSys: OnWeaponTakeAim
    CanAimSys->>Target: EnsureComponentOnTarget (додає OnSightComponent)
    CanAimSys->>AlertSys: Показати OnSightAlert цілі
    Note right of Target: Ціль бачить алерт "Ти на мушці!"

    Target->>OnSightSys: MoveEvent (рухається)
    OnSightSys->>AlertSys: Прибрати OnSightAlert
    OnSightSys->>Gun: OnAimingTargetMoveEvent
    Gun->>CanAimSys: OnAimingTargetMove
    CanAimSys->>Gun: Стріляє у ціль
```

## Poem

> ʕ•́ᴥ•̀ʔっ🐾  
> Новий приціл, новий страх,  
> Зайчик цілиться — не жарт!  
> Якщо ти ворухнешся —  
> Вистрілить без вагань.  
> Алерт миготить, іконка блищить,  
> Краще не рухайся, ціль,  
> Бо зайчик стріляти спішить!

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between e66d1f605729911831cd4598abf687d4599b978c and 6874e8589669bd379758b98e5d4334988c4bf41d.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `Content.Shared/Alert/AlertCategory.cs` (1 hunks)
* `Resources/Prototypes/Alerts/categories.yml` (1 hunks)
* `Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml` (1 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (2)</summary>

* Content.Shared/Alert/AlertCategory.cs
* Resources/Prototypes/Alerts/categories.yml

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* Resources/Prototypes/_Pirate/Aiming/Alerts/alerts.yml

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: build (ubuntu-latest)
* GitHub Check: build (ubuntu-latest)
* GitHub Check: YAML Linter
* GitHub Check: Test Packaging

</details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=SpaceStationUA/station&utm_content=103):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-18T11:29:20Z
body: RSI Diff Bot; head commit 6874e8589669bd379758b98e5d4334988c4bf41d merging into 7957e42209068fb441c0af65b52f04a2ae0585d4
This PR makes changes to 1 or more RSIs. Here is a summary of all changes:

<details><summary>Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi</summary>
<p>

| State | Old | New | Status
| --- | --- | --- | --- |
| icon | ![]() | ![](https://raw.githubusercontent.com/CarryUa/station-fork/6874e8589669bd379758b98e5d4334988c4bf41d/Resources/Textures/_Pirate/Interface/Alerts/aiming.rsi/icon.png) | Added


</p>
</details>
Edit: diff updated after 6874e8589669bd379758b98e5d4334988c4bf41d
author_association: CONTRIBUTOR