]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Cleanbot tweaks (#15821)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Mon, 1 May 2023 14:30:15 +0000 (00:30 +1000)
committerGitHub <noreply@github.com>
Mon, 1 May 2023 14:30:15 +0000 (00:30 +1000)
Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs
Content.Server/NPC/HTN/PrimitiveTasks/Operators/Fluid/PickPuddleOperator.cs [new file with mode: 0644]
Content.Server/NPC/HTN/PrimitiveTasks/Operators/InteractWithOperator.cs [new file with mode: 0644]
Content.Server/NPC/HTN/PrimitiveTasks/Operators/PickAccessibleComponentOperator.cs
Content.Server/NPC/NPCBlackboard.cs
Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml
Resources/Prototypes/NPCs/cleanbot.yml
Resources/Prototypes/NPCs/idle.yml

index a63cc350d0d2980a2111a781b9d18d0c2eecae89..ce6ba487abda28c38bf9f840599de9d3baeec633 100644 (file)
@@ -31,6 +31,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         base.Initialize();
         SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit);
         SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract);
+        SubscribeLocalEvent<AbsorbentComponent, InteractNoHandEvent>(OnInteractNoHand);
         SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange);
     }
 
@@ -79,31 +80,38 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         Dirty(component);
     }
 
-    private void OnAfterInteract(EntityUid uid, AbsorbentComponent component, AfterInteractEvent args)
+    private void OnInteractNoHand(EntityUid uid, AbsorbentComponent component, InteractNoHandEvent args)
     {
-        if (!args.CanReach || args.Handled || _useDelay.ActiveDelay(uid))
+        if (args.Handled || args.Target == null)
             return;
 
-        if (!_solutionSystem.TryGetSolution(args.Used, AbsorbentComponent.SolutionName, out var absorberSoln))
+        Mop(uid, args.Target.Value, uid, component);
+        args.Handled = true;
+    }
+
+    private void OnAfterInteract(EntityUid uid, AbsorbentComponent component, AfterInteractEvent args)
+    {
+        if (!args.CanReach || args.Handled || args.Target == null)
             return;
 
-        // Didn't click anything so don't do anything.
-        if (args.Target is not { Valid: true } target)
-        {
+        Mop(args.User, args.Target.Value, args.Used, component);
+        args.Handled = true;
+    }
+
+    private void Mop(EntityUid user, EntityUid target, EntityUid used, AbsorbentComponent component)
+    {
+        if (!_solutionSystem.TryGetSolution(used, AbsorbentComponent.SolutionName, out var absorberSoln))
             return;
-        }
 
         // If it's a puddle try to grab from
-        if (!TryPuddleInteract(args.User, uid, target, component, absorberSoln))
+        if (!TryPuddleInteract(user, used, target, component, absorberSoln))
         {
             // Do a transfer, try to get water onto us and transfer anything else to them.
 
             // If it's anything else transfer to
-            if (!TryTransferAbsorber(args.User, uid, target, component, absorberSoln))
+            if (!TryTransferAbsorber(user, used, target, component, absorberSoln))
                 return;
         }
-
-        args.Handled = true;
     }
 
     /// <summary>
diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Fluid/PickPuddleOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Fluid/PickPuddleOperator.cs
new file mode 100644 (file)
index 0000000..ecce69c
--- /dev/null
@@ -0,0 +1,106 @@
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Content.Server.Chemistry.EntitySystems;
+using Content.Server.Fluids.EntitySystems;
+using Content.Server.NPC.Pathfinding;
+using Content.Shared.Fluids.Components;
+using Robust.Shared.Map;
+
+namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Fluid;
+
+/// <summary>
+/// Picks a nearby evaporatable puddle.
+/// </summary>
+public sealed class PickPuddleOperator : HTNOperator
+{
+    // This is similar to PickAccessibleComponent however I have an idea on generic utility queries
+    // that can also be re-used for melee that needs further fleshing out.
+
+    [Dependency] private readonly IComponentFactory _factory = default!;
+    [Dependency] private readonly IEntityManager _entManager = default!;
+    private PathfindingSystem _pathfinding = default!;
+    private EntityLookupSystem _lookup = default!;
+
+    [DataField("rangeKey", required: true)]
+    public string RangeKey = string.Empty;
+
+    [DataField("target")] public string Target = "Target";
+
+    [DataField("targetKey", required: true)]
+    public string TargetKey = string.Empty;
+
+    /// <summary>
+    /// Where the pathfinding result will be stored (if applicable). This gets removed after execution.
+    /// </summary>
+    [DataField("pathfindKey")]
+    public string PathfindKey = NPCBlackboard.PathfindKey;
+
+    public override void Initialize(IEntitySystemManager sysManager)
+    {
+        base.Initialize(sysManager);
+        _lookup = sysManager.GetEntitySystem<EntityLookupSystem>();
+        _pathfinding = sysManager.GetEntitySystem<PathfindingSystem>();
+    }
+
+    /// <inheritdoc/>
+    [Obsolete("Obsolete")]
+    public override async Task<(bool Valid, Dictionary<string, object>? Effects)> Plan(NPCBlackboard blackboard,
+        CancellationToken cancelToken)
+    {
+        var range = blackboard.GetValueOrDefault<float>(RangeKey, _entManager);
+        var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
+
+        if (!blackboard.TryGetValue<EntityCoordinates>(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager))
+        {
+            return (false, null);
+        }
+
+        var targets = new List<EntityUid>();
+        var puddleSystem = _entManager.System<PuddleSystem>();
+        var solSystem = _entManager.System<SolutionContainerSystem>();
+
+        foreach (var comp in _lookup.GetComponentsInRange<PuddleComponent>(coordinates, range))
+        {
+            if (comp.Owner == owner ||
+                !solSystem.TryGetSolution(comp.Owner, comp.SolutionName, out var puddleSolution) ||
+                puddleSystem.CanFullyEvaporate(puddleSolution))
+            {
+                continue;
+            }
+
+            targets.Add((comp.Owner));
+        }
+
+        if (targets.Count == 0)
+        {
+            return (false, null);
+        }
+
+        foreach (var target in targets)
+        {
+            var path = await _pathfinding.GetPath(
+                owner,
+                target,
+                1f,
+                cancelToken,
+                flags: _pathfinding.GetFlags(blackboard));
+
+            if (path.Result != PathResult.Path)
+            {
+                return (false, null);
+            }
+
+            var xform = _entManager.GetComponent<TransformComponent>(target);
+
+            return (true, new Dictionary<string, object>()
+            {
+                { Target, target },
+                { TargetKey, xform.Coordinates },
+                { PathfindKey, path}
+            });
+        }
+
+        return (false, null);
+    }
+}
diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/InteractWithOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/InteractWithOperator.cs
new file mode 100644 (file)
index 0000000..1a4238d
--- /dev/null
@@ -0,0 +1,31 @@
+using Content.Server.Interaction;
+using Content.Shared.Timing;
+
+namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators;
+
+public sealed class InteractWithOperator : HTNOperator
+{
+    [Dependency] private readonly IEntityManager _entManager = default!;
+
+    /// <summary>
+    /// Key that contains the target entity.
+    /// </summary>
+    [DataField("targetKey", required: true)]
+    public string TargetKey = default!;
+
+    public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
+    {
+        var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
+
+        if (_entManager.System<UseDelaySystem>().ActiveDelay(owner) ||
+            !blackboard.TryGetValue<EntityUid>(TargetKey, out var moveTarget, _entManager) ||
+            !_entManager.TryGetComponent<TransformComponent>(moveTarget, out var targetXform))
+        {
+            return HTNOperatorStatus.Continuing;
+        }
+
+        _entManager.System<InteractionSystem>().UserInteraction(owner, targetXform.Coordinates, moveTarget);
+
+        return HTNOperatorStatus.Finished;
+    }
+}
index 174f9559dc8962ab7b6164f511818c7e1d2e4cc3..53f1826c82d22ec8ec14d0e1fbdb142c0587bea6 100644 (file)
@@ -22,6 +22,9 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
     [DataField("targetKey", required: true)]
     public string TargetKey = string.Empty;
 
+    [DataField("target")]
+    public string TargetEntity = "Target";
+
     [DataField("component", required: true)]
     public string Component = string.Empty;
 
@@ -58,7 +61,7 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
 
         var compType = registration.Type;
         var query = _entManager.GetEntityQuery(compType);
-        var targets = new List<Component>();
+        var targets = new List<EntityUid>();
 
         // TODO: Need to get ones that are accessible.
         // TODO: Look at unreal HTN to see repeatable ones maybe?
@@ -68,7 +71,7 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
             if (entity == owner || !query.TryGetComponent(entity, out var comp))
                 continue;
 
-            targets.Add(comp);
+            targets.Add(entity);
         }
 
         if (targets.Count == 0)
@@ -76,16 +79,12 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
             return (false, null);
         }
 
-        blackboard.TryGetValue<float>(RangeKey, out var maxRange, _entManager);
-
-        if (maxRange == 0f)
-            maxRange = 7f;
-
-        while (targets.Count > 0)
+        foreach (var target in targets)
         {
-            var path = await _pathfinding.GetRandomPath(
+            var path = await _pathfinding.GetPath(
                 owner,
-                maxRange,
+                target,
+                1f,
                 cancelToken,
                 flags: _pathfinding.GetFlags(blackboard));
 
@@ -94,12 +93,13 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
                 return (false, null);
             }
 
-            var target = path.Path.Last().Coordinates;
+            var xform = _entManager.GetComponent<TransformComponent>(target);
 
             return (true, new Dictionary<string, object>()
             {
-                { TargetKey, target },
-                { PathfindKey, path}
+                { TargetEntity, target },
+                { TargetKey, xform.Coordinates },
+                { PathfindKey, path }
             });
         }
 
index e60be3fb96ab4067608661262c7a625d415578a3..0e12827cd71fdb74e6b1b0fbd188b32d5b7427bb 100644 (file)
@@ -1,7 +1,9 @@
 using System.Collections;
 using System.Diagnostics.CodeAnalysis;
+using Content.Server.Interaction;
 using Content.Shared.Access.Systems;
 using Content.Shared.ActionBlocker;
+using Content.Shared.Interaction;
 using Robust.Shared.Utility;
 
 namespace Content.Server.NPC;
@@ -18,6 +20,7 @@ public sealed class NPCBlackboard : IEnumerable<KeyValuePair<string, object>>
         {"FollowCloseRange", 3f},
         {"FollowRange", 7f},
         {"IdleRange", 7f},
+        {"InteractRange", SharedInteractionSystem.InteractionRange},
         {"MaximumIdleTime", 7f},
         {MedibotInjectRange, 4f},
         {MeleeMissChance, 0.3f},
index 6ff998396eb5060a19c3eb3abf830c36b78da63d..1faa7ec9a08c5936913c9987b8a18752a7e4ff6c 100644 (file)
     drawdepth: Mobs
     sprite: Mobs/Silicon/Bots/cleanbot.rsi
     state: cleanbot
-  - type: Drain
-    range: 1
-    unitsDestroyedPerSecond: 6
   - type: Construction
     graph: CleanBot
     node: bot
   - type: SentienceTarget
     flavorKind: station-event-random-sentience-flavor-mechanical
+  - type: Absorbent
+    pickupAmount: 10
+  - type: UseDelay
+    delay: 2
+  - type: SolutionRegeneration
+    solution: absorbed
+    generated:
+      reagents:
+        - ReagentId: Water
+          Quantity: 10
+  - type: SolutionPurge
+    solution: absorbed
+    preserve:
+      - Water
+    quantity: 10
   - type: SolutionContainerManager
     solutions:
-      drainBuffer:
-        maxVol: 30
+      absorbed:
+        maxVol: 50
   - type: MovementSpeedModifier
     baseWalkSpeed: 2
     baseSprintSpeed: 3
index 97d64de659162c0c00e5d3290b142881491959ba..ca3835338a1fa72ac96722a2b21d4049ad387487 100644 (file)
     - tasks:
         - id: PickPuddlePrimitive
         - id: MoveToAccessiblePrimitive
-        - id: SetIdleTimePrimitive
-        - id: WaitIdleTimePrimitive
-
+        - id: InteractWithPrimitive
 
 - type: htnPrimitive
   id: PickPuddlePrimitive
-  operator: !type:PickAccessibleComponentOperator
+  operator: !type:PickPuddleOperator
     rangeKey: BufferRange
     targetKey: MovementTarget
     component: Puddle
index d4e0d12988794d048b3906cc0d33b1853f6d75a2..f97e7dff63bf3345ee614398daf7bc44f2defb46 100644 (file)
 
 # Primitives
 - type: htnPrimitive
-  id: PickRandomRotationPrimitive
-  operator: !type:PickRandomRotationOperator
-    targetKey: RotateTarget
+  id: InteractWithPrimitive
+  preconditions:
+    - !type:TargetInRangePrecondition
+      targetKey: Target
+      rangeKey: InteractRange
+  operator: !type:InteractWithOperator
+    targetKey: Target
 
 - type: htnPrimitive
-  id: RotateToTargetPrimitive
-  operator: !type:RotateToTargetOperator
-    targetKey: RotateTarget
+  id: MoveToAccessiblePrimitive
+  operator: !type:MoveToOperator
+    pathfindInPlanning: false
 
 - type: htnPrimitive
   id: PickAccessiblePrimitive
     targetKey: MovementTarget
 
 - type: htnPrimitive
-  id: MoveToAccessiblePrimitive
-  operator: !type:MoveToOperator
-    pathfindInPlanning: false
+  id: PickRandomRotationPrimitive
+  operator: !type:PickRandomRotationOperator
+    targetKey: RotateTarget
+
+- type: htnPrimitive
+  id: RotateToTargetPrimitive
+  operator: !type:RotateToTargetOperator
+    targetKey: RotateTarget
 
 - type: htnPrimitive
   id: RandomIdleTimePrimitive