@fboes/aerofly-custom-missions 1.2.3 → 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 (46) hide show
  1. package/.editorconfig +1 -5
  2. package/CHANGELOG.md +56 -22
  3. package/dist/dto/AeroflyLocalizedText.js +12 -8
  4. package/dist/dto/AeroflyMission.js +53 -51
  5. package/dist/dto/AeroflyMissionCheckpoint.js +26 -20
  6. package/dist/dto/AeroflyMissionConditions.js +26 -24
  7. package/dist/dto/AeroflyMissionConditionsCloud.js +18 -8
  8. package/dist/dto/AeroflyMissionTargetPlane.js +10 -5
  9. package/dist/dto/AeroflyMissionsList.js +15 -7
  10. package/dist/index.test.js +14 -15
  11. package/dist/node/AeroflyConfigurationNode.js +82 -0
  12. package/docs/custom_missions_user.tmc +3 -6
  13. package/docs/custom_missions_user.xml +100 -0
  14. package/package.json +1 -1
  15. package/src/dto/AeroflyLocalizedText.ts +13 -8
  16. package/src/dto/AeroflyMission.ts +76 -81
  17. package/src/dto/AeroflyMissionCheckpoint.ts +31 -32
  18. package/src/dto/AeroflyMissionConditions.ts +37 -44
  19. package/src/dto/AeroflyMissionConditionsCloud.ts +19 -8
  20. package/src/dto/AeroflyMissionTargetPlane.ts +11 -5
  21. package/src/dto/AeroflyMissionsList.ts +23 -7
  22. package/src/index.test.ts +15 -16
  23. package/src/node/AeroflyConfigurationNode.ts +89 -0
  24. package/types/dto/AeroflyConfigFileSet.d.ts +1 -1
  25. package/types/dto/AeroflyConfiguration.interface.d.ts +4 -0
  26. package/types/dto/AeroflyConfiguration.interface.d.ts.map +1 -0
  27. package/types/dto/AeroflyLocalizedText.d.ts +7 -3
  28. package/types/dto/AeroflyLocalizedText.d.ts.map +1 -1
  29. package/types/dto/AeroflyMission.d.ts +27 -72
  30. package/types/dto/AeroflyMission.d.ts.map +1 -1
  31. package/types/dto/AeroflyMissionCheckpoint.d.ts +17 -43
  32. package/types/dto/AeroflyMissionCheckpoint.d.ts.map +1 -1
  33. package/types/dto/AeroflyMissionConditions.d.ts +16 -30
  34. package/types/dto/AeroflyMissionConditions.d.ts.map +1 -1
  35. package/types/dto/AeroflyMissionConditionsCloud.d.ts +9 -3
  36. package/types/dto/AeroflyMissionConditionsCloud.d.ts.map +1 -1
  37. package/types/dto/AeroflyMissionTargetPlane.d.ts +6 -1
  38. package/types/dto/AeroflyMissionTargetPlane.d.ts.map +1 -1
  39. package/types/dto/AeroflyMissionsList.d.ts +7 -1
  40. package/types/dto/AeroflyMissionsList.d.ts.map +1 -1
  41. package/types/index.d.ts +1 -1
  42. package/types/index.test.d.ts +1 -1
  43. package/types/node/AeroflyConfigurationNode.d.ts +14 -0
  44. package/types/node/AeroflyConfigurationNode.d.ts.map +1 -0
  45. package/dist/dto/AeroflyConfigFileSet.js +0 -43
  46. package/src/dto/AeroflyConfigFileSet.ts +0 -35
package/.editorconfig CHANGED
@@ -8,9 +8,5 @@ insert_final_newline = true
8
8
  trim_trailing_whitespace = true
9
9
  charset = utf-8
10
10
  indent_style = space
11
- indent_size = 2
12
- max_line_length = 120
13
-
14
- # To accomodate TypeScript compiler
15
- [*.{js,ts}]
16
11
  indent_size = 4
12
+ max_line_length = 120
package/CHANGELOG.md CHANGED
@@ -1,56 +1,90 @@
1
1
  # Changelog
2
2
 
