From 4454e1feba006513294fe95bef99bbb37627591b Mon Sep 17 00:00:00 2001
From: Rain Caldwell <67359748+Just-a-Unity-Dev@users.noreply.github.com>
Date: Wed, 10 Jul 2024 18:38:37 +0800
Subject: [PATCH] Advanced targeting
---
.../Components/NPCRangedCombatComponent.cs | 6 +++
.../Operators/Combat/Ranged/GunOperator.cs | 1 +
Content.Server/NPC/NPCBlackboard.cs | 1 +
.../NPC/Systems/NPCCombatSystem.Ranged.cs | 45 ++++++++++++-------
.../Weapons/Ranged/Systems/GunSystem.cs | 2 -
.../Weapons/Guns/Naval/Ballistic/20mm.yml | 22 +++++++++
6 files changed, 60 insertions(+), 17 deletions(-)
diff --git a/Content.Server/NPC/Components/NPCRangedCombatComponent.cs b/Content.Server/NPC/Components/NPCRangedCombatComponent.cs
index 2e4fcf5298..7ae9b47dae 100644
--- a/Content.Server/NPC/Components/NPCRangedCombatComponent.cs
+++ b/Content.Server/NPC/Components/NPCRangedCombatComponent.cs
@@ -34,6 +34,12 @@ public sealed partial class NPCRangedCombatComponent : Component
[ViewVariables(VVAccess.ReadWrite)]
public float LOSAccumulator = 0f;
+ ///
+ /// Does this predict where the target is moving towards, and then fires there?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ public bool Advanced = false;
+
///
/// Is the target still considered in LOS since the last check.
///
diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs
index 53c5ed1952..177152d85a 100644
--- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs
+++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs
@@ -58,6 +58,7 @@ public override void Startup(NPCBlackboard blackboard)
base.Startup(blackboard);
var ranged = _entManager.EnsureComponent(blackboard.GetValue(NPCBlackboard.Owner));
ranged.Target = blackboard.GetValue(TargetKey);
+ ranged.Advanced = blackboard.GetValueOrDefault("AdvancedTargeting", _entManager);
if (blackboard.TryGetValue(NPCBlackboard.RotateSpeed, out var rotSpeed, _entManager))
{
diff --git a/Content.Server/NPC/NPCBlackboard.cs b/Content.Server/NPC/NPCBlackboard.cs
index 322ff0f85b..9919f2eb6a 100644
--- a/Content.Server/NPC/NPCBlackboard.cs
+++ b/Content.Server/NPC/NPCBlackboard.cs
@@ -32,6 +32,7 @@ public sealed partial class NPCBlackboard : IEnumerable _physicsQuery;
private EntityQuery _xformQuery;
- // TODO: Don't predict for hitscan
- private const float ShootSpeed = 20f;
-
///
/// Cooldown on raycasting to check LOS.
///
@@ -121,20 +119,42 @@ private void UpdateRanged(float frameTime)
comp.LOSAccumulator -= frameTime;
- var worldPos = _transform.GetWorldPosition(xform);
- var targetPos = _transform.GetWorldPosition(targetXform);
+ var (x, worldRot) = _transform.GetWorldPositionRotation(xform);
+ var v = gun.ProjectileSpeed; // bullet velocity
+ var (xt, targetRot) = _transform.GetWorldPositionRotation(targetXform);
+ var vt = targetBody.LinearVelocity; // target velocity
- // We'll work out the projected spot of the target and shoot there instead of where they are.
- var distance = (targetPos - worldPos).Length();
- var oldInLos = comp.TargetInLOS;
+ Vector2 targetSpot;
+ Angle goalRotation;
+ var dx = xt - x; // target displacement from gun
+ var distance = dx.Length(); // distance to target
+
+ if (comp.Advanced)
+ {
+ var phi = (-dx).ToWorldAngle() - vt.ToWorldAngle();
+ var theta = Math.Asin(vt.Length() / v * Math.Sin(phi.Theta));
+ goalRotation = dx.ToWorldAngle() + theta;
+ var psi = Math.PI - phi - theta;
+ var intercept_dist = (float)(distance * Math.Sin(theta)/Math.Sin(psi));
+ targetSpot = xt + vt.Normalized() * intercept_dist;
+ }
+ else
+ {
+ // We'll work out the projected spot of the target and shoot there instead of where they are.
+ targetSpot = xt + vt * distance / v;
+ goalRotation = (targetSpot - x).ToWorldAngle();
+ }
// TODO: Should be doing these raycasts in parallel
// Ideally we'd have 2 steps, 1. to go over the normal details for shooting and then 2. to handle beep / rotate / shoot
+ var oldInLos = comp.TargetInLOS;
+
if (comp.LOSAccumulator < 0f)
{
comp.LOSAccumulator += UnoccludedCooldown;
// For consistency with NPC steering.
- comp.TargetInLOS = _interaction.InRangeUnobstructed(uid, Transform(comp.Target).Coordinates, distance + 0.1f);
+ comp.TargetInLOS = _interaction.InRangeUnobstructed(comp.Owner, comp.Target, distance + 0.1f) &&
+ (!comp.Advanced | _interaction.InRangeUnobstructed(comp.Owner, new MapCoordinates(targetSpot, xform.MapID), distance + 0.1f));
}
if (!comp.TargetInLOS)
@@ -162,11 +182,6 @@ private void UpdateRanged(float frameTime)
continue;
}
- var mapVelocity = targetBody.LinearVelocity;
- var targetSpot = targetPos + mapVelocity * distance / ShootSpeed;
-
- // If we have a max rotation speed then do that.
- var goalRotation = (targetSpot - worldPos).ToWorldAngle();
var rotationSpeed = comp.RotationSpeed;
if (!_rotate.TryRotateTo(uid, goalRotation, frameTime, comp.AccuracyThreshold, rotationSpeed?.Theta ?? double.MaxValue, xform))
@@ -187,7 +202,7 @@ private void UpdateRanged(float frameTime)
EntityCoordinates targetCordinates;
- if (_mapManager.TryFindGridAt(xform.MapID, targetPos, out var gridUid, out var mapGrid))
+ if (_mapManager.TryFindGridAt(xform.MapID, xt, out var gridUid, out var mapGrid))
{
targetCordinates = new EntityCoordinates(gridUid, mapGrid.WorldToLocal(targetSpot));
}
diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
index 7f7c7ba855..df4c83f8c9 100644
--- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
+++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
@@ -304,8 +304,6 @@ private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVeloci
if (!HasComp(uid))
{
RemoveShootable(uid);
- // TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack.
- ThrowingSystem.TryThrow(uid, mapDirection, gun.ProjectileSpeedModified, user);
return;
}
diff --git a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/Ballistic/20mm.yml b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/Ballistic/20mm.yml
index a40545f847..9cf81cf129 100644
--- a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/Ballistic/20mm.yml
+++ b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/Ballistic/20mm.yml
@@ -30,3 +30,25 @@
- type: NpcFactionMember
factions:
- PointDefenseGun
+ - type: BallisticAmmoProvider
+ proto: Cartridge20mm
+ capacity: 1500
+ - type: Gun
+ fireRate: 20
+ selectedMode: FullAuto
+ availableModes:
+ - FullAuto
+ - type: HTN
+ rootTask:
+ task: TurretCompound
+ blackboard:
+ RotateSpeed: !type:Single
+ 15.705 # 3.141 * 5
+ SoundTargetInLOS: !type:SoundPathSpecifier
+ path: /Audio/Effects/double_beep.ogg
+ AdvancedTargeting: !type:Bool
+ true
+ RangedRange: !type:Single
+ 60.0
+ VisionRadius: !type:Single
+ 100.0