@fboes/aerofly-custom-missions 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.editorconfig +1 -5
  2. package/.eslintrc.json +24 -0
  3. package/CHANGELOG.md +59 -21
  4. package/dist/dto/AeroflyLocalizedText.js +44 -0
  5. package/dist/dto/AeroflyMission.js +173 -0
  6. package/dist/dto/AeroflyMissionCheckpoint.js +127 -0
  7. package/dist/dto/AeroflyMissionConditions.js +124 -0
  8. package/dist/dto/AeroflyMissionConditionsCloud.js +90 -0
  9. package/dist/dto/AeroflyMissionTargetPlane.js +35 -0
  10. package/dist/dto/AeroflyMissionsList.js +36 -0
  11. package/dist/index.js +7 -634
  12. package/dist/index.test.js +17 -11
  13. package/dist/node/AeroflyConfigurationNode.js +82 -0
  14. package/docs/custom_missions_user.tmc +3 -6
  15. package/docs/custom_missions_user.xml +100 -0
  16. package/package.json +9 -10
  17. package/src/dto/AeroflyLocalizedText.ts +74 -0
  18. package/src/dto/AeroflyMission.ts +372 -0
  19. package/src/dto/AeroflyMissionCheckpoint.ts +234 -0
  20. package/src/dto/AeroflyMissionConditions.ts +189 -0
  21. package/src/dto/AeroflyMissionConditionsCloud.ts +111 -0
  22. package/src/dto/AeroflyMissionTargetPlane.ts +62 -0
  23. package/src/dto/AeroflyMissionsList.ts +52 -0
  24. package/src/index.test.ts +22 -25
  25. package/src/index.ts +7 -1099
  26. package/src/node/AeroflyConfigurationNode.ts +89 -0
  27. package/types/AeroflyMissionCheckpoint.d.ts +140 -0
  28. package/types/AeroflyMissionTargetPlane.d.ts +40 -0
  29. package/types/dto/AeroflyConfigFileSet.d.ts +10 -0
  30. package/types/dto/AeroflyConfigFileSet.d.ts.map +1 -0
  31. package/types/dto/AeroflyConfiguration.interface.d.ts +4 -0
  32. package/types/dto/AeroflyConfiguration.interface.d.ts.map +1 -0
  33. package/types/dto/AeroflyLocalizedText.d.ts +58 -0
  34. package/types/dto/AeroflyLocalizedText.d.ts.map +1 -0
  35. package/types/dto/AeroflyMission.d.ts +163 -0
  36. package/types/dto/AeroflyMission.d.ts.map +1 -0
  37. package/types/dto/AeroflyMissionCheckpoint.d.ts +126 -0
  38. package/types/dto/AeroflyMissionCheckpoint.d.ts.map +1 -0
  39. package/types/dto/AeroflyMissionConditions.d.ts +102 -0
  40. package/types/dto/AeroflyMissionConditions.d.ts.map +1 -0
  41. package/types/dto/AeroflyMissionConditionsCloud.d.ts +57 -0
  42. package/types/dto/AeroflyMissionConditionsCloud.d.ts.map +1 -0
  43. package/types/dto/AeroflyMissionTargetPlane.d.ts +45 -0
  44. package/types/dto/AeroflyMissionTargetPlane.d.ts.map +1 -0
  45. package/types/dto/AeroflyMissionsList.d.ts +30 -0
  46. package/types/dto/AeroflyMissionsList.d.ts.map +1 -0
  47. package/types/dto/basicTypes.d.ts +1 -0
  48. package/types/index.d.ts +7 -585
  49. package/types/index.d.ts.map +1 -1
  50. package/types/node/AeroflyConfigurationNode.d.ts +14 -0
  51. package/types/node/AeroflyConfigurationNode.d.ts.map +1 -0
  52. package/eslint.config.js +0 -29