3
+ This changelog documents all notable changes to the Aerofly Custom Missions project. Each version entry includes a list of changes, with the most recent version at the top.
4
+
5
+ ## 1.3.0
6
+
7
+ - Changed TMC code generation with nodes
8
+
3
9
  ## 1.2.3
4
10
 
5
- - Internal restructuring
11
+ - Internal restructuring of mission generation logic
12
+ - Optimized waypoint handling and validation
13
+ - Improved error handling for mission parsing
6
14
 
7
15
  ## 1.2.2
8
16
 
9
- - Added altitude constraint property
10
- - Improved handling of cloud layers
17
+ - Added altitude constraint property for waypoints
18
+ - Improved handling of cloud layers with better validation
11
19
 
12
20
  ## 1.2.1
13
21
 
14
- - Changed handling of checkpoints, as it is obviously possible to have missions without checkpoints
15
- - Changed actual file generation to improve programmatic adding of entries to file
16
- - Adding `is_scheduled` and `tutorial_name` property
17
- - Improved cloud handling
18
- - Improved handling off unset values
19
- - Added `cold_and_dark`, `before_start`, `pushback"` as flight setting
22
+ - Changed handling of checkpoints to support missions without checkpoints
23
+ - Improved file generation for programmatic mission creation
24
+ - Added new properties:
25
+ - `is_scheduled` for mission scheduling
26
+ - `tutorial_name` for tutorial identification
27
+ - Enhanced cloud handling with better validation
28
+ - Improved handling of unset values with default fallbacks
29
+ - Added new flight settings:
30
+ - `cold_and_dark`
31
+ - `before_start`
32
+ - `pushback`
20
33
 
21
34
  ## 1.2.0
22
35
 
23
- - Adding new cloud level `cirrus_cover` / `cirrus_base`
24
- - Adding new waypoint property `fly_over`
25
- - Adding `finish` property
36
+ - Added new cloud level properties:
37
+ - `cirrus_cover` for high-altitude cloud coverage
38
+ - `cirrus_base` for cirrus cloud base altitude
39
+ - Added new waypoint property `fly_over` for precise waypoint navigation
40
+ - Added `finish` property to mark mission completion points
26
41
 
27
42
  ## 1.1.1
28
43
 
29
- - Minor styling fixes
44
+ - Fixed styling issues in mission display
45
+ - Improved UI consistency across different mission types
46
+ - Enhanced error message formatting
30
47
 
31
48
  ## 1.1.0
32
49
 
33
- - Added new mission properties `tags`, `isFeatured`, `difficulty`, `distance`, `duration` and translations
34
- - Added new flight settings `winch_launch`, `aerotow`
35
- - Improved temperature property
50
+ - Added new mission metadata properties:
51
+ - `tags` for mission categorization
52
+ - `isFeatured` for highlighting special missions
53
+ - `difficulty` for mission complexity rating
54
+ - `distance` for mission length in kilometers
55
+ - `duration` for estimated completion time
56
+ - Multi-language support for mission descriptions
57
+ - Added new flight settings:
58
+ - `winch_launch` for glider operations
59
+ - `aerotow` for towed aircraft operations
60
+ - Improved temperature property with better unit handling
36
61
 
37
62
  ## 1.0.4
38
63
 
39
- - Added documentation for known issues
40
- - Added property `AeroflyMissionConditions.temperature`
64
+ - Added comprehensive documentation for known issues and workarounds
65
+ - Added `AeroflyMissionConditions.temperature` property with Celsius support
66
+ - Improved error handling for weather conditions
41
67
 
42
68
  ## 1.0.3
43
69
 
44
- - Improved documentation
70
+ - Enhanced API documentation with examples
71
+ - Added detailed parameter descriptions
72
+ - Improved code documentation
45
73
 
46
74
  ## 1.0.2
47
75
 
48
- - Added more short hand properties
76
+ - Added shorthand properties for common mission parameters
77
+ - Improved property access methods
78
+ - Enhanced mission validation
49
79
 
50
80
  ## 1.0.1
51
81
 
52
- - Improved documentation
82
+ - Added initial API documentation
83
+ - Improved code comments
84
+ - Added basic usage examples
53
85
 
54
86
  ## 1.0.0
55
87
 
