// Settings — the runtime config the renderer reads. The .saver host populates
// this from ScreenSaverDefaults; the harness from CLI args.
struct Settings {
enum Speed: String, CaseIterable {
case slow, norm, fast
var multiplier: Float {
switch self {
case .slow: return 0.32
case .norm: return 1
case .fast: return 2.8
}
}
}
enum Altitude: String, CaseIterable {
case low, mid, high
var value: Float { switch self { case .low: return 4; case .mid: return 11; case .high: return 24 } }
}
enum Fog: String, CaseIterable {
case clear, auto, dense
var mul: Float { switch self { case .clear: return 0.45; case .auto: return 1; case .dense: return 2.2 } }
}
var scene: String = "terrain"
var presetID: String = "toxic-haze"
var speed: Speed = .norm
var cityAltitude: Altitude = .low
var showHud = true // HUD panels (flight data, crosshair, scene label)
var showRadar = true // tactical radar (bottom-right); gated also by showHud
var flatHud = false // draw HUD + terminal flat AFTER the CRT composite (crisp, uncurved) vs in-monitor
var reducedMotion = false // a11y: route scene transitions through the CALM profile (set by the app host from NSWorkspace)
var bootEnabled = true // BIOS-style boot overlay on startup, then CRT power-on
enum BootSpeed: String, CaseIterable { case fast, normal, cinematic }
var bootSpeed: BootSpeed = .normal
enum TerminalLayout: String, CaseIterable { case off, strip, stripDark = "stripdark", window }
var terminalLayout: TerminalLayout = .strip // narrative terminal: off / strip / full-width band / Lisa window
var terminalBandHeight: Float = 0.24 // band layout: fraction of screen height it occupies
var bloomScale: Float = 1 // multiplier on the per-preset bloom strength (0 = off)
var fog: Fog = .auto
var curvature: Float = 0.012 // CRT screen barrel curvature (0 = flat; gentle by design)
var apertureMask: Float = 0.22 // RGB phosphor grille strength (0 = off)
var trails: Float = 0.35 // phosphor persistence decay (0 = off, ~0.9 = long)
var terminalScale: Float = 1 // terminal text size multiplier
var ntsc: Float = 0 // NTSC/composite shimmer (0 = off)
var halation: Float = 0.15 // warm glow bleed around bright areas
var dayNight = false // slow brightness/bloom day-night cycle
var matrix = false // matrix-rain background
var reactiveWorld = true // the 3D world reacts to the narrative shift phase (fog/CRT escalation)
enum Weather: String, CaseIterable {
case clear, lightFog = "light-fog", heavyFog = "heavy-fog", storm, dust
/// Fog factor, mirroring controller.ts:363. The web folds weather into
/// baseFogDensity (scene default 0.01): 'clear' sets an absolute 0.004,
/// 'heavy-fog' multiplies by 2.5, 'light-fog' leaves the scene default.
var fogMul: Float {
switch self {
case .lightFog: return 1 // the no-op case — what .clear used to be here
case .clear: return 0.4 // web: 0.004 / 0.01
case .heavyFog: return 2.5
case .storm: return 1.7
case .dust: return 2.4
}
}
}
var weather: Weather = .lightFog
var bankStrength: Float = 1 // flight banking intensity (0 = level flight, 2 = aggressive)
var crtIntensity: Float = 0.35 // 0..1, drives the glitch scheduler cadence
var tunnelAutoBoost: Bool = true // tunnel: speed surge on straight sections
var seed: Int32? = nil // nil → fresh per activation
var preset: ColorPreset { Palette.preset(presetID) }
}