@fboes/aerofly-custom-missions 1.2.2 → 1.2.3

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 (46) hide show
  1. package/.eslintrc.json +24 -0
  2. package/CHANGELOG.md +4 -0
  3. package/dist/dto/AeroflyConfigFileSet.js +43 -0
  4. package/dist/dto/AeroflyLocalizedText.js +40 -0
  5. package/dist/dto/AeroflyMission.js +171 -0
  6. package/dist/dto/AeroflyMissionCheckpoint.js +121 -0
  7. package/dist/dto/AeroflyMissionConditions.js +122 -0
  8. package/dist/dto/AeroflyMissionConditionsCloud.js +80 -0
  9. package/dist/dto/AeroflyMissionTargetPlane.js +30 -0
  10. package/dist/dto/AeroflyMissionsList.js +28 -0
  11. package/dist/index.js +7 -634
  12. package/dist/index.test.js +12 -5
  13. package/package.json +9 -10
  14. package/src/dto/AeroflyConfigFileSet.ts +35 -0
  15. package/src/dto/AeroflyLocalizedText.ts +69 -0
  16. package/src/dto/AeroflyMission.ts +377 -0
  17. package/src/dto/AeroflyMissionCheckpoint.ts +235 -0
  18. package/src/dto/AeroflyMissionConditions.ts +196 -0
  19. package/src/dto/AeroflyMissionConditionsCloud.ts +100 -0
  20. package/src/dto/AeroflyMissionTargetPlane.ts +56 -0
  21. package/src/dto/AeroflyMissionsList.ts +36 -0
  22. package/src/index.test.ts +8 -10
  23. package/src/index.ts +7 -1099
  24. package/types/AeroflyMissionCheckpoint.d.ts +140 -0
  25. package/types/AeroflyMissionTargetPlane.d.ts +40 -0
  26. package/types/dto/AeroflyConfigFileSet.d.ts +10 -0
  27. package/types/dto/AeroflyConfigFileSet.d.ts.map +1 -0
  28. package/types/dto/AeroflyLocalizedText.d.ts +54 -0
  29. package/types/dto/AeroflyLocalizedText.d.ts.map +1 -0
  30. package/types/dto/AeroflyMission.d.ts +208 -0
  31. package/types/dto/AeroflyMission.d.ts.map +1 -0
  32. package/types/dto/AeroflyMissionCheckpoint.d.ts +152 -0
  33. package/types/dto/AeroflyMissionCheckpoint.d.ts.map +1 -0
  34. package/types/dto/AeroflyMissionConditions.d.ts +116 -0
  35. package/types/dto/AeroflyMissionConditions.d.ts.map +1 -0
  36. package/types/dto/AeroflyMissionConditionsCloud.d.ts +51 -0
  37. package/types/dto/AeroflyMissionConditionsCloud.d.ts.map +1 -0
  38. package/types/dto/AeroflyMissionTargetPlane.d.ts +40 -0
  39. package/types/dto/AeroflyMissionTargetPlane.d.ts.map +1 -0
  40. package/types/dto/AeroflyMissionsList.d.ts +24 -0
  41. package/types/dto/AeroflyMissionsList.d.ts.map +1 -0
  42. package/types/dto/basicTypes.d.ts +1 -0
  43. package/types/index.d.ts +8 -586
  44. package/types/index.d.ts.map +1 -1
  45. package/types/index.test.d.ts +1 -1
  46. package/eslint.config.js +0 -29
