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))