@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
@@ -1,10 +1,18 @@
1
- import { AeroflyConfigFileSet, AeroflyMissionsList, AeroflyMission, AeroflyMissionConditions, AeroflyMissionConditionsCloud, AeroflyMissionCheckpoint, AeroflyLocalizedText, AeroflyMissionTargetPlane, } from "./index.js";
2
1
  import { strict as assert } from "node:assert";
2
+ import { AeroflyLocalizedText } from "./index.js";
3
+ import { AeroflyMission } from "./index.js";
4
+ import { AeroflyMissionCheckpoint } from "./index.js";
5
+ import { AeroflyMissionConditions } from "./index.js";
6
+ import { AeroflyMissionConditionsCloud } from "./index.js";
7
+ import { AeroflyMissionsList } from "./index.js";
8
+ import { AeroflyMissionTargetPlane } from "./index.js";
9
+ import { AeroflyConfigurationNode } from "./node/AeroflyConfigurationNode.js";
3
10
  const assertValidAeroflyStructure = (aeroflyString) => {
4
11
  const openingBrackets = aeroflyString.match(/</g);
5
12
  const closingBrackets = aeroflyString.match(/>/g);
6
13
  const openingBrackets2 = aeroflyString.match(/\[/g);
7
14
  const closingBrackets2 = aeroflyString.match(/\]/g);
15
+ assert.ok(openingBrackets?.length ?? 0 > 0, "Has opening <");
8
16
  assert.strictEqual(openingBrackets?.length, closingBrackets?.length, "Number of <> matches");
9
17
  assert.strictEqual(openingBrackets2?.length, closingBrackets2?.length, "Number of [] matches");
10
18
  };
@@ -12,16 +20,12 @@ const assertIncludes = (string, includes) => {
12
20
  assert.ok(string.includes(includes), `Includes "${includes}"`);
13
21
  };
14
22
  {
15
- const file = new AeroflyConfigFileSet(0, "file", "");
16
- file.pushRaw(new AeroflyConfigFileSet(1, "tmmissions_list", "")
17
- .pushRaw(new AeroflyConfigFileSet(2, "list_tmmission_definition", "missions")
18
- .pushRaw(new AeroflyConfigFileSet(3, "tmmission_definition", "mission")
19
- .push("string8", "title", "KCCR #1: Concord / Buchanan Field")
20
- .push("float64", "origin_alt", 1066.799965862401, "3500 ft MSL")
21
- .toString())
22
- .pushRaw(new AeroflyConfigFileSet(3, "tmmission_definition", "mission").toString())
23
- .toString())
24
- .toString());
23
+ const file = new AeroflyConfigurationNode("file", "");
24
+ file.append(new AeroflyConfigurationNode("tmmissions_list", "").append(new AeroflyConfigurationNode("list_tmmission_definition", "missions")
25
+ .append(new AeroflyConfigurationNode("tmmission_definition", "mission")
26
+ .appendChild("string8", "title", "KCCR #1: Concord / Buchanan Field")
27
+ .appendChild("float64", "origin_alt", 1066.799965862401, "3500 ft MSL"))
28
+ .append(new AeroflyConfigurationNode("tmmission_definition", "mission"))));
25
29
  assertValidAeroflyStructure(file.toString());
26
30
  console.log("✅ AeroflyMission test successful");
27
31
  }
