This commit is contained in:
itsrubberduck
2025-09-21 17:43:45 +02:00
8 changed files with 72 additions and 13 deletions

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fa09610c1ef12eed0ca70878896f5873308ba1ab34b264d0d04d4eadafcfb89a
size 2146902

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:40b406d3b31ded0eff41024e33b94d018e1449a0411ade3237e85b98002747ef
size 2249127

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b0284350316cb391bec945b26d754c59fa378cd79b7d5d977c2498b8537121f2
size 2005788

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ac94774ef63657715069069446592220163ae318eef81163e8a1fb34f2c8302a
size 2186393

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:188c73efbd184f9a0f1cf4726e0ec62ba61cc482fd2867f70346dd2e86c5bae9
size 2260863

View File

@@ -1,5 +1,5 @@
import { createBaseScenario, createScenarioSeries, formatTemp } from '~~/shared/learn/scenario'
import type { ModuleDef } from '~~/shared/learn/types'
import type { ModuleDef, Scenario } from '~~/shared/learn/types'
function gradientArt(colors: string[]): string {
const stops = colors
@@ -2194,6 +2194,18 @@ const decisionTreeLessons = [
const fullFlightScenario = createScenarioSeries()
export function seedFullFlightScenario(scenario: Scenario | null) {
if (scenario) {
fullFlightScenario.setScenario(scenario)
} else {
fullFlightScenario.setScenario(null)
}
}
export function peekFullFlightScenario(): Scenario | null {
return fullFlightScenario.peek()
}
const makeFullFlightGenerator = (reset = false) => () => {
if (reset) {
fullFlightScenario.reset()
@@ -2950,6 +2962,12 @@ export const learnModules: ModuleDef[] = [
id: 'full-flight',
title: 'Full Flight · Gate to Gate',
subtitle: 'One linked scenario from clearance to taxi-in',
art: '/img/learn/modules/img6.jpeg',
lessons: fullFlightLessons,
meta: {
flightPlan: true,
briefingArt: '/img/learn/missions/full-flight/briefing-hero.png'
}
art: '/img/learn/modules/img12.jpeg',
lessons: fullFlightLessons
}

View File

@@ -198,7 +198,7 @@ function generateSquawk(): string {
return code
}
function digitsToWords(value: string): string {
export function digitsToWords(value: string): string {
return value
.split('')
.map(char => atcNumberWords[char] ?? char)
@@ -207,7 +207,7 @@ function digitsToWords(value: string): string {
.trim()
}
function lettersToNato(value: string): string {
export function lettersToNato(value: string): string {
return value
.toUpperCase()
.split('')
@@ -215,7 +215,7 @@ function lettersToNato(value: string): string {
.join(' ')
}
function runwayToWords(runway: string): string {
export function runwayToWords(runway: string): string {
const digits = runway.replace(/[^0-9]/g, '').padStart(2, '0')
const suffix = runway.replace(/[0-9]/g, '')
const base = digits
@@ -274,17 +274,17 @@ function visibilityToWords(vis: string): string {
return vis
}
function minutesToWords(minutes: number): string {
export function minutesToWords(minutes: number): string {
const value = Math.max(1, Math.round(minutes))
const unit = value === 1 ? 'minute' : 'minutes'
return `${digitsToWords(value.toString())} ${unit}`
}
function speedToWords(speed: number): string {
export function speedToWords(speed: number): string {
return `${digitsToWords(Math.round(speed).toString())} knots`
}
function altitudeToWords(value: number): string {
export function altitudeToWords(value: number): string {
const thousands = Math.floor(value / 1000)
const remainder = value % 1000
let words = thousands ? `${digitsToWords(thousands.toString())} thousand` : ''
@@ -491,18 +491,38 @@ function cloneScenario(scenario: Scenario): Scenario {
return JSON.parse(JSON.stringify(scenario)) as Scenario
}
export type ScenarioGenerator = (() => Scenario) & { reset: () => void }
export type ScenarioGenerator = (() => Scenario) & {
reset: () => void
setScenario: (scenario: Scenario | null) => void
peek: () => Scenario | null
}
export function createScenarioSeries(): ScenarioGenerator {
export function createScenarioSeries(source?: () => Scenario): ScenarioGenerator {
let cached: Scenario | null = null
const generator = () => {
let override: Scenario | null = null
const resolveSource = () => {
if (override) {
return cloneScenario(override)
}
return source ? source() : createBaseScenario()
}
const generator = (() => {
if (!cached) {
cached = createBaseScenario()
cached = resolveSource()
}
return cloneScenario(cached)
}
}) as ScenarioGenerator
generator.reset = () => {
cached = null
cached = override ? cloneScenario(override) : null
}
generator.setScenario = (scenario: Scenario | null) => {
override = scenario ? cloneScenario(scenario) : null
cached = override ? cloneScenario(override) : null
}
generator.peek = () => {
if (cached) return cloneScenario(cached)
if (override) return cloneScenario(override)
return null
}
return generator
}

View File

@@ -161,10 +161,16 @@ export type Lesson = {
generate: () => Scenario
}
export type ModuleMeta = {
flightPlan?: boolean
briefingArt?: string
}
export type ModuleDef = {
id: string
title: string
subtitle: string
art: string
lessons: Lesson[]
meta?: ModuleMeta
}