Cleanbot tweaks (#15821)

This commit is contained in:
metalgearsloth
2023-05-02 00:30:15 +10:00
committed by GitHub
parent 65d42352a5
commit 638009f5d3
8 changed files with 209 additions and 42 deletions

View File

@@ -31,6 +31,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit); SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit);
SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<AbsorbentComponent, InteractNoHandEvent>(OnInteractNoHand);
SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange); SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange);
} }
@@ -79,31 +80,38 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
Dirty(component); Dirty(component);
} }
private void OnInteractNoHand(EntityUid uid, AbsorbentComponent component, InteractNoHandEvent args)
{
if (args.Handled || args.Target == null)
return;
Mop(uid, args.Target.Value, uid, component);
args.Handled = true;
}
private void OnAfterInteract(EntityUid uid, AbsorbentComponent component, AfterInteractEvent args) private void OnAfterInteract(EntityUid uid, AbsorbentComponent component, AfterInteractEvent args)
{ {
if (!args.CanReach || args.Handled || _useDelay.ActiveDelay(uid)) if (!args.CanReach || args.Handled || args.Target == null)
return; return;
if (!_solutionSystem.TryGetSolution(args.Used, AbsorbentComponent.SolutionName, out var absorberSoln)) Mop(args.User, args.Target.Value, args.Used, component);
return; args.Handled = true;
}
// Didn't click anything so don't do anything. private void Mop(EntityUid user, EntityUid target, EntityUid used, AbsorbentComponent component)
if (args.Target is not { Valid: true } target) {
{ if (!_solutionSystem.TryGetSolution(used, AbsorbentComponent.SolutionName, out var absorberSoln))
return; return;
}
// If it's a puddle try to grab from // 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. // Do a transfer, try to get water onto us and transfer anything else to them.
// If it's anything else transfer to // If it's anything else transfer to
if (!TryTransferAbsorber(args.User, uid, target, component, absorberSoln)) if (!TryTransferAbsorber(user, used, target, component, absorberSoln))
return; return;
} }
args.Handled = true;
} }
/// <summary> /// <summary>

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -22,6 +22,9 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
[DataField("targetKey", required: true)] [DataField("targetKey", required: true)]
public string TargetKey = string.Empty; public string TargetKey = string.Empty;
[DataField("target")]
public string TargetEntity = "Target";
[DataField("component", required: true)] [DataField("component", required: true)]
public string Component = string.Empty; public string Component = string.Empty;
@@ -58,7 +61,7 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
var compType = registration.Type; var compType = registration.Type;
var query = _entManager.GetEntityQuery(compType); var query = _entManager.GetEntityQuery(compType);
var targets = new List<Component>(); var targets = new List<EntityUid>();
// TODO: Need to get ones that are accessible. // TODO: Need to get ones that are accessible.
// TODO: Look at unreal HTN to see repeatable ones maybe? // 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)) if (entity == owner || !query.TryGetComponent(entity, out var comp))
continue; continue;
targets.Add(comp); targets.Add(entity);
} }
if (targets.Count == 0) if (targets.Count == 0)
@@ -76,16 +79,12 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
return (false, null); return (false, null);
} }
blackboard.TryGetValue<float>(RangeKey, out var maxRange, _entManager); foreach (var target in targets)
if (maxRange == 0f)
maxRange = 7f;
while (targets.Count > 0)
{ {
var path = await _pathfinding.GetRandomPath( var path = await _pathfinding.GetPath(
owner, owner,
maxRange, target,
1f,
cancelToken, cancelToken,
flags: _pathfinding.GetFlags(blackboard)); flags: _pathfinding.GetFlags(blackboard));
@@ -94,12 +93,13 @@ public sealed class PickAccessibleComponentOperator : HTNOperator
return (false, null); return (false, null);
} }
var target = path.Path.Last().Coordinates; var xform = _entManager.GetComponent<TransformComponent>(target);
return (true, new Dictionary<string, object>() return (true, new Dictionary<string, object>()
{ {
{ TargetKey, target }, { TargetEntity, target },
{ PathfindKey, path} { TargetKey, xform.Coordinates },
{ PathfindKey, path }
}); });
} }

