Shooting NPCs and more (#18042)
* Add pirate shooting * Shooting working * Basics working * Refactor time * More conversion * Update primitives * Update yml * weh * Building again * Draft * weh * b * Start shutdown * Starting to take form * Code side done * is it worky * Fix prototypes * stuff * Shitty working * Juke events working * Even more cleanup * RTX * Fix interaction combat mode and compquery * GetAmmoCount relays * Fix rotation speed * Juke fixes * fixes * weh * The collision avoidance never ends * Fixes * Pause support * framework * lazy * Fix idling * Fix drip * goobed * Fix takeover shutdown bug * Merge fixes * shitter * Fix carpos
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
using Content.Server.NPC.Components;
|
||||
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat;
|
||||
|
||||
public sealed class JukeOperator : HTNOperator, IHtnConditionalShutdown
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
[DataField("jukeType")]
|
||||
public JukeType JukeType = JukeType.AdjacentTile;
|
||||
|
||||
[DataField("shutdownState")]
|
||||
public HTNPlanState ShutdownState { get; } = HTNPlanState.PlanFinished;
|
||||
|
||||
public override void Startup(NPCBlackboard blackboard)
|
||||
{
|
||||
base.Startup(blackboard);
|
||||
var juke = _entManager.EnsureComponent<NPCJukeComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
||||
juke.JukeType = JukeType;
|
||||
}
|
||||
|
||||
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||
{
|
||||
return HTNOperatorStatus.Finished;
|
||||
}
|
||||
|
||||
public void ConditionalShutdown(NPCBlackboard blackboard)
|
||||
{
|
||||
_entManager.RemoveComponent<NPCJukeComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,25 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Melee;
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat.Melee;
|
||||
|
||||
/// <summary>
|
||||
/// Attacks the specified key in melee combat.
|
||||
/// </summary>
|
||||
public sealed class MeleeOperator : HTNOperator
|
||||
public sealed class MeleeOperator : HTNOperator, IHtnConditionalShutdown
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// When to shut the task down.
|
||||
/// </summary>
|
||||
[DataField("shutdownState")]
|
||||
public HTNPlanState ShutdownState { get; } = HTNPlanState.TaskFinished;
|
||||
|
||||
/// <summary>
|
||||
/// Key that contains the target entity.
|
||||
/// </summary>
|
||||
@@ -53,10 +60,11 @@ public sealed class MeleeOperator : HTNOperator
|
||||
return (true, null);
|
||||
}
|
||||
|
||||
public override void Shutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
public void ConditionalShutdown(NPCBlackboard blackboard)
|
||||
{
|
||||
base.Shutdown(blackboard, status);
|
||||
_entManager.RemoveComponent<NPCMeleeCombatComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
||||
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||
_entManager.System<SharedCombatModeSystem>().SetInCombatMode(owner, false);
|
||||
_entManager.RemoveComponent<NPCMeleeCombatComponent>(owner);
|
||||
blackboard.Remove<EntityUid>(TargetKey);
|
||||
}
|
||||
|
||||
@@ -96,9 +104,10 @@ public sealed class MeleeOperator : HTNOperator
|
||||
status = HTNOperatorStatus.Failed;
|
||||
}
|
||||
|
||||
if (status != HTNOperatorStatus.Continuing)
|
||||
// Mark it as finished to continue the plan.
|
||||
if (status == HTNOperatorStatus.Continuing && ShutdownState == HTNPlanState.PlanFinished)
|
||||
{
|
||||
_entManager.RemoveComponent<NPCMeleeCombatComponent>(owner);
|
||||
status = HTNOperatorStatus.Finished;
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -1,16 +1,20 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Ranged;
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat.Ranged;
|
||||
|
||||
public sealed class RangedOperator : HTNOperator
|
||||
public sealed class GunOperator : HTNOperator, IHtnConditionalShutdown
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
[DataField("shutdownState")]
|
||||
public HTNPlanState ShutdownState { get; } = HTNPlanState.TaskFinished;
|
||||
|
||||
/// <summary>
|
||||
/// Key that contains the target entity.
|
||||
/// </summary>
|
||||
@@ -23,6 +27,12 @@ public sealed class RangedOperator : HTNOperator
|
||||
[DataField("targetState")]
|
||||
public MobState TargetState = MobState.Alive;
|
||||
|
||||
/// <summary>
|
||||
/// Do we require line of sight of the target before failing.
|
||||
/// </summary>
|
||||
[DataField("requireLOS")]
|
||||
public bool RequireLOS = 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,
|
||||
@@ -60,10 +70,11 @@ public sealed class RangedOperator : HTNOperator
|
||||
}
|
||||
}
|
||||
|
||||
public override void Shutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
public void ConditionalShutdown(NPCBlackboard blackboard)
|
||||
{
|
||||
base.Shutdown(blackboard, status);
|
||||
_entManager.RemoveComponent<NPCRangedCombatComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
||||
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||
_entManager.System<SharedCombatModeSystem>().SetInCombatMode(owner, false);
|
||||
_entManager.RemoveComponent<NPCRangedCombatComponent>(owner);
|
||||
blackboard.Remove<EntityUid>(TargetKey);
|
||||
}
|
||||
|
||||
@@ -89,9 +100,14 @@ public sealed class RangedOperator : HTNOperator
|
||||
switch (combat.Status)
|
||||
{
|
||||
case CombatStatus.TargetUnreachable:
|
||||
case CombatStatus.NotInSight:
|
||||
status = HTNOperatorStatus.Failed;
|
||||
break;
|
||||
case CombatStatus.NotInSight:
|
||||
if (RequireLOS)
|
||||
status = HTNOperatorStatus.Failed;
|
||||
else
|
||||
status = HTNOperatorStatus.Continuing;
|
||||
break;
|
||||
case CombatStatus.Normal:
|
||||
status = HTNOperatorStatus.Continuing;
|
||||
break;
|
||||
@@ -106,9 +122,10 @@ public sealed class RangedOperator : HTNOperator
|
||||
status = HTNOperatorStatus.Failed;
|
||||
}
|
||||
|
||||
if (status != HTNOperatorStatus.Continuing)
|
||||
// Mark it as finished to continue the plan.
|
||||
if (status == HTNOperatorStatus.Continuing && ShutdownState == HTNPlanState.PlanFinished)
|
||||
{
|
||||
_entManager.RemoveComponent<NPCRangedCombatComponent>(owner);
|
||||
status = HTNOperatorStatus.Finished;
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -4,14 +4,14 @@ using System.Threading.Tasks;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction;
|
||||
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators;
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Interactions;
|
||||
|
||||
public sealed class AltInteractOperator : HTNOperator
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
[DataField("targetKey")]
|
||||
public string Key = "CombatTarget";
|
||||
public string Key = "Target";
|
||||
|
||||
/// <summary>
|
||||
/// If this alt-interaction started a do_after where does the key get stored.
|
||||
@@ -0,0 +1,31 @@
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Shared.Hands.Components;
|
||||
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Interactions;
|
||||
|
||||
/// <summary>
|
||||
/// Drops the active hand entity underneath us.
|
||||
/// </summary>
|
||||
public sealed class DropOperator : HTNOperator
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||
{
|
||||
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, _entManager))
|
||||
{
|
||||
return HTNOperatorStatus.Finished;
|
||||
}
|
||||
|
||||
var owner = blackboard.GetValueOrDefault<EntityUid>(NPCBlackboard.Owner, _entManager);
|
||||
// TODO: Need some sort of interaction cooldown probably.
|
||||
var handsSystem = _entManager.System<HandsSystem>();
|
||||
|
||||
if (handsSystem.TryDrop(owner))
|
||||
{
|
||||
return HTNOperatorStatus.Finished;
|
||||
}
|
||||
|
||||
return HTNOperatorStatus.Failed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Server.Hands.Systems;
|
||||
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Interactions;
|
||||
|
||||
public sealed class EquipOperator : HTNOperator
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
[DataField("target")]
|
||||
public string Target = "Target";
|
||||
|
||||
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||
{
|
||||
if (!blackboard.TryGetValue<EntityUid>(Target, out var target, _entManager))
|
||||
{
|
||||
return HTNOperatorStatus.Failed;
|
||||
}
|
||||
|
||||
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||
var handsSystem = _entManager.System<HandsSystem>();
|
||||
|
||||
// TODO: As elsewhere need some generic interaction cooldown system
|
||||
if (handsSystem.TryPickup(owner, target))
|
||||
{
|
||||
return HTNOperatorStatus.Finished;
|
||||
}
|
||||
|
||||
return HTNOperatorStatus.Failed;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using Content.Server.Interaction;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Timing;
|
||||
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators;
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Interactions;
|
||||
|
||||
public sealed class InteractWithOperator : HTNOperator
|
||||
{
|
||||
@@ -24,6 +25,7 @@ public sealed class InteractWithOperator : HTNOperator
|
||||
return HTNOperatorStatus.Continuing;
|
||||
}
|
||||
|
||||
_entManager.System<SharedCombatModeSystem>().SetInCombatMode(owner, false);
|
||||
_entManager.System<InteractionSystem>().UserInteraction(owner, targetXform.Coordinates, moveTarget);
|
||||
|
||||
return HTNOperatorStatus.Finished;
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Shared.Hands.Components;
|
||||
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Interactions;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Swaps to any free hand.
|
||||
/// </summary>
|
||||
public sealed class SwapToFreeHandOperator : HTNOperator
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
public override async Task<(bool Valid, Dictionary<string, object>? Effects)> Plan(NPCBlackboard blackboard, CancellationToken cancelToken)
|
||||
{
|
||||
if (!blackboard.TryGetValue<List<string>>(NPCBlackboard.FreeHands, out var hands, _entManager) ||
|
||||
!_entManager.TryGetComponent<HandsComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner), out var handsComp))
|
||||
{
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
foreach (var hand in hands)
|
||||
{
|
||||
return (true, new Dictionary<string, object>()
|
||||
{
|
||||
{
|
||||
NPCBlackboard.ActiveHand, handsComp.Hands[hand]
|
||||
},
|
||||
{
|
||||
NPCBlackboard.ActiveHandFree, true
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||
{
|
||||
// TODO: Need interaction cooldown
|
||||
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||
var handSystem = _entManager.System<HandsSystem>();
|
||||
|
||||
if (!handSystem.TrySelectEmptyHand(owner))
|
||||
{
|
||||
return HTNOperatorStatus.Failed;
|
||||
}
|
||||
|
||||
return HTNOperatorStatus.Finished;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators;
|
||||
/// <summary>
|
||||
/// Moves an NPC to the specified target key. Hands the actual steering off to NPCSystem.Steering
|
||||
/// </summary>
|
||||
public sealed class MoveToOperator : HTNOperator
|
||||
public sealed class MoveToOperator : HTNOperator, IHtnConditionalShutdown
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
@@ -19,6 +19,12 @@ public sealed class MoveToOperator : HTNOperator
|
||||
private PathfindingSystem _pathfind = default!;
|
||||
private SharedTransformSystem _transform = default!;
|
||||
|
||||
/// <summary>
|
||||
/// When to shut the task down.
|
||||
/// </summary>
|
||||
[DataField("shutdownState")]
|
||||
public HTNPlanState ShutdownState { get; } = HTNPlanState.TaskFinished;
|
||||
|
||||
/// <summary>
|
||||
/// Should we assume the MovementTarget is reachable during planning or should we pathfind to it?
|
||||
/// </summary>
|
||||
@@ -35,7 +41,7 @@ public sealed class MoveToOperator : HTNOperator
|
||||
/// Target Coordinates to move to. This gets removed after execution.
|
||||
/// </summary>
|
||||
[DataField("targetKey")]
|
||||
public string TargetKey = "MovementTarget";
|
||||
public string TargetKey = "TargetCoordinates";
|
||||
|
||||
/// <summary>
|
||||
/// Where the pathfinding result will be stored (if applicable). This gets removed after execution.
|
||||
@@ -49,6 +55,12 @@ public sealed class MoveToOperator : HTNOperator
|
||||
[DataField("rangeKey")]
|
||||
public string RangeKey = "MovementRange";
|
||||
|
||||
/// <summary>
|
||||
/// Do we only need to move into line of sight.
|
||||
/// </summary>
|
||||
[DataField("stopOnLineOfSight")]
|
||||
public bool StopOnLineOfSight;
|
||||
|
||||
private const string MovementCancelToken = "MovementCancelToken";
|
||||
|
||||
public override void Initialize(IEntitySystemManager sysManager)
|
||||
@@ -132,6 +144,7 @@ public sealed class MoveToOperator : HTNOperator
|
||||
|
||||
// Re-use the path we may have if applicable.
|
||||
var comp = _steering.Register(uid, targetCoordinates);
|
||||
comp.ArriveOnLineOfSight = StopOnLineOfSight;
|
||||
|
||||
if (blackboard.TryGetValue<float>(RangeKey, out var range, _entManager))
|
||||
{
|
||||
@@ -150,10 +163,30 @@ public sealed class MoveToOperator : HTNOperator
|
||||
}
|
||||
}
|
||||
|
||||
public override void Shutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||
{
|
||||
base.Shutdown(blackboard, status);
|
||||
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||
|
||||
if (!_entManager.TryGetComponent<NPCSteeringComponent>(owner, out var steering))
|
||||
return HTNOperatorStatus.Failed;
|
||||
|
||||
// Just keep moving in the background and let the other tasks handle it.
|
||||
if (ShutdownState == HTNPlanState.PlanFinished && steering.Status == SteeringStatus.Moving)
|
||||
{
|
||||
return HTNOperatorStatus.Finished;
|
||||
}
|
||||
|
||||
return steering.Status switch
|
||||
{
|
||||
SteeringStatus.InRange => HTNOperatorStatus.Finished,
|
||||
SteeringStatus.NoPath => HTNOperatorStatus.Failed,
|
||||
SteeringStatus.Moving => HTNOperatorStatus.Continuing,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public void ConditionalShutdown(NPCBlackboard blackboard)
|
||||
{
|
||||
// Cleanup the blackboard and remove steering.
|
||||
if (blackboard.TryGetValue<CancellationTokenSource>(MovementCancelToken, out var cancelToken, _entManager))
|
||||
{
|
||||
@@ -171,20 +204,4 @@ public sealed class MoveToOperator : HTNOperator
|
||||
|
||||
_steering.Unregister(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
||||
}
|
||||
|
||||
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||
{
|
||||
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||
|
||||
if (!_entManager.TryGetComponent<NPCSteeringComponent>(owner, out var steering))
|
||||
return HTNOperatorStatus.Failed;
|
||||
|
||||
return steering.Status switch
|
||||
{
|
||||
SteeringStatus.InRange => HTNOperatorStatus.Finished,
|
||||
SteeringStatus.NoPath => HTNOperatorStatus.Failed,
|
||||
SteeringStatus.Moving => HTNOperatorStatus.Continuing,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators;
|
||||
|
||||
/// <summary>
|
||||
/// What it sounds like.
|
||||
/// </summary>
|
||||
public sealed class NoOperator : HTNOperator
|
||||
{
|
||||
|
||||
}
|
||||
@@ -16,8 +16,8 @@ public sealed class PickAccessibleOperator : HTNOperator
|
||||
[DataField("rangeKey", required: true)]
|
||||
public string RangeKey = string.Empty;
|
||||
|
||||
[DataField("targetKey", required: true)]
|
||||
public string TargetKey = string.Empty;
|
||||
[DataField("targetCoordinates")]
|
||||
public string TargetCoordinates = "TargetCoordinates";
|
||||
|
||||
/// <summary>
|
||||
/// Where the pathfinding result will be stored (if applicable). This gets removed after execution.
|
||||
@@ -58,7 +58,7 @@ public sealed class PickAccessibleOperator : HTNOperator
|
||||
|
||||
return (true, new Dictionary<string, object>()
|
||||
{
|
||||
{ TargetKey, target },
|
||||
{ TargetCoordinates, target },
|
||||
{ PathfindKey, path}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ public sealed class RotateToTargetOperator : HTNOperator
|
||||
_rotate = sysManager.GetEntitySystem<RotateToFaceSystem>();
|
||||
}
|
||||
|
||||
public override void Shutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
{
|
||||
base.Shutdown(blackboard, status);
|
||||
base.TaskShutdown(blackboard, status);
|
||||
blackboard.Remove<Angle>(TargetKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ public sealed class MedibotInjectOperator : HTNOperator
|
||||
_solution = sysManager.GetEntitySystem<SolutionContainerSystem>();
|
||||
}
|
||||
|
||||
public override void Shutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
{
|
||||
base.Shutdown(blackboard, status);
|
||||
base.TaskShutdown(blackboard, status);
|
||||
blackboard.Remove<EntityUid>(TargetKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ public sealed class UtilityOperator : HTNOperator
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
[DataField("key")] public string Key = "CombatTarget";
|
||||
[DataField("key")] public string Key = "Target";
|
||||
|
||||
/// <summary>
|
||||
/// The EntityCoordinates of the specified target.
|
||||
/// </summary>
|
||||
[DataField("keyCoordinates")]
|
||||
public string KeyCoordinates = "CombatTargetCoordinates";
|
||||
public string KeyCoordinates = "TargetCoordinates";
|
||||
|
||||
[DataField("proto", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<UtilityQueryPrototype>))]
|
||||
public string Prototype = string.Empty;
|
||||
|
||||
@@ -25,9 +25,9 @@ public sealed class WaitOperator : HTNOperator
|
||||
return timer <= 0f ? HTNOperatorStatus.Finished : HTNOperatorStatus.Continuing;
|
||||
}
|
||||
|
||||
public override void Shutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||
{
|
||||
base.Shutdown(blackboard, status);
|
||||
base.TaskShutdown(blackboard, status);
|
||||
|
||||
// The replacement plan may want this value so only dump it if we're successful.
|
||||
if (status != HTNOperatorStatus.BetterPlan)
|
||||
|
||||
Reference in New Issue
Block a user