Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b12e139b2c | ||
|
|
b1403b3066 | ||
|
|
3376a6f35d | ||
|
|
1d22597d3f | ||
|
|
467336590b | ||
|
|
5ce9a0a299 | ||
|
|
dff16702d4 | ||
|
|
97e02a332a | ||
|
|
9acdec8ae6 | ||
|
|
5ad232b83e | ||
|
|
e39815b44c | ||
|
|
09a52f18ab | ||
|
|
e06948d99d | ||
|
|
51fe8f095e | ||
|
|
8a0eaab753 | ||
|
|
3c49ba9d47 | ||
|
|
da1527c945 | ||
|
|
459e3a965a | ||
|
|
b1f1ef4a28 | ||
|
|
3c306ca039 | ||
|
|
92fd83615d | ||
|
|
29103533fb | ||
|
|
5aa5f731a6 | ||
|
|
1982df9f16 |
20
CHANGELOG.md
@@ -1,5 +1,25 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v.1.5.0
|
||||
|
||||
- Add Towrope Configuration dialog
|
||||
|
||||
## v.1.4.1
|
||||
|
||||
- Fix crash by missing parameter
|
||||
|
||||
## v.1.4.0
|
||||
|
||||
- Add Douglas C-47 as a tow plane
|
||||
- Add possibility to take off in the bush
|
||||
- Add a separate engine sound for the DR400
|
||||
- Add French translation of meta data
|
||||
- Add possibility to save/load route waypoints with description
|
||||
|
||||
## v.1.3.0
|
||||
|
||||
- Add engine sound for AI aircrafts
|
||||
|
||||
## v.1.2.0
|
||||
|
||||
- Add possibility to change the flight plan of AI tow plane.
|
||||
|
||||
266
FGData/AI/Aircraft/Aerotow/C-47/Models/c-47-ai.xml
Normal file
@@ -0,0 +1,266 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<description>C-47</description>
|
||||
<path>c-47.ac</path>
|
||||
|
||||
<offsets>
|
||||
<pitch-deg>11.8</pitch-deg>
|
||||
<z-m>2.65</z-m>
|
||||
</offsets>
|
||||
|
||||
<sound>
|
||||
<path>AI/Aircraft/Aerotow/C-47/Sound/sound.xml</path>
|
||||
</sound>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>roueG</object-name>
|
||||
<object-name>axeG1</object-name>
|
||||
<object-name>axeG2</object-name>
|
||||
<object-name>axeG3</object-name>
|
||||
<object-name>axeG4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>70</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>80</ind><dep>17.5</dep></entry>
|
||||
<entry><ind>90</ind><dep>35</dep></entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>-4.24966</x-m>
|
||||
<y-m>-2.814 </y-m>
|
||||
<z-m>-1.24846</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>1</y>
|
||||
<z>0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>translate</type>
|
||||
<object-name>roueG</object-name>
|
||||
<object-name>axeG1</object-name>
|
||||
<object-name>axeG2</object-name>
|
||||
<object-name>axeG4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.0 </dep></entry>
|
||||
<entry><ind> 70 </ind><dep> 0.0 </dep></entry>
|
||||
<entry><ind>80</ind><dep>.35</dep></entry>
|
||||
<entry><ind>90</ind><dep>.7</dep></entry>
|
||||
</interpolation>
|
||||
<axis>
|
||||
<x> 0 </x>
|
||||
<y> 0 </y>
|
||||
<z> 1 </z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>axeG4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>70</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>80</ind><dep>-5</dep></entry>
|
||||
<entry><ind>90</ind><dep>-10</dep></entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>-4.23614</x-m>
|
||||
<y-m>-2.814</y-m>
|
||||
<z-m>-3.00605</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>1</y>
|
||||
<z>0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>roueD</object-name>
|
||||
<object-name>axeD1</object-name>
|
||||
<object-name>axeD2</object-name>
|
||||
<object-name>axeD3</object-name>
|
||||
<object-name>axeD4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>70</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>80</ind><dep>17.5</dep></entry>
|
||||
<entry><ind>90</ind><dep>35</dep></entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>-4.24966</x-m>
|
||||
<y-m>-2.814 </y-m>
|
||||
<z-m>-1.40846</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>1</y>
|
||||
<z>0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>translate</type>
|
||||
<object-name>roueD</object-name>
|
||||
<object-name>axeD1</object-name>
|
||||
<object-name>axeD2</object-name>
|
||||
<object-name>axeD4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.0 </dep></entry>
|
||||
<entry><ind> 70 </ind><dep> 0.0 </dep></entry>
|
||||
<entry><ind>80</ind><dep>.35</dep></entry>
|
||||
<entry><ind>90</ind><dep>.7</dep></entry>
|
||||
</interpolation>
|
||||
<axis>
|
||||
<x> 0 </x>
|
||||
<y> 0 </y>
|
||||
<z> 1 </z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>axeD4</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>70</ind><dep>0.0</dep></entry>
|
||||
<entry><ind>80</ind><dep>-5</dep></entry>
|
||||
<entry><ind>90</ind><dep>-10</dep></entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>-4.23614</x-m>
|
||||
<y-m> 2.814</y-m>
|
||||
<z-m>-3.00605</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>1</y>
|
||||
<z>0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<name>leftprop</name>
|
||||
<object-name>heliceL</object-name>
|
||||
<object-name>propblurL</object-name>
|
||||
<object-name>propdiscL</object-name>
|
||||
</animation>
|
||||
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>propblurL</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>100</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>propdiskL</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>50</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>heliceL</object-name>
|
||||
<condition>
|
||||
<less-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>60</value>
|
||||
</less-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>spin</type>
|
||||
<object-name>heliceL</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>10</factor>
|
||||
<center>
|
||||
<x-m>-7.44425</x-m>
|
||||
<y-m>-2.814</y-m>
|
||||
<z-m>-1.189</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x> -1.000 </x>
|
||||
<y> 0.000 </y>
|
||||
<z> 0.000 </z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<name>rightprop</name>
|
||||
<object-name>heliceR</object-name>
|
||||
<object-name>propblurR</object-name>
|
||||
<object-name>propdiscR</object-name>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>propblurR</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>100</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>propdiskR</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>50</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>heliceR</object-name>
|
||||
<condition>
|
||||
<less-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>60</value>
|
||||
</less-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>spin</type>
|
||||
<object-name>heliceR</object-name>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor> 10 </factor>
|
||||
<center>
|
||||
<x-m>-7.44425</x-m>
|
||||
<y-m> 2.814</y-m>
|
||||
<z-m>-1.189</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x> 1.000 </x>
|
||||
<y> 0.000 </y>
|
||||
<z> 0.000 </z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
423789
FGData/AI/Aircraft/Aerotow/C-47/Models/c-47.ac
Normal file
BIN
FGData/AI/Aircraft/Aerotow/C-47/Models/prop.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
FGData/AI/Aircraft/Aerotow/C-47/Models/texture.png
Normal file
|
After Width: | Height: | Size: 728 KiB |
BIN
FGData/AI/Aircraft/Aerotow/C-47/Sound/Croisiere_ext_1.wav
Normal file
BIN
FGData/AI/Aircraft/Aerotow/C-47/Sound/Croisiere_ext_2.wav
Normal file
BIN
FGData/AI/Aircraft/Aerotow/C-47/Sound/Croisiere_int_1.wav
Normal file
BIN
FGData/AI/Aircraft/Aerotow/C-47/Sound/Croisiere_int_2.wav
Normal file
25
FGData/AI/Aircraft/Aerotow/C-47/Sound/sound.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<fx>
|
||||
<engine>
|
||||
<name>engine</name>
|
||||
<mode>looped</mode>
|
||||
<path>Croisiere_ext_2.wav</path>
|
||||
<condition>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
</condition>
|
||||
<volume>
|
||||
<offset>-0.80</offset>
|
||||
</volume>
|
||||
<pitch>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>0.012</factor>
|
||||
<offset>0.3</offset>
|
||||
<min>0.3</min>
|
||||
<max>1.0</max>
|
||||
</pitch>
|
||||
<reference-dist>150.0</reference-dist>
|
||||
<max-dist>5000.0</max-dist>
|
||||
</engine>
|
||||
</fx>
|
||||
</PropertyList>
|
||||
7
FGData/AI/Aircraft/Aerotow/Cub/Models/Cub-ai.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList include="AI/Aircraft/Cub/Models/Cub-ai.xml">
|
||||
<sound>
|
||||
<path>AI/Aircraft/Aerotow/Sound/sound.xml</path>
|
||||
</sound>
|
||||
</PropertyList>
|
||||
66879
FGData/AI/Aircraft/Aerotow/Cub/Models/Cub.ac
Normal file
BIN
FGData/AI/Aircraft/Aerotow/Cub/Models/placards.png
Normal file
|
After Width: | Height: | Size: 423 KiB |
BIN
FGData/AI/Aircraft/Aerotow/Cub/Models/propeller.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
FGData/AI/Aircraft/Aerotow/Cub/Models/propeller_slow.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
FGData/AI/Aircraft/Aerotow/Cub/Models/texture.png
Normal file
|
After Width: | Height: | Size: 4.6 MiB |
7
FGData/AI/Aircraft/Aerotow/DR400/Models/dr400-ai.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList include="AI/Aircraft/DR400/Models/dr400-ai.xml">
|
||||
<sound>
|
||||
<path>AI/Aircraft/Aerotow/DR400/Sound/sound.xml</path>
|
||||
</sound>
|
||||
</PropertyList>
|
||||
44494
FGData/AI/Aircraft/Aerotow/DR400/Models/dr400.ac
Normal file
48
FGData/AI/Aircraft/Aerotow/DR400/Models/light/GreenLight.ac
Normal file
@@ -0,0 +1,48 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Light"
|
||||
data 4
|
||||
Mesh
|
||||
texture "lights.png"
|
||||
texrep 1 1
|
||||
crease 45.000000
|
||||
numvert 12
|
||||
0.5 0.5 0
|
||||
-0.5 0.5 0
|
||||
-0.5 -0.5 0
|
||||
0.5 -0.5 0
|
||||
0 -0.5 -0.5
|
||||
0 -0.5 0.5
|
||||
0 0.5 0.5
|
||||
0 0.5 -0.5
|
||||
0.5 0 -0.5
|
||||
-0.5 0 -0.5
|
||||
-0.5 0 0.5
|
||||
0.5 0 0.5
|
||||
numsurf 3
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
0 0.948961138725 0.442461937666
|
||||
1 0.563538074493 0.442461848259
|
||||
2 0.563538074493 0.0570387989283
|
||||
3 0.948961138725 0.0570387542248
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
7 0.948961257935 0.0570388287306
|
||||
6 0.948961257935 0.442461937666
|
||||
5 0.563538074493 0.442461937666
|
||||
4 0.563538074493 0.0570387542248
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
11 0.948961257935 0.442461937666
|
||||
10 0.563538193703 0.442461937666
|
||||
9 0.563538074493 0.0570388287306
|
||||
8 0.948961257935 0.0570387542248
|
||||
kids 0
|
||||
38
FGData/AI/Aircraft/Aerotow/DR400/Models/light/GreenLight.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>GreenLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
48
FGData/AI/Aircraft/Aerotow/DR400/Models/light/RedLight.ac
Normal file
@@ -0,0 +1,48 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Light"
|
||||
data 4
|
||||
Mesh
|
||||
texture "lights.png"
|
||||
texrep 1 1
|
||||
crease 45.000000
|
||||
numvert 12
|
||||
0.5 0.5 0
|
||||
-0.5 0.5 0
|
||||
-0.5 -0.5 0
|
||||
0.5 -0.5 0
|
||||
0 -0.5 -0.5
|
||||
0 -0.5 0.5
|
||||
0 0.5 0.5
|
||||
0 0.5 -0.5
|
||||
0.5 0 -0.5
|
||||
-0.5 0 -0.5
|
||||
-0.5 0 0.5
|
||||
0.5 0 0.5
|
||||
numsurf 3
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
0 0.448961138725 0.442461937666
|
||||
1 0.0635380744934 0.442461848259
|
||||
2 0.0635380744934 0.0570387989283
|
||||
3 0.448961138725 0.0570387542248
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
7 0.448961257935 0.0570388287306
|
||||
6 0.448961257935 0.442461937666
|
||||
5 0.0635380744934 0.442461937666
|
||||
4 0.0635380744934 0.0570387542248
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
11 0.448961257935 0.442461937666
|
||||
10 0.0635381937027 0.442461937666
|
||||
9 0.0635380744934 0.0570388287306
|
||||
8 0.448961257935 0.0570387542248
|
||||
kids 0
|
||||
38
FGData/AI/Aircraft/Aerotow/DR400/Models/light/RedLight.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>RedLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>WhiteLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<path>toggle-white.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Cube</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Cube</object-name>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>0</ind>
|
||||
<dep>0.25</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>20</ind>
|
||||
<dep>.50</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1500</ind>
|
||||
<dep>5</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
48
FGData/AI/Aircraft/Aerotow/DR400/Models/light/WhiteLight.ac
Normal file
@@ -0,0 +1,48 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Light"
|
||||
data 4
|
||||
Mesh
|
||||
texture "lights.png"
|
||||
texrep 1 1
|
||||
crease 45.000000
|
||||
numvert 12
|
||||
0.5 0.5 0
|
||||
-0.5 0.5 0
|
||||
-0.5 -0.5 0
|
||||
0.5 -0.5 0
|
||||
0 -0.5 -0.5
|
||||
0 -0.5 0.5
|
||||
0 0.5 0.5
|
||||
0 0.5 -0.5
|
||||
0.5 0 -0.5
|
||||
-0.5 0 -0.5
|
||||
-0.5 0 0.5
|
||||
0.5 0 0.5
|
||||
numsurf 3
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
0 0.943492412567 0.942461967468
|
||||
1 0.558069348335 0.942461848259
|
||||
2 0.558069348335 0.557038784027
|
||||
3 0.943492412567 0.557038784027
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
7 0.943492531776 0.557038843632
|
||||
6 0.943492531776 0.942461967468
|
||||
5 0.558069348335 0.942461967468
|
||||
4 0.558069348335 0.557038784027
|
||||
SURF 0x20
|
||||
mat 1
|
||||
refs 4
|
||||
11 0.943492531776 0.942461967468
|
||||
10 0.558069467545 0.942461967468
|
||||
9 0.558069348335 0.557038843632
|
||||
8 0.943492531776 0.557038784027
|
||||
kids 0
|
||||
38
FGData/AI/Aircraft/Aerotow/DR400/Models/light/WhiteLight.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>WhiteLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
38
FGData/AI/Aircraft/Aerotow/DR400/Models/light/beacon.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>RedLight.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red> 1.0 </red>
|
||||
<green> 1.0 </green>
|
||||
<blue> 1.0 </blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind> 0 </ind><dep> 0.25 </dep></entry>
|
||||
<entry><ind> 20 </ind><dep> 0.50 </dep></entry>
|
||||
<entry><ind> 1500 </ind><dep> 5.00 </dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
3268
FGData/AI/Aircraft/Aerotow/DR400/Models/light/light-cone.ac
Normal file
12
FGData/AI/Aircraft/Aerotow/DR400/Models/light/light-cone.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>light-cone.ac</path>
|
||||
|
||||
<effect>
|
||||
<inherits-from>Effects/light-cone</inherits-from>
|
||||
<object-name>Cone</object-name>
|
||||
</effect>
|
||||
|
||||
</PropertyList>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
BIN
FGData/AI/Aircraft/Aerotow/DR400/Models/light/lights.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,95 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "Material" rgb 0.92013 0.92013 0.92013 amb 0.5 0.5 0.5 emis 0 0 0 spec 1 1 1 shi 32 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Cube"
|
||||
data 4
|
||||
Cube
|
||||
crease 30
|
||||
numvert 12
|
||||
0.009182 0.007635 -0.00748
|
||||
0.001377 -0.000598 -0.00748
|
||||
-0.005892 0.002033 -0.00748
|
||||
-0.001976 0.012793 -0.00748
|
||||
0.009182 0.007635 0.00748
|
||||
0.001377 -0.000598 0.00748
|
||||
-0.005892 0.002033 0.00748
|
||||
-0.001976 0.012793 0.00748
|
||||
-0.009655 -0.008307 -0.00748
|
||||
-0.009655 -0.008307 0.00748
|
||||
0.002203 -0.01154 -0.00748
|
||||
0.002203 -0.01154 0.00748
|
||||
numsurf 10
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
1 0 0
|
||||
2 0 0
|
||||
3 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
4 0 0
|
||||
7 0 0
|
||||
6 0 0
|
||||
5 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
4 0 0
|
||||
5 0 0
|
||||
1 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
2 0 0
|
||||
6 0 0
|
||||
7 0 0
|
||||
3 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
6 0 0
|
||||
2 0 0
|
||||
8 0 0
|
||||
9 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
1 0 0
|
||||
5 0 0
|
||||
11 0 0
|
||||
10 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
5 0 0
|
||||
6 0 0
|
||||
9 0 0
|
||||
11 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
2 0 0
|
||||
1 0 0
|
||||
10 0 0
|
||||
8 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
10 0 0
|
||||
11 0 0
|
||||
9 0 0
|
||||
8 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
3 0 0
|
||||
7 0 0
|
||||
4 0 0
|
||||
kids 0
|
||||
BIN
FGData/AI/Aircraft/Aerotow/DR400/Models/prop.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
FGData/AI/Aircraft/Aerotow/DR400/Models/texture.png
Normal file
|
After Width: | Height: | Size: 361 KiB |
BIN
FGData/AI/Aircraft/Aerotow/DR400/Models/texture.xcf.gz
Normal file
BIN
FGData/AI/Aircraft/Aerotow/DR400/Sound/DR400-engine.wav
Normal file
25
FGData/AI/Aircraft/Aerotow/DR400/Sound/sound.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<fx>
|
||||
<engine>
|
||||
<name>engine</name>
|
||||
<mode>looped</mode>
|
||||
<path>DR400-engine.wav</path>
|
||||
<condition>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
</condition>
|
||||
<volume>
|
||||
<offset>-0.95</offset>
|
||||
</volume>
|
||||
<pitch>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>0.012</factor>
|
||||
<offset>0.3</offset>
|
||||
<min>0.3</min>
|
||||
<max>1.0</max>
|
||||
</pitch>
|
||||
<reference-dist>150.0</reference-dist>
|
||||
<max-dist>5000.0</max-dist>
|
||||
</engine>
|
||||
</fx>
|
||||
</PropertyList>
|
||||
25
FGData/AI/Aircraft/Aerotow/Sound/sound.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<fx>
|
||||
<engine>
|
||||
<name>engine</name>
|
||||
<mode>looped</mode>
|
||||
<path>wasp.wav</path>
|
||||
<condition>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
</condition>
|
||||
<volume>
|
||||
<offset>-0.80</offset>
|
||||
</volume>
|
||||
<pitch>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<factor>0.012</factor>
|
||||
<offset>0.3</offset>
|
||||
<min>0.3</min>
|
||||
<max>1.0</max>
|
||||
</pitch>
|
||||
<reference-dist>150.0</reference-dist>
|
||||
<max-dist>5000.0</max-dist>
|
||||
</engine>
|
||||
</fx>
|
||||
</PropertyList>
|
||||
BIN
FGData/AI/Aircraft/Aerotow/Sound/wasp.wav
Normal file
53
FGData/AI/Aircraft/Aerotow/c182/Models/BeaconLight.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<path>WhiteLight1.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>material</type>
|
||||
<object-name>Light</object-name>
|
||||
<emission>
|
||||
<red>1.0</red>
|
||||
<green>0.0</green>
|
||||
<blue>0.0</blue>
|
||||
</emission>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>Light</object-name>
|
||||
<condition>
|
||||
<and>
|
||||
<greater-than>
|
||||
<property>/sim/time/sun-angle-rad</property>
|
||||
<value>1.57</value>
|
||||
</greater-than>
|
||||
<greater-than>
|
||||
<property>velocities/true-airspeed-kt</property>
|
||||
<value>0</value>
|
||||
</greater-than>
|
||||
</and>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry><ind>0</ind><dep>0.25</dep></entry>
|
||||
<entry><ind>20</ind><dep>0.50</dep></entry>
|
||||
<entry><ind>1500</ind><dep>5</dep></entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/Halos.rgb
Normal file
35
FGData/AI/Aircraft/Aerotow/c182/Models/StrobeLight.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<path>WhiteLight1.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Light</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Light</object-name>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>0</ind>
|
||||
<dep>0.25</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>20</ind>
|
||||
<dep>.50</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1500</ind>
|
||||
<dep>5</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
35
FGData/AI/Aircraft/Aerotow/c182/Models/ToggleLight.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<PropertyList>
|
||||
<path>toggle-white.ac</path>
|
||||
|
||||
<animation>
|
||||
<type>alpha-test</type>
|
||||
<alpha-factor>0.01</alpha-factor>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>billboard</type>
|
||||
<object-name>Cube</object-name>
|
||||
<spherical type="bool">true</spherical>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>dist-scale</type>
|
||||
<object-name>Cube</object-name>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>0</ind>
|
||||
<dep>0.25</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>20</ind>
|
||||
<dep>.50</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1500</ind>
|
||||
<dep>5</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
44
FGData/AI/Aircraft/Aerotow/c182/Models/WhiteLight1.ac
Normal file
@@ -0,0 +1,44 @@
|
||||
AC3Db
|
||||
MATERIAL "ac3dmat1" rgb 1 1 1 amb 0.2 0.2 0.2 emis 1 1 1 spec 0 0 0 shi 10 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Light"
|
||||
texture "Halos.rgb"
|
||||
crease 45.000000
|
||||
numvert 12
|
||||
0.5 0.5 2.23517e-08
|
||||
-0.5 0.5 -2.23517e-08
|
||||
-0.5 -0.5 -2.23517e-08
|
||||
0.5 -0.5 2.23517e-08
|
||||
0 -0.5 -0.5
|
||||
0 -0.5 0.5
|
||||
0 0.5 0.5
|
||||
0 0.5 -0.5
|
||||
0.5 -4.96048e-10 -0.5
|
||||
-0.5 4.42074e-08 -0.5
|
||||
-0.5 4.96048e-10 0.5
|
||||
0.5 -4.42074e-08 0.5
|
||||
numsurf 3
|
||||
SURF 0x20
|
||||
mat 0
|
||||
refs 4
|
||||
0 0 0.5
|
||||
1 0.5 0.5
|
||||
2 0.5 0
|
||||
3 0 0
|
||||
SURF 0x20
|
||||
mat 0
|
||||
refs 4
|
||||
7 0 0.5
|
||||
6 0.5 0.5
|
||||
5 0.5 0
|
||||
4 0 0
|
||||
SURF 0x20
|
||||
mat 0
|
||||
refs 4
|
||||
11 0 0.5
|
||||
10 0.5 0.5
|
||||
9 0.5 0
|
||||
8 0 0
|
||||
kids 0
|
||||
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/c182-01.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/c182-02.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
7
FGData/AI/Aircraft/Aerotow/c182/Models/c182-ai.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList include="AI/Aircraft/c182/Models/c182-ai.xml">
|
||||
<sound>
|
||||
<path>AI/Aircraft/Aerotow/Sound/sound.xml</path>
|
||||
</sound>
|
||||
</PropertyList>
|
||||
217
FGData/AI/Aircraft/Aerotow/c182/Models/c182-dpm.xml
Normal file
@@ -0,0 +1,217 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
|
||||
<path>c182.ac</path>
|
||||
|
||||
|
||||
<!-- Object movements -->
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>Propeller</object-name>
|
||||
<condition>
|
||||
<less-than>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<value>500</value>
|
||||
</less-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>select</type>
|
||||
<object-name>Propeller.2</object-name>
|
||||
<condition>
|
||||
<greater-than>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<value>400</value>
|
||||
</greater-than>
|
||||
</condition>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>blend</type>
|
||||
<object-name>Propeller.2</object-name>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<factor>0.0004</factor>
|
||||
<max>1.0</max>
|
||||
<min>0.0</min>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>NoseWheel</object-name>
|
||||
<property>surface-positions/rudder-pos-norm</property>
|
||||
<factor>-20</factor>
|
||||
<center>
|
||||
<x-m>-0.8</x-m>
|
||||
<y-m>0.0</y-m>
|
||||
<z-m>-0.8</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<z>1</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>spin</type>
|
||||
<object-name>Propeller</object-name>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<factor>-1</factor>
|
||||
<center>
|
||||
<x-m>0</x-m>
|
||||
<y-m>0</y-m>
|
||||
<z-m>-0.214544</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>1.0</x>
|
||||
<y>0.0</y>
|
||||
<z>0.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>spin</type>
|
||||
<object-name>Propeller.2</object-name>
|
||||
<property>engines/engine[0]/rpm</property>
|
||||
<factor>-1</factor>
|
||||
<center>
|
||||
<x-m>0</x-m>
|
||||
<y-m>0</y-m>
|
||||
<z-m>-0.214544</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>1.0</x>
|
||||
<y>0.0</y>
|
||||
<z>0.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>LeftAileron</object-name>
|
||||
<property>surface-positions/left-aileron-pos-norm</property>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>-1.0</ind>
|
||||
<dep>15</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>0.0</ind>
|
||||
<dep>0</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1.0</ind>
|
||||
<dep>-20</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>0.94</x-m>
|
||||
<y-m>-3.83</y-m>
|
||||
<z-m>0.40</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>-0.15</x>
|
||||
<y>-1.00</y>
|
||||
<z>0.03</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>RightAileron</object-name>
|
||||
<property>surface-positions/left-aileron-pos-norm</property>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>-1.0</ind>
|
||||
<dep>15</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>0.0</ind>
|
||||
<dep>0</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1.0</ind>
|
||||
<dep>-20</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>0.94</x-m>
|
||||
<y-m>3.83</y-m>
|
||||
<z-m>0.40</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>-0.15</x>
|
||||
<y>1.00</y>
|
||||
<z>0.03</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>LeftFlap</object-name>
|
||||
<object-name>RightFlap</object-name>
|
||||
<property>surface-positions/flap-pos-norm</property>
|
||||
<factor>30</factor>
|
||||
<center>
|
||||
<x-m>1.1</x-m>
|
||||
<y-m>0.0</y-m>
|
||||
<z-m>0.28</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0.0</x>
|
||||
<y>1.0</y>
|
||||
<z>0.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>LeftElevator</object-name>
|
||||
<object-name>RightElevator</object-name>
|
||||
<property>surface-positions/elevator-pos-norm</property>
|
||||
<interpolation>
|
||||
<entry>
|
||||
<ind>-1.0</ind>
|
||||
<dep>-28</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>0</ind>
|
||||
<dep>0</dep>
|
||||
</entry>
|
||||
<entry>
|
||||
<ind>1.0</ind>
|
||||
<dep>23</dep>
|
||||
</entry>
|
||||
</interpolation>
|
||||
<center>
|
||||
<x-m>5.1</x-m>
|
||||
<y-m>0.0</y-m>
|
||||
<z-m>-0.45</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0.0</x>
|
||||
<y>1.0</y>
|
||||
<z>0.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
<animation>
|
||||
<type>rotate</type>
|
||||
<object-name>Rudder</object-name>
|
||||
<property>surface-positions/rudder-pos-norm</property>
|
||||
<factor>17.5</factor>
|
||||
<center>
|
||||
<x-m>5.45</x-m>
|
||||
<y-m>0.0</y-m>
|
||||
<z-m>0.0</z-m>
|
||||
</center>
|
||||
<axis>
|
||||
<x>0.72</x>
|
||||
<y>0.0</y>
|
||||
<z>1.0</z>
|
||||
</axis>
|
||||
</animation>
|
||||
|
||||
</PropertyList>
|
||||
8778
FGData/AI/Aircraft/Aerotow/c182/Models/c182.ac
Normal file
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/chrome2.rgb
Normal file
64
FGData/AI/Aircraft/Aerotow/c182/Models/lights.ac
Normal file
@@ -0,0 +1,64 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Cube"
|
||||
data 8
|
||||
Cube.002
|
||||
texture "lights.rgb"
|
||||
texrep 1 1
|
||||
crease 30
|
||||
numvert 8
|
||||
5.2e-005 -0.024954 -0.09
|
||||
5.2e-005 -0.024954 0.173732
|
||||
-0.027131 -0.024954 0.173679
|
||||
-0.027131 -0.024954 -0.09
|
||||
5.2e-005 0.047045 -0.09
|
||||
5.2e-005 0.047045 0.173732
|
||||
-0.027131 0.047045 0.173679
|
||||
-0.027131 0.047045 -0.09
|
||||
numsurf 6
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
0 0.0462104454637 0.277649432421
|
||||
1 0.0462104454637 0.0592489615083
|
||||
2 0.264610916376 0.0592489615083
|
||||
3 0.264610916376 0.277649432421
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
4 0.0462104454637 0.277649432421
|
||||
7 0.0462104454637 0.0592489615083
|
||||
6 0.264610916376 0.0592489615083
|
||||
5 0.264610916376 0.277649432421
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
0 0.994239091873 0.706083416939
|
||||
4 0.994238674641 0.996782541275
|
||||
5 0.00671458244324 0.996782541275
|
||||
1 0.00671491026878 0.706083416939
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
1 0.0462104454637 0.277649432421
|
||||
5 0.0462104454637 0.0592489615083
|
||||
6 0.264610916376 0.0592489615083
|
||||
2 0.264610916376 0.277649432421
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
2 0.0 1.0
|
||||
6 0.0 0.0
|
||||
7 1.0 0.0
|
||||
3 1.0 1.0
|
||||
SURF 0x00
|
||||
mat 0
|
||||
refs 4
|
||||
4 0.0462104454637 0.277649432421
|
||||
0 0.0462104454637 0.0592489615083
|
||||
3 0.264610916376 0.0592489615083
|
||||
7 0.264610916376 0.277649432421
|
||||
kids 0
|
||||
BIN
FGData/AI/Aircraft/Aerotow/c182/Models/lights.rgb
Normal file
95
FGData/AI/Aircraft/Aerotow/c182/Models/toggle-white.ac
Normal file
@@ -0,0 +1,95 @@
|
||||
AC3Db
|
||||
MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0
|
||||
MATERIAL "Material" rgb 0.92013 0.92013 0.92013 amb 0.5 0.5 0.5 emis 0 0 0 spec 1 1 1 shi 32 trans 0
|
||||
OBJECT world
|
||||
kids 1
|
||||
OBJECT poly
|
||||
name "Cube"
|
||||
data 4
|
||||
Cube
|
||||
crease 30
|
||||
numvert 12
|
||||
0.009182 0.007635 -0.00748
|
||||
0.001377 -0.000598 -0.00748
|
||||
-0.005892 0.002033 -0.00748
|
||||
-0.001976 0.012793 -0.00748
|
||||
0.009182 0.007635 0.00748
|
||||
0.001377 -0.000598 0.00748
|
||||
-0.005892 0.002033 0.00748
|
||||
-0.001976 0.012793 0.00748
|
||||
-0.009655 -0.008307 -0.00748
|
||||
-0.009655 -0.008307 0.00748
|
||||
0.002203 -0.01154 -0.00748
|
||||
0.002203 -0.01154 0.00748
|
||||
numsurf 10
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
1 0 0
|
||||
2 0 0
|
||||
3 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
4 0 0
|
||||
7 0 0
|
||||
6 0 0
|
||||
5 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
4 0 0
|
||||
5 0 0
|
||||
1 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
2 0 0
|
||||
6 0 0
|
||||
7 0 0
|
||||
3 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
6 0 0
|
||||
2 0 0
|
||||
8 0 0
|
||||
9 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
1 0 0
|
||||
5 0 0
|
||||
11 0 0
|
||||
10 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
5 0 0
|
||||
6 0 0
|
||||
9 0 0
|
||||
11 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
2 0 0
|
||||
1 0 0
|
||||
10 0 0
|
||||
8 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
10 0 0
|
||||
11 0 0
|
||||
9 0 0
|
||||
8 0 0
|
||||
SURF 0x00
|
||||
mat 1
|
||||
refs 4
|
||||
0 0 0
|
||||
3 0 0
|
||||
7 0 0
|
||||
4 0 0
|
||||
kids 0
|
||||
@@ -24,6 +24,8 @@ Start FlightGear at any airport with your aircraft as a glider, such as ASK 21.
|
||||
|
||||
From the top menu, select `Aerotow Everywhere` -> `Call for Piper J3 Cub aircraft`, `Robin DR400` or `Cessna 182` (yes, you can choose from many aircrafts). The AI aircraft will appear to your right and align to the centreline of the runway in front of you. At this time you should hook up to the aircraft, most often by pressing the `Ctrl-o` key (check help of your glider). The AI aircraft will begin to accelerate and take off.
|
||||
|
||||
You can also take off in the bush. Then the tow plane will position itself in front of you (glider's course), so what heading you have is important. If you move away from the runway threshold further than 100 m, then the take-off of the tow plane will be as in the bush, i.e. according to the glider heading and not the runway.
|
||||
|
||||
## How does the AI tow aircraft fly by default?
|
||||
|
||||
The tow plane always takes off in front of your runway and flies along the runway for 5 km, then turns back and flies downwind for 6 km, then turns back again and flies another 6 km. During this flight it is constantly gaining altitude. Then when it has completed the entire set route it turns to unknown direction and flies at a constant altitude.
|
||||
|
||||
@@ -49,6 +49,9 @@
|
||||
<alt>0</alt>
|
||||
</total>
|
||||
</route>
|
||||
<sound>
|
||||
<enable type="bool">false</enable>
|
||||
</sound>
|
||||
<help-text type="string">=== How to start? ===
|
||||
|
||||
From the top menu, select "Aerotow Everywhere" -> "Call for Piper J3 Cub aircraft", "Robin DR400" or "Cessna 182" (yes, you can choose from many aircrafts).
|
||||
|
||||
@@ -23,12 +23,50 @@ var unload = func(addon) {
|
||||
# e.g. myCanvas.del();
|
||||
|
||||
aerotow.uninit();
|
||||
thermal.uninit();
|
||||
}
|
||||
|
||||
var main = func(addon) {
|
||||
print("Aerotow Everywhere add-on initialized from path ", addon.basePath);
|
||||
|
||||
loadExtraNasalFiles(addon);
|
||||
|
||||
createDirectories(addon);
|
||||
|
||||
aerotow.init(addon);
|
||||
}
|
||||
|
||||
#
|
||||
# Load extra Nasal files in main add-on directory
|
||||
#
|
||||
# addon - Addob object
|
||||
#
|
||||
var loadExtraNasalFiles = func (addon) {
|
||||
var modules = [
|
||||
"nasal/timer",
|
||||
"nasal/aircraft",
|
||||
"nasal/message",
|
||||
"nasal/dialogs/route",
|
||||
"nasal/dialogs/thermal",
|
||||
"nasal/flight-plan",
|
||||
"nasal/scenario",
|
||||
"nasal/io/flight-plan-writer",
|
||||
"nasal/aerotow",
|
||||
"aerotow",
|
||||
];
|
||||
|
||||
foreach (var scriptName; modules) {
|
||||
var fileName = addon.basePath ~ "/" ~ scriptName ~ ".nas";
|
||||
|
||||
if (io.load_nasal(fileName, "aerotow")) {
|
||||
print("Aerotown Add-on module \"", scriptName, "\" loaded OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Create all needed directories.
|
||||
#
|
||||
var createDirectories = func (addon) {
|
||||
# Create $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow directory
|
||||
addon.createStorageDir();
|
||||
|
||||
@@ -38,21 +76,7 @@ var main = func(addon) {
|
||||
var path = os.path.new(addon.storagePath ~ "/AI/FlightPlans/dummy-file.txt");
|
||||
path.create_dir();
|
||||
|
||||
loadExtraNasalFiles(addon);
|
||||
|
||||
aerotow.init();
|
||||
thermal.init();
|
||||
}
|
||||
|
||||
#
|
||||
# Load extra Nasal files in main add-on directory
|
||||
#
|
||||
var loadExtraNasalFiles = func (addon) {
|
||||
foreach (var scriptName; ["aerotow", "messages", "thermal"]) {
|
||||
var fileName = addon.basePath ~ "/" ~ scriptName ~ ".nas";
|
||||
|
||||
if (io.load_nasal(fileName, scriptName)) {
|
||||
print("Aerotown Add-on module \"", scriptName, "\" loaded OK");
|
||||
}
|
||||
}
|
||||
# Create /route-saves directory in $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow/
|
||||
path = os.path.new(addon.storagePath ~ "/" ~ aerotow.RouteDialog.ROUTE_SAVES_DIR ~ "/dummy-file.txt");
|
||||
path.create_dir();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,13 @@
|
||||
<dialog-name>route-aerotow</dialog-name>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Towrope Configuration</label>
|
||||
<binding>
|
||||
<command>dialog-show</command>
|
||||
<dialog-name>towrope-config</dialog-name>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Call for Piper J3 Cub aircraft</label>
|
||||
<binding>
|
||||
@@ -60,11 +67,29 @@
|
||||
<value type="string">c182</value>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Call for Douglas C-47 aircraft</label>
|
||||
<binding>
|
||||
<command>property-assign</command>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/ai-model</property>
|
||||
<value type="string">C47</value>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Disable tow aircraft</label>
|
||||
<binding>
|
||||
<command>property-assign</command>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/sound/enable</property>
|
||||
<value type="bool">false</value>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[aerotow.stopAerotow();]]></script>
|
||||
<script><![CDATA[
|
||||
# Run stopAerotow() with a delay to ensure that the engine sound turns off
|
||||
aerotow.Timer.new().singleShot(1, aerotow.g_Aerotow, func () {
|
||||
aerotow.g_Aerotow.stopAerotow();
|
||||
});
|
||||
]]></script>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<addon>
|
||||
<identifier type="string">org.flightgear.addons.Aerotow</identifier>
|
||||
<name type="string">Aerotow Everywhere</name>
|
||||
<version type="string">1.2.0</version>
|
||||
<version type="string">1.5.0</version>
|
||||
|
||||
<authors>
|
||||
<author>
|
||||
@@ -49,6 +49,16 @@
|
||||
</long-description>
|
||||
|
||||
<localized>
|
||||
<fr>
|
||||
<short-description type="string">
|
||||
Remorquage d’un planeur depuis n’importe quel aérodrome.
|
||||
</short-description>
|
||||
|
||||
<long-description type="string">
|
||||
Ce greffon permet d’activer le scénario IA « avion remorqueur » pour démarrer avec un planeur depuis n’importe quel aérodrome.
|
||||
</long-description>
|
||||
</fr>
|
||||
|
||||
<pl>
|
||||
<short-description type="string">
|
||||
Samoloty holownicze dla szybowców na każdym lotnisku.
|
||||
|
||||
676
aerotow.nas
@@ -9,682 +9,22 @@
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
var ADDON = addons.getAddon("org.flightgear.addons.Aerotow");
|
||||
var ADDON_NODE_PATH = ADDON.node.getPath();
|
||||
var FILENAME_SCENARIO = "aerotown-addon.xml";
|
||||
var FILENAME_FLIGHTPLAN = "aerotown-addon-flightplan.xml";
|
||||
var PATH_SCENARIO = ADDON.storagePath ~ "/" ~ FILENAME_SCENARIO;
|
||||
var PATH_FLIGHTPLAN = ADDON.storagePath ~ "/AI/FlightPlans/" ~ FILENAME_FLIGHTPLAN;
|
||||
var SCENARIO_ID = "aerotow_addon";
|
||||
var SCENARIO_NAME = "Aerotow Add-on";
|
||||
var SCENARIO_DESC = "This scenario starts the towing plane at the airport where the pilot with the glider is located. Use Ctrl-o to hook the plane.";
|
||||
var MAX_ROUTE_WAYPOINT = 10;
|
||||
var DISTANCE_DETERMINANT = 1000; # meters
|
||||
var g_Aerotow = nil;
|
||||
|
||||
#
|
||||
# Global variables
|
||||
# Initialize Aerotow
|
||||
#
|
||||
var g_wptCount = 0;
|
||||
var g_isScenarioLoaded = 0;
|
||||
var g_fpFileHandler = nil; # Handler for wrire flight plan to file
|
||||
var g_coord = nil; # Coordinates for flight plan
|
||||
var g_heading = nil; # AI plane heading
|
||||
var g_altitude = nil; # AI plane altitude
|
||||
var g_towListeners = [];
|
||||
|
||||
# addon - Addon object
|
||||
#
|
||||
# Initialize thermal module
|
||||
#
|
||||
var init = func () {
|
||||
# Listener for ai-model property triggered when the user select a tow aircraft from add-on menu
|
||||
append(g_towListeners, setlistener(ADDON_NODE_PATH ~ "/addon-devel/ai-model", func () {
|
||||
startAerotow();
|
||||
}));
|
||||
|
||||
append(g_towListeners, setlistener("/sim/presets/longitude-deg", func () {
|
||||
# User change airport/runway
|
||||
initialFlightPlan();
|
||||
}));
|
||||
|
||||
initialFlightPlan();
|
||||
|
||||
# Set listener for aerotow combo box value in route dialog for recalculate altitude change
|
||||
append(g_towListeners, setlistener(ADDON_NODE_PATH ~ "/addon-devel/route/ai-model", func () {
|
||||
calculateAltChangeAndTotals();
|
||||
}));
|
||||
|
||||
# Set listeners for distance fields for calculate altitude change
|
||||
for (var i = 0; i < MAX_ROUTE_WAYPOINT; i = i + 1) {
|
||||
append(g_towListeners, setlistener(ADDON_NODE_PATH ~ "/addon-devel/route/wpt[" ~ i ~ "]/distance-m", func (node) {
|
||||
calculateAltChangeAndTotals();
|
||||
}));
|
||||
}
|
||||
var init = func (addon) {
|
||||
g_Aerotow = Aerotow.new(addon);
|
||||
}
|
||||
|
||||
#
|
||||
# Calculate total distance and altitude and put in to property tree
|
||||
#
|
||||
var calculateAltChangeAndTotals = func () {
|
||||
var totalDistance = 0.0;
|
||||
var totalAlt = 0.0;
|
||||
var isEnd = 0;
|
||||
|
||||
var isRouteMode = 1;
|
||||
var perf = getAircraftPerformance(isRouteMode);
|
||||
|
||||
for (var i = 0; i < MAX_ROUTE_WAYPOINT; i = i + 1) {
|
||||
var distance = getprop(ADDON_NODE_PATH ~ "/addon-devel/route/wpt[" ~ i ~ "]/distance-m");
|
||||
var altChange = getAltChange(perf.vs, distance);
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/wpt[" ~ i ~ "]/alt-change-agl-ft", altChange);
|
||||
|
||||
if (!isEnd) {
|
||||
if (distance > 0.0) {
|
||||
totalDistance = totalDistance + distance;
|
||||
totalAlt = totalAlt + altChange;
|
||||
}
|
||||
else {
|
||||
isEnd = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/total/distance", totalDistance);
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/total/alt", totalAlt);
|
||||
}
|
||||
|
||||
#
|
||||
# Uninitialize thermal module
|
||||
# Uninitialize Aerotow
|
||||
#
|
||||
var uninit = func () {
|
||||
foreach (var listener; g_towListeners) {
|
||||
removelistener(listener);
|
||||
if (g_Aerotow) {
|
||||
g_Aerotow.del();
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Main function to prepare AI scenario and run it.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var startAerotow = func () {
|
||||
var args = props.Node.new({ "name": SCENARIO_ID });
|
||||
if (g_isScenarioLoaded) {
|
||||
fgcommand("unload-scenario", args);
|
||||
}
|
||||
|
||||
generateScenarioXml();
|
||||
|
||||
if (!generateFlightPlanXml()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fgcommand("load-scenario", args)) {
|
||||
g_isScenarioLoaded = 1;
|
||||
messages.displayOk("Let's fly!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
messages.displayError("Tow failed!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Function for unload our AI scenario.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var stopAerotow = func () {
|
||||
if (g_isScenarioLoaded) {
|
||||
var args = props.Node.new({ "name": SCENARIO_ID });
|
||||
if (fgcommand("unload-scenario", args)) {
|
||||
g_isScenarioLoaded = 0;
|
||||
|
||||
messages.displayOk("Aerotown disabled");
|
||||
return 1;
|
||||
}
|
||||
|
||||
messages.displayError("Aerotown disable failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
messages.displayOk("Aerotown already disabled");
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Generate the XML file with the AI scenario.
|
||||
# The file will be stored to $FG_HOME/Export/aerotown-addon.xml.
|
||||
#
|
||||
var generateScenarioXml = func () {
|
||||
var scenarioXml = {
|
||||
"PropertyList": {
|
||||
"scenario": {
|
||||
"name": SCENARIO_NAME,
|
||||
"description": SCENARIO_DESC,
|
||||
"entry": {
|
||||
"callsign": "FG-TOW",
|
||||
"type": "aircraft",
|
||||
"class": "aerotow-dragger",
|
||||
"model": "Aircraft/Cub/Models/Cub-ai.xml", # default Cub
|
||||
"flightplan": FILENAME_FLIGHTPLAN,
|
||||
"repeat": 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var aiModel = getSelectedAircraft();
|
||||
if (aiModel == "DR400") {
|
||||
scenarioXml.PropertyList.scenario.entry.model = "Aircraft/DR400/Models/dr400-ai.xml";
|
||||
}
|
||||
else if (aiModel == "c182") {
|
||||
scenarioXml.PropertyList.scenario.entry.model = "Aircraft/c182/Models/c182-ai.xml";
|
||||
}
|
||||
|
||||
var node = props.Node.new(scenarioXml);
|
||||
io.writexml(PATH_SCENARIO, node);
|
||||
|
||||
addScenarioToPropertyList();
|
||||
}
|
||||
|
||||
#
|
||||
# Add our new scenario to the "/sim/ai/scenarios" property list
|
||||
# so that FlightGear will be able to load it by "load-scenario" command.
|
||||
#
|
||||
var addScenarioToPropertyList = func () {
|
||||
if (!isScenarioAdded()) {
|
||||
var scenarioData = {
|
||||
"name": SCENARIO_NAME,
|
||||
"id": SCENARIO_ID,
|
||||
"description": SCENARIO_DESC,
|
||||
"path": PATH_SCENARIO,
|
||||
};
|
||||
|
||||
props.globals.getNode("/sim/ai/scenarios").addChild("scenario").setValues(scenarioData);
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Return 1 if scenario is already added to "/sim/ai/scenarios" property list, otherwise return 0.
|
||||
#
|
||||
var isScenarioAdded = func () {
|
||||
foreach (var scenario; props.globals.getNode("/sim/ai/scenarios").getChildren("scenario")) {
|
||||
var id = scenario.getChild("id");
|
||||
if (id != nil and id.getValue() == SCENARIO_ID) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Return name of selected aircraft. Possible values depend of isRouteMode: "Cub", "DR400", "c182".
|
||||
#
|
||||
# isRouteMode - use 1 to get the plane for the "Aerotow Route" dialog, use 0 (default) for call the airplane for towing
|
||||
#
|
||||
var getSelectedAircraft = func (isRouteMode = 0) {
|
||||
if (isRouteMode) {
|
||||
return getprop(ADDON_NODE_PATH ~ "/addon-devel/route/ai-model") or "Piper J3 Cub";
|
||||
}
|
||||
|
||||
return getprop(ADDON_NODE_PATH ~ "/addon-devel/ai-model") or "Cub";
|
||||
}
|
||||
|
||||
#
|
||||
# Get airport an runway hash where the glider is located.
|
||||
#
|
||||
# Return hash with "airport" and "runway", otherwise nil.
|
||||
#
|
||||
var getAirportAndRunway = func () {
|
||||
var icao = getprop("/sim/airport/closest-airport-id");
|
||||
if (icao == nil) {
|
||||
messages.displayError("Airport code cannot be obtained.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
var runwayName = getprop("/sim/atc/runway");
|
||||
if (runwayName == nil) {
|
||||
messages.displayError("Runway name cannot be obtained.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
var airport = airportinfo(icao);
|
||||
|
||||
if (!contains(airport.runways, runwayName)) {
|
||||
messages.displayError("The " ~ icao ~" airport does not have runway " ~ runwayName);
|
||||
return nil;
|
||||
}
|
||||
|
||||
var runway = airport.runways[runwayName];
|
||||
|
||||
var minRwyLength = getMinRunwayLength();
|
||||
if (runway.length < minRwyLength) {
|
||||
messages.displayError(
|
||||
"This runway is too short. Please choose a longer one than " ~ minRwyLength ~ " m "
|
||||
~ "(" ~ math.round(minRwyLength * globals.M2FT) ~ " ft)."
|
||||
);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return {
|
||||
"airport": airport,
|
||||
"runway": runway,
|
||||
};
|
||||
}
|
||||
|
||||
#
|
||||
# Initialize flight plan and set it to property tree
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var initialFlightPlan = func () {
|
||||
var location = getAirportAndRunway();
|
||||
if (location == nil) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var perf = getAircraftPerformance();
|
||||
|
||||
initAircraftVariable(location.airport, location.runway, 0);
|
||||
|
||||
# inittial readonly waypoint
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/init-wpt/heading-change", g_heading);
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/init-wpt/distance-m", 100);
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/init-wpt/alt-change-agl-ft", perf.vs / 10);
|
||||
|
||||
# in air
|
||||
var wptData = [
|
||||
{"hdgChange": 0, "dist": 5000, "altChange": perf.vs * 5},
|
||||
{"hdgChange": -90, "dist": 1000, "altChange": perf.vs},
|
||||
{"hdgChange": -90, "dist": 1000, "altChange": perf.vs},
|
||||
{"hdgChange": 0, "dist": 5000, "altChange": perf.vs * 5},
|
||||
{"hdgChange": -90, "dist": 1500, "altChange": perf.vs * 1.5},
|
||||
{"hdgChange": -90, "dist": 1000, "altChange": perf.vs},
|
||||
{"hdgChange": 0, "dist": 5000, "altChange": perf.vs * 5},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
];
|
||||
|
||||
# Default route
|
||||
# ^ - airport with heading direction to north
|
||||
# 1 - 1st waypoint
|
||||
# 2 - 2nd waypoint, etc.
|
||||
#
|
||||
# 2 . . 1 7
|
||||
# . . .
|
||||
# . . .
|
||||
# 3 . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . ^ 6
|
||||
# . .
|
||||
# . .
|
||||
# 4 . . . . 5
|
||||
|
||||
var index = 0;
|
||||
foreach (var wpt; wptData) {
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/wpt[" ~ index ~ "]/heading-change", wpt.hdgChange);
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/wpt[" ~ index ~ "]/distance-m", wpt.dist);
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/route/wpt[" ~ index ~ "]/alt-change-agl-ft", wpt.altChange);
|
||||
|
||||
index = index + 1;
|
||||
}
|
||||
|
||||
calculateAltChangeAndTotals();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#
|
||||
# Generate the XML file with the flight plane for our plane for AI scenario.
|
||||
# The file will be stored to $FG_HOME/Export/aerotown-addon-flightplan.xml.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var generateFlightPlanXml = func () {
|
||||
g_wptCount = 0;
|
||||
|
||||
var location = getAirportAndRunway();
|
||||
if (location == nil) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_fpFileHandler = io.open(PATH_FLIGHTPLAN, "w");
|
||||
io.write(
|
||||
g_fpFileHandler,
|
||||
"<?xml version=\"1.0\"?>\n\n" ~
|
||||
"<!-- This file is generated automatically by the Aerotow Everywhere add-on -->\n\n" ~
|
||||
"<PropertyList>\n" ~
|
||||
" <flightplan>\n"
|
||||
);
|
||||
|
||||
var perf = getAircraftPerformance();
|
||||
|
||||
initAircraftVariable(location.airport, location.runway, 1);
|
||||
|
||||
# Start at 2 o'clock from the glider...
|
||||
# Inital ktas must be >= 1.0
|
||||
addWptGround({"hdgChange": 60, "dist": 25}, {"altChange": 0, "ktas": 5});
|
||||
|
||||
# Reset coord and heading
|
||||
initAircraftVariable(location.airport, location.runway, 0);
|
||||
|
||||
var gliderOffsetM = getGliderOffsetFromRunwayThreshold(location.runway);
|
||||
|
||||
# ... and line up with the runway
|
||||
addWptGround({"hdgChange": 0, "dist": 30 + gliderOffsetM}, {"altChange": 0, "ktas": 2.5});
|
||||
|
||||
# Rolling
|
||||
addWptGround({"hdgChange": 0, "dist": 10}, {"altChange": 0, "ktas": 5});
|
||||
addWptGround({"hdgChange": 0, "dist": 20}, {"altChange": 0, "ktas": 5});
|
||||
addWptGround({"hdgChange": 0, "dist": 20}, {"altChange": 0, "ktas": perf.speed / 6});
|
||||
addWptGround({"hdgChange": 0, "dist": 10}, {"altChange": 0, "ktas": perf.speed / 5});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 4});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 3.5});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 3});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 2.5});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 2});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 1.75});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 1.5});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed / 1.25});
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed});
|
||||
|
||||
# Takeof
|
||||
addWptAir({"hdgChange": 0, "dist": 100 * perf.rolling}, {"elevationPlus": 3, "ktas": perf.speed * 1.05});
|
||||
addWptAir({"hdgChange": 0, "dist": 100}, {"altChange": perf.vs / 10, "ktas": perf.speed * 1.025});
|
||||
|
||||
var speedInc = 1.0;
|
||||
foreach (var wptNode; props.globals.getNode(ADDON_NODE_PATH ~ "/addon-devel/route").getChildren("wpt")) {
|
||||
var dist = wptNode.getChild("distance-m").getValue();
|
||||
if (dist <= 0.0) {
|
||||
break;
|
||||
}
|
||||
|
||||
var hdgChange = wptNode.getChild("heading-change").getValue();
|
||||
var altChange = getAltChange(perf.vs, dist);
|
||||
|
||||
speedInc = speedInc + ((dist / DISTANCE_DETERMINANT) * 0.025);
|
||||
var ktas = perf.speed * speedInc;
|
||||
if (ktas > perf.speedLimit) {
|
||||
ktas = perf.speedLimit;
|
||||
}
|
||||
|
||||
addWptAir({"hdgChange": hdgChange, "dist": dist}, {"altChange": altChange, "ktas": ktas});
|
||||
}
|
||||
|
||||
addWptEnd();
|
||||
|
||||
io.write(
|
||||
g_fpFileHandler,
|
||||
" </flightplan>\n" ~
|
||||
"</PropertyList>\n\n"
|
||||
);
|
||||
io.close(g_fpFileHandler);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#
|
||||
# Return hash with "vs", "speed", "rolling".
|
||||
#
|
||||
var getAircraftPerformance = func (isRouteMode = 0) {
|
||||
# Cub
|
||||
# Cruise Speed 61 kt
|
||||
# Max Speed 106 kt
|
||||
# Approach speed 44-52 kt
|
||||
# Stall speed 33 kt
|
||||
|
||||
# Robin DR 400
|
||||
# Cruise Speed 134 kt
|
||||
# Max speeed 166 kt
|
||||
# Stall speed 51 kt
|
||||
# Rate of climb: 825 ft/min
|
||||
|
||||
# Cessna 182
|
||||
# Cruise Speed 145 kt
|
||||
# Max speeed 175 kt
|
||||
# Stall speed 50 kt
|
||||
# Best climb: 924 ft/min
|
||||
|
||||
var aiModel = getSelectedAircraft(isRouteMode);
|
||||
if (aiModel == "DR400" or aiModel == "Robin DR400") {
|
||||
return {
|
||||
"vs": 285, # ft per DISTANCE_DETERMINANT m
|
||||
"speed": 70,
|
||||
"speedLimit": 75,
|
||||
"rolling": 2,
|
||||
};
|
||||
}
|
||||
|
||||
if (aiModel == "c182" or aiModel == "Cessna 182") {
|
||||
return {
|
||||
"vs": 295, # ft per DISTANCE_DETERMINANT m
|
||||
"speed": 75,
|
||||
"speedLimit": 80,
|
||||
"rolling": 2.2,
|
||||
};
|
||||
}
|
||||
|
||||
# Cub
|
||||
return {
|
||||
"vs": 200, # ft per DISTANCE_DETERMINANT m
|
||||
"speed": 55,
|
||||
"speedLimit": 60,
|
||||
"rolling": 1,
|
||||
};
|
||||
}
|
||||
|
||||
#
|
||||
# Return how much the altitide increases for a given vertical speed and distance
|
||||
#
|
||||
# vs - vertical speed for DISTANCE_DETERMINANT m
|
||||
# distance - distance in meters
|
||||
#
|
||||
var getAltChange = func (vs, distance) {
|
||||
return vs * (distance / DISTANCE_DETERMINANT);
|
||||
}
|
||||
|
||||
#
|
||||
# Initialize AI aircraft variable
|
||||
#
|
||||
# airport - Object from airportinfo().
|
||||
# runway - Object of runway from which the glider start.
|
||||
# isGliderPos - Pass 1 for set AI aircraft's coordinates as glider position, 0 set coordinates as runway threshold.
|
||||
#
|
||||
var initAircraftVariable = func (airport, runway, isGliderPos = 1) {
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
|
||||
# Set coordinates as glider position or runway threshold
|
||||
g_coord = isGliderPos
|
||||
? gliderCoord
|
||||
: geo.Coord.new().set_latlon(runway.lat, runway.lon);
|
||||
|
||||
# Set airplane heading as runway heading
|
||||
g_heading = runway.heading;
|
||||
|
||||
# Set AI airplane altitude as glider altitude (assumed it's on the ground).
|
||||
# It is more accurate than airport.elevation.
|
||||
g_altitude = gliderCoord.alt() * globals.M2FT;
|
||||
}
|
||||
|
||||
#
|
||||
# Get distance from glider to runway threshold e.g. in case that the user taxi from the runway threshold
|
||||
#
|
||||
# runway - Object of runway from which the glider start
|
||||
# Return the distance in metres, of the glider's displacement from the runway threshold.
|
||||
#
|
||||
var getGliderOffsetFromRunwayThreshold = func (runway) {
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
var rwyThreshold = geo.Coord.new().set_latlon(runway.lat, runway.lon);
|
||||
|
||||
return rwyThreshold.distance_to(gliderCoord);
|
||||
}
|
||||
|
||||
#
|
||||
# Get the minimum runway length required, in meters
|
||||
#
|
||||
var getMinRunwayLength = func () {
|
||||
var aiModel = getSelectedAircraft();
|
||||
if (aiModel == "DR400") {
|
||||
return 470;
|
||||
}
|
||||
|
||||
if (aiModel == "c182") {
|
||||
return 508;
|
||||
}
|
||||
|
||||
return 280;
|
||||
}
|
||||
|
||||
#
|
||||
# Add new waypoint on ground
|
||||
#
|
||||
# coordOffset - Hash for calculate next coordinates (lat, lon), with following fields:
|
||||
# {
|
||||
# hdgChange - How the aircraft's heading supposed to change? 0 - keep the same heading.
|
||||
# dis - Distance in meters to calculate next waypoint coordinates.
|
||||
# }
|
||||
# performance - Hash with following fields:
|
||||
# {
|
||||
# altChange - How the aircraft's altitude is supposed to change? 0 - keep the same altitude.
|
||||
# ktas - True air speed of AI plane at the waypoint.
|
||||
# }
|
||||
#
|
||||
var addWptGround = func (coordOffset, performance) {
|
||||
wrireWpt(nil, coordOffset, performance, "ground");
|
||||
}
|
||||
|
||||
#
|
||||
# Add new waypoint in air
|
||||
#
|
||||
var addWptAir = func (coordOffset, performance) {
|
||||
wrireWpt(nil, coordOffset, performance, "air");
|
||||
}
|
||||
|
||||
#
|
||||
# Add "WAIT" waypoint
|
||||
#
|
||||
# sec - Number of seconds for wait
|
||||
#
|
||||
var addWptWait = func (sec) {
|
||||
wrireWpt("WAIT", {}, {}, nil, sec);
|
||||
}
|
||||
|
||||
#
|
||||
# Add "END" waypoint
|
||||
#
|
||||
var addWptEnd = func () {
|
||||
wrireWpt("END", {}, {});
|
||||
}
|
||||
|
||||
#
|
||||
# Write waypoint to flight plan file
|
||||
#
|
||||
# name - The name of waypoint
|
||||
# coordOffset.hdgChange - How the aircraft's heading supposed to change?
|
||||
# coordOffset.dist - Distance in meters to calculate next waypoint coordinates
|
||||
# performance.altChange - How the aircraft's altitude is supposed to change?
|
||||
# performance.elevationPlus - Set aircraft altitude as current terrain elevation + given value in feets.
|
||||
# It's best to use for the first point in the air to avoid the plane collapsing into
|
||||
# the ground in a bumpy airport
|
||||
# performance.ktas - True air speed of AI plane at the waypoint
|
||||
# groundAir - Allowed value: "ground or "air". The "ground" means that AI plane is on the ground, "air" - in air
|
||||
# sec - Number of seconds for "WAIT" waypoint
|
||||
#
|
||||
var wrireWpt = func (
|
||||
name,
|
||||
coordOffset,
|
||||
performance,
|
||||
groundAir = nil,
|
||||
sec = nil
|
||||
) {
|
||||
var coord = nil;
|
||||
if (contains(coordOffset, "hdgChange") and contains(coordOffset, "dist")) {
|
||||
g_heading = g_heading + coordOffset.hdgChange;
|
||||
if (g_heading < 0) {
|
||||
g_heading = 360 + g_heading;
|
||||
}
|
||||
|
||||
if (g_heading > 360) {
|
||||
g_heading = g_heading - 360;
|
||||
}
|
||||
|
||||
g_coord.apply_course_distance(g_heading, coordOffset.dist);
|
||||
coord = g_coord;
|
||||
}
|
||||
|
||||
var alt = nil;
|
||||
if (coord != nil and contains(performance, "elevationPlus")) {
|
||||
var elevation = geo.elevation(coord.lat(), coord.lon());
|
||||
if (elevation == nil) {
|
||||
g_altitude = g_altitude + performance.elevationPlus;
|
||||
}
|
||||
else {
|
||||
g_altitude = elevation * globals.M2FT + performance.elevationPlus;
|
||||
}
|
||||
alt = g_altitude;
|
||||
}
|
||||
else if (contains(performance, "altChange")) {
|
||||
g_altitude = g_altitude + performance.altChange;
|
||||
alt = g_altitude;
|
||||
}
|
||||
|
||||
var ktas = contains(performance, "ktas") ? performance.ktas : nil;
|
||||
|
||||
name = name == nil ? g_wptCount : name;
|
||||
var data = getWptString(name, coord, alt, ktas, groundAir, sec);
|
||||
|
||||
io.write(g_fpFileHandler, data);
|
||||
|
||||
g_wptCount = g_wptCount + 1;
|
||||
}
|
||||
|
||||
#
|
||||
# Get single waypoint data as a string.
|
||||
#
|
||||
# name - Name of waypoint. Special names are: "WAIT", "END".
|
||||
# coord - The Coord object
|
||||
# alt - Altitude AMSL of AI plane
|
||||
# ktas - True air speed of AI plane
|
||||
# groundAir - Allowe value: "ground or "air". The "ground" means that AI plane is on the ground, "air" - in air
|
||||
# sec - Number of seconds for "WAIT" waypoint
|
||||
#
|
||||
var getWptString = func (name, coord = nil, alt = nil, ktas = nil, groundAir = nil, sec = nil) {
|
||||
var str = " <wpt>\n"
|
||||
~ " <name>" ~ name ~ "</name>\n";
|
||||
|
||||
if (coord != nil) {
|
||||
str = str ~ " <lat>" ~ coord.lat() ~ "</lat>\n";
|
||||
str = str ~ " <lon>" ~ coord.lon() ~ "</lon>\n";
|
||||
str = str ~ " <!-- " ~ coord.lat() ~ "," ~ coord.lon() ~ " -->\n";
|
||||
}
|
||||
|
||||
if (alt != nil) {
|
||||
# str = str ~ " <alt>" ~ alt ~ "</alt>\n";
|
||||
str = str ~ " <crossat>" ~ alt ~ "</crossat>\n";
|
||||
}
|
||||
|
||||
if (ktas != nil) {
|
||||
str = str ~ " <ktas>" ~ ktas ~ "</ktas>\n";
|
||||
}
|
||||
|
||||
if (groundAir != nil) {
|
||||
var onGround = groundAir == "ground" ? "true" : "false";
|
||||
str = str ~ " <on-ground>" ~ onGround ~ "</on-ground>\n";
|
||||
}
|
||||
|
||||
if (sec != nil) {
|
||||
str = str ~ " <time-sec>" ~ sec ~ "</time-sec>\n";
|
||||
}
|
||||
|
||||
return str ~ " </wpt>\n";
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<hrule/>
|
||||
|
||||
<text>
|
||||
<label>Aerotow Everywhere version 1.2.0 - 17th August 2022</label>
|
||||
<label>Aerotow Everywhere version 1.5.0 - 22nd August 2022</label>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
|
||||
@@ -189,7 +189,9 @@
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[thermal.add();]]></script>
|
||||
<script><![CDATA[
|
||||
aerotow.g_Aerotow.thermal.add();
|
||||
]]></script>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
<value>Piper J3 Cub</value>
|
||||
<value>Robin DR400</value>
|
||||
<value>Cessna 182</value>
|
||||
<value>Douglas C-47</value>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
@@ -115,7 +116,7 @@
|
||||
<row>0</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label>Alt change (AGL ft)</label>
|
||||
<label>Alt change (AGL)</label>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
@@ -132,7 +133,7 @@
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format>%.f</format>
|
||||
<format>%.f m</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/init-wpt/distance-m</property>
|
||||
</text>
|
||||
@@ -141,7 +142,7 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/init-wpt/alt-change-agl-ft</property>
|
||||
</text>
|
||||
@@ -162,14 +163,14 @@
|
||||
<row>2</row>
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label>Alt change (AGL ft)</label>
|
||||
<label>Alt change (AGL)</label>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>3</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[0]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[0]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -179,7 +180,7 @@
|
||||
<row>3</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[0]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[0]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -190,16 +191,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[0]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[0]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>4</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[1]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[1]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -209,7 +210,7 @@
|
||||
<row>4</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[1]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[1]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -220,16 +221,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[1]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[1]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>5</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[2]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[2]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -239,7 +240,7 @@
|
||||
<row>5</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[2]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[2]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -250,16 +251,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[2]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[2]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>6</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[3]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[3]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -269,7 +270,7 @@
|
||||
<row>6</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[3]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[3]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -280,16 +281,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[3]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[3]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>7</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[4]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[4]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -299,7 +300,7 @@
|
||||
<row>7</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[4]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[4]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -310,16 +311,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[4]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[4]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>8</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[5]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[5]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -329,7 +330,7 @@
|
||||
<row>8</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[5]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[5]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -340,16 +341,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[5]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[5]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>9</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[6]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[6]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -359,7 +360,7 @@
|
||||
<row>9</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[6]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[6]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -370,16 +371,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[6]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[6]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>10</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[7]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[7]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -389,7 +390,7 @@
|
||||
<row>10</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[7]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[7]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -400,16 +401,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[7]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[7]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>11</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[8]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[8]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -419,7 +420,7 @@
|
||||
<row>11</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[8]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[8]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -430,16 +431,16 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[8]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[8]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<row>12</row>
|
||||
<col>0</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[9]/heading-change</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[9]/heading-change</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -449,7 +450,7 @@
|
||||
<row>12</row>
|
||||
<col>1</col>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[9]/distance-m</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[9]/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
@@ -460,9 +461,9 @@
|
||||
<col>2</col>
|
||||
<halign>left</halign>
|
||||
<label></label>
|
||||
<format> %.f</format>
|
||||
<format> %.f ft</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpt[9]/alt-change-agl-ft</property>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/wpt[9]/alt-change-agl-ft</property>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
@@ -493,6 +494,23 @@
|
||||
|
||||
<hrule/>
|
||||
|
||||
<text>
|
||||
<halign>left</halign>
|
||||
<label>Description:</label>
|
||||
</text>
|
||||
|
||||
<input>
|
||||
<halign>fill</halign>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/route/wpts/description</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<group>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
@@ -516,7 +534,31 @@
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[aerotow.initialFlightPlan();]]></script>
|
||||
<script><![CDATA[
|
||||
aerotow.g_Aerotow.scenario.flightPlan.initial();
|
||||
]]></script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Save...</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[
|
||||
aerotow.g_Aerotow.scenario.routeDialog.save();
|
||||
]]></script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Load...</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[
|
||||
aerotow.g_Aerotow.scenario.routeDialog.load();
|
||||
]]></script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
|
||||
342
gui/dialogs/towrope-configuration.xml
Normal file
@@ -0,0 +1,342 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
<name>towrope-config</name>
|
||||
<layout>vbox</layout>
|
||||
<default-padding>5</default-padding>
|
||||
<modal>false</modal>
|
||||
<width>680</width>
|
||||
|
||||
<!-- title bar -->
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
<text>
|
||||
<label>Towrope Configuration</label>
|
||||
</text>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
<button>
|
||||
<legend/>
|
||||
<key>Esc</key>
|
||||
<pref-width>16</pref-width>
|
||||
<pref-height>16</pref-height>
|
||||
<border>2</border>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<!-- aerotow parameters -->
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
|
||||
<group>
|
||||
<layout>table</layout>
|
||||
|
||||
<!-- tow length -->
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Towrope Length</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.0f m </format>
|
||||
<property>/sim/hitches/aerotow/tow/length</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<!-- break force -->
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Weak Link Break Force</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.0f N </format>
|
||||
<property>/sim/hitches/aerotow/tow/brake-force</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<!-- tow characteristic -->
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Towrope Elastic Constant</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.0f N</format>
|
||||
<property>/sim/hitches/aerotow/tow/elastic-constant</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<!-- tow thickness -->
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Towrope Diameter</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.0f mm</format>
|
||||
<property>/sim/hitches/aerotow/rope/rope-diameter-mm</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<!-- tow weight -->
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Towrope Weight per Meter</label>
|
||||
</text>
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label></label>
|
||||
<live>true</live>
|
||||
<format>%.3f kg/m</format>
|
||||
<property>/sim/hitches/aerotow/tow/weight-per-m-kg-m</property>
|
||||
<color>
|
||||
<red>0.2</red>
|
||||
<green>0.9</green>
|
||||
<blue>0.2</blue>
|
||||
</color>
|
||||
</text>
|
||||
</group>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
|
||||
<vrule/>
|
||||
|
||||
<group>
|
||||
<layout>table</layout>
|
||||
|
||||
<!-- tow length -->
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>20 m</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>0</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>tow_length</name>
|
||||
<property>/sim/hitches/aerotow/tow/length</property>
|
||||
<min>20</min>
|
||||
<max>200</max>
|
||||
<step>5</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>200 m</label>
|
||||
</text>
|
||||
|
||||
<!-- break force -->
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>100 N</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>1</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>break_force</name>
|
||||
<property>/sim/hitches/aerotow/tow/brake-force</property>
|
||||
<min>100</min>
|
||||
<max>100000</max>
|
||||
<step>100</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>100,000 N</label>
|
||||
</text>
|
||||
|
||||
<!-- tow characteristic -->
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>0 N</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>2</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>elastic_constant</name>
|
||||
<property>/sim/hitches/aerotow/tow/elastic-constant</property>
|
||||
<min>0</min>
|
||||
<max>1500000</max>
|
||||
<step>200</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>1,500,000 N</label>
|
||||
</text>
|
||||
|
||||
<!-- tow thickness -->
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>0 mm</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>3</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>rope_diameter</name>
|
||||
<property>/sim/hitches/aerotow/rope/rope-diameter-mm</property>
|
||||
<min>0</min>
|
||||
<max>50</max>
|
||||
<step>1</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>50 mm</label>
|
||||
</text>
|
||||
|
||||
<!-- tow weight -->
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>1</col>
|
||||
<halign>right</halign>
|
||||
<label>0 kg/m</label>
|
||||
</text>
|
||||
<slider>
|
||||
<row>4</row>
|
||||
<col>2</col>
|
||||
<live>true</live>
|
||||
<halign>fill</halign>
|
||||
<pref-width>300</pref-width>
|
||||
<name>weight_per_m</name>
|
||||
<property>/sim/hitches/aerotow/tow/weight-per-m-kg-m</property>
|
||||
<min>0</min>
|
||||
<max>1</max>
|
||||
<step>0.001</step>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</slider>
|
||||
<text>
|
||||
<row>4</row>
|
||||
<col>3</col>
|
||||
<halign>left</halign>
|
||||
<label>1 kg/m</label>
|
||||
</text>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<!-- bottom line -->
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
<button>
|
||||
<legend>Close</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
<button>
|
||||
<legend>Default</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[
|
||||
setprop("/sim/hitches/aerotow/tow/length", 60.0);
|
||||
setprop("/sim/hitches/aerotow/tow/brake-force", 100000.0);
|
||||
setprop("/sim/hitches/aerotow/tow/elastic-constant", 10000.0);
|
||||
setprop("/sim/hitches/aerotow/rope/rope-diameter-mm", 20);
|
||||
setprop("/sim/hitches/aerotow/tow/weight-per-m-kg-m", 1.0);
|
||||
]]></script>
|
||||
</binding>
|
||||
</button>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
</group>
|
||||
</PropertyList>
|
||||
54
messages.nas
@@ -1,54 +0,0 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Display given message as OK.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
#
|
||||
var displayOk = func (message) {
|
||||
display(message, "ok");
|
||||
}
|
||||
|
||||
#
|
||||
# Display given message as an error.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
#
|
||||
var displayError = func (message) {
|
||||
display(message, "error");
|
||||
}
|
||||
|
||||
#
|
||||
# Display given message.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
# type - The type of message. It can take values as "ok" or "error".
|
||||
#
|
||||
var display = func (message, type) {
|
||||
# Print to console
|
||||
print("Aerotow Everywhere add-on: " ~ message);
|
||||
|
||||
# Read the message by speech synthesizer
|
||||
props.globals.getNode("/sim/sound/voices/ai-plane").setValue(message);
|
||||
|
||||
# Display message on the screen
|
||||
var durationInSec = int(size(message) / 12) + 3;
|
||||
var window = screen.window.new(nil, -40, 10, durationInSec);
|
||||
window.bg = [0.0, 0.0, 0.0, 0.40];
|
||||
|
||||
window.fg = type == "error"
|
||||
? [1.0, 0.0, 0.0, 1]
|
||||
: [0.0, 1.0, 0.0, 1];
|
||||
|
||||
window.align = "center";
|
||||
window.write(message);
|
||||
}
|
||||
106
nasal/aerotow.nas
Normal file
@@ -0,0 +1,106 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Aerotow for enable and disable scenario with aerotow aircraft.
|
||||
#
|
||||
var Aerotow = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
#
|
||||
new: func (addon) {
|
||||
var obj = { parents: [Aerotow] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
obj.listeners = [];
|
||||
|
||||
obj.message = Message.new();
|
||||
obj.thermal = Thermal.new(addon, obj.message);
|
||||
obj.scenario = Scenario.new(addon, obj.message);
|
||||
|
||||
# Listener for ai-model property triggered when the user select a tow aircraft from add-on menu
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/ai-model", func () {
|
||||
obj.restartAerotow();
|
||||
}));
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Uninitialize aerotow module
|
||||
#
|
||||
del: func () {
|
||||
me.thermal.del();
|
||||
|
||||
foreach (var listener; me.listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Function for restart AI scenario with delay when the sound has to stop.
|
||||
#
|
||||
restartAerotow: func () {
|
||||
me.message.success("Aerotow on the way");
|
||||
|
||||
# Stop playing engine sound
|
||||
setprop(me.addonNodePath ~ "/addon-devel/sound/enable", 0);
|
||||
|
||||
# Wait a second for the engine sound to turn off
|
||||
Timer.new().singleShot(1, me, func () {
|
||||
me.unloadScenario();
|
||||
});
|
||||
},
|
||||
|
||||
#
|
||||
# Unload scenario and start a new one
|
||||
#
|
||||
unloadScenario: func () {
|
||||
if (!me.scenario.unload()) {
|
||||
return;
|
||||
}
|
||||
|
||||
# Start aerotow with delay to avoid duplicate engine sound playing
|
||||
Timer.new().singleShot(1, me, func () {
|
||||
me.startAerotow();
|
||||
});
|
||||
},
|
||||
|
||||
#
|
||||
# Main function to prepare AI scenario and run it.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
startAerotow: func () {
|
||||
if (!me.scenario.unload()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!me.scenario.generateXml()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return me.scenario.load();
|
||||
},
|
||||
|
||||
#
|
||||
# Function for unload our AI scenario.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
stopAerotow: func () {
|
||||
var withMessages = 1;
|
||||
return me.scenario.unload(withMessages);
|
||||
},
|
||||
};
|
||||
208
nasal/aircraft.nas
Normal file
@@ -0,0 +1,208 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Parent class of Aircraft
|
||||
#
|
||||
var Aircraft = {
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
DISTANCE_DETERMINANT: 1000, # meters
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# vs - vertical speed in ft per DISTANCE_DETERMINANT m
|
||||
# seed - take-off speed
|
||||
# speedLimit - max speed
|
||||
# rolling - factor for rolling
|
||||
# minRwyLength - minimum runway length required, in meters
|
||||
# name - full name of aircraft used in route dialog
|
||||
# nameMenuCall - short name of aircraft for call a plane from menu
|
||||
# modelPath - Path to the aircraft model
|
||||
#
|
||||
new: func (vs, speed, speedLimit, rolling, minRwyLength, name, nameMenuCall, modelPath) {
|
||||
var obj = { parents: [Aircraft] };
|
||||
|
||||
obj.vs = vs;
|
||||
obj.speed = speed;
|
||||
obj.speedLimit = speedLimit;
|
||||
obj.rolling = rolling;
|
||||
obj.minRwyLength = minRwyLength;
|
||||
obj.name = name;
|
||||
obj.nameMenuCall = nameMenuCall;
|
||||
obj.modelPath = modelPath;
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Check that given name match to aircraft name
|
||||
#
|
||||
# name - Name of aircraft to check.
|
||||
#
|
||||
# Return 1 when match, otherwise 0.
|
||||
#
|
||||
isModelName: func (name) {
|
||||
return name == me.name or name == me.nameMenuCall;
|
||||
},
|
||||
|
||||
#
|
||||
# Return how much the altitide increases for a given vertical speed and distance
|
||||
#
|
||||
# distance - distance in meters
|
||||
#
|
||||
getAltChange: func (distance) {
|
||||
return me.vs * (distance / Aircraft.DISTANCE_DETERMINANT);
|
||||
},
|
||||
|
||||
#
|
||||
# Return selected Aircraft object
|
||||
#
|
||||
# addon - Addon object
|
||||
# isRouteMode - Use 1 to get the plane for the "Aerotow Route" dialog,
|
||||
# use 0 (default) for call the airplane for towing.
|
||||
#
|
||||
getSelected: func (addon, isRouteMode = 0) {
|
||||
var name = Aircraft.getSelectedAircraftName(addon, isRouteMode);
|
||||
foreach (var aircraft; g_Aircrafts) {
|
||||
if (aircraft.isModelName(name)) {
|
||||
return aircraft;
|
||||
}
|
||||
}
|
||||
|
||||
# Fist as default
|
||||
return g_Aircrafts[0];
|
||||
},
|
||||
|
||||
#
|
||||
# Return name of selected aircraft. Possible values depend of isRouteMode: "Cub", "DR400", "c182".
|
||||
#
|
||||
#
|
||||
# addon - Addon object
|
||||
# isRouteMode - Use 1 to get the plane for the "Aerotow Route" dialog,
|
||||
# use 0 (default) for call the airplane for towing.
|
||||
#
|
||||
getSelectedAircraftName: func (addon, isRouteMode = 0) {
|
||||
if (isRouteMode) {
|
||||
return getprop(addon.node.getPath() ~ "/addon-devel/route/ai-model") or g_Aircrafts[0].name;
|
||||
}
|
||||
|
||||
return getprop(addon.node.getPath() ~ "/addon-devel/ai-model") or g_Aircrafts[0].nameMenuCall;
|
||||
},
|
||||
};
|
||||
|
||||
#
|
||||
# Cub
|
||||
# Cruise Speed 61 kt
|
||||
# Max Speed 106 kt
|
||||
# Approach speed 44-52 kt
|
||||
# Stall speed 33 kt
|
||||
#
|
||||
var AircraftCub = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
return {
|
||||
parents: [Aircraft],
|
||||
vs: 200,
|
||||
speed: 55,
|
||||
speedLimit: 60,
|
||||
rolling: 1,
|
||||
minRwyLength: 280,
|
||||
name: "Piper J3 Cub",
|
||||
nameMenuCall: "Cub",
|
||||
modelPath: "Aircraft/Aerotow/Cub/Models/Cub-ai.xml",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
#
|
||||
# Robin DR 400
|
||||
# Cruise Speed 134 kt
|
||||
# Max speeed 166 kt
|
||||
# Stall speed 51 kt
|
||||
# Rate of climb: 825 ft/min
|
||||
#
|
||||
var AircraftRobin = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
return {
|
||||
parents: [Aircraft],
|
||||
vs: 285,
|
||||
speed: 70,
|
||||
speedLimit: 75,
|
||||
rolling: 2,
|
||||
minRwyLength: 470,
|
||||
name: "Robin DR400",
|
||||
nameMenuCall: "DR400",
|
||||
modelPath: "Aircraft/Aerotow/DR400/Models/dr400-ai.xml",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
#
|
||||
# Cessna 182
|
||||
# Cruise Speed 145 kt
|
||||
# Max speeed 175 kt
|
||||
# Stall speed 50 kt
|
||||
# Best climb: 924 ft/min
|
||||
#
|
||||
var AircraftC182 = {
|
||||
new: func () {
|
||||
return {
|
||||
parents: [Aircraft],
|
||||
vs: 295,
|
||||
speed: 75,
|
||||
speedLimit: 80,
|
||||
rolling: 2.2,
|
||||
minRwyLength: 508,
|
||||
name: "Cessna 182",
|
||||
nameMenuCall: "c182",
|
||||
modelPath: "Aircraft/Aerotow/c182/Models/c182-ai.xml",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
#
|
||||
# Douglas C-47
|
||||
# Cruise Speed 152 kt
|
||||
# Max speeed 199 kt
|
||||
# Stall speed 57 kt
|
||||
# Best climb: 1052 ft/min
|
||||
#
|
||||
var AircraftC47 = {
|
||||
new: func () {
|
||||
return {
|
||||
parents: [Aircraft],
|
||||
vs: 310,
|
||||
speed: 85,
|
||||
speedLimit: 90,
|
||||
rolling: 2.2,
|
||||
minRwyLength: 508,
|
||||
name: "Douglas C-47",
|
||||
nameMenuCall: "C47",
|
||||
modelPath: "Aircraft/Aerotow/C-47/Models/c-47-ai.xml",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
# Create Aircraft objects
|
||||
var g_Aircrafts = [
|
||||
AircraftCub.new(),
|
||||
AircraftRobin.new(),
|
||||
AircraftC182.new(),
|
||||
AircraftC47.new(),
|
||||
];
|
||||
135
nasal/dialogs/route.nas
Normal file
@@ -0,0 +1,135 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class for hande Route Dialog
|
||||
#
|
||||
var RouteDialog = {
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
ROUTE_SAVES_DIR: "route-saves",
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
#
|
||||
new: func (addon, message) {
|
||||
var obj = { parents: [RouteDialog] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.message = message;
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
|
||||
obj.savePath = addon.storagePath ~ "/" ~ RouteDialog.ROUTE_SAVES_DIR;
|
||||
obj.maxRouteWaypoints = 10;
|
||||
obj.listeners = [];
|
||||
|
||||
# Set listener for aerotow combo box value in route dialog for recalculate altitude change
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/route/ai-model", func () {
|
||||
obj.calculateAltChangeAndTotals();
|
||||
}));
|
||||
|
||||
# Set listeners for distance fields for calculate altitude change
|
||||
for (var i = 0; i < obj.maxRouteWaypoints; i += 1) {
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ i ~ "]/distance-m", func () {
|
||||
obj.calculateAltChangeAndTotals();
|
||||
}));
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Destructor
|
||||
#
|
||||
del: func () {
|
||||
foreach (var listener; me.listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Calculate total distance and altitude and put in to property tree
|
||||
#
|
||||
calculateAltChangeAndTotals: func () {
|
||||
var totalDistance = 0.0;
|
||||
var totalAlt = 0.0;
|
||||
var isEnd = 0;
|
||||
|
||||
var isRouteMode = 1;
|
||||
var aircraft = Aircraft.getSelected(me.addon, isRouteMode);
|
||||
|
||||
for (var i = 0; i < me.maxRouteWaypoints; i += 1) {
|
||||
var distance = getprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ i ~ "]/distance-m");
|
||||
if (distance == nil) {
|
||||
break;
|
||||
}
|
||||
|
||||
var altChange = aircraft.getAltChange(distance);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ i ~ "]/alt-change-agl-ft", altChange);
|
||||
|
||||
if (!isEnd) {
|
||||
if (distance > 0.0) {
|
||||
totalDistance += distance;
|
||||
totalAlt += altChange;
|
||||
}
|
||||
else {
|
||||
isEnd = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/total/distance", totalDistance);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/total/alt", totalAlt);
|
||||
},
|
||||
|
||||
#
|
||||
# Save route with description to the XML file.
|
||||
#
|
||||
save: func () {
|
||||
me.openFileSelector(
|
||||
func (node) {
|
||||
var nodeSave = props.globals.getNode(me.addonNodePath ~ "/addon-devel/route/wpts");
|
||||
if (io.write_properties(node.getValue(), nodeSave)) {
|
||||
me.message.success("The route has been saved");
|
||||
}
|
||||
},
|
||||
"Save route",
|
||||
"Save"
|
||||
);
|
||||
},
|
||||
|
||||
#
|
||||
# Load route with description from the XML file.
|
||||
#
|
||||
load: func () {
|
||||
me.openFileSelector(
|
||||
func (node) {
|
||||
var nodeLoad = props.globals.getNode(me.addonNodePath ~ "/addon-devel/route/wpts");
|
||||
if (io.read_properties(node.getValue(), nodeLoad)) {
|
||||
me.message.success("The route has been loaded");
|
||||
}
|
||||
},
|
||||
"Load route",
|
||||
"Load"
|
||||
);
|
||||
},
|
||||
|
||||
#
|
||||
# Open file selector dialog for save/load XML file with route.
|
||||
#
|
||||
openFileSelector: func (callback, title, button) {
|
||||
var fileSelector = gui.FileSelector.new(callback, title, button, ["*.xml"], me.savePath, "route.xml");
|
||||
fileSelector.open();
|
||||
},
|
||||
};
|
||||
96
nasal/dialogs/thermal.nas
Normal file
@@ -0,0 +1,96 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Thermal for handle add new thermal.
|
||||
#
|
||||
var Thermal = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
# message - Message object
|
||||
#
|
||||
new: func (addon, message) {
|
||||
var obj = { parents: [Thermal] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
obj.message = message;
|
||||
obj.listeners = [];
|
||||
|
||||
# Listener for calculate distance from meters to nautical miles.
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/add-thermal/distance-m", func (node) {
|
||||
setprop(obj.addonNodePath ~ "/addon-devel/add-thermal/distance-nm", node.getValue() * globals.M2NM);
|
||||
}));
|
||||
|
||||
# Listener for calculate strength from ft/s to m/s.
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/add-thermal/strength-fps", func (node) {
|
||||
setprop(obj.addonNodePath ~ "/addon-devel/add-thermal/strength-mps", node.getValue() * globals.FPS2KT * globals.KT2MPS);
|
||||
}));
|
||||
|
||||
# Listener for calculate diameter from ft to m.
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/add-thermal/diameter-ft", func (node) {
|
||||
setprop(obj.addonNodePath ~ "/addon-devel/add-thermal/diameter-m", node.getValue() * globals.FT2M);
|
||||
}));
|
||||
|
||||
# Listener for calculate height from ft to m.
|
||||
append(obj.listeners, setlistener(obj.addonNodePath ~ "/addon-devel/add-thermal/height-msl", func (node) {
|
||||
setprop(obj.addonNodePath ~ "/addon-devel/add-thermal/height-msl-m", node.getValue() * globals.FT2M);
|
||||
}));
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Destructor
|
||||
#
|
||||
del: func () {
|
||||
foreach (var listener; me.listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Add thermal 300 m before glider position.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
add: func () {
|
||||
var heading = getprop("/orientation/heading-deg") or 0;
|
||||
var distance = getprop(me.addonNodePath ~ "/addon-devel/add-thermal/distance-m") or 300;
|
||||
|
||||
var position = geo.aircraft_position();
|
||||
position.apply_course_distance(heading, distance);
|
||||
|
||||
# Get random layer from 1 to 4
|
||||
var layer = int(rand() * 4) + 1;
|
||||
|
||||
var args = props.Node.new({
|
||||
"type": "thermal",
|
||||
"model": "Models/Weather/altocumulus_layer" ~ layer ~ ".xml",
|
||||
"latitude": position.lat(),
|
||||
"longitude": position.lon(),
|
||||
"strength-fps": getprop(me.addonNodePath ~ "/addon-devel/add-thermal/strength-fps") or 16.0,
|
||||
"diameter-ft": getprop(me.addonNodePath ~ "/addon-devel/add-thermal/diameter-ft") or 4000,
|
||||
"height-msl": getprop(me.addonNodePath ~ "/addon-devel/add-thermal/height-msl") or 9000,
|
||||
"search-order": "DATA_ONLY"
|
||||
});
|
||||
|
||||
if (fgcommand("add-aiobject", args)) {
|
||||
me.message.success("The thermal has been added");
|
||||
return 1;
|
||||
}
|
||||
|
||||
me.message.error("Adding thermal failed");
|
||||
return 0;
|
||||
},
|
||||
};
|
||||
422
nasal/flight-plan.nas
Normal file
@@ -0,0 +1,422 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class FlightPlan for crate and save as XML file the flight plan for AI scenario.
|
||||
#
|
||||
var FlightPlan = {
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
FILENAME_FLIGHTPLAN: "aerotown-addon-flightplan.xml",
|
||||
MAX_RUNWAY_DISTANCE: 100, # meters
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
# message - Message object
|
||||
# routeDialog - RouteDialog object
|
||||
#
|
||||
new: func (addon, message, routeDialog) {
|
||||
var obj = { parents: [FlightPlan] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.message = message;
|
||||
obj.routeDialog = routeDialog;
|
||||
obj.flightPlanWriter = FlightPlanWriter.new(addon);
|
||||
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
|
||||
obj.wptCount = 0;
|
||||
obj.coord = nil; # Coordinates for flight plan
|
||||
obj.heading = nil; # AI plane heading
|
||||
obj.altitude = nil; # AI plane altitude
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Get inital location of glider.
|
||||
#
|
||||
# Return object with "lat", "lon" and 'heading".
|
||||
#
|
||||
getLocation: func () {
|
||||
var icao = getprop("/sim/airport/closest-airport-id");
|
||||
if (icao == nil or icao == "") {
|
||||
me.message.error("Airport code cannot be obtained.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
# Find nearest runway threshold
|
||||
var airport = airportinfo(icao);
|
||||
if (airport == nil) {
|
||||
me.message.error("An airport with the code " ~ icao ~ " cannot be found.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
|
||||
var rwyResult = me.findRunway(airport, gliderCoord);
|
||||
|
||||
if (rwyResult.distance > FlightPlan.MAX_RUNWAY_DISTANCE) {
|
||||
# The runway is too far away, we assume a bush start
|
||||
return {
|
||||
"type" : "bush",
|
||||
"lat" : gliderCoord.lat(),
|
||||
"lon" : gliderCoord.lon(),
|
||||
"heading" : getprop("/orientation/heading-deg"),
|
||||
};
|
||||
}
|
||||
|
||||
# We have a runway
|
||||
|
||||
var minRwyLength = Aircraft.getSelected(me.addon).minRwyLength;
|
||||
if (rwyResult.runway.length < minRwyLength) {
|
||||
me.message.error(
|
||||
"This runway is too short. Please choose a longer one than " ~ minRwyLength ~ " m "
|
||||
~ "(" ~ math.round(minRwyLength * globals.M2FT) ~ " ft)."
|
||||
);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return {
|
||||
"type" : "runway",
|
||||
"lat" : rwyResult.runway.lat,
|
||||
"lon" : rwyResult.runway.lon,
|
||||
"heading" : rwyResult.runway.heading,
|
||||
};
|
||||
},
|
||||
|
||||
#
|
||||
# Find nearest runway for given airport
|
||||
#
|
||||
# Return hash with distance to nearest runway threshold and runway object itself.
|
||||
#
|
||||
findRunway: func (airport, gliderCoord) {
|
||||
var result = {
|
||||
"runway" : nil,
|
||||
"distance" : 999999999,
|
||||
};
|
||||
|
||||
foreach (var runwayName; keys(airport.runways)) {
|
||||
var runway = airport.runways[runwayName];
|
||||
var rwyThreshold = geo.Coord.new().set_latlon(runway.lat, runway.lon);
|
||||
|
||||
var distanceToThreshold = rwyThreshold.distance_to(gliderCoord);
|
||||
if (distanceToThreshold < result.distance) {
|
||||
result.runway = runway;
|
||||
result.distance = distanceToThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
#
|
||||
# Initialize flight plan and set it to property tree
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
initial: func () {
|
||||
var location = me.getLocation();
|
||||
if (location == nil) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var aircraft = Aircraft.getSelected(me.addon);
|
||||
|
||||
var isGliderPos = 0;
|
||||
me.initAircraftVariable(location, isGliderPos);
|
||||
|
||||
# inittial readonly waypoint
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/init-wpt/heading-change", me.heading);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/init-wpt/distance-m", 100);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/init-wpt/alt-change-agl-ft", aircraft.vs / 10);
|
||||
|
||||
# in air
|
||||
var wptData = [
|
||||
{"hdgChange": 0, "dist": 5000, "altChange": aircraft.vs * 5},
|
||||
{"hdgChange": -90, "dist": 1000, "altChange": aircraft.vs},
|
||||
{"hdgChange": -90, "dist": 1000, "altChange": aircraft.vs},
|
||||
{"hdgChange": 0, "dist": 5000, "altChange": aircraft.vs * 5},
|
||||
{"hdgChange": -90, "dist": 1500, "altChange": aircraft.vs * 1.5},
|
||||
{"hdgChange": -90, "dist": 1000, "altChange": aircraft.vs},
|
||||
{"hdgChange": 0, "dist": 5000, "altChange": aircraft.vs * 5},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
{"hdgChange": 0, "dist": 0, "altChange": 0},
|
||||
];
|
||||
|
||||
# Default route
|
||||
# ^ - airport with heading direction to north
|
||||
# 1 - 1st waypoint
|
||||
# 2 - 2nd waypoint, etc.
|
||||
#
|
||||
# 2 . . 1 7
|
||||
# . . .
|
||||
# . . .
|
||||
# 3 . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . . .
|
||||
# . ^ 6
|
||||
# . .
|
||||
# . .
|
||||
# 4 . . . . 5
|
||||
|
||||
var index = 0;
|
||||
foreach (var wpt; wptData) {
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ index ~ "]/heading-change", wpt.hdgChange);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ index ~ "]/distance-m", wpt.dist);
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/wpt[" ~ index ~ "]/alt-change-agl-ft", wpt.altChange);
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
me.routeDialog.calculateAltChangeAndTotals();
|
||||
|
||||
setprop(me.addonNodePath ~ "/addon-devel/route/wpts/description", "Default route around the start location");
|
||||
|
||||
return 1;
|
||||
},
|
||||
|
||||
#
|
||||
# Generate the XML file with the flight plane for our plane for AI scenario.
|
||||
# The file will be stored to $Fobj.HOME/Export/aerotown-addon-flightplan.xml.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
generateXml: func () {
|
||||
me.wptCount = 0;
|
||||
|
||||
var location = me.getLocation();
|
||||
if (location == nil) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
me.flightPlanWriter.open();
|
||||
|
||||
var aircraft = Aircraft.getSelected(me.addon);
|
||||
|
||||
var isGliderPos = 1;
|
||||
me.initAircraftVariable(location, isGliderPos);
|
||||
|
||||
# Start at 2 o'clock from the glider...
|
||||
# Inital ktas must be >= 1.0
|
||||
me.addWptGround({"hdgChange": 60, "dist": 25}, {"altChange": 0, "ktas": 5});
|
||||
|
||||
# Reset coord and heading
|
||||
isGliderPos = 0;
|
||||
me.initAircraftVariable(location, isGliderPos);
|
||||
|
||||
var gliderOffsetM = me.getGliderOffsetFromRunwayThreshold(location);
|
||||
|
||||
# ... and line up with the runway
|
||||
me.addWptGround({"hdgChange": 0, "dist": me.getInitialDistance() + gliderOffsetM}, {"altChange": 0, "ktas": 2.5});
|
||||
|
||||
# Rolling
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10}, {"altChange": 0, "ktas": 5});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 20}, {"altChange": 0, "ktas": 5});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 20}, {"altChange": 0, "ktas": aircraft.speed / 6});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10}, {"altChange": 0, "ktas": aircraft.speed / 5});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed / 4});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed / 3.5});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed / 3});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed / 2.5});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed / 2});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed / 1.75});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed / 1.5});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed / 1.25});
|
||||
me.addWptGround({"hdgChange": 0, "dist": 10 * aircraft.rolling}, {"altChange": 0, "ktas": aircraft.speed});
|
||||
|
||||
# Take-off
|
||||
me.addWptAir({"hdgChange": 0, "dist": 100 * aircraft.rolling}, {"elevationPlus": 3, "ktas": aircraft.speed * 1.05});
|
||||
me.addWptAir({"hdgChange": 0, "dist": 100}, {"altChange": aircraft.vs / 10, "ktas": aircraft.speed * 1.025});
|
||||
|
||||
var speedInc = 1.0;
|
||||
foreach (var wptNode; props.globals.getNode(me.addonNodePath ~ "/addon-devel/route/wpts").getChildren("wpt")) {
|
||||
var dist = wptNode.getChild("distance-m").getValue();
|
||||
if (dist <= 0.0) {
|
||||
break;
|
||||
}
|
||||
|
||||
var hdgChange = wptNode.getChild("heading-change").getValue();
|
||||
var altChange = aircraft.getAltChange(dist);
|
||||
|
||||
speedInc += ((dist / Aircraft.DISTANCE_DETERMINANT) * 0.025);
|
||||
var ktas = aircraft.speed * speedInc;
|
||||
if (ktas > aircraft.speedLimit) {
|
||||
ktas = aircraft.speedLimit;
|
||||
}
|
||||
|
||||
me.addWptAir({"hdgChange": hdgChange, "dist": dist}, {"altChange": altChange, "ktas": ktas});
|
||||
}
|
||||
|
||||
me.addWptEnd();
|
||||
|
||||
me.flightPlanWriter.close();
|
||||
|
||||
return 1;
|
||||
},
|
||||
|
||||
#
|
||||
# Get initial distance AI plane from the glider that the tow is nearly tautened.
|
||||
#
|
||||
# Return distance in meters.
|
||||
#
|
||||
getInitialDistance: func () {
|
||||
var ropeLengthM = getprop("/sim/hitches/aerotow/tow/length") or 60;
|
||||
var tautenRelative = 0.68;
|
||||
return ropeLengthM * tautenRelative;
|
||||
},
|
||||
|
||||
#
|
||||
# Initialize AI aircraft variable
|
||||
#
|
||||
# location - Object of location from which the glider start.
|
||||
# isGliderPos - Pass 1 for set AI aircraft's coordinates as glider position, 0 set coordinates as runway threshold.
|
||||
#
|
||||
initAircraftVariable: func (location, isGliderPos = 1) {
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
|
||||
# Set coordinates as glider position or runway threshold
|
||||
me.coord = isGliderPos
|
||||
? gliderCoord
|
||||
: geo.Coord.new().set_latlon(location.lat, location.lon);
|
||||
|
||||
# Set airplane heading as runway or glider heading
|
||||
me.heading = location.heading;
|
||||
|
||||
# Set AI airplane altitude as glider altitude (assumed it's on the ground).
|
||||
# It is more accurate than airport.elevation.
|
||||
me.altitude = gliderCoord.alt() * globals.M2FT;
|
||||
},
|
||||
|
||||
#
|
||||
# Get distance from glider to runway threshold e.g. in case that the user taxi from the runway threshold
|
||||
#
|
||||
# location - Object of location from which the glider start.
|
||||
# Return the distance in metres, of the glider's displacement from the runway threshold.
|
||||
#
|
||||
getGliderOffsetFromRunwayThreshold: func (location) {
|
||||
if (location.type == "runway") {
|
||||
var gliderCoord = geo.aircraft_position();
|
||||
var rwyThreshold = geo.Coord.new().set_latlon(location.lat, location.lon);
|
||||
|
||||
return rwyThreshold.distance_to(gliderCoord);
|
||||
}
|
||||
|
||||
# We are not on the runway
|
||||
return 0;
|
||||
},
|
||||
|
||||
#
|
||||
# Add new waypoint on ground
|
||||
#
|
||||
# coordOffset - Hash for calculate next coordinates (lat, lon), with following fields:
|
||||
# {
|
||||
# hdgChange - How the aircraft's heading supposed to change? 0 - keep the same heading.
|
||||
# dis - Distance in meters to calculate next waypoint coordinates.
|
||||
# }
|
||||
# performance - Hash with following fields:
|
||||
# {
|
||||
# altChange - How the aircraft's altitude is supposed to change? 0 - keep the same altitude.
|
||||
# ktas - True air speed of AI plane at the waypoint.
|
||||
# }
|
||||
#
|
||||
addWptGround: func (coordOffset, performance) {
|
||||
me.wrireWpt(nil, coordOffset, performance, "ground");
|
||||
},
|
||||
|
||||
#
|
||||
# Add new waypoint in air
|
||||
#
|
||||
addWptAir: func (coordOffset, performance) {
|
||||
me.wrireWpt(nil, coordOffset, performance, "air");
|
||||
},
|
||||
|
||||
#
|
||||
# Add "WAIT" waypoint
|
||||
#
|
||||
# sec - Number of seconds for wait
|
||||
#
|
||||
addWptWait: func (sec) {
|
||||
me.wrireWpt("WAIT", {}, {}, nil, sec);
|
||||
},
|
||||
|
||||
#
|
||||
# Add "END" waypoint
|
||||
#
|
||||
addWptEnd: func () {
|
||||
me.wrireWpt("END", {}, {});
|
||||
},
|
||||
|
||||
#
|
||||
# Write waypoint to flight plan file
|
||||
#
|
||||
# name - The name of waypoint
|
||||
# coordOffset.hdgChange - How the aircraft's heading supposed to change?
|
||||
# coordOffset.dist - Distance in meters to calculate next waypoint coordinates
|
||||
# performance.altChange - How the aircraft's altitude is supposed to change?
|
||||
# performance.elevationPlus - Set aircraft altitude as current terrain elevation + given value in feets.
|
||||
# It's best to use for the first point in the air to avoid the plane collapsing into
|
||||
# the ground in a bumpy airport
|
||||
# performance.ktas - True air speed of AI plane at the waypoint
|
||||
# groundAir - Allowed value: "ground or "air". The "ground" means that AI plane is on the ground, "air" - in air
|
||||
# sec - Number of seconds for "WAIT" waypoint
|
||||
#
|
||||
wrireWpt: func (
|
||||
name,
|
||||
coordOffset,
|
||||
performance,
|
||||
groundAir = nil,
|
||||
sec = nil
|
||||
) {
|
||||
var coord = nil;
|
||||
if (contains(coordOffset, "hdgChange") and contains(coordOffset, "dist")) {
|
||||
me.heading += coordOffset.hdgChange;
|
||||
if (me.heading < 0) {
|
||||
me.heading += 360;
|
||||
}
|
||||
|
||||
if (me.heading > 360) {
|
||||
me.heading -= 360;
|
||||
}
|
||||
|
||||
me.coord.apply_course_distance(me.heading, coordOffset.dist);
|
||||
coord = me.coord;
|
||||
}
|
||||
|
||||
var alt = nil;
|
||||
if (coord != nil and contains(performance, "elevationPlus")) {
|
||||
var elevation = geo.elevation(coord.lat(), coord.lon());
|
||||
me.altitude = elevation == nil
|
||||
? me.altitude + performance.elevationPlus
|
||||
: elevation * globals.M2FT + performance.elevationPlus;
|
||||
alt = me.altitude;
|
||||
}
|
||||
else if (contains(performance, "altChange")) {
|
||||
me.altitude += performance.altChange;
|
||||
alt = me.altitude;
|
||||
}
|
||||
|
||||
var ktas = contains(performance, "ktas") ? performance.ktas : nil;
|
||||
|
||||
name = name == nil ? me.wptCount : name;
|
||||
me.flightPlanWriter.write(name, coord, alt, ktas, groundAir, sec);
|
||||
|
||||
me.wptCount += 1;
|
||||
},
|
||||
};
|
||||
111
nasal/io/flight-plan-writer.nas
Normal file
@@ -0,0 +1,111 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class FlightPlanWriter for write flight plan to the XML file
|
||||
#
|
||||
var FlightPlanWriter = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
#
|
||||
new: func (addon) {
|
||||
var obj = { parents: [FlightPlanWriter] };
|
||||
|
||||
obj.fpFileHandler = nil; # Handler for wrire flight plan to file
|
||||
obj.flightPlanPath = addon.storagePath ~ "/AI/FlightPlans/" ~ FlightPlan.FILENAME_FLIGHTPLAN;
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Open XML file to wrire flight plan
|
||||
#
|
||||
open: func () {
|
||||
me.fpFileHandler = io.open(me.flightPlanPath, "w");
|
||||
|
||||
if (me.fpFileHandler) {
|
||||
io.write(
|
||||
me.fpFileHandler,
|
||||
"<?xml version=\"1.0\"?>\n\n" ~
|
||||
"<!-- This file is generated automatically by the Aerotow Everywhere add-on -->\n\n" ~
|
||||
"<PropertyList>\n" ~
|
||||
" <flightplan>\n"
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Write single waypoint to XML file with flight plan
|
||||
#
|
||||
# name - Name of waypoint. Special names are: "WAIT", "END".
|
||||
# coord - The Coord object
|
||||
# alt - Altitude AMSL of AI plane
|
||||
# ktas - True air speed of AI plane
|
||||
# groundAir - Allowe value: "ground or "air". The "ground" means that AI plane is on the ground, "air" - in air
|
||||
# sec - Number of seconds for "WAIT" waypoint
|
||||
#
|
||||
write: func (name, coord = nil, alt = nil, ktas = nil, groundAir = nil, sec = nil) {
|
||||
if (!me.fpFileHandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
var str = " <wpt>\n"
|
||||
~ " <name>" ~ name ~ "</name>\n";
|
||||
|
||||
if (coord != nil) {
|
||||
str ~= " <lat>" ~ coord.lat() ~ "</lat>\n";
|
||||
str ~= " <lon>" ~ coord.lon() ~ "</lon>\n";
|
||||
str ~= " <!--\n"
|
||||
~ " " ~ coord.lat() ~ "," ~ coord.lon() ~ "\n"
|
||||
~ " -->\n";
|
||||
}
|
||||
|
||||
if (alt != nil) {
|
||||
# str ~= " <alt>" ~ alt ~ "</alt>\n";
|
||||
str ~= " <crossat>" ~ alt ~ "</crossat>\n";
|
||||
}
|
||||
|
||||
if (ktas != nil) {
|
||||
str ~= " <ktas>" ~ ktas ~ "</ktas>\n";
|
||||
}
|
||||
|
||||
if (groundAir != nil) {
|
||||
var onGround = groundAir == "ground" ? "true" : "false";
|
||||
str ~= " <on-ground>" ~ onGround ~ "</on-ground>\n";
|
||||
}
|
||||
|
||||
if (sec != nil) {
|
||||
str ~= " <time-sec>" ~ sec ~ "</time-sec>\n";
|
||||
}
|
||||
|
||||
str ~= " </wpt>\n";
|
||||
|
||||
io.write(me.fpFileHandler, str);
|
||||
},
|
||||
|
||||
#
|
||||
# Close XML file with flight plan
|
||||
#
|
||||
close: func () {
|
||||
if (me.fpFileHandler) {
|
||||
io.write(
|
||||
me.fpFileHandler,
|
||||
" </flightplan>\n" ~
|
||||
"</PropertyList>\n\n"
|
||||
);
|
||||
|
||||
io.close(me.fpFileHandler);
|
||||
me.fpFileHandler = nil;
|
||||
}
|
||||
},
|
||||
};
|
||||
66
nasal/message.nas
Normal file
@@ -0,0 +1,66 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Message for display messages on the screen with read by speech synthesizer.
|
||||
#
|
||||
var Message = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
return { parents: [Message] };
|
||||
},
|
||||
|
||||
#
|
||||
# Display given message as OK.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
#
|
||||
success: func (message) {
|
||||
me.display(message, "ok");
|
||||
},
|
||||
|
||||
#
|
||||
# Display given message as an error.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
#
|
||||
error: func (message) {
|
||||
me.display(message, "error");
|
||||
},
|
||||
|
||||
#
|
||||
# Display given message.
|
||||
#
|
||||
# message - The text message to display on the screen and read by speech synthesizer.
|
||||
# type - The type of message. It can take values as "ok" or "error".
|
||||
#
|
||||
display: func (message, type) {
|
||||
# Print to console
|
||||
print("Aerotow Everywhere add-on: " ~ message);
|
||||
|
||||
# Read the message by speech synthesizer
|
||||
props.globals.getNode("/sim/sound/voices/ai-plane").setValue(message);
|
||||
|
||||
# Display message on the screen
|
||||
var durationInSec = int(size(message) / 12) + 3;
|
||||
var window = screen.window.new(nil, -40, 10, durationInSec);
|
||||
window.bg = [0.0, 0.0, 0.0, 0.40];
|
||||
|
||||
window.fg = type == "error"
|
||||
? [1.0, 0.0, 0.0, 1]
|
||||
: [0.0, 1.0, 0.0, 1];
|
||||
|
||||
window.align = "center";
|
||||
window.write(message);
|
||||
},
|
||||
};
|
||||
180
nasal/scenario.nas
Normal file
@@ -0,0 +1,180 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Scenario for create AI scenario XML file and attach it to scenario property list for load it.
|
||||
#
|
||||
var Scenario = {
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
SCENARIO_ID: "aerotow_addon",
|
||||
SCENARIO_NAME: "Aerotow Add-on",
|
||||
SCENARIO_DESC: "This scenario starts the towing plane at the airport where the pilot with the glider is located. Use Ctrl-o to hook the plane.",
|
||||
FILENAME_SCENARIO: "aerotown-addon.xml",
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
# addon - Addon object
|
||||
# message - Message object
|
||||
#
|
||||
new: func (addon, message) {
|
||||
var obj = { parents: [Scenario] };
|
||||
|
||||
obj.addon = addon;
|
||||
obj.message = message;
|
||||
|
||||
obj.addonNodePath = addon.node.getPath();
|
||||
|
||||
obj.listeners = [];
|
||||
obj.routeDialog = RouteDialog.new(addon, message);
|
||||
obj.flightPlan = FlightPlan.new(addon, message, obj.routeDialog);
|
||||
obj.isScenarioLoaded = 0;
|
||||
obj.scenarioPath = addon.storagePath ~ "/" ~ Scenario.FILENAME_SCENARIO;
|
||||
|
||||
obj.flightPlan.initial();
|
||||
|
||||
append(obj.listeners, setlistener("/sim/presets/longitude-deg", func () {
|
||||
# User change airport/runway
|
||||
obj.flightPlan.initial();
|
||||
}));
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
#
|
||||
# Destructor
|
||||
#
|
||||
del: func () {
|
||||
me.routeDialog.del();
|
||||
|
||||
foreach (var listener; me.listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Generate the XML file with the AI scenario.
|
||||
# The file will be stored to $FG_HOME/Export/aerotown-addon.xml.
|
||||
#
|
||||
generateXml: func () {
|
||||
if (!me.flightPlan.generateXml()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var scenarioXml = {
|
||||
"PropertyList": {
|
||||
"scenario": {
|
||||
"name": Scenario.SCENARIO_NAME,
|
||||
"description": Scenario.SCENARIO_DESC,
|
||||
"entry": {
|
||||
"callsign": "FG-TOW",
|
||||
"type": "aircraft",
|
||||
"class": "aerotow-dragger",
|
||||
"model": Aircraft.getSelected(me.addon).modelPath,
|
||||
"flightplan": FlightPlan.FILENAME_FLIGHTPLAN,
|
||||
"repeat": 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var node = props.Node.new(scenarioXml);
|
||||
io.writexml(me.scenarioPath, node);
|
||||
|
||||
me.addScenarioToPropertyList();
|
||||
|
||||
return 1;
|
||||
},
|
||||
|
||||
#
|
||||
# Add our new scenario to the "/sim/ai/scenarios" property list
|
||||
# so that FlightGear will be able to load it by "load-scenario" command.
|
||||
#
|
||||
addScenarioToPropertyList: func () {
|
||||
if (!me.isAlreadyAdded()) {
|
||||
var scenarioData = {
|
||||
"name": Scenario.SCENARIO_NAME,
|
||||
"id": Scenario.SCENARIO_ID,
|
||||
"description": Scenario.SCENARIO_DESC,
|
||||
"path": me.scenarioPath,
|
||||
};
|
||||
|
||||
props.globals.getNode("/sim/ai/scenarios").addChild("scenario").setValues(scenarioData);
|
||||
}
|
||||
},
|
||||
|
||||
#
|
||||
# Return 1 if scenario is already added to "/sim/ai/scenarios" property list, otherwise return 0.
|
||||
#
|
||||
isAlreadyAdded: func () {
|
||||
foreach (var scenario; props.globals.getNode("/sim/ai/scenarios").getChildren("scenario")) {
|
||||
var id = scenario.getChild("id");
|
||||
if (id != nil and id.getValue() == Scenario.SCENARIO_ID) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
|
||||
#
|
||||
# Load scenario
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
load: func () {
|
||||
var args = props.Node.new({ "name": Scenario.SCENARIO_ID });
|
||||
if (fgcommand("load-scenario", args)) {
|
||||
me.isScenarioLoaded = 1;
|
||||
me.message.success("Let's fly!");
|
||||
|
||||
# Enable engine sound
|
||||
setprop(me.addonNodePath ~ "/addon-devel/sound/enable", 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
me.message.error("Tow failed!");
|
||||
return 0;
|
||||
},
|
||||
|
||||
#
|
||||
# Unload scenario
|
||||
#
|
||||
# withMessages - Set 1 to display messages.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
unload: func (withMessages = 0) {
|
||||
if (me.isScenarioLoaded) {
|
||||
var args = props.Node.new({ "name": Scenario.SCENARIO_ID });
|
||||
if (fgcommand("unload-scenario", args)) {
|
||||
me.isScenarioLoaded = 0;
|
||||
|
||||
if (withMessages) {
|
||||
me.message.success("Aerotown disabled");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (withMessages) {
|
||||
me.message.error("Aerotown disable failed");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (withMessages) {
|
||||
me.message.success("Aerotown already disabled");
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
};
|
||||
41
nasal/timer.nas
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Class Timer for wrapping maketimer() function.
|
||||
#
|
||||
var Timer = {
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
new: func () {
|
||||
return { parents: [Timer] };
|
||||
},
|
||||
|
||||
#
|
||||
# Run timer as single shot
|
||||
#
|
||||
# delaySec - Delay in seconds for execute timer's callback.
|
||||
# self - Specifying what any "me" references in the function being called will refer to.
|
||||
# callback - Function to be called after given delay.
|
||||
#
|
||||
# Return timer handler object
|
||||
#
|
||||
singleShot: func (delaySec, self, callback) {
|
||||
var timer = maketimer(delaySec, self, func () {
|
||||
callback();
|
||||
});
|
||||
timer.singleShot = 1;
|
||||
timer.start();
|
||||
|
||||
return timer;
|
||||
},
|
||||
};
|
||||
90
thermal.nas
@@ -1,90 +0,0 @@
|
||||
#
|
||||
# Aerotow Everywhere - Add-on for FlightGear
|
||||
#
|
||||
# Written and developer by Roman Ludwicki (PlayeRom, SP-ROM)
|
||||
#
|
||||
# Copyright (C) 2022 Roman Ludwicki
|
||||
#
|
||||
# Aerotow Everywhere is an Open Source project and it is licensed
|
||||
# under the GNU Public License v3 (GPLv3)
|
||||
#
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
var ADDON = addons.getAddon("org.flightgear.addons.Aerotow");
|
||||
var ADDON_NODE_PATH = ADDON.node.getPath();
|
||||
|
||||
#
|
||||
# Variables
|
||||
#
|
||||
var g_thermalListeners = [];
|
||||
|
||||
#
|
||||
# Initialize thermal module
|
||||
#
|
||||
var init = func () {
|
||||
# Listener for calculate distance from meters to nautical miles.
|
||||
append(g_thermalListeners, setlistener(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/distance-m", func (node) {
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/distance-nm", node.getValue() * globals.M2NM);
|
||||
}));
|
||||
|
||||
# Listener for calculate strength from ft/s to m/s.
|
||||
append(g_thermalListeners, setlistener(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/strength-fps", func (node) {
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/strength-mps", node.getValue() * globals.FPS2KT * globals.KT2MPS);
|
||||
}));
|
||||
|
||||
# Listener for calculate diameter from ft to m.
|
||||
append(g_thermalListeners, setlistener(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/diameter-ft", func (node) {
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/diameter-m", node.getValue() * globals.FT2M);
|
||||
}));
|
||||
|
||||
# Listener for calculate height from ft to m.
|
||||
append(g_thermalListeners, setlistener(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/height-msl", func (node) {
|
||||
setprop(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/height-msl-m", node.getValue() * globals.FT2M);
|
||||
}));
|
||||
}
|
||||
|
||||
#
|
||||
# Uninitialize thermal module
|
||||
#
|
||||
var uninit = func () {
|
||||
foreach (var listener; g_thermalListeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Add thermal 300 m before glider position.
|
||||
#
|
||||
# Return 1 on successful, otherwise 0.
|
||||
#
|
||||
var add = func () {
|
||||
var heading = getprop("/orientation/heading-deg") or 0;
|
||||
var distance = getprop(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/distance-m") or 300;
|
||||
|
||||
var position = geo.aircraft_position();
|
||||
position.apply_course_distance(heading, distance);
|
||||
|
||||
# Get random layer from 1 to 4
|
||||
var layer = int(rand() * 4) + 1;
|
||||
|
||||
var args = props.Node.new({
|
||||
"type": "thermal",
|
||||
"model": "Models/Weather/altocumulus_layer" ~ layer ~ ".xml",
|
||||
"latitude": position.lat(),
|
||||
"longitude": position.lon(),
|
||||
"strength-fps": getprop(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/strength-fps") or 16.0,
|
||||
"diameter-ft": getprop(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/diameter-ft") or 4000,
|
||||
"height-msl": getprop(ADDON_NODE_PATH ~ "/addon-devel/add-thermal/height-msl") or 9000,
|
||||
"search-order": "DATA_ONLY"
|
||||
});
|
||||
|
||||
if (fgcommand("add-aiobject", args)) {
|
||||
messages.displayOk("The thermal has been added");
|
||||
return 1;
|
||||
}
|
||||
|
||||
messages.displayError("Adding thermal failed");
|
||||
return 0;
|
||||
}
|
||||