]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Sentry turrets - Part 2: Basic prototype (#35031)
authorchromiumboy <50505512+chromiumboy@users.noreply.github.com>
Sun, 23 Feb 2025 13:39:44 +0000 (07:39 -0600)
committerGitHub <noreply@github.com>
Sun, 23 Feb 2025 13:39:44 +0000 (00:39 +1100)
Content.Server/Power/Components/ApcPowerReceiverBatteryComponent.cs [new file with mode: 0644]
Content.Server/Power/EntitySystems/PowerNetSystem.cs
Content.Shared/Power/SharedPowerDevice.cs
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_base.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_broken.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Objects/Weapons/Guns/turrets.yml [deleted file]

diff --git a/Content.Server/Power/Components/ApcPowerReceiverBatteryComponent.cs b/Content.Server/Power/Components/ApcPowerReceiverBatteryComponent.cs
new file mode 100644 (file)
index 0000000..e2af146
--- /dev/null
@@ -0,0 +1,49 @@
+using Content.Server.Power.EntitySystems;
+using Content.Shared.Power.EntitySystems;
+
+namespace Content.Server.Power.Components;
+
+/// <summary>
+/// Attached to APC powered entities that possess a rechargeable internal battery.
+/// If external power is interrupted, the entity will draw power from this battery instead. 
+/// Requires <see cref="ApcPowerReceiverComponent"/> and <see cref="BatteryComponent"/> to function.
+/// </summary>
+[RegisterComponent]
+[Access([typeof(PowerNetSystem), typeof(SharedPowerReceiverSystem)])]
+public sealed partial class ApcPowerReceiverBatteryComponent : Component
+{
+    /// <summary>
+    /// Indicates whether power is currently being drawn from the battery.
+    /// </summary>
+    [DataField]
+    public bool Enabled = false;
+
+    /// <summary>
+    /// The passive load the entity places on the APC power network.
+    /// If not connected to an active APC power network, this amount
+    /// of power is drained from the battery every second.
+    /// </summary>
+    [DataField]
+    public float IdleLoad { get; set; } = 5f;
+
+    /// <summary>
+    /// Determines how much battery charge the entity's battery gains 
+    /// per second when connected to an active APC power network.
+    /// </summary>
+    [DataField]
+    public float BatteryRechargeRate { get; set; } = 50f;
+
+    /// <summary>
+    /// While the battery is being recharged, the load this entity places on the APC 
+    /// power network is increased by the <see cref="BatteryRechargeRate"/> multiplied
+    /// by this factor.
+    /// </summary>
+    [DataField]
+    public float BatteryRechargeEfficiency { get; set; } = 1f;
+}
+
+/// <summary>
+/// Raised whenever an ApcPowerReceiverBattery starts / stops discharging
+/// </summary>
+[ByRefEvent]
+public readonly record struct ApcPowerReceiverBatteryChangedEvent(bool Enabled);
index a7098649ceffb19f38c626d709576c775ea8045d..dbc17f2b9d15b9e0592d5044be50f3fc6e634889 100644 (file)
@@ -22,6 +22,7 @@ namespace Content.Server.Power.EntitySystems
         [Dependency] private readonly PowerNetConnectorSystem _powerNetConnector = default!;
         [Dependency] private readonly IConfigurationManager _cfg = default!;
         [Dependency] private readonly IParallelManager _parMan = default!;
+        [Dependency] private readonly BatterySystem _battery = default!;
 
         private readonly PowerState _powerState = new();
         private readonly HashSet<PowerNet> _powerNetReconnectQueue = new();
@@ -278,7 +279,7 @@ namespace Content.Server.Power.EntitySystems
             // Send events where necessary.
             // TODO: Instead of querying ALL power components every tick, and then checking if an event needs to be
             // raised, should probably assemble a list of entity Uids during the actual solver steps.
-            UpdateApcPowerReceiver();
+            UpdateApcPowerReceiver(frameTime);
             UpdatePowerConsumer();
             UpdateNetworkBattery();
         }
@@ -306,10 +307,12 @@ namespace Content.Server.Power.EntitySystems
             _powerNetReconnectQueue.Clear();
         }
 
-        private void UpdateApcPowerReceiver()
+        private void UpdateApcPowerReceiver(float frameTime)
         {
             var appearanceQuery = GetEntityQuery<AppearanceComponent>();
             var metaQuery = GetEntityQuery<MetaDataComponent>();
+            var apcBatteryQuery = GetEntityQuery<ApcPowerReceiverBatteryComponent>();
+
             var enumerator = AllEntityQuery<ApcPowerReceiverComponent>();
             while (enumerator.MoveNext(out var uid, out var apcReceiver))
             {
@@ -318,6 +321,42 @@ namespace Content.Server.Power.EntitySystems
                                   || MathHelper.CloseToPercent(apcReceiver.NetworkLoad.ReceivingPower,
                                       apcReceiver.Load));
 
+                // Check if the entity has an internal battery
+                if (apcBatteryQuery.TryComp(uid, out var apcBattery) && TryComp<BatteryComponent>(uid, out var battery))
+                {
+                    apcReceiver.Load = apcBattery.IdleLoad;
+
+                    // Try to draw power from the battery if there isn't sufficient external power
+                    var requireBattery = !powered && !apcReceiver.PowerDisabled;
+
+                    if (requireBattery)
+                    {
+                        _battery.SetCharge(uid, battery.CurrentCharge - apcBattery.IdleLoad * frameTime, battery);
+                    }
+
+                    // Otherwise try to charge the battery
+                    else if (powered && !_battery.IsFull(uid, battery))
+                    {
+                        apcReceiver.Load += apcBattery.BatteryRechargeRate * apcBattery.BatteryRechargeEfficiency;
+                        _battery.SetCharge(uid, battery.CurrentCharge + apcBattery.BatteryRechargeRate * frameTime, battery);
+                    }
+
+                    // Enable / disable the battery if the state changed
+                    var enableBattery = requireBattery && battery.CurrentCharge > 0;
+
+                    if (apcBattery.Enabled != enableBattery)
+                    {
+                        apcBattery.Enabled = enableBattery;
+
+                        var apcBatteryEv = new ApcPowerReceiverBatteryChangedEvent(enableBattery);
+                        RaiseLocalEvent(uid, ref apcBatteryEv);
+
+                        _appearance.SetData(uid, PowerDeviceVisuals.BatteryPowered, enableBattery);
+                    }
+
+                    powered |= enableBattery;
+                }
+
                 // If new value is the same as the old, then exit
                 if (!apcReceiver.Recalculate && apcReceiver.Powered == powered)
                     continue;
index 4274dde60f901cf53498b9ee4805b8b435870285..043a4dac48dd451b3c17905488021a640c87dbf1 100644 (file)
@@ -6,6 +6,7 @@ namespace Content.Shared.Power
     public enum PowerDeviceVisuals : byte
     {
         VisualState,
-        Powered
+        Powered,
+        BatteryPowered
     }
 }
index 9f34a1c5d1c9b694906699febc4fccd764cf54e6..d71e40aa14ecec14994337cb93c0dea98a23aa9f 100644 (file)
     tags:
       - HideContextMenu
   - type: AnimationPlayer
+  
+- type: entity
+  parent: MuzzleFlashEffect
+  id: MuzzleFlashEffectOmnilaser
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: Sprite
+    drawdepth: BelowMobs
+    offset: 0.15, 0
+    layers:
+    - shader: unshaded
+      map: ["enum.EffectLayers.Unshaded"]
+      sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
+      state: omnilaser_flash
+  
+- type: entity
+  parent: MuzzleFlashEffect
+  id: MuzzleFlashEffectHeavyLaser
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: Sprite
+    drawdepth: BelowMobs
+    offset: 0.15, 0
+    layers:
+    - shader: unshaded
+      map: ["enum.EffectLayers.Unshaded"]
+      sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
+      state: heavylaser_flash
 
 # One bullet to bring them all into the darkness and bind them
 - type: entity
       collection: WeakHit
     forceSound: true
 
+- type: entity
+  name : energy bolt
+  id: BulletEnergyTurretBase
+  parent: BaseBullet
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: Reflective
+    reflective:
+    - Energy
+  - type: FlyBySound
+    sound:
+      collection: EnergyMiss
+      params:
+        volume: 5
+  - type: Sprite
+    sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
+    layers:
+    - state: heavylaser
+      shader: unshaded
+  - type: Physics
+  - type: Fixtures
+    fixtures:
+      projectile:
+        shape:
+          !type:PhysShapeAabb
+          bounds: "-0.15,-0.3,0.15,0.3"
+        hard: false
+        mask:
+        - Opaque
+      fly-by: *flybyfixture
+  - type: Ammo
+
+- type: entity
+  name : laser bolt
+  id: BulletEnergyTurretLaser
+  parent: BulletEnergyTurretBase
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: Ammo
+    muzzleFlash: MuzzleFlashEffectHeavyLaser
+  - type: Sprite
+    sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
+    layers:
+    - state: heavylaser
+      shader: unshaded
+  - type: Projectile
+    impactEffect: BulletImpactEffectOrangeDisabler
+    damage:
+      types:
+        Heat: 28
+
+- type: entity
+  name : disabler bolt
+  id: BulletEnergyTurretDisabler
+  parent: BulletEnergyTurretBase
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: Ammo
+    muzzleFlash: MuzzleFlashEffectOmnilaser
+  - type: Sprite
+    sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
+    layers:
+    - state: omnilaser
+      shader: unshaded
+  - type: StaminaDamageOnCollide
+    damage: 30
+  - type: Projectile
+    impactEffect: BulletImpactEffectDisabler
+    damage:
+      types:
+        Heat: 0
+    soundHit:
+      collection: WeakHit
+    forceSound: true
+
 - type: entity
   name: tesla gun lightning
   id: TeslaGunBullet
   - type: ProjectileSpread
     proto: BulletDisablerSmg
     count: 3 #bit stronger than a disabler if you hit your shots you goober, still not a 2 hit stun though
-    spread: 9
+    spread: 9
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml
new file mode 100644 (file)
index 0000000..c5dbb1b
--- /dev/null
@@ -0,0 +1,138 @@
+- type: entity
+  parent: [BaseWeaponBallisticTurret, BaseSyndicateContraband]
+  id: WeaponTurretSyndicate
+  suffix: Syndicate
+  components:
+  - type: NpcFactionMember
+    factions:
+    - Syndicate
+
+- type: entity
+  parent: BaseWeaponBallisticTurret
+  id: WeaponTurretSyndicateDisposable
+  name: disposable ballistic turret
+  suffix: Syndicate, Disposable
+  components:
+  - type: NpcFactionMember
+    factions:
+    - Syndicate
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 600
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+    - trigger:
+        !type:DamageTrigger
+        damage: 100
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:TriggerBehavior
+  - type: Gun
+    fireRate: 2
+    selectedMode: FullAuto
+    availableModes:
+    - FullAuto
+    soundGunshot: /Audio/Weapons/Guns/Gunshots/gun_sentry.ogg
+  - type: BallisticAmmoProvider
+    proto: CartridgePistol
+    capacity: 50
+  - type: Construction
+    deconstructionTarget: null
+    graph: WeaponTurretSyndicateDisposable
+    node: disposableTurret
+  - type: Repairable
+    qualityNeeded: "Anchoring"
+    doAfterDelay: 3
+  - type: TriggerWhenEmpty
+  - type: ExplodeOnTrigger
+  - type: Explosive
+    explosionType: Default
+    maxIntensity: 10
+    intensitySlope: 1.5
+    totalIntensity: 30
+    canCreateVacuum: false
+
+- type: entity
+  parent: BaseWeaponBallisticTurret
+  id: WeaponTurretNanoTrasen
+  suffix: NanoTrasen
+  components:
+  - type: NpcFactionMember
+    factions:
+    - NanoTrasen
+
+- type: entity
+  parent: BaseWeaponBallisticTurret
+  id: WeaponTurretHostile
+  suffix: Hostile
+  components:
+  - type: NpcFactionMember
+    factions:
+    - SimpleHostile
+
+- type: entity
+  parent: BaseWeaponBallisticTurret
+  id: WeaponTurretAllHostile
+  suffix: All hostile
+  components:
+  - type: NpcFactionMember
+    factions:
+    - AllHostile
+
+- type: entity
+  parent: BaseWeaponBallisticTurret
+  id: WeaponTurretXeno
+  name: xeno turret
+  suffix: Xeno
+  description: Shoots 9mm acid projectiles.
+  components:
+  - type: NpcFactionMember
+    factions:
+    - Xeno
+  - type: Sprite
+    sprite: Objects/Weapons/Guns/Turrets/xenoturret.rsi
+    noRot: true
+    layers:
+    - state: acid_turret
+  - type: BallisticAmmoProvider
+    proto: BulletAcid
+    capacity: 500
+  - type: Gun
+    fireRate: 1
+    selectedMode: FullAuto
+    soundGunshot: /Audio/Weapons/Xeno/alien_spitacid.ogg
+  - type: HTN
+    rootTask:
+      task: TurretCompound
+    blackboard:
+      SoundTargetInLOS: !type:SoundPathSpecifier
+        path: /Audio/Animals/snake_hiss.ogg
+  - type: Damageable
+    damageContainer: Biological
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 100
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          path: /Audio/Effects/gib1.ogg
+      - !type:SpawnEntitiesBehavior
+        spawn:
+          FoodMeatXeno:
+            min: 3
+            max: 5
+  - type: InteractionPopup
+    interactDelay: 1.0
+    successChance: 0.8
+    interactSuccessString: petting-success-generic
+    interactFailureString: petting-failure-generic
+    interactSuccessSound:
+      path: /Audio/Animals/snake_hiss.ogg
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_base.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_base.yml
new file mode 100644 (file)
index 0000000..aaa45a2
--- /dev/null
@@ -0,0 +1,135 @@
+- type: entity
+  parent: BaseStructure
+  id: BaseWeaponTurret
+  name: turret
+  abstract: true
+  components:
+  - type: Clickable
+  - type: InteractionOutline
+  - type: Actions
+  - type: Fixtures
+    fixtures:
+      fix1:
+        shape:
+          !type:PhysShapeAabb
+          bounds: "-0.45,-0.45,0.45,0.45"
+        density: 60
+        mask:
+        - MachineMask
+        layer:
+        - MachineLayer
+  - type: Sprite
+    sprite: Objects/Weapons/Guns/Turrets/turrets.rsi
+    drawdepth: Mobs
+    layers:
+    - state: syndie_lethal
+  - type: InteractionPopup
+    interactDelay: 0.2
+    successChance: 0.8
+    interactSuccessString: petting-success-generic
+    interactFailureString: petting-failure-generic
+    interactSuccessSound:
+      path: /Audio/Effects/double_beep.ogg
+  - type: CombatMode
+    toggleMouseRotator: false
+  - type: Damageable
+    damageContainer: Inorganic
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 600
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          collection: MetalGlassBreak
+    - trigger:
+        !type:DamageTrigger
+        damage: 300
+       # TODO: Construction graph
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          collection: MetalGlassBreak
+      - !type:SpawnEntitiesBehavior
+        spawn:
+          SheetSteel1:
+            min: 3
+            max: 5
+  - type: HTN
+    rootTask:
+      task: TurretCompound
+    blackboard:
+      RotateSpeed: !type:Single
+        3.141
+      SoundTargetInLOS: !type:SoundPathSpecifier
+        path: /Audio/Effects/double_beep.ogg
+  - type: MouseRotator
+    angleTolerance: 5
+    rotationSpeed: 180
+    simple4DirMode: false
+  - type: NoRotateOnInteract
+  - type: NoRotateOnMove
+  - type: Input
+    context: "human"
+
+- type: entity
+  parent: BaseWeaponTurret
+  id: BaseWeaponBallisticTurret
+  description: A ballistic machine gun auto-turret.
+  name: ballistic turret
+  abstract: true
+  components:
+  - type: ContainerContainer
+    containers:
+      ballistic-ammo: !type:Container
+  - type: Gun
+    fireRate: 6
+    selectedMode: FullAuto
+    availableModes:
+    - FullAuto
+    soundGunshot:
+      path: /Audio/Weapons/Guns/Gunshots/gun_sentry.ogg
+  - type: BallisticAmmoProvider
+    proto: CartridgeCaselessRifle
+    capacity: 500
+      
+- type: entity
+  parent: BaseWeaponTurret
+  id: BaseWeaponEnergyTurret
+  name: laser turret
+  description: An auto-turret armed with a heavy laser. Its weapon will recharge while connected to an active power grid.
+  abstract: true
+  components:
+  - type: Sprite
+    sprite: Objects/Weapons/Guns/Turrets/sentry_turret.rsi
+    drawdepth: FloorObjects
+    granularLayersRendering: true
+    layers:
+    - state: support
+      renderingStrategy: NoRotation
+    - state: base
+    - state: lethal
+      shader: unshaded
+  - type: Gun
+    projectileSpeed: 15
+    fireRate: 1.5
+    soundGunshot:
+      path: /Audio/Weapons/Guns/Gunshots/taser2.ogg
+  - type: ProjectileBatteryAmmoProvider
+    proto: BulletEnergyTurretLaser
+    fireCost: 100
+  - type: Battery
+    maxCharge: 2000
+    startingCharge: 0
+  - type: ApcPowerReceiverBattery
+    idlePowerUse: 5
+    batteryRechargeRate: 200
+    batteryRechargeEfficiency: 1.225
+  - type: ApcPowerReceiver
+    powerLoad: 5
+  - type: ExtensionCableReceiver
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_broken.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_broken.yml
new file mode 100644 (file)
index 0000000..96218cd
--- /dev/null
@@ -0,0 +1,31 @@
+- type: entity
+  parent: BaseStructure
+  id: WeaponTurretSyndicateBroken
+  name: ballistic turret (broken)
+  description: A ballistic machine gun auto-turret.
+  components:
+  - type: Clickable
+  - type: InteractionOutline
+  - type: Sprite
+    sprite: Objects/Weapons/Guns/Turrets/turrets.rsi
+    drawdepth: Mobs
+    layers:
+    - state: syndie_broken
+  - type: Damageable
+    damageContainer: Inorganic
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 450
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          collection: MetalGlassBreak
+      - !type:SpawnEntitiesBehavior
+        spawn:
+          SheetSteel1:
+            min: 2
+            max: 4
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/turrets.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/turrets.yml
deleted file mode 100644 (file)
index 8da72b4..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-- type: entity
-  parent: BaseStructure
-  id: WeaponTurretSyndicateBroken
-  name: ballistic turret (broken)
-  description: A ballistic machine gun auto-turret.
-  components:
-    - type: Clickable
-    - type: InteractionOutline
-    - type: Sprite
-      sprite: Objects/Weapons/Guns/Turrets/turrets.rsi
-      drawdepth: Mobs
-      layers:
-        - state: syndie_broken
-    - type: Damageable
-      damageContainer: Inorganic
-    - type: Destructible
-      thresholds:
-        - trigger:
-            !type:DamageTrigger
-            damage: 450
-          behaviors:
-            - !type:DoActsBehavior
-              acts: [ "Destruction" ]
-            - !type:PlaySoundBehavior
-              sound:
-                collection: MetalGlassBreak
-            - !type:SpawnEntitiesBehavior
-              spawn:
-                SheetSteel1:
-                  min: 2
-                  max: 4
-
-- type: entity
-  parent: BaseStructure
-  id: BaseWeaponTurret
-  name: ballistic turret
-  abstract: true
-  components:
-    - type: Clickable
-    - type: InteractionOutline
-    - type: Actions
-    - type: Fixtures
-      fixtures:
-        fix1:
-          shape:
-            !type:PhysShapeAabb
-            bounds: "-0.45,-0.45,0.45,0.45"
-          density: 60
-          mask:
-            - MachineMask
-          layer:
-            - MachineLayer
-    - type: ContainerContainer
-      containers:
-        ballistic-ammo: !type:Container
-    - type: Sprite
-      sprite: Objects/Weapons/Guns/Turrets/turrets.rsi
-      drawdepth: Mobs
-      layers:
-        - state: syndie_lethal
-    - type: InteractionPopup
-      interactDelay: 0.2
-      successChance: 0.8
-      interactSuccessString: petting-success-generic
-      interactFailureString: petting-failure-generic
-      interactSuccessSound:
-        path: /Audio/Effects/double_beep.ogg
-    - type: CombatMode
-      toggleMouseRotator: false
-    - type: Damageable
-      damageContainer: Inorganic
-    - type: Destructible
-      thresholds:
-        - trigger:
-            !type:DamageTrigger
-            damage: 600
-          behaviors:
-            - !type:DoActsBehavior
-              acts: [ "Destruction" ]
-        - trigger:
-            !type:DamageTrigger
-            damage: 300
-          # TODO: Construction graph
-          behaviors:
-            - !type:DoActsBehavior
-              acts: [ "Destruction" ]
-            - !type:PlaySoundBehavior
-              sound:
-                collection: MetalGlassBreak
-            - !type:SpawnEntitiesBehavior
-              spawn:
-                SheetSteel1:
-                  min: 3
-                  max: 5
-    - type: Gun
-      fireRate: 6
-      selectedMode: FullAuto
-      availableModes:
-        - FullAuto
-      soundGunshot: /Audio/Weapons/Guns/Gunshots/gun_sentry.ogg
-    # TODO: Power ammo provider?
-    - type: BallisticAmmoProvider
-      proto: CartridgeCaselessRifle
-      capacity: 500
-    - type: HTN
-      rootTask:
-        task: TurretCompound
-      blackboard:
-        RotateSpeed: !type:Single
-          3.141
-        SoundTargetInLOS: !type:SoundPathSpecifier
-          path: /Audio/Effects/double_beep.ogg
-    - type: MouseRotator
-      angleTolerance: 5
-      rotationSpeed: 180
-      simple4DirMode: false
-    - type: NoRotateOnInteract
-    - type: NoRotateOnMove
-    - type: Input
-      context: "human"
-
-- type: entity
-  parent: [BaseWeaponTurret, BaseSyndicateContraband]
-  id: WeaponTurretSyndicate
-  suffix: Syndicate
-  components:
-    - type: NpcFactionMember
-      factions:
-        - Syndicate
-
-- type: entity
-  parent: BaseWeaponTurret
-  name: disposable ballistic turret
-  id: WeaponTurretSyndicateDisposable
-  suffix: Syndicate, Disposable
-  components:
-    - type: NpcFactionMember
-      factions:
-        - Syndicate
-    - type: Destructible
-      thresholds:
-        - trigger:
-            !type:DamageTrigger
-            damage: 600
-          behaviors:
-            - !type:DoActsBehavior
-              acts: [ "Destruction" ]
-        - trigger:
-            !type:DamageTrigger
-            damage: 100
-          behaviors:
-            - !type:DoActsBehavior
-              acts: [ "Destruction" ]
-            - !type:TriggerBehavior
-    - type: Gun
-      fireRate: 2
-      selectedMode: FullAuto
-      availableModes:
-        - FullAuto
-      soundGunshot: /Audio/Weapons/Guns/Gunshots/gun_sentry.ogg
-    - type: BallisticAmmoProvider
-      proto: CartridgePistol
-      capacity: 50
-    - type: Construction
-      deconstructionTarget: null
-      graph: WeaponTurretSyndicateDisposable
-      node: disposableTurret
-    - type: Repairable
-      qualityNeeded: "Anchoring"
-      doAfterDelay: 3
-    - type: TriggerWhenEmpty
-    - type: ExplodeOnTrigger
-    - type: Explosive
-      explosionType: Default
-      maxIntensity: 10
-      intensitySlope: 1.5
-      totalIntensity: 30
-      canCreateVacuum: false
-
-- type: entity
-  parent: BaseWeaponTurret
-  id: WeaponTurretNanoTrasen
-  suffix: NanoTrasen
-  components:
-  - type: NpcFactionMember
-    factions:
-    - NanoTrasen
-
-- type: entity
-  parent: BaseWeaponTurret
-  id: WeaponTurretHostile
-  suffix: Hostile
-  components:
-  - type: NpcFactionMember
-    factions:
-    - SimpleHostile
-
-- type: entity
-  parent: BaseWeaponTurret
-  id: WeaponTurretAllHostile
-  suffix: All hostile
-  components:
-  - type: NpcFactionMember
-    factions:
-    - AllHostile
-
-- type: entity
-  name: xeno turret
-  description: Shoots 9mm acid projectiles.
-  parent: BaseWeaponTurret
-  id: WeaponTurretXeno
-  suffix: Xeno
-  components:
-    - type: NpcFactionMember
-      factions:
-        - Xeno
-    - type: Sprite
-      sprite: Objects/Weapons/Guns/Turrets/xenoturret.rsi
-      noRot: true
-      layers:
-        - state: acid_turret
-    - type: BallisticAmmoProvider
-      proto: BulletAcid
-      capacity: 500
-    - type: Gun
-      fireRate: 1
-      selectedMode: FullAuto
-      soundGunshot: /Audio/Weapons/Xeno/alien_spitacid.ogg
-    - type: HTN
-      rootTask:
-        task: TurretCompound
-      blackboard:
-        SoundTargetInLOS: !type:SoundPathSpecifier
-          path: /Audio/Animals/snake_hiss.ogg
-    - type: Damageable
-      damageContainer: Biological
-    - type: Destructible
-      thresholds:
-        - trigger:
-            !type:DamageTrigger
-            damage: 100
-          behaviors:
-            - !type:DoActsBehavior
-              acts: [ "Destruction" ]
-            - !type:PlaySoundBehavior
-              sound:
-                path: /Audio/Effects/gib1.ogg
-            - !type:SpawnEntitiesBehavior
-              spawn:
-                FoodMeatXeno:
-                  min: 3
-                  max: 5
-    - type: InteractionPopup
-      interactDelay: 1.0
-      successChance: 0.8
-      interactSuccessString: petting-success-generic
-      interactFailureString: petting-failure-generic
-      interactSuccessSound:
-        path: /Audio/Animals/snake_hiss.ogg