Aetheria/Assets/Scripts/ServerShared/VapourClouds.cs
Meta Vens 6dcf33d11c Vapour clouds: Zone owns them, a VapourDump behaviour vents one (union 42)
Zone.Vent is the one adder, StepClouds the one remover, Zone.Obscuration the
one reader: the product of (1 - opacity) over clouds whose disc the segment
touches (closest point of the segment to the centre within Radius), 1f when
none. Derived from live clouds at each call, never cached. VapourDump vents
once on its first run, drifting with its venter. Nothing reads Obscuration
yet (Sensor.Gain takes it in the next commit); this commit builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:22:00 +02:00

31 lines
1.3 KiB
C#

using CultMath;
using static CultMath.math;
using float2 = CultMath.float2;
// A vented vapour cloud: a free-floating disc that thins every sensor sight line through it. Not an Entity,
// never priced or rolled, never saved. Zone.Vent is the only adder and Zone's cloud step the only remover;
// Zone.Obscuration is the one reader detection uses.
public sealed class VapourCloud
{
public FloatingBodyId Id;
public KinematicBody Body;
public double VentedAt;
public float Lifetime;
public float Radius;
// The opacity at the moment of venting; it fades linearly to nothing over Lifetime.
public float Opacity;
public Entity Venter;
public float OpacityAt(double now) =>
Lifetime <= 0f ? 0f : saturate(Opacity) * saturate(1f - (float) (now - VentedAt) / Lifetime);
// Whether the segment a-b reaches the disc: the closest point of the segment to the centre lies within the
// radius. A cloud past either end of the segment touches it only if it is near that end.
public bool Touches(float2 a, float2 b)
{
var ab = b - a;
var length2 = lengthsq(ab);
var t = length2 > 0f ? saturate(dot(Body.Position - a, ab) / length2) : 0f;
return lengthsq(Body.Position - (a + ab * t)) <= Radius * Radius;
}
}