Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cee3f44622 | ||
|
|
602396729c | ||
|
|
2fa6110ba0 | ||
|
|
2e5dbbc0b8 | ||
|
|
4a8e1e4981 | ||
|
|
1f39ea2347 | ||
|
|
33c0c12984 | ||
|
|
6ecea39828 | ||
|
|
a31e99bf4b |
12
CHANGELOG.md
Normal file
12
CHANGELOG.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v.1.1.0
|
||||
|
||||
- Fix AI aircraft going underground during take-off due to uneven runway elevations.
|
||||
- Possibility to add thermal with configuration of its parameters.
|
||||
|
||||
## v.1.0.0
|
||||
|
||||
- Possibility to call a towing aircraft at any airport
|
||||
- One defined route for the towing aircraft
|
||||
- 3 types of tow airplanes
|
||||
24
README.md
24
README.md
@@ -2,10 +2,10 @@
|
||||
|
||||
This is an add-on designed to include an AI aircraft that will be able to tow a glider. The main idea is to be able to do this at any airport where you start with your glider.
|
||||
|
||||
## Instalation
|
||||
## Installation
|
||||
|
||||
This add-on creates a flight plan in real-time based on the airport you are at. Unfortunately this causes a problem which you will have to solve manually by following the instructions below.
|
||||
Namely, the flight plan for AI aircraft, by default, must be stored in the `$FG_ROOT/AI/FlightPlanes/` directory. But for security reasons, Nasal scripts cannot save files to that directory, but can save e.g. to `$FG_HOME/Export/`. Therefore, the run-time created flight plan will just be saved to `$FG_HOME/Export/Addons/org.flightgear.addons.Aerotow/AI/FlightPlanes/`. The problem is that FlightGear does not know that it is supposed to look for the flight plan file in this location as well, so you have to tell it manually by using the `--data` command line option.
|
||||
Namely, the flight plan for AI aircraft, by default, must be stored in the `$FG_ROOT/AI/FlightPlans/` directory. But for security reasons, Nasal scripts cannot save files to that directory, but can save e.g. to `$FG_HOME/Export/`. Therefore, the run-time created flight plan will just be saved to `$FG_HOME/Export/Addons/org.flightgear.addons.Aerotow/AI/FlightPlans/`. The problem is that FlightGear does not know that it is supposed to look for the flight plan file in this location as well, so you have to tell it manually by using the `--data` command line option.
|
||||
|
||||
`$FG_HOME` is the FlightGear home path. It differs depending on the operating system.
|
||||
Under Linux/macOS `$FG_HOME` is `/home/{username}/.fgfs/`.
|
||||
@@ -32,14 +32,28 @@ You can disconnect from the aircraft at any time, most often by pressing the `o`
|
||||
|
||||
## Menu of add-on
|
||||
|
||||
This add-on add a new item to main menu named "Aerotow Everywhere" with following items:
|
||||
This add-on add a new item to main menu named `Aerotow Everywhere` with following items:
|
||||
|
||||
1. `Call for Piper J3 Cub aircraft` - load AI tow sceneraio with Piper J3 Cub. Possible altitude to reach ~3,600 ft.
|
||||
2. `Call for Robin DR400 aircraft` - load AI tow sceneraio with Robin DR400. This aircraft has better performance and can take you to over 4,500 ft.
|
||||
3. `Call for Cessna 182 aircraft` - load AI tow sceneraio with Cessna 182. This aircraft has little bit better performance than Robin.
|
||||
4. `Disable tow aircraft` - unload AI tow sceneraio.
|
||||
5. `Help` - display help dialog.
|
||||
6. `About` - display about dialog with add-on information.
|
||||
5. `Add thermal` - display the dialog for configuring and adding thermal.
|
||||
6. `Help` - display help dialog.
|
||||
7. `About` - display about dialog with add-on information.
|
||||
|
||||
## Adding thermals
|
||||
|
||||
An additional feature of this add-on is the possibility of placing the thermals just in front of the glider. To do this, go to menu `Aerotow Everywhere` -> `Add thermal` where you can configure the following parameters:
|
||||
|
||||
1. `Distance` - the distance in metres at which the thermals will be placed in front of the gliders.
|
||||
2. `Strength` - thermal strength in feet per second.
|
||||
3. `Diameter` - diameter of thermal in feet.
|
||||
4. `Height` - height of thermal in feet above mean sea level.
|
||||
|
||||
Click `Add thermal` button for add the thermal.
|
||||
|
||||
Many thanks to the forum user "wlbragg" for proposing and presenting a solution to this feature.
|
||||
|
||||
## Limitations
|
||||
|
||||
|
||||
@@ -27,6 +27,16 @@
|
||||
<org.flightgear.addons.Aerotow>
|
||||
<addon-devel>
|
||||
<ai-model type="string">Cub</ai-model>
|
||||
<add-thermal>
|
||||
<distance-m type="int">300</distance-m>
|
||||
<distance-nm type="double">0.16</distance-nm>
|
||||
<strength-fps type="double">16.0</strength-fps>
|
||||
<strength-mps type="double">4.88</strength-mps>
|
||||
<diameter-ft type="int">4000</diameter-ft>
|
||||
<diameter-m type="int">1219</diameter-m>
|
||||
<height-msl type="int">9000</height-msl>
|
||||
<height-msl-m type="int">2742</height-msl-m>
|
||||
</add-thermal>
|
||||
<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).
|
||||
|
||||
@@ -21,11 +21,15 @@ var unload = func(addon) {
|
||||
#
|
||||
# Other resources should be freed by adding the corresponding code here,
|
||||
# e.g. myCanvas.del();
|
||||
|
||||
aerotow.uninit();
|
||||
thermal.uninit();
|
||||
}
|
||||
|
||||
var main = func(addon) {
|
||||
print("Aerotow Everywhere add-on initialized from path ", addon.basePath);
|
||||
|
||||
# Create $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow directory
|
||||
addon.createStorageDir();
|
||||
|
||||
# Create /AI/FlightPlans/ directory in $FG_HOME/Export/Addons/org.flightgear.addons.Aerotow/
|
||||
@@ -36,17 +40,19 @@ var main = func(addon) {
|
||||
|
||||
loadExtraNasalFiles(addon);
|
||||
|
||||
setlistener("/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/ai-model", func(n) {
|
||||
aerotow.startAerotow();
|
||||
});
|
||||
aerotow.init();
|
||||
thermal.init();
|
||||
}
|
||||
|
||||
#
|
||||
# Load extra Nasal files in main add-on directory
|
||||
#
|
||||
var loadExtraNasalFiles = func (addon) {
|
||||
foreach (var scriptName; ["aerotow", "messages"]) {
|
||||
foreach (var scriptName; ["aerotow", "messages", "thermal"]) {
|
||||
var fileName = addon.basePath ~ "/" ~ scriptName ~ ".nas";
|
||||
|
||||
print("Loading Aerotown Add-on module ", fileName);
|
||||
|
||||
io.load_nasal(fileName, scriptName);
|
||||
if (io.load_nasal(fileName, scriptName)) {
|
||||
print("Aerotown Add-on module \"", scriptName, "\" loaded OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +53,6 @@
|
||||
<value type="string">c182</value>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>------------------</label>
|
||||
<enabled>false</enabled>
|
||||
</item>
|
||||
<item>
|
||||
<label>Disable tow aircraft</label>
|
||||
<binding>
|
||||
@@ -65,7 +61,18 @@
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>------------------</label>
|
||||
<label>---------------------</label>
|
||||
<enabled>false</enabled>
|
||||
</item>
|
||||
<item>
|
||||
<label>Add thermal</label>
|
||||
<binding>
|
||||
<command>dialog-show</command>
|
||||
<dialog-name>add-thermal</dialog-name>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>---------------------</label>
|
||||
<enabled>false</enabled>
|
||||
</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.0.0</version>
|
||||
<version type="string">1.1.0</version>
|
||||
|
||||
<authors>
|
||||
<author>
|
||||
@@ -81,7 +81,7 @@
|
||||
</home-page>
|
||||
|
||||
<download type="string">
|
||||
https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere
|
||||
https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere/releases/latest
|
||||
</download>
|
||||
|
||||
<support type="string">
|
||||
@@ -97,6 +97,7 @@
|
||||
<tag type="string">aerotow</tag>
|
||||
<tag type="string">AI</tag>
|
||||
<tag type="string">scenario</tag>
|
||||
<tag type="string">thermal</tag>
|
||||
</tags>
|
||||
</addon>
|
||||
</PropertyList>
|
||||
|
||||
93
aerotow.nas
93
aerotow.nas
@@ -30,6 +30,26 @@ 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_listeners = [];
|
||||
|
||||
#
|
||||
# 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_listeners, setlistener(addon.node.getPath() ~ "/addon-devel/ai-model", func () {
|
||||
startAerotow();
|
||||
}));
|
||||
}
|
||||
|
||||
#
|
||||
# Uninitialize thermal module
|
||||
#
|
||||
var uninit = func () {
|
||||
foreach (var listener; g_listeners) {
|
||||
removelistener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Main function to prepare AI scenario and run it.
|
||||
@@ -152,7 +172,7 @@ var isScenarioAdded = func () {
|
||||
# Return name of selected aircraft. Possible values: "Cub", "DR400", "c182".
|
||||
#
|
||||
var getSelectedAircraft = func () {
|
||||
return getprop("/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/ai-model") or "Cub";
|
||||
return getprop(addon.node.getPath() ~ "/addon-devel/ai-model") or "Cub";
|
||||
}
|
||||
|
||||
#
|
||||
@@ -177,13 +197,19 @@ var generateFlightPlanXml = func () {
|
||||
}
|
||||
|
||||
var airport = airportinfo(icao);
|
||||
|
||||
if (!contains(airport.runways, runwayName)) {
|
||||
messages.displayError("The " ~ icao ~" airport does not have runway " ~ runwayName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 * M2FT) ~ " ft)."
|
||||
~ "(" ~ math.round(minRwyLength * globals.M2FT) ~ " ft)."
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -229,7 +255,7 @@ var generateFlightPlanXml = func () {
|
||||
addWptGround({"hdgChange": 0, "dist": 10 * perf.rolling}, {"altChange": 0, "ktas": perf.speed});
|
||||
|
||||
# Takeof
|
||||
addWptAir({"hdgChange": 0, "dist": 100 * perf.rolling}, {"altChange": 3, "ktas": perf.speed * 1.05});
|
||||
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});
|
||||
|
||||
# Circle around airport
|
||||
@@ -298,7 +324,7 @@ var getAircraftPerformance = func () {
|
||||
"rolling": 2,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (aiModel == "c182") {
|
||||
return {
|
||||
"vs": 295, # ft per 1000 m
|
||||
@@ -318,27 +344,28 @@ var getAircraftPerformance = func () {
|
||||
#
|
||||
# Initialize AI aircraft variable
|
||||
#
|
||||
# isGliderPos - Pass 1 for set AI aircraft's coordinates as glider position, 0 set coordinates as runway threshold.
|
||||
# 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
|
||||
g_coord = isGliderPos
|
||||
? gliderCoord
|
||||
: geo.Coord.new().set_latlon(runway.lat, runway.lon);
|
||||
|
||||
# Set airplane heading as runway heading
|
||||
g_heading = runway.heading;
|
||||
|
||||
# Set airplane altitude as airport elevation
|
||||
g_altitude = gliderCoord.alt() * M2FT;
|
||||
# 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
|
||||
# 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.
|
||||
@@ -358,7 +385,7 @@ var getMinRunwayLength = func () {
|
||||
if (aiModel == "DR400") {
|
||||
return 470;
|
||||
}
|
||||
|
||||
|
||||
if (aiModel == "c182") {
|
||||
return 508;
|
||||
}
|
||||
@@ -397,25 +424,28 @@ var addWptAir = func (coordOffset, performance) {
|
||||
# sec - Number of seconds for wait
|
||||
#
|
||||
var addWptWait = func (sec) {
|
||||
wrireWpt("WAIT", {"hdgChange": nil, "dist": nil}, {"altChange": nil, "ktas": nil}, nil, sec);
|
||||
wrireWpt("WAIT", {}, {}, nil, sec);
|
||||
}
|
||||
|
||||
#
|
||||
# Add "END" waypoint
|
||||
#
|
||||
var addWptEnd = func () {
|
||||
wrireWpt("END", {"hdgChange": nil, "dist": nil}, {"altChange": nil, "ktas": nil});
|
||||
wrireWpt("END", {}, {});
|
||||
}
|
||||
|
||||
#
|
||||
# Write waipoint to flight plan file
|
||||
# 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 - Allowe value: "ground or "air". The "ground" means that AI plane is on the ground, "air" - in air
|
||||
# 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 (
|
||||
@@ -425,8 +455,8 @@ var wrireWpt = func (
|
||||
groundAir = nil,
|
||||
sec = nil
|
||||
) {
|
||||
var localCoord = nil;
|
||||
if (coordOffset.hdgChange != nil and coordOffset.dist != 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;
|
||||
@@ -437,24 +467,29 @@ var wrireWpt = func (
|
||||
}
|
||||
|
||||
g_coord.apply_course_distance(g_heading, coordOffset.dist);
|
||||
localCoord = g_coord;
|
||||
coord = g_coord;
|
||||
}
|
||||
|
||||
var localAlt = nil;
|
||||
if (performance.altChange != nil) {
|
||||
g_altitude = g_altitude + performance.altChange;
|
||||
localAlt = g_altitude;
|
||||
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,
|
||||
localCoord,
|
||||
localAlt,
|
||||
performance.ktas,
|
||||
groundAir,
|
||||
sec
|
||||
);
|
||||
var data = getWptString(name, coord, alt, ktas, groundAir, sec);
|
||||
|
||||
io.write(g_fpFileHandler, data);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<hrule/>
|
||||
|
||||
<text>
|
||||
<label>Aerotow Everywhere version 1.0.0 - 13th August 2022</label>
|
||||
<label>Aerotow Everywhere version 1.1.0 - 16th August 2022</label>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
|
||||
214
gui/dialogs/add-thermal.xml
Normal file
214
gui/dialogs/add-thermal.xml
Normal file
@@ -0,0 +1,214 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
#
|
||||
# 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)
|
||||
#
|
||||
-->
|
||||
|
||||
<!-- This file requires FlightGear version 2018.2 or newer -->
|
||||
|
||||
<PropertyList>
|
||||
<name>add-thermal</name>
|
||||
<resizable>false</resizable>
|
||||
<layout>vbox</layout>
|
||||
<default-padding>3</default-padding>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
|
||||
<empty>
|
||||
<stretch>1</stretch>
|
||||
</empty>
|
||||
|
||||
<text>
|
||||
<label>Add thermal</label>
|
||||
</text>
|
||||
|
||||
<empty>
|
||||
<stretch>1</stretch>
|
||||
</empty>
|
||||
|
||||
<button>
|
||||
<pref-width>16</pref-width>
|
||||
<pref-height>16</pref-height>
|
||||
<legend></legend>
|
||||
<keynum>27</keynum>
|
||||
<border>2</border>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<text>
|
||||
<halign>left</halign>
|
||||
<label> This option allows the thermal to be placed at the distance </label>
|
||||
</text>
|
||||
<text>
|
||||
<halign>left</halign>
|
||||
<label> designated below in front of the glider along with other parameters. </label>
|
||||
</text>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<group>
|
||||
<layout>table</layout>
|
||||
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label> Distance in front of the glider</label>
|
||||
</text>
|
||||
<input>
|
||||
<row>0</row>
|
||||
<col>1</col>
|
||||
<label>m</label>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/distance-m</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>0</row>
|
||||
<col>2</col>
|
||||
<label>Dummy label</label>
|
||||
<halign>left</halign>
|
||||
<format>(%.2f nm)</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/distance-nm</property>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Strength</label>
|
||||
</text>
|
||||
<input>
|
||||
<row>1</row>
|
||||
<col>1</col>
|
||||
<label>ft/s</label>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/strength-fps</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>1</row>
|
||||
<col>2</col>
|
||||
<label>Dummy label</label>
|
||||
<halign>left</halign>
|
||||
<format> (%.2f m/s)</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/strength-mps</property>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Diameter</label>
|
||||
</text>
|
||||
<input>
|
||||
<row>2</row>
|
||||
<col>1</col>
|
||||
<label>ft</label>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/diameter-ft</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>2</row>
|
||||
<col>2</col>
|
||||
<label>Dummy label</label>
|
||||
<halign>left</halign>
|
||||
<format>(%.f m)</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/diameter-m</property>
|
||||
</text>
|
||||
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>0</col>
|
||||
<halign>right</halign>
|
||||
<label>Height MSL</label>
|
||||
</text>
|
||||
<input>
|
||||
<row>3</row>
|
||||
<col>1</col>
|
||||
<label>ft</label>
|
||||
<halign>left</halign>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/height-msl</property>
|
||||
<live>true</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
</input>
|
||||
<text>
|
||||
<row>3</row>
|
||||
<col>2</col>
|
||||
<label>Dummy label</label>
|
||||
<halign>left</halign>
|
||||
<format>(%.f m)</format>
|
||||
<live>true</live>
|
||||
<property>/addons/by-id/org.flightgear.addons.Aerotow/addon-devel/add-thermal/height-msl-m</property>
|
||||
</text>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<group>
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
|
||||
<layout>hbox</layout>
|
||||
|
||||
<button>
|
||||
<legend>Add thermal</legend>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script><![CDATA[thermal.add();]]></script>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Cancel</legend>
|
||||
<equal>true</equal>
|
||||
<default>true</default>
|
||||
<key>Esc</key>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<empty>
|
||||
<stretch>true</stretch>
|
||||
</empty>
|
||||
</group>
|
||||
|
||||
</PropertyList>
|
||||
14
messages.nas
14
messages.nas
@@ -34,20 +34,20 @@ var displayError = func (message) {
|
||||
# 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 screan
|
||||
# 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];
|
||||
|
||||
if (type == "error") {
|
||||
window.fg = [1.0, 0.0, 0.0, 1];
|
||||
}
|
||||
else {
|
||||
window.fg = [0.0, 1.0, 0.0, 1];
|
||||
}
|
||||
window.fg = type == "error"
|
||||
? [1.0, 0.0, 0.0, 1]
|
||||
: [0.0, 1.0, 0.0, 1];
|
||||
|
||||
window.align = "center";
|
||||
window.write(message);
|
||||
|
||||
97
thermal.nas
Normal file
97
thermal.nas
Normal file
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# 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");
|
||||
|
||||
#
|
||||
# Variables
|
||||
#
|
||||
var g_thermalListeners = [];
|
||||
|
||||
#
|
||||
# Initialize thermal module
|
||||
#
|
||||
var init = func () {
|
||||
append(g_thermalListeners, setlistener(addon.node.getPath() ~ "/addon-devel/add-thermal/distance-m", func () {
|
||||
setprop(
|
||||
addon.node.getPath() ~ "/addon-devel/add-thermal/distance-nm",
|
||||
getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/distance-m") * globals.M2NM
|
||||
);
|
||||
}));
|
||||
|
||||
append(g_thermalListeners, setlistener(addon.node.getPath() ~ "/addon-devel/add-thermal/strength-fps", func () {
|
||||
setprop(
|
||||
addon.node.getPath() ~ "/addon-devel/add-thermal/strength-mps",
|
||||
getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/strength-fps") * globals.FPS2KT * globals.KT2MPS
|
||||
);
|
||||
}));
|
||||
|
||||
append(g_thermalListeners, setlistener(addon.node.getPath() ~ "/addon-devel/add-thermal/diameter-ft", func () {
|
||||
setprop(
|
||||
addon.node.getPath() ~ "/addon-devel/add-thermal/diameter-m",
|
||||
getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/diameter-ft") * globals.FT2M
|
||||
);
|
||||
}));
|
||||
|
||||
append(g_thermalListeners, setlistener(addon.node.getPath() ~ "/addon-devel/add-thermal/height-msl", func () {
|
||||
setprop(
|
||||
addon.node.getPath() ~ "/addon-devel/add-thermal/height-msl-m",
|
||||
getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/height-msl") * 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.getPath() ~ "/addon-devel/add-thermal/distance-m") or 300;
|
||||
|
||||
var position = geo.aircraft_position();
|
||||
position.apply_course_distance(heading, distance);
|
||||
|
||||
# Get random layer from 1 do 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.getPath() ~ "/addon-devel/add-thermal/strength-fps") or 16.0,
|
||||
"diameter-ft": getprop(addon.node.getPath() ~ "/addon-devel/add-thermal/diameter-ft") or 4000,
|
||||
"height-msl": getprop(addon.node.getPath() ~ "/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;
|
||||
}
|
||||
Reference in New Issue
Block a user