Add simple miss chances for NPCs (#12978)
Doesn't consider juking potential but okay for now.
This commit is contained in:
@@ -11,6 +11,12 @@ public sealed class NPCMeleeCombatComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables] public EntityUid Weapon;
|
[ViewVariables] public EntityUid Weapon;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the target is moving what is the chance for this NPC to miss.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MissChance;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public EntityUid Target;
|
public EntityUid Target;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public sealed class MeleeOperator : HTNOperator
|
|||||||
{
|
{
|
||||||
base.Startup(blackboard);
|
base.Startup(blackboard);
|
||||||
var melee = _entManager.EnsureComponent<NPCMeleeCombatComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
var melee = _entManager.EnsureComponent<NPCMeleeCombatComponent>(blackboard.GetValue<EntityUid>(NPCBlackboard.Owner));
|
||||||
|
melee.MissChance = blackboard.GetValueOrDefault<float>(NPCBlackboard.MeleeMissChance, _entManager);
|
||||||
melee.Target = blackboard.GetValue<EntityUid>(TargetKey);
|
melee.Target = blackboard.GetValue<EntityUid>(TargetKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public sealed class NPCBlackboard : IEnumerable<KeyValuePair<string, object>>
|
|||||||
{"IdleRange", 7f},
|
{"IdleRange", 7f},
|
||||||
{"MaximumIdleTime", 7f},
|
{"MaximumIdleTime", 7f},
|
||||||
{MedibotInjectRange, 4f},
|
{MedibotInjectRange, 4f},
|
||||||
|
{MeleeMissChance, 0.3f},
|
||||||
{"MeleeRange", 1f},
|
{"MeleeRange", 1f},
|
||||||
{"MinimumIdleTime", 2f},
|
{"MinimumIdleTime", 2f},
|
||||||
{"MovementRange", 1.5f},
|
{"MovementRange", 1.5f},
|
||||||
@@ -190,6 +191,9 @@ public sealed class NPCBlackboard : IEnumerable<KeyValuePair<string, object>>
|
|||||||
public const string CanMove = "CanMove";
|
public const string CanMove = "CanMove";
|
||||||
public const string FollowTarget = "FollowTarget";
|
public const string FollowTarget = "FollowTarget";
|
||||||
public const string MedibotInjectRange = "MedibotInjectRange";
|
public const string MedibotInjectRange = "MedibotInjectRange";
|
||||||
|
|
||||||
|
public const string MeleeMissChance = "MeleeMissChance";
|
||||||
|
|
||||||
public const string Owner = "Owner";
|
public const string Owner = "Owner";
|
||||||
public const string OwnerCoordinates = "OwnerCoordinates";
|
public const string OwnerCoordinates = "OwnerCoordinates";
|
||||||
public const string MovementTarget = "MovementTarget";
|
public const string MovementTarget = "MovementTarget";
|
||||||
@@ -216,7 +220,6 @@ public sealed class NPCBlackboard : IEnumerable<KeyValuePair<string, object>>
|
|||||||
|
|
||||||
public const string RotateSpeed = "RotateSpeed";
|
public const string RotateSpeed = "RotateSpeed";
|
||||||
public const string VisionRadius = "VisionRadius";
|
public const string VisionRadius = "VisionRadius";
|
||||||
public const float MeleeRange = 1f;
|
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using Content.Shared.MobState;
|
|||||||
using Content.Shared.MobState.Components;
|
using Content.Shared.MobState.Components;
|
||||||
using Content.Shared.Weapons.Melee;
|
using Content.Shared.Weapons.Melee;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
namespace Content.Server.NPC.Systems;
|
namespace Content.Server.NPC.Systems;
|
||||||
|
|
||||||
@@ -42,6 +44,8 @@ public sealed partial class NPCCombatSystem
|
|||||||
{
|
{
|
||||||
var combatQuery = GetEntityQuery<CombatModeComponent>();
|
var combatQuery = GetEntityQuery<CombatModeComponent>();
|
||||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
|
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||||
|
var curTime = _timing.CurTime;
|
||||||
|
|
||||||
foreach (var (comp, _) in EntityQuery<NPCMeleeCombatComponent, ActiveNPCComponent>())
|
foreach (var (comp, _) in EntityQuery<NPCMeleeCombatComponent, ActiveNPCComponent>())
|
||||||
{
|
{
|
||||||
@@ -51,17 +55,14 @@ public sealed partial class NPCCombatSystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Attack(comp, xformQuery);
|
Attack(comp, curTime, physicsQuery, xformQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Attack(NPCMeleeCombatComponent component, EntityQuery<TransformComponent> xformQuery)
|
private void Attack(NPCMeleeCombatComponent component, TimeSpan curTime, EntityQuery<PhysicsComponent> physicsQuery, EntityQuery<TransformComponent> xformQuery)
|
||||||
{
|
{
|
||||||
component.Status = CombatStatus.Normal;
|
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<MeleeWeaponComponent>(component.Weapon, out var weapon))
|
if (!TryComp<MeleeWeaponComponent>(component.Weapon, out var weapon))
|
||||||
{
|
{
|
||||||
component.Status = CombatStatus.NoWeapon;
|
component.Status = CombatStatus.NoWeapon;
|
||||||
@@ -105,6 +106,19 @@ public sealed partial class NPCCombatSystem
|
|||||||
|
|
||||||
// Gets unregistered on component shutdown.
|
// Gets unregistered on component shutdown.
|
||||||
_steering.TryRegister(component.Owner, new EntityCoordinates(component.Target, Vector2.Zero), steering);
|
_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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using Content.Server.Interaction;
|
using Content.Server.Interaction;
|
||||||
using Content.Server.Weapons.Ranged.Systems;
|
using Content.Server.Weapons.Ranged.Systems;
|
||||||
using Content.Shared.CombatMode;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Weapons.Melee;
|
using Content.Shared.Weapons.Melee;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.NPC.Systems;
|
namespace Content.Server.NPC.Systems;
|
||||||
@@ -13,7 +12,9 @@ namespace Content.Server.NPC.Systems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class NPCCombatSystem : EntitySystem
|
public sealed partial class NPCCombatSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly GunSystem _gun = default!;
|
[Dependency] private readonly GunSystem _gun = default!;
|
||||||
[Dependency] private readonly InteractionSystem _interaction = default!;
|
[Dependency] private readonly InteractionSystem _interaction = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
|||||||
@@ -261,6 +261,11 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
return null;
|
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)
|
public void AttemptLightAttack(EntityUid user, MeleeWeaponComponent weapon, EntityUid target)
|
||||||
{
|
{
|
||||||
if (!TryComp<TransformComponent>(target, out var targetXform))
|
if (!TryComp<TransformComponent>(target, out var targetXform))
|
||||||
|
|||||||
Reference in New Issue
Block a user