Delete HardpointTransforms; mount direction and arc become sim data
Entity.HardpointTransforms was written only by Unity (EntityInstance.cs),
so the simulation's notion of where a gun points was a readback from a
renderer that never runs headless -- Combat.cs threw KeyNotFoundException
on the first combat tick outside Unity. Deletes the field, its writer, and
the readback branch in Behavior.Direction, leaving the item-rotation path
(the same convention reaction thrusters use, R6) as the only source of
mount direction.
Adds FireControl (ServerShared), a static class owning whether a weapon
bears on a point: MountDirection (planar, from ItemRotation), ArcFor (the
per-hardpoint HardpointData.FiringArc override, falling back to the new
GameplaySettings.FiringArc default of 120), and InArc (the planar dot-cone
test, R7 -- target height never enters it, and an arc of 360+ passes
unconditionally so a turret hardpoint needs no new type, just a number).
Combat.cs and TurretController.cs now gate AI fire through InArc instead
of the old hardcoded 0.99 dot-product test against a live Unity transform.
Settings.cs drops AgentFiringMinDot, which was declared and read by
nothing.
docs/fire-control-cut.md, Cut 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 07:37:52 +02:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
2026-09-19 13:02:05 +02:00
|
|
|
using System.Linq;
|
Delete HardpointTransforms; mount direction and arc become sim data
Entity.HardpointTransforms was written only by Unity (EntityInstance.cs),
so the simulation's notion of where a gun points was a readback from a
renderer that never runs headless -- Combat.cs threw KeyNotFoundException
on the first combat tick outside Unity. Deletes the field, its writer, and
the readback branch in Behavior.Direction, leaving the item-rotation path
(the same convention reaction thrusters use, R6) as the only source of
mount direction.
Adds FireControl (ServerShared), a static class owning whether a weapon
bears on a point: MountDirection (planar, from ItemRotation), ArcFor (the
per-hardpoint HardpointData.FiringArc override, falling back to the new
GameplaySettings.FiringArc default of 120), and InArc (the planar dot-cone
test, R7 -- target height never enters it, and an arc of 360+ passes
unconditionally so a turret hardpoint needs no new type, just a number).
Combat.cs and TurretController.cs now gate AI fire through InArc instead
of the old hardcoded 0.99 dot-product test against a live Unity transform.
Settings.cs drops AgentFiringMinDot, which was declared and read by
nothing.
docs/fire-control-cut.md, Cut 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 07:37:52 +02:00
|
|
|
using CultMath;
|
|
|
|
|
using static CultMath.math;
|
|
|
|
|
|
2026-09-19 13:02:05 +02:00
|
|
|
// Cut 1 (docs/fire-control-cut.md): geometry -- whether a weapon bears on a point is decided here and
|
Delete HardpointTransforms; mount direction and arc become sim data
Entity.HardpointTransforms was written only by Unity (EntityInstance.cs),
so the simulation's notion of where a gun points was a readback from a
renderer that never runs headless -- Combat.cs threw KeyNotFoundException
on the first combat tick outside Unity. Deletes the field, its writer, and
the readback branch in Behavior.Direction, leaving the item-rotation path
(the same convention reaction thrusters use, R6) as the only source of
mount direction.
Adds FireControl (ServerShared), a static class owning whether a weapon
bears on a point: MountDirection (planar, from ItemRotation), ArcFor (the
per-hardpoint HardpointData.FiringArc override, falling back to the new
GameplaySettings.FiringArc default of 120), and InArc (the planar dot-cone
test, R7 -- target height never enters it, and an arc of 360+ passes
unconditionally so a turret hardpoint needs no new type, just a number).
Combat.cs and TurretController.cs now gate AI fire through InArc instead
of the old hardcoded 0.99 dot-product test against a live Unity transform.
Settings.cs drops AgentFiringMinDot, which was declared and read by
nothing.
docs/fire-control-cut.md, Cut 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 07:37:52 +02:00
|
|
|
// nowhere else -- no prefab, transform, ArticulationPoint or renderer may influence it (R6, R7). Mount
|
|
|
|
|
// direction and arc are both derived per read from catalog data and the item's authored rotation; neither
|
|
|
|
|
// is ever stored on the entity.
|
2026-09-19 13:02:05 +02:00
|
|
|
// Cut 2 adds the reveal rule (R5) and the resolved targeting-system stats an entity fires with, both
|
|
|
|
|
// likewise derived on every read and never cached.
|
Delete HardpointTransforms; mount direction and arc become sim data
Entity.HardpointTransforms was written only by Unity (EntityInstance.cs),
so the simulation's notion of where a gun points was a readback from a
renderer that never runs headless -- Combat.cs threw KeyNotFoundException
on the first combat tick outside Unity. Deletes the field, its writer, and
the readback branch in Behavior.Direction, leaving the item-rotation path
(the same convention reaction thrusters use, R6) as the only source of
mount direction.
Adds FireControl (ServerShared), a static class owning whether a weapon
bears on a point: MountDirection (planar, from ItemRotation), ArcFor (the
per-hardpoint HardpointData.FiringArc override, falling back to the new
GameplaySettings.FiringArc default of 120), and InArc (the planar dot-cone
test, R7 -- target height never enters it, and an arc of 360+ passes
unconditionally so a turret hardpoint needs no new type, just a number).
Combat.cs and TurretController.cs now gate AI fire through InArc instead
of the old hardcoded 0.99 dot-product test against a live Unity transform.
Settings.cs drops AgentFiringMinDot, which was declared and read by
nothing.
docs/fire-control-cut.md, Cut 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 07:37:52 +02:00
|
|
|
public static class FireControl
|
|
|
|
|
{
|
|
|
|
|
// The planar unit vector a hardpoint's weapon points along, derived solely from the item's authored
|
|
|
|
|
// rotation (R6, the same convention reaction thrusters already use) -- never from a live Unity
|
|
|
|
|
// transform. This is the item-rotation path Behavior.Direction now uses unconditionally, after the
|
|
|
|
|
// HardpointTransforms readback branch is deleted.
|
|
|
|
|
public static float3 MountDirection(EquippedItem item)
|
|
|
|
|
{
|
|
|
|
|
var itemDirection = item.Entity.Direction.Rotate(item.EquippableItem.Rotation);
|
|
|
|
|
return normalize(float3(itemDirection.x, 0, itemDirection.y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Full arc width in degrees: the per-hardpoint override when authored (nonzero), else
|
|
|
|
|
// GameplaySettings.FiringArc. Zero on the hardpoint means "use the default," not "no arc."
|
|
|
|
|
public static float ArcFor(EquippedItem item)
|
|
|
|
|
{
|
|
|
|
|
var hardpoint = item.Entity.Hardpoints[item.Position.x, item.Position.y];
|
|
|
|
|
var arc = hardpoint?.FiringArc ?? 0f;
|
|
|
|
|
return arc > 0 ? arc : item.ItemManager.GameplaySettings.FiringArc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Planar bearing test (R7: the simulation is 2D, the 3D is set dressing -- target height never enters
|
|
|
|
|
// this). An arc of 360 degrees or more passes unconditionally, which is how a turret hardpoint
|
|
|
|
|
// (authored FiringArc: 360, Q1) tracks all the way around without a second type.
|
|
|
|
|
public static bool InArc(EquippedItem weapon, float3 toTarget)
|
|
|
|
|
{
|
|
|
|
|
var arc = ArcFor(weapon);
|
|
|
|
|
if (arc >= 360f) return true;
|
|
|
|
|
var planarTarget = normalize(float3(toTarget.x, 0, toTarget.z));
|
|
|
|
|
return dot(MountDirection(weapon), planarTarget) >= cos(radians(arc / 2f));
|
|
|
|
|
}
|
2026-09-19 13:02:05 +02:00
|
|
|
|
|
|
|
|
// Cut 2, R5: whether `observer` has resolved `item` (which must belong to some entity in the zone) well
|
|
|
|
|
// enough to aim at it. Derived fresh from EntityInfoGathered on every call -- nothing caches a reveal
|
|
|
|
|
// result, so decay drops it the moment info falls back below tier, with no clearing loop anywhere.
|
|
|
|
|
//
|
|
|
|
|
// Ranking: the target's own non-hull equipment, hardpoint-mounted gear first (the big obvious things),
|
|
|
|
|
// then by size descending, ties broken by equipment order (OrderByDescending is a stable sort, so this
|
|
|
|
|
// needs no explicit tie-break key). Item i of N reveals once info crosses
|
|
|
|
|
// lerp(TargetArmorInfoThreshold, TargetGearInfoThreshold, i / max(1, N - 1)) -- the first item to reveal
|
|
|
|
|
// does so at the armor tier, the last at the gear tier.
|
|
|
|
|
public static bool IsRevealed(Entity observer, EquippedItem item)
|
|
|
|
|
{
|
|
|
|
|
var target = item.Entity;
|
|
|
|
|
var ranked = target.Equipment
|
|
|
|
|
.Where(e => e.Data.HardpointType != HardpointType.Hull)
|
|
|
|
|
.OrderByDescending(e => e.Data.HardpointType != HardpointType.Tool)
|
|
|
|
|
.ThenByDescending(e => e.Data.Shape.Coordinates.Length)
|
|
|
|
|
.ToList();
|
|
|
|
|
var index = ranked.IndexOf(item);
|
|
|
|
|
if (index < 0) return false; // the hull itself, or an item that is no longer on this target
|
|
|
|
|
|
|
|
|
|
var settings = observer.ItemManager.GameplaySettings;
|
|
|
|
|
var info = observer.EntityInfoGathered.TryGetValue(target, out var gathered) ? gathered : 0f;
|
|
|
|
|
var tier = lerp(settings.TargetArmorInfoThreshold, settings.TargetGearInfoThreshold,
|
|
|
|
|
(float) index / max(1, ranked.Count - 1));
|
|
|
|
|
return info >= tier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cut 2, Q4: the stats an entity actually fires with. A working targeting system (equipped, online --
|
|
|
|
|
// not destroyed, not shut down) supplies its own resolved stats; anything else falls back to the
|
|
|
|
|
// authored unaided figures, which make aiming without one a last resort rather than an alternative.
|
|
|
|
|
// Cut 3's roll is the only planned reader.
|
|
|
|
|
public static float Accuracy(Entity entity)
|
|
|
|
|
{
|
|
|
|
|
var system = entity.GetBehavior<TargetingSystem>();
|
|
|
|
|
return system != null && system.Item.Active.Value
|
|
|
|
|
? system.Accuracy
|
|
|
|
|
: entity.ItemManager.GameplaySettings.UnaidedAccuracy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float Resolution(Entity entity)
|
|
|
|
|
{
|
|
|
|
|
var system = entity.GetBehavior<TargetingSystem>();
|
|
|
|
|
return system != null && system.Item.Active.Value ? system.Resolution : 1f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float Precision(Entity entity)
|
|
|
|
|
{
|
|
|
|
|
var system = entity.GetBehavior<TargetingSystem>();
|
|
|
|
|
return system != null && system.Item.Active.Value ? system.Precision : 0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float Tracking(Entity entity)
|
|
|
|
|
{
|
|
|
|
|
var system = entity.GetBehavior<TargetingSystem>();
|
|
|
|
|
return system != null && system.Item.Active.Value ? system.Tracking : 0f;
|
|
|
|
|
}
|
Delete HardpointTransforms; mount direction and arc become sim data
Entity.HardpointTransforms was written only by Unity (EntityInstance.cs),
so the simulation's notion of where a gun points was a readback from a
renderer that never runs headless -- Combat.cs threw KeyNotFoundException
on the first combat tick outside Unity. Deletes the field, its writer, and
the readback branch in Behavior.Direction, leaving the item-rotation path
(the same convention reaction thrusters use, R6) as the only source of
mount direction.
Adds FireControl (ServerShared), a static class owning whether a weapon
bears on a point: MountDirection (planar, from ItemRotation), ArcFor (the
per-hardpoint HardpointData.FiringArc override, falling back to the new
GameplaySettings.FiringArc default of 120), and InArc (the planar dot-cone
test, R7 -- target height never enters it, and an arc of 360+ passes
unconditionally so a turret hardpoint needs no new type, just a number).
Combat.cs and TurretController.cs now gate AI fire through InArc instead
of the old hardcoded 0.99 dot-product test against a live Unity transform.
Settings.cs drops AgentFiringMinDot, which was declared and read by
nothing.
docs/fire-control-cut.md, Cut 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 07:37:52 +02:00
|
|
|
}
|