package/src/index.ts CHANGED
@@ -1,1099 +1,7 @@
1
- const feetPerMeter = 3.28084;
2
- const meterPerStatuteMile = 1609.344;
3
-
4
- /**
5
- * Represents origin or destination conditions for flight
6
- * @property icao uppercase ICAO airport ID
7
- * @property longitude easting, using the World Geodetic
8
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
9
- * of decimal degrees; -180..180
10
- * @property latitude northing, using the World Geodetic
11
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
12
- * of decimal degrees; -90..90
13
- * @property dir in degree
14
- * @property alt the height in meters above or below the WGS
15
- * 84 reference ellipsoid
16
- */
17
- export type AeroflyMissionPosition = {
18
- icao: string;
19
- longitude: number;
20
- latitude: number;
21
- dir: number;
22
- alt: number;
23
- };
24
-
25
- /**
26
- * State of aircraft systems. Configures power settings, flap positions etc
27
- */
28
- export type AeroflyMissionSetting =
29
- | "cold_and_dark"
30
- | "before_start"
31
- | "taxi"
32
- | "takeoff"
33
- | "cruise"
34
- | "approach"
35
- | "landing"
36
- | "winch_launch"
37
- | "aerotow"
38
- | "pushback";
39
-
40
- /**
41
- * Types of checkpoints. Required are usually "origin", "departure_runway" at the start and "destination_runway", "destination" at the end.
42
- */
43
- export type AeroflyMissionCheckpointType =
44
- | "origin"
45
- | "departure_runway"
46
- | "departure"
47
- | "waypoint"
48
- | "arrival"
49
- | "approach"
50
- | "destination_runway"
51
- | "destination";
52
-
53
- /**
54
- * Data for the aircraft to use on this mission
55
- * @property name lowercase Aerofly aircraft ID
56
- * @property icao ICAO aircraft code
57
- * @property livery (not used yet)
58
- */
59
- export type AeroflyMissionAircraft = {
60
- name: string;
61
- icao: string;
62
- livery: string;
63
- };
64
-
65
- /**
66
- * Weather data for wind
67
- * @property direction in degree
68
- * @property speed in kts
69
- * @property gusts in kts
70
- */
71
- export type AeroflyMissionConditionsWind = {
72
- direction: number;
73
- speed: number;
74
- gusts: number;
75
- };
76
-
77
- /**
78
- * Cloud coverage codes
79
- */
80
- export type AeroflyMissionConditionsCloudCoverCode = "CLR" | "FEW" | "SCT" | "BKN" | "OVC";
81
-
82
- export class AeroflyConfigFileSet {
83
- #indent: number;
84
- elements: string[];
85
-
86
- constructor(indent: number, type: string, name: string, value: string = "") {
87
- this.#indent = indent;
88
- this.elements = [`${this.indent}<[${type}][${name}][${value}]`];
89
- }
90
-
91
- get indent(): string {
92
- return " ".repeat(this.#indent);
93
- }
94
-
95
- push(type: string, name: string, value: string | number | string[] | number[] | boolean, comment: string = "") {
96
- if (value instanceof Array) {
97
- value = value.join(" ");
98
- } else if (typeof value === "boolean") {
99
- value = value ? "true" : "false";
100
- }
101
- let tag = `${this.indent} <[${type}][${name}][${value}]>`;
102
- if (comment) {
103
- tag += ` // ${comment}`;
104
- }
105
- return this.pushRaw(tag);
106
- }
107
-
108
- pushRaw(s: string) {
109
- this.elements.push(s);
110
- return this;
111
- }
112
-
113
- toString() {
114
- return this.elements.join("\n") + "\n" + `${this.indent}>`;
115
- }
116
- }
117
-
118
- /**
119
- * @class
120
- * A list of flight plans.
121
- *
122
- * The purpose of this class is to collect data needed for Aerofly FS4's
123
- * `custom_missions_user.tmc` flight plan file format, and export the structure
124
- * for this file via the `toString()` method.
125
- */
126
- export class AeroflyMissionsList {
127
- /**
128
- * @property {AeroflyMission[]} missions in this mission list
129
- */
130
- missions: AeroflyMission[];
131
-
132
- /**
133
- * @param {AeroflyMission[]} missions in this mission list
134
- */
135
- constructor(missions: AeroflyMission[] = []) {
136
- this.missions = missions;
137
- }
138
-
139
- /**
140
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
141
- */
142
- toString(): string {
143
- const separator = "\n// -----------------------------------------------------------------------------\n";
144
- return `\
145
- <[file][][]
146
- <[tmmissions_list][][]
147
- <[list_tmmission_definition][missions][]${separator + this.missions.join(separator) + separator} >
148
- >
149
- >`;
150
- }
151
- }
152
-
153
- /**
154
- * @class
155
- * A single flighplan, containing aircraft and weather data as well.
156
- *
157
- * The purpose of this class is to collect data needed for Aerofly FS4's
158
- * `custom_missions_user.tmc` flight plan file format, and export the structure
159
- * for this file via the `toString()` method.
160
- */
161
- export class AeroflyMission {
162
- /**
163
- * @property {?string} tutorialName will create a link to a tutorial page at https://www.aerofly.com/aircraft-tutorials/
164
- */
165
- tutorialName: string | null;
166
-
167
- /**
168
- * @property {string} title of this flight plan
169
- */
170
- title: string;
171
-
172
- /**
173
- * @property {string} description text, mission briefing, etc
174
- */
175
- description: string;
176
-
177
- /**
178
- * @property {AeroflyLocalizedText[]} localizedTexts for title and description
179
- */
180
- localizedTexts: AeroflyLocalizedText[];
181
-
182
- /**
183
- * @property {string[]} tags
184
- */
185
- tags: string[];
186
-
187
- /**
188
- * @property {?boolean} isFeatured makes this mission pop up in "Challenges"
189
- */
190
- isFeatured: boolean | null;
191
-
192
- /**
193
- * @property {?number} difficulty values between 0.00 and 2.00 have been encountered, but they seem to be without limit
194
- */
195
- difficulty: number | null;
196
-
197
- /**
198
- * @property {"cold_and_dark"|"before_start"|"taxi"|"takeoff"|"cruise"|"approach"|"landing"|"winch_launch"|"aerotow"|"pushback"} flightSetting of aircraft, like "taxi", "cruise"
199
- */
200
- flightSetting: AeroflyMissionSetting;
201
-
202
- /**
203
- * @property {object} aircraft for this mission
204
- */
205
- aircraft: AeroflyMissionAircraft;
206
-
207
- /**
208
- * @property {string} callsign of aircraft, uppercased
209
- */
210
- callsign: string;
211
-
212
- /**
213
- * @property {object} origin position of aircraft, as well as name of starting airport. Position does not have match airport.
214
- */
215
- origin: AeroflyMissionPosition;
216
-
217
- /**
218
- * @property {object} destination position of aircraft, as well as name of destination airport. Position does not have match airport.
219
- */
220
- destination: AeroflyMissionPosition;
221
-
222
- /**
223
- * @property {?number} distance in meters
224
- */
225
- distance: number | null;
226
-
227
- /**
228
- * @property {?number} duration in seconds
229
- */
230
- duration: number | null;
231
-
232
- /**
233
- * @property {?boolean} isScheduled marks this flight as "Scheduled flight".
234
- */
235
- isScheduled: boolean | null;
236
-
237
- /**
238
- * @property {?AeroflyMissionTargetPlane} finish as finish condition
239
- */
240
- finish: AeroflyMissionTargetPlane | null;
241
-
242
- /**
243
- * @property {AeroflyMissionConditions} conditions like time and weather for mission
244
- */
245
- conditions: AeroflyMissionConditions;
246
-
247
- /**
248
- * @property {AeroflyMissionConditions} checkpoints form the actual flight plan
249
- */
250
- checkpoints: AeroflyMissionCheckpoint[];
251
-
252
- /**
253
- * @param {string} title of this flight plan
254
- * @param {object} [additionalAttributes] allows to set additional attributes on creation
255
- * @param {string} [additionalAttributes.description] text, mission briefing, etc
256
- * @param {AeroflyLocalizedText[]} [additionalAttributes.localizedTexts] translations for title and description
257
- * @param {?string} [additionalAttributes.tutorialName] will create a link to a tutorial page at https://www.aerofly.com/aircraft-tutorials/
258
- * @param {string[]} [additionalAttributes.tags]
259
- * @param {?boolean} [additionalAttributes.isFeatured] makes this mission pop up in "Challenges"
260
- * @param {?number} [additionalAttributes.difficulty] values between 0.00 and 2.00 have been encountered, but they seem to be without limit
261
- * @param {"cold_and_dark"|"before_start"|"taxi"|"takeoff"|"cruise"|"approach"|"landing"|"winch_launch"|"aerotow"|"pushback"} [additionalAttributes.flightSetting] of aircraft, like "taxi", "cruise"
262
- * @param {{name:string,livery:string,icao:string}} [additionalAttributes.aircraft] for this mission
263
- * @param {string} [additionalAttributes.callsign] of aircraft, uppercased
264
- * @param {object} [additionalAttributes.origin] position of aircraft, as well as name of starting airport. Position does not have match airport.
265
- * @param {object} [additionalAttributes.destination] position of aircraft, as well as name of destination airport. Position does not have match airport.
266
- * @param {?number} [additionalAttributes.distance] in meters
267
- * @param {?number} [additionalAttributes.duration] in seconds
268
- * @param {?boolean} [additionalAttributes.isScheduled] marks this flight as "Scheduled flight".
269
- * @param {?AeroflyMissionTargetPlane} [additionalAttributes.finish] as finish condition
270
- * @param {AeroflyMissionConditions} [additionalAttributes.conditions] like time and weather for mission
271
- * @param {AeroflyMissionCheckpoint[]} [additionalAttributes.checkpoints] form the actual flight plan
272
- */
273
- constructor(
274
- title: string,
275
- {
276
- tutorialName = null,
277
- description = "",
278
- localizedTexts = [],
279
- tags = [],
280
- isFeatured = null,
281
- difficulty = null,
282
- flightSetting = "taxi",
283
- aircraft = {
284
- name: "c172",
285
- icao: "",
286
- livery: "",
287
- },
288
- callsign = "",
289
- origin = {
290
- icao: "",
291
- longitude: 0,
292
- latitude: 0,
293
- dir: 0,
294
- alt: 0,
295
- },
296
- destination = {
297
- icao: "",
298
- longitude: 0,
299
- latitude: 0,
300
- dir: 0,
301
- alt: 0,
302
- },
303
- distance = null,
304
- duration = null,
305
- isScheduled = null,
306
- finish = null,
307
- conditions = new AeroflyMissionConditions(),
308
- checkpoints = [],
309
- }: {
310
- tutorialName?: string | null;
311
- description?: string;
312
- localizedTexts?: AeroflyLocalizedText[];
313
- tags?: string[];
314
- isFeatured?: boolean | null;
315
- difficulty?: number | null;
316
- flightSetting?: AeroflyMissionSetting;
317
- aircraft?: AeroflyMissionAircraft;
318
- callsign?: string;
319
- origin?: AeroflyMissionPosition;
320
- destination?: AeroflyMissionPosition;
321
- distance?: number | null;
322
- duration?: number | null;
323
- isScheduled?: boolean | null;
324
- finish?: AeroflyMissionTargetPlane | null;
325
- conditions?: AeroflyMissionConditions;
326
- checkpoints?: AeroflyMissionCheckpoint[];
327
- } = {},
328
- ) {
329
- this.tutorialName = tutorialName;
330
- this.title = title;
331
- this.checkpoints = checkpoints;
332
- this.description = description;
333
- this.localizedTexts = localizedTexts;
334
- this.tags = tags;
335
- this.isFeatured = isFeatured;
336
- this.difficulty = difficulty;
337
- this.flightSetting = flightSetting;
338
- this.aircraft = aircraft;
339
- this.callsign = callsign;
340
- this.origin = origin;
341
- this.destination = destination;
342
- this.distance = distance;
343
- this.duration = duration;
344
- this.isScheduled = isScheduled;
345
- this.finish = finish;
346
- this.conditions = conditions;
347
- }
348
-
349
- /**
350
- * @returns {string} indexed checkpoints
351
- */
352
- getCheckpointsString(): string {
353
- return this.checkpoints
354
- .map((c: AeroflyMissionCheckpoint, index: number): string => {
355
- return c.toString(index);
356
- })
357
- .join("\n");
358
- }
359
-
360
- /**
361
- * @returns {string} indexed checkpoints
362
- */
363
- getLocalizedTextsString(): string {
364
- return this.localizedTexts
365
- .map((c: AeroflyLocalizedText, index: number): string => {
366
- return c.toString(index);
367
- })
368
- .join("\n");
369
- }
370
-
371
- /**
372
- * @throws {Error} on missing waypoints
373
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
374
- */
375
- toString(): string {
376
- if (!this.origin.icao) {
377
- const firstCheckpoint = this.checkpoints[0];
378
- this.origin = {
379
- icao: firstCheckpoint.name,
380
- longitude: firstCheckpoint.longitude,
381
- latitude: firstCheckpoint.latitude,
382
- dir: this.origin.dir,
383
- alt: firstCheckpoint.altitude,
384
- };
385
- }
386
-
387
- if (!this.destination.icao) {
388
- const lastCheckpoint = this.checkpoints[this.checkpoints.length - 1];
389
- this.destination = {
390
- icao: lastCheckpoint.name,
391
- longitude: lastCheckpoint.longitude,
392
- latitude: lastCheckpoint.latitude,
393
- dir: lastCheckpoint.direction ?? 0,
394
- alt: lastCheckpoint.altitude,
395
- };
396
- }
397
-
398
- const fileSet = new AeroflyConfigFileSet(3, "tmmission_definition", "mission");
399
- fileSet.push("string8", "title", this.title);
400
- fileSet.push("string8", "description", this.description);
401
-
402
- if (this.tutorialName !== null) {
403
- fileSet.push(
404
- "string8",
405
- "tutorial_name",
406
- this.tutorialName,
407
- `Opens https://www.aerofly.com/aircraft-tutorials/${this.tutorialName}`,
408
- );
409
- }
410
- if (this.localizedTexts.length > 0) {
411
- fileSet.pushRaw(
412
- new AeroflyConfigFileSet(4, "list_tmmission_definition_localized", "localized_text")
413
- .pushRaw(this.getLocalizedTextsString())
414
- .toString(),
415
- );
416
- }
417
- if (this.tags.length > 0) {
418
- fileSet.push("string8u", "tags", this.tags.join(" "));
419
- }
420
- if (this.difficulty !== null) {
421
- fileSet.push("float64", "difficulty", this.difficulty);
422
- }
423
- if (this.isFeatured !== null) {
424
- fileSet.push("bool", "is_featured", this.isFeatured);
425
- }
426
-
427
- fileSet.push("string8", "flight_setting", this.flightSetting);
428
- fileSet.push("string8u", "aircraft_name", this.aircraft.name);
429
- /*if (this.aircraft.livery) {
430
- fileSet.push("string8", "aircraft_livery", this.aircraft.livery);
431
- }*/
432
- fileSet.push("stringt8c", "aircraft_icao", this.aircraft.icao);
433
- fileSet.push("stringt8c", "callsign", this.callsign);
434
- fileSet.push("stringt8c", "origin_icao", this.origin.icao);
435
- fileSet.push("tmvector2d", "origin_lon_lat", [this.origin.longitude, this.origin.latitude]);
436
- fileSet.push("float64", "origin_alt", this.origin.alt, `${Math.ceil(this.origin.alt * feetPerMeter)} ft MSL`);
437
- fileSet.push("float64", "origin_dir", this.origin.dir);
438
- fileSet.push("stringt8c", "destination_icao", this.destination.icao);
439
- fileSet.push("tmvector2d", "destination_lon_lat", [this.destination.longitude, this.destination.latitude]);
440
- fileSet.push(
441
- "float64",
442
- "destination_alt",
443
- this.destination.alt,
444
- `${Math.ceil(this.destination.alt * feetPerMeter)} ft MSL`,
445
- );
446
- fileSet.push("float64", "destination_dir", this.destination.dir);
447
-
448
- if (this.distance !== null) {
449
- fileSet.push("float64", "distance", this.distance, `${Math.round(this.distance / 1000)} km`);
450
- }
451
- if (this.duration !== null) {
452
- fileSet.push("float64", "duration", this.duration, `${Math.round(this.duration / 60)} min`);
453
- }
454
- if (this.isScheduled !== null) {
455
- fileSet.push("bool", "is_scheduled", this.isScheduled ? "true" : "false");
456
- }
457
- if (this.finish !== null) {
458
- fileSet.pushRaw(this.finish.toString());
459
- }
460
-
461
- fileSet.pushRaw(this.conditions.toString());
462
- if (this.checkpoints.length > 0) {
463
- fileSet.pushRaw(
464
- new AeroflyConfigFileSet(4, "list_tmmission_checkpoint", "checkpoints")
465
- .pushRaw(this.getCheckpointsString())
466
- .toString(),
467
- );
468
- }
469
-
470
- return fileSet.toString();
471
- }
472
- }
473
-
474
- /**
475
- * @class
476
- * Time and weather data for the given flight plan
477
- *
478
- * The purpose of this class is to collect data needed for Aerofly FS4's
479
- * `custom_missions_user.tmc` flight plan file format, and export the structure
480
- * for this file via the `toString()` method.
481
- */
482
- export class AeroflyMissionConditions {
483
- /**
484
- * @property {Date} time of flight plan. Relevant is the UTC part, so
485
- * consider setting this date in UTC.
486
- */
487
- time: Date;
488
-
489
- /**
490
- * @property {object} wind state
491
- */
492
- wind: AeroflyMissionConditionsWind;
493
-
494
- /**
495
- * @property {number} 0..1, percentage
496
- */
497
- turbulenceStrength: number;
498
-
499
- /**
500
- * @property {number} 0..1, percentage
501
- */
502
- thermalStrength: number;
503
-
504
- /**
505
- * @property {number} visibility in meters
506
- */
507
- visibility: number;
508
-
509
- /**
510
- * @property {AeroflyMissionConditionsCloud[]} clouds for the whole flight
511
- */
512
- clouds: AeroflyMissionConditionsCloud[] = [];
513
-
514
- /**
515
- * @param {object} additionalAttributes allows to set additional attributes on creation
516
- * @param {Date} [additionalAttributes.time] of flight plan. Relevant is the UTC part, so
517
- * consider setting this date in UTC.
518
- * @param {{direction: number, speed: number, gusts: number}} [additionalAttributes.wind] state
519
- * @param {number} [additionalAttributes.turbulenceStrength] 0..1, percentage
520
- * @param {number} [additionalAttributes.thermalStrength] 0..1, percentage
521
- * @param {number} [additionalAttributes.visibility] in meters
522
- * @param {?number} [additionalAttributes.visibility_sm] in statute miles, will overwrite visibility
523
- * @param {?number} [additionalAttributes.temperature] in °C, will overwrite thermalStrength
524
- * @param {AeroflyMissionConditionsCloud[]} [additionalAttributes.clouds] for the whole flight
525
- */
526
- constructor({
527
- time = new Date(),
528
- wind = {
529
- direction: 0,
530
- speed: 0,
531
- gusts: 0,
532
- },
533
- turbulenceStrength = 0,
534
- thermalStrength = 0,
535
- visibility = 25_000,
536
- visibility_sm = null,
537
- temperature = null,
538
- clouds = [],
539
- }: {
540
- time?: Date;
541
- wind?: {
542
- direction: number;
543
- speed: number;
544
- gusts: number;
545
- };
546
- turbulenceStrength?: number;
547
- thermalStrength?: number;
548
- visibility?: number;
549
- visibility_sm?: number | null;
550
- temperature?: number | null;
551
- clouds?: AeroflyMissionConditionsCloud[];
552
- } = {}) {
553
- this.time = time;
554
- this.wind = wind;
555
- this.turbulenceStrength = turbulenceStrength;
556
- this.thermalStrength = thermalStrength;
557
- this.visibility = visibility;
558
- this.clouds = clouds;
559
-
560
- if (visibility_sm) {
561
- this.visibility_sm = visibility_sm;
562
- }
563
- if (temperature) {
564
- this.temperature = temperature;
565
- }
566
- }
567
-
568
- /**
569
- * @returns {number} the Aerofly value for UTC hours + minutes/60 + seconds/3600. Ignores milliseconds ;)
570
- */
571
- get time_hours(): number {
572
- return this.time.getUTCHours() + this.time.getUTCMinutes() / 60 + this.time.getUTCSeconds() / 3600;
573
- }
574
-
575
- /**
576
- * @returns {string} Time representation like "20:15:00"
577
- */
578
- get time_presentational(): string {
579
- return [this.time.getUTCHours(), this.time.getUTCMinutes(), this.time.getUTCSeconds()]
580
- .map((t) => {
581
- return String(t).padStart(2, "0");
582
- })
583
- .join(":");
584
- }
585
-
586
- /**
587
- * @param {number} visibility_sm `this.visibility` in statute miles instead of meters
588
- */
589
- set visibility_sm(visibility_sm: number) {
590
- this.visibility = visibility_sm * meterPerStatuteMile;
591
- }
592
-
593
- /**
594
- * @returns {number} `this.visibility` in statute miles instead of meters
595
- */
596
- get visibility_sm(): number {
597
- return this.visibility / meterPerStatuteMile;
598
- }
599
-
600
- /**
601
- * Will set `this.thermalStrength`
602
- * @param {number} temperature in °C
603
- */
604
- set temperature(temperature: number) {
605
- // Range from -15°C to 35°C
606
- this.thermalStrength = Math.max(0, (temperature + 15) / 50) ** 2;
607
- }
608
-
609
- /**
610
- * @returns {number} in °C
611
- */
612
- get temperature(): number {
613
- return Math.sqrt(this.thermalStrength) * 50 - 15;
614
- }
615
-
616
- /**
617
- * @returns {string}
618
- */
619
- getCloudsString(): string {
620
- return this.clouds
621
- .map((c: AeroflyMissionConditionsCloud, index: number): string => {
622
- return c.toString(index);
623
- })
624
- .join("\n");
625
- }
626
-
627
- /**
628
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
629
- */
630
- toString(): string {
631
- if (this.clouds.length < 1) {
632
- this.clouds = [new AeroflyMissionConditionsCloud(0, 0)];
633
- }
634
-
635
- return new AeroflyConfigFileSet(4, "tmmission_conditions", "conditions")
636
- .pushRaw(
637
- new AeroflyConfigFileSet(5, "tm_time_utc", "time")
638
- .push("int32", "time_year", this.time.getUTCFullYear())
639
- .push("int32", "time_month", this.time.getUTCMonth() + 1)
640
- .push("int32", "time_day", this.time.getUTCDate())
641
- .push("float64", "time_hours", this.time_hours, `${this.time_presentational} UTC`)
642
- .toString(),
643
- )
644
- .push("float64", "wind_direction", this.wind.direction)
645
- .push("float64", "wind_speed", this.wind.speed, "kts")
646
- .push("float64", "wind_gusts", this.wind.gusts, "kts")
647
- .push("float64", "turbulence_strength", this.turbulenceStrength)
648
- .push("float64", "thermal_strength", this.thermalStrength, `${this.temperature} °C`)
649
- .push("float64", "visibility", this.visibility, `${this.visibility_sm} SM`)
650
- .pushRaw(this.getCloudsString())
651
- .toString();
652
- }
653
- }
654
-
655
- /**
656
- * @class
657
- * A cloud layer for the current flight plan's weather data
658
- *
659
- * The purpose of this class is to collect data needed for Aerofly FS4's
660
- * `custom_missions_user.tmc` flight plan file format, and export the structure
661
- * for this file via the `toString()` method.
662
- */
663
- export class AeroflyMissionConditionsCloud {
664
- /**
665
- * @property {number} cover 0..1, percentage
666
- */
667
- cover: number;
668
-
669
- /**
670
- * @property {number} base altitude in meters AGL
671
- */
672
- base: number;
673
-
674
- /**
675
- * @param {number} cover 0..1, percentage
676
- * @param {number} base altitude in meters AGL
677
- */
678
- constructor(cover: number, base: number) {
679
- this.cover = cover;
680
- this.base = base;
681
- }
682
-
683
- /**
684
- * @param {number} cover 0..1, percentage
685
- * @param {number} base_feet altitude, but in feet AGL instead of meters AGL
686
- * @returns {AeroflyMissionConditionsCloud}
687
- */
688
- static createInFeet(cover: number, base_feet: number): AeroflyMissionConditionsCloud {
689
- return new AeroflyMissionConditionsCloud(cover, base_feet / feetPerMeter);
690
- }
691
-
692
- /**
693
- * @param {number} base_feet `this.base` in feet instead of meters
694
- */
695
- set base_feet(base_feet: number) {
696
- this.base = base_feet / feetPerMeter;
697
- }
698
-
699
- /**
700
- * @returns {number} `this.base` in feet instead of meters
701
- */
702
- get base_feet(): number {
703
- return this.base * feetPerMeter;
704
- }
705
-
706
- /**
707
- * @returns {string} Cloud coverage as text representation like "OVC" for `this.cover`
708
- */
709
- get cover_code(): AeroflyMissionConditionsCloudCoverCode {
710
- if (this.cover < 1 / 8) {
711
- return "CLR";
712
- } else if (this.cover <= 2 / 8) {
713
- return "FEW";
714
- } else if (this.cover <= 4 / 8) {
715
- return "SCT";
716
- } else if (this.cover <= 7 / 8) {
717
- return "BKN";
718
- }
719
- return "OVC";
720
- }
721
-
722
- /**
723
- * @param {number} index if used in an array will se the array index
724
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
725
- */
726
- toString(index: number = 0): string {
727
- const getIndexString = (index: number) => {
728
- switch (index) {
729
- case 0:
730
- return "cloud";
731
- case 1:
732
- return "cirrus";
733
- case 2:
734
- return "cumulus_mediocris";
735
- default:
736
- return "more_clouds";
737
- }
738
- };
739
-
740
- const indexString = getIndexString(index);
741
- const comment = index > 1 ? '//' : '';
742
-
743
- return `\
744
- ${comment}<[float64][${indexString}_cover][${this.cover ?? 0}]> // ${this.cover_code}
745
- ${comment}<[float64][${indexString}_base][${this.base}]> // ${this.base_feet} ft AGL`;
746
- }
747
- }
748
-
749
- /**
750
- * @class
751
- * A single way point for the given flight plan
752
- *
753
- * The purpose of this class is to collect data needed for Aerofly FS4's
754
- * `custom_missions_user.tmc` flight plan file format, and export the structure
755
- * for this file via the `toString()` method.
756
- */
757
- export class AeroflyMissionCheckpoint {
758
- /**
759
- * @property {"origin"|"departure_runway"|"departure"|"waypoint"|"arrival"|"approach"|"destination_runway"|"destination"} type of checkpoint, like "departure_runway"
760
- */
761
- type: AeroflyMissionCheckpointType;
762
-
763
- /**
764
- * @property {string} name ICAO code for airport, runway designator, navaid
765
- * designator, fix name, or custom name
766
- */
767
- name: string;
768
-
769
- /**
770
- * @property {number} longitude easting, using the World Geodetic
771
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
772
- * of decimal degrees; -180..180
773
- */
774
- longitude: number;
775
-
776
- /**
777
- * @property {number} latitude northing, using the World Geodetic
778
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
779
- * of decimal degrees; -90..90
780
- */
781
- latitude: number;
782
-
783
- /**
784
- * @property {number} altitude The height in meters above or below the WGS
785
- * 84 reference ellipsoid
786
- */
787
- altitude: number;
788
-
789
- /**
790
- * @property {?boolean} altitudeConstraint The altitude given in `altitude`
791
- * will be interpreted as mandatory flight plan altitude instead of
792
- * suggestion.
793
- */
794
- altitudeConstraint: boolean | null;
795
-
796
- /**
797
- * @property {?number} direction of runway, in degree
798
- */
799
- direction: number | null;
800
-
801
- /**
802
- * @property {?number} slope of runway
803
- */
804
- slope: number | null;
805
-
806
- /**
807
- * @property {?number} length of runway, in meters
808
- */
809
- length: number | null;
810
-
811
- /**
812
- * @property {?number} frequency of runways or navigational aids, in Hz; multiply by 1000 for kHz, 1_000_000 for MHz
813
- */
814
- frequency: number | null;
815
-
816
- /**
817
- * @property {?boolean} flyOver if waypoint is meant to be flown over
818
- */
819
- flyOver: boolean | null;
820
-
821
- /**
822
- * @param {string} name ICAO code for airport, runway designator, navaid
823
- * designator, fix name, or custom name
824
- * @param {"origin"|"departure_runway"|"departure"|"waypoint"|"arrival"|"approach"|"destination_runway"|"destination"} type Type of checkpoint, like "departure_runway"
825
- * @param {number} longitude easting, using the World Geodetic
826
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
827
- * of decimal degrees; -180..180
828
- * @param {number} latitude northing, using the World Geodetic
829
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
830
- * of decimal degrees; -90..90
831
- * @param {object} additionalAttributes allows to set additional attributes on creation
832
- * @param {number} [additionalAttributes.altitude] The height in meters above or below the WGS
833
- * 84 reference ellipsoid
834
- * @param {?number} [additionalAttributes.altitude_feet] The height in feet above or below the WGS
835
- * 84 reference ellipsoid. Will overwrite altitude
836
- * @param {number} [additionalAttributes.altitudeConstraint] The altitude given in `altitude`
837
- * will be interpreted as mandatory flight plan altitude instead of
838
- * suggestion.
839
- * @param {boolean} [additionalAttributes.direction] of runway, in degree
840
- * @param {?number} [additionalAttributes.slope] of runway
841
- * @param {?number} [additionalAttributes.length] of runway, in meters
842
- * @param {?number} [additionalAttributes.length_feet] of runway, in feet. Will overwrite length
843
- * @param {?number} [additionalAttributes.frequency] of runways or navigational aids, in Hz; multiply by 1000 for kHz, 1_000_000 for MHz
844
- * @param {?boolean} [additionalAttributes.flyOver] if waypoint is meant to be flown over
845
- */
846
- constructor(
847
- name: string,
848
- type: AeroflyMissionCheckpointType,
849
- longitude: number,
850
- latitude: number,
851
- {
852
- altitude = 0,
853
- altitude_feet = null,
854
- altitudeConstraint = null,
855
- direction = null,
856
- slope = null,
857
- length = null,
858
- length_feet = null,
859
- frequency = null,
860
- flyOver = null,
861
- }: {
862
- altitude?: number;
863
- altitude_feet?: number | null;
864
- altitudeConstraint?: boolean | null;
865
- direction?: number | null;
866
- slope?: number | null;
867
- length?: number | null;
868
- length_feet?: number | null;
869
- frequency?: number | null;
870
- flyOver?: boolean | null;
871
- } = {},
872
- ) {
873
- this.type = type;
874
- this.name = name;
875
- this.longitude = longitude;
876
- this.latitude = latitude;
877
- this.altitude = altitude;
878
- this.altitudeConstraint = altitudeConstraint;
879
- this.direction = direction;
880
- this.slope = slope;
881
- this.length = length;
882
- this.frequency = frequency;
883
- this.flyOver = flyOver;
884
-
885
- if (altitude_feet) {
886
- this.altitude_feet = altitude_feet;
887
- }
888
- if (length_feet) {
889
- this.length_feet = length_feet;
890
- }
891
- }
892
-
893
- /**
894
- * @param {number} altitude_feet
895
- */
896
- set altitude_feet(altitude_feet: number) {
897
- this.altitude = altitude_feet / feetPerMeter;
898
- }
899
-
900
- /**
901
- * @returns {number} altitude_feet
902
- */
903
- get altitude_feet(): number {
904
- return this.altitude * feetPerMeter;
905
- }
906
-
907
- /**
908
- * @param {number} length_feet
909
- */
910
- set length_feet(length_feet: number) {
911
- this.length = length_feet / feetPerMeter;
912
- }
913
-
914
- /**
915
- * @returns {number} length_feet
916
- */
917
- get length_feet(): number {
918
- return (this.length ?? 0) * feetPerMeter;
919
- }
920
-
921
- /**
922
- * @returns {string}
923
- */
924
- get frequency_string(): string {
925
- if (!this.frequency) {
926
- return "None";
927
- }
928
- if (this.frequency > 1_000_000) {
929
- return String(this.frequency / 1_000_000) + " MHz";
930
- }
931
- if (this.frequency > 1_000) {
932
- return String(this.frequency / 1_000) + " kHz";
933
- }
934
-
935
- return String(this.frequency) + " Hz";
936
- }
937
-
938
- /**
939
- * @param {number} index if used in an array will se the array index
940
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
941
- */
942
- toString(index: number = 0): string {
943
- const fileSet = new AeroflyConfigFileSet(5, "tmmission_checkpoint", "element", String(index))
944
- .push("string8u", "type", this.type)
945
- .push("string8u", "name", this.name)
946
- .push("vector2_float64", "lon_lat", [this.longitude, this.latitude])
947
- .push("float64", "altitude", this.altitude, `${Math.ceil(this.altitude_feet)} ft`)
948
- .push("float64", "direction", this.direction ?? (index === 0 ? -1 : 0))
949
- .push("float64", "slope", this.slope ?? 0);
950
-
951
- if (this.altitudeConstraint !== null) {
952
- fileSet.push("bool", "alt_cst", this.altitudeConstraint);
953
- }
954
- if (this.length) {
955
- fileSet.push("float64", "length", this.length ?? 0, `${Math.floor(this.length_feet)} ft`);
956
- }
957
- if (this.frequency) {
958
- fileSet.push("float64", "frequency", this.frequency ?? 0, `${this.frequency_string}`);
959
- }
960
- if (this.flyOver !== null) {
961
- fileSet.push("bool", "fly_over", this.flyOver);
962
- }
963
-
964
- return fileSet.toString();
965
- }
966
- }
967
-
968
- /**
969
- * @class
970
- * A translation for the mission title and description.
971
- */
972
- export class AeroflyLocalizedText {
973
- /**
974
- * @property {string} language ISO 639-1 like
975
- * - br
976
- * - cn
977
- * - de
978
- * - es
979
- * - fr
980
- * - id
981
- * - it
982
- * - jp
983
- * - kr
984
- * - pl
985
- * - sv
986
- * - tr
987
- */
988
- language: string;
989
-
990
- /**
991
- * @property {string} title of this flight plan
992
- */
993
- title: string;
994
-
995
- /**
996
- * @property {string} description text, mission briefing, etc
997
- */
998
- description: string;
999
-
1000
- /**
1001
- * @param {string} language ISO 639-1 like
1002
- * - br
1003
- * - cn
1004
- * - de
1005
- * - es
1006
- * - fr
1007
- * - id
1008
- * - it
1009
- * - jp
1010
- * - kr
1011
- * - pl
1012
- * - sv
1013
- * - tr
1014
- * @param {string} title of this flight plan
1015
- * @param {string} description text, mission briefing, etc
1016
- */
1017
- constructor(language: string, title: string, description: string) {
1018
- this.language = language;
1019
- this.title = title;
1020
- this.description = description;
1021
- }
1022
-
1023
- /**
1024
- * @param {number} index if used in an array will se the array index
1025
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
1026
- */
1027
- toString(index: number = 0): string {
1028
- return new AeroflyConfigFileSet(4, "tmmission_definition_localized", "element", String(index))
1029
- .push("string8u", "language", this.language)
1030
- .push("string8", "title", this.title)
1031
- .push("string8", "description", this.description)
1032
- .toString();
1033
- }
1034
- }
1035
-
1036
- /**
1037
- * @class
1038
- * A target plane which the aircraft needs to cross.
1039
- */
1040
- export class AeroflyMissionTargetPlane {
1041
- /**
1042
- * @property {number} longitude easting, using the World Geodetic
1043
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
1044
- * of decimal degrees; -180..180
1045
- */
1046
- longitude: number;
1047
-
1048
- /**
1049
- * @property {number} latitude northing, using the World Geodetic
1050
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
1051
- * of decimal degrees; -90..90
1052
- */
1053
- latitude: number;
1054
-
1055
- /**
1056
- * @property {number} dir in degree
1057
- */
1058
- dir: number;
1059
-
1060
- /**
1061
- * @property {string} name of property
1062
- */
1063
- name: string;
1064
-
1065
- /**
1066
- *
1067
- * @param {number} longitude easting, using the World Geodetic
1068
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
1069
- * of decimal degrees; -180..180
1070
- * @param {number}latitude northing, using the World Geodetic
1071
- * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
1072
- * of decimal degrees; -90..90
1073
- * @param {number} dir in degree
1074
- * @param {string} name of property
1075
- */
1076
- constructor(longitude: number, latitude: number, dir: number, name: string = "finish") {
1077
- this.longitude = longitude;
1078
- this.latitude = latitude;
1079
- this.dir = dir;
1080
- this.name = name;
1081
- }
1082
-
1083
- toString(): string {
1084
- return new AeroflyConfigFileSet(4, "tmmission_target_plane", this.name)
1085
- .push("vector2_float64", "lon_lat", [this.longitude, this.latitude])
1086
- .push("float64", "direction", this.dir)
1087
- .toString();
1088
- }
1089
- }
1090
-
1091
- export default {
1092
- AeroflyMissionsList,
1093
- AeroflyMission,
1094
- AeroflyMissionConditions,
1095
- AeroflyMissionConditionsCloud,
1096
- AeroflyMissionCheckpoint,
1097
- AeroflyLocalizedText,
1098
- AeroflyMissionTargetPlane,
1099
- };
1
+ export { AeroflyLocalizedText } from "./dto/AeroflyLocalizedText.js";
2
+ export { AeroflyMission } from "./dto/AeroflyMission.js";
3
+ export { AeroflyMissionCheckpoint } from "./dto/AeroflyMissionCheckpoint.js";
4
+ export { AeroflyMissionConditions } from "./dto/AeroflyMissionConditions.js";
5
+ export { AeroflyMissionConditionsCloud } from "./dto/AeroflyMissionConditionsCloud.js";
6
+ export { AeroflyMissionsList } from "./dto/AeroflyMissionsList.js";
7
+ export { AeroflyMissionTargetPlane } from "./dto/AeroflyMissionTargetPlane.js";