56
- - Initial commit
88
+ - Initial release of Aerofly Custom Missions
89
+ - Basic mission creation and editing functionality
90
+ - Support for essential mission parameters
@@ -1,4 +1,4 @@
1
- import { AeroflyConfigFileSet } from "./AeroflyConfigFileSet.js";
1
+ import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
2
2
  /**
3
3
  * @class
4
4
  * A translation for the mission title and description.
@@ -27,14 +27,18 @@ export class AeroflyLocalizedText {
27
27
  this.description = description;
28
28
  }
29
29
  /**
30
- * @param {number} index if used in an array will se the array index
30
+ * @returns {AeroflyConfigurationNode} to use in Aerofly FS4's `custom_missions_user.tmc`
31
+ */
32
+ getElement() {
33
+ return new AeroflyConfigurationNode("tmmission_definition_localized", "element")
34
+ .appendChild("string8u", "language", this.language)
35
+ .appendChild("string8", "title", this.title)
36
+ .appendChild("string8", "description", this.description);
37
+ }
38
+ /**
31
39
  * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
32
40
  */
33
- toString(index = 0) {
34
- return new AeroflyConfigFileSet(4, "tmmission_definition_localized", "element", String(index))
35
- .push("string8u", "language", this.language)
36
- .push("string8", "title", this.title)
37
- .push("string8", "description", this.description)
38
- .toString();
41
+ toString() {
42
+ return this.getElement().toString();
39
43
  }
40
44
  }
@@ -1,4 +1,4 @@
1
- import { AeroflyConfigFileSet } from "./AeroflyConfigFileSet.js";
1
+ import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
2
2
  import { AeroflyMissionConditions } from "./AeroflyMissionConditions.js";
3
3
  export const feetPerMeter = 3.28084;
4
4
  export const meterPerStatuteMile = 1609.344;
@@ -17,7 +17,7 @@ export class AeroflyMission {
17
17
  * @param {string} [additionalAttributes.description] text, mission briefing, etc
18
18
  * @param {AeroflyLocalizedText[]} [additionalAttributes.localizedTexts] translations for title and description
19
19
  * @param {?string} [additionalAttributes.tutorialName] will create a link to a tutorial page at https://www.aerofly.com/aircraft-tutorials/
20
- * @param {string[]} [additionalAttributes.tags]
20
+ * @param {string[]} [additionalAttributes.tags] free-text tags
21
21
  * @param {?boolean} [additionalAttributes.isFeatured] makes this mission pop up in "Challenges"
22
22
  * @param {?number} [additionalAttributes.difficulty] values between 0.00 and 2.00 have been encountered, but they seem to be without limit
23
23
  * @param {"cold_and_dark"|"before_start"|"taxi"|"takeoff"|"cruise"|"approach"|"landing"|"winch_launch"|"aerotow"|"pushback"} [additionalAttributes.flightSetting] of aircraft, like "taxi", "cruise"
@@ -69,30 +69,27 @@ export class AeroflyMission {
69
69
  this.conditions = conditions;
70
70
  }
71
71
  /**
72
- * @returns {string} indexed checkpoints
72
+ * @returns {AeroflyConfigurationNode[]} indexed checkpoints
73
73
  */
74
- getCheckpointsString() {
75
- return this.checkpoints
76
- .map((c, index) => {
77
- return c.toString(index);
78
- })
79
- .join("\n");
74
+ getCheckpointElements() {
75
+ return this.checkpoints.map((c, index) => {
76
+ return c.getElement(index);
77
+ });
80
78
  }
81
79
  /**
82
- * @returns {string} indexed checkpoints
80
+ * @returns {AeroflyConfigurationNode[]} indexed checkpoints
83
81
  */
84
- getLocalizedTextsString() {
85
- return this.localizedTexts
86
- .map((c, index) => {
87
- return c.toString(index);
88
- })
89
- .join("\n");
82
+ getLocalizedTextElements() {
83
+ return this.localizedTexts.map((c, index) => {
84
+ const el = c.getElement();
85
+ el.value = String(index);
86
+ return el;
87
+ });
90
88
  }
91
89
  /**
92
- * @throws {Error} on missing waypoints
93
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
90
+ * @returns {AeroflyConfigurationNode} for this mission
94
91
  */
95
- toString() {
92
+ getElement() {
96
93
  if (!this.origin.icao) {
97
94
  const firstCheckpoint = this.checkpoints[0];
98
95
  this.origin = {
@@ -113,59 +110,64 @@ export class AeroflyMission {
113
110
  alt: lastCheckpoint.altitude,
114
111
  };
115
112
  }
116
- const fileSet = new AeroflyConfigFileSet(3, "tmmission_definition", "mission");
117
- fileSet.push("string8", "title", this.title);
118
- fileSet.push("string8", "description", this.description);
113
+ const element = new AeroflyConfigurationNode("tmmission_definition", "mission");
114
+ element.appendChild("string8", "title", this.title);
115
+ element.appendChild("string8", "description", this.description);
119
116
  if (this.tutorialName !== null) {
120
- fileSet.push("string8", "tutorial_name", this.tutorialName, `Opens https://www.aerofly.com/aircraft-tutorials/${this.tutorialName}`);
117
+ element.appendChild("string8", "tutorial_name", this.tutorialName, `Opens https://www.aerofly.com/aircraft-tutorials/${this.tutorialName}`);
121
118
  }
122
119
  if (this.localizedTexts.length > 0) {
123
- fileSet.pushRaw(new AeroflyConfigFileSet(4, "list_tmmission_definition_localized", "localized_text")
124
- .pushRaw(this.getLocalizedTextsString())
125
- .toString());
120
+ element.append(new AeroflyConfigurationNode("list_tmmission_definition_localized", "localized_text").append(...this.getLocalizedTextElements()));
126
121
  }
127
122
  if (this.tags.length > 0) {
128
- fileSet.push("string8u", "tags", this.tags.join(" "));
123
+ element.appendChild("string8u", "tags", this.tags);
129
124
  }
130
125
  if (this.difficulty !== null) {
131
- fileSet.push("float64", "difficulty", this.difficulty);
126
+ element.appendChild("float64", "difficulty", this.difficulty);
132
127
  }
133
128
  if (this.isFeatured !== null) {
134
- fileSet.push("bool", "is_featured", this.isFeatured);
129
+ element.appendChild("bool", "is_featured", this.isFeatured);
135
130
  }
136
- fileSet.push("string8", "flight_setting", this.flightSetting);
137
- fileSet.push("string8u", "aircraft_name", this.aircraft.name);
131
+ element.appendChild("string8", "flight_setting", this.flightSetting);
132
+ element.appendChild("string8u", "aircraft_name", this.aircraft.name);
138
133
  /*if (this.aircraft.livery) {
139
- fileSet.push("string8", "aircraft_livery", this.aircraft.livery);
134
+ mission.createChild("string8", "aircraft_livery", this.aircraft.livery);
140
135
  }*/
141
- fileSet.push("stringt8c", "aircraft_icao", this.aircraft.icao);
142
- fileSet.push("stringt8c", "callsign", this.callsign);
143
- fileSet.push("stringt8c", "origin_icao", this.origin.icao);
144
- fileSet.push("tmvector2d", "origin_lon_lat", [this.origin.longitude, this.origin.latitude]);
145
- fileSet.push("float64", "origin_alt", this.origin.alt, `${Math.ceil(this.origin.alt * feetPerMeter)} ft MSL`);
146
- fileSet.push("float64", "origin_dir", this.origin.dir);
147
- fileSet.push("stringt8c", "destination_icao", this.destination.icao);
148
- fileSet.push("tmvector2d", "destination_lon_lat", [this.destination.longitude, this.destination.latitude]);
149
- fileSet.push("float64", "destination_alt", this.destination.alt, `${Math.ceil(this.destination.alt * feetPerMeter)} ft MSL`);
150
- fileSet.push("float64", "destination_dir", this.destination.dir);
136
+ element.appendChild("stringt8c", "aircraft_icao", this.aircraft.icao);
137
+ element.appendChild("stringt8c", "callsign", this.callsign);
138
+ element.appendChild("stringt8c", "origin_icao", this.origin.icao);
139
+ element.appendChild("tmvector2d", "origin_lon_lat", [this.origin.longitude, this.origin.latitude]);
140
+ element.appendChild("float64", "origin_alt", this.origin.alt, `${Math.ceil(this.origin.alt * feetPerMeter)} ft MSL`);
141
+ element.appendChild("float64", "origin_dir", this.origin.dir);
142
+ element.appendChild("stringt8c", "destination_icao", this.destination.icao);
143
+ element.appendChild("tmvector2d", "destination_lon_lat", [
144
+ this.destination.longitude,
145
+ this.destination.latitude,
146
+ ]);
147
+ element.appendChild("float64", "destination_alt", this.destination.alt, `${Math.ceil(this.destination.alt * feetPerMeter)} ft MSL`);
148
+ element.appendChild("float64", "destination_dir", this.destination.dir);
151
149
  if (this.distance !== null) {
152
- fileSet.push("float64", "distance", this.distance, `${Math.round(this.distance / 1000)} km`);
150
+ element.appendChild("float64", "distance", this.distance, `${Math.round(this.distance / 1000)} km`);
153
151
  }
154
152
  if (this.duration !== null) {
155
- fileSet.push("float64", "duration", this.duration, `${Math.round(this.duration / 60)} min`);
153
+ element.appendChild("float64", "duration", this.duration, `${Math.round(this.duration / 60)} min`);
156
154
  }
157
155
  if (this.isScheduled !== null) {
158
- fileSet.push("bool", "is_scheduled", this.isScheduled ? "true" : "false");
156
+ element.appendChild("bool", "is_scheduled", this.isScheduled);
159
157
  }
160
158
  if (this.finish !== null) {
161
- fileSet.pushRaw(this.finish.toString());
159
+ element.append(this.finish.getElement());
162
160
  }
163
- fileSet.pushRaw(this.conditions.toString());
161
+ element.append(this.conditions.getElement());
164
162
  if (this.checkpoints.length > 0) {
165
- fileSet.pushRaw(new AeroflyConfigFileSet(4, "list_tmmission_checkpoint", "checkpoints")
166
- .pushRaw(this.getCheckpointsString())
167
- .toString());
163
+ element.append(new AeroflyConfigurationNode("list_tmmission_checkpoint", "checkpoints").append(...this.getCheckpointElements()));
168
164
  }
169
- return fileSet.toString();
165
+ return element;
166
+ }
167
+ /**
168
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
169
+ */
170
+ toString() {
171
+ return this.getElement().toString();
170
172
  }
171
173
  }
@@ -1,4 +1,4 @@
1
- import { AeroflyConfigFileSet } from "./AeroflyConfigFileSet.js";
1
+ import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
2
2
  import { feetPerMeter } from "./AeroflyMission.js";
3
3
  /**
4
4
  * @class
@@ -34,7 +34,7 @@ export class AeroflyMissionCheckpoint {
34
34
  * @param {?number} [additionalAttributes.frequency] of runways or navigational aids, in Hz; multiply by 1000 for kHz, 1_000_000 for MHz
35
35
  * @param {?boolean} [additionalAttributes.flyOver] if waypoint is meant to be flown over
36
36
  */
37
- constructor(name, type, longitude, latitude, { altitude = 0, altitude_feet = null, altitudeConstraint = null, direction = null, slope = null, length = null, length_feet = null, frequency = null, flyOver = null, } = {}) {
37
+ constructor(name, type, longitude, latitude, { altitude = 0, altitude_feet = 0, altitudeConstraint = null, direction = null, slope = null, length = null, length_feet = 0, frequency = null, flyOver = null, } = {}) {
38
38
  this.type = type;
39
39
  this.name = name;
40
40
  this.longitude = longitude;
@@ -54,7 +54,7 @@ export class AeroflyMissionCheckpoint {
54
54
  }
55
55
  }
56
56
  /**
57
- * @param {number} altitude_feet
57
+ * @param {number} altitude_feet in feet
58
58
  */
59
59
  set altitude_feet(altitude_feet) {
60
60
  this.altitude = altitude_feet / feetPerMeter;
@@ -66,7 +66,7 @@ export class AeroflyMissionCheckpoint {
66
66
  return this.altitude * feetPerMeter;
67
67
  }
68
68
  /**
69
- * @param {number} length_feet
69
+ * @param {number} length_feet in feet
70
70
  */
71
71
  set length_feet(length_feet) {
72
72
  this.length = length_feet / feetPerMeter;
@@ -78,7 +78,7 @@ export class AeroflyMissionCheckpoint {
78
78
  return (this.length ?? 0) * feetPerMeter;
79
79
  }
80
80
  /**
81
- * @returns {string}
81
+ * @returns {string} with MHz / kHz attached
82
82
  */
83
83
  get frequency_string() {
84
84
  if (!this.frequency) {
@@ -93,29 +93,35 @@ export class AeroflyMissionCheckpoint {
93
93
  return String(this.frequency) + " Hz";
94
94
  }
95
95
  /**
96
- * @param {number} index if used in an array will se the array index
97
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
96
+ * @param {number} index default: 0
97
+ * @returns {AeroflyConfigurationNode} to use in Aerofly FS4's `custom_missions_user.tmc`
98
98
  */
99
- toString(index = 0) {
100
- const fileSet = new AeroflyConfigFileSet(5, "tmmission_checkpoint", "element", String(index))
101
- .push("string8u", "type", this.type)
102
- .push("string8u", "name", this.name)
103
- .push("vector2_float64", "lon_lat", [this.longitude, this.latitude])
104
- .push("float64", "altitude", this.altitude, `${Math.ceil(this.altitude_feet)} ft`)
105
- .push("float64", "direction", this.direction ?? (index === 0 ? -1 : 0))
106
- .push("float64", "slope", this.slope ?? 0);
99
+ getElement(index = 0) {
100
+ const element = new AeroflyConfigurationNode("tmmission_checkpoint", "element", String(index))
101
+ .appendChild("string8u", "type", this.type)
102
+ .appendChild("string8u", "name", this.name)
103
+ .appendChild("vector2_float64", "lon_lat", [this.longitude, this.latitude])
104
+ .appendChild("float64", "altitude", this.altitude, `${Math.ceil(this.altitude_feet)} ft`)
105
+ .appendChild("float64", "direction", this.direction ?? (index === 0 ? -1 : 0))
106
+ .appendChild("float64", "slope", this.slope ?? 0);
107
107
  if (this.altitudeConstraint !== null) {
108
- fileSet.push("bool", "alt_cst", this.altitudeConstraint);
108
+ element.appendChild("bool", "alt_cst", this.altitudeConstraint);
109
109
  }
110
110
  if (this.length) {
111
- fileSet.push("float64", "length", this.length ?? 0, `${Math.floor(this.length_feet)} ft`);
111
+ element.appendChild("float64", "length", this.length ?? 0, `${Math.floor(this.length_feet)} ft`);
112
112
  }
113
113
  if (this.frequency) {
114
- fileSet.push("float64", "frequency", this.frequency ?? 0, `${this.frequency_string}`);
114
+ element.appendChild("float64", "frequency", this.frequency ?? 0, `${this.frequency_string}`);
115
115
  }
116
116
  if (this.flyOver !== null) {
117
- fileSet.push("bool", "fly_over", this.flyOver);
117
+ element.appendChild("bool", "fly_over", this.flyOver);
118
118
  }
119
- return fileSet.toString();
119
+ return element;
120
+ }
121
+ /**
122
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
123
+ */
124
+ toString() {
125
+ return this.getElement().toString();
120
126
  }
121
127
  }
@@ -1,4 +1,4 @@
1
- import { AeroflyConfigFileSet } from "./AeroflyConfigFileSet.js";
1
+ import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
2
2
  import { meterPerStatuteMile } from "./AeroflyMission.js";
3
3
  import { AeroflyMissionConditionsCloud } from "./AeroflyMissionConditionsCloud.js";
4
4
  /**
@@ -26,7 +26,7 @@ export class AeroflyMissionConditions {
26
26
  direction: 0,
27
27
  speed: 0,
28
28
  gusts: 0,
29
- }, turbulenceStrength = 0, thermalStrength = 0, visibility = 25_000, visibility_sm = null, temperature = null, clouds = [], } = {}) {
29
+ }, turbulenceStrength = 0, thermalStrength = 0, visibility = 25000, visibility_sm = 0, temperature = 0, clouds = [], } = {}) {
30
30
  /**
31
31
  * @property {AeroflyMissionConditionsCloud[]} clouds for the whole flight
32
32
  */
@@ -87,36 +87,38 @@ export class AeroflyMissionConditions {
87
87
  return Math.sqrt(this.thermalStrength) * 50 - 15;
88
88
  }
89
89
  /**
90
- * @returns {string}
90
+ * @returns {AeroflyConfigurationNode[]} cloud elements
91
91
  */
92
- getCloudsString() {
92
+ getCloudElements() {
93
93
  return this.clouds
94
- .map((c, index) => {
95
- return c.toString(index);
96
- })
97
- .join("\n");
94
+ .slice(0, 2) // Aerofly FS4 supports max 2 cloud layers
95
+ .flatMap((c, index) => c.getElements(index));
98
96
  }
99
97
  /**
100
98
  * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
101
99
  */
102
- toString() {
100
+ getElement() {
103
101
  if (this.clouds.length < 1) {
104
102
  this.clouds = [new AeroflyMissionConditionsCloud(0, 0)];
105
103
  }
106
- return new AeroflyConfigFileSet(4, "tmmission_conditions", "conditions")
107
- .pushRaw(new AeroflyConfigFileSet(5, "tm_time_utc", "time")
108
- .push("int32", "time_year", this.time.getUTCFullYear())
109
- .push("int32", "time_month", this.time.getUTCMonth() + 1)
110
- .push("int32", "time_day", this.time.getUTCDate())
111
- .push("float64", "time_hours", this.time_hours, `${this.time_presentational} UTC`)
112
- .toString())
113
- .push("float64", "wind_direction", this.wind.direction)
114
- .push("float64", "wind_speed", this.wind.speed, "kts")
115
- .push("float64", "wind_gusts", this.wind.gusts, "kts")
116
- .push("float64", "turbulence_strength", this.turbulenceStrength)
117
- .push("float64", "thermal_strength", this.thermalStrength, `${this.temperature} °C`)
118
- .push("float64", "visibility", this.visibility, `${this.visibility_sm} SM`)
119
- .pushRaw(this.getCloudsString())
120
- .toString();
104
+ return new AeroflyConfigurationNode("tmmission_conditions", "conditions")
105
+ .append(new AeroflyConfigurationNode("tm_time_utc", "time")
106
+ .appendChild("int32", "time_year", this.time.getUTCFullYear())
107
+ .appendChild("int32", "time_month", this.time.getUTCMonth() + 1)
108
+ .appendChild("int32", "time_day", this.time.getUTCDate())
109
+ .appendChild("float64", "time_hours", this.time_hours, `${this.time_presentational} UTC`))
110
+ .appendChild("float64", "wind_direction", this.wind.direction)
111
+ .appendChild("float64", "wind_speed", this.wind.speed, "kts")
112
+ .appendChild("float64", "wind_gusts", this.wind.gusts, "kts")
113
+ .appendChild("float64", "turbulence_strength", this.turbulenceStrength)
114
+ .appendChild("float64", "thermal_strength", this.thermalStrength, `${this.temperature} °C`)
115
+ .appendChild("float64", "visibility", this.visibility, `${this.visibility_sm} SM`)
116
+ .append(...this.getCloudElements());
117
+ }
118
+ /**
119
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
120
+ */
121
+ toString() {
122
+ return this.getElement().toString();
121
123
  }
122
124
  }
@@ -1,3 +1,4 @@
1
+ import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
1
2
  import { feetPerMeter } from "./AeroflyMission.js";
2
3
  /**
3
4
  * @class
@@ -19,7 +20,7 @@ export class AeroflyMissionConditionsCloud {
19
20
  /**
20
21
  * @param {number} cover 0..1, percentage
21
22
  * @param {number} base_feet altitude, but in feet AGL instead of meters AGL
22
- * @returns {AeroflyMissionConditionsCloud}
23
+ * @returns {AeroflyMissionConditionsCloud} self
23
24
  */
24
25
  static createInFeet(cover, base_feet) {
25
26
  return new AeroflyMissionConditionsCloud(cover, base_feet / feetPerMeter);
@@ -55,10 +56,10 @@ export class AeroflyMissionConditionsCloud {
55
56
  return "OVC";
56
57
  }
57
58
  /**
58
- * @param {number} index if used in an array will se the array index
59
- * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
59
+ * @param {number} index if used in an array will set the array index
60
+ * @returns {AeroflyConfigurationNode[]} to use in Aerofly FS4's `custom_missions_user.tmc`
60
61
  */
61
- toString(index = 0) {
62
+ getElements(index = 0) {
62
63
  const getIndexString = (index) => {
63
64
  switch (index) {
64
65
  case 0:
@@ -72,9 +73,18 @@ export class AeroflyMissionConditionsCloud {
72
73
  }
73
74
  };
74
75
  const indexString = getIndexString(index);
75
- const comment = index > 1 ? "//" : "";
76
- return `\
77
- ${comment}<[float64][${indexString}_cover][${this.cover ?? 0}]> // ${this.cover_code}
78
- ${comment}<[float64][${indexString}_base][${this.base}]> // ${this.base_feet} ft AGL`;
76
+ return [
77
+ new AeroflyConfigurationNode("float64", `${indexString}_cover`, this.cover ?? 0, this.cover_code),
78
+ new AeroflyConfigurationNode("float64", `${indexString}_base`, this.base, `${this.base_feet} ft AGL`),
79
+ ];
80
+ }
81
+ /**
82
+ * @param {number} index if used in an array will set the array index
83
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
84
+ */
85
+ toString(index = 0) {
86
+ return this.getElements()
87
+ .map((element) => element.toString(index))
88
+ .join("\n");
79
89
  }
80
90
  }
@@ -1,4 +1,4 @@
1
- import { AeroflyConfigFileSet } from "./AeroflyConfigFileSet.js";
1
+ import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
2
2
  /**
3
3
  * @class
4
4
  * A target plane which the aircraft needs to cross.
@@ -21,10 +21,15 @@ export class AeroflyMissionTargetPlane {
21
21
  this.dir = dir;
22
22
  this.name = name;
23
23
  }
24
+ getElement() {
25
+ return new AeroflyConfigurationNode("tmmission_target_plane", this.name)
26
+ .appendChild("vector2_float64", "lon_lat", [this.longitude, this.latitude])
27
+ .appendChild("float64", "direction", this.dir);
28
+ }
29
+ /**
30
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
31
+ */
24
32
  toString() {
25
- return new AeroflyConfigFileSet(4, "tmmission_target_plane", this.name)
26
- .push("vector2_float64", "lon_lat", [this.longitude, this.latitude])
27
- .push("float64", "direction", this.dir)
28
- .toString();
33
+ return this.getElement().toString();
29
34
  }
30
35
  }
@@ -1,3 +1,4 @@
1
+ import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
1
2
  /**
2
3
  * @class
3
4
  * A list of flight plans.
@@ -13,16 +14,23 @@ export class AeroflyMissionsList {
13
14
  constructor(missions = []) {
14
15
  this.missions = missions;
15
16
  }
17
+ getElement() {
18
+ return new AeroflyConfigurationNode("file", "").append(new AeroflyConfigurationNode("tmmissions_list", "").append(new AeroflyConfigurationNode("list_tmmission_definition", "missions").append(...this.missions.map((m) => {
19
+ const mission = m.getElement();
20
+ mission._comment = `End of ${mission.name}`;
21
+ return mission;
22
+ }))));
23
+ }
16
24
  /**
17
25
  * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
18
26
  */
19
27
  toString() {
20
- const separator = "\n// -----------------------------------------------------------------------------\n";
21
- return `\
22
- <[file][][]
23
- <[tmmissions_list][][]
24
- <[list_tmmission_definition][missions][]${separator + this.missions.join(separator) + separator} >
25
- >
26
- >`;
28
+ return this.getElement().toString();
29
+ }
30
+ /**
31
+ * @returns {string} XML represenation of this mission list
32
+ */
33
+ toXmlString() {
34
+ return this.getElement().toXmlString();
27
35
  }
28
36
  }