2026-08-10 14:57:50 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using UnityEngine;
|
2026-09-14 17:39:12 +02:00
|
|
|
|
using CultMath;
|
2026-09-19 14:10:59 +02:00
|
|
|
|
using CultMath.UnityBridge;
|
2026-09-14 17:39:12 +02:00
|
|
|
|
using static CultMath.math;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
using Random = UnityEngine.Random;
|
|
|
|
|
|
using static Noise1D;
|
2026-09-14 17:39:12 +02:00
|
|
|
|
using float3 = CultMath.float3;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
Fire control Cut 3: hit authority moves to FireControl
Deletes the Unity physics hit path (HullCollider.Hit/Splash, and the
raycast/shield/SendHit blocks in Projectile, GuidedProjectile, Laser and
Lightning) and replaces it with a single owner in the same cut: FireControl
now decides whether a shot hits, Zone owns the pending-shot lifecycle, and
Entity.DamageSchematic/ApplyHit (moved verbatim from EntityInstance) apply the
result. Presentation subscribes to Zone.ShotCommitted/ShotResolved and applies
nothing.
FireControl.HitProbability is the pure targeting solution (Accuracy * pSensor
* pSpread, gated by detection/range/lock/arc). FireControl.Fire freezes the
weapon's payload and the shooter's resolved targeting stats at the instant the
trigger is pulled (Q6), computes the predicted intercept through the one
shared PredictedIntercept function (CombatState now calls the same function
instead of duplicating first_order_intercept), and queues a PendingShot.
FireControl.Step commits each shot's outcome at ArrivalTime - CommitHorizon --
deviation from the predicted intercept, measured once, live, at that instant
-- and applies it at ArrivalTime. A shot whose flight is shorter than the
horizon commits and resolves in the same tick it fires. Rolls draw from the
shooter's own ItemManager.Random (Q5, seeded and reproducible); an out-of-arc
or otherwise gated shot draws nothing.
Player fire is arc-gated through the same predicate AI and turret fire use
(Weapon.ArcAllowsFire, checked in InstantWeapon.Trigger -- the one point every
shooter's trigger passes through), closing Q2.
Adds GameplaySettings.CommitHorizon, SchematicCellSize and
AgentMinHitProbability (first guesses; the headless suite is the tuning
harness, operator play smoke is the arbiter).
FORK reported, not worked around silently: HullCollider.SendHit/SendSplash
cannot be deleted yet without breaking ConstantLaser.cs, ConstantLightning.cs,
ConstantParticleWeapon.cs, HitscanEffect.cs and Mine.cs, which still call them
from their own live Physics queries -- that's Cut 4's migration. They fire
into the void today: nothing subscribes to Hit/Splash any more for the four
weapon kinds this cut owns.
tests/Aetheria.Shared.Tests/FireAuthorityTests.cs pins the roll's seeding, the
gate-consumes-no-draw rule, resolve-on-arrival, the commit horizon and its
evasion window, short-flight degradation, the frozen-payload snapshot, aimed
hits, the item-then-hull damage order, the planar penetration march and the
shield-absorbs branch. FireControlTests.cs and InputCapacitorTests.cs are
updated for the new AI gate and OnFire(int) signature.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 14:07:07 +02:00
|
|
|
|
// Cut 3 (docs/fire-control-cut.md): the raycast, shield branch and SendHit are deleted -- FireControl already
|
|
|
|
|
|
// decided this shot's fate before this object was ever spawned (R8). The homing/guidance flight (the whole
|
|
|
|
|
|
// point of this presentation) is untouched; children spawned on split carry no damage of their own to apply
|
|
|
|
|
|
// either, since they never did anything but inherit their parent's now-deleted fields.
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public class GuidedProjectile : MonoBehaviour
|
|
|
|
|
|
{
|
|
|
|
|
|
public Prototype HitEffect;
|
|
|
|
|
|
public Prototype ChildProjectile;
|
|
|
|
|
|
public int Children;
|
|
|
|
|
|
public float SplitTime;
|
|
|
|
|
|
public float SplitSeparationForwardness;
|
|
|
|
|
|
public float SplitSeparationVelocity;
|
|
|
|
|
|
public ParticleSystem Particles;
|
|
|
|
|
|
public float FadeOutTime;
|
|
|
|
|
|
public AnimationCurve ThrustCurve;
|
|
|
|
|
|
public AnimationCurve GuidanceCurve;
|
|
|
|
|
|
public AnimationCurve LiftCurve;
|
|
|
|
|
|
public float Frequency;
|
|
|
|
|
|
public float Thrust;
|
|
|
|
|
|
public float TopSpeed;
|
|
|
|
|
|
public Transform Source;
|
|
|
|
|
|
public Func<Vector3> TargetPosition;
|
Fire control Cut 3: hit authority moves to FireControl
Deletes the Unity physics hit path (HullCollider.Hit/Splash, and the
raycast/shield/SendHit blocks in Projectile, GuidedProjectile, Laser and
Lightning) and replaces it with a single owner in the same cut: FireControl
now decides whether a shot hits, Zone owns the pending-shot lifecycle, and
Entity.DamageSchematic/ApplyHit (moved verbatim from EntityInstance) apply the
result. Presentation subscribes to Zone.ShotCommitted/ShotResolved and applies
nothing.
FireControl.HitProbability is the pure targeting solution (Accuracy * pSensor
* pSpread, gated by detection/range/lock/arc). FireControl.Fire freezes the
weapon's payload and the shooter's resolved targeting stats at the instant the
trigger is pulled (Q6), computes the predicted intercept through the one
shared PredictedIntercept function (CombatState now calls the same function
instead of duplicating first_order_intercept), and queues a PendingShot.
FireControl.Step commits each shot's outcome at ArrivalTime - CommitHorizon --
deviation from the predicted intercept, measured once, live, at that instant
-- and applies it at ArrivalTime. A shot whose flight is shorter than the
horizon commits and resolves in the same tick it fires. Rolls draw from the
shooter's own ItemManager.Random (Q5, seeded and reproducible); an out-of-arc
or otherwise gated shot draws nothing.
Player fire is arc-gated through the same predicate AI and turret fire use
(Weapon.ArcAllowsFire, checked in InstantWeapon.Trigger -- the one point every
shooter's trigger passes through), closing Q2.
Adds GameplaySettings.CommitHorizon, SchematicCellSize and
AgentMinHitProbability (first guesses; the headless suite is the tuning
harness, operator play smoke is the arbiter).
FORK reported, not worked around silently: HullCollider.SendHit/SendSplash
cannot be deleted yet without breaking ConstantLaser.cs, ConstantLightning.cs,
ConstantParticleWeapon.cs, HitscanEffect.cs and Mine.cs, which still call them
from their own live Physics queries -- that's Cut 4's migration. They fire
into the void today: nothing subscribes to Hit/Splash any more for the four
weapon kinds this cut owns.
tests/Aetheria.Shared.Tests/FireAuthorityTests.cs pins the roll's seeding, the
gate-consumes-no-draw rule, resolve-on-arrival, the commit horizon and its
evasion window, short-flight degradation, the frozen-payload snapshot, aimed
hits, the item-then-hull damage order, the planar penetration march and the
shield-absorbs branch. FireControlTests.cs and InputCapacitorTests.cs are
updated for the new AI gate and OnFire(int) signature.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 14:07:07 +02:00
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
private float _phase;
|
|
|
|
|
|
private float _prevDist;
|
|
|
|
|
|
private bool _active;
|
|
|
|
|
|
private bool _alive;
|
2026-09-29 21:16:52 +02:00
|
|
|
|
private float _spawnTime;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
private Vector3 _targetVelocity;
|
|
|
|
|
|
private Vector3 _previousTargetPosition;
|
Fire control Cut 3: hit authority moves to FireControl
Deletes the Unity physics hit path (HullCollider.Hit/Splash, and the
raycast/shield/SendHit blocks in Projectile, GuidedProjectile, Laser and
Lightning) and replaces it with a single owner in the same cut: FireControl
now decides whether a shot hits, Zone owns the pending-shot lifecycle, and
Entity.DamageSchematic/ApplyHit (moved verbatim from EntityInstance) apply the
result. Presentation subscribes to Zone.ShotCommitted/ShotResolved and applies
nothing.
FireControl.HitProbability is the pure targeting solution (Accuracy * pSensor
* pSpread, gated by detection/range/lock/arc). FireControl.Fire freezes the
weapon's payload and the shooter's resolved targeting stats at the instant the
trigger is pulled (Q6), computes the predicted intercept through the one
shared PredictedIntercept function (CombatState now calls the same function
instead of duplicating first_order_intercept), and queues a PendingShot.
FireControl.Step commits each shot's outcome at ArrivalTime - CommitHorizon --
deviation from the predicted intercept, measured once, live, at that instant
-- and applies it at ArrivalTime. A shot whose flight is shorter than the
horizon commits and resolves in the same tick it fires. Rolls draw from the
shooter's own ItemManager.Random (Q5, seeded and reproducible); an out-of-arc
or otherwise gated shot draws nothing.
Player fire is arc-gated through the same predicate AI and turret fire use
(Weapon.ArcAllowsFire, checked in InstantWeapon.Trigger -- the one point every
shooter's trigger passes through), closing Q2.
Adds GameplaySettings.CommitHorizon, SchematicCellSize and
AgentMinHitProbability (first guesses; the headless suite is the tuning
harness, operator play smoke is the arbiter).
FORK reported, not worked around silently: HullCollider.SendHit/SendSplash
cannot be deleted yet without breaking ConstantLaser.cs, ConstantLightning.cs,
ConstantParticleWeapon.cs, HitscanEffect.cs and Mine.cs, which still call them
from their own live Physics queries -- that's Cut 4's migration. They fire
into the void today: nothing subscribes to Hit/Splash any more for the four
weapon kinds this cut owns.
tests/Aetheria.Shared.Tests/FireAuthorityTests.cs pins the roll's seeding, the
gate-consumes-no-draw rule, resolve-on-arrival, the commit horizon and its
evasion window, short-flight degradation, the frozen-payload snapshot, aimed
hits, the item-then-hull damage order, the planar penetration march and the
shield-absorbs branch. FireControlTests.cs and InputCapacitorTests.cs are
updated for the new AI gate and OnFire(int) signature.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 14:07:07 +02:00
|
|
|
|
|
|
|
|
|
|
// Cut 3: FireControl.Fire's ShotId -- this missile's handle onto Zone.ShotCommitted/ShotResolved.
|
|
|
|
|
|
public int ShotId { get; set; }
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public Transform Target { get; set; }
|
|
|
|
|
|
public float3 StartPosition { get; set; }
|
|
|
|
|
|
public float Range { get; set; }
|
|
|
|
|
|
public Vector3 Velocity { get; set; }
|
|
|
|
|
|
public Entity SourceEntity { get; set; }
|
|
|
|
|
|
|
2026-09-29 21:16:52 +02:00
|
|
|
|
// How long after spawn the round keeps its binding to the simulation's verdict: the shot's own flight time (the
|
|
|
|
|
|
// manager sets it from the committed shot's ArrivalTime), so a round that faded out early still lives to show
|
|
|
|
|
|
// the detonation the simulation resolves at arrival. 0 (the default) kills the round when its fade ends.
|
|
|
|
|
|
public float BindingLifetime { get; set; }
|
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public event Action OnKill;
|
|
|
|
|
|
|
2026-09-29 17:05:15 +02:00
|
|
|
|
// The simulation's verdict on this round, bound by GuidedProjectileManager to Zone.ShotCommitted and
|
|
|
|
|
|
// ShotResolved for its ShotId: this object flies and looks, and neither decides where the round bursts nor when.
|
2026-09-29 18:37:02 +02:00
|
|
|
|
// The one owner of the subscription's lifetime: a new binding disposes the one it replaces, Kill disposes it,
|
|
|
|
|
|
// and so does destruction, so a projectile torn down with its scene never leaves an observer on the zone's
|
|
|
|
|
|
// subjects to touch a destroyed transform.
|
|
|
|
|
|
public IDisposable Binding
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _binding;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
_binding?.Dispose();
|
|
|
|
|
|
_binding = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private IDisposable _binding;
|
|
|
|
|
|
|
|
|
|
|
|
void OnDestroy() => Binding = null;
|
2026-09-29 17:05:15 +02:00
|
|
|
|
|
|
|
|
|
|
// The commit named a burst point: fly to it, whatever this round was homing on (a target it would have
|
2026-09-29 18:37:02 +02:00
|
|
|
|
// passed, or a Range clamp it would have overshot). The point is a static target, so the old target's motion
|
|
|
|
|
|
// is dropped with it: the jump to the new point must not feed the guidance a target velocity.
|
2026-09-29 17:05:15 +02:00
|
|
|
|
public void BurstAt(Vector3 point)
|
|
|
|
|
|
{
|
|
|
|
|
|
Target = null;
|
|
|
|
|
|
TargetPosition = () => point;
|
2026-09-29 18:37:02 +02:00
|
|
|
|
_previousTargetPosition = point;
|
|
|
|
|
|
_targetVelocity = Vector3.zero;
|
2026-09-29 17:05:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-29 18:37:02 +02:00
|
|
|
|
// The resolution. A round that detonated (a hit, or a burst) bursts now, at the simulation's burst point when
|
|
|
|
|
|
// it has one; a round the simulation resolved without a detonation (a contact or delayed round that missed,
|
|
|
|
|
|
// one whose target is gone) just falls away, with no explosion to show.
|
|
|
|
|
|
public void Resolve(bool detonated, Vector3? point)
|
2026-09-29 17:05:15 +02:00
|
|
|
|
{
|
2026-09-29 21:16:52 +02:00
|
|
|
|
// A round that already faded out (overshot, out of flight) still shows the simulation's detonation: only a
|
|
|
|
|
|
// round that has been killed is done. The fade is stopped so it cannot Kill the round a second time.
|
|
|
|
|
|
if (!_alive) return;
|
2026-09-29 18:37:02 +02:00
|
|
|
|
if (!detonated)
|
|
|
|
|
|
{
|
2026-09-29 21:16:52 +02:00
|
|
|
|
if (_active) StartCoroutine(FadeOut());
|
2026-09-29 18:37:02 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-09-29 21:16:52 +02:00
|
|
|
|
StopAllCoroutines();
|
2026-09-29 22:15:53 +02:00
|
|
|
|
// A hit carries no burst point: the effect plays where the round was homing (a round that faded out has stopped
|
|
|
|
|
|
// short of it), and is skipped when the round never knew where its target was.
|
|
|
|
|
|
Vector3? effectAt = point ?? (TargetPosition != null ? TargetPosition() : Target ? Target.position : (Vector3?) null);
|
2026-09-29 17:05:15 +02:00
|
|
|
|
if (point.HasValue) transform.position = point.Value;
|
2026-09-29 22:15:53 +02:00
|
|
|
|
if (HitEffect != null && effectAt.HasValue)
|
2026-09-29 17:05:15 +02:00
|
|
|
|
{
|
|
|
|
|
|
var ht = HitEffect.Instantiate<Transform>();
|
2026-09-29 22:15:53 +02:00
|
|
|
|
ht.position = effectAt.Value;
|
2026-09-29 17:05:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
StartCoroutine(Kill());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
void OnEnable()
|
|
|
|
|
|
{
|
|
|
|
|
|
_active = _alive = true;
|
2026-09-29 21:16:52 +02:00
|
|
|
|
_spawnTime = Time.time;
|
2026-09-29 22:15:53 +02:00
|
|
|
|
BindingLifetime = 0f; // a pooled round starts unbound; the manager binds it after enabling
|
2026-08-10 14:57:50 +01:00
|
|
|
|
_phase = Random.value * 100;
|
|
|
|
|
|
_prevDist = Single.MaxValue;
|
|
|
|
|
|
Particles.startColor = Color.white;
|
|
|
|
|
|
//var main = Particles.main;
|
|
|
|
|
|
//main.startColor = new ParticleSystem.MinMaxGradient { mode = ParticleSystemGradientMode.Color, color = Color.white };
|
|
|
|
|
|
Particles.Clear(true);
|
|
|
|
|
|
Particles.Play(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Update ()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (SourceEntity == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
var t = transform;
|
|
|
|
|
|
|
|
|
|
|
|
if (_active)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (TargetPosition == null && !Target)
|
|
|
|
|
|
{
|
|
|
|
|
|
StartCoroutine(FadeOut());
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
Convert engine types through CultMath.UnityBridge
CultMath has no implicit conversions to or from Vector*, Quaternion or
Color, so Assembly-CSharp converts explicitly with ToCultMath/ToUnity at
each engine boundary: FieldDriver, GuidedProjectile(Manager),
EntityInstance, ActionGameManager, HullCollider, GridObject, ShipInstance,
ShieldManager, ItemPickup, FieldTester, PlaceUIElementWorldspace,
MapMenuInput, MapRenderer, SectorRenderer and ZoneRenderer. CultMath math
calls that were handed Unity vectors now take converted values;
Vector3.Scale in FieldDriver becomes a float3 product, and ItemPickup's
Vector3.Dot on float3s becomes dot. packages-lock.json records the
cultmath git package at f0605367 and demotes com.unity.mathematics to a
transitive dependency.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:33 +02:00
|
|
|
|
var position = t.position.ToCultMath();
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
var targetPosition = TargetPosition?.Invoke() ?? Target.position;
|
Convert engine types through CultMath.UnityBridge
CultMath has no implicit conversions to or from Vector*, Quaternion or
Color, so Assembly-CSharp converts explicitly with ToCultMath/ToUnity at
each engine boundary: FieldDriver, GuidedProjectile(Manager),
EntityInstance, ActionGameManager, HullCollider, GridObject, ShipInstance,
ShieldManager, ItemPickup, FieldTester, PlaceUIElementWorldspace,
MapMenuInput, MapRenderer, SectorRenderer and ZoneRenderer. CultMath math
calls that were handed Unity vectors now take converted values;
Vector3.Scale in FieldDriver becomes a float3 product, and ItemPickup's
Vector3.Dot on float3s becomes dot. packages-lock.json records the
cultmath git package at f0605367 and demotes com.unity.mathematics to a
transitive dependency.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:33 +02:00
|
|
|
|
_targetVelocity = lerp(_targetVelocity.ToCultMath(), (targetPosition - _previousTargetPosition).ToCultMath(), saturate(Time.deltaTime * 5)).ToUnity();
|
2026-08-10 14:57:50 +01:00
|
|
|
|
_previousTargetPosition = targetPosition;
|
Delete AetheriaMath in favour of CultMath
AetheriaMath predates the CultMath swap and re-implemented helpers CultMath
now owns. Callers use CultMath directly: decay (Ship, AetherDrive, Entity),
damp (VelocityConversion, FieldTester, FieldDriver), first_order_intercept
(TurretController, Combat, GuidedProjectile), quadratic_bezier (FieldDriver)
and smootherstep (Ship). Parameter order matches in every case, and those
formulas have the same float evaluation order, so behaviour is unchanged.
Galaxy's commented-out segment check names distance_to_segment.
The other overloads had no callers and are dropped without a CultMath
counterpart being needed: the double Damp overloads, FindDistanceToSegment,
GetCatmullRomPosition, GetCubicSplinePosition, Smoothstep(x), and the
Deg2Rad/Rad2Deg constants.
AetherDb census, factions and hardpoint-fit are byte-identical to the
pre-cut captures.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 18:02:41 +02:00
|
|
|
|
targetPosition = first_order_intercept(position,float3.zero, TopSpeed, targetPosition.ToCultMath(), _targetVelocity.ToCultMath()).ToUnity();
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
var diff = targetPosition - transform.position;
|
|
|
|
|
|
var targetDist = diff.magnitude;
|
|
|
|
|
|
var sourceDist = length(StartPosition.xz - position.xz);
|
|
|
|
|
|
|
2026-09-29 20:20:06 +02:00
|
|
|
|
// Out of range, or flown past its target: the round falls away. Only the simulation's detonation
|
|
|
|
|
|
// (Resolve) shows an explosion; a round that merely ran out of flight has none.
|
Convert engine types through CultMath.UnityBridge
CultMath has no implicit conversions to or from Vector*, Quaternion or
Color, so Assembly-CSharp converts explicitly with ToCultMath/ToUnity at
each engine boundary: FieldDriver, GuidedProjectile(Manager),
EntityInstance, ActionGameManager, HullCollider, GridObject, ShipInstance,
ShieldManager, ItemPickup, FieldTester, PlaceUIElementWorldspace,
MapMenuInput, MapRenderer, SectorRenderer and ZoneRenderer. CultMath math
calls that were handed Unity vectors now take converted values;
Vector3.Scale in FieldDriver becomes a float3 product, and ItemPickup's
Vector3.Dot on float3s becomes dot. packages-lock.json records the
cultmath git package at f0605367 and demotes com.unity.mathematics to a
transitive dependency.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:33 +02:00
|
|
|
|
if (sourceDist > Range || dot(diff.ToCultMath(), Velocity.ToCultMath()) < 0)
|
2026-08-10 14:57:50 +01:00
|
|
|
|
{
|
|
|
|
|
|
StartCoroutine(FadeOut());
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
_prevDist = targetDist;
|
|
|
|
|
|
|
|
|
|
|
|
var targetDistFlat = diff.Flatland().magnitude;
|
|
|
|
|
|
var curveLerp = 1 - targetDistFlat / (sourceDist + targetDistFlat);
|
|
|
|
|
|
var dir = diff.normalized;
|
Convert engine types through CultMath.UnityBridge
CultMath has no implicit conversions to or from Vector*, Quaternion or
Color, so Assembly-CSharp converts explicitly with ToCultMath/ToUnity at
each engine boundary: FieldDriver, GuidedProjectile(Manager),
EntityInstance, ActionGameManager, HullCollider, GridObject, ShipInstance,
ShieldManager, ItemPickup, FieldTester, PlaceUIElementWorldspace,
MapMenuInput, MapRenderer, SectorRenderer and ZoneRenderer. CultMath math
calls that were handed Unity vectors now take converted values;
Vector3.Scale in FieldDriver becomes a float3 product, and ItemPickup's
Vector3.Dot on float3s becomes dot. packages-lock.json records the
cultmath git package at f0605367 and demotes com.unity.mathematics to a
transitive dependency.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:33 +02:00
|
|
|
|
var right = cross(dir.ToCultMath(), float3(0, 1, 0));
|
|
|
|
|
|
var up = cross(dir.ToCultMath(), right);
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
if (Children > 0 && SplitTime < curveLerp)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < Children; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var child = ChildProjectile.Instantiate<GuidedProjectile>();
|
|
|
|
|
|
child.transform.position = t.position;
|
|
|
|
|
|
child.StartPosition = StartPosition;
|
Convert engine types through CultMath.UnityBridge
CultMath has no implicit conversions to or from Vector*, Quaternion or
Color, so Assembly-CSharp converts explicitly with ToCultMath/ToUnity at
each engine boundary: FieldDriver, GuidedProjectile(Manager),
EntityInstance, ActionGameManager, HullCollider, GridObject, ShipInstance,
ShieldManager, ItemPickup, FieldTester, PlaceUIElementWorldspace,
MapMenuInput, MapRenderer, SectorRenderer and ZoneRenderer. CultMath math
calls that were handed Unity vectors now take converted values;
Vector3.Scale in FieldDriver becomes a float3 product, and ItemPickup's
Vector3.Dot on float3s becomes dot. packages-lock.json records the
cultmath git package at f0605367 and demotes com.unity.mathematics to a
transitive dependency.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:33 +02:00
|
|
|
|
var randomDirection = normalize(Random.insideUnitCircle.ToCultMath());
|
2026-08-10 14:57:50 +01:00
|
|
|
|
var perpendicularRandom = randomDirection.x * right + randomDirection.y * up;
|
Convert engine types through CultMath.UnityBridge
CultMath has no implicit conversions to or from Vector*, Quaternion or
Color, so Assembly-CSharp converts explicitly with ToCultMath/ToUnity at
each engine boundary: FieldDriver, GuidedProjectile(Manager),
EntityInstance, ActionGameManager, HullCollider, GridObject, ShipInstance,
ShieldManager, ItemPickup, FieldTester, PlaceUIElementWorldspace,
MapMenuInput, MapRenderer, SectorRenderer and ZoneRenderer. CultMath math
calls that were handed Unity vectors now take converted values;
Vector3.Scale in FieldDriver becomes a float3 product, and ItemPickup's
Vector3.Dot on float3s becomes dot. packages-lock.json records the
cultmath git package at f0605367 and demotes com.unity.mathematics to a
transitive dependency.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:33 +02:00
|
|
|
|
child.Velocity = (normalize(lerp(perpendicularRandom, dir.ToCultMath(), SplitSeparationForwardness)) * length(Velocity.ToCultMath()) * SplitSeparationVelocity).ToUnity();
|
2026-08-10 14:57:50 +01:00
|
|
|
|
child.Range = Range;
|
|
|
|
|
|
child.Source = Source;
|
|
|
|
|
|
child.Target = Target;
|
|
|
|
|
|
child.SourceEntity = SourceEntity;
|
|
|
|
|
|
child.GuidanceCurve = GuidanceCurve;
|
|
|
|
|
|
child.LiftCurve = LiftCurve;
|
|
|
|
|
|
child.ThrustCurve = ThrustCurve;
|
|
|
|
|
|
child.Thrust = Thrust;
|
|
|
|
|
|
child.TopSpeed = TopSpeed;
|
|
|
|
|
|
child.Frequency = Frequency;
|
|
|
|
|
|
child.Thrust = Thrust;
|
|
|
|
|
|
}
|
|
|
|
|
|
StartCoroutine(Kill());
|
|
|
|
|
|
if (HitEffect != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var ht = HitEffect.Instantiate<Transform>();
|
|
|
|
|
|
ht.position = t.position;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var dodge = normalize(lerp(
|
|
|
|
|
|
normalize(right * noise(Time.time * Frequency + _phase) + up * noise(Time.time * Frequency + (100 + _phase))),
|
Convert engine types through CultMath.UnityBridge
CultMath has no implicit conversions to or from Vector*, Quaternion or
Color, so Assembly-CSharp converts explicitly with ToCultMath/ToUnity at
each engine boundary: FieldDriver, GuidedProjectile(Manager),
EntityInstance, ActionGameManager, HullCollider, GridObject, ShipInstance,
ShieldManager, ItemPickup, FieldTester, PlaceUIElementWorldspace,
MapMenuInput, MapRenderer, SectorRenderer and ZoneRenderer. CultMath math
calls that were handed Unity vectors now take converted values;
Vector3.Scale in FieldDriver becomes a float3 product, and ItemPickup's
Vector3.Dot on float3s becomes dot. packages-lock.json records the
cultmath git package at f0605367 and demotes com.unity.mathematics to a
transitive dependency.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 17:47:33 +02:00
|
|
|
|
float3(0, 1, 0), LiftCurve.Evaluate(curveLerp))).ToUnity();
|
2026-08-10 14:57:50 +01:00
|
|
|
|
var desired = Vector3.Slerp(dodge, dir, GuidanceCurve.Evaluate(curveLerp)).normalized * TopSpeed;
|
|
|
|
|
|
var thrustCurve = ThrustCurve.Evaluate(curveLerp);
|
|
|
|
|
|
var thrust = Thrust * thrustCurve;
|
|
|
|
|
|
var c = Color.white * thrustCurve;
|
|
|
|
|
|
c.a = 1;
|
|
|
|
|
|
Particles.startColor = c;
|
|
|
|
|
|
Velocity += (desired-Velocity).normalized * (thrust * Time.deltaTime);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(_alive)
|
|
|
|
|
|
{
|
|
|
|
|
|
t.position += Velocity * Time.deltaTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
IEnumerator FadeOut()
|
|
|
|
|
|
{
|
|
|
|
|
|
_active = false;
|
|
|
|
|
|
var startTime = Time.time;
|
|
|
|
|
|
while (Time.time - startTime < FadeOutTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
var lerp = 1 - (Time.time - startTime) / FadeOutTime;
|
|
|
|
|
|
var c = Color.white * lerp;
|
|
|
|
|
|
c.a = 1;
|
|
|
|
|
|
Particles.startColor = c;
|
|
|
|
|
|
//var main = Particles.main;
|
|
|
|
|
|
//main.startColor = new ParticleSystem.MinMaxGradient { mode = ParticleSystemGradientMode.Color, color = Color.white * lerp };
|
|
|
|
|
|
yield return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-29 22:15:53 +02:00
|
|
|
|
// The wait holds still: a round waiting on the verdict must not drift off its last position.
|
|
|
|
|
|
Velocity = Vector3.zero;
|
2026-09-29 21:16:52 +02:00
|
|
|
|
// Faded, but the simulation's verdict has not necessarily arrived: wait for it until the shot's flight is over.
|
|
|
|
|
|
while (Time.time - _spawnTime < BindingLifetime) yield return null;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
StartCoroutine(Kill());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
IEnumerator Kill()
|
|
|
|
|
|
{
|
2026-09-29 17:05:15 +02:00
|
|
|
|
Binding = null;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
_active = false;
|
|
|
|
|
|
_alive = false;
|
|
|
|
|
|
Particles.Stop(true, ParticleSystemStopBehavior.StopEmitting);
|
|
|
|
|
|
var startTime = Time.time;
|
|
|
|
|
|
var lifetime = Particles.main.startLifetime.constant;
|
|
|
|
|
|
while (Time.time - startTime < lifetime)
|
|
|
|
|
|
{
|
|
|
|
|
|
yield return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
OnKill?.Invoke();
|
|
|
|
|
|
GetComponent<Prototype>().ReturnToPool();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|