Stuart BUCHANAN:
Nasal/XML based tutorial system
This commit is contained in:
parent
3ee8f31785
commit
ceeb9503f8
5 changed files with 1454 additions and 1 deletions
800
Aircraft/c172p/c172-tutorial.xml
Normal file
800
Aircraft/c172p/c172-tutorial.xml
Normal file
|
@ -0,0 +1,800 @@
|
|||
<PropertyList>
|
||||
<tutorial>
|
||||
<name>Take-off</name>
|
||||
<description>
|
||||
This tutorial starts from Half-Moon Bay (KHAF), a small airport on the coast near San Francisco, in clear, still, weather.
|
||||
|
||||
Take-off and climb out at 600fpm (about 70 kts), continuing on the runway heading. Level off at 1000ft to complete the tutorial.
|
||||
</description>
|
||||
<audio-dir>Aircraft/c172p/tutorial</audio-dir>
|
||||
<timeofday>morning</timeofday>
|
||||
<presets>
|
||||
<airport-id>KHAF</airport-id>
|
||||
<on-ground>1</on-ground>
|
||||
<runway>12</runway>
|
||||
<altitude-ft>-9999</altitude-ft>
|
||||
<latitude-deg>-9999</latitude-deg>
|
||||
<longitude-deg>-9999</longitude-deg>
|
||||
<heading-deg>0</heading-deg>
|
||||
<airspeed-kt>0</airspeed-kt>
|
||||
<glideslope-deg>0</glideslope-deg>
|
||||
<offset-azimuth>0</offset-azimuth>
|
||||
<offset-distance>0</offset-distance>
|
||||
</presets>
|
||||
<init>
|
||||
<set>
|
||||
<prop>/controls/gear/brake-parking</prop>
|
||||
<val>1</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/flight/flaps</prop>
|
||||
<val>0.0</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<val>3</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/throttle</prop>
|
||||
<val>0.5</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/engines/engine/running</prop>
|
||||
<val>true</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/instrumentation/adf/frequencies/selected-khz</prop>
|
||||
<val>340</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/environment/weather-scenario</prop>
|
||||
<val>Fair weather</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/flight/elevator-trim</prop>
|
||||
<val>0.17</val>
|
||||
</set>
|
||||
</init>
|
||||
<step>
|
||||
<instruction>Release the parking brakes, smoothly apply full power and start your take-off.</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/throttle</prop>
|
||||
<lt>0.95</lt>
|
||||
<msg>Apply full throttle for take-off.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/controls/gear/brake-parking</prop>
|
||||
<eq>1</eq>
|
||||
<msg>Release the parking brake using Shift B</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<gt>5.0</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Stay on the center-line of the runway using the rudder.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<gt>45.0</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Rotate at 65 knots and climb straight out on heading 120 at 600 feet per minute.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/vertical-speed-indicator/indicated-speed-fpm</prop>
|
||||
<gt>300</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Continue your climb on heading 120 at 600 feet per minute to 1000 feet.</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/vertical-speed-indicator/indicated-speed-fpm</prop>
|
||||
<lt>400</lt>
|
||||
<msg>Your climb angle is too shallow - raise the nose slightly to increase your climb rate.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/vertical-speed-indicator/indicated-speed-fpm</prop>
|
||||
<gt>900</gt>
|
||||
<msg>Your climb angle is too great - lower the nose slightly to decrease your climb rate.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<lt>110</lt>
|
||||
<msg>You are heading too far left, Turn right slightly to heading 120.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<gt>130</gt>
|
||||
<msg>You are heading too far right, Turn left slightly to heading 120.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<lt>69.0</lt>
|
||||
<msg>You are in danger of being "behind the power curve". Drop the nose to speed up to 70 knots.</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<gt>1000</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Level off at between 1000 and 1100 feet.</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<lt>1000</lt>
|
||||
<msg>You are too low</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<gt>1100</gt>
|
||||
<msg>You are too high</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<gt>1000</gt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<lt>1100</lt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>instrumentation/vertical-speed-indicator/indicated-speed-fpm</prop>
|
||||
<lt>150</lt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>instrumentation/vertical-speed-indicator/indicated-speed-fpm</prop>
|
||||
<gt>-150</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<endtext>Congratulations, you've successfully taken off. Now try landing!</endtext>
|
||||
</tutorial>
|
||||
<tutorial>
|
||||
<name>Straight-in Landing</name>
|
||||
<description>Landings are not optional...
|
||||
|
||||
Land the aircraft from an altitude of 1000ft on runway 30 at KHAF on a calm morning.
|
||||
|
||||
Unlike normal flight, when landing you use power alone to control descent rate. The runway should stay in the same position throughout the descent - about a third of the distance above the glare-shield.
|
||||
|
||||
KHAF has a VASI to the left of runway 30, a series of lights designed to help you judge your approach. Simply put, the VASI will display red lights if you are descending too fast (indicating that you need to increase power), and white lights if you are too high (indicating that you need to decrease power). If you are descending at the right angle, you will see 3 red lights and 3 white.
|
||||
|
||||
You start the tutorial cruising at around 1000ft and 90kts. To begin your descent, you will need to reduce power and drop one notch flaps. You are aiming to approach at 80kts and 300fpm. As you get closer, you will need to reduce power further and drop more flaps, aiming to touch down "on the numbers" at 65kts.
|
||||
</description>
|
||||
<timeofday>afternoon</timeofday>
|
||||
<presets>
|
||||
<airport-id>KHAF</airport-id>
|
||||
<on-ground>0</on-ground>
|
||||
<runway>30</runway>
|
||||
<altitude-ft>1000</altitude-ft>
|
||||
<latitude-deg>-9999</latitude-deg>
|
||||
<longitude-deg>-9999</longitude-deg>
|
||||
<heading-deg>300.0</heading-deg>
|
||||
<airspeed-kt>90.0</airspeed-kt>
|
||||
<glideslope-deg>0</glideslope-deg>
|
||||
<offset-azimuth>0</offset-azimuth>
|
||||
<offset-distance>3</offset-distance>
|
||||
<ndb-id>0</ndb-id>
|
||||
<vor-id>0</vor-id>
|
||||
<fix>0</fix>
|
||||
</presets>
|
||||
<init>
|
||||
<set>
|
||||
<prop>/controls/flight/flaps</prop>
|
||||
<val>0.0</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<val>3</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/throttle</prop>
|
||||
<val>0.5</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/engines/engine/running</prop>
|
||||
<val>true</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/instrumentation/adf/frequencies/selected-khz</prop>
|
||||
<val>340</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/environment/weather-scenario</prop>
|
||||
<val>Fair weather</val>
|
||||
</set>
|
||||
</init>
|
||||
<step>
|
||||
<instruction>Reduce throttle to 50%. Apply one degree of flaps.
|
||||
Start your descent, staying lined up with the runway a hands-width above the glare-shield.
|
||||
You are aiming to descend at around 500 feet per minute and 80 knots.
|
||||
</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/airspeed-indicator/indicated-speed-kt</prop>
|
||||
<gt>80.0</gt>
|
||||
<msg>You are going too fast. Reduce power and pitch up to reduce your airspeed to 80 knots.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/throttle</prop>
|
||||
<gt>0.5</gt>
|
||||
<msg>Reduce throttle to 50%</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/position/latitude-deg</prop>
|
||||
<gt>37.48</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Let down the second step of flaps and use the VASI to check your descent rate.
|
||||
White - too high. Red - too low, Red and white - just right.
|
||||
Use throttle to control your descent rate rather than attitude.
|
||||
</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/position/latitude-deg</prop>
|
||||
<gt>37.495</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Let down your final set of flaps and continue to follow the VASI glideslope. Stay lined up with the middle of the runway.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/position/latitude-deg</prop>
|
||||
<gt>37.506</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
|
||||
<step>
|
||||
<instruction>Reduce power to idle. When you are about 10 feet above the ground, smoothly bring the nose up so it is level with the horizon.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/position/altitude-agl-ft</prop>
|
||||
<lt>7.0</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Keep tracking straight down the runway and let the aircraft land on the main gear.
|
||||
</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<lt>45.0</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Lower the nose wheel, continue to track down the runway and gently brake to a stop.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<lt>5.0</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<endtext>Congratulations on a successful landing.</endtext>
|
||||
</tutorial>
|
||||
|
||||
<tutorial>
|
||||
<name>Flying the Pattern</name>
|
||||
<description>
|
||||
Take-off from KHAF, fly a left-hand traffic pattern and land.
|
||||
|
||||
The traffic pattern is used as a method to stop aircraft crashing into each other when near an airport.
|
||||
|
||||
The pattern is a rectangular flight path with the runway forming one of the long sides.
|
||||
|
||||
It consists of the following sections:
|
||||
- Upwind. Once we've taken off, we flying straight ahead on the runway heading to 500ft.
|
||||
- Crosswind. Once at 500ft, we turn left 90 degrees and continue climbing until we get to 1000ft.
|
||||
- Downwind. When we reach 1000ft, we turn left again so we are parallel to the runway, reduce power and fly at 100kts. When we are "abeam the numbers" (opposite the number on the runway we intend to land on), we reduce power to 50%, drop one notch of flat and descend 100ft at 90kts.
|
||||
- Base We now make another left-hand turn, add another notch of flaps, and let speed continue to dissipate.
|
||||
- Final At the correct moment (based on visual queues),we make a final left turn to line up with the runway. We drop our final lot of flaps, descend and land.
|
||||
|
||||
As before, we'll be flying at KHAF, on runway 12, on a clear day.
|
||||
|
||||
Runway 12 does not have a VASI. Instead you will need to judge whether you are too low or too high by the view of the runway. If the runway is going away from you, you will land short. If the runway is coming towards you, you will over-shoot. Remember, you should aim to have the runway about a third of the way above the glare-shield, and adjust your rate of descent using power.
|
||||
</description>
|
||||
<audio-dir>Aircraft/c172p/tutorial</audio-dir>
|
||||
<timeofday>morning</timeofday>
|
||||
<presets>
|
||||
<airport-id>KHAF</airport-id>
|
||||
<on-ground>1</on-ground>
|
||||
<runway>12</runway>
|
||||
<altitude-ft>-9999</altitude-ft>
|
||||
<latitude-deg>-9999</latitude-deg>
|
||||
<longitude-deg>-9999</longitude-deg>
|
||||
<heading-deg>0</heading-deg>
|
||||
<airspeed-kt>0</airspeed-kt>
|
||||
<glideslope-deg>0</glideslope-deg>
|
||||
<offset-azimuth>0</offset-azimuth>
|
||||
<offset-distance>0</offset-distance>
|
||||
</presets>
|
||||
<init>
|
||||
<set>
|
||||
<prop>/controls/flight/flaps</prop>
|
||||
<val>0.0</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/gear/brake-parking</prop>
|
||||
<val>1</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<val>3</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/throttle</prop>
|
||||
<val>0.5</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/engines/engine/running</prop>
|
||||
<val>true</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/instrumentation/adf/frequencies/selected-khz</prop>
|
||||
<val>340</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/environment/weather-scenario</prop>
|
||||
<val>Fair weather</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/flight/elevator-trim</prop>
|
||||
<val>0.17</val>
|
||||
</set>
|
||||
</init>
|
||||
<step>
|
||||
<instruction>Release the parking brakes, smoothly apply full power and start your take-off.</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/throttle</prop>
|
||||
<lt>0.95</lt>
|
||||
<msg>Apply full throttle for take-off</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/controls/gear/brake-parking</prop>
|
||||
<eq>1</eq>
|
||||
<msg>Release the parking brake using Shift B</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<gt>5.0</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Stay on the center-line of the runway using the rudder.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<gt>45.0</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Take-off at 65 knots and climb straight out on heading 120 at 600 feet per minute (about 70 knots).</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/vertical-speed-indicator/indicated-speed-fpm</prop>
|
||||
<gt>300</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Continue your climb on heading 120 at 600 feet per minute to 500 feet.</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/vertical-speed-indicator/indicated-speed-fpm</prop>
|
||||
<lt>400</lt>
|
||||
<msg>Your climb angle is too shallow - raise the nose slightly to increase your climb rate.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/vertical-speed-indicator/indicated-speed-fpm</prop>
|
||||
<gt>900</gt>
|
||||
<msg>Your climb angle is too great - lower the nose slightly to decrease your climb rate.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<lt>110</lt>
|
||||
<msg>You are heading too far left, Turn right slightly to heading 120.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<gt>130</gt>
|
||||
<msg>You are heading too far right, Turn left slightly to heading 120.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<lt>69</lt>
|
||||
<msg>You are in danger of being "behind the power curve". Drop the nose to speed up to 70 knots.</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<gt>500</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Make a left turn to crosswind (heading 030) and continue climbing to 1000 feet.</instruction>
|
||||
<error>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<gt>900</gt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<gt>020</gt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<lt>040</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Level off at 1000 feet and make another left turn to downwind (heading 300).</instruction>
|
||||
<error>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<gt>950</gt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<gt>290</gt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<lt>310</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Reduce power to 60%, and trim for level flight. Continue flying parallel to the runway.</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<lt>290</lt>
|
||||
<msg>You are heading too far left, Turn right slightly to heading 300.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<gt>310</gt>
|
||||
<msg>You are heading too far right, Turn left slightly to heading 300.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<gt>1100</gt>
|
||||
<msg>You are too high. Pattern altitude is 1000 feet.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<lt>900</lt>
|
||||
<msg>You are too low. Pattern altitude is 1000 feet.</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/position/latitude-deg</prop>
|
||||
<gt>37.528</gt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>You are now opposite the end of the runway. Reduce power to 50%, set flaps to 10.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/controls/flight/flaps</prop>
|
||||
<gt>0.3</gt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/throttle</prop>
|
||||
<lt>0.55</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Let the aircraft descend and slow. Continue flying a heading of 300.</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<lt>290</lt>
|
||||
<msg>You are heading too far left, Turn right slightly to heading 300.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<gt>310</gt>
|
||||
<msg>You are heading too far right, Turn left slightly to heading 300.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<gt>1100</gt>
|
||||
<msg>You are too high. You should be descending to around 850 feet.</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/altimeter/indicated-altitude-ft</prop>
|
||||
<lt>900</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Turn left to base (heading 210), and set flaps to 20. Continue to descend at around 500 feet per minute.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/position/longitude-deg</prop>
|
||||
<lt>-122.520</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Turn onto final (heading 120), and line up with the runway.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<lt>140</lt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/heading-indicator/indicated-heading-deg</prop>
|
||||
<gt>100</gt>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/position/latitude-deg</prop>
|
||||
<lt>37.545</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Set flaps to 30. Control your descent rate using the throttle.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/position/latitude-deg</prop>
|
||||
<lt>37.525</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Reduce power to idle. When you are about 10 feet above the ground, smoothly bring the nose up so it is just above the horizon.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/position/altitude-agl-ft</prop>
|
||||
<lt>10.0</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Keep tracking straight down the runway using the rudder and let the aircraft land on the main gear.
|
||||
</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<lt>45.0</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Lower the nose wheel, continue to track down the runway and gently brake to a stop.</instruction>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/velocities/airspeed-kt</prop>
|
||||
<lt>5.0</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<endtext>Congratulations on successfully following the pattern.
|
||||
If you want to practice more landings, you can take off again immediately.
|
||||
Don't forget to lift your flaps though! This is known as a Touch and Go.
|
||||
</endtext>
|
||||
</tutorial>
|
||||
|
||||
<tutorial>
|
||||
<name>Engine Failure</name>
|
||||
<description>Engine failure is very rare in flight simulators, but a real risk in real life.
|
||||
|
||||
|
||||
The tutorial starts with you cruising at 3500ft over the hills of San Francisco. A total engine failure will occur, with no possibility of restart - black smoke, chunks of metal pushed out of the cowling.
|
||||
|
||||
Glide the aircraft to the nearest airport and make a successful emergency landing. You must manage your altitude and fly a normal pattern from "abeam the numbers". Don't deploy flaps until you have the runway "made".
|
||||
|
||||
The Cessna 172 glides at a ratio of 10:1 at a best glide speed of 90kts IAS. The glide ratio assumes no flaps and a "windmilling" propeller. If you fly any faster or slower, or have flaps deployed, the glide rate will be worse.
|
||||
|
||||
Both KSFO (San Francisco International) and KHAF (Half Moon Bay) are within glide distance. In real life, you would opt for the larger of the two airports, but for an extra challenge, try to land at KHAF.
|
||||
</description>
|
||||
<timeofday>evening</timeofday>
|
||||
<presets>
|
||||
<airport-id>KHAF</airport-id>
|
||||
<on-ground>0</on-ground>
|
||||
<runway>30</runway>
|
||||
<altitude-ft>3500</altitude-ft>
|
||||
<latitude-deg>37.5</latitude-deg>
|
||||
<longitude-deg>-122.4</longitude-deg>
|
||||
<heading-deg>010.0</heading-deg>
|
||||
<airspeed-kt>105.0</airspeed-kt>
|
||||
<glideslope-deg>0</glideslope-deg>
|
||||
<offset-azimuth>0</offset-azimuth>
|
||||
<offset-distance>0</offset-distance>
|
||||
<ndb-id>0</ndb-id>
|
||||
<vor-id>0</vor-id>
|
||||
<fix>0</fix>
|
||||
</presets>
|
||||
<init>
|
||||
<set>
|
||||
<prop>/controls/flight/flaps</prop>
|
||||
<val>0.0</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<val>3</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/throttle</prop>
|
||||
<val>0.5</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/engines/engine/running</prop>
|
||||
<val>true</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/instrumentation/adf/frequencies/selected-khz</prop>
|
||||
<val>340</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/environment/weather-scenario</prop>
|
||||
<val>Fair weather</val>
|
||||
</set>
|
||||
</init>
|
||||
<step>
|
||||
<instruction>We're happily cruising along, enjoying an evenings flight.</instruction>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Engine failure! Engine failure! Trim for a best glide speed of 90 knots.</instruction>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/faults/left-magneto-serviceable</prop>
|
||||
<val>false</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/faults/right-magneto-serviceable</prop>
|
||||
<val>false</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/faults/spark-plugs-serviceable</prop>
|
||||
<val>false</val>
|
||||
</set>
|
||||
<set>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<val>0</val>
|
||||
</set>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Now look around and choose an airport, or emergency landing site. Stay at 90 knots.</instruction>
|
||||
<set>
|
||||
<prop>/engines/engine/running</prop>
|
||||
<val>false</val>
|
||||
</set>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/airspeed-indicator/indicated-speed-kt</prop>
|
||||
<lt>88</lt>
|
||||
<msg>You are too slow. You need to fly at 90 knots IAS for maximum glide.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/airspeed-indicator/indicated-speed-kt</prop>
|
||||
<gt>92</gt>
|
||||
<msg>You are too fast. You need to fly at 90 knots IAS for maximum glide.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<gt>0</gt>
|
||||
<msg>Engine failure is currently simulated by switching off the magnetos. Please switch them off again to continue the tutorial.</msg>
|
||||
</check>
|
||||
</error>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Start maneuvering towards your chosen emergency landing spot, keeping your speed at 90 knots IAS.</instruction>
|
||||
<set>
|
||||
<prop>/engines/engine/running</prop>
|
||||
<val>false</val>
|
||||
</set>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/airspeed-indicator/indicated-speed-kt</prop>
|
||||
<lt>88</lt>
|
||||
<msg>You are too slow. You need to fly at 90 knots IAS for maximum glide.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/airspeed-indicator/indicated-speed-kt</prop>
|
||||
<gt>92</gt>
|
||||
<msg>You are too fast. You need to fly at 90 knots IAS for maximum glide.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<gt>0</gt>
|
||||
<msg>Engine failure is currently simulated by switching off the magnetos. Please switch them off again to continue the tutorial.</msg>
|
||||
</check>
|
||||
</error>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>At this point we would radio an emergency/mayday. Remember - Aviate, Navigate, Communicate - in that order.</instruction>
|
||||
<set>
|
||||
<prop>/engines/engine/running</prop>
|
||||
<val>false</val>
|
||||
</set>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/instrumentation/airspeed-indicator/indicated-speed-kt</prop>
|
||||
<lt>88</lt>
|
||||
<msg>You are too slow. You need to fly at 90 knots IAS for maximum glide.</msg>
|
||||
|
||||
</check>
|
||||
<check>
|
||||
<prop>/instrumentation/airspeed-indicator/indicated-speed-kt</prop>
|
||||
<gt>92</gt>
|
||||
<msg>You are too fast. You need to fly at 90 knots IAS for maximum glide.</msg>
|
||||
</check>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<gt>0</gt>
|
||||
<msg>Engine failure is currently simulated by switching off the magnetos. Please switch them off again to continue the tutorial.</msg>
|
||||
</check>
|
||||
</error>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Continue flying towards your emergency landing spot. If we had the spare capacity, we might attempt to restart the engine at this point.</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<gt>0</gt>
|
||||
<msg>Engine failure is currently simulated by switching off the magnetos. Please switch them off again to continue the tutorial.</msg>
|
||||
</check>
|
||||
</error>
|
||||
</step>
|
||||
<step>
|
||||
<instruction>Stay a 90 knots for maximum glide. Only use flaps once the runway is "made".</instruction>
|
||||
<error>
|
||||
<check>
|
||||
<prop>/controls/engines/engine/magnetos</prop>
|
||||
<gt>0</gt>
|
||||
<msg>Engine failure is currently simulated by switching off the magnetos. Please switch them off again to continue the tutorial.</msg>
|
||||
</check>
|
||||
</error>
|
||||
<exit>
|
||||
<check>
|
||||
<prop>/instrumentation/airspeed-indicator/indicated-speed-kt</prop>
|
||||
<lt>10</lt>
|
||||
</check>
|
||||
</exit>
|
||||
</step>
|
||||
<endtext>Assuming the aircraft is upright and you landed on a runway, you walked away from a total engine failure - well done!</endtext>
|
||||
</tutorial>
|
||||
</PropertyList>
|
|
@ -22,6 +22,17 @@ Started October 23 2001 by John Check, fgpanels@rockfish.net
|
|||
<path archive="y">Aircraft/c172p/Models/c172p.xml</path>
|
||||
</model>
|
||||
|
||||
<help>
|
||||
<title>Cessna 172P</title>
|
||||
<line>Vx 59 KIAS</line>
|
||||
<line>Vy 73 KIAS</line>
|
||||
<line>Vne 160 KIAS</line>
|
||||
<line>________________________Procedures_________________________</line>
|
||||
<line>Takeoff: no flaps, full throttle, rotate at 55 KIAS</line>
|
||||
<line>Climbout: no flaps, full throttle, 80 KIAS</line>
|
||||
<line>Cruise: Throttle 65%, Mixture rich of peak,</line>
|
||||
<line>Landing: full flaps, 65 KIAS</line>
|
||||
</help>
|
||||
<!-- hide the 2D panel -->
|
||||
|
||||
<panel>
|
||||
|
@ -59,6 +70,8 @@ Started October 23 2001 by John Check, fgpanels@rockfish.net
|
|||
<title>Cessna 172P</title>
|
||||
</help>
|
||||
|
||||
<tutorial include="c172-tutorial.xml"></tutorial>
|
||||
|
||||
</sim>
|
||||
|
||||
<!-- trim for level cruise -->
|
||||
|
|
171
Nasal/gui.nas
171
Nasal/gui.nas
|
@ -154,6 +154,177 @@ nextStyle = func {
|
|||
|
||||
dialog = {};
|
||||
|
||||
##
|
||||
# Dynamically generate a tutorial dialog allowing the user to select a tutorial.
|
||||
#
|
||||
showSelTutDialog = func {
|
||||
name = "selectTutorial";
|
||||
title = "Tutorial Wizard - Step 1/2";
|
||||
|
||||
#
|
||||
# Immediately stop any tutorials that are running.
|
||||
tutorial.stopTutorial();
|
||||
|
||||
#
|
||||
# General Dialog Structure
|
||||
#
|
||||
dialog[name] = Widget.new();
|
||||
dialog[name].set("name", name);
|
||||
dialog[name].set("layout", "vbox");
|
||||
|
||||
header = dialog[name].addChild("text");
|
||||
header.set("label", title);
|
||||
|
||||
dialog[name].addChild("hrule").set("pref-height", 1);
|
||||
|
||||
if (props.globals.getNode("/sim/tutorial") == nil) {
|
||||
msg = dialog[name].addChild("text");
|
||||
msg.set("label", "No tutorials available for this aircraft");
|
||||
cancel = dialog[name].addChild("button");
|
||||
cancel.set("legend", "Cancel");
|
||||
cancel.prop().getNode("binding[0]/command", 1).setValue("dialog-close");
|
||||
fgcommand("dialog-new", dialog[name].prop());
|
||||
showDialog(name);
|
||||
return;
|
||||
}
|
||||
|
||||
ltextarea = dialog[name].addChild("text");
|
||||
ltextarea.set("label", "Please choose a tutorial from the list below");
|
||||
ltextarea.set("halign", "center");
|
||||
|
||||
contentArea = dialog[name].addChild("group");
|
||||
contentArea.set("layout", "hbox");
|
||||
|
||||
label = contentArea.addChild("text");
|
||||
label.set("label", "Tutorial");
|
||||
label.set("halign", "right");
|
||||
|
||||
combo = contentArea.addChild("combo");
|
||||
combo.set("pref-width", "200");
|
||||
combo.set("property", "/sim/tutorial/current-tutorial");
|
||||
|
||||
# Get a list of all tutorials
|
||||
ltutorials = props.globals.getNode("/sim/tutorial").getChildren("tutorial");
|
||||
for(i=0; i<size(ltutorials); i+=1)
|
||||
{
|
||||
c = ltutorials[i];
|
||||
if (c.getChild("name") != nil)
|
||||
{
|
||||
lname = c.getChild("name").getValue();
|
||||
lentry = combo.addChild("value");
|
||||
lentry.prop().setValue(lname);
|
||||
}
|
||||
}
|
||||
|
||||
buttonBar = dialog[name].addChild("group");
|
||||
buttonBar.set("layout", "hbox");
|
||||
buttonBar.set("default-padding", 10);
|
||||
|
||||
lcancel = buttonBar.addChild("button");
|
||||
lcancel.set("legend", "Cancel");
|
||||
lcancel.prop().getNode("binding[0]/command", 1).setValue("dialog-close");
|
||||
|
||||
lnext = buttonBar.addChild("button");
|
||||
lnext.set("legend", "Next");
|
||||
lnext.set("keynum", 27);
|
||||
lnext.prop().getNode("binding[0]/command", 1).setValue("dialog-apply");
|
||||
lnext.prop().getNode("binding[1]/command", 1).setValue("nasal");
|
||||
lnext.prop().getNode("binding[1]/script", 1).setValue("gui.showTutorialDialog()");
|
||||
lnext.prop().getNode("binding[2]/command", 1).setValue("dialog-close");
|
||||
|
||||
# All done: pop it up
|
||||
fgcommand("dialog-new", dialog[name].prop());
|
||||
showDialog(name);
|
||||
}
|
||||
|
||||
|
||||
showTutorialDialog = func {
|
||||
name = "displayTutorial";
|
||||
title = "Tutorial Wizard - Step 2/2";
|
||||
|
||||
#
|
||||
# General Dialog Structure
|
||||
#
|
||||
dialog[name] = Widget.new();
|
||||
dialog[name].set("name", name);
|
||||
dialog[name].set("layout", "vbox");
|
||||
|
||||
header = dialog[name].addChild("text");
|
||||
header.set("label", title);
|
||||
|
||||
dialog[name].addChild("hrule").set("pref-height", 1);
|
||||
|
||||
# Get the tutorial description
|
||||
ltutorial = getprop("/sim/tutorial/current-tutorial");
|
||||
|
||||
if (ltutorial == nil) { ltutorial = "<undefined>"; }
|
||||
|
||||
lfound = 0;
|
||||
ldescription = "No description available for this tutorial.";
|
||||
|
||||
foreach(c; props.globals.getNode("/sim/tutorial").getChildren("tutorial"))
|
||||
{
|
||||
if (c.getChild("name").getValue() == ltutorial)
|
||||
{
|
||||
lfound = 1;
|
||||
if (c.getChild("description") != nil)
|
||||
{
|
||||
ldescription = c.getChild("description") .getValue();
|
||||
setprop("/sim/tutorial/description", ldescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lfound == 0) {
|
||||
msg = dialog[name].addChild("text");
|
||||
msg.set("label", "Unable to find tutorial " ~ ltutorial);
|
||||
cancel = dialog[name].addChild("button");
|
||||
cancel.set("legend", "Cancel");
|
||||
cancel.prop().getNode("binding[0]/command", 1).setValue("dialog-close");
|
||||
fgcommand("dialog-new", dialog[name].prop());
|
||||
showDialog(name);
|
||||
return;
|
||||
}
|
||||
|
||||
contentArea = dialog[name].addChild("group");
|
||||
contentArea.set("layout", "hbox");
|
||||
|
||||
label = contentArea.addChild("text");
|
||||
label.set("label", "Tutorial: " ~ ltutorial);
|
||||
label.set("halign", "left");
|
||||
|
||||
textarea = dialog[name].addChild("textbox");
|
||||
textarea.set("pref-width", "600");
|
||||
textarea.set("pref-height", "400");
|
||||
textarea.set("slider", "12");
|
||||
textarea.set("live", "true");
|
||||
textarea.set("wrap", "true");
|
||||
textarea.set("editable", "false");
|
||||
textarea.set("valign", "top");
|
||||
textarea.set("halign", "left");
|
||||
textarea.set("property", "/sim/tutorial/description");
|
||||
|
||||
buttonBar = dialog[name].addChild("group");
|
||||
buttonBar.set("layout", "hbox");
|
||||
buttonBar.set("default-padding", 10);
|
||||
|
||||
lcancel = buttonBar.addChild("button");
|
||||
lcancel.set("legend", "Cancel");
|
||||
lcancel.prop().getNode("binding[0]/command", 1).setValue("dialog-close");
|
||||
|
||||
lnext = buttonBar.addChild("button");
|
||||
lnext.set("legend", "Start Tutorial");
|
||||
lnext.set("keynum", 27);
|
||||
lnext.prop().getNode("binding[0]/command", 1).setValue("dialog-apply");
|
||||
lnext.prop().getNode("binding[1]/command", 1).setValue("nasal");
|
||||
lnext.prop().getNode("binding[1]/script", 1).setValue("tutorial.startTutorial()");
|
||||
lnext.prop().getNode("binding[2]/command", 1).setValue("dialog-close");
|
||||
|
||||
# All done: pop it up
|
||||
fgcommand("dialog-new", dialog[name].prop());
|
||||
showDialog(name);
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# Dynamically generates a weight & fuel configuration dialog specific to
|
||||
|
|
455
Nasal/tutorial.nas
Normal file
455
Nasal/tutorial.nas
Normal file
|
@ -0,0 +1,455 @@
|
|||
#
|
||||
# Functions for XML-based tutorials
|
||||
#
|
||||
|
||||
#
|
||||
# Each tutorial consists of the following XML sections
|
||||
#
|
||||
# <name> - Tutorial Name
|
||||
# <description> - description
|
||||
# <audio-dir> - Optional: Directory to pick up audio instructions from.
|
||||
# Relative to FG_ROOT
|
||||
# <timeofday> - Optional: Time of day setting for tutorial:
|
||||
# dawn/morning/noon/afternoon etc.
|
||||
# <presets> - Optional: set of presets to used for start position.
|
||||
# See commit-presets and gui/dialog/location-*.xml for details.
|
||||
# <airport-id>
|
||||
# <on-ground>
|
||||
# <runway>
|
||||
# <altitude-ft>
|
||||
# <latitude-deg>
|
||||
# <longitude-deg>
|
||||
# <heading-deg>
|
||||
# <airspeed-kt>
|
||||
# <init> - Optional: Initialization section consist of one or more
|
||||
# set nodes:
|
||||
# <set>
|
||||
# <prop> - Property to set
|
||||
# <val> - value
|
||||
# <step> - Tutorial step - a segment of the tutorial, consisting of
|
||||
# the following:
|
||||
# <instruction> - Text instruction displayed when the tutorial reaches
|
||||
# this step, and when neither the exit nor any error
|
||||
# criteria have been fulfilled
|
||||
# <instruction-voice> - Optional: wav filename to play when displaying
|
||||
# instruction
|
||||
# <error> - Error conditions, causing error messages to be displayed.
|
||||
# The tutorial doesn't advance while any error conditions are
|
||||
# fulfilled. Consists of one or more check nodes:
|
||||
# <check>
|
||||
# <prop> - property to check.
|
||||
# <lt> - less than value. One of <lt>, <gt>, <eq> must be defined
|
||||
# <gt> - greater than value. One of <lt>, <gt>, <eq> must be defined
|
||||
# <eq> - equal value. One of <lt>, <gt>, <eq> must be defined
|
||||
# <msg> - Error message to display if error criteria fulfilled.
|
||||
# <msg-voice> - Optional: wav filename to play when error condition fulfilled.
|
||||
# <exit> - Exit criteria causing tutorial to progress to next step. Consists of
|
||||
# one or more check nodes. All check nodes must be fulfilled at the same
|
||||
# time of the tutorial to progress to the next step.
|
||||
# <prop> - property to check.
|
||||
# <lt> - less than value. One of <lt>, <gt>, <eq> must be defined
|
||||
# <gt> - greater than value. One of <lt>, <gt>, <eq> must be defined
|
||||
# <eq> - equal value. One of <lt>, <gt>, <eq> must be defined
|
||||
#
|
||||
# <endtext> - Optional: Text to display when the tutorial exits the last step.
|
||||
# <endtext-voice> - Optional: wav filename to play when the tutorial exits the last step
|
||||
|
||||
#
|
||||
# GLOBAL VARIABLES
|
||||
#
|
||||
|
||||
# Time between tutorial steps.
|
||||
STEP_TIME = 5;
|
||||
|
||||
# We also have a gap between the fulfillment of a step and the start
|
||||
# of the next step. This can be much shorter.
|
||||
STEP_EXIT = 1;
|
||||
|
||||
m_currentStep = 0;
|
||||
m_errors = 0;
|
||||
m_tutorial = 0;
|
||||
m_firstEntry = 1;
|
||||
m_stop = 0;
|
||||
m_audioDir = "";
|
||||
m_lastmsg = "";
|
||||
m_lastmsgcount = 0;
|
||||
|
||||
#
|
||||
# startTutorial()
|
||||
#
|
||||
# Start a tutorial defined within xiProp
|
||||
#
|
||||
startTutorial = func {
|
||||
m_currentStep = 0;
|
||||
m_errors = 0;
|
||||
|
||||
if (getprop("/sim/tutorial/current-tutorial") == nil)
|
||||
{
|
||||
# Tutorial not defined - exit
|
||||
screen.log.write("No tutorial selected");
|
||||
return;
|
||||
}
|
||||
|
||||
ltutorial = getprop("/sim/tutorial/current-tutorial");
|
||||
lfound = 0;
|
||||
|
||||
foreach(c; props.globals.getNode("/sim/tutorial").getChildren("tutorial"))
|
||||
{
|
||||
if (c.getChild("name").getValue() == ltutorial)
|
||||
{
|
||||
m_tutorial = c;
|
||||
lfound = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lfound == 0)
|
||||
{
|
||||
# Unable to find tutorial
|
||||
screen.log.write("Unable to find tutorial : " ~ ltutorial);
|
||||
return;
|
||||
}
|
||||
|
||||
screen.log.write("Loading tutorial: " ~ ltutorial ~ " ...");
|
||||
|
||||
# If defined, get the audio directory
|
||||
if (m_tutorial.getChild("audio-dir") != nil)
|
||||
{
|
||||
fg_root = getprop("/sim/fg-root");
|
||||
m_audioDir = sprintf("%s/%s/", fg_root, m_tutorial.getChild("audio-dir").getValue());
|
||||
}
|
||||
|
||||
# Set the time of day, if present
|
||||
timeofday = m_tutorial.getChild("timeofday");
|
||||
if (timeofday != nil)
|
||||
{
|
||||
fgcommand("timeofday", props.Node.new("timeofday", timeofday.getValue()));
|
||||
}
|
||||
|
||||
# First, set any presets that might be present
|
||||
presets = m_tutorial.getChild("presets");
|
||||
|
||||
if ((presets != nil) and (presets.getChildren() != nil))
|
||||
{
|
||||
children = presets.getChildren();
|
||||
foreach(c; children)
|
||||
{
|
||||
setprop("/sim/presets/" ~ c.getName(), c.getValue());
|
||||
}
|
||||
|
||||
# Apply the presets
|
||||
fgcommand("presets-commit", props.Node.new());
|
||||
|
||||
# Set the various engines to be running
|
||||
if (getprop("/sim/presets/on-ground"))
|
||||
{
|
||||
var eng = props.globals.getNode("/controls/engines");
|
||||
if (eng != nil)
|
||||
{
|
||||
foreach (c; eng.getChildren("engine"))
|
||||
{
|
||||
c.getNode("magnetos", 1).setIntValue(3);
|
||||
c.getNode("throttle", 1).setDoubleValue(0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Run through any initialization nodes
|
||||
inits = m_tutorial.getChild("init");
|
||||
if ((inits != nil) and (inits.getChildren("set") != nil))
|
||||
{
|
||||
children = inits.getChildren("set");
|
||||
foreach(c; children)
|
||||
{
|
||||
setVal(c);
|
||||
}
|
||||
}
|
||||
|
||||
# Pick up any weather conditions/scenarios set
|
||||
setprop("/environment/rebuild_layers", getprop("/environment/rebuild_layers")+1);
|
||||
|
||||
# Set the timer to start the first tutorial step
|
||||
settimer(stepTutorial, STEP_TIME);
|
||||
}
|
||||
|
||||
#
|
||||
# stopTutorial
|
||||
#
|
||||
# Stops the current tutorial by setting m_stop.
|
||||
#
|
||||
|
||||
stopTutorial = func
|
||||
{
|
||||
m_stop = 1;
|
||||
settimer(resetStop, STEP_TIME);
|
||||
}
|
||||
|
||||
#
|
||||
# resetStop
|
||||
#
|
||||
# Reset the stop value, having given any running tutorials the
|
||||
# chance to stop. Also reset the various state variables incase
|
||||
# they have been changed.
|
||||
resetStop = func
|
||||
{
|
||||
m_stop = 0;
|
||||
m_currentStep = 0;
|
||||
m_firstEntry = 1;
|
||||
m_errors = 0;
|
||||
}
|
||||
|
||||
#
|
||||
# stepTutorial
|
||||
#
|
||||
# This function does the actual work. It is executed every 5 seconds.
|
||||
#
|
||||
# Each iteration it:
|
||||
# - Gets the current step node from the tutorial
|
||||
# - If this is the first time the step is entered, it displays the instruction message
|
||||
# - Otherwise, it
|
||||
# - Checks if the exit conditions have been met. If so, it increments the step counter.
|
||||
# - Checks for any error conditions, in which case it displays a message to the screen and
|
||||
# increments an error counter
|
||||
# - Otherwise display the instructions for the step.
|
||||
# - Sets the timer for 5 seconds again.
|
||||
#
|
||||
stepTutorial = func {
|
||||
|
||||
lerror = 0;
|
||||
ltts = nil;
|
||||
lsnd = nil;
|
||||
lmessage = nil;
|
||||
|
||||
if (m_stop == 1)
|
||||
{
|
||||
# If we've been told to stop, just do so.
|
||||
return;
|
||||
}
|
||||
|
||||
# If we've reached the end of the tutorial, simply indicate and exit
|
||||
if (m_currentStep == size(m_tutorial.getChildren("step")))
|
||||
{
|
||||
# End of tutorial.
|
||||
|
||||
lfinished = "Tutorial finished.";
|
||||
|
||||
if (m_tutorial.getChild("endtext") != nil)
|
||||
{
|
||||
lfinished = m_tutorial.getChild("endtext").getValue();
|
||||
}
|
||||
|
||||
if (m_tutorial.getChild("endtext-voice") != nil)
|
||||
{
|
||||
lsnd = m_tutorial.getChild("endtext-voice").getValue();
|
||||
}
|
||||
|
||||
say(lfinished ~ "\nDeviations: " ~ m_errors, lsnd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lstep = m_tutorial.getChildren("step")[m_currentStep];
|
||||
|
||||
lmessage = "Tutorial step " ~ m_currentStep;
|
||||
|
||||
|
||||
if (lstep.getChild("instruction") != nil)
|
||||
{
|
||||
# By default, display the current instruction
|
||||
lmessage = lstep.getChild("instruction").getValue();
|
||||
}
|
||||
|
||||
if (m_firstEntry == 1)
|
||||
{
|
||||
# If this is the first time we've encountered this step :
|
||||
# - Set any values required
|
||||
# - Display any messages
|
||||
# - Play any instructions.
|
||||
#
|
||||
# We then do not go through the error or exit processing, giving the user
|
||||
# time to react to the instructions.
|
||||
|
||||
if (lstep.getChild("instruction-voice") != nil)
|
||||
{
|
||||
lsnd = lstep.getChild("instruction-voice").getValue();
|
||||
}
|
||||
|
||||
say(lmessage, lsnd);
|
||||
|
||||
# Set any properties
|
||||
foreach (c; lstep.getChildren("set"))
|
||||
{
|
||||
setVal(c);
|
||||
}
|
||||
|
||||
m_firstEntry = 0;
|
||||
settimer(stepTutorial, STEP_TIME);
|
||||
return;
|
||||
}
|
||||
|
||||
# Check for error conditions
|
||||
if ((lstep.getChild("error") != nil) and
|
||||
(lstep.getChild("error").getChildren("check") != nil))
|
||||
{
|
||||
#print("Checking errors");
|
||||
|
||||
foreach(c; lstep.getChild("error").getChildren("check"))
|
||||
{
|
||||
if (checkVal(c))
|
||||
{
|
||||
# and error condition was fulfilled - set the error message
|
||||
lerror = 1;
|
||||
m_errors = m_errors + 1;
|
||||
|
||||
if (c.getChild("msg") != nil)
|
||||
{
|
||||
lmessage = c.getChild("msg").getValue();
|
||||
|
||||
if (c.getChild("msg-voice") != nil)
|
||||
{
|
||||
lsnd = c.getChild("msg-voice").getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Check for exit condition, but only if we didn't hit any errors
|
||||
if (lerror == 0)
|
||||
{
|
||||
if ((lstep.getChild("exit") == nil) or
|
||||
(lstep.getChild("exit").getChildren("check") == nil))
|
||||
{
|
||||
m_currentStep = m_currentStep + 1;
|
||||
m_firstEntry = 1;
|
||||
settimer(stepTutorial, STEP_EXIT);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
lexit = 1;
|
||||
foreach(c; lstep.getChild("exit").getChildren("check"))
|
||||
{
|
||||
if (checkVal(c) == 0)
|
||||
{
|
||||
lexit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (lexit == 1)
|
||||
{
|
||||
# Passed all exit steps
|
||||
m_currentStep = m_currentStep + 1;
|
||||
m_firstEntry = 1;
|
||||
settimer(stepTutorial, STEP_EXIT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Display the resulting message and wait to go around again.
|
||||
say(lmessage, lsnd);
|
||||
|
||||
settimer(stepTutorial, STEP_TIME);
|
||||
}
|
||||
|
||||
# Set a value in the property tree based on an entry of the form
|
||||
# <set>
|
||||
# <prop>/foo/bar</prop>
|
||||
# <val>woof</val>
|
||||
# </set>
|
||||
#
|
||||
setVal = func {
|
||||
node = arg[0];
|
||||
|
||||
if (node.getName("set"))
|
||||
{
|
||||
lprop = node.getChild("prop").getValue();
|
||||
lval = node.getChild("val").getValue();
|
||||
|
||||
if ((lprop != nil) and (lval != nil))
|
||||
{
|
||||
setprop(lprop, lval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check a value in the property tree based on an entry of the form
|
||||
#
|
||||
# <check>
|
||||
# <prop>/foo/bar</prop>
|
||||
# <_operator_>woof</_operator_>
|
||||
#
|
||||
# where _operator_ may be one of "eq", "lt", "gt"
|
||||
#
|
||||
checkVal = func {
|
||||
node = arg[0];
|
||||
|
||||
if (node.getName("check"))
|
||||
{
|
||||
lprop = node.getChild("prop").getValue();
|
||||
|
||||
if (getprop(lprop) == nil)
|
||||
{
|
||||
# This is probably an error
|
||||
print("Undefined property: " ~ lprop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((node.getChild("eq") != nil) and
|
||||
(getprop(lprop) == node.getChild("eq").getValue()))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((node.getChild("lt") != nil) and
|
||||
(getprop(lprop) < node.getChild("lt").getValue() ))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((node.getChild("gt") != nil) and
|
||||
(getprop(lprop) > node.getChild("gt").getValue() ))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Output the message and optional sound recording.
|
||||
#
|
||||
say = func(msg, snd=nil)
|
||||
{
|
||||
# We only display the same message after 20 seconds. This stops
|
||||
# any voice instructions or festival TTS repeating too quickly.
|
||||
if ((msg != m_lastmsg) or (m_lastmsgcount == 3))
|
||||
{
|
||||
# We're either re-displaying after 20 seconds, or
|
||||
# displaying a new message.
|
||||
if (snd == nil)
|
||||
{
|
||||
# Simply set to the co-pilot channel. TTS is picked up automatically.
|
||||
setprop("/sim/messages/copilot", msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
# Play the audio, and write directly to the screen-logger to avoid
|
||||
# any tts being sent to festival.
|
||||
lprop = { path : m_audioDir, file : snd };
|
||||
fgcommand("play-audio-message", props.Node.new(lprop) );
|
||||
screen.log.write(msg, 1, 1, 1);
|
||||
}
|
||||
|
||||
m_lastmsg = msg;
|
||||
m_lastmsgcount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lastmsgcount = m_lastmsgcount + 1;
|
||||
}
|
||||
}
|
|
@ -455,7 +455,21 @@
|
|||
</binding>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<label>Start Tutorial</label>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>gui.showSelTutDialog()</script>
|
||||
</binding>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<label>End Tutorial</label>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>tutorial.stopTutorial()</script>
|
||||
</binding>
|
||||
</item>
|
||||
</menu>
|
||||
|
||||
|
||||
</PropertyList>
|
||||
|
|
Loading…
Reference in a new issue