Aetheria/Assets/Scripts/ServerShared/FireControl.cs
Meta Vens 2b98aaa762 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

43 lines
2.2 KiB
C#

/* 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/. */
using CultMath;
using static CultMath.math;
// Cut 1 (docs/fire-control-cut.md): geometry only. Whether a weapon bears on a point is decided here and
// 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.
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));
}
}