From 31491775e597fe9906df66d7285d5b63ba92daa2 Mon Sep 17 00:00:00 2001 From: Tornado Tech <54727692+Tornado-Technology@users.noreply.github.com> Date: Wed, 8 May 2024 19:18:03 +1000 Subject: [PATCH] Added new HTN operations and preconditions (#27486) * Added new HTN operations & preconditions * Ok I forgot about partial * Namespace pierce the skies * Some fixes, debug and new operators * Bruh git eat my files --- .../HTN/Preconditions/GunAmmoPrecondition.cs | 2 +- .../Preconditions/KeyNotExistsPrecondition.cs | 12 +++ .../Math/KeyBoolEqualsPrecondition.cs | 23 +++++ .../Math/KeyFloatEqualsPrecondition.cs | 18 ++++ .../Math/KeyFloatGreaterPrecondition.cs | 17 ++++ .../Math/KeyFloatLessPrecondition.cs | 17 ++++ .../Operators/Math/AddFloatOperator.cs | 33 +++++++ .../Operators/Math/SetBoolOperator.cs | 28 ++++++ .../Operators/{ => Math}/SetFloatOperator.cs | 18 ++-- .../Operators/Math/SetRandomFloatOperator.cs | 34 +++++++ .../Operators/PlaySoundOperator.cs | 28 ++++++ .../Operators/SayKeyOperator.cs | 36 ++++++++ .../Entities/Mobs/Debugging/debug_counter.yml | 53 +++++++++++ Resources/Prototypes/NPCs/debug.yml | 90 +++++++++++++++++++ 14 files changed, 401 insertions(+), 8 deletions(-) create mode 100644 Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs rename Content.Server/NPC/HTN/PrimitiveTasks/Operators/{ => Math}/SetFloatOperator.cs (52%) create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/PlaySoundOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs create mode 100644 Resources/Prototypes/Entities/Mobs/Debugging/debug_counter.yml create mode 100644 Resources/Prototypes/NPCs/debug.yml diff --git a/Content.Server/NPC/HTN/Preconditions/GunAmmoPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/GunAmmoPrecondition.cs index fe3b844ae3..58647d8874 100644 --- a/Content.Server/NPC/HTN/Preconditions/GunAmmoPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/GunAmmoPrecondition.cs @@ -35,7 +35,7 @@ public sealed partial class GunAmmoPrecondition : HTNPrecondition else percent = ammoEv.Count / (float) ammoEv.Capacity; - percent = Math.Clamp(percent, 0f, 1f); + percent = System.Math.Clamp(percent, 0f, 1f); if (MaxPercent < percent) return false; diff --git a/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs new file mode 100644 index 0000000000..c12663901c --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs @@ -0,0 +1,12 @@ +namespace Content.Server.NPC.HTN.Preconditions; + +public sealed partial class KeyNotExistsPrecondition : HTNPrecondition +{ + [DataField(required: true)] + public string Key = string.Empty; + + public override bool IsMet(NPCBlackboard blackboard) + { + return !blackboard.ContainsKey(Key); + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs new file mode 100644 index 0000000000..8c7920e8be --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs @@ -0,0 +1,23 @@ +namespace Content.Server.NPC.HTN.Preconditions.Math; + +/// +/// Checks for the presence of data in the blackboard and makes a comparison with the specified boolean +/// +public sealed partial class KeyBoolEqualsPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + [DataField(required: true)] + public bool Value; + + public override bool IsMet(NPCBlackboard blackboard) + { + if (!blackboard.TryGetValue(Key, out var value, _entManager)) + return false; + + return Value == value; + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs new file mode 100644 index 0000000000..802fdaf2b9 --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs @@ -0,0 +1,18 @@ +namespace Content.Server.NPC.HTN.Preconditions.Math; + +public sealed partial class KeyFloatEqualsPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + [DataField(required: true)] + public float Value; + + public override bool IsMet(NPCBlackboard blackboard) + { + return blackboard.TryGetValue(Key, out var value, _entManager) && + MathHelper.CloseTo(value, value); + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs new file mode 100644 index 0000000000..3a9ac36698 --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs @@ -0,0 +1,17 @@ +namespace Content.Server.NPC.HTN.Preconditions.Math; + +public sealed partial class KeyFloatGreaterPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + [DataField(required: true)] + public float Value; + + public override bool IsMet(NPCBlackboard blackboard) + { + return blackboard.TryGetValue(Key, out var value, _entManager) && value > Value; + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs new file mode 100644 index 0000000000..5cd51d7a7c --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs @@ -0,0 +1,17 @@ +namespace Content.Server.NPC.HTN.Preconditions.Math; + +public sealed partial class KeyFloatLessPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + [DataField(required: true)] + public float Value; + + public override bool IsMet(NPCBlackboard blackboard) + { + return blackboard.TryGetValue(Key, out var value, _entManager) && value < Value; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs new file mode 100644 index 0000000000..00404517c9 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs @@ -0,0 +1,33 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; + +/// +/// Gets the key, and adds the value to that float +/// +public sealed partial class AddFloatOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string TargetKey = string.Empty; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float Amount; + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + if (!blackboard.TryGetValue(TargetKey, out var value, _entManager)) + return (false, null); + + return ( + true, + new Dictionary + { + { TargetKey, value + Amount } + } + ); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs new file mode 100644 index 0000000000..a40b96798d --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs @@ -0,0 +1,28 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; + +/// +/// Just sets a blackboard key to a bool +/// +public sealed partial class SetBoolOperator : HTNOperator +{ + [DataField(required: true)] + public string TargetKey = string.Empty; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool Value; + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + return ( + true, + new Dictionary + { + { TargetKey, Value } + } + ); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SetFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs similarity index 52% rename from Content.Server/NPC/HTN/PrimitiveTasks/Operators/SetFloatOperator.cs rename to Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs index 7a460592cb..76842b431f 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SetFloatOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs @@ -1,24 +1,28 @@ using System.Threading; using System.Threading.Tasks; -namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; /// /// Just sets a blackboard key to a float /// public sealed partial class SetFloatOperator : HTNOperator { - [DataField("targetKey", required: true)] public string TargetKey = string.Empty; + [DataField(required: true)] + public string TargetKey = string.Empty; - [ViewVariables(VVAccess.ReadWrite), DataField("amount")] + [DataField, ViewVariables(VVAccess.ReadWrite)] public float Amount; public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, CancellationToken cancelToken) { - return (true, new Dictionary() - { - {TargetKey, Amount}, - }); + return ( + true, + new Dictionary + { + { TargetKey, Amount } + } + ); } } diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs new file mode 100644 index 0000000000..999756f1f7 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Threading.Tasks; +using Robust.Shared.Random; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; + +/// +/// Sets a random float from MinAmount to MaxAmount to blackboard +/// +public sealed partial class SetRandomFloatOperator : HTNOperator +{ + [Dependency] private readonly IRobustRandom _random = default!; + + [DataField(required: true)] + public string TargetKey = string.Empty; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float MaxAmount = 1f; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float MinAmount = 0f; + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + return ( + true, + new Dictionary + { + { TargetKey, _random.NextFloat(MinAmount, MaxAmount) } + } + ); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PlaySoundOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PlaySoundOperator.cs new file mode 100644 index 0000000000..57cc2e91e4 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PlaySoundOperator.cs @@ -0,0 +1,28 @@ +using Robust.Server.Audio; +using Robust.Shared.Audio; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; + +public sealed partial class PlaySoundOperator : HTNOperator +{ + private AudioSystem _audio = default!; + + [DataField(required: true)] + public SoundSpecifier? Sound; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + + _audio = IoCManager.Resolve().GetEntitySystem(); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var uid = blackboard.GetValue(NPCBlackboard.Owner); + + _audio.PlayPvs(Sound, uid); + + return base.Update(blackboard, frameTime); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs new file mode 100644 index 0000000000..d1c7d61915 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs @@ -0,0 +1,36 @@ +using Content.Server.Chat.Systems; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; + +public sealed partial class SayKeyOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + private ChatSystem _chat = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + /// + /// Whether to hide message from chat window and logs. + /// + [DataField] + public bool Hidden; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _chat = IoCManager.Resolve().GetEntitySystem(); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + if (!blackboard.TryGetValue(Key, out var value, _entManager)) + return HTNOperatorStatus.Failed; + + var speaker = blackboard.GetValue(NPCBlackboard.Owner); + _chat.TrySendInGameICMessage(speaker, value.ToString() ?? "Oh no...", InGameICChatType.Speak, hideChat: Hidden, hideLog: Hidden); + + return base.Update(blackboard, frameTime); + } +} diff --git a/Resources/Prototypes/Entities/Mobs/Debugging/debug_counter.yml b/Resources/Prototypes/Entities/Mobs/Debugging/debug_counter.yml new file mode 100644 index 0000000000..05707c7151 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Debugging/debug_counter.yml @@ -0,0 +1,53 @@ +- type: entity + parent: MobHuman + id: MobDebugCounter + name: debug counter + description: He can count + suffix: AI, DEBUG + components: + - type: HTN + rootTask: + task: DebugCounterCompound + blackboard: + MinimumIdleTime: !type:Single + 0.5 + MaximumIdleTime: !type:Single + 0.5 + Count: !type:Single + 0 + +- type: entity + parent: MobHuman + id: MobDebugRandomCounter + name: debug random counter + description: He can randomize + suffix: AI, DEBUG + components: + - type: HTN + rootTask: + task: DebugRandomCounterCompound + blackboard: + MinimumIdleTime: !type:Single + 1 + MaximumIdleTime: !type:Single + 1 + Count: !type:Single + 0 + +- type: entity + parent: MobHuman + id: MobDebugRandomLess + name: debug random less + description: He can lessing + suffix: AI, DEBUG + components: + - type: HTN + rootTask: + task: DebugRandomLessCompound + blackboard: + MinimumIdleTime: !type:Single + 1 + MaximumIdleTime: !type:Single + 1 + Count: !type:Single + 0 diff --git a/Resources/Prototypes/NPCs/debug.yml b/Resources/Prototypes/NPCs/debug.yml new file mode 100644 index 0000000000..c7929be103 --- /dev/null +++ b/Resources/Prototypes/NPCs/debug.yml @@ -0,0 +1,90 @@ +- type: htnCompound + id: DebugCounterCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:AddFloatOperator + targetKey: Count + amount: 1 + + - !type:HTNPrimitiveTask + operator: !type:SayKeyOperator + key: Count + + - !type:HTNPrimitiveTask + operator: !type:RandomOperator + targetKey: IdleTime + minKey: MinimumIdleTime + maxKey: MaximumIdleTime + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime + +- type: htnCompound + id: DebugRandomCounterCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:SetRandomFloatOperator + targetKey: Count + minAmount: 0 + maxAmount: 100 + + - !type:HTNPrimitiveTask + operator: !type:SayKeyOperator + key: Count + + - !type:HTNPrimitiveTask + operator: !type:RandomOperator + targetKey: IdleTime + minKey: MinimumIdleTime + maxKey: MaximumIdleTime + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime + +- type: htnCompound + id: DebugRandomLessCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:SetRandomFloatOperator + targetKey: Count + minAmount: 0 + maxAmount: 100 + + - !type:HTNPrimitiveTask + operator: !type:SayKeyOperator + key: Count + preconditions: + - !type:KeyFloatLessPrecondition + key: Count + value: 50 + + - !type:HTNPrimitiveTask + operator: !type:RandomOperator + targetKey: IdleTime + minKey: MinimumIdleTime + maxKey: MaximumIdleTime + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime + + - tasks: + - !type:HTNPrimitiveTask + operator: !type:SpeakOperator + speech: "fuck!" + + \ No newline at end of file -- 2.52.0