diff --git a/public/img/learn/missions/full-flight/briefing-arrival.png b/public/img/learn/missions/full-flight/briefing-arrival.png new file mode 100644 index 0000000..45799b7 --- /dev/null +++ b/public/img/learn/missions/full-flight/briefing-arrival.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa09610c1ef12eed0ca70878896f5873308ba1ab34b264d0d04d4eadafcfb89a +size 2146902 diff --git a/public/img/learn/missions/full-flight/briefing-departure.png b/public/img/learn/missions/full-flight/briefing-departure.png new file mode 100644 index 0000000..a378df8 --- /dev/null +++ b/public/img/learn/missions/full-flight/briefing-departure.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40b406d3b31ded0eff41024e33b94d018e1449a0411ade3237e85b98002747ef +size 2249127 diff --git a/public/img/learn/missions/full-flight/briefing-hero.png b/public/img/learn/missions/full-flight/briefing-hero.png new file mode 100644 index 0000000..277ee85 --- /dev/null +++ b/public/img/learn/missions/full-flight/briefing-hero.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0284350316cb391bec945b26d754c59fa378cd79b7d5d977c2498b8537121f2 +size 2005788 diff --git a/public/img/learn/missions/full-flight/briefing-route.png b/public/img/learn/missions/full-flight/briefing-route.png new file mode 100644 index 0000000..5070adf --- /dev/null +++ b/public/img/learn/missions/full-flight/briefing-route.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac94774ef63657715069069446592220163ae318eef81163e8a1fb34f2c8302a +size 2186393 diff --git a/public/img/learn/missions/full-flight/briefing-weather.png b/public/img/learn/missions/full-flight/briefing-weather.png new file mode 100644 index 0000000..41e1233 --- /dev/null +++ b/public/img/learn/missions/full-flight/briefing-weather.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:188c73efbd184f9a0f1cf4726e0ec62ba61cc482fd2867f70346dd2e86c5bae9 +size 2260863 diff --git a/shared/data/learnModules.ts b/shared/data/learnModules.ts index dae8b0e..50f2773 100644 --- a/shared/data/learnModules.ts +++ b/shared/data/learnModules.ts @@ -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 } diff --git a/shared/learn/scenario.ts b/shared/learn/scenario.ts index ed8800c..8adefac 100644 --- a/shared/learn/scenario.ts +++ b/shared/learn/scenario.ts @@ -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 } diff --git a/shared/learn/types.ts b/shared/learn/types.ts index e957b84..e5a3aad 100644 --- a/shared/learn/types.ts +++ b/shared/learn/types.ts @@ -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 }