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>
This commit is contained in:
parent
678c1ea99a
commit
2b98aaa762
9 changed files with 64 additions and 26 deletions
|
|
@ -511,11 +511,6 @@ public class EntityInstance : MonoBehaviour
|
|||
x.Value.material.SetFloat("_Emission", Entity.ItemManager.GameplaySettings.TemperatureEmissionCurve.Evaluate(x.Key.RadiatorTemperature));
|
||||
}
|
||||
|
||||
foreach (var x in Barrels)
|
||||
{
|
||||
Entity.HardpointTransforms[x.Key] = (x.Value[0].position.ToCultMath(), x.Value[0].forward.ToCultMath());
|
||||
}
|
||||
|
||||
LookAtPoint.position = transform.position + Entity.LookDirection.ToUnity() *
|
||||
(Entity.Target.Value != null ? max(Entity.TargetRange,Entity.ItemManager.GameplaySettings.ConvergenceMinimumDistance) : 10000);
|
||||
LocalSpace.localPosition = transform.position = Entity.Position.ToUnity();
|
||||
|
|
|
|||
|
|
@ -97,9 +97,7 @@ public class CombatState : BaseState
|
|||
toTarget = normalize(predictedPosition - _agent.Ship.Position);
|
||||
}
|
||||
|
||||
var shouldFire = dot(
|
||||
_agent.Ship.HardpointTransforms[_agent.Ship.Hardpoints[testWeapon.Item.Position.x, testWeapon.Item.Position.y]].direction,
|
||||
toTarget) > .99f;
|
||||
var shouldFire = FireControl.InArc(testWeapon.Item, toTarget);
|
||||
foreach (var weapon in _agent.Ship.WeaponGroups[selectedGroup].weapons)
|
||||
{
|
||||
if (shouldFire)
|
||||
|
|
|
|||
|
|
@ -27,16 +27,8 @@ public abstract class Behavior
|
|||
{
|
||||
if(Item != null)
|
||||
{
|
||||
var hardpoint = Entity.Hardpoints[Item.Position.x, Item.Position.y];
|
||||
if (hardpoint != null && Entity.HardpointTransforms.ContainsKey(hardpoint))
|
||||
{
|
||||
return normalize(Entity.HardpointTransforms[hardpoint].direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
var itemDirection = Entity.Direction.Rotate(Item.EquippableItem.Rotation);
|
||||
return float3(itemDirection.x, 0, itemDirection.y);
|
||||
}
|
||||
var itemDirection = Entity.Direction.Rotate(Item.EquippableItem.Rotation);
|
||||
return float3(itemDirection.x, 0, itemDirection.y);
|
||||
}
|
||||
|
||||
return float3(Entity.Direction.x, 0, Entity.Direction.y);
|
||||
|
|
|
|||
|
|
@ -78,9 +78,7 @@ public class TurretController : Behavior, IInitializableBehavior
|
|||
foreach (var x in _weapons)
|
||||
{
|
||||
var data = x.Data as WeaponData;
|
||||
var fire = dot(
|
||||
x.Direction,
|
||||
Entity.LookDirection) > .99f;
|
||||
var fire = FireControl.InArc(x.Item, Entity.LookDirection);
|
||||
if (x.Evaluate(data.Range) > dist && fire)
|
||||
{
|
||||
x.Activate();
|
||||
|
|
|
|||
|
|
@ -54,10 +54,8 @@ public abstract class Entity
|
|||
|
||||
public readonly Dictionary<string, float> Messages = new Dictionary<string, float>();
|
||||
public readonly Dictionary<object, float> VisibilitySources = new Dictionary<object, float>();
|
||||
public readonly ReactiveDictionary<Entity, float> EntityInfoGathered = new ReactiveDictionary<Entity, float>();
|
||||
public readonly Dictionary<HardpointData, (float3 position, float3 direction)> HardpointTransforms =
|
||||
new Dictionary<HardpointData, (float3 position, float3 direction)>();
|
||||
|
||||
public readonly ReactiveDictionary<Entity, float> EntityInfoGathered = new ReactiveDictionary<Entity, float>();
|
||||
|
||||
public (List<Weapon> weapons, List<EquippedItem> items)[] WeaponGroups;
|
||||
|
||||
public List<IPopulationAssignment> PopulationAssignments = new List<IPopulationAssignment>();
|
||||
|
|
|
|||
43
Assets/Scripts/ServerShared/FireControl.cs
Normal file
43
Assets/Scripts/ServerShared/FireControl.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* 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));
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ServerShared/FireControl.cs.meta
Normal file
11
Assets/Scripts/ServerShared/FireControl.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e84f481f2a2e9fdc572e40e239eacc92
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -543,6 +543,9 @@ public class HardpointData
|
|||
[Inspectable, JsonProperty("transform"), Key(3)] public string Transform;
|
||||
[Inspectable, JsonProperty("rotation"), Key(4)] public ItemRotation Rotation;
|
||||
[Inspectable, JsonProperty("armor"), Key(5)] public float Armor;
|
||||
// Full arc width in degrees a weapon on this hardpoint may bear across; 0 means "use
|
||||
// GameplaySettings.FiringArc" (docs/fire-control-cut.md R6). Turrets author 360 (Q1).
|
||||
[Inspectable, JsonProperty("firingArc"), Key(6)] public float FiringArc;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -212,10 +212,10 @@ public class GameplaySettings
|
|||
public float TargetArmorInfoThreshold;
|
||||
public float TargetGearInfoThreshold;
|
||||
public float ConvergenceMinimumDistance;
|
||||
public float FiringArc = 120;
|
||||
public float AgentRangeExponent = .25f;
|
||||
public float AgentForwardLerp = .5f;
|
||||
public float AgentMaxForwardDistance = 50;
|
||||
public float AgentFiringMinDot = .99f;
|
||||
}
|
||||
|
||||
[Serializable, MessagePackObject(keyAsPropertyName: true), JsonObject]
|
||||
|
|
|
|||
Loading…
Reference in a new issue