Merge origin/master into damage-falloff
Conflicts: FusedRounds scenario brief and fixture comment, SmokeRoundsSteppedTests, and AetherDb's usage line keep both sides, with DeathCluster renamed Cluster Cannon. GameData/Aetheria.cc is master's blob; range-falloff is rerun on it in the next commit. This commit's catalog does not carry the falloff stats, so CatalogRole/falloff catalog tests do not pass here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
97b7566ee9
65 changed files with 3360 additions and 336 deletions
|
|
@ -191,6 +191,8 @@ public interface IPowerConsumer
|
|||
Union(47, typeof(MunitionFuelData)),
|
||||
Union(48, typeof(PickupData)),
|
||||
Union(49, typeof(LaunchSuppressorData)),
|
||||
Union(50, typeof(ListenerData)),
|
||||
Union(51, typeof(ScannerData)),
|
||||
JsonObject(MemberSerialization.OptIn)]
|
||||
public abstract class BehaviorData
|
||||
{
|
||||
|
|
|
|||
44
Assets/Scripts/ServerShared/Behaviors/Listener.cs
Normal file
44
Assets/Scripts/ServerShared/Behaviors/Listener.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* 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 MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
// Passive listening gear (ruling sensor-stat-set): hears other ships' emissions at a multiple of their own range,
|
||||
// bearing only. It emits nothing; Entity.HearingMultiple asks each active listener at hearing time. Its power is the
|
||||
// design's EnergyDraw and its activity is the item's Active, as for sensors.
|
||||
[Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn), RuntimeInspectable]
|
||||
public class ListenerData : BehaviorData
|
||||
{
|
||||
// How many times an emitter's own reach this listener hears it at.
|
||||
[Inspectable, JsonProperty("multiple"), Key(1), RuntimeInspectable]
|
||||
public PerformanceStat Multiple = new PerformanceStat();
|
||||
|
||||
public override Behavior CreateInstance(EquippedItem item)
|
||||
{
|
||||
return new Listener(this, item);
|
||||
}
|
||||
public override Behavior CreateInstance(ConsumableItemEffect item)
|
||||
{
|
||||
return new Listener(this, item);
|
||||
}
|
||||
}
|
||||
|
||||
public class Listener : Behavior
|
||||
{
|
||||
private readonly ListenerData _data;
|
||||
|
||||
public Listener(ListenerData data, EquippedItem item) : base(data, item)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
public Listener(ListenerData data, ConsumableItemEffect item) : base(data, item)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public float Multiple => Evaluate(_data.Multiple);
|
||||
|
||||
public override bool Execute(float dt) => true;
|
||||
}
|
||||
11
Assets/Scripts/ServerShared/Behaviors/Listener.cs.meta
Normal file
11
Assets/Scripts/ServerShared/Behaviors/Listener.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5b0e1f6c2d7a4e39a8c4d1f27e6b3a90
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
98
Assets/Scripts/ServerShared/Behaviors/Scanner.cs
Normal file
98
Assets/Scripts/ServerShared/Behaviors/Scanner.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* 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 MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
using static CultMath.math;
|
||||
|
||||
// Inspection (campaign aetheria-release, rulings quiet-gear-identifiable-hull-layout-known, scan-held-beam,
|
||||
// scan-is-an-act): a gear that, held on the ship's tracked target within Range for ScanTime, reveals everything fitted to
|
||||
// that target, quiet gear that never acts included. The scan is an emission like a lock: the target is painted and
|
||||
// third parties hear it at Range over the intercept factor.
|
||||
[Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn), RuntimeInspectable]
|
||||
public class ScannerData : BehaviorData
|
||||
{
|
||||
// The planar distance to the target within which a scan builds.
|
||||
[Inspectable, JsonProperty("range"), Key(1), RuntimeInspectable]
|
||||
public PerformanceStat Range = new PerformanceStat();
|
||||
|
||||
// Seconds a held scan takes to finish; a broken scan decays over the same span.
|
||||
[Inspectable, JsonProperty("scanTime"), Key(2), RuntimeInspectable]
|
||||
public PerformanceStat ScanTime = new PerformanceStat();
|
||||
|
||||
// The ratio of how far this scanner sees to how far others hear it (ruling sensor-stat-set); a design without it
|
||||
// reads as 1.
|
||||
[Inspectable, JsonProperty("interceptFactor"), Key(3)]
|
||||
public PerformanceStat InterceptFactor;
|
||||
|
||||
public override Behavior CreateInstance(EquippedItem item)
|
||||
{
|
||||
return new Scanner(this, item);
|
||||
}
|
||||
public override Behavior CreateInstance(ConsumableItemEffect item)
|
||||
{
|
||||
return new Scanner(this, item);
|
||||
}
|
||||
}
|
||||
|
||||
public class Scanner : Behavior, IProgressBehavior
|
||||
{
|
||||
private readonly ScannerData _data;
|
||||
private Entity _target;
|
||||
private float _built;
|
||||
private double _heldAt;
|
||||
|
||||
// The scan's progress now: what Execute last built, less ScanTime-relative decay over the time since a tick last held
|
||||
// it (ruling scan-held-beam: a broken scan decays over ScanTime, the item inactive or destroyed included). Derived from
|
||||
// the zone clock, never from tick order, so a scanner decays on the tick it breaks whether its host runs Execute
|
||||
// before the always-updated pass (EquippedItem) or after it (ConsumableItemEffect). The one decay site.
|
||||
public float Progress => Remaining(_heldAt);
|
||||
|
||||
public Scanner(ScannerData data, EquippedItem item) : base(data, item)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
public Scanner(ScannerData data, ConsumableItemEffect item) : base(data, item)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
private float Remaining(double since)
|
||||
{
|
||||
if (_built <= 0f) return 0f;
|
||||
var now = Entity.Zone?.ExactTime ?? since;
|
||||
return saturate(_built - (float) (now - since) / Evaluate(_data.ScanTime));
|
||||
}
|
||||
|
||||
// Runs only while the item is Active. The scan builds while the ship's own Target is a tracked ship within Range, and
|
||||
// is painted and emitted on every tick it builds; at 1 the target's fitted items enter the book and the scan starts over.
|
||||
// A tick that does not hold the scan builds nothing, and the zone clock carries its decay (Progress).
|
||||
public override bool Execute(float dt)
|
||||
{
|
||||
var target = Entity.Target.Value.Entity;
|
||||
if (_target != target)
|
||||
{
|
||||
_built = 0f;
|
||||
_target = target;
|
||||
}
|
||||
|
||||
var range = Evaluate(_data.Range);
|
||||
if (target != null && Entity.VisibleEntities.Contains(target) && Entity.TargetRange <= range)
|
||||
{
|
||||
// Decay over the ticks since the last held one, not counting this tick, which holds.
|
||||
var progress = saturate(Remaining(_heldAt + dt) + dt / Evaluate(_data.ScanTime));
|
||||
_built = progress;
|
||||
_heldAt = Entity.Zone?.ExactTime ?? _heldAt;
|
||||
target.ReceivePaint(Entity, Item, PaintMode.Scan, progress);
|
||||
Entity.Emit(Item, range / (_data.InterceptFactor == null ? 1f : Evaluate(_data.InterceptFactor)));
|
||||
if (progress >= 1f)
|
||||
{
|
||||
Entity.RecordInspection(target);
|
||||
_built = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ServerShared/Behaviors/Scanner.cs.meta
Normal file
11
Assets/Scripts/ServerShared/Behaviors/Scanner.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bf9e034c7a31410ea1c516f46b43825c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -247,7 +247,7 @@ public abstract class Entity
|
|||
// sensor-stat-set): written in Update, read by Sense in the same tick, cleared by the next PublishInbox.
|
||||
private readonly List<ActiveEmission> _emissions = new List<ActiveEmission>();
|
||||
// What this observer has seen each ship's items do (ruling visibility-per-capability-reveal), keyed (subject,
|
||||
// item). Written only by RecordReveal (an act enters) and EndReveals (an end the observer perceives, or a departed
|
||||
// item). Written only by RecordReveal (an act, or a scan through RecordInspection, enters) and EndReveals (an end the observer perceives, or a departed
|
||||
// subject's entries aged past BearingMemory), cleared by Deactivate (ruling reveal-ends-on-perception).
|
||||
private readonly Dictionary<Entity, Dictionary<EquippedItem, Reveal>> _reveals = new Dictionary<Entity, Dictionary<EquippedItem, Reveal>>();
|
||||
private readonly List<EquippedItem> _endedItems = new List<EquippedItem>();
|
||||
|
|
@ -714,8 +714,23 @@ public abstract class Entity
|
|||
}
|
||||
|
||||
// How much of an emission's reach this observer's instruments hear it at (ruling sensor-stat-set: listeners hear at
|
||||
// a multiple of an emitter's own range): 1 with an active sensor, 0 without. Listener gear extends it.
|
||||
private int HearingMultiple() => ActiveSensors().Count > 0 ? 1 : 0;
|
||||
// a multiple of an emitter's own range): the largest of 1 (any active sensor) and each active listener's Multiple,
|
||||
// 0 with neither. Catalog values come from AetherDb sensor-stat-set.
|
||||
private float HearingMultiple()
|
||||
{
|
||||
var multiple = ActiveSensors().Count > 0 ? 1f : 0f;
|
||||
for (var i = 0; i < Equipment.Count; i++)
|
||||
{
|
||||
var item = Equipment[i];
|
||||
if (!item.Active.Value) continue;
|
||||
foreach (var behavior in item.Behaviors)
|
||||
if (behavior is Listener listener) multiple = max(multiple, listener.Multiple);
|
||||
}
|
||||
foreach (var effect in _activeConsumables)
|
||||
foreach (var behavior in effect.Behaviors)
|
||||
if (behavior is Listener listener) multiple = max(multiple, listener.Multiple);
|
||||
return multiple;
|
||||
}
|
||||
|
||||
// Cut 12.3 (docs/fire-control-cut.md): "armour absorbs first" -- the per-cell armour phase, split out of
|
||||
// what used to be Absorb's own first half (moved verbatim from DamageSchematic's per-cell body, Cut 3) so
|
||||
|
|
@ -934,14 +949,14 @@ public abstract class Entity
|
|||
_paintInbox.Add(new PendingPaint(new PaintWarning(normalize((emitter.Position - Position).xz), emitter.ClaimOf(item), mode, strength), emitter, item));
|
||||
}
|
||||
|
||||
// The reveal book's one way in and its one membership gate (callers: Sense and PublishInbox, both in this file):
|
||||
// this observer saw `item` of `subject` act on `channel`, now, from this bearing. An item-less act (hull
|
||||
// The reveal book's one way in and its one membership gate (callers: Sense, PublishInbox and RecordInspection, all in
|
||||
// this file): this observer saw `item` of `subject` act on `channel`, now, from this bearing. An item-less act (hull
|
||||
// radiation, a consumable) reveals nothing, and neither does a subject that is not in the zone: a departed
|
||||
// subject's entries are never created or refreshed, from any caller, and neither does an item this observer
|
||||
// perceives destroyed (PerceivesDestroyed). A later act of the same item replaces the
|
||||
// earlier one. `claimed` is the class the carrier (signature, emission or paint) stamped: what this observer's
|
||||
// classifying readers will believe of the act; `item` stays the aim and book key.
|
||||
internal void RecordReveal(Entity subject, EquippedItem item, SignatureChannel channel, EquippableItemData claimed)
|
||||
internal void RecordReveal(Entity subject, EquippedItem item, SignatureChannel? channel, EquippableItemData claimed)
|
||||
{
|
||||
if (!_active || item == null || !Zone.Entities.Contains(subject) || PerceivesDestroyed(subject, item)) return;
|
||||
if (!_reveals.TryGetValue(subject, out var book))
|
||||
|
|
@ -949,6 +964,21 @@ public abstract class Entity
|
|||
book[item] = new Reveal(item, channel, normalize((subject.Position - Position).xz), Zone.Time, claimed);
|
||||
}
|
||||
|
||||
// A finished scan (Scanner.Execute; rulings scan-is-an-act, quiet-gear-identifiable-hull-layout-known: quiet gear
|
||||
// that never acts stays identifiable through special gear): this observer learns every fitted item of `subject`,
|
||||
// channel null. It records what the item claims, not what it is (ClaimOf, as every act's carrier stamps it: the
|
||||
// aetheria-ew one judge, classify-from-belief), skips the hull (its class is the track's to give) and a destroyed
|
||||
// item, and goes through RecordReveal's gate like any act. It calls no EndReveals: a scan ends nothing
|
||||
// (reveal-ends-on-perception).
|
||||
internal void RecordInspection(Entity subject)
|
||||
{
|
||||
foreach (var item in subject.Equipment)
|
||||
{
|
||||
if (item == subject.EquippedHull || item.EquippableItem.IsDestroyed) continue;
|
||||
RecordReveal(subject, item, null, subject.ClaimOf(item));
|
||||
}
|
||||
}
|
||||
|
||||
// This observer resolves `subject`'s gear (ContactOf's Identified gate) and `item` of it is destroyed: the one rule
|
||||
// for a destroyed item an observer perceives, read by the book's entry (RecordReveal) and its end (Sense).
|
||||
private bool PerceivesDestroyed(Entity subject, EquippedItem item) =>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using float2 = CultMath.float2;
|
|||
public class ZoneEnvironment
|
||||
{
|
||||
[JsonProperty("nebula"), Key(0)] public NebulaSettings Nebula;
|
||||
[JsonProperty("flow"), Key(1)] public FlowSettings Flow;
|
||||
// Key 1 is retired (it held the flow, now PlanetSettings.Flow); never reuse it.
|
||||
[JsonProperty("noise"), Key(2)] public NoiseSettings Noise;
|
||||
[JsonProperty("lighting"), Key(3)] public AmbientLightingSettings Lighting;
|
||||
[JsonProperty("grid"), Key(4)] public GridSettings Grid;
|
||||
|
|
|
|||
78
Assets/Scripts/ServerShared/FlowField.cs
Normal file
78
Assets/Scripts/ServerShared/FlowField.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using CultMath;
|
||||
using static CultMath.math;
|
||||
using float2 = CultMath.float2;
|
||||
using float3 = CultMath.float3;
|
||||
|
||||
/// <summary>
|
||||
/// The global flow law: the one definition of the drift that stardust and nebula follow and that the sim knows.
|
||||
/// A pure function of the zone's FlowSettings, a position, a scroll and a downhill gradient; nothing here keeps
|
||||
/// a clock. Zone.Flow evaluates it at zone time. Assets/Shaders/Volumetric.cginc (globalFlow, slopeFlow) is its
|
||||
/// HLSL lowering, driven by VolumeSampling.LowerFlow and pinned to it by Aetheria.EditorTools.FlowFieldParityVerify.
|
||||
/// </summary>
|
||||
public static class FlowField
|
||||
{
|
||||
// The second octave of the noise samples at position * Golden (Volumetric.cginc Tri3D).
|
||||
const float Golden = 1.61803398875f;
|
||||
|
||||
// The scroll of the noise field, in field units: speed times zone time, for each of the two octaves, as a phase.
|
||||
// Every term of Tri3D reaches the scrolled axis through frac, so a phase is only meaningful modulo 1: the fold
|
||||
// happens here in double, where zone time is exact, and the float that reaches the shaders is in [0, 1) however
|
||||
// old the zone is. x is the first octave's phase, y the second's (the second octave's axis is scaled by Golden).
|
||||
public static float2 Scroll(FlowSettings settings, double zoneTime)
|
||||
{
|
||||
var scroll = (double) settings.GlobalScrollSpeed * zoneTime;
|
||||
return float2(Phase(scroll), Phase(scroll * Golden));
|
||||
}
|
||||
|
||||
// A phase in [0, 1) strictly: a double just below an integer rounds up to 1f in the cast, and 1 is 0 modulo 1.
|
||||
static float Phase(double value)
|
||||
{
|
||||
var phase = (float) frac(value);
|
||||
return phase < 1f ? phase : 0f;
|
||||
}
|
||||
|
||||
// The phase of a cycle of `period` seconds at zone time, in [0, 1). The nebula's flow-advected noise cycles and
|
||||
// Stardust's particle lifetimes take their phase from here, so drift follows sim time and stops with the sim.
|
||||
public static float Cycle(double zoneTime, float period) => period == 0f ? 0f : Phase(zoneTime / period);
|
||||
|
||||
// The procedural term (Volumetric.cginc globalFlow): a Tri3D field of position / scale, scrolled along y.
|
||||
// Skipped when its amplitude is zero, as the shader's FLOW_GLOBAL keyword skips it.
|
||||
public static float3 Global(FlowSettings settings, float3 position, float2 scroll)
|
||||
{
|
||||
if (settings.GlobalAmplitude == 0f) return float3.zero;
|
||||
var field = position / settings.GlobalScale;
|
||||
var sample = Tri3D(field - float3(0f, scroll.x, 0f), field * Golden - float3(0f, scroll.y, 0f)) * settings.GlobalAmplitude;
|
||||
sample.y *= .5f;
|
||||
return sample;
|
||||
}
|
||||
|
||||
// The slope term (Volumetric.cginc slopeFlow): swirl around and slide along the gravity gradient.
|
||||
// `downhill` is the DOWNHILL gradient, -grad(height) in world xz. The shader's texel gradient is that same
|
||||
// vector (getUV negates the world x and z axes), and Zone.GetNormal's xz is proportional to it.
|
||||
// Skipped when both amplitudes are zero (the FLOW_SLOPE keyword), and zero for a zero gradient.
|
||||
public static float3 Slope(FlowSettings settings, float2 downhill)
|
||||
{
|
||||
if (settings.SlopeAmplitude == 0f && settings.SwirlAmplitude == 0f) return float3.zero;
|
||||
if (dot(downhill, downhill) == 0f) return float3.zero;
|
||||
var direction = normalize(downhill);
|
||||
return float3(direction.y, 0f, -direction.x) * settings.SwirlAmplitude
|
||||
+ float3(direction.x, 0f, direction.y) * settings.SlopeAmplitude;
|
||||
}
|
||||
|
||||
public static float3 At(FlowSettings settings, float3 position, float2 scroll, float2 downhill) =>
|
||||
Global(settings, position, scroll) + Slope(settings, downhill);
|
||||
|
||||
// Volumetric.cginc tri / tri3 / Tri3D, term for term.
|
||||
static float Tri(float x) => abs(frac(x) - .5f);
|
||||
|
||||
static float3 Tri3(float3 p) =>
|
||||
float3(Tri(p.z + Tri(p.y)), Tri(p.z + Tri(p.x)), Tri(p.y + Tri(p.x)));
|
||||
|
||||
// The two octaves' positions are given already scrolled (see Scroll).
|
||||
static float3 Tri3D(float3 p, float3 pGolden)
|
||||
{
|
||||
var t1 = normalize(Tri3(p));
|
||||
var t2 = normalize(Tri3(pGolden));
|
||||
return cross(t1, t2);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/ServerShared/FlowField.cs.meta
Normal file
2
Assets/Scripts/ServerShared/FlowField.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8105a45840c4552459aaf44e6c0e19ce
|
||||
|
|
@ -20,13 +20,13 @@ public sealed class AiFusedDiscipline : Scenario
|
|||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Compact Reactor", int2(2, 4), None),
|
||||
(weapon, int2(1, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise));
|
||||
|
||||
stage.Player(stage.Generated("Djinni"), float2(0, 0));
|
||||
|
|
|
|||
|
|
@ -27,27 +27,27 @@ public sealed class Arcs : Scenario
|
|||
("Cockpit 2x2", int2(4, 6), None),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Large Drive", int2(5, 0), Reversed),
|
||||
("Talaria", int2(4, 14), CounterClockwise),
|
||||
("Talaria", int2(5, 14), Clockwise),
|
||||
("Core Power", int2(4, 4), None),
|
||||
("GT 3K", int2(2, 5), None),
|
||||
("GT 3K", int2(7, 5), None),
|
||||
("FastBlast+-", int2(3, 8), None),
|
||||
("FastBlast+-", int2(6, 8), None),
|
||||
("Iapyx", int2(4, 2), CounterClockwise),
|
||||
("Iapyx", int2(5, 2), Clockwise),
|
||||
("not if i see you first", int2(5, 10), None),
|
||||
("Rugged Drive", int2(4, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(5, 14), Clockwise),
|
||||
("Compact Reactor", int2(4, 4), None),
|
||||
("Guided Missile Launcher", int2(2, 5), None),
|
||||
("Guided Missile Launcher", int2(7, 5), None),
|
||||
("Cell Blaster", int2(3, 8), None),
|
||||
("Cell Blaster", int2(6, 8), None),
|
||||
("Radiator", int2(4, 2), CounterClockwise),
|
||||
("Radiator", int2(5, 2), Clockwise),
|
||||
("Compact Sensor", int2(5, 10), None),
|
||||
("Fire Control Array", int2(3, 6), Clockwise),
|
||||
("Autocannon", int2(8, 4), Clockwise),
|
||||
("ChargeBlast SG", int2(0, 4), CounterClockwise));
|
||||
("Charged Scatter Blaster", int2(0, 4), CounterClockwise));
|
||||
var turret = stage.Fit("Turret",
|
||||
("ClearPath", int2(1, 2), None),
|
||||
("ClearPath", int2(5, 2), None),
|
||||
("Skiron", int2(0, 2), CounterClockwise),
|
||||
("Skiron", int2(7, 2), Clockwise),
|
||||
("Core Power", int2(3, 3), None),
|
||||
("Light Machine Gun", int2(1, 2), None),
|
||||
("Light Machine Gun", int2(5, 2), None),
|
||||
("Long Radiator", int2(0, 2), CounterClockwise),
|
||||
("Long Radiator", int2(7, 2), Clockwise),
|
||||
("Compact Reactor", int2(3, 3), None),
|
||||
("Turret Control Module", int2(3, 5), None),
|
||||
("not if i see you first", int2(4, 5), None),
|
||||
("Compact Sensor", int2(4, 5), None),
|
||||
("Fire Control Array", int2(3, 6), None));
|
||||
|
||||
stage.Player(fighter, float2(0, 0), facing: float2(0, 1));
|
||||
|
|
|
|||
|
|
@ -23,16 +23,16 @@ public sealed class Belt : Scenario
|
|||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("GT 3K", int2(0, 5), None),
|
||||
("GT 3K", int2(5, 5), None),
|
||||
("plight", int2(1, 8), None),
|
||||
("FastBlast+-", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Compact Reactor", int2(2, 4), None),
|
||||
("Guided Missile Launcher", int2(0, 5), None),
|
||||
("Guided Missile Launcher", int2(5, 5), None),
|
||||
("Lightning Projector", int2(1, 8), None),
|
||||
("Cell Blaster", int2(4, 8), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise));
|
||||
|
||||
// The belt is a ring about its parent's position at the belt orbit's distance. The player stands 100 m outside
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ using static ItemRotation;
|
|||
public sealed class Crossing : Scenario
|
||||
{
|
||||
public override string Name => "Crossing";
|
||||
public override string Brief => "plight at the starboard gun and a FastBlast+- at the port gun, a fast Djinni crossing close ahead and an " +
|
||||
"unpiloted one waiting at range. The Djinni starts to port moving across the bow; the arena's gravity bends its run. Make the pass coasting: give no throttle until the Djinni has crossed the bow, then fly freely (thrusting from the start carries you past it). Verify: as the Djinni crosses close, FastBlast lands some and plight " +
|
||||
public override string Brief => "A Lightning Projector at the starboard gun and a Cell Blaster at the port gun, a fast Djinni crossing close ahead and an " +
|
||||
"unpiloted one waiting at range. The Djinni starts to port moving across the bow; the arena's gravity bends its run. Make the pass coasting: give no throttle until the Djinni has crossed the bow, then fly freely (thrusting from the start carries you past it). Verify: as the Djinni crosses close, FastBlast lands some and projector " +
|
||||
"almost none; against the Djinni at range both land. Fly it again without the Fire Control Array: " +
|
||||
"with it the small gun lands more.";
|
||||
public override uint Seed => 112;
|
||||
|
|
@ -13,11 +13,11 @@ public sealed class Crossing : Scenario
|
|||
|
||||
// Where the Djinni sit: the player is at the origin facing +z. The crosser starts ahead and to port, facing across
|
||||
// the bow and already moving at the Djinni's 50 m/s cap, so its run sweeps across the bow within seconds (the arena
|
||||
// pulls the player to port, which brings the two together). The waiting Djinni sits inside plight's arc at 80% of
|
||||
// pulls the player to port, which brings the two together). The waiting Djinni sits inside the Lightning Projector's arc at 80% of
|
||||
// its Range.
|
||||
public static readonly CultMath.float2 CrosserAt = float2(-150, 200), CrosserFacing = float2(1, 0), CrosserVelocity = float2(50, 0);
|
||||
public const float PlightRange = 1216f; // plight's Range in metres, 1216.4 as the fit stands settled at rest
|
||||
public static readonly CultMath.float2 Waiting = float2(0, PlightRange * .8f);
|
||||
public const float ProjectorRange = 1216f; // the Lightning Projector's Range in metres, 1216.4 as the fit stands settled at rest
|
||||
public static readonly CultMath.float2 Waiting = float2(0, ProjectorRange * .8f);
|
||||
|
||||
public override Galaxy Generate(GalaxyStage stage) => stage.Prelude(DemoTerminus.Cast);
|
||||
|
||||
|
|
@ -27,16 +27,16 @@ public sealed class Crossing : Scenario
|
|||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("GT 3K", int2(0, 5), None),
|
||||
("GT 3K", int2(5, 5), None),
|
||||
("plight", int2(1, 8), None),
|
||||
("FastBlast+-", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Compact Reactor", int2(2, 4), None),
|
||||
("Guided Missile Launcher", int2(0, 5), None),
|
||||
("Guided Missile Launcher", int2(5, 5), None),
|
||||
("Lightning Projector", int2(1, 8), None),
|
||||
("Cell Blaster", int2(4, 8), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise));
|
||||
|
||||
stage.Player(fighter, float2(0, 0), facing: float2(0, 1));
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using static ItemRotation;
|
|||
public sealed class Duel : Scenario
|
||||
{
|
||||
public override string Name => "Duel";
|
||||
public override string Brief => "One piloted, hostile Longinus with slow GT 3K missiles and a Spectra in its hold. Verify: rolled " +
|
||||
public override string Brief => "One piloted, hostile Longinus with slow guided missiles and a Light Laser in its hold. Verify: rolled " +
|
||||
"hits and near-misses, subsystem aim, beam reads above bow, armour takes the missile, loot keeps its brand.";
|
||||
public override uint Seed => 104;
|
||||
public override bool Ambient => false;
|
||||
|
|
@ -17,22 +17,22 @@ public sealed class Duel : Scenario
|
|||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("GT 3K", int2(0, 5), None),
|
||||
("GT 3K", int2(5, 5), None),
|
||||
("FastBlast+-", int2(1, 8), None),
|
||||
("FastBlast+-", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("PotaT+-", int2(1, 5), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Compact Reactor", int2(2, 4), None),
|
||||
("Guided Missile Launcher", int2(0, 5), None),
|
||||
("Guided Missile Launcher", int2(5, 5), None),
|
||||
("Cell Blaster", int2(1, 8), None),
|
||||
("Cell Blaster", int2(4, 8), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Capacitor", int2(1, 5), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise),
|
||||
("Store-All Plus", int2(2, 8), None));
|
||||
("Cargo Bay", int2(2, 8), None));
|
||||
|
||||
stage.Player(fighter, float2(0, 0));
|
||||
var rival = stage.Place(fighter, float2(0, 900), facing: float2(0, -1), stance: ScenarioStance.Hostile, piloted: true);
|
||||
stage.Cargo(rival, "Spectra");
|
||||
stage.Cargo(rival, "Light Laser");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@ using static ItemRotation;
|
|||
public sealed class FusedRounds : Scenario
|
||||
{
|
||||
public override string Name => "Fused Rounds";
|
||||
public override string Brief => "The Smoke Test Hull with Smoke Proximity, Smoke Delayed and a DeathCluster; a hostile bare Longinus at mid range, a bare " +
|
||||
public override string Brief => "The Smoke Test Hull with Smoke Proximity, Smoke Delayed and a Cluster Cannon; a hostile bare Longinus at mid range, a bare " +
|
||||
"hull dead ahead, a fragile one to starboard, a close one inside the proximity blast radius, and one astern. " +
|
||||
"Verify with nothing selected: aim past every hull and the round bursts at max range along the aim; aim at the hull " +
|
||||
"ahead and it stops and bursts on it; fire the DeathCluster at the fragile hull and, once it is hurt, a Smoke " +
|
||||
"ahead and it stops and bursts on it; fire the Cluster Cannon at the fragile hull and, once it is hurt, a Smoke " +
|
||||
"Proximity round at it: the hull dies before the round lands, no burst. Select the astern hull: the round fires and bursts at its range along the arc edge. Select the " +
|
||||
"close hull: the burst is pushed out to the blast radius from your centre. A Smoke Proximity burst beside the " +
|
||||
"Longinus damages where the model shows the blast; a Smoke Delayed round into its nose bursts inside. The HUD " +
|
||||
"reads Direct, Burst at N m or Refused for the fused weapons and a percentage for the DeathCluster, and the " +
|
||||
"reads Direct, Burst at N m or Refused for the fused weapons and a percentage for the Cluster Cannon, and the " +
|
||||
"smoke weapons show no Manufacturer row.";
|
||||
public override uint Seed => 108;
|
||||
public override bool Ambient => false;
|
||||
|
||||
// Where everything sits: the player is at the origin facing +z. Ahead lies on the initial aim line, inside every forward
|
||||
// mount's arc. Fragile is a bare Longinus inside the DeathCluster's effective range (full damage; the scenario test reads the bound from the weapon); Close is inside Smoke Proximity's blast radius
|
||||
// mount's arc. Fragile is a bare Longinus inside the Cluster Cannon's effective range (full damage; the scenario test reads the bound from the weapon); Close is inside Smoke Proximity's blast radius
|
||||
// (8 m from the ship's centre) and beside the aim line, not across it; Stern is outside every mount's arc.
|
||||
public static readonly CultMath.float2 Mid = float2(-200, 600), Ahead = float2(0, 300), Fragile = float2(80, 240),
|
||||
Close = float2(6.4f, 4f), Stern = float2(0, -400);
|
||||
|
|
@ -30,15 +30,15 @@ public sealed class FusedRounds : Scenario
|
|||
("Cockpit 2x2", int2(4, 6), None),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Large Drive", int2(5, 0), Reversed),
|
||||
("Talaria", int2(4, 14), CounterClockwise),
|
||||
("Talaria", int2(5, 14), Clockwise),
|
||||
("Core Power", int2(4, 4), None),
|
||||
("Rugged Drive", int2(4, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(5, 14), Clockwise),
|
||||
("Compact Reactor", int2(4, 4), None),
|
||||
("Smoke Proximity", int2(3, 8), None),
|
||||
("Smoke Delayed", int2(6, 8), None),
|
||||
("DeathCluster", int2(5, 17), None),
|
||||
("Iapyx", int2(4, 2), CounterClockwise),
|
||||
("Iapyx", int2(5, 2), Clockwise),
|
||||
("not if i see you first", int2(5, 10), None),
|
||||
("Cluster Cannon", int2(5, 17), None),
|
||||
("Radiator", int2(4, 2), CounterClockwise),
|
||||
("Radiator", int2(5, 2), Clockwise),
|
||||
("Compact Sensor", int2(5, 10), None),
|
||||
("Fire Control Array", int2(3, 6), Clockwise));
|
||||
|
||||
stage.Player(shooter, float2(0, 0), facing: float2(0, 1));
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ using static ItemRotation;
|
|||
public sealed class LauncherAngles : Scenario
|
||||
{
|
||||
public override string Name => "Launcher Angles";
|
||||
public override string Brief => "A GT 3K, a pswarm and two FastBlasts; a hostile bare Longinus broadside on, another beside it. " +
|
||||
"Verify: hits pulse only the facing edge; a pswarm round whose target dies fades unexploded.";
|
||||
public override string Brief => "A Guided Missile Launcher, a Swarm Rocket Pod and two Cell Blasters; a hostile bare Longinus broadside on, another beside it. " +
|
||||
"Verify: hits pulse only the facing edge; a Swarm Rocket Pod round whose target dies fades unexploded.";
|
||||
public override uint Seed => 105;
|
||||
public override bool Ambient => false;
|
||||
|
||||
|
|
@ -17,16 +17,16 @@ public sealed class LauncherAngles : Scenario
|
|||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("GT 3K", int2(0, 5), None),
|
||||
("pswarm", int2(5, 5), None),
|
||||
("FastBlast+-", int2(1, 8), None),
|
||||
("FastBlast+-", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Compact Reactor", int2(2, 4), None),
|
||||
("Guided Missile Launcher", int2(0, 5), None),
|
||||
("Swarm Rocket Pod", int2(5, 5), None),
|
||||
("Cell Blaster", int2(1, 8), None),
|
||||
("Cell Blaster", int2(4, 8), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise));
|
||||
|
||||
stage.Player(launcher, float2(0, 0));
|
||||
|
|
|
|||
|
|
@ -24,19 +24,19 @@ public sealed class LockOn : Scenario
|
|||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("GT 3K", int2(0, 5), None),
|
||||
("GT 3K", int2(5, 5), None),
|
||||
("FastBlast+-", int2(1, 8), None),
|
||||
("FastBlast+-", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("PotaT+-", int2(1, 5), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Compact Reactor", int2(2, 4), None),
|
||||
("Guided Missile Launcher", int2(0, 5), None),
|
||||
("Guided Missile Launcher", int2(5, 5), None),
|
||||
("Cell Blaster", int2(1, 8), None),
|
||||
("Cell Blaster", int2(4, 8), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Capacitor", int2(1, 5), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise),
|
||||
("Store-All Plus", int2(2, 8), None));
|
||||
("Cargo Bay", int2(2, 8), None));
|
||||
|
||||
stage.Player(fighter, float2(0, 0), facing: float2(0, 1));
|
||||
stage.Place(fighter, HostileAt, facing: normalize(-HostileAt), stance: ScenarioStance.Hostile, piloted: true);
|
||||
|
|
|
|||
|
|
@ -23,35 +23,35 @@ public sealed class Pursuit : Scenario
|
|||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Compact Reactor", int2(2, 4), None),
|
||||
("Mine Launcher", int2(0, 5), None),
|
||||
("pswarm", int2(5, 5), None),
|
||||
("FastBlast+-", int2(1, 8), None),
|
||||
("FastBlast+-", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("Swarm Rocket Pod", int2(5, 5), None),
|
||||
("Cell Blaster", int2(1, 8), None),
|
||||
("Cell Blaster", int2(4, 8), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise),
|
||||
("Store-All Plus", int2(2, 8), None));
|
||||
("Cargo Bay", int2(2, 8), None));
|
||||
var pursuer = stage.Fit("Longinus",
|
||||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("GT 3K", int2(0, 5), None),
|
||||
("GT 3K", int2(5, 5), None),
|
||||
("FastBlast+-", int2(1, 8), None),
|
||||
("FastBlast+-", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("PotaT+-", int2(1, 5), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Compact Reactor", int2(2, 4), None),
|
||||
("Guided Missile Launcher", int2(0, 5), None),
|
||||
("Guided Missile Launcher", int2(5, 5), None),
|
||||
("Cell Blaster", int2(1, 8), None),
|
||||
("Cell Blaster", int2(4, 8), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Capacitor", int2(1, 5), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise),
|
||||
("Store-All Plus", int2(2, 8), None));
|
||||
("Cargo Bay", int2(2, 8), None));
|
||||
|
||||
var player = stage.Player(layer, float2(0, 0), facing: float2(0, 1));
|
||||
stage.Cargo(player, "Coolant Vent", "Coolant Vent", "Coolant Vent", "Thruster Overdrive", "Thruster Overdrive");
|
||||
|
|
|
|||
|
|
@ -22,16 +22,16 @@ public sealed class RefusedRounds : Scenario
|
|||
("Cockpit 2x2", int2(4, 6), None),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Large Drive", int2(5, 0), Reversed),
|
||||
("Talaria", int2(4, 14), CounterClockwise),
|
||||
("Talaria", int2(5, 14), Clockwise),
|
||||
("Core Power", int2(4, 4), None),
|
||||
("Rugged Drive", int2(4, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(5, 14), Clockwise),
|
||||
("Compact Reactor", int2(4, 4), None),
|
||||
("Smoke Refused", int2(3, 8), None),
|
||||
("Smoke Refused Beam", int2(6, 8), None),
|
||||
("Smoke Refused Charge", int2(3, 17), None),
|
||||
("Autocannon", int2(5, 17), None),
|
||||
("Iapyx", int2(4, 2), CounterClockwise),
|
||||
("Iapyx", int2(5, 2), Clockwise),
|
||||
("not if i see you first", int2(5, 10), None),
|
||||
("Radiator", int2(4, 2), CounterClockwise),
|
||||
("Radiator", int2(5, 2), Clockwise),
|
||||
("Compact Sensor", int2(5, 10), None),
|
||||
("Fire Control Array", int2(3, 6), Clockwise));
|
||||
|
||||
stage.Player(shooter, float2(0, 0), facing: float2(0, 1));
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using static ItemRotation;
|
|||
public sealed class StarvedReactor : Scenario
|
||||
{
|
||||
public override string Name => "Starved Reactor";
|
||||
public override string Brief => "Two Spectras on a MoveOnPro, which can fall to a quarter of Core Power's charge, and a bare hull " +
|
||||
public override string Brief => "Two Light Lasers on a Muon Fusion Reactor, which can fall to a quarter of the Compact Reactor's charge, and a bare hull " +
|
||||
"to shoot. Verify: starvation reads as starvation, brownout degrades, refills stutter.";
|
||||
public override uint Seed => 102;
|
||||
public override bool Ambient => false;
|
||||
|
|
@ -17,14 +17,14 @@ public sealed class StarvedReactor : Scenario
|
|||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("MoveOnPro", int2(2, 4), None),
|
||||
("Spectra", int2(1, 8), None),
|
||||
("Spectra", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("Rugged Drive", int2(2, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(3, 14), Clockwise),
|
||||
("Muon Fusion Reactor", int2(2, 4), None),
|
||||
("Light Laser", int2(1, 8), None),
|
||||
("Light Laser", int2(4, 8), None),
|
||||
("Radiator", int2(2, 2), CounterClockwise),
|
||||
("Radiator", int2(3, 2), Clockwise),
|
||||
("Compact Sensor", int2(3, 10), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise));
|
||||
stage.Player(starved, float2(0, 0));
|
||||
stage.Place(stage.Bare("Longinus"), float2(0, 400));
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ using static ItemRotation;
|
|||
public sealed class WeaponFeel : Scenario
|
||||
{
|
||||
public override string Name => "Weapon Feel";
|
||||
public override string Brief => "The Smoke Test Hull with a ChargeBlast SG, an Autocannon and a FastBlast+- burst weapon; a bare Longinus dead ahead. " +
|
||||
"Verify: ChargeBlast SG and Autocannon fire with effects, particles and action-bar icons; the burst weapon's " +
|
||||
"cadence is as before; a fast release-and-press on the ChargeBlast SG starts no second charge.";
|
||||
public override string Brief => "The Smoke Test Hull with a Charged Scatter Blaster, an Autocannon and a Cell Blaster burst weapon; a bare Longinus dead ahead. " +
|
||||
"Verify: Charged Scatter Blaster and Autocannon fire with effects, particles and action-bar icons; the burst weapon's " +
|
||||
"cadence is as before; a fast release-and-press on the Charged Scatter Blaster starts no second charge.";
|
||||
public override uint Seed => 107;
|
||||
public override bool Ambient => false;
|
||||
|
||||
|
|
@ -21,15 +21,15 @@ public sealed class WeaponFeel : Scenario
|
|||
("Cockpit 2x2", int2(4, 6), None),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Large Drive", int2(5, 0), Reversed),
|
||||
("Talaria", int2(4, 14), CounterClockwise),
|
||||
("Talaria", int2(5, 14), Clockwise),
|
||||
("Core Power", int2(4, 4), None),
|
||||
("ChargeBlast SG", int2(3, 17), None),
|
||||
("Rugged Drive", int2(4, 14), CounterClockwise),
|
||||
("Rugged Drive", int2(5, 14), Clockwise),
|
||||
("Compact Reactor", int2(4, 4), None),
|
||||
("Charged Scatter Blaster", int2(3, 17), None),
|
||||
("Autocannon", int2(5, 17), None),
|
||||
("FastBlast+-", int2(3, 8), None),
|
||||
("Iapyx", int2(4, 2), CounterClockwise),
|
||||
("Iapyx", int2(5, 2), Clockwise),
|
||||
("not if i see you first", int2(5, 10), None),
|
||||
("Cell Blaster", int2(3, 8), None),
|
||||
("Radiator", int2(4, 2), CounterClockwise),
|
||||
("Radiator", int2(5, 2), Clockwise),
|
||||
("Compact Sensor", int2(5, 10), None),
|
||||
("Fire Control Array", int2(3, 6), Clockwise));
|
||||
|
||||
stage.Player(shooter, float2(0, 0), facing: float2(0, 1));
|
||||
|
|
|
|||
|
|
@ -38,11 +38,13 @@ public readonly struct Signature
|
|||
|
||||
// What a painted ship learns of a beam on it (ruling sensor-stat-set item 4): a Search paint is a ping sweeping
|
||||
// it (Strength 0); a Track paint is a lock (Strength is the emitter's own lock progress, 0..1, ruling
|
||||
// lock-warning-strength). Never summed.
|
||||
// lock-warning-strength). A Scan paint is a held scanner: Strength is its scan progress 0..1 (ruling scan-is-an-act).
|
||||
// Never summed.
|
||||
public enum PaintMode
|
||||
{
|
||||
Search,
|
||||
Track
|
||||
Track,
|
||||
Scan
|
||||
}
|
||||
|
||||
// An ally's ping crossed `Subject` at `IlluminatedRange` through `IlluminatedObscuration`; `MaxRange` is that
|
||||
|
|
@ -100,16 +102,17 @@ public readonly struct PaintWarning
|
|||
// bearing at that act and the zone clock then. Held per (subject, item) in the observer's Entity; the latest act
|
||||
// replaces the earlier one. Position is never carried: only the track gives it. `Item` stays the true item: it is the
|
||||
// book's key and what aim reads. `Claimed` is the class the act claimed, and is what every classifying reader reads
|
||||
// (aetheria-ew, classify-from-belief).
|
||||
// (aetheria-ew, classify-from-belief). A null `Channel` is a reveal by the observer's own scan (Entity.RecordInspection),
|
||||
// whose `Claimed` is the subject's claim for the item.
|
||||
public readonly struct Reveal
|
||||
{
|
||||
public readonly EquippedItem Item;
|
||||
public readonly EquippableItemData Claimed;
|
||||
public readonly SignatureChannel Channel;
|
||||
public readonly SignatureChannel? Channel;
|
||||
public readonly float2 Bearing;
|
||||
public readonly float Time;
|
||||
|
||||
public Reveal(EquippedItem item, SignatureChannel channel, float2 bearing, float time, EquippableItemData claimed)
|
||||
public Reveal(EquippedItem item, SignatureChannel? channel, float2 bearing, float time, EquippableItemData claimed)
|
||||
{
|
||||
Item = item;
|
||||
Claimed = claimed;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ public class PlanetSettings
|
|||
public float SecureAreaRadiusMultiplier = .45f;
|
||||
|
||||
public ExponentialCurve OrbitPeriod;
|
||||
|
||||
// The global flow stardust and nebula follow and the sim knows (FlowField). Zero flow by default.
|
||||
public FlowSettings Flow = new FlowSettings();
|
||||
}
|
||||
|
||||
[Serializable, MessagePackObject(keyAsPropertyName:true), JsonObject]
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public class Zone
|
|||
}
|
||||
|
||||
// Zone time at full precision, for the index's orbital keys, which must agree with ChunkPose's own.
|
||||
internal double ExactTime => _time;
|
||||
public double ExactTime => _time;
|
||||
public ZonePack Pack { get; }
|
||||
public GalaxyZone GalaxyZone { get; }
|
||||
public Galaxy Galaxy { get; }
|
||||
|
|
@ -695,6 +695,20 @@ public class Zone
|
|||
return GravityAcceleration(new float2(normal.x, normal.z), Settings.GravityStrength);
|
||||
}
|
||||
|
||||
// The global flow velocity at `position` in zone time, in u/s (FlowField is the law). The slope term reads the
|
||||
// downhill gradient, which GetNormal's xz is proportional to.
|
||||
public float3 Flow(float3 position)
|
||||
{
|
||||
var normal = GetNormal(position.xz);
|
||||
return FlowField.At(Settings.Flow, position, FlowScroll, new float2(normal.x, normal.z));
|
||||
}
|
||||
|
||||
// The flow's noise scroll at zone time (Zone.Flow evaluates at exactly this); lowered to the shaders' _FlowScroll.
|
||||
public float2 FlowScroll => FlowField.Scroll(Settings.Flow, _time);
|
||||
|
||||
// The phase of a cycle of `period` seconds at zone time, for presenters that animate on the flow.
|
||||
public float CyclePhase(float period) => FlowField.Cycle(_time, period);
|
||||
|
||||
public static float PowerPulse(float x, float exponent)
|
||||
{
|
||||
x *= 2;
|
||||
|
|
|
|||
Loading…
Reference in a new issue