1== README.tutorials =========================================================== 2 3FlightGear offers a flexible tutorial system, entirely written in the Nasal 4language. Tutorials can be started and stopped from the "Help" menu. They are 5defined in XML files. Each of them has to be loaded into /sim/tutorials/ under 6a separate tutorial[n]/ branch: 7 8 <sim> 9 <tutorials> 10 <tutorial include="Tutorials/take-off.xml"/> 11 <tutorial include="Tutorials/landing.xml"/> 12 </tutorial> 13 </sim> 14 15Alternatively, all tutorials can be defined in one file, with <tutorial> tags 16around each tutorial. This is then included like so: 17 18 <sim> 19 <tutorials include="foo-tutorials.xml"/> 20 </sim> 21 22Finally, tutorials are automatically generated from any valid checklists 23on startup. See README.checklists for details. 24 25 26== TUTORIAL STRUCTURE ========================================================= 27 28 29A tutorial has this structure, where some of the elements are described 30in detail below: 31 32 33<tutorial> 34 <name>...</name> mandatory; short identifier, also shown in the 35 tutorial selection dialog 36 <description>...</description> mandatory; longer description for the dialog 37 <audio-dir>...</audio-dir> optional; defines where to load sound samples 38 <timeofday>noon</timeofday> optional; defines daytime; any of "dawn", 39 "morning", "noon", "afternoon", 40 "evening", "dusk", "midnight", "real" 41 42 <step-time> optional; period between each step being executed. 43 Default 5 44 <exit-time> optional; period between exit/abort conditions being 45 checked. Default 1 46 47 <nasal> 48 ... optional; initial Nasal code; see below 49 </nasal> 50 51 <models> 52 ... optional; scenery objects; see below 53 </models> 54 55 <targets> 56 ... optional; targets; see below 57 </targets> 58 59 <presets> 60 ... optional; initial simulator state; see below 61 </presets> 62 63 64 <init> optional; initial settings; see below 65 <set> 66 ... optional; property settings; allowed multiple 67 </set> times 68 <view> 69 ... optional; view settings 70 </view> 71 <marker> 72 ... optional; marker coordinates 73 </marker> 74 <nasal> 75 ... optional; Nasal code 76 </nasal> 77 </init> 78 79 <step> mandatory; well, not really, but if there's not 80 at least one <step>, then the whole tutorial 81 won't do anything; see below for details 82 83 <message>...</message> optional; message to be displayed/spoken when 84 <step> is entered; allowed multiple times, in 85 which case one is chosen at random 86 87 <message-param> optional; allowed up to 4 times. 88 <property>...</property> property to substitute into the <message> string 89 </message-param> using sprintf() formatting. E.g. %d, %.2f 90 91 <audio>...</audio> optional; file name of *.wav sample to be played; 92 may be used multiple times (random) 93 <set> 94 ... optional; allowed several times 95 </set> 96 <view> 97 ... optional 98 </view> 99 <marker> 100 ... optional 101 </marker> 102 <nasal> 103 ... optional; Nasal code that is executed when the 104 </nasal> step is entered 105 106 <wait>10</wait> optional; wait period after initial messages etc. 107 108 <error> optional; allowed several times 109 <message>..</message> optional; text displayed/spoken 110 <audio>...</audio> optional; name of *.wav sample to be played 111 112 <condition> 113 ... optional, but one should be there to make sense 114 </condition> see $FG_ROOT/Docs/README.conditions 115 116 <nasal> 117 ... optional; Nasal code that is executed when the 118 </nasal> error condition was fulfilled 119 </error> 120 121 <exit> optional; defines when to leave this <step> 122 <condition> see $FG_ROOT/Docs/README.conditions 123 ... 124 </condition> 125 126 <nasal> 127 ... optional; Nasal code that is executed when the 128 </nasal> exit condition was met 129 </exit> 130 </step> 131 132 133 <end> optional; final settings & actions; see below 134 <message>...</message> optional; multiple times (random) 135 <audio>...</audio> optional; multiple times (random) 136 <set> 137 ... optional 138 </set> 139 <view> 140 ... optional 141 </view> 142 <nasal> 143 ... optional 144 </nasal> 145 </end> 146</tutorial> 147 148 149 150After the tutorial has finished initialization, it goes through all <steps>. 151For each it outputs the <message> or <audio>, optionally sets a <marker> and/or 152a <view>, then it checks all <error>s and, if an <error><condition> is fulfilled, 153outputs the respective <error><message>. If none of the <error>s occurred, then 154it checks if the <exit><condition> is true, and if so, it jumps to the next 155<step>. Otherwise the current <step> is endlessly repeated. Finally, after all 156<step>s were processed, the <end> group is executed. 157 158 159 160 161 162 163 164 165== ELEMENTS =================================================================== 166 167 168-- <nasal> -------------------------------------------------------------------- 169 170Embedded Nasal is supported on the top level, in <init> in each <step>, in a 171<step>'s <error> and <exit>, and in <end>. All Nasal runs in a separate 172namespace __tutorial, so it's possible to define a function in the <init>'s 173Nasal block, and to use this function in other blocks without prefix. The 174namespace is preloaded with some functions: 175 176 177 next([n=1]) ... to switch n <step>s forward 178 previous([n=1]) ... to switch n <step>s backwards 179 180 say(what [, who="copilot [, delay=0]]) 181 ... says 'what' with voice 'copilot' after 'delay' seconds 182 183 184A Nasal group looks like this: 185 186 <nasal> 187 <script> 188 say("Hi, I'm the pilot!", "pilot"); 189 </script> 190 <module>__tutorial</module> optional; preset with __tutorial 191 </nasal> 192 193 194 195 196 197-- <models> ------------------------------------------------------------------- 198 199This loads models into the scenery. It can be used to place, for example, 200a helicopter landing pad at an airport where normally none is, so that the 201tutorial can train landing. The layout is the following, with <path> being 202relative to $FG_ROOT: 203 204 <models> 205 <model> 206 <path>Models/Airport/supacat_winch.ac</path> mandatory 207 <longitude-deg>-122.4950109</longitude-deg> mandatory 208 <latitude-deg>37.51403798</latitude-deg> mandatory 209 <elevation-ft>51</elevation-ft> mandatory 210 <heading-deg>2.488888979</heading-deg> optional (default: 0) 211 <pitch-deg>0</pitch-deg> optional (default: 0) 212 <roll-deg>0</roll-deg> optional (default: 0) 213 </model> 214 215 <model> 216 ... another model 217 </model> 218 </models> 219 220The models are only removed before a new tutorial is loaded. Otherwise they 221remain in the scenery for the whole FlightGear session. They aren't permanently 222added. 223 224 225 226 227 228-- <targets> ------------------------------------------------------------------ 229 230These are simple pairs of longitude/latitude under an arbitrary name (here 231"hospital" and "helipad"): 232 233 <targets> 234 <hospital> 235 <longitude-deg>-122.4950109</longitude-deg> mandatory 236 <latitude-deg>37.51403798</latitude-deg> mandatory 237 </hospital> 238 239 <helipad> 240 ... 241 </helipad> 242 </targets> 243 244 245The tutorial system will for each calculate how the user's aircraft is positioned 246relative to the respective target, and offer the information in this structure: 247 248 <sim> 249 <tutorials> 250 <targets> 251 <hospital> 252 <direction-deg>12.345</direction-deg> 253 <heading-deg>33.333</heading-deg> 254 <distance-m>12404.932</distance-m> 255 <eta-min>39.2358</eta-min> 256 </hospital> 257 258 <helipad> 259 ... 260 </helipad> 261 </targets> 262 </tutorials> 263 </sim> 264 265 266Where: 267 <direction-deg> is an angle between the aircraft's velocity vector and the 268 azimuth to the target. 0 means that the aircraft is moving 269 right towards the target. 10 means that the target is slightly 270 to the right, -90 means that it's exactly left, and -180 or 271 179.9999 that it's right behind. 272 273 <heading-deg> is the absolute heading that the aircraft would currently 274 have to fly with in a straight line to reach the target 275 276 <distance-m> is the distance in meters 277 278 <eta-min> is the "Estimated Time of Arrival" given the aircraft's 279 current speed towards the target. Positive times mean that 280 the aircraft is getting nearer to the target and can arrive 281 there in this time given the current speed. It will, of course, 282 only arrive there, if <direction-deg> is zero. A negative 283 number means that the aircraft moves away, or in other words: 284 that in this number of minutes it will be away twice as far. 285 286 287 288 289 290-- <presets> ------------------------------------------------------------------ 291 292These set the initial simulator state. All properties are optional. 293The last three entries are to define the position relative to the 294airport/runway or the longitude/latitude. 295 296 <presets> 297 <airport-id>KHAF</airport-id> 298 <on-ground>1</on-ground> 299 <runway>12</runway> 300 <!-- 301 <altitude-ft>122.333</altitude-ft> 302 <latitude-deg>37.555</latitude-deg> 303 <longitude-deg>1000</longitude-deg> 304 --> 305 <heading-deg>0</heading-deg> 306 <airspeed-kt>0</airspeed-kt> 307 <glideslope-deg>0</glideslope-deg> 308 <offset-azimuth>0</offset-azimuth> 309 <offset-distance>0</offset-distance> 310 </presets> 311 312 313 314 315 316-- <set> ---------------------------------------------------------------------- 317 318<set> groups can be used in <init>, <step>, and <end>. They set a <property> 319to a given <value> or to the value that a second <property> points to. They 320can also reset values that were only temporarily changed for the duration 321of the tutorial. This is desirable for properties that are saved to the 322aircraft config file or to ~/.fgfs/autosave.xml. 323 324 <set> 325 <property>/foo/bar</property> set /foo/bar to 123 326 <value>123</value> 327 </set> 328 329 <set> 330 <property>/foo/bar</property> set /foo/bar to value of /test 331 <property>/test</property> 332 </set> 333 334 335 336 337 338-- <view> --------------------------------------------------------------------- 339 340These groups can be used in <init>, <step>, and <end>. They smoothly move the 341view to a new view position/direction. All parameters are optional. If, for 342example, only <field-of-view> is set, then the view will only zoom in -- the 343direction and position will remain the same. This feature is meant for cockpit 344tutorials, where the pilot's view is directed to some switch or instrument. 345view-number can be used to switch between different views, i.e. to tower-view, 346copilot view etc. Default view-number is 0 (captain's view). 347 348 <view> 349 <view-number>0</view-number> 0=captain's view, 1=copilot,... 350 <heading-offset-deg>20</heading-offset-deg> positive is left 351 <pitch-offset-deg>-4</pitch-offset-deg> positive is up 352 <roll-offset-deg>0</roll-offset-deg> positive is roll right 353 <x-offset-m>0.2</x-offset-m> positive is move right 354 <y-offset-m>0.2</y-offset-m> positive is move up 355 <z-offset-m>0.2</z-offset-m> positive is move back 356 <field-of-view>55</field-of-view> default: 55; smaller zooms in 357 </view> bigger zooms out 358 359 360 361 362 363-- <marker> ------------------------------------------------------------------- 364 365These are supported in <init>, <step>, and <end>. They show a magenta colored 366circle at given position (relative to aircraft origin) in given size. See the 367last section for how to conveniently find the proper coordinates. 368 369 <marker> 370 <x-m>1.3</x-m> positive is back 371 <y-m>0.3</y-m> positive is to the right 372 <z-m>0.1</z-m> positive is up 373 <scale>1.3</scale> optional; default: 1 374 </marker> 375 376 377For this to work, the aircraft model needs to include the tutorial marker 378model in its animation xml file: 379 380 <PropertyList> 381 <path>lightning-f1a.ac</path> 382 383 <model> 384 <path>Aircraft/Generic/marker.xml</path> 385 </model> 386 387 ... 388 </PropertyList> 389 390 391 392 393 394-- <message>/<audio> ---------------------------------------------------------- 395 396Groups <step> and <end> can have one or more <message> entries, and one or 397more <audio> entries. If more are used of a kind, then the tutorial chooses 398one at random. If <audio> are available, then the contents are interpreted as 399file name of a *.wav sample, which is appended to the <audio-dir> path defined 400at the <tutorial> top level (default: "") and played by the tutorial system. 401Otherwise the <message> is handed over to the voice system, and synthesized 402to speech by the Festival speech synthesizer (if installed). In either 403case the chosen <message> is displayed on top of the screen. Neither <message> 404nor <audio> are mandatory. 405 406Because one and the same <message> string can be displayed *and* be synthesized, 407which can be problematic in some cases, there is a way to specify parts for 408either display *or* voice synthesizer: "{<display part>|<voice part}". 409Example: 410 411 <message>Press the {No1|number one} button!</message> 412 413Here, "No1" would be displayed on the screen, but "number one" would be 414sent to the speech synthesis system. This can also be used to add 415invisible but audible exclamation marks: "Press the button{|!}" 416 417 418 419 420 421-- <condition> ---------------------------------------------------------------- 422 423These are explained in detail in $FG_ROOT/Docs/README.conditions. Here's just 424one example: 425 426 <condition> 427 <less-than> 428 <property>/foo/bar</property> 429 <value>12</value> 430 </less-than> 431 </condition> 432 433This condition is true when the value of /foo/bar is less than 12, and false 434otherwise. 435 436 437 438 439 440 441 442 443== FINDING MARKER COORDINATES ================================================= 444 445If an aircraft tutorial wants to use the marker, then the aircraft animation 446file needs to include the marker model (see above). If this is done, then one 447can use the "marker-adjust" dialog to find the respective <marker> coordinates. 448Just type this into the "Help->Nasal Console" dialog: 449 450 tutorial.dialog() 451 452 453Or temporarily add a key binding to the *-set.xml file: 454 455 <key n="96"> 456 <name>Backtick</name> 457 <desc>Open marker adjust dialog</desc> 458 <binding> 459 <command>dialog-show</command> 460 <dialog-name>marker-adjust</dialog-name> 461 </binding> 462 </key> 463 464 465The dialog allows to move a red cross around, which has the blinking marker 466circle in the middle. Note that ctrl- and shift-modifiers modulate the slider 467movements. Ctrl makes positioning coarser, and shift finer. The [Reset] 468button moves the marker back to aircraft origin, the [Center] button centers 469the sliders, and the [Dump] button dumps the marker coordinates to the 470terminal, for example: 471 472 <marker> 473 <x-m>1.1425</x-m> 474 <y-m>0.1994</y-m> 475 <z-m>-0.0844</z-m> 476 <scale>2.0489</scale> 477 </marker> 478 479This just needs to be copied to the tutorial XML file. 480