View File

@@ -1,7 +1,9 @@
using System.Collections; using System.Collections;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Content.Server.Interaction;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Interaction;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.NPC; namespace Content.Server.NPC;
@@ -18,6 +20,7 @@ public sealed class NPCBlackboard : IEnumerable<KeyValuePair<string, object>>
{"FollowCloseRange", 3f}, {"FollowCloseRange", 3f},
{"FollowRange", 7f}, {"FollowRange", 7f},
{"IdleRange", 7f}, {"IdleRange", 7f},
{"InteractRange", SharedInteractionSystem.InteractionRange},
{"MaximumIdleTime", 7f}, {"MaximumIdleTime", 7f},
{MedibotInjectRange, 4f}, {MedibotInjectRange, 4f},
{MeleeMissChance, 0.3f}, {MeleeMissChance, 0.3f},

View File

@@ -194,18 +194,30 @@
drawdepth: Mobs drawdepth: Mobs
sprite: Mobs/Silicon/Bots/cleanbot.rsi sprite: Mobs/Silicon/Bots/cleanbot.rsi
state: cleanbot state: cleanbot
- type: Drain
range: 1
unitsDestroyedPerSecond: 6
- type: Construction - type: Construction
graph: CleanBot graph: CleanBot
node: bot node: bot
- type: SentienceTarget - type: SentienceTarget
flavorKind: station-event-random-sentience-flavor-mechanical 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 - type: SolutionContainerManager
solutions: solutions:
drainBuffer: absorbed:
maxVol: 30 maxVol: 50
- type: MovementSpeedModifier - type: MovementSpeedModifier
baseWalkSpeed: 2 baseWalkSpeed: 2
baseSprintSpeed: 3 baseSprintSpeed: 3

View File

@@ -13,13 +13,11 @@
- tasks: - tasks:
- id: PickPuddlePrimitive - id: PickPuddlePrimitive
- id: MoveToAccessiblePrimitive - id: MoveToAccessiblePrimitive
- id: SetIdleTimePrimitive - id: InteractWithPrimitive
- id: WaitIdleTimePrimitive
- type: htnPrimitive - type: htnPrimitive
id: PickPuddlePrimitive id: PickPuddlePrimitive
operator: !type:PickAccessibleComponentOperator operator: !type:PickPuddleOperator
rangeKey: BufferRange rangeKey: BufferRange
targetKey: MovementTarget targetKey: MovementTarget
component: Puddle component: Puddle

View File

@@ -32,14 +32,18 @@
# Primitives # Primitives
- type: htnPrimitive - type: htnPrimitive
id: PickRandomRotationPrimitive id: InteractWithPrimitive
operator: !type:PickRandomRotationOperator preconditions:
targetKey: RotateTarget - !type:TargetInRangePrecondition
targetKey: Target
rangeKey: InteractRange
operator: !type:InteractWithOperator
targetKey: Target
- type: htnPrimitive - type: htnPrimitive
id: RotateToTargetPrimitive id: MoveToAccessiblePrimitive
operator: !type:RotateToTargetOperator operator: !type:MoveToOperator
targetKey: RotateTarget pathfindInPlanning: false
- type: htnPrimitive - type: htnPrimitive
id: PickAccessiblePrimitive id: PickAccessiblePrimitive
@@ -48,9 +52,14 @@
targetKey: MovementTarget targetKey: MovementTarget
- type: htnPrimitive - type: htnPrimitive
id: MoveToAccessiblePrimitive id: PickRandomRotationPrimitive
operator: !type:MoveToOperator operator: !type:PickRandomRotationOperator
pathfindInPlanning: false targetKey: RotateTarget
- type: htnPrimitive
id: RotateToTargetPrimitive
operator: !type:RotateToTargetOperator
targetKey: RotateTarget
- type: htnPrimitive - type: htnPrimitive
id: RandomIdleTimePrimitive id: RandomIdleTimePrimitive