@@ -0,0 +1,235 @@
1
+ import { AeroflyConfigFileSet } from "./AeroflyConfigFileSet.js";
2
+ import { feetPerMeter } from "./AeroflyMission.js";
3
+
4
+ /**
5
+ * Types of checkpoints. Required are usually "origin", "departure_runway" at the start and "destination_runway", "destination" at the end.
6
+ */
7
+ export type AeroflyMissionCheckpointType =
8
+ | "origin"
9
+ | "departure_runway"
10
+ | "departure"
11
+ | "waypoint"
12
+ | "arrival"
13
+ | "approach"
14
+ | "destination_runway"
15
+ | "destination";
16
+
17
+ /**
18
+ * @class
19
+ * A single way point for the given flight plan
20
+ *
21
+ * The purpose of this class is to collect data needed for Aerofly FS4's
22
+ * `custom_missions_user.tmc` flight plan file format, and export the structure
23
+ * for this file via the `toString()` method.
24
+ */
25
+
26
+ export class AeroflyMissionCheckpoint {
27
+ /**
28
+ * @property {"origin"|"departure_runway"|"departure"|"waypoint"|"arrival"|"approach"|"destination_runway"|"destination"} type of checkpoint, like "departure_runway"
29
+ */
30
+ type: AeroflyMissionCheckpointType;
31
+
32
+ /**
33
+ * @property {string} name ICAO code for airport, runway designator, navaid
34
+ * designator, fix name, or custom name
35
+ */
36
+ name: string;
37
+
38
+ /**
39
+ * @property {number} longitude easting, using the World Geodetic
40
+ * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
41
+ * of decimal degrees; -180..180
42
+ */
43
+ longitude: number;
44
+
45
+ /**
46
+ * @property {number} latitude northing, using the World Geodetic
47
+ * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
48
+ * of decimal degrees; -90..90
49
+ */
50
+ latitude: number;
51
+
52
+ /**
53
+ * @property {number} altitude The height in meters above or below the WGS
54
+ * 84 reference ellipsoid
55
+ */
56
+ altitude: number;
57
+
58
+ /**
59
+ * @property {?boolean} altitudeConstraint The altitude given in `altitude`
60
+ * will be interpreted as mandatory flight plan altitude instead of
61
+ * suggestion.
62
+ */
63
+ altitudeConstraint: boolean | null;
64
+
65
+ /**
66
+ * @property {?number} direction of runway, in degree
67
+ */
68
+ direction: number | null;
69
+
70
+ /**
71
+ * @property {?number} slope of runway
72
+ */
73
+ slope: number | null;
74
+
75
+ /**
76
+ * @property {?number} length of runway, in meters
77
+ */
78
+ length: number | null;
79
+
80
+ /**
81
+ * @property {?number} frequency of runways or navigational aids, in Hz; multiply by 1000 for kHz, 1_000_000 for MHz
82
+ */
83
+ frequency: number | null;
84
+
85
+ /**
86
+ * @property {?boolean} flyOver if waypoint is meant to be flown over
87
+ */
88
+ flyOver: boolean | null;
89
+
90
+ /**
91
+ * @param {string} name ICAO code for airport, runway designator, navaid
92
+ * designator, fix name, or custom name
93
+ * @param {"origin"|"departure_runway"|"departure"|"waypoint"|"arrival"|"approach"|"destination_runway"|"destination"} type Type of checkpoint, like "departure_runway"
94
+ * @param {number} longitude easting, using the World Geodetic
95
+ * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
96
+ * of decimal degrees; -180..180
97
+ * @param {number} latitude northing, using the World Geodetic
98
+ * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
99
+ * of decimal degrees; -90..90
100
+ * @param {object} additionalAttributes allows to set additional attributes on creation
101
+ * @param {number} [additionalAttributes.altitude] The height in meters above or below the WGS
102
+ * 84 reference ellipsoid
103
+ * @param {?number} [additionalAttributes.altitude_feet] The height in feet above or below the WGS
104
+ * 84 reference ellipsoid. Will overwrite altitude
105
+ * @param {number} [additionalAttributes.altitudeConstraint] The altitude given in `altitude`
106
+ * will be interpreted as mandatory flight plan altitude instead of
107
+ * suggestion.
108
+ * @param {boolean} [additionalAttributes.direction] of runway, in degree
109
+ * @param {?number} [additionalAttributes.slope] of runway
110
+ * @param {?number} [additionalAttributes.length] of runway, in meters
111
+ * @param {?number} [additionalAttributes.length_feet] of runway, in feet. Will overwrite length
112
+ * @param {?number} [additionalAttributes.frequency] of runways or navigational aids, in Hz; multiply by 1000 for kHz, 1_000_000 for MHz
113
+ * @param {?boolean} [additionalAttributes.flyOver] if waypoint is meant to be flown over
114
+ */
115
+ constructor(
116
+ name: string,
117
+ type: AeroflyMissionCheckpointType,
118
+ longitude: number,
119
+ latitude: number,
120
+ {
121
+ altitude = 0,
122
+ altitude_feet = null,
123
+ altitudeConstraint = null,
124
+ direction = null,
125
+ slope = null,
126
+ length = null,
127
+ length_feet = null,
128
+ frequency = null,
129
+ flyOver = null,
130
+ }: {
131
+ altitude?: number;
132
+ altitude_feet?: number | null;
133
+ altitudeConstraint?: boolean | null;
134
+ direction?: number | null;
135
+ slope?: number | null;
136
+ length?: number | null;
137
+ length_feet?: number | null;
138
+ frequency?: number | null;
139
+ flyOver?: boolean | null;
140
+ } = {},
141
+ ) {
142
+ this.type = type;
143
+ this.name = name;
144
+ this.longitude = longitude;
145
+ this.latitude = latitude;
146
+ this.altitude = altitude;
147
+ this.altitudeConstraint = altitudeConstraint;
148
+ this.direction = direction;
149
+ this.slope = slope;
150
+ this.length = length;
151
+ this.frequency = frequency;
152
+ this.flyOver = flyOver;
153
+
154
+ if (altitude_feet) {
155
+ this.altitude_feet = altitude_feet;
156
+ }
157
+ if (length_feet) {
158
+ this.length_feet = length_feet;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * @param {number} altitude_feet
164
+ */
165
+ set altitude_feet(altitude_feet: number) {
166
+ this.altitude = altitude_feet / feetPerMeter;
167
+ }
168
+
169
+ /**
170
+ * @returns {number} altitude_feet
171
+ */
172
+ get altitude_feet(): number {
173
+ return this.altitude * feetPerMeter;
174
+ }
175
+
176
+ /**
177
+ * @param {number} length_feet
178
+ */
179
+ set length_feet(length_feet: number) {
180
+ this.length = length_feet / feetPerMeter;
181
+ }
182
+
183
+ /**
184
+ * @returns {number} length_feet
185
+ */
186
+ get length_feet(): number {
187
+ return (this.length ?? 0) * feetPerMeter;
188
+ }
189
+
190
+ /**
191
+ * @returns {string}
192
+ */
193
+ get frequency_string(): string {
194
+ if (!this.frequency) {
195
+ return "None";
196
+ }
197
+ if (this.frequency > 1000000) {
198
+ return String(this.frequency / 1000000) + " MHz";
199
+ }
200
+ if (this.frequency > 1000) {
201
+ return String(this.frequency / 1000) + " kHz";
202
+ }
203
+
204
+ return String(this.frequency) + " Hz";
205
+ }
206
+
207
+ /**
208
+ * @param {number} index if used in an array will se the array index
209
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
210
+ */
211
+ toString(index: number = 0): string {
212
+ const fileSet = new AeroflyConfigFileSet(5, "tmmission_checkpoint", "element", String(index))
213
+ .push("string8u", "type", this.type)
214
+ .push("string8u", "name", this.name)
215
+ .push("vector2_float64", "lon_lat", [this.longitude, this.latitude])
216
+ .push("float64", "altitude", this.altitude, `${Math.ceil(this.altitude_feet)} ft`)
217
+ .push("float64", "direction", this.direction ?? (index === 0 ? -1 : 0))
218
+ .push("float64", "slope", this.slope ?? 0);
219
+
220
+ if (this.altitudeConstraint !== null) {
221
+ fileSet.push("bool", "alt_cst", this.altitudeConstraint);
222
+ }
223
+ if (this.length) {
224
+ fileSet.push("float64", "length", this.length ?? 0, `${Math.floor(this.length_feet)} ft`);
225
+ }
226
+ if (this.frequency) {
227
+ fileSet.push("float64", "frequency", this.frequency ?? 0, `${this.frequency_string}`);
228
+ }
229
+ if (this.flyOver !== null) {
230
+ fileSet.push("bool", "fly_over", this.flyOver);
231
+ }
232
+
233
+ return fileSet.toString();
234
+ }
235
+ }
@@ -0,0 +1,196 @@
1
+ import { AeroflyConfigFileSet } from "./AeroflyConfigFileSet.js";
2
+ import { meterPerStatuteMile } from "./AeroflyMission.js";
3
+ import { AeroflyMissionConditionsCloud } from "./AeroflyMissionConditionsCloud.js";
4
+
5
+ /**
6
+ * Weather data for wind
7
+ * @property direction in degree
8
+ * @property speed in kts
9
+ * @property gusts in kts
10
+ */
11
+ export type AeroflyMissionConditionsWind = {
12
+ direction: number;
13
+ speed: number;
14
+ gusts: number;
15
+ };
16
+
17
+ /**
18
+ * @class
19
+ * Time and weather data for the given flight plan
20
+ *
21
+ * The purpose of this class is to collect data needed for Aerofly FS4's
22
+ * `custom_missions_user.tmc` flight plan file format, and export the structure
23
+ * for this file via the `toString()` method.
24
+ */
25
+ export class AeroflyMissionConditions {
26
+ /**
27
+ * @property {Date} time of flight plan. Relevant is the UTC part, so
28
+ * consider setting this date in UTC.
29
+ */
30
+ time: Date;
31
+
32
+ /**
33
+ * @property {object} wind state
34
+ */
35
+ wind: AeroflyMissionConditionsWind;
36
+
37
+ /**
38
+ * @property {number} 0..1, percentage
39
+ */
40
+ turbulenceStrength: number;
41
+
42
+ /**
43
+ * @property {number} 0..1, percentage
44
+ */
45
+ thermalStrength: number;
46
+
47
+ /**
48
+ * @property {number} visibility in meters
49
+ */
50
+ visibility: number;
51
+
52
+ /**
53
+ * @property {AeroflyMissionConditionsCloud[]} clouds for the whole flight
54
+ */
55
+ clouds: AeroflyMissionConditionsCloud[] = [];
56
+
57
+ /**
58
+ * @param {object} additionalAttributes allows to set additional attributes on creation
59
+ * @param {Date} [additionalAttributes.time] of flight plan. Relevant is the UTC part, so
60
+ * consider setting this date in UTC.
61
+ * @param {{direction: number, speed: number, gusts: number}} [additionalAttributes.wind] state
62
+ * @param {number} [additionalAttributes.turbulenceStrength] 0..1, percentage
63
+ * @param {number} [additionalAttributes.thermalStrength] 0..1, percentage
64
+ * @param {number} [additionalAttributes.visibility] in meters
65
+ * @param {?number} [additionalAttributes.visibility_sm] in statute miles, will overwrite visibility
66
+ * @param {?number} [additionalAttributes.temperature] in °C, will overwrite thermalStrength
67
+ * @param {AeroflyMissionConditionsCloud[]} [additionalAttributes.clouds] for the whole flight
68
+ */
69
+ constructor({
70
+ time = new Date(),
71
+ wind = {
72
+ direction: 0,
73
+ speed: 0,
74
+ gusts: 0,
75
+ },
76
+ turbulenceStrength = 0,
77
+ thermalStrength = 0,
78
+ visibility = 25_000,
79
+ visibility_sm = null,
80
+ temperature = null,
81
+ clouds = [],
82
+ }: {
83
+ time?: Date;
84
+ wind?: {
85
+ direction: number;
86
+ speed: number;
87
+ gusts: number;
88
+ };
89
+ turbulenceStrength?: number;
90
+ thermalStrength?: number;
91
+ visibility?: number;
92
+ visibility_sm?: number | null;
93
+ temperature?: number | null;
94
+ clouds?: AeroflyMissionConditionsCloud[];
95
+ } = {}) {
96
+ this.time = time;
97
+ this.wind = wind;
98
+ this.turbulenceStrength = turbulenceStrength;
99
+ this.thermalStrength = thermalStrength;
100
+ this.visibility = visibility;
101
+ this.clouds = clouds;
102
+
103
+ if (visibility_sm) {
104
+ this.visibility_sm = visibility_sm;
105
+ }
106
+ if (temperature) {
107
+ this.temperature = temperature;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * @returns {number} the Aerofly value for UTC hours + minutes/60 + seconds/3600. Ignores milliseconds ;)
113
+ */
114
+ get time_hours(): number {
115
+ return this.time.getUTCHours() + this.time.getUTCMinutes() / 60 + this.time.getUTCSeconds() / 3600;
116
+ }
117
+
118
+ /**
119
+ * @returns {string} Time representation like "20:15:00"
120
+ */
121
+ get time_presentational(): string {
122
+ return [this.time.getUTCHours(), this.time.getUTCMinutes(), this.time.getUTCSeconds()]
123
+ .map((t) => {
124
+ return String(t).padStart(2, "0");
125
+ })
126
+ .join(":");
127
+ }
128
+
129
+ /**
130
+ * @param {number} visibility_sm `this.visibility` in statute miles instead of meters
131
+ */
132
+ set visibility_sm(visibility_sm: number) {
133
+ this.visibility = visibility_sm * meterPerStatuteMile;
134
+ }
135
+
136
+ /**
137
+ * @returns {number} `this.visibility` in statute miles instead of meters
138
+ */
139
+ get visibility_sm(): number {
140
+ return this.visibility / meterPerStatuteMile;
141
+ }
142
+
143
+ /**
144
+ * Will set `this.thermalStrength`
145
+ * @param {number} temperature in °C
146
+ */
147
+ set temperature(temperature: number) {
148
+ // Range from -15°C to 35°C
149
+ this.thermalStrength = Math.max(0, (temperature + 15) / 50) ** 2;
150
+ }
151
+
152
+ /**
153
+ * @returns {number} in °C
154
+ */
155
+ get temperature(): number {
156
+ return Math.sqrt(this.thermalStrength) * 50 - 15;
157
+ }
158
+
159
+ /**
160
+ * @returns {string}
161
+ */
162
+ getCloudsString(): string {
163
+ return this.clouds
164
+ .map((c: AeroflyMissionConditionsCloud, index: number): string => {
165
+ return c.toString(index);
166
+ })
167
+ .join("\n");
168
+ }
169
+
170
+ /**
171
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
172
+ */
173
+ toString(): string {
174
+ if (this.clouds.length < 1) {
175
+ this.clouds = [new AeroflyMissionConditionsCloud(0, 0)];
176
+ }
177
+
178
+ return new AeroflyConfigFileSet(4, "tmmission_conditions", "conditions")
179
+ .pushRaw(
180
+ new AeroflyConfigFileSet(5, "tm_time_utc", "time")
181
+ .push("int32", "time_year", this.time.getUTCFullYear())
182
+ .push("int32", "time_month", this.time.getUTCMonth() + 1)
183
+ .push("int32", "time_day", this.time.getUTCDate())
184
+ .push("float64", "time_hours", this.time_hours, `${this.time_presentational} UTC`)
185
+ .toString(),
186
+ )
187
+ .push("float64", "wind_direction", this.wind.direction)
188
+ .push("float64", "wind_speed", this.wind.speed, "kts")
189
+ .push("float64", "wind_gusts", this.wind.gusts, "kts")
190
+ .push("float64", "turbulence_strength", this.turbulenceStrength)
191
+ .push("float64", "thermal_strength", this.thermalStrength, `${this.temperature} °C`)
192
+ .push("float64", "visibility", this.visibility, `${this.visibility_sm} SM`)
193
+ .pushRaw(this.getCloudsString())
194
+ .toString();
195
+ }
196
+ }
@@ -0,0 +1,100 @@
1
+ import { feetPerMeter } from "./AeroflyMission.js";
2
+
3
+ /**
4
+ * Cloud coverage codes
5
+ */
6
+ export type AeroflyMissionConditionsCloudCoverCode = "CLR" | "FEW" | "SCT" | "BKN" | "OVC";
7
+
8
+ /**
9
+ * @class
10
+ * A cloud layer for the current flight plan's weather data
11
+ *
12
+ * The purpose of this class is to collect data needed for Aerofly FS4's
13
+ * `custom_missions_user.tmc` flight plan file format, and export the structure
14
+ * for this file via the `toString()` method.
15
+ */
16
+ export class AeroflyMissionConditionsCloud {
17
+ /**
18
+ * @property {number} cover 0..1, percentage
19
+ */
20
+ cover: number;
21
+
22
+ /**
23
+ * @property {number} base altitude in meters AGL
24
+ */
25
+ base: number;
26
+
27
+ /**
28
+ * @param {number} cover 0..1, percentage
29
+ * @param {number} base altitude in meters AGL
30
+ */
31
+ constructor(cover: number, base: number) {
32
+ this.cover = cover;
33
+ this.base = base;
34
+ }
35
+
36
+ /**
37
+ * @param {number} cover 0..1, percentage
38
+ * @param {number} base_feet altitude, but in feet AGL instead of meters AGL
39
+ * @returns {AeroflyMissionConditionsCloud}
40
+ */
41
+ static createInFeet(cover: number, base_feet: number): AeroflyMissionConditionsCloud {
42
+ return new AeroflyMissionConditionsCloud(cover, base_feet / feetPerMeter);
43
+ }
44
+
45
+ /**
46
+ * @param {number} base_feet `this.base` in feet instead of meters
47
+ */
48
+ set base_feet(base_feet: number) {
49
+ this.base = base_feet / feetPerMeter;
50
+ }
51
+
52
+ /**
53
+ * @returns {number} `this.base` in feet instead of meters
54
+ */
55
+ get base_feet(): number {
56
+ return this.base * feetPerMeter;
57
+ }
58
+
59
+ /**
60
+ * @returns {string} Cloud coverage as text representation like "OVC" for `this.cover`
61
+ */
62
+ get cover_code(): AeroflyMissionConditionsCloudCoverCode {
63
+ if (this.cover < 1 / 8) {
64
+ return "CLR";
65
+ } else if (this.cover <= 2 / 8) {
66
+ return "FEW";
67
+ } else if (this.cover <= 4 / 8) {
68
+ return "SCT";
69
+ } else if (this.cover <= 7 / 8) {
70
+ return "BKN";
71
+ }
72
+ return "OVC";
73
+ }
74
+
75
+ /**
76
+ * @param {number} index if used in an array will se the array index
77
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
78
+ */
79
+ toString(index: number = 0): string {
80
+ const getIndexString = (index: number) => {
81
+ switch (index) {
82
+ case 0:
83
+ return "cloud";
84
+ case 1:
85
+ return "cirrus";
86
+ case 2:
87
+ return "cumulus_mediocris";
88
+ default:
89
+ return "more_clouds";
90
+ }
91
+ };
92
+
93
+ const indexString = getIndexString(index);
94
+ const comment = index > 1 ? "//" : "";
95
+
96
+ return `\
97
+ ${comment}<[float64][${indexString}_cover][${this.cover ?? 0}]> // ${this.cover_code}
98
+ ${comment}<[float64][${indexString}_base][${this.base}]> // ${this.base_feet} ft AGL`;
99
+ }
100
+ }
@@ -0,0 +1,56 @@
1
+ import { AeroflyConfigFileSet } from "./AeroflyConfigFileSet.js";
2
+
3
+ /**
4
+ * @class
5
+ * A target plane which the aircraft needs to cross.
6
+ */
7
+ export class AeroflyMissionTargetPlane {
8
+ /**
9
+ * @property {number} longitude easting, using the World Geodetic
10
+ * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
11
+ * of decimal degrees; -180..180
12
+ */
13
+ longitude: number;
14
+
15
+ /**
16
+ * @property {number} latitude northing, using the World Geodetic
17
+ * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
18
+ * of decimal degrees; -90..90
19
+ */
20
+ latitude: number;
21
+
22
+ /**
23
+ * @property {number} dir in degree
24
+ */
25
+ dir: number;
26
+
27
+ /**
28
+ * @property {string} name of property
29
+ */
30
+ name: string;
31
+
32
+ /**
33
+ *
34
+ * @param {number} longitude easting, using the World Geodetic
35
+ * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
36
+ * of decimal degrees; -180..180
37
+ * @param {number}latitude northing, using the World Geodetic
38
+ * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
39
+ * of decimal degrees; -90..90
40
+ * @param {number} dir in degree
41
+ * @param {string} name of property
42
+ */
43
+ constructor(longitude: number, latitude: number, dir: number, name: string = "finish") {
44
+ this.longitude = longitude;
45
+ this.latitude = latitude;
46
+ this.dir = dir;
47
+ this.name = name;
48
+ }
49
+
50
+ toString(): string {
51
+ return new AeroflyConfigFileSet(4, "tmmission_target_plane", this.name)
52
+ .push("vector2_float64", "lon_lat", [this.longitude, this.latitude])
53
+ .push("float64", "direction", this.dir)
54
+ .toString();
55
+ }
56
+ }
@@ -0,0 +1,36 @@
1
+ import { AeroflyMission } from "./AeroflyMission.js";
2
+
3
+ /**
4
+ * @class
5
+ * A list of flight plans.
6
+ *
7
+ * The purpose of this class is to collect data needed for Aerofly FS4's
8
+ * `custom_missions_user.tmc` flight plan file format, and export the structure
9
+ * for this file via the `toString()` method.
10
+ */
11
+ export class AeroflyMissionsList {
12
+ /**
13
+ * @property {AeroflyMission[]} missions in this mission list
14
+ */
15
+ missions: AeroflyMission[];
16
+
17
+ /**
18
+ * @param {AeroflyMission[]} missions in this mission list
19
+ */
20
+ constructor(missions: AeroflyMission[] = []) {
21
+ this.missions = missions;
22
+ }
23
+
24
+ /**
25
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
26
+ */
27
+ toString(): string {
28
+ const separator = "\n// -----------------------------------------------------------------------------\n";
29
+ return `\
30
+ <[file][][]
31
+ <[tmmissions_list][][]
32
+ <[list_tmmission_definition][missions][]${separator + this.missions.join(separator) + separator} >
33
+ >
34
+ >`;
35
+ }
36
+ }
package/src/index.test.ts CHANGED
@@ -1,14 +1,12 @@
1
- import {
2
- AeroflyConfigFileSet,
3
- AeroflyMissionsList,
4
- AeroflyMission,
5
- AeroflyMissionConditions,
6
- AeroflyMissionConditionsCloud,
7
- AeroflyMissionCheckpoint,
8
- AeroflyLocalizedText,
9
- AeroflyMissionTargetPlane,
10
- } from "./index.js";
11
1
  import { strict as assert } from "node:assert";
2
+ import { AeroflyConfigFileSet } from "./dto/AeroflyConfigFileSet.js";
3
+ import { AeroflyLocalizedText } from "./index.js";
4
+ import { AeroflyMission } from "./index.js";
5
+ import { AeroflyMissionCheckpoint } from "./index.js";
6
+ import { AeroflyMissionConditions } from "./index.js";
7
+ import { AeroflyMissionConditionsCloud } from "./index.js";
8
+ import { AeroflyMissionsList } from "./index.js";
9
+ import { AeroflyMissionTargetPlane } from "./index.js";
12
10
 
13
11
  const assertValidAeroflyStructure = (aeroflyString: string): void => {
14
12
  const openingBrackets = aeroflyString.match(/</g);