@@ -202,6 +206,7 @@ const assertIncludes = (string, includes) => {
202
206
  assert.strictEqual(missionList.missions[0].aircraft.name, "c172");
203
207
  assert.strictEqual(missionList.missions[0].aircraft.icao, "C172");
204
208
  let missionListString = missionList.toString();
209
+ assert.strictEqual(missionListString, missionList.toString());
205
210
  assertIncludes(missionListString, "[origin]");
206
211
  assertIncludes(missionListString, "[tmmission_definition]");
207
212
  assertIncludes(missionListString, "[list_tmmission_checkpoint]");
@@ -248,5 +253,6 @@ const assertIncludes = (string, includes) => {
248
253
  assertValidAeroflyStructure(missionListString);
249
254
  //console.dir(missionList.missions[0], { depth: null });
250
255
  //console.log(missionListString);
256
+ //console.log(missionList.getElement().toXmlString());
251
257
  console.log("✅ AeroflyMissionsList test successful");
252
258
  }
@@ -0,0 +1,82 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _AeroflyConfigurationNode_instances, _AeroflyConfigurationNode_children, _AeroflyConfigurationNode_aeroflyEscape, _AeroflyConfigurationNode_xmlEscape;
7
+ export class AeroflyConfigurationNode {
8
+ constructor(type, name, value = "", _comment = "") {
9
+ this.type = type;
10
+ this.name = name;
11
+ this.value = value;
12
+ this._comment = _comment;
13
+ _AeroflyConfigurationNode_instances.add(this);
14
+ _AeroflyConfigurationNode_children.set(this, []);
15
+ }
16
+ append(...nodes) {
17
+ __classPrivateFieldGet(this, _AeroflyConfigurationNode_children, "f").push(...nodes);
18
+ return this;
19
+ }
20
+ appendChild(type, name, value = "", _comment = "") {
21
+ this.append(new AeroflyConfigurationNode(type, name, value, _comment));
22
+ return this;
23
+ }
24
+ get valueAsString() {
25
+ let value = "";
26
+ if (this.value instanceof Array) {
27
+ value = this.value.join(" ");
28
+ }
29
+ else if (typeof value === "boolean") {
30
+ value = this.value ? "true" : "false";
31
+ }
32
+ else {
33
+ value = this.value.toString();
34
+ }
35
+ return value;
36
+ }
37
+ toString(indent = 0) {
38
+ const indentation = " ".repeat(4 * indent);
39
+ let tag = `${indentation}<[${this.type}][${this.name}][${__classPrivateFieldGet(this, _AeroflyConfigurationNode_instances, "m", _AeroflyConfigurationNode_aeroflyEscape).call(this, this.valueAsString)}]`;
40
+ if (__classPrivateFieldGet(this, _AeroflyConfigurationNode_children, "f").length > 0) {
41
+ tag += "\n";
42
+ tag += __classPrivateFieldGet(this, _AeroflyConfigurationNode_children, "f").map((child) => child.toString(indent + 1)).join("\n");
43
+ tag += `\n${indentation}>`;
44
+ }
45
+ else {
46
+ tag += ">";
47
+ }
48
+ if (this._comment) {
49
+ tag += ` // ${this._comment.replace(/[\n\r]/g, " ")}`;
50
+ }
51
+ return tag;
52
+ }
53
+ toXmlString(indent = 0) {
54
+ const indentation = " ".repeat(4 * indent);
55
+ const name = this.name || this.type;
56
+ let tag = indentation;
57
+ if (__classPrivateFieldGet(this, _AeroflyConfigurationNode_children, "f").length > 0) {
58
+ const index = this.valueAsString ? ` index="${__classPrivateFieldGet(this, _AeroflyConfigurationNode_instances, "m", _AeroflyConfigurationNode_xmlEscape).call(this, this.valueAsString)}"` : "";
59
+ tag += `<${name} type="${this.type}"${index}>`;
60
+ tag += "\n";
61
+ tag += __classPrivateFieldGet(this, _AeroflyConfigurationNode_children, "f").map((child) => child.toXmlString(indent + 1)).join("\n");
62
+ tag += `\n${indentation}</${name}>`;
63
+ }
64
+ else {
65
+ tag += `<${name} type="${this.type}">${__classPrivateFieldGet(this, _AeroflyConfigurationNode_instances, "m", _AeroflyConfigurationNode_xmlEscape).call(this, this.valueAsString)}</${name}>`;
66
+ }
67
+ if (this._comment) {
68
+ tag += ` <!-- ${__classPrivateFieldGet(this, _AeroflyConfigurationNode_instances, "m", _AeroflyConfigurationNode_xmlEscape).call(this, this._comment)} -->`;
69
+ }
70
+ return tag;
71
+ }
72
+ }
73
+ _AeroflyConfigurationNode_children = new WeakMap(), _AeroflyConfigurationNode_instances = new WeakSet(), _AeroflyConfigurationNode_aeroflyEscape = function _AeroflyConfigurationNode_aeroflyEscape(text) {
74
+ return text.replace(/\[/g, "(").replace(/\]/g, ")");
75
+ }, _AeroflyConfigurationNode_xmlEscape = function _AeroflyConfigurationNode_xmlEscape(text) {
76
+ return text
77
+ .replace(/&/g, "&amp;")
78
+ .replace(/</g, "&lt;")
79
+ .replace(/>/g, "&gt;")
80
+ .replace(/"/g, "&quot;")
81
+ .replace(/'/g, "&#039;");
82
+ };
@@ -1,7 +1,6 @@
1
1
  <[file][][]
2
2
  <[tmmissions_list][][]
3
3
  <[list_tmmission_definition][missions][]
4
- // -----------------------------------------------------------------------------
5
4
  <[tmmission_definition][mission][]
6
5
  <[string8][title][KCCR #1: Concord / Buchanan Field]>
7
6
  <[string8][description][It is a gusty, clear early morning, and you are 8 NM to the north of the towered airport Concord / Buchanan Field (27ft). As the wind is 11 kts from 190°, the main landing runway is 19L (191° / 844m). Fly the pattern and land safely.
@@ -10,7 +9,7 @@
10
9
  - Local navigational aids: VOR/DME CCR (117.00) 3.4 NM to the north]>
11
10
  <[string8][tutorial_name][c172]> // Opens https://www.aerofly.com/aircraft-tutorials/c172
12
11
  <[list_tmmission_definition_localized][localized_text][]
13
- <[tmmission_definition_localized][element][0]
12
+ <[tmmission_definition_localized][element][0]
14
13
  <[string8u][language][de]>
15
14
  <[string8][title][Landeübung #1]>
16
15
  <[string8][description][Probier die Landung]>
@@ -21,7 +20,6 @@
21
20
  <[bool][is_featured][true]>
22
21
  <[string8][flight_setting][cruise]>
23
22
  <[string8u][aircraft_name][c172]>
24
- //<[string8u][aircraft_livery][]>
25
23
  <[stringt8c][aircraft_icao][C172]>
26
24
  <[stringt8c][callsign][N51911]>
27
25
  <[stringt8c][origin_icao][KCCR]>
@@ -32,7 +30,7 @@
32
30
  <[tmvector2d][destination_lon_lat][-70.6139 41.3934]>
33
31
  <[float64][destination_alt][20]> // 66 ft MSL
34
32
  <[float64][destination_dir][221]>
35
- <[float64][distance][1400]> // 2 km
33
+ <[float64][distance][1400]> // 1 km
36
34
  <[float64][duration][7200]> // 120 min
37
35
  <[bool][is_scheduled][true]>
38
36
  <[tmmission_target_plane][finish][]
@@ -95,8 +93,7 @@
95
93
  <[bool][fly_over][false]>
96
94
  >
97
95
  >
98
- >
99
- // -----------------------------------------------------------------------------
96
+ > // End of mission
100
97
  >
101
98
  >
102
99
  >
@@ -0,0 +1,100 @@
1
+ <file type="file">
2
+ <tmmissions_list type="tmmissions_list">
3
+ <missions type="list_tmmission_definition">
4
+ <mission type="tmmission_definition">
5
+ <title type="string8">KCCR #1: Concord / Buchanan Field</title>
6
+ <description type="string8">It is a gusty, clear early morning, and you are 8 NM to the north of the towered airport Concord / Buchanan Field (27ft). As the wind is 11 kts from 190°, the main landing runway is 19L (191°
7
+ / 844m). Fly the pattern and land safely.
8
+
9
+ - Local tower / CTAF frequency: 123.90
10
+ - Local navigational aids: VOR/DME CCR (117.00) 3.4 NM to the north</description>
11
+ <tutorial_name type="string8">c172</tutorial_name> <!-- Opens https://www.aerofly.com/aircraft-tutorials/c172 -->
12
+ <localized_text type="list_tmmission_definition_localized">
13
+ <element type="tmmission_definition_localized" index="0">
14
+ <language type="string8u">de</language>
15
+ <title type="string8">Landeübung #1</title>
16
+ <description type="string8">Probier die Landung</description>
17
+ </element>
18
+ </localized_text>
19
+ <tags type="string8u">approach pattern</tags>
20
+ <difficulty type="float64">1</difficulty>
21
+ <is_featured type="bool">true</is_featured>
22
+ <flight_setting type="string8">cruise</flight_setting>
23
+ <aircraft_name type="string8u">c172</aircraft_name>
24
+ <aircraft_icao type="stringt8c">C172</aircraft_icao>
25
+ <callsign type="stringt8c">N51911</callsign>
26
+ <origin_icao type="stringt8c">KCCR</origin_icao>
27
+ <origin_lon_lat type="tmvector2d">-122.0736009331662 38.122300745843944</origin_lon_lat>
28
+ <origin_alt type="float64">1066.799965862401</origin_alt> <!-- 3500 ft MSL -->
29
+ <origin_dir type="float64">174.37511511143452</origin_dir>
30
+ <destination_icao type="stringt8c">KMVY</destination_icao>
31
+ <destination_lon_lat type="tmvector2d">-70.6139 41.3934</destination_lon_lat>
32
+ <destination_alt type="float64">20</destination_alt> <!-- 66 ft MSL -->
33
+ <destination_dir type="float64">221</destination_dir>
34
+ <distance type="float64">1400</distance> <!-- 1 km -->
35
+ <duration type="float64">7200</duration> <!-- 120 min -->
36
+ <is_scheduled type="bool">true</is_scheduled>
37
+ <finish type="tmmission_target_plane">
38
+ <lon_lat type="vector2_float64">0 1</lon_lat>
39
+ <direction type="float64">2</direction>
40
+ </finish>
41
+ <conditions type="tmmission_conditions">
42
+ <time type="tm_time_utc">
43
+ <time_year type="int32">2024</time_year>
44
+ <time_month type="int32">6</time_month>
45
+ <time_day type="int32">14</time_day>
46
+ <time_hours type="float64">13.260555555555555</time_hours> <!-- 13:15:38 UTC -->
47
+ </time>
48
+ <wind_direction type="float64">190</wind_direction>
49
+ <wind_speed type="float64">11</wind_speed> <!-- kts -->
50
+ <wind_gusts type="float64">22</wind_gusts> <!-- kts -->
51
+ <turbulence_strength type="float64">1</turbulence_strength>
52
+ <thermal_strength type="float64">0.5184</thermal_strength> <!-- 21 °C -->
53
+ <visibility type="float64">14484.096000000001</visibility> <!-- 9 SM -->
54
+ <cloud_cover type="float64">0.1</cloud_cover> <!-- CLR -->
55
+ <cloud_base type="float64">1523.9999512320016</cloud_base> <!-- 5000 ft AGL -->
56
+ <cirrus_cover type="float64">0.2</cirrus_cover> <!-- FEW -->
57
+ <cirrus_base type="float64">2285.9999268480024</cirrus_base> <!-- 7500 ft AGL -->
58
+ </conditions>
59
+ <checkpoints type="list_tmmission_checkpoint">
60
+ <element type="tmmission_checkpoint" index="0">
61
+ <type type="string8u">origin</type>
62
+ <name type="string8u">KCCR</name>
63
+ <lon_lat type="vector2_float64">-122.057 37.9897</lon_lat>
64
+ <altitude type="float64">8</altitude> <!-- 27 ft -->
65
+ <direction type="float64">-1</direction>
66
+ <slope type="float64">0</slope>
67
+ </element>
68
+ <element type="tmmission_checkpoint" index="1">
69
+ <type type="string8u">departure_runway</type>
70
+ <name type="string8u">19L</name>
71
+ <lon_lat type="vector2_float64">-122.05504061196366 37.993168229891225</lon_lat>
72
+ <altitude type="float64">0</altitude> <!-- 0 ft -->
73
+ <direction type="float64">0</direction>
74
+ <slope type="float64">0</slope>
75
+ <length type="float64">844.2959729825288</length> <!-- 2770 ft -->
76
+ </element>
77
+ <element type="tmmission_checkpoint" index="2">
78
+ <type type="string8u">destination_runway</type>
79
+ <name type="string8u">24</name>
80
+ <lon_lat type="vector2_float64">-70.60730234370952 41.399093035543366</lon_lat>
81
+ <altitude type="float64">20</altitude> <!-- 66 ft -->
82
+ <direction type="float64">0</direction>
83
+ <slope type="float64">0</slope>
84
+ <length type="float64">1677.6191463161874</length> <!-- 5504 ft -->
85
+ <frequency type="float64">108700000</frequency> <!-- 108.7 MHz -->
86
+ </element>
87
+ <element type="tmmission_checkpoint" index="3">
88
+ <type type="string8u">destination</type>
89
+ <name type="string8u">KMVY</name>
90
+ <lon_lat type="vector2_float64">-70.6139 41.3934</lon_lat>
91
+ <altitude type="float64">20</altitude> <!-- 66 ft -->
92
+ <direction type="float64">0</direction>
93
+ <slope type="float64">0</slope>
94
+ <fly_over type="bool">false</fly_over>
95
+ </element>
96
+ </checkpoints>
97
+ </mission> <!-- End of mission -->
98
+ </missions>
99
+ </tmmissions_list>
100
+ </file>
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@fboes/aerofly-custom-missions",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Builder for Aerofly FS4 Custom Missions Files",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
7
  "test": "node ./dist/index.test.js",
8
8
  "start": "node ./dist/index.js",
9
9
  "prettier": "npx prettier --cache --write .",
10
- "eslint": "npx eslint **/*.js --fix",
10
+ "eslint": "npx eslint src/*.ts --fix",
11
11
  "tsc": "npx tsc --build",
12
12
  "tsc-watch": "npx tsc --watch",
13
13
  "build": "npm run tsc && npm run eslint && npm run prettier"
@@ -21,15 +21,14 @@
21
21
  "license": "MIT",
22
22
  "type": "module",
23
23
  "devDependencies": {
24
- "@eslint/js": "^9.5.0",
25
- "@types/node": "^20.14.2",
26
- "eslint": "^9.5.0",
24
+ "@types/node": "^20.11.19",
25
+ "@typescript-eslint/eslint-plugin": "^7.0.1",
26
+ "@typescript-eslint/parser": "^7.0.1",
27
+ "eslint": "^8.56.0",
27
28
  "eslint-config-prettier": "^9.1.0",
28
- "eslint-plugin-jsdoc": "^48.2.12",
29
- "eslint-plugin-prettier": "^5.1.3",
30
- "globals": "^15.4.0",
31
- "prettier": "3.3.2",
32
- "typescript": "^5.4.5"
29
+ "eslint-plugin-jsdoc": "^48.1.0",
30
+ "prettier": "3.2.4",
31
+ "typescript": "^5.3.3"
33
32
  },
34
33
  "types": "./types/index.d.ts"
35
34
  }
@@ -0,0 +1,74 @@
1
+ import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
2
+
3
+ /**
4
+ * @class
5
+ * A translation for the mission title and description.
6
+ */
7
+ export class AeroflyLocalizedText {
8
+ /**
9
+ * @property {string} language ISO 639-1 like
10
+ * - br
11
+ * - cn
12
+ * - de
13
+ * - es
14
+ * - fr
15
+ * - id
16
+ * - it
17
+ * - jp
18
+ * - kr
19
+ * - pl
20
+ * - sv
21
+ * - tr
22
+ */
23
+ language: string;
24
+
25
+ /**
26
+ * @property {string} title of this flight plan
27
+ */
28
+ title: string;
29
+
30
+ /**
31
+ * @property {string} description text, mission briefing, etc
32
+ */
33
+ description: string;
34
+
35
+ /**
36
+ * @param {string} language ISO 639-1 like
37
+ * - br
38
+ * - cn
39
+ * - de
40
+ * - es
41
+ * - fr
42
+ * - id
43
+ * - it
44
+ * - jp
45
+ * - kr
46
+ * - pl
47
+ * - sv
48
+ * - tr
49
+ * @param {string} title of this flight plan
50
+ * @param {string} description text, mission briefing, etc
51
+ */
52
+ constructor(language: string, title: string, description: string) {
53
+ this.language = language;
54
+ this.title = title;
55
+ this.description = description;
56
+ }
57
+
58
+ /**
59
+ * @returns {AeroflyConfigurationNode} to use in Aerofly FS4's `custom_missions_user.tmc`
60
+ */
61
+ getElement(): AeroflyConfigurationNode {
62
+ return new AeroflyConfigurationNode("tmmission_definition_localized", "element")
63
+ .appendChild("string8u", "language", this.language)
64
+ .appendChild("string8", "title", this.title)
65
+ .appendChild("string8", "description", this.description);
66
+ }
67
+
68
+ /**
69
+ * @returns {string} to use in Aerofly FS4's `custom_missions_user.tmc`
70
+ */
71
+ toString(): string {
72
+ return this.getElement().toString();
73
+ }
74
+ }