From 83fede79eb51dffd84bc4b05f102e0402306b914 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Mon, 12 Dec 2022 00:37:09 +1100 Subject: [PATCH] Add simple miss chances for NPCs (#12978) Doesn't consider juking potential but okay for now. --- .../NPC/Components/NPCMeleeCombatComponent.cs | 6 +++++ .../Operators/Melee/MeleeOperator.cs | 1 + Content.Server/NPC/NPCBlackboard.cs | 5 +++- .../NPC/Systems/NPCCombatSystem.Melee.cs | 26 ++++++++++++++----- Content.Server/NPC/Systems/NPCCombatSystem.cs | 5 ++-- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 5 ++++ 6 files changed, 39 insertions(+), 9 deletions(-) diff --git a/Content.Server/NPC/Components/NPCMeleeCombatComponent.cs b/Content.Server/NPC/Components/NPCMeleeCombatComponent.cs index 3e46058250..cf5131f7a8 100644 --- a/Content.Server/NPC/Components/NPCMeleeCombatComponent.cs +++ b/Content.Server/NPC/Components/NPCMeleeCombatComponent.cs @@ -11,6 +11,12 @@ public sealed class NPCMeleeCombatComponent : Component /// [ViewVariables] public EntityUid Weapon; + /// + /// If the target is moving what is the chance for this NPC to miss. + /// + [ViewVariables(VVAccess.ReadWrite)] + public float MissChance; + [ViewVariables] public EntityUid Target; diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Melee/MeleeOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Melee/MeleeOperator.cs index 00ca4dc611..634672ec5d 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Melee/MeleeOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Melee/MeleeOperator.cs @@ -31,6 +31,7 @@ public sealed class MeleeOperator : HTNOperator { base.Startup(blackboard); var melee = _entManager.EnsureComponent(blackboard.GetValue(NPCBlackboard.Owner)); + melee.MissChance = blackboard.GetValueOrDefault(NPCBlackboard.MeleeMissChance, _entManager); melee.Target = blackboard.GetValue(TargetKey); } diff --git a/Content.Server/NPC/NPCBlackboard.cs b/Content.Server/NPC/NPCBlackboard.cs index 6d93b6c814..f0a87a2098 100644 --- a/Content.Server/NPC/NPCBlackboard.cs +++ b/Content.Server/NPC/NPCBlackboard.cs @@ -20,6 +20,7 @@ public sealed class NPCBlackboard : IEnumerable> {"IdleRange", 7f}, {"MaximumIdleTime", 7f}, {MedibotInjectRange, 4f}, + {MeleeMissChance, 0.3f}, {"MeleeRange", 1f}, {"MinimumIdleTime", 2f}, {"MovementRange", 1.5f}, @@ -190,6 +191,9 @@ public sealed class NPCBlackboard : IEnumerable> public const string CanMove = "CanMove"; public const string FollowTarget = "FollowTarget"; public const string MedibotInjectRange = "MedibotInjectRange"; + + public const string MeleeMissChance = "MeleeMissChance"; + public const string Owner = "Owner"; public const string OwnerCoordinates = "OwnerCoordinates"; public const string MovementTarget = "MovementTarget"; @@ -216,7 +220,6 @@ public sealed class NPCBlackboard : IEnumerable> public const string RotateSpeed = "RotateSpeed"; public const string VisionRadius = "VisionRadius"; - public const float MeleeRange = 1f; public IEnumerator> GetEnumerator() { diff --git a/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs b/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs index 7cc4659d8c..befa0e1e98 100644 --- a/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs +++ b/Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs @@ -4,6 +4,8 @@ using Content.Shared.MobState; using Content.Shared.MobState.Components; using Content.Shared.Weapons.Melee; using Robust.Shared.Map; +using Robust.Shared.Physics.Components; +using Robust.Shared.Random; namespace Content.Server.NPC.Systems; @@ -42,6 +44,8 @@ public sealed partial class NPCCombatSystem { var combatQuery = GetEntityQuery(); var xformQuery = GetEntityQuery(); + var physicsQuery = GetEntityQuery(); + var curTime = _timing.CurTime; foreach (var (comp, _) in EntityQuery()) { @@ -51,17 +55,14 @@ public sealed partial class NPCCombatSystem continue; } - Attack(comp, xformQuery); + Attack(comp, curTime, physicsQuery, xformQuery); } } - private void Attack(NPCMeleeCombatComponent component, EntityQuery xformQuery) + private void Attack(NPCMeleeCombatComponent component, TimeSpan curTime, EntityQuery physicsQuery, EntityQuery xformQuery) { component.Status = CombatStatus.Normal; - // TODO: - // Also need some blackboard data for stuff like juke frequency, assigning target slots (to surround targets), etc. - // miss % if (!TryComp(component.Weapon, out var weapon)) { component.Status = CombatStatus.NoWeapon; @@ -105,6 +106,19 @@ public sealed partial class NPCCombatSystem // Gets unregistered on component shutdown. _steering.TryRegister(component.Owner, new EntityCoordinates(component.Target, Vector2.Zero), steering); - _melee.AttemptLightAttack(component.Owner, weapon, component.Target); + + if (weapon.NextAttack > curTime) + return; + + if (_random.Prob(component.MissChance) && + physicsQuery.TryGetComponent(component.Target, out var targetPhysics) && + targetPhysics.LinearVelocity.LengthSquared != 0f) + { + _melee.AttemptLightAttackMiss(component.Owner, weapon, targetXform.Coordinates.Offset(_random.NextVector2(0.5f))); + } + else + { + _melee.AttemptLightAttack(component.Owner, weapon, component.Target); + } } } diff --git a/Content.Server/NPC/Systems/NPCCombatSystem.cs b/Content.Server/NPC/Systems/NPCCombatSystem.cs index c8f4b1ea27..06d0172b39 100644 --- a/Content.Server/NPC/Systems/NPCCombatSystem.cs +++ b/Content.Server/NPC/Systems/NPCCombatSystem.cs @@ -1,9 +1,8 @@ using Content.Server.Interaction; using Content.Server.Weapons.Ranged.Systems; -using Content.Shared.CombatMode; -using Content.Shared.Interaction; using Content.Shared.Weapons.Melee; using Robust.Shared.Map; +using Robust.Shared.Random; using Robust.Shared.Timing; namespace Content.Server.NPC.Systems; @@ -13,7 +12,9 @@ namespace Content.Server.NPC.Systems; /// public sealed partial class NPCCombatSystem : EntitySystem { + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly GunSystem _gun = default!; [Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 1e01b2a141..2b30c0b761 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -261,6 +261,11 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem return null; } + public void AttemptLightAttackMiss(EntityUid user, MeleeWeaponComponent weapon, EntityCoordinates coordinates) + { + AttemptAttack(user, weapon, new LightAttackEvent(null, weapon.Owner, coordinates), null); + } + public void AttemptLightAttack(EntityUid user, MeleeWeaponComponent weapon, EntityUid target) { if (!TryComp(target, out var targetXform))