]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Sentry turrets - Part 3: Turret AI (#35058)
authorchromiumboy <50505512+chromiumboy@users.noreply.github.com>
Sat, 1 Mar 2025 17:42:33 +0000 (11:42 -0600)
committerGitHub <noreply@github.com>
Sat, 1 Mar 2025 17:42:33 +0000 (18:42 +0100)
* Initial commit

* Updated Access/command.yml

* Fix for Access/AccessLevelPrototype.cs

* Added silicon access levels to admin items

* Included self-recharging battery changes

* Revert "Included self-recharging battery changes"

* Addressed reviewers comments

* Additional reviewer comments

23 files changed:
Content.Server/Access/Systems/IdCardSystem.cs
Content.Server/NPC/Components/NPCRangedCombatComponent.cs
Content.Server/NPC/HTN/HTNComponent.cs
Content.Server/NPC/HTN/HTNSystem.cs
Content.Server/NPC/HTN/Preconditions/TargetInLOSPrecondition.cs
Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs
Content.Server/NPC/Queries/Considerations/TargetTargetingCon.cs [new file with mode: 0644]
Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs
Content.Server/NPC/Systems/NPCUtilitySystem.cs
Content.Shared/Access/AccessLevelPrototype.cs
Content.Shared/Turrets/TurretTargetSettingsComponent.cs [new file with mode: 0644]
Content.Shared/Turrets/TurretTargetSettingsSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/prototypes/access/accesses.ftl
Resources/Prototypes/Access/command.yml
Resources/Prototypes/Access/silicon.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml
Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml
Resources/Prototypes/Entities/Mobs/Player/silicon.yml
Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml
Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml
Resources/Prototypes/NPCs/root.yml
Resources/Prototypes/NPCs/utility_queries.yml

index aeb4cc163fb4ad167ada24aab264bb8a0c14fb37..a9b08aac8b3abe5e882fc4f483d54d8494e81ab7 100644 (file)
@@ -31,7 +31,7 @@ public sealed class IdCardSystem : SharedIdCardSystem
     private void OnMicrowaved(EntityUid uid, IdCardComponent component, BeingMicrowavedEvent args)
     {
         if (!component.CanMicrowave || !TryComp<MicrowaveComponent>(args.Microwave, out var micro) || micro.Broken)
-            return;   
+            return;
 
         if (TryComp<AccessComponent>(uid, out var access))
         {
@@ -78,7 +78,12 @@ public sealed class IdCardSystem : SharedIdCardSystem
             }
 
             // Give them a wonderful new access to compensate for everything
-            var random = _random.Pick(_prototypeManager.EnumeratePrototypes<AccessLevelPrototype>().ToArray());
+            var ids = _prototypeManager.EnumeratePrototypes<AccessLevelPrototype>().Where(x => x.CanAddToIdCard).ToArray();
+
+            if (ids.Length == 0)
+                return;
+
+            var random = _random.Pick(ids);
 
             access.Tags.Add(random.ID);
             Dirty(uid, access);
index 2e4fcf5298b4a1a4a9b7015c2f08b59580c64aec..21cdfba8017409889670d5df0a88117b3dec5aad 100644 (file)
@@ -40,6 +40,13 @@ public sealed partial class NPCRangedCombatComponent : Component
     [ViewVariables(VVAccess.ReadWrite)]
     public bool TargetInLOS = false;
 
+    /// <summary>
+    /// If true, only opaque objects will block line of sight.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite)]
+    // ReSharper disable once InconsistentNaming
+    public bool UseOpaqueForLOSChecks = false;
+
     /// <summary>
     /// Delay after target is in LOS before we start shooting.
     /// </summary>
index f482e0808cb9489bcd650821613dbf4a15feb18f..788a3476382b71c9aaa2c88baa778fcc27b7a62a 100644 (file)
@@ -10,7 +10,7 @@ public sealed partial class HTNComponent : NPCComponent
     /// The base task to use for planning
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite),
-     DataField("rootTask", required: true)]
+    DataField("rootTask", required: true)]
     public HTNCompoundTask RootTask = default!;
 
     /// <summary>
@@ -47,4 +47,10 @@ public sealed partial class HTNComponent : NPCComponent
     /// Is this NPC currently planning?
     /// </summary>
     [ViewVariables] public bool Planning => PlanningJob != null;
+
+    /// <summary>
+    /// Determines whether plans should be made / updated for this entity
+    /// </summary>
+    [DataField]
+    public bool Enabled = true;
 }
index cfa670e14453f5b9c65de3359d9839e5032e59be..ce4b248a0d267e5d147efad902a22ed69d712b6d 100644 (file)
@@ -133,6 +133,39 @@ public sealed class HTNSystem : EntitySystem
         component.PlanningJob = null;
     }
 
+    /// <summary>
+    /// Enable / disable the hierarchical task network of an entity
+    /// </summary>
+    /// <param name="ent">The entity and its <see cref="HTNComponent"/></param>
+    /// <param name="state">Set 'true' to enable, or 'false' to disable, the HTN</param>
+    /// <param name="planCooldown">Specifies a time in seconds before the entity can start planning a new action (only takes effect when the HTN is enabled)</param>
+    // ReSharper disable once InconsistentNaming
+    [PublicAPI]
+    public void SetHTNEnabled(Entity<HTNComponent> ent, bool state, float planCooldown = 0f)
+    {
+        if (ent.Comp.Enabled == state)
+            return;
+
+        ent.Comp.Enabled = state;
+        ent.Comp.PlanAccumulator = planCooldown;
+
+        ent.Comp.PlanningToken?.Cancel();
+        ent.Comp.PlanningToken = null;
+
+        if (ent.Comp.Plan != null)
+        {
+            var currentOperator = ent.Comp.Plan.CurrentOperator;
+
+            ShutdownTask(currentOperator, ent.Comp.Blackboard, HTNOperatorStatus.Failed);
+            ShutdownPlan(ent.Comp);
+
+            ent.Comp.Plan = null;
+        }
+
+        if (ent.Comp.Enabled && ent.Comp.PlanAccumulator <= 0)
+            RequestPlan(ent.Comp);
+    }
+
     /// <summary>
     /// Forces the NPC to replan.
     /// </summary>
@@ -147,12 +180,15 @@ public sealed class HTNSystem : EntitySystem
         _planQueue.Process();
         var query = EntityQueryEnumerator<ActiveNPCComponent, HTNComponent>();
 
-        while(query.MoveNext(out var uid, out _, out var comp))
+        while (query.MoveNext(out var uid, out _, out var comp))
         {
             // If we're over our max count or it's not MapInit then ignore the NPC.
             if (count >= maxUpdates)
                 break;
 
+            if (!comp.Enabled)
+                continue;
+
             if (comp.PlanningJob != null)
             {
                 if (comp.PlanningJob.Exception != null)
index 0b233292a4beb0ee7171722981c5a3574aaf17c6..bb27ae8868f4644fd13ecda2eb251068b5d3785a 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Interaction;
+using Content.Shared.Physics;
 
 namespace Content.Server.NPC.HTN.Preconditions;
 
@@ -13,6 +14,9 @@ public sealed partial class TargetInLOSPrecondition : HTNPrecondition
     [DataField("rangeKey")]
     public string RangeKey = "RangeKey";
 
+    [DataField("opaqueKey")]
+    public bool UseOpaqueForLOSChecksKey = true;
+
     public override void Initialize(IEntitySystemManager sysManager)
     {
         base.Initialize(sysManager);
@@ -27,7 +31,8 @@ public sealed partial class TargetInLOSPrecondition : HTNPrecondition
             return false;
 
         var range = blackboard.GetValueOrDefault<float>(RangeKey, _entManager);
+        var collisionGroup = UseOpaqueForLOSChecksKey ? CollisionGroup.Opaque : (CollisionGroup.Impassable | CollisionGroup.InteractImpassable);
 
-        return _interaction.InRangeUnobstructed(owner, target, range);
+        return _interaction.InRangeUnobstructed(owner, target, range, collisionGroup);
     }
 }
index 53c5ed19522b3a2177c7bd033141ec566f6dd00f..65620e73aa9a0b026c7c2ea93bea0f3e9f70c516 100644 (file)
@@ -33,6 +33,12 @@ public sealed partial class GunOperator : HTNOperator, IHtnConditionalShutdown
     [DataField("requireLOS")]
     public bool RequireLOS = false;
 
+    /// <summary>
+    /// If true, only opaque objects will block line of sight.
+    /// </summary>
+    [DataField("opaqueKey")]
+    public bool UseOpaqueForLOSChecks = false;
+
     // Like movement we add a component and pass it off to the dedicated system.
 
     public override async Task<(bool Valid, Dictionary<string, object>? Effects)> Plan(NPCBlackboard blackboard,
@@ -56,8 +62,10 @@ public sealed partial class GunOperator : HTNOperator, IHtnConditionalShutdown
     public override void Startup(NPCBlackboard blackboard)
     {
         base.Startup(blackboard);
+
         var ranged = _entManager.EnsureComponent<NPCRangedCombatComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
         ranged.Target = blackboard.GetValue<EntityUid>(TargetKey);
+        ranged.UseOpaqueForLOSChecks = UseOpaqueForLOSChecks;
 
         if (blackboard.TryGetValue<float>(NPCBlackboard.RotateSpeed, out var rotSpeed, _entManager))
         {
diff --git a/Content.Server/NPC/Queries/Considerations/TargetTargetingCon.cs b/Content.Server/NPC/Queries/Considerations/TargetTargetingCon.cs
new file mode 100644 (file)
index 0000000..600eb20
--- /dev/null
@@ -0,0 +1,12 @@
+namespace Content.Server.NPC.Queries.Considerations;
+
+/// <summary>
+/// Returns 0f if the NPC has a <see cref="TurretTargetSettingsComponent"/> and the 
+/// target entity is exempt from being targeted, otherwise it returns 1f.
+/// See <see cref="TurretTargetSettingsSystem.EntityIsTargetForTurret"/>
+/// for further details on turret target validation.
+/// </summary>
+public sealed partial class TurretTargetingCon : UtilityConsideration
+{
+
+}
index d7196ea73c71dcb9602b12363dc170e057ca6477..f4e312fbd4eaa4dc9a3ab16df2f579938aff2dcd 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Server.NPC.Components;
 using Content.Shared.CombatMode;
 using Content.Shared.Interaction;
+using Content.Shared.Physics;
 using Content.Shared.Weapons.Ranged.Components;
 using Content.Shared.Weapons.Ranged.Events;
 using Robust.Shared.Map;
@@ -132,8 +133,10 @@ public sealed partial class NPCCombatSystem
             if (comp.LOSAccumulator < 0f)
             {
                 comp.LOSAccumulator += UnoccludedCooldown;
+
                 // For consistency with NPC steering.
-                comp.TargetInLOS = _interaction.InRangeUnobstructed(uid, comp.Target, distance + 0.1f);
+                var collisionGroup = comp.UseOpaqueForLOSChecks ? CollisionGroup.Opaque : (CollisionGroup.Impassable | CollisionGroup.InteractImpassable);
+                comp.TargetInLOS = _interaction.InRangeUnobstructed(uid, comp.Target, distance + 0.1f, collisionGroup);
             }
 
             if (!comp.TargetInLOS)
index 60bc5cdfd8b86f7f846f0ce989b238e468a63001..b5d3ac3cbde377434e9b9dea96a5c6bdb250366f 100644 (file)
@@ -20,6 +20,7 @@ using Content.Shared.NPC.Systems;
 using Content.Shared.Nutrition.Components;
 using Content.Shared.Nutrition.EntitySystems;
 using Content.Shared.Tools.Systems;
+using Content.Shared.Turrets;
 using Content.Shared.Weapons.Melee;
 using Content.Shared.Weapons.Ranged.Components;
 using Content.Shared.Weapons.Ranged.Events;
@@ -53,6 +54,7 @@ public sealed class NPCUtilitySystem : EntitySystem
     [Dependency] private readonly ExamineSystemShared _examine = default!;
     [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
     [Dependency] private readonly MobThresholdSystem _thresholdSystem = default!;
+    [Dependency] private readonly TurretTargetSettingsSystem _turretTargetSettings = default!;
 
     private EntityQuery<PuddleComponent> _puddleQuery;
     private EntityQuery<TransformComponent> _xformQuery;
@@ -358,6 +360,14 @@ public sealed class NPCUtilitySystem : EntitySystem
                         return 1f;
                     return 0f;
                 }
+            case TurretTargetingCon:
+                {
+                    if (!TryComp<TurretTargetSettingsComponent>(owner, out var turretTargetSettings) ||
+                        _turretTargetSettings.EntityIsTargetForTurret((owner, turretTargetSettings), targetUid))
+                        return 1f;
+
+                    return 0f;
+                }
             default:
                 throw new NotImplementedException();
         }
index e3a3b426b09b88be34a7378b9832a7c03ab6ed48..f44ad64228828223986ec243fa8c0c295967ad5b 100644 (file)
@@ -15,9 +15,15 @@ namespace Content.Shared.Access
         /// <summary>
         ///     The player-visible name of the access level, in the ID card console and such.
         /// </summary>
-        [DataField("name")]
+        [DataField]
         public string? Name { get; set; }
 
+        /// <summary>
+        ///     Denotes whether this access level is intended to be assignable to a crew ID card.
+        /// </summary>
+        [DataField]
+        public bool CanAddToIdCard = true;
+
         public string GetAccessLevelName()
         {
             if (Name is { } name)
diff --git a/Content.Shared/Turrets/TurretTargetSettingsComponent.cs b/Content.Shared/Turrets/TurretTargetSettingsComponent.cs
new file mode 100644 (file)
index 0000000..d019b09
--- /dev/null
@@ -0,0 +1,19 @@
+using Content.Shared.Access;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Turrets;
+
+/// <summary>
+/// Attached to entities to provide them with turret target selection data.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(TurretTargetSettingsSystem))]
+public sealed partial class TurretTargetSettingsComponent : Component
+{
+    /// <summary>
+    /// Crew with one or more access levels from this list are exempt from being targeted by turrets.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public HashSet<ProtoId<AccessLevelPrototype>> ExemptAccessLevels = new();
+}
diff --git a/Content.Shared/Turrets/TurretTargetSettingsSystem.cs b/Content.Shared/Turrets/TurretTargetSettingsSystem.cs
new file mode 100644 (file)
index 0000000..56f60e0
--- /dev/null
@@ -0,0 +1,126 @@
+using Content.Shared.Access;
+using Content.Shared.Access.Systems;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Turrets;
+
+/// <summary>
+/// This system is used for validating potential targets for NPCs with a <see cref="TurretTargetSettingsComponent"/> (i.e., turrets).
+/// A turret will consider an entity a valid target if the entity does not possess any access tags which appear on the
+/// turret's <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list.
+/// </summary>
+public sealed partial class TurretTargetSettingsSystem : EntitySystem
+{
+    [Dependency] private readonly AccessReaderSystem _accessReader = default!;
+
+    private ProtoId<AccessLevelPrototype> _accessLevelBorg = "Borg";
+    private ProtoId<AccessLevelPrototype> _accessLevelBasicSilicon = "BasicSilicon";
+
+    /// <summary>
+    /// Adds or removes access levels from a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list.
+    /// </summary>
+    /// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
+    /// <param name="exemption">The proto ID for the access level</param>
+    /// <param name="enabled">Set 'true' to add the exemption, or 'false' to remove it</param>
+    [PublicAPI]
+    public void SetAccessLevelExemption(Entity<TurretTargetSettingsComponent> ent, ProtoId<AccessLevelPrototype> exemption, bool enabled)
+    {
+        if (enabled)
+            ent.Comp.ExemptAccessLevels.Add(exemption);
+        else
+            ent.Comp.ExemptAccessLevels.Remove(exemption);
+    }
+
+    /// <summary>
+    /// Adds or removes a collection of access levels from a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list.
+    /// </summary>
+    /// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
+    /// <param name="exemption">The collection of access level proto IDs to add or remove</param>
+    /// <param name="enabled">Set 'true' to add the collection as exemptions, or 'false' to remove them</param>
+    [PublicAPI]
+    public void SetAccessLevelExemptions(Entity<TurretTargetSettingsComponent> ent, ICollection<ProtoId<AccessLevelPrototype>> exemptions, bool enabled)
+    {
+        foreach (var exemption in exemptions)
+            SetAccessLevelExemption(ent, exemption, enabled);
+    }
+
+    /// <summary>
+    /// Sets a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list to contain only a supplied collection of access levels.
+    /// </summary>
+    /// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
+    /// <param name="exemptions">The supplied collection of access level proto IDs</param>
+    [PublicAPI]
+    public void SyncAccessLevelExemptions(Entity<TurretTargetSettingsComponent> ent, ICollection<ProtoId<AccessLevelPrototype>> exemptions)
+    {
+        ent.Comp.ExemptAccessLevels.Clear();
+        SetAccessLevelExemptions(ent, exemptions, true);
+    }
+
+    /// <summary>
+    /// Sets a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list to match that of another.
+    /// </summary>
+    /// <param name="target">The entity this is having its exemption list updated <see cref="TurretTargetSettingsComponent"/></param>
+    /// <param name="source">The entity that is being used as a template for the target</param>
+    [PublicAPI]
+    public void SyncAccessLevelExemptions(Entity<TurretTargetSettingsComponent> target, Entity<TurretTargetSettingsComponent> source)
+    {
+        SyncAccessLevelExemptions(target, source.Comp.ExemptAccessLevels);
+    }
+
+    /// <summary>
+    /// Returns whether a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list contains a specific access level.
+    /// </summary>
+    /// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
+    /// <param name="exemption">The access level proto ID being checked</param>
+    [PublicAPI]
+    public bool HasAccessLevelExemption(Entity<TurretTargetSettingsComponent> ent, ProtoId<AccessLevelPrototype> exemption)
+    {
+        if (ent.Comp.ExemptAccessLevels.Count == 0)
+            return false;
+
+        return ent.Comp.ExemptAccessLevels.Contains(exemption);
+    }
+
+    /// <summary>
+    /// Returns whether a <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list contains one or more access levels from another collection.
+    /// </summary>
+    /// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
+    /// <param name="exemptions"></param>
+    [PublicAPI]
+    public bool HasAnyAccessLevelExemption(Entity<TurretTargetSettingsComponent> ent, ICollection<ProtoId<AccessLevelPrototype>> exemptions)
+    {
+        if (ent.Comp.ExemptAccessLevels.Count == 0)
+            return false;
+
+        foreach (var exemption in exemptions)
+        {
+            if (HasAccessLevelExemption(ent, exemption))
+                return true;
+        }
+
+        return false;
+    }
+
+    /// <summary>
+    /// Returns whether an entity is a valid target for a turret.
+    /// </summary>
+    /// <remarks>
+    /// Returns false if the target possesses one or more access tags that are present on the entity's <see cref="TurretTargetSettingsComponent.ExemptAccessLevels"/> list.
+    /// </remarks>
+    /// <param name="ent">The entity and its <see cref="TurretTargetSettingsComponent"/></param>
+    /// <param name="target">The target entity</param>
+    [PublicAPI]
+    public bool EntityIsTargetForTurret(Entity<TurretTargetSettingsComponent> ent, EntityUid target)
+    {
+        var accessLevels = _accessReader.FindAccessTags(target);
+
+        if (accessLevels.Contains(_accessLevelBorg))
+            return !HasAccessLevelExemption(ent, _accessLevelBorg);
+
+        if (accessLevels.Contains(_accessLevelBasicSilicon))
+            return !HasAccessLevelExemption(ent, _accessLevelBasicSilicon);
+
+        return !HasAnyAccessLevelExemption(ent, accessLevels);
+    }
+}
index 1f867447efe7e366660301c29b0cc57458f59531..3d72fc59a227a97b430467c9432a32e45d95afbf 100644 (file)
@@ -45,3 +45,7 @@ id-card-access-level-syndicate-agent = Syndicate Agent
 id-card-access-level-central-command = Central Command
 
 id-card-access-level-wizard = Wizard
+
+id-card-access-level-station-ai = Artifical Intelligence
+id-card-access-level-borg = Cyborg
+id-card-access-level-basic-silicon = Robot
\ No newline at end of file
index b74b4bb3b13861386dd7311b0ef28a99c488ba00..92e1355f798c1a4dfb6275167826a7cdce86052d 100644 (file)
 - type: accessGroup
   id: Command
   tags:
-  - Command
   - Captain
-  - HeadOfPersonnel
+  - Command
+  - ChiefEngineer
+  - ChiefMedicalOfficer
   - Cryogenics
-
+  - HeadOfPersonnel
+  - HeadOfSecurity
+  - Quartermaster
+  - ResearchDirector
+  
 - type: accessLevel
   id: EmergencyShuttleRepealAll
   name: id-card-access-level-emergency-shuttle-repeal
+  canAddToIdCard: false
 
 - type: accessLevel
   id: Cryogenics
diff --git a/Resources/Prototypes/Access/silicon.yml b/Resources/Prototypes/Access/silicon.yml
new file mode 100644 (file)
index 0000000..a8f9198
--- /dev/null
@@ -0,0 +1,21 @@
+- type: accessLevel
+  id: Borg
+  name: id-card-access-level-borg
+  canAddToIdCard: false
+
+- type: accessLevel
+  id: BasicSilicon
+  name: id-card-access-level-basic-silicon
+  canAddToIdCard: false
+
+- type: accessLevel
+  id: StationAi
+  name: id-card-access-level-station-ai
+  canAddToIdCard: false
+  
+- type: accessGroup
+  id: Silicon
+  tags:
+  - StationAi
+  - Borg
+  - BasicSilicon
index 79f3d2d83bb24d22fbedce57716339ecb971fc26..6fd3b3fbb9b582570ad1505301745ff4d00e79b1 100644 (file)
     enabled: false
     groups:
     - AllAccess
+    tags:
+    - Borg
   - type: AccessReader
     access: [["Command"], ["Research"]]
   - type: ShowJobIcons
index 6a6e4285ebb7b0ecab02f76b86921a963215a9e6..9d193a52e60109d4e306f7dca16cac6cc0331a3b 100644 (file)
@@ -31,6 +31,9 @@
   - type: NpcFactionMember
     factions:
     - SimpleNeutral
+  - type: Access
+    tags:
+    - BasicSilicon
   - type: IntrinsicRadioReceiver
   - type: ActiveRadio
     channels:
index 3eabbd870021ae714cceee9f08c210b7f441deac..0933cd557340965df3ce3d9e257227edd2d75a89 100644 (file)
@@ -30,6 +30,7 @@
   - type: Access
     groups:
     - AllAccess
+    - Silicon
     tags:
     - NuclearOperative
     - SyndicateAgent
index 8dc147249d41d8a774fac0be2d3090fd25baaa9f..c287c9f0083a3a9649e292bbf20590f4a639be21 100644 (file)
   - type: Access
     groups:
     - AllAccess
+    - Silicon
   - type: Eye
     drawFov: false
   - type: Examiner
index 126b8fc943551f1e3b71da5a51267bb5862d25f1..45f86e11b0c1dd5bb8913aa4fcc0bb46811e70af 100644 (file)
   - type: Access
     groups:
     - AllAccess
+    - Silicon
     tags:
     - CentralCommand
     - NuclearOperative
index 257ec06c9178142a477f7b9b3677b6225027d026..6dd624af7ab014e2e127047f3900e6751c06c4d3 100644 (file)
@@ -90,6 +90,8 @@
     - Armory
     - Atmospherics
     - Bar
+    - BasicSilicon
+    - Borg
     - Brig
     - Detective
     - Captain
     - Salvage
     - Security
     - Service
+    - StationAi
     - Theatre
     - CentralCommand
     - NuclearOperative
index c6d2a8ee2659a42a1aadead32b0c71c2eb94af47..d32e140b29248d015a89f63b9864852d72c38817 100644 (file)
     - tasks:
         - !type:HTNCompoundTask
           task: IdleSpinCompound
+          
+- type: htnCompound
+  id: EnergyTurretCompound
+  branches:
+    - tasks:
+        - !type:HTNPrimitiveTask
+          operator: !type:UtilityOperator
+            proto: NearbyGunTargets
+        
+        - !type:HTNPrimitiveTask
+          preconditions:
+            - !type:KeyExistsPrecondition
+              key: Target
+            - !type:TargetInRangePrecondition
+              targetKey: Target
+              # TODO: Non-scuffed
+              rangeKey: RangedRange
+            - !type:TargetInLOSPrecondition
+              targetKey: Target
+              rangeKey: RangedRange
+              opaqueKey: true
+          operator: !type:GunOperator
+            targetKey: Target
+            opaqueKey: true
+          services:
+            - !type:UtilityService
+              id: RangedService
+              proto: NearbyGunTargets
+              key: Target
+
+    - tasks:
+        - !type:HTNCompoundTask
+          task: IdleSpinCompound
 
 - type: htnCompound
   id: SimpleRangedHostileCompound
index e10a0ed30cd8f31dfc32859ad2ac403b4389bb5d..03764e2b1f1518887b91d350526decd31a248e42 100644 (file)
   considerations:
     - !type:TargetIsAliveCon
       curve: !type:BoolCurve
+    - !type:TargetIsCritCon
+      curve: !type:InverseBoolCurve
+    - !type:TurretTargetingCon
+      curve: !type:BoolCurve
     - !type:TargetDistanceCon
       curve: !type:PresetCurve
         preset: TargetDistance