FlyWithLua Quick Reference Manual Fly With Lua En
FlyWithLua_Manual_en
User Manual:
Open the PDF directly: View PDF .
Page Count: 122
Download | |
Open PDF In Browser | View PDF |
FlyWithLua V2.7 NG Reference Manual Carsten Lynker November 21, 2018 Contents Contents Contents 1 Using the Plugin 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 1.10 2 11 What’s needed . . . . . . . . . . . . . . . . . . . . . . Difference between Core, Complete and Source Edition Installation . . . . . . . . . . . . . . . . . . . . . . . How to interact with Lua . . . . . . . . . . . . . . . . Lua variables and DataRefs . . . . . . . . . . . . . . . Writing a first config file . . . . . . . . . . . . . . . . Pre-defined variables . . . . . . . . . . . . . . . . . . Loop Callbacks . . . . . . . . . . . . . . . . . . . . . Menu entries . . . . . . . . . . . . . . . . . . . . . . Menu switches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reference 2.1 11 11 12 12 13 13 14 14 14 15 16 Predefined variables . . . . . . . . . . . . . 2.1.1 LONGITUDE . . . . . . . . . . . 2.1.2 LATITUDE . . . . . . . . . . . . . 2.1.3 PLANE_ICAO . . . . . . . . . . . 2.1.4 PLANE_TAILNUMBER . . . . . . 2.1.5 SCREEN_WIDTH . . . . . . . . . 2.1.6 SCREEN_HIGHT . . . . . . . . . 2.1.7 MOUSE_X . . . . . . . . . . . . . 2.1.8 MOUSE_Y . . . . . . . . . . . . . 2.1.9 XSB_METAR . . . . . . . . . . . 2.1.10 LUA_RUN . . . . . . . . . . . . . 2.1.11 XPLANE_VERSION . . . . . . . 2.1.12 XPLANE_HOSTID . . . . . . . . 2.1.13 SDK_VERSION . . . . . . . . . . 2.1.14 SYSTEM . . . . . . . . . . . . . . 2.1.15 SYSTEM_ARCHITECTURE . . . 2.1.16 XPLANE_LANGUAGE . . . . . . 2.1.17 DIRECTORY_SEPARATOR . . . 2.1.18 SYSTEM_DIRECTORY . . . . . . 2.1.19 SCRIPT_DIRECTORY . . . . . . 2.1.20 INTERNALS_DIRECTORY . . . . 2.1.21 MODULES_DIRECTORY . . . . . 2.1.22 AIRCRAFT_PATH . . . . . . . . . 2.1.23 AIRCRAFT_FILENAME . . . . . 2.1.24 DO_EVERY_FRAME_TIME_SEC Page 2 of 122 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 16 16 16 16 16 16 17 17 17 17 17 17 17 18 18 18 19 19 19 19 19 20 20 20 Contents 2.2 Contents 2.1.25 DO_EVERY_DRAW_TIME_SEC . . . . . . . . . . . . . . . . . . . . 20 2.1.26 DO_SOMETIMES_TIME_SEC . . . . . . . . . . . . . . . . . . . . . 20 2.1.27 DO_OFTEN_TIME_SEC . . . . . . . . . . . . . . . . . . . . . . . . 20 2.1.28 SCRIPTS_LOADING_TIME_SEC . . . . . . . . . . . . . . . . . . . 20 2.1.29 CLOCKS_PER_SEC . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.1.30 LUA_MEMORY_USAGE_KB . . . . . . . . . . . . . . . . . . . . . 20 Lua functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.2.1 DataRef( "variable name", "DataRef name" ) . . . . . . . . . . . . . . 21 2.2.2 DataRef( "variable name", "DataRef name", "readonly" ) . . . . . . . . 21 2.2.3 DataRef( "variable name", "DataRef name", "readonly", index ) . . . . 21 2.2.4 table = dataref_table( "DataRef name") . . . . . . . . . . . . . . . . . 22 2.2.5 define_shared_DataRef("DataRef name", "DataRef type") . . . . . . . 22 2.2.6 table = create_dataref_table( "DataRef name", "DataRef type") . . . . . 22 2.2.7 DataRef name, Index, readonly, DataRef type, DataRef ID = get_DataRef_binding( "variable name" ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.2.8 button( button number ) . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.2.9 last_button( button number ) . . . . . . . . . . . . . . . . . . . . . . . 23 2.2.10 create_switch( button number, DataRef name, index, off value, on value ) 24 2.2.11 create_positive_edge_flip( button number, DataRef name, index, first value, second value ) . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.2.12 create_negative_edge_flip( button number, DataRef name, index, first value, second value ) . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.2.13 create_positive_edge_trigger( button number, DataRef name, index, value ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.2.14 create_negative_edge_trigger( button number, DataRef name, index, value ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.2.15 create_positive_edge_increment( button number, DataRef name, index, stepping, limit, rounding ) . . . . . . . . . . . . . . . . . . . . . . . . 26 2.2.16 create_negative_edge_increment( button number, DataRef name, index, stepping, limit, rounding ) . . . . . . . . . . . . . . . . . . . . . . . . 26 2.2.17 create_positive_edge_decrement( button number, DataRef name, index, stepping, limit, rounding ) . . . . . . . . . . . . . . . . . . . . . . . . 27 2.2.18 create_negative_edge_decrement( button number, DataRef name, index, stepping, limit, rounding ) . . . . . . . . . . . . . . . . . . . . . . . . 27 2.2.19 create_axis_median( axis number, variable name ) . . . . . . . . . . . 27 2.2.20 get( "DataRef name" ) . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.2.21 get( "DataRef name", index ) . . . . . . . . . . . . . . . . . . . . . . . 28 2.2.22 set( "DataRef name", value ) . . . . . . . . . . . . . . . . . . . . . . . 29 2.2.23 set_array( "DataRef name", index, value ) . . . . . . . . . . . . . . . . 29 2.2.24 set_button_assignment( button number, "simulator function") . . . . . 29 2.2.25 set_axis_assignment( axis number, "axis function", "reverse") . . . . . 30 2.2.26 clear_all_axis_assignments() . . . . . . . . . . . . . . . . . . . . . . . 30 2.2.27 clear_all_button_assignments() . . . . . . . . . . . . . . . . . . . . . 30 2.2.28 set_pilots_head( x, y, z, heading, pitch ) . . . . . . . . . . . . . . . . . 30 Page 3 of 122 Contents Contents 2.2.29 2.2.30 2.2.31 2.2.32 2.2.33 2.2.34 2.2.35 2.2.36 2.2.37 2.2.38 2.2.39 2.2.40 2.2.41 2.2.42 2.2.43 2.2.44 2.2.45 2.2.46 2.2.47 2.2.48 2.2.49 2.2.50 2.2.51 2.2.52 2.2.53 2.2.54 2.2.55 2.2.56 2.2.57 2.2.58 2.2.59 2.2.60 2.2.61 2.2.62 2.2.63 2.2.64 2.2.65 2.2.66 2.2.67 2.2.68 2.2.69 2.2.70 x, y, z, heading, pitch = get_pilots_head( ) . . . . . . . . . . . . . . . . command_begin( "simulator function" ) . . . . . . . . . . . . . . . . . command_once( "simulator function" ) . . . . . . . . . . . . . . . . . command_end( "simulator function" ) . . . . . . . . . . . . . . . . . . logMsg( "string" ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . XSBSpeakString( "string" ) . . . . . . . . . . . . . . . . . . . . . . . XPLMSpeakString( "string" ) . . . . . . . . . . . . . . . . . . . . . . print( "string" ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . do_sometimes( "Lua code string" ) . . . . . . . . . . . . . . . . . . . do_often( "Lua code string" ) . . . . . . . . . . . . . . . . . . . . . . do_every_frame( "Lua code string" ) . . . . . . . . . . . . . . . . . . do_every_draw( "Lua code string" ) . . . . . . . . . . . . . . . . . . . do_on_keystroke( "Lua code string" ) . . . . . . . . . . . . . . . . . . do_on_mouse_wheel( "Lua code string" ) . . . . . . . . . . . . . . . . do_on_mouse_click( "Lua code string" ) . . . . . . . . . . . . . . . . do_on_new_metar( "Lua code string" ) . . . . . . . . . . . . . . . . . do_on_new_XSB_text( "Lua code string" ) . . . . . . . . . . . . . . . do_on_exit( "Lua code string" ) . . . . . . . . . . . . . . . . . . . . . draw_string( x, y, "string" ) . . . . . . . . . . . . . . . . . . . . . . . . draw_string( x, y, "string", "color" ) . . . . . . . . . . . . . . . . . . . draw_string( x, y, "string", red, green, blue ) . . . . . . . . . . . . . . . draw_string_Helvetica_10( x, y, "string" ) . . . . . . . . . . . . . . . . draw_string_Helvetica_12( x, y, "string" ) . . . . . . . . . . . . . . . . draw_string_Helvetica_18( x, y, "string" ) . . . . . . . . . . . . . . . . draw_string_Times_Roman_10( x, y, "string" ) . . . . . . . . . . . . . draw_string_Times_Roman_24( x, y, "string" ) . . . . . . . . . . . . . measure_string( "string" ) . . . . . . . . . . . . . . . . . . . . . . . . measure_string( "string", "font name" ) . . . . . . . . . . . . . . . . . hight, width = bubble( x, y, "title", . . . ) . . . . . . . . . . . . . . . . . hight, width = big_bubble( x, y, "title", . . . ) . . . . . . . . . . . . . . . hight, width = huge_bubble( x, y, "title", . . . ) . . . . . . . . . . . . . . add_macro( "macro name", "Lua code string" ) . . . . . . . . . . . . . add_ATC_macro( "macro name", "Lua code string" ) . . . . . . . . . . add_macro( "macro name", "activation code string", "deactivation code string", "default state" ) . . . . . . . . . . . . . . . . . . . . . . . . . create_command( "command name", "command description", "begin code string", "continue code string", "end code string" ) . . . . . . . . . . . table = directory_to_table( "path" ) . . . . . . . . . . . . . . . . . . . place_aircraft_at( "ICAO" ) . . . . . . . . . . . . . . . . . . . . . . . . load_aircraft( "path and full filename" ) . . . . . . . . . . . . . . . . . load_situation( "path and full filename" ) . . . . . . . . . . . . . . . . save_situation( "path and full filename" ) . . . . . . . . . . . . . . . . reload_scenery() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . crash_the_sim() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Page 4 of 122 31 31 31 31 32 32 32 33 33 33 33 33 34 34 35 35 35 36 36 36 37 37 37 37 37 38 38 38 38 38 39 39 39 39 40 40 41 41 41 41 42 42 Contents 3 Modules 3.1 3.2 3.3 4 43 The Radio Module . . . . . . . . . . . . . . . . . . . The XSquawkBox Module . . . . . . . . . . . . . . . 3.2.1 XSBConnect() . . . . . . . . . . . . . . . . . 3.2.2 XSBUserLogin() . . . . . . . . . . . . . . . . 3.2.3 XSBDisconnect() . . . . . . . . . . . . . . . . 3.2.4 XSBShowFlightplan() . . . . . . . . . . . . . 3.2.5 XSBSendFlightplan() . . . . . . . . . . . . . . 3.2.6 frequency = XSBLookupATC( "name string" ) The Bit Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OpenAL sound 4.1 4.2 4.3 5 Contents 46 Buffers, Sounds and Listeners . . . . . . . . . . . . . Loading and defining sounds . . . . . . . . . . . . . . 4.2.1 table position = load_WAV_file( filename ) . . 4.2.2 let_sound_loop( table position, boolean value ) 4.2.3 set_sound_pitch( table position, float value ) . 4.2.4 set_sound_gain( table position, float value ) . . 4.2.5 unload_all_sounds( ) . . . . . . . . . . . . . . 4.2.6 replace_WAV_file(table position, filename ) . . Using the sounds from the sound table . . . . . . . . . 4.3.1 play_sound( table position ) . . . . . . . . . . 4.3.2 stop_sound( table position ) . . . . . . . . . . 4.3.3 pause_sound( table position ) . . . . . . . . . 4.3.4 rewind_sound( table position ) . . . . . . . . . OpenGL graphics 5.1 43 44 44 44 44 45 45 45 45 46 46 46 47 47 47 47 48 49 49 49 49 49 50 Functions of OpenGL . . . . . . . . . . . 5.1.1 glBegin_POINTS() . . . . . . . . 5.1.2 glBegin_LINES() . . . . . . . . . 5.1.3 glBegin_LINE_STRIP() . . . . . 5.1.4 glBegin_LINE_LOOP() . . . . . 5.1.5 glBegin_POLYGON() . . . . . . 5.1.6 glBegin_TRIANGLES() . . . . . 5.1.7 glBegin_TRIANGLE_STRIP() . 5.1.8 glBegin_TRIANGLE_FAN() . . . 5.1.9 glBegin_QUADS() . . . . . . . . 5.1.10 glBegin_QUAD_STRIP() . . . . 5.1.11 glEnd() . . . . . . . . . . . . . . 5.1.12 glVertex2f(x, y) . . . . . . . . . . 5.1.13 glVertex3f(x, y, z) . . . . . . . . 5.1.14 glLineWidth(width) . . . . . . . 5.1.15 glColor3f(red, green, blue) . . . . 5.1.16 glColor4f(red, green, blue, alpha) . . . . . . . . . . . . . . . . . Page 5 of 122 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 50 50 50 50 50 50 50 50 50 50 50 50 50 51 51 51 Contents Contents 5.1.17 glRectf(x1, y1, x2, y2) . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.18 XPLMSetGraphicsState(EnableFog, NumberTexUnits, EnableLighting, EnableAlphaTesting, EnableAlphaBlending, EnableDepthTesting, EnableDepthWriting) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 7 8 51 51 The graphics module 52 6.1 52 52 52 52 52 53 53 53 Functions of graphics module . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1 x_result, y_result = graphics.move_angle( x, y, angle, length ) . . . . . 6.1.2 graphics.draw_line( x1, y1, x2, y2 ) . . . . . . . . . . . . . . . . . . . 6.1.3 graphics.draw_rectangle( x1, y1, x2, y2 ) . . . . . . . . . . . . . . . . . 6.1.4 graphics.draw_triangle( x1, y1, x2, y2, x3, y3 ) . . . . . . . . . . . . . 6.1.5 graphics.set_color( red, green, blue, alpha ) . . . . . . . . . . . . . . . 6.1.6 graphics.set_width( width ) . . . . . . . . . . . . . . . . . . . . . . . . 6.1.7 graphics.draw_angle_line( x, y, angle, length ) . . . . . . . . . . . . . 6.1.8 graphics.draw_angle_arrow( x, y, angle, length, arrowhead’s length, line width ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.9 graphics.draw_circle( x, y, radius, line width ) . . . . . . . . . . . . . . 6.1.10 graphics.draw_filled_circle( x, y, radius ) . . . . . . . . . . . . . . . . 6.1.11 graphics.draw_arc( x, y, start angle, end angle, radius, line width ) . . . 6.1.12 graphics.draw_filled_arc( x, y, start angle, end angle, radius ) . . . . . 6.1.13 graphics.draw_tick_mark( x, y, angle, radius, length, width ) . . . . . . 6.1.14 graphics.draw_outer_tracer( x, y, angle, radius, size ) . . . . . . . . . . 6.1.15 graphics.draw_inner_tracer( x, y, angle, radius, size ) . . . . . . . . . . 53 54 54 54 54 54 54 55 HUD module 56 7.1 7.2 7.3 56 56 59 59 59 59 60 An Interactive HUD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . An Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Functions from HUD module . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.1 HUD.begin_HUD( x, y, width, hight, "name", "always" ) . . . . . . . . 7.3.2 HUD.end_HUD( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.3 HUD.create_element( "name", x, y, width, hight, red, green, blue, alpha ) 7.3.4 HUD.draw_string( x, y, fontsize, "string", red, green, blue, alpha ) . . . 7.3.5 HUD.draw_fstring( x, y, fontsize, "format", "expression", red, green, blue, alpha ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.6 HUD.create_backlight_indicator( x, y, width, hight, "condition", red, green, blue, alpha ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.7 HUD.create_click_action( x, y, width, hight, "action" ) . . . . . . . . . 7.3.8 HUD.create_click_switch( x, y, width, hight, "variable", value, alternative value ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.9 HUD.create_wheel_action( x, y, width, hight, "action" ) . . . . . . . . 60 60 61 61 62 XPLMNavigation 63 8.1 63 63 Functions from XPLMNavigation . . . . . . . . . . . . . . . . . . . . . . . . 8.1.1 nav_reference = XPLMGetFirstNavAid() . . . . . . . . . . . . . . . . Page 6 of 122 Contents Contents 8.1.2 8.1.3 8.1.4 8.1.5 8.1.6 8.1.7 8.1.8 8.1.9 8.1.10 8.1.11 8.1.12 8.1.13 8.1.14 8.1.15 9 next_nav_reference = XPLMGetNextNavAid( inNavAidRef ) . . . . . first_nav_reference = XPLMFindFirstNavAidOfType( inType ) . . . . last_nav_reference = XPLMFindLastNavAidOfType( inType ) . . . . . nav_reference = XPLMFindNavAid( inNameFragment, inIDFragment, inLat, inLon, inFrequency, inType) . . . . . . . . . . . . . . . . . . . outType, outLatitude, outLongitude, outHeight, outFrequency, outHeading, outID, outName = XPLMGetNavAidInfo( inRef ) . . . . . . . . . index_count = XPLMCountFMSEntries() . . . . . . . . . . . . . . . . index = XPLMGetDisplayedFMSEntry() . . . . . . . . . . . . . . . . index = XPLMGetDestinationFMSEntry() . . . . . . . . . . . . . . . . XPLMSetDisplayedFMSEntry( inIndex ) . . . . . . . . . . . . . . . . XPLMSetDestinationFMSEntry( inIndex ) . . . . . . . . . . . . . . . outType, outID, outRef, outAltitude, outLat, outLon = XPLMGetFMSEntryInfo( inIndex ) . . . . . . . . . . . . . . . . . . . . . . . . . . . XPLMSetFMSEntryInfo( inIndex, inRef, inAltitude) . . . . . . . . . . XPLMSetFMSEntryLatLon( inIndex, inLat, inLon, inAltitude) . . . . . XPLMClearFMSEntry( inIndex ) . . . . . . . . . . . . . . . . . . . . 63 63 63 63 63 63 63 63 63 63 63 64 64 64 Access HID devices 65 9.1 65 65 65 65 65 66 66 66 67 67 67 67 68 9.2 9.3 Pre-defined variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.1.1 NUMBER_OF_HID_DEVICES . . . . . . . . . . . . . . . . . . . . . 9.1.2 ALL_HID_DEVICES . . . . . . . . . . . . . . . . . . . . . . . . . . HID related functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2.1 table, number = create_HID_table() . . . . . . . . . . . . . . . . . . . 9.2.2 device = hid_open( vendor_ID, product_ID ) . . . . . . . . . . . . . . 9.2.3 device = hid_open_path( path ) . . . . . . . . . . . . . . . . . . . . . . 9.2.4 hid_close( device ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2.5 hid_write( device, report ID, value, ... ) . . . . . . . . . . . . . . . . . 9.2.6 nov, variable, ... = hid_read_timeout( device, nov wanted, milliseconds ) 9.2.7 nov, variable, ... = hid_read_timeout( device, nov wanted ) . . . . . . . 9.2.8 success = hid_set_nonblocking( device, nonblock ) . . . . . . . . . . . 9.2.9 nobw = hid_send_feature_report( device, report ID, value, ... ) . . . . . 9.2.10 nobw = hid_send_filled_feature_report( device, report ID, nobts, value, ... ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2.11 nobr, report ID, variable, ... = hid_get_feature_report( device, novw ) . The Arcaze USB module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.3.1 device = arcaze.open_first_device() . . . . . . . . . . . . . . . . . . . 9.3.2 A1, A2, A3, ..., B19, B20 = arcaze.read_pins( device ) . . . . . . . . . 9.3.3 ADC1, ADC2, ADC3, ADC4, ADC5, ADC6 = arcaze.read_ADCs( device ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.3.4 E1, E2, E3, ..., E19, E20 = arcaze.read_encoders( device ) . . . . . . . 9.3.5 arcaze.set_all_pins_for_input( device ) . . . . . . . . . . . . . . . . . 9.3.6 arcaze.set_pin_direction( device, pin, direction ) . . . . . . . . . . . . 9.3.7 arcaze.set_pin( device, pin, value ) . . . . . . . . . . . . . . . . . . . . Page 7 of 122 68 69 69 70 70 71 71 71 72 72 Contents Contents 9.3.8 arcaze.init_display( device, address, intensity, scan_limit ) . . . . . . . 9.3.9 arcaze.init_display( device, address ) . . . . . . . . . . . . . . . . . . 9.3.10 arcaze.show( device, address, mask, value_string ) . . . . . . . . . . . 10 Classic and modern mode 74 10.1 Reading classic functions . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.1 variable = XPLMGetDatai( DataRef ) . . . . . . . . . . . . . . . 10.1.2 variable = XPLMGetDataf( DataRef ) . . . . . . . . . . . . . . . 10.1.3 variable = XPLMGetDatad( DataRef ) . . . . . . . . . . . . . . 10.1.4 table = XPLMGetDatavi( DataRef, inIndex, inMax ) . . . . . . . 10.1.5 table = XPLMGetDatavf( DataRef ) . . . . . . . . . . . . . . . . 10.1.6 userdata variable = XPLMFindDataRef( DataRef Name ) . . . . 10.1.7 datatype variable = XPLMGetDataRefTypes( DataRef reference ) 10.2 Writing classic functions . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2.1 XPLMSetDatai( DataRef, variable or value) . . . . . . . . . . . 10.2.2 XPLMSetDataf( DataRef, variable or value) . . . . . . . . . . . 10.2.3 XPLMSetDatad( DataRef, variable or value) . . . . . . . . . . . 10.2.4 XPLMSetDatavi( DataRef, table, inIndex, inMax ) . . . . . . . . 10.2.5 XPLMSetDatavf( DataRef, table, inIndex, inMax ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 The Lua way to access DataRefs 74 74 75 75 75 75 76 76 78 78 78 78 78 79 79 11.1 A magic metatable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1.1 table = dataref_table( DataRef ) . . . . . . . . . . . . . . . . . . . . . 12 Manage your joysticks 12.1 12.2 12.3 12.4 72 73 73 79 80 81 Get a basic configuration . . . . Define your sticks . . . . . . . . Define type specific assignments Lua for cockpit builders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 82 82 83 13 Understanding PLCs 85 14 Basic knowledge about DataRefs 87 14.1 14.2 14.3 14.4 What are DataRefs? . . Find the right DataRefs Accessing DataRefs . . Observe the DataRef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Strings inside of strings . Multiple line strings . . . Global or local variables? Tables are tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Take Lua into consideration 15.1 15.2 15.3 15.4 87 87 88 89 91 16 FlyWithLua 2.7.x Added New Features Page 8 of 122 91 92 92 93 94 Contents Contents 17 Floating Windows 99 17.1 17.2 17.3 17.4 17.5 17.6 17.7 17.8 17.9 17.10 17.11 17.12 17.13 17.14 17.15 17.16 demo_floating_wnd = float_wnd_create(800,450,1,false) . . . . . . . . . . . . 99 float_wnd_set_title(demo_floating_wnd, "floating window Demo") . . . . . . 99 float_wnd_set_position(demo_floating_wnd, 775, 650) . . . . . . . . . . . . . 100 float_wnd_set_ondraw(demo_floating_wnd, "on_draw_floating_window") . . 100 float_wnd_set_onclick(demo_floating_wnd, "on_click_floating_window") . . 100 float_wnd_set_onclose(demo_floating_wnd, "on_close_floating_window") . . 100 xplm_wnd = float_wnd_get_xplm_handle(demo_floating_wnd) . . . . . . . . 100 function on_draw_floating_window(demo_floating_wnd, x3, y3) . . . . . . . 101 function on_click_floating_window(demo_floating_wnd, x3, y3) . . . . . . . 101 function on_close_floating_window(demo_floating_wnd) . . . . . . . . . . . 101 is_popped = float_wnd_is_popped(fwl_wnd) . . . . . . . . . . . . . . . . . . 102 is_visible = float_wnd_get_visible(fwl_wnd) . . . . . . . . . . . . . . . . . . 102 is_front = float_wnd_is_front(fwl_wnd) . . . . . . . . . . . . . . . . . . . . . 102 is_vr = float_wnd_is_vr(fwl_wnd) . . . . . . . . . . . . . . . . . . . . . . . . 102 function float_wnd_bring_to_front(fwl_wnd) . . . . . . . . . . . . . . . . . . 102 function float_wnd_set_resizing_limits(fwl_wnd, minWidth, minHeight, maxWidth, maxHeight) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 17.17 function float_wnd_set_positioning_mode(fwl_wnd, 1, -1) . . . . . . . . . . . 103 17.18 function float_wnd_set_gravity(fwl_wnd, gLeft, gTop, gRight, gBottom) . . . 103 17.19 function float_wnd_set_geometry(fwl_wnd, nLeft, nTop, nRight, nBottom) . . 104 17.20 function float_wnd_set_geometry(fwl_wnd, pLeft, pTop, pRight, pBottom) . . 104 17.21 function float_wnd_set_geometry(fwl_wnd, vrWidth, vrHeight) . . . . . . . . 105 17.22 nwinLeft, nwinTop, nwinRight, nwinBottom = float_wnd_get_geometry(fwl_wnd)106 17.23 pwinLeft, pwinTop, pwinRight, pwinBottom = float_wnd_get_geometry(fwl_wnd)107 17.24 vrwinWidth, vrwinHeight = float_wnd_get_geometry(fwl_wnd) . . . . . . . . 107 17.25 msgx, msgy = XPLMGetMouseLocationGlobal() . . . . . . . . . . . . . . . . 108 17.26 ssWidth, ssHeight = XPLMGetScreenSize() . . . . . . . . . . . . . . . . . . . 108 17.27 bLeft, bTop, bRight, bBottom = XPLMGetScreenBoundsGlobal() . . . . . . . 109 17.28 tOS = XPLMGetAllMonitorBoundsOS() . . . . . . . . . . . . . . . . . . . . 110 17.29 tGB = XPLMGetAllMonitorBoundsGlobal() . . . . . . . . . . . . . . . . . . 110 18 Imgui Windows 18.1 18.2 18.3 18.4 18.5 18.6 18.7 18.8 111 demo_wnd = float_wnd_create(800, 450, 1, true) . . . . . . float_wnd_set_title(demo_wnd, "Demo Window") . . . . . . float_wnd_set_position(demo_wnd, 775, 650) . . . . . . . . float_wnd_set_imgui_builder(demo_wnd, "build_demo") . . float_wnd_set_onclose(demo_wnd, "closed_demo") . . . . . image_id = float_wnd_load_image(SCRIPT_DIRECTORY .. function build_demo(wnd, x, y) . . . . . . . . . . . . . . . function closed_demo(wnd) . . . . . . . . . . . . . . . . . 19 Debugging . . . . . . . . . . 111 . . . . . . . . . . 111 . . . . . . . . . . 112 . . . . . . . . . . 112 . . . . . . . . . . 112 "/imgui_demo.jpg")113 . . . . . . . . . . 113 . . . . . . . . . . 113 114 Page 9 of 122 Contents Contents 20 Integrate foreign libraries 115 21 The new 64-bit architecture 116 21.1 Architecture exclusive script loading . . . . . . . . . . . . . . . . . . . . . . . 116 21.2 Checking architecture inside a script . . . . . . . . . . . . . . . . . . . . . . . 116 21.3 64-bit DLLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 22 Q&A 117 22.1 My script doesn’t work. What can I do? . . . . . . 22.1.1 Check the debug info file and Log.txt . . . 22.1.2 Check for conflicts to other scripts . . . . . 22.1.3 I really can’t solve it! . . . . . . . . . . . . 22.2 How to ask the developer of FlyWithLua for help? . 22.3 Is the debug file privacy safe? . . . . . . . . . . . . 22.4 Where are the Splines? . . . . . . . . . . . . . . . 22.5 Feature requests . . . . . . . . . . . . . . . . . . . 22.6 Can I store Lua files inside the aircraft’s folder? . . 22.7 I want full access to X-Plane’s plugin SDK! . . . . 22.8 Using Lua For Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 117 117 117 118 118 118 119 119 120 121 23 Credits 122 24 License 122 Page 10 of 122 1 USING THE PLUGIN 1 Using the Plugin 1.1 What’s needed To use FlyWithLua, you will need following: a) The plugin itself. b) A nice text editor (VIM, GNU Emacs, Notepad++, ...). c) The plugin »XSquawkBox« (optional, but usefull). d) The plugin »DataRef Editor« (optional, but extremely high recommended). e) Some skills in programming Lua (FlyWithLua uses LuaJIT, compatible to Lua 5.1). f) Knowledge about DataRefs. 1.2 Difference between Core, Complete and Source Edition FlyWithLua is originally published in three different flavors. The first version is the Core Edition. This is the right version to start your first steps in using Lua to customize your X-Plane installation. The Core Edition is fully supported by the FlyWithLua developer team and acts as the heart of the FlyWithLua economy. If you got an error with this version, check if you have the latest version installed and report your issue together with a Log.txt and FlyWithLua_debug.txt file at the official FlyWithLua support forum. Please ask for support here: HelpMe Forum of FlyWithLua The second edition was made for the user who know what they are doing. We call it the Complete Edition. The Complete Edition is a ready to use binary version of FlyWithLua with additional libraries and no restrictions (access to private art control DataRefs is possible with this version). Remember, Pandora keeps her box widely opened for you in the Complete Edition, but this has not only positive effects. The more possibilities you get to manipulate XPlane, the more possible frustration may be the result, when Laminar Research updates X-Plane. If you do not know what the last sentence is about, you should not use the Complete Edition! This version of FlyWithLua has a lower level of support. Please ask in the same HelpMe Forum to get some assistance. For the real hackers we have the whole source code of FlyWithLua Core Edition published on GitHub. Just grab the code and compile it for your own, to have the full control. You want all menu entries blink in pink? If you are a C/C++ crack, just edit the source and compile your own flavor. FlyWithLua is published with the MIT license, so you can do everything you want to the source code without getting into trouble with us. Years of hard work is our free gift to you. Use it with respect and honor. Page 11 of 122 1.3 Installation 1 USING THE PLUGIN 1.3 Installation To use the plugin, just copy the complete folder FlyWithLua into X-Plane’s main plugin folder. The main plugin folder looks like this: «place where you store the sim»/X-Plane 10/Resources/plugins/1 When the plugin starts up, there must be a folder named «place where you store the sim»/X-Plane 10/Resources/plugins/FlyWithLua/Scripts/2 with at least one file in it (no matter if it is a Lua script or not). When you copy the complete folder, you will start with two subfolders, Scripts and Scripts (disabled). All scripts inside the Scripts folder will be run automatically by the plugin. When the plugin starts, it will run all files inside the Scripts folder, who end as: .fwl, .FWL, .lua, .Lua or .LUA If a file is hidden (it’s name begins with a single dot), the file will be ignored by the plugin. This means, you have three ways to disable a script. a) Change the endian. b) Hide it (let the name start with ».«). c) Move it to another folder. I prefer the last way, so you find the folder Scripts (disabled) filled with examples. All these examples may produce an enormous frustration, if you just copy them into the »active« folder. They may redefine your joystick setting for example. So be very carefully and modify them before usage. Lua scripts are a powerful weapon! 1.4 How to interact with Lua If you have XSquawkBox3 installed, there is an easy way to talk to Lua. If you type in a line into XSquawkBox starting with a > (a greater than sign), the line is send directly to Lua, instead of talking to your VATSIM channel (on COM1). Try the following code: >print(2+3) 1 If you use X-Plane 9 instead of X-Plane 10, search for the README_XP9.txt file and follow the instruction inside. 2 If you rename the plugin, it will stop working. So never change it’s folder name. not, you can click on the menu entry »Enter a line of code« in FlyWithLua’s plugin menu. There is no need to have XSquawkBox installed to use FlyWithLua. On the other hand, flying online on VATSIM is really cool. 3 If Page 12 of 122 1.5 Lua variables and DataRefs 1 USING THE PLUGIN If everything is fine, Lua will print a 5 into the XSquawkBox’s main text display4 . The output produced by Lua is not forwarded to the VATSIM COM1 channel. So don’t be afraid of disturbing the controller. You can check this behavior as XSquawkBox prints all internal information in dark red color. So FlyWithLua is a little pocket calculator? Hmm, why not. But this is not the intension of the plugin. FlyWithLua was made to interact with DataRefs. 1.5 Lua variables and DataRefs Lua can handle variables. You can try it out: >LuaIsNice = true >print(LuaIsNice) Not very spectacular, but wait, let’s tell Lua to bind a variable to a DataRef: >DataRef("battery", "sim/cockpit/electrical/battery_on", "writable") >print(battery) Wow! Lua prints out a 1 if the battery is on, or a 0 when the battery is off. That’s magic! But it goes even better. Turn on your plane and type this: >battery = 0 Plopp – your plane is down. If a variable is connected to a DataRef, and you define the connection as not readonly (the third parameter of the function call was "writable"), all changes on the variable will be pushed to the DataRef instandly. 1.6 Writing a first config file With the knowledge now, we can write a little config script like this: 1 2 3 4 5 6 D a t a R e f ( " p i t c h _ n u l l z o n e " , " sim / j o y s t i c k / j o y s t i c k _ p i t c h _ n u l l z o n e " , " w r i t a b l e " ) pitch_nullzone = 0.0 D a t a R e f ( " r o l l _ n u l l z o n e " , " sim / j o y s t i c k / j o y s t i c k _ r o l l _ n u l l z o n e " , " w r i t a b l e " ) roll_nullzone = 0.0 D a t a R e f ( " h e a d i n g _ n u l l z o n e " , " sim / j o y s t i c k / j o y s t i c k _ h e a d i n g _ n u l l z o n e " , " w r i t a b l e " ) heading_nullzone = 0.0 This works very well, but it is not really user friendly. So I decided to give FlyWithLua some extra functions, who make the code more cheerful. The config file can be alternatively written as: 1 2 3 s e t ( " sim / j o y s t i c k / j o y s t i c k _ p i t c h _ n u l l z o n e " , 0.0 ) s e t ( " sim / j o y s t i c k / j o y s t i c k _ r o l l _ n u l l z o n e " , 0.0 ) s e t ( " sim / j o y s t i c k / j o y s t i c k _ h e a d i n g _ n u l l z o n e " , 0 . 0 ) 4 Use the developer console to see the output, if you don’t have XSquawkBox installed. Page 13 of 122 1.7 Pre-defined variables 1 USING THE PLUGIN Much easier to read, isn’t it? 1.7 Pre-defined variables But what to code if you want a nullzone of 0.1 in your piston, but 0.0 in your helicopter? You can use the pre-defined variable PLANE_ICAO. 1 2 3 4 5 6 7 8 9 10 11 12 −− n u l l z o n e o f my l i t t l e C e s s n a i f ( PLANE_ICAO == " C172 " ) t h e n s e t ( " sim / j o y s t i c k / j o y s t i c k _ p i t c h _ n u l l z o n e " , 0.1 s e t ( " sim / j o y s t i c k / j o y s t i c k _ r o l l _ n u l l z o n e " , 0.1 s e t ( " sim / j o y s t i c k / j o y s t i c k _ h e a d i n g _ n u l l z o n e " , 0 . 1 end −− n u l l z o n e o f my l i t t l e c o f f e e m i l l i f ( PLANE_ICAO == " R22 " ) t h e n s e t ( " sim / j o y s t i c k / j o y s t i c k _ p i t c h _ n u l l z o n e " , 0.0 s e t ( " sim / j o y s t i c k / j o y s t i c k _ r o l l _ n u l l z o n e " , 0.0 s e t ( " sim / j o y s t i c k / j o y s t i c k _ h e a d i n g _ n u l l z o n e " , 0 . 0 end ) ) ) ) ) ) 1.8 Loop Callbacks All your code will be calculated automatically during startup, if you change the plane or position, or when you click on Reload all Lua script files in the plugin’s main menu. If this is not enough to you, you can generate code, that will be calculated continuously in a loop callback. These two commands given to Lua (using XSquawkBox’s input line) will produce an ugly behavior: >DataRef("poslight", "sim/cockpit/electrical/nav_lights_on", "writable") >do_sometimes("poslight = 0") Now, from time to time, Lua will turn off your navigation lights. Try it out to see how it works. You will not bewitch the simulator for the rest of your life. A simple click on the menu entry Reload all Lua script files will reset your magic spell. 1.9 Menu entries Every time you get your little bird back to Paderborn/Lippstadt (EDLP), you want to talk to the tower controller (on frequency 133.375) to initialize your VFR approach, and want your needle pointing to PAD (on frequency 354). Maybe you want to tune in the ILS signal. You can define an ATC menu entry to script it. This will help a lot, because the code will only be calculated if you click on the menu entry. Nobody always want to fly around Paderborn, right? Write a little file like this and name it »EDLP_VFR_Approach.lua«. Page 14 of 122 1.10 1 2 3 Menu switches 1 USING THE PLUGIN D a t a R e f ( "COM1" , " sim / c o c k p i t / r a d i o s / c o m 1 _ f r e q _ h z " , " w r i t a b l e " ) D a t a R e f ( "NAV1" , " sim / c o c k p i t / r a d i o s / n a v 1 _ f r e q _ h z " , " w r i t a b l e " ) D a t a R e f ( "ADF1" , " sim / c o c k p i t / r a d i o s / a d f 1 _ f r e q _ h z " , " w r i t a b l e " ) 4 5 6 7 8 add_ATC_macro ( " coming home " , [ [−− f l y home t o EDLP COM1 = 13337 NAV1 = 10855 ADF1 = 3 5 4 ] ] ) As the code of your macro needs more than one line, you use the double brackets [[ and ]] to write down a multi line string as the second argument of the function add_ATC_macro. So don’t forget the closing normal bracket, as shown in the last line above. 1.10 Menu switches If you want to toggle a special behavior of your simulator, defined in a script, but you do not want to rename or move the file and reload all scripts to use it or not, you can use menu entry switches. It’s just as easy as giving Lua’s add_macro function two additional string parameters. Write a file like this and name it »transponder_helper.lua«. If you don’t want to copy&paste the code, just take a look into the Scripts (disabled) folder. 1 2 3 4 5 6 −− The g r o u n d s p e e d i s i n m / s ( m e t e r p e r s e c o n d ) , n o t kn ( k n o t s ) , and a l w a y s r e a d o n l y D a t a R e f ( " g r o u n d s p e e d " , " sim / f l i g h t m o d e l / p o s i t i o n / g r o u n d s p e e d " ) −− The t r a n s p o n d e r c o d e i s a 4− d i g i t i n t e g e r D a t a R e f ( " t r a n s p o n d e r _ c o d e " , " sim / c o c k p i t / r a d i o s / t r a n s p o n d e r _ c o d e " , " w r i t a b l e " ) −− T r a n s p o n d e r mode ( o f f =0 , s t d b y =1 , on =2 , t e s t =3) D a t a R e f ( " t r a n s p o n d e r _ m o d e " , " sim / c o c k p i t / r a d i o s / t r a n s p o n d e r _ m o d e " , " w r i t a b l e " ) 7 8 9 −− we s t a r t i n Europe m o s t o f t h e t i m e t r a n s p o n d e r _ c o d e = 7000 10 11 12 13 14 15 16 17 18 19 20 21 22 23 −− t u r n on y o u r t r a n s p o n d e r when f l y i n g f a s t e r t h a n 20 m / s ( a b o u t 40 kn ) function check_transponder () i f ( t r a n s p o n d e r _ h e l p == t r u e ) t h e n i f ( ( g r o u n d s p e e d > 2 0 ) and ( t r a n s p o n d e r _ m o d e ~= 2 ) ) t h e n transponder_mode = 2 XPLMSpeakString ( " T r a n s p o n d e r s e t t o a c t i v e " ) end i f ( ( g r o u n d s p e e d < 2 0 ) and ( t r a n s p o n d e r _ m o d e > 1 ) ) t h e n transponder_mode = 1 XPLMSpeakString ( " T r a n s p o n d e r s e t t o s t a n d b y " ) end end end 24 25 26 −− c h e c k i t e v e r y 10 s e c do_sometimes ( " c h e c k _ t r a n s p o n d e r ( ) " ) 27 28 29 −− make a s w i t c h a b l e menu e n t r y , d e f a u l t i s on add_macro ( " A u t o m a t i c a l l y s e t T r a n s p o n d e r " , " t r a n s p o n d e r _ h e l p = t r u e " , " t r a n s p o n d e r _ h e l p = false " , " activate ") Page 15 of 122 2 REFERENCE 2 Reference 2.1 Predefined variables All predefined variables are readonly. If you change them, X-Plane will not recognize it. 2.1.1 LONGITUDE Gives the actual longitude value in decimal as a double float value (remember Lua didn’t know float but only number). As X-Plane uses the data type double, but the numbers in Lua are only float, you will get an approximation. The value is readonly, like all predefined variables. So it isn’t possible to replace the plane in it’s position by changing the value. Positive values in LONGITUDE are east, negative values are west. 2.1.2 LATITUDE This gives the latitude value of the plane’s position as a decimal value. So for example seven degree thirty minutes north is represented as 7.5 (a positive value, negative values are south). 2.1.3 PLANE_ICAO A string holding the plane’s ICAO code in it. 2.1.4 PLANE_TAILNUMBER The Tailnumber of the plane as a string. 2.1.5 SCREEN_WIDTH The screen width in pixel. 2.1.6 SCREEN_HIGHT The screen hight in pixel. Page 16 of 122 2.1 Predefined variables 2 REFERENCE 2.1.7 MOUSE_X Horizontally position of the mouse pointer. Coordinates start on the left side with 0 (zero). 2.1.8 MOUSE_Y Vertically position of the mouse pointer. Coordinates start on the bottom side with 0 (zero). 2.1.9 XSB_METAR A string containing the last metar received by XSquawkBox. If you are not connected to VATSIM, the variable will be useless. It is readonly. Readonly means, you can’t modify the online weather by changing the XSB_METAR variable! 2.1.10 LUA_RUN An integer value showing how often Lua was (re)started. During the very first run of Lua, it’s value is 1. You can use it to do things only once after X-Plane was started, and do not repeat when a new plane was loaded or the airport was changed. (Both will force a Lua restart.) 2.1.11 XPLANE_VERSION An integer value showing the version number of X-Plane. FlyWithLua is designed to run on X-Plane version 10.x, but it may run on X-Plane 9. To check if you are really on X-Plane 10 (or newer), you can say: if XPLANE_VERSION > 1000 then ... end. Example given: the version X-Plane 10.10rc3 shows: XPLANE_VERSION = 10101 2.1.12 XPLANE_HOSTID An integer value showing the HostID of X-Plane, an OS-specific value (totally unnecessary). 2.1.13 SDK_VERSION An integer value showing the version number of the SDK, FlyWithLua is running on. The SDK version should be 210 or above for X-Plane 10. If not, download a version for X-Plane 10 of FlyWithLua. Page 17 of 122 2.1 Predefined variables 2 REFERENCE 2.1.14 SYSTEM A string telling you on witch computer system FlyWithLua (the simulator) is running. It’s value is "IBM" on Windows systems, "APL" on Apple Macintosh systems and "LIN" on Linux systems. 2.1.15 SYSTEM_ARCHITECTURE A number either 32 or 64, depending on the architecture. 64 means X-Plane is running in 64-bit, 32 means the simulator and all plugins are running in 32-bit. 2.1.16 XPLANE_LANGUAGE A string value showing the language of X-Plane’s menus. The value can be "English", "French", "German", "Italian", "Spanish", "Korean", "Russian", "Greek", "Japanese", "Chinese" or "Unknown". Page 18 of 122 2.1 Predefined variables 2 REFERENCE Since FlyWithLua version 2.0 all menu entries are no longer forced to English. To create a multiple language support for your plugin, write code like this5 : 1 d a t a r e f ( "COM1" , " sim / c o c k p i t / r a d i o s / c o m 1 _ f r e q _ h z " , " w r i t a b l e " ) 2 3 4 i f XPLANE_LANGUAGE == " German " t h e n add_macro ( " S t e l l e d a s FunkgerÃd’t a u f UNICOM" , "COM1 = 12280 " ) 5 6 7 e l s e i f XPLANE_LANGUAGE == " F r e n c h " t h e n add_macro ( " FrÃl’quence r a d i o p o i n t s u r l ’UNICOM" , "COM1 = 12280 " ) 8 9 10 e l s e i f XPLANE_LANGUAGE == " S p a n i s h " t h e n add_macro ( " P u n t o de f r e c u e n c i a de r a d i o en l a UNICOM" , "COM1 = 12280 " ) 11 12 13 e l s e i f XPLANE_LANGUAGE == " I t a l i a n " t h e n add_macro ( " P u n t o d i f r e q u e n z a r a d i o s u l l a UNICOM" , "COM1 = 12280 " ) 14 15 16 17 else add_macro ( " S e t r a d i o t o UNICOM" , "COM1 = 12280 " ) end 2.1.17 DIRECTORY_SEPARATOR A string containing the directory separator of the current OS. 2.1.18 SYSTEM_DIRECTORY The complete OS-specific path to the X-Plane root directory with trailing slash. 2.1.19 SCRIPT_DIRECTORY The complete OS-specific path to the scripts including a directory separator as it’s last character. If you want to write a file named my_info.txt into the scripts directory (instead of X-Plane’s main directory), use code like this: infofile = os.open(SCRIPT_DIRECTORY .. "my_info.txt", "w") 2.1.20 INTERNALS_DIRECTORY The complete OS-specific path to the FlyWithLua Internals directory with trailing slash. 2.1.21 MODULES_DIRECTORY The complete OS-specific path to the FlyWithLua Modules directory with trailing slash. 5 All text other than English or German was translated using Google Translator. Page 19 of 122 2.1 Predefined variables 2 REFERENCE 2.1.22 AIRCRAFT_PATH The full path to your aircraft file, ending with a directory separator. 2.1.23 AIRCRAFT_FILENAME The name of the ACF aircraft file, including the endian ".acf". 2.1.24 DO_EVERY_FRAME_TIME_SEC The duration time in seconds of the every frame loop. 2.1.25 DO_EVERY_DRAW_TIME_SEC The duration time in seconds of the drawing loop. 2.1.26 DO_SOMETIMES_TIME_SEC The duration time in seconds of the loop to be executed sometimes. 2.1.27 DO_OFTEN_TIME_SEC The duration time in seconds of the often executed loop. 2.1.28 SCRIPTS_LOADING_TIME_SEC The time it takes to load all scripts. 2.1.29 CLOCKS_PER_SEC The number of clock ticks in one second, a C value depending on your system. 1 CLOCKS_PER_SEC is the ninimal time that can be maesured. 2.1.30 LUA_MEMORY_USAGE_KB The memory usage of the Lua environment in kB. This is not the complete memory consumption of your scripts, as some objects like wave files are not stored into Lua, but are allocated in C by the plugin FlyWithLua. Page 20 of 122 2.2 Lua functions 2 REFERENCE 2.2 Lua functions The following functions are written in core C++ and are a part of the plugin. Most of them are multi-defined to handle different count of arguments. 2.2.1 DataRef( "variable name", "DataRef name" ) a) variable name = Name of the Lua variable representing the DataRef. b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. Binds a Lua variable to a DataRef6 . The connection will be forced to readonly. Not possible to array DataRefs. 2.2.2 DataRef( "variable name", "DataRef name", "readonly" ) a) variable name = Name of the Lua variable representing the DataRef. b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) readonly = Should the variable be pushed to X-Plane when it is changed? Say "readonly" to have it readonly or "writable" to make it writable. Binds a Lua variable to a DataRef. The connection will be writealbe if you say "writable" as the third argument and if the DataRef is writable. Not possible to array DataRefs. 2.2.3 DataRef( "variable name", "DataRef name", "readonly", index ) a) variable name = Name of the Lua variable representing the DataRef. b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) readonly = Should the variable be pushed to X-Plane when it is changed? Say "readonly" to have it readonly or "writable" to make it writable. d) index = The index of an array DataRef. Binds a Lua variable to a DataRef. The connection will be writalbe if you say "writable" as the third argument and if the DataRef is writable. For array DataRefs use a fourth argument, the index starting at 0 (Zero). It will bind the element at the given index. It will not bind an array as a Lua table. 6 You can spell it dataref() instead of DataRef(), if you don’t like uppercase letters. Page 21 of 122 2.2 Lua functions 2 REFERENCE 2.2.4 table = dataref_table( "DataRef name") a) table = Name of the Lua variable representing the DataRef. The variable will be a Lua table, access it like an anrray with index starting at 0 (Zero). b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. This function will create a table variable to access a DataRef. You can use it for any type of DataRef, no matter if the DataRef is an array or not. Here are some examples: 1 2 −− t h e b a t t e r y D a t a R e f i s an a r r a y o f up t o 8 b a t t e r y s w i t c h e s b a t t e r y = d a t a r e f _ t a b l e ( " sim / c o c k p i t 2 / e l e c t r i c a l / b a t t e r y _ o n " ) 3 4 5 −− now we t u r n on t h e f i r s t battery [0] = 1 b a t t e r y of the plane 6 7 8 −− b u t i t w i l l work on non−a r r a y D a t a R e f s t o o COM1FREQ = d a t a r e f _ t a b l e ( " sim / c o c k p i t / r a d i o s / c o m 1 _ f r e q _ h z " ) 9 10 11 −− s w i t c h i t t o UNICOM COM1FREQ[ 0 ] = 12280 2.2.5 define_shared_DataRef("DataRef name", "DataRef type") a) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. b) DataRef type = Type of the DataRef. This type is a string argument and can be "Int", "Float", "IntArray", "FloatArray", "Double" or "Data". With this function you will create a DataRef of the given type if it does not exist. If it exist and the type is different, you will get an error message. If it still exists with the same type, this command will do nothing. Shared DataRefs should help you interacting with other plugins. 2.2.6 table = create_dataref_table( "DataRef name", "DataRef type") a) table = Name of the Lua variable representing the DataRef. The variable will be a Lua table, access it like an anrray with index starting at 0 (Zero). b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) DataRef type = Type of the DataRef. This type is a string argument and can be "Int", "Float", "IntArray", "FloatArray", "Double" or "Data". Here you have the best way to create a new DataRef and connect it to a table variable inside Lua. This is an example: Page 22 of 122 2.2 1 2 Lua functions 2 REFERENCE −− A s t r i n g m u s t be c r e a t e d a s a " Data " t y p e m y _ w e l c o m e _ s t r i n g = c r e a t e _ d a t a r e f _ t a b l e ( " F l y W i t h L u a / u s e l e s s _ s a m p l e s / welcome " , " D a t a " ) 3 4 5 −− no m a t t e r i f i t l o o k s l i k e a t a b l e , j u s t i n d e x t h e f i r s t e l e m e n t t o f i l l m y _ w e l c o m e _ s t r i n g [ 0 ] = " H e l l o World ! " it 6 7 8 9 −− a n o t h e r example , now we c r e a t e an i n t e g e r v a l u e th e_ an sw er = c r e a t e _ d a t a r e f _ t a b l e ( " FlyWithLua / u s e l e s s _ s a m p l e s / t he _a ns we r " , " I n t " ) t h e _ a n s w e r [ 0 ] = 42 10 11 12 13 14 −− i n t i m e s all_answers all_answers all_answers of = [0] [1] a l t e r n a t i v e f a c t s , we can h a v e m u l t i p l e a n s w e r s c r e a t e _ d a t a r e f _ t a b l e ( " FlyWithLua / u s e l e s s _ s a m p l e s / answers " , " I n t A r r a y " ) = 42 = 2017 2.2.7 DataRef name, Index, readonly, DataRef type, DataRef ID = get_DataRef_binding( "variable name" ) a) variable name = Name of the Lua variable representing the DataRef. b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) Index = The Index of the DataRef. This is always 0 (zero) if the DataRef isn’t an array. d) readonly = This results to true is the DataRef is readonly and false if it’s writable. e) DataRef type = Type of the DataRef. 1 = integer, 2 = float, 4 = double, 8 = float array, 16 = integer array and 32 = data (strings). f) DataRef ID = The ID of the DataRef. A pointer to the memory, the DataRef is stored. 2.2.8 button( button number ) a) button number = Number of the button, starting at 0 (zero). Returns the value of the actual state of a joystick button. button(i) will result in true if the button is pressed, else it gives back false. The argument i must be an integer, ranging from 0 to 1599. Check out the number in X-Plane’s advanced buttons menu. 2.2.9 last_button( button number ) a) button number = Number of the button, starting at 0 (zero). Page 23 of 122 2.2 Lua functions 2 REFERENCE Returns the value of the state of a joystick button, as it was during the last frame. last_button(i) will result in true if the button was pressed, else it gives back false. The argument i must be an integer, ranging from 0 to 1599. Check out the number in X-Plane’s advanced buttons menu. Advice: Always use button() and last_button() to grab joystick values, instead of using DataRef("MyButton", "sim/joystick/joystick_button_values", "readonly", 123), if you like super fast code execution. The functions button() and last_button() deliver values much efficient than user defined DataRefs. 2.2.10 create_switch( button number, DataRef name, index, off value, on value ) a) button number = Number of the button, starting at 0 (zero). b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) index = The index of an array DataRef, else 0. d) off value = Value to be set when button is not pressed (off). e) on value = Value to be set when button is pressed (on). This will turn a joystick buton into a switch controlling a DataRef. If the button is not pressed, the DataRef will be set to the off value, else to the on value. The index is 0 (zero) for non-array DataRefs. The last three arguments are optional. If you leave them away, Lua will guess 0 for the index and the off value and 1 for the on value. So this will be fine to let button no. 15 control the battery as a real hardware switch: >create_switch(15, "sim/cockpit/electrical/battery_on") 2.2.11 create_positive_edge_flip( button number, DataRef name, index, first value, second value ) a) button number = Number of the button, starting at 0 (zero). b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) index = The index of an array DataRef, else 0. d) first value = Value to be set when button is just pressed (positive edge detecttion). e) second value = Value to be set when button is just pressed but the DataRef’s value is equal to the first value. Page 24 of 122 2.2 Lua functions 2 REFERENCE This is similar to the create_switch() function, but it will flip between the two values, given as the last two arguments, every time a positive edge was detected (when the button is pressed just in that moment). The last three arguments are optional. If you leave them away, Lua will guess 0 for the index and the first value and 1 for the second value. So this will be fine to let button no. 15 control the battery and flip it every time it is pressed: >create_positive_edge_flip(15, "sim/cockpit/electrical/battery_on") Lua will automatically handle it like this: >create_positive_edge_flip(15, "sim/cockpit/electrical/battery_on", 0, 0, 1) 2.2.12 create_negative_edge_flip( button number, DataRef name, index, first value, second value ) a) button number = Number of the button, starting at 0 (zero). b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) index = The index of an array DataRef, else 0. d) first value = Value to be set when button is just released (positive edge detecttion). e) second value = Value to be set when button is just released but the DataRef’s value is equal to the first value. Nearly the same as the function create_positive_edge_flip(), but it will react when the button is released. For an engeneer, this is the negative edge of the button’s signal. 2.2.13 create_positive_edge_trigger( button number, DataRef name, index, value ) a) button number = Number of the button, starting at 0 (zero). b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) index = The index of an array DataRef, else 0. d) value = Value to be set in the moment when button is pressed down (positive signal edge). This will set the DataRef to the given value in the moment, when the button is pressed down, not during hold. In engineer’s words it’s a positive edge detection. Page 25 of 122 2.2 Lua functions 2 REFERENCE 2.2.14 create_negative_edge_trigger( button number, DataRef name, index, value ) a) button number = Number of the button, starting at 0 (zero). b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) index = The index of an array DataRef, else 0. d) value = Value to be set in the moment when button is released (negative signal edge). This will set the DataRef to the given value in the moment, when the button is released. In engineer’s words it’s a negative edge detection. 2.2.15 create_positive_edge_increment( button number, DataRef name, index, stepping, limit, rounding ) a) button number = Number of the button, starting at 0 (zero). b) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. c) index = The index of an array DataRef, else 0. d) stepping = Value to be added to the DataRef when the button is pressed down (positive signal edge). e) limit = Value that should not be trespass. f) rounding = Value must be a power of ten, the DataRef should be rounded to. This will increase the DataRef by the given step when the button is pressed down. The parameter rounding is optional. If you give this value to Lua, the DataRef will be rounded. Here is an example: >create_positive_edge_increment(13, "sim/flightmodel/engine/ENGN_cowl", 2, 0.1, 1.0, 0.1) This will increase the cowl flap of engine no. 3 (X-Plane starts numbering at 0) by 0.1 up to the limit of 1.0 – and the result will be rounded to one decimal place. Rounding is only possible to float DataRefs. 2.2.16 create_negative_edge_increment( button number, DataRef name, index, stepping, limit, rounding ) The same as before, but with negative edge detection (works when the button is released). Page 26 of 122 2.2 Lua functions 2 REFERENCE 2.2.17 create_positive_edge_decrement( button number, DataRef name, index, stepping, limit, rounding ) Same as before, but decreases the DataRef. An other example (the radio frequency is an integer DataRef): >create_positive_edge_increment(15, "sim/cockpit/radios/com1_freq_hz", 0, 100, 13797) >create_positive_edge_decrement(14, "sim/cockpit/radios/com1_freq_hz", 0, 100, 11800) 2.2.18 create_negative_edge_decrement( button number, DataRef name, index, stepping, limit, rounding ) What the hell could this does? ;) 2.2.19 create_axis_median( axis number, variable name ) a) axis number = Number of the axis, starting at 0 (zero). b) variable name = Name of the variable to be filled with the median value of the axis. Calculates a median value of the last five values from an axis and puts it into a Lua variable. This is an example how to store the median value of axis no. 3 (the fourth axis shown in X-Plane, as we start counting with zero) into the variable median_throttle: >create_axis_median(3, "median_throttle") 2.2.20 get( "DataRef name" ) a) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. Pulling a DataRef to Lua. This function returns one value pulled from the DataRef. Slower than the automatic pull, but does not need a variable. Good for initial scripts or macros. Highly not recommended in callbacks. This is the version used for non array DataRefs. If you try to pull an array DataRef with this function, you will get the first element of the array. An easy way of reading out a DataRef with the XSquawkBox’s input line. Check DataRefs like this: >print(get("sim/aircraft/weight/acf_m_fuel_tot")) Page 27 of 122 2.2 Lua functions 2 REFERENCE 2.2.21 get( "DataRef name", index ) a) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. b) index = The index of an array DataRef. Pulling a DataRef to Lua. This function returns one value pulled from the DataRef. Slower than the automatic pull, but does not need a variable. Good for initial scripts or macros. Highly not recommended in callbacks. This is the version used for array DataRefs. Page 28 of 122 2.2 Lua functions 2 REFERENCE 2.2.22 set( "DataRef name", value ) a) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. b) value = The value you want to push to the DataRef. Pushing a given value to a DataRef. Not possible for array or string DataRefs. The set function is slower than the automatic pushing of variables to DataRefs. But on the other hand it will not create a global variable. This can provide getting in confict between multiple scripts using the same variable for different DataRefs, a situation normally crashing the system. Use the set function to fill DataRefs during startup (typically config files) or in macros, when you only need to push the values (and do not need to pull them into Lua variables). 2.2.23 set_array( "DataRef name", index, value ) a) DataRef name = Name of the DataRef. Look at the listing of all DataRefs. b) index = The index of an array DataRef. c) value = The value you want to push to the DataRef. Does the same as the set() function, but to be used for array DataRefs. 2.2.24 set_button_assignment( button number, "simulator function") a) button number = Number of the button (starting with 0). b) simulator function = Name of the function you want to assign. You can copy&paste the name from X-Plane’s advanced button setting menu. Must be a string, don’t forget the brackets. Assigning a function given by X-Plane to a joystick button. The same as clicking it inside the advanced button settings menu. Usefull to make different configs for different planes or situations. Page 29 of 122 2.2 Lua functions 2 REFERENCE 2.2.25 set_axis_assignment( axis number, "axis function", "reverse") a) axis number = Number of the axis (starting with 0). Since X-Plane 10.10 Austin forces you to guess the numbers or to view inside the config files. Counting them inside the menu is no longer possible. Please ask him why he did it, not me. b) axis function = Name of the function you want to assign. You can copy&paste the name from X-Plane’s advanced button setting menu. Must be a string, don’t forget the brackets. c) reverse = a string telling X-Plane to reverse the axis if the value is "reverse" or to set a normal axis if the value is "normal". Assigning axis functions. Possible values for the function names are: "none", "pitch", "roll", "yaw", "throttle", "collective", "left toe brake", , "right toe brake", "prop", "mixture", "carb heat", "flaps", "thrust vector", "wing sweep", "speedbrakes", "displacement", "reverse", "elev trim", "ailn trim", "rudd trim", "throttle 1", "throttle 2", "throttle 3", "throttle 4", "prop 1", "prop 2", "prop 3", "prop 4", "mixture 1", "mixture 2", "mixture 3", "mixture 4", "reverse 1", "reverse 2", "reverse 3", "reverse 4", "landing gear", "nosewheel tiller", "backup throttle", "cowl flaps", "view left/right", "view up/down", "view zoom", "camera left/right", "camera up/down", "camera zoom", "gun/bomb left/right", "gun/bomb up/down", "VR Touchpad X", "VR Touchpad Y" and "VR Trigger". 2.2.26 clear_all_axis_assignments() Sets all assignments to "none". 2.2.27 clear_all_button_assignments() Sets all assignments to "sim/none/none". 2.2.28 set_pilots_head( x, y, z, heading, pitch ) a) x, y, z = Position of pilot’s head relative to the plane. b) heading = The heading of pilot’s head. c) pitch = The pitch of pilot’s head. This will set the pilot’s head in position and angle. If we are not in 3D view, 3D view will be set. Page 30 of 122 2.2 Lua functions 2 REFERENCE 2.2.29 x, y, z, heading, pitch = get_pilots_head( ) a) x, y, z = Position of pilot’s head relative to the plane. b) heading = The heading of pilot’s head. c) pitch = The pitch of pilot’s head. This will get the pilot’s head position and angle. 2.2.30 command_begin( "simulator function" ) a) simulator function = Name of the function you want to assign. You can copy&paste the name from X-Plane’s advanced button setting menu. Execute a simulator given command only one time. This will execute the "begin" part of a command. X-Plane’s commands have three phases. What really will happen when you execute one of this parts depends on the original programming made by Laminar Research (or other). In some cases you will have to experiment the right command call (the right phase). 2.2.31 command_once( "simulator function" ) a) simulator function = Name of the function you want to assign. You can copy&paste the name from X-Plane’s advanced button setting menu. Execute a simulator given command only one time. This will execute the "main" part of a command. X-Plane’s commands have three phases. What really will happen when you execute one of this parts depends on the original programming made by Laminar Research (or other). In some cases you will have to experiment the right command call (the right phase). In nearly 100% this is the phase you want to call. 2.2.32 command_end( "simulator function" ) a) simulator function = Name of the function you want to assign. You can copy&paste the name from X-Plane’s advanced button setting menu. Page 31 of 122 2.2 Lua functions 2 REFERENCE Execute a simulator given command only one time. This will execute the "end" part of a command. X-Plane’s commands have three phases. What really will happen when you execute one of this parts depends on the original programming made by Laminar Research (or other). In some cases you will have to experiment the right command call (the right phase). 2.2.33 logMsg( "string" ) a) string = What you want to say. Write a string into the Log.txt file in X-Plane’s main directory. You can take a look into the log file by assigning a button to the simulator function "sim/operation/dev_console". Or you choose the viewing toggle from the specials menu. 2.2.34 XSBSpeakString( "string" ) a) string = What you want to say. Write a string into the XSquawkBox. The string will not be send to other pilots or controllers when connected to VATSIM. Only to give you an easy way to print notes on the screen. 2.2.35 XPLMSpeakString( "string" ) a) string = What you want to say. Write a string onto the screen for a few seconds and speaks the string if text-to-speak is enabled. This function will return immediately, the string will be spoken asynchronously. If you fire up multiple strings at once, you will get a confusing mix. So take care of timing. Page 32 of 122 2.2 Lua functions 2 REFERENCE 2.2.36 print( "string" ) a) string = What you want to say. Similar to XSBSpeakString(), but uses it’s own box to display. All text will be displayed for 5 seconds, then the box fades away. To display the box again, you can move the mouse pointer to the top of the screen. To scroll through the lines, just move the mouse pointer left or right. 2.2.37 do_sometimes( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every 10 sec. Calculates a string of Lua code from time to time. 2.2.38 do_often( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every sec. Calculates a string of Lua code very often. 2.2.39 do_every_frame( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every single frame. Calculates a string of Lua code every single frame. Can slow down the simulator at a glance. Use this function carefully! 2.2.40 do_every_draw( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every single draw. Calculates a string of Lua code every single draw. Seems to be the same as do_every_frame(), but it is different. Only in this drawing callback you are able to draw things like colored text. To save CPU time, the automatic DataRefs to variables transfer is disabled during a draw callback. So do not read or write DataRefs, use it only to draw your messages. Can slow down the simulator at a glance. Use this function carefully! Page 33 of 122 2.2 Lua functions 2 REFERENCE 2.2.41 do_on_keystroke( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every time when the user presses or releases a key. When the user (pilot) presses or releases a key, a keystroke event starts your Lua code given by this function. Lua provides these special variables, the last one is writable: a) VKEY = An integer value representing the key you pressed. Play around with this value a little bit, it is not the ASCII value. b) CKEY = The key as a char (string with a single letter). c) SHIFT_KEY = A boolean value, representing the state of the shift key. If a shift key is pressed, the value is true, else false. d) OPTION_KEY = A boolean value, representing the state of the option or alt key. If an option or alt key is pressed, the value is true, else false. e) CONTROL_KEY = A boolean value, representing the state of the control key. If a control key is pressed, the value is true, else false. f) KEY_ACTION = A string either resulting in "pressed" or "released", depending on the user action. g) RESUME_KEY = A boolean value. If it is set to true your script will resume the keystroke and X-Plane will not recognize it. Default value is false, to not disturb X-Plane or other plugins. 2.2.42 do_on_mouse_wheel( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every time when the user presses, holds down or releases the primary mouse button. When the user (pilot) moves a mouse wheel, an event handler starts your Lua code given by this function. Lua provides these special variables, the last one is writable: a) MOUSE_WHEEL_NUMBER = An positive integer value starting with 0 (zero), indicating what wheel causes the event. Some operating systems allow more than one mouse wheel. If not, it will be always 0. b) MOUSE_WHEEL_CLICKS = An integer value indicating the number of steps the user moved the wheel. Can be positive or negative depending on the moving direction. c) RESUME_MOUSE_WHEEL = A boolean value. If it is set to true your script will resume the mouse wheel movement and X-Plane will not recognize it. Default value is false, to not disturb X-Plane or other plugins. Page 34 of 122 2.2 Lua functions 2 REFERENCE 2.2.43 do_on_mouse_click( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every time when the user presses, holds down or releases the primary mouse button. When the user (pilot) presses, holds or releases the primary mouse button, an event handler starts your Lua code given by this function. Lua provides these special variables, the last one is writable: a) MOUSE_STATUS = A string either "down", "drag" or "up". "down" says, the user just starts pressing the button, "drag" means, he holds down the mouse button and if he releases the button, you get "up". b) RESUME_MOUSE_CLICK = A boolean value. If it is set to true your script will resume the mouse click and X-Plane will not recognize it. Default value is false, to not disturb X-Plane or other plugins. 2.2.44 do_on_new_metar( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every time when the plugin receives a new METAR from XSquawkBox. This is called by a XSquawkBox event. You can read out the predefined variable XSB_METAR, or do whatever you like when XSquawkBox sends a new METAR (changes the weather). 2.2.45 do_on_new_XSB_text( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be calculated every time when the plugin receives a new text message from XSquawkBox. This is called by a XSquawkBox event. You can read out the predefined variable XSB_TEXT_MESSAGE, XSB_TEXT_FROM, XSB_TEXT_FREQ, XSB_TEXT_USE, or do whatever you like when XSquawkBox receives a new text message. The variable XSB_TEXT_USE can be 0 or 1. If XSB_TEXT_USE is 0 (zero), XSquawkBox will not display or evaluate the message. Only this variable is pushed back to the XSquawkBox. The other three variables are read only (pulled from the XSquawkBox before executing the Lua code string, but not pushed back when finished). Please keep in mind that the code is executed during the event call (inter plugin communication). So you can not write into a variable defined by the DataRef() function. But the dataref_table() function works (you can use the variables created this way to write to XPlane). There is an example script delivered with FlyWithLua. As you can see, the XSB_TEXT_FREQ contains an integer value representing the frequency in 10 Hz multiplied by the value. Page 35 of 122 2.2 Lua functions 2 REFERENCE 2.2.46 do_on_exit( "Lua code string" ) a) Lua code string = A string containing Lua code you want to be executed when Lua stops. The will be executed only on normal stops, like changing the airport or aircraft or shutting down X-Plane. The code can/will not be execuded on errors. Use this function to collect code that is executed when Lua stops working because of a script reload. This is not for error handling, but can be usefull if you want to store values to disk for the next time you start Lua. 2.2.47 draw_string( x, y, "string" ) a) x = Horizontally position where you want to draw. Starts on the left side from 0 (Zero). b) y = Vertically position where you want to draw. Starts from the bottom with value 0 (Zero). c) string = The string you want to see on top of the screen. Prints a string onto the screen. Will only work during draw callbacks. The color is set to white. The drawing system is for advanced users only! 2.2.48 draw_string( x, y, "string", "color" ) a) x = Horizontally position where you want to draw. Starts on the left side from 0 (Zero). b) y = Vertically position where you want to draw. Starts from the bottom with value 0 (Zero). c) string = The string you want to see on top of the screen. d) color = A string describing the color you want to choose. Prints a string onto the screen. Will only work during draw callbacks. The color can be "white", "black", "grey", "red", "green", "blue", "yellow", "magenta" or "cyan". The drawing system is for advanced users only! Page 36 of 122 2.2 Lua functions 2 REFERENCE 2.2.49 draw_string( x, y, "string", red, green, blue ) a) x = Horizontally position where you want to draw. Starts on the left side from 0 (Zero). b) y = Vertically position where you want to draw. Starts from the bottom with value 0 (Zero). c) string = The string you want to see on top of the screen. d) red = A float value from 0.0 to 1.0 choosing the red part of a RGB color. e) green = A float value from 0.0 to 1.0 choosing the green part of a RGB color. f) blue = A float value from 0.0 to 1.0 choosing the blue part of a RGB color. If the predefined color don’t fit your needs, choose a custom RGB value. The drawing system is for advanced users only! 2.2.50 draw_string_Helvetica_10( x, y, "string" ) a) x = Horizontally position where you want to draw. Starts on the left side from 0 (Zero). b) y = Vertically position where you want to draw. Starts from the bottom with value 0 (Zero). c) string = The string you want to see on top of the screen. Prints a string onto the screen. Will only work during draw callbacks. This will print the text to screen using the GLUT library instead of the X-Plane SDK. So you will have to set the color first by glColor4f(red, green, blue, alpha). It will print the text using the bitmap font GLUT_BITMAP_HELVETICA_10. 2.2.51 draw_string_Helvetica_12( x, y, "string" ) The same as above, but using the bitmap font GLUT_BITMAP_HELVETICA_12. 2.2.52 draw_string_Helvetica_18( x, y, "string" ) The same as above, but using the bitmap font GLUT_BITMAP_HELVETICA_18. 2.2.53 draw_string_Times_Roman_10( x, y, "string" ) The same as above, but using the bitmap font GLUT_BITMAP_TIMES_ROMAN_10. Page 37 of 122 2.2 Lua functions 2 REFERENCE 2.2.54 draw_string_Times_Roman_24( x, y, "string" ) The same as above, but using the bitmap font GLUT_BITMAP_TIMES_ROMAN_24. 2.2.55 measure_string( "string" ) a) string = The string you want to measure. Returns the length of a given string in screen pixel as a float number. Calculation is based on the standard proportional font used by draw_string(). 2.2.56 measure_string( "string", "font name" ) a) string = The string you want to measure. b) font name = The name of the font for use with GLUT. Returns the length of a given string in screen pixel as an integer number. Calculation is based on the font given by the second argument. It can be Helvetica_10, Helvetica_12, Helvetica_18, Times_Roman_10 or Times_Roman_24. 2.2.57 hight, width = bubble( x, y, "title", . . . ) a) x = Horizontally position where you want to draw the bubble. Starts on the left side from 0 (Zero). b) y = Vertically position where you want to draw the bubble. Starts from the bottom with value 0 (Zero). c) title = The string you want to see on top of the bubble in a slightly bigger font size. d) . . . = An optional set of strings for the text lines. Each line must be a single string argument without an CR/LF in it. The function bubble() is only allowed inside the drawing loop callback. So only use it with do_every_draw() – or you will see no result. The two returned arguments will give you the maximum x and y screen coordinate the bubble will use. The scripts QNH_helper.lua and bubble copilot.lua will show you some examples how to use bubbles. 2.2.58 hight, width = big_bubble( x, y, "title", . . . ) The same as bubble(), but with a bigger font size. Page 38 of 122 2.2 Lua functions 2 REFERENCE 2.2.59 hight, width = huge_bubble( x, y, "title", . . . ) The same as bubble(), but with a much bigger font size. 2.2.60 add_macro( "macro name", "Lua code string" ) a) macro name = Name of the macro. This string is used for the menu entry. b) Lua code string = A string containing Lua code you want to be calculated when the user clicks on the menu entry. Make a menu entry to calculate a little piece of Lua code on demand. 2.2.61 add_ATC_macro( "macro name", "Lua code string" ) a) macro name = Name of the macro. This string is used for the menu entry. b) Lua code string = A string containing Lua code you want to be calculated when the user clicks on the menu entry. Make a menu entry to calculate a little piece of Lua code on demand. Menu entry will be created inside the ATC menu, instead of the macro menu. Nice to collect some radio settings for recurrent situations like flying home to your base airport. Can save a lot of clicks. 2.2.62 add_macro( "macro name", "activation code string", "deactivation code string", "default state" ) a) macro name = Name of the macro. This string is used for the menu entry. b) activation code string = A string containing Lua code you want to be calculated when the user turns on the menu item. c) deactivation code string = A string containing Lua code you want to be calculated when the user turns off the menu item. d) default state = A string either "activate" or "deactivate" to define the default state of the menu entry. Creating a menu entry with a switch. Only possible for macro menu, not for the ATC menu. See the tutorial above for an example (auto setting the transponder). Page 39 of 122 2.2 Lua functions 2 REFERENCE 2.2.63 create_command( "command name", "command description", "begin code string", "continue code string", "end code string" ) a) command name = Name of the command, as X-Plane wants it to be (slash separated). b) command description = A string describing your command. Can be found in X-Plane’s keyboard and joystick menu. c) begin code string = A string containing Lua code you want to be calculated when the command begins. d) continue code string = A string containing Lua code you want to be calculated when the command continues (one per frame). e) end code string = A string containing Lua code you want to be calculated when the command ends. Creates a classic custom command. As you can check button states and call Lua functions direct in every frame loops, custom commands are pretty useless. If you want to script a classic command to provide a nice new feature to the X-Plane universe, keep in mind that all of your little script have to contain classic code. Do not use modern code (variables connected to DataRefs). You will never know if the user, who downloaded your custom command script file, will use the same writable DataRef with a different variable name. If so, FlyWithLua will stop working and presents an error message. Attention: Never use a custom command name like "sim/...", or Austin will kill you. There is a demo script »test command.lua«, showing how to use this powerful feature. And the QNH tool »automatic set qnh.lua« will also provide a custom command. 2.2.64 table = directory_to_table( "path" ) a) table = A variable you want to be filled with a Lua table containing all filenames inside the given directory. b) path = A string with the path of the directory. The path can be written in Unix stile, independent from the OS FlyWithLua is running on. Will return a simple table containing all filenames in alphabetical order. Only the filenames are returned, without the path. Page 40 of 122 2.2 Lua functions 2 REFERENCE 2.2.65 place_aircraft_at( "ICAO" ) a) ICAO = A string with the ICAO code of the airport you want to place the user to. Lua will be stopped for a moment and the plane will be replaced. This can take some time, so please be patient. Please remember that all Lua scripts will be reloaded after this action! 2.2.66 load_aircraft( "path and full filename" ) a) path and full filename = A string with the path of your file. The path can be written in Unix stile, independent from the OS FlyWithLua is running on. You will need the full filename including the ending. Lua will be stopped for a moment and the plane will be changed. This can take some time, so please be patient. Please remember that all Lua scripts will be reloaded after this action! This is an example: 1 l o a d _ a i r c r a f t ( " A i r c r a f t / G e n e r a l A v i a t i o n / C e s s n a 172 SP / Cessna_172SP . a c f " ) 2.2.67 load_situation( "path and full filename" ) a) path and full filename = A string with the path of your file. The path can be written in Unix stile, independent from the OS FlyWithLua is running on. You will need the full filename including the ending. Lua will be stopped for a moment and the situation file will be loaded. This can take some time, so please be patient. Please remember that all Lua scripts will be reloaded after this action! This does the same as the menu item "File" -> "Load Situation". 2.2.68 save_situation( "path and full filename" ) a) path and full filename = A string with the path of your file. The path can be written in Unix stile, independent from the OS FlyWithLua is running on. You will need the full filename including the ending. The current situation is written into a file. You will have to use the ending ".sit" for situation files, to make sure that you can load them with X-Plane’s menu. This does the same as the menu item "File" -> "Save Situation". Page 41 of 122 2.2 Lua functions 2 REFERENCE 2.2.69 reload_scenery() This will reload the scenery silently. But keep in mind that reloading the scenery will freeze the simulation for a couple of time, depending on the complexity of your scenery and the power of your CPU. Reloading the scenery with this function call will not reload the scripts. 2.2.70 crash_the_sim() Believe me, you will never ever want to know what this function does to your simulator. Page 42 of 122 3 MODULES 3 Modules You can write a module file to create new functions, predefined DataRefs, global variables or whatever you need for more than one script file. The file must be named name you want.lua and must be copied into this folder: «place where you store the sim»/X-Plane 10/Resources/plugins/FlyWithLua/Modules/ For the more advanced Lua coders, the Lua engine will look for modules inside the folder named above with the search pattern: "?.lua;?/init.lua" If you do not understand it, don’t matter. This will be a quick and ugly info: Create a file with an ending .lua and start with a first line exactly as shown: 1 module ( . . . , p a c k a g e . s e e a l l ) ; 2 3 4 5 6 −− h e r e comes y o u r own c o d e function say_hello () X S B S p e a k S t r i n g ( " H e l l o World ! " ) end When the first line is spelled 100% correct, you can get access to the module from every script inside the Scripts folder. Just start with the name of the module followed by "." (a dot) followed by what you want to access. If you named the file shown above "nonsense.lua", then you can write a script like this. 1 require ( " nonsense " ) 2 3 4 −− l e t ’ s s a y h e l l o nonsense . say_hello ( ) In all files you want access to your module, you have to start the script with a Lua command require(), to tell Lua which module you want to use. If you want access to more than one module, use multiple require() commands. 3.1 The Radio Module If you start your script with a line like this: require("radio") you will get all major DataRefs writable to access to the radios. They are: COM1, COM2, COM1_STDBY, COM2_STDBY, NAV1, NAV2, NAV1_STDBY, NAV2_STDBY, ADF1, ADF2, ADF1_STDBY, ADF2_STDBY, DME, DME_STDBY, OBS1, OBS2, SQUAWK (0000 to 7777), TRANSPONDER\_MODE (0=off, 1=standby, 2=on, 3=test) and HDG. Page 43 of 122 3.2 The XSquawkBox Module 3 MODULES 3.2 The XSquawkBox Module To access all common DataRefs provided by XSquawkBox, you should start your script with: require("XSquawkBox") The DataRefs are: XSB_VERS_NUMBER,XSB_VERS_STRING, XSB_CON_CALLSIGN, XSB_CON_SERVER, XSB_CON_PORT, XSB_CON_PILOT_ID, XSB_CON_PASSWORD, XSB_CON_REALNAME, XSB_CON_MODEL, XSB_CON_STATUS, XSB_FP_FLIGHT_TYPE, XSB_FP_TCAS_TYPE, XSB_FP_NAV_TYPE, XSB_FP_SPEED, XSB_FP_DEPARTURE_AIRPORT, XSB_FP_DEPARTURE_TIME, XSB_FP_DEPARTURE_TIME_ACTUAL, XSB_FP_CRUISE_ALTITUDE, XSB_FP_ARRIVAL_AIRPORT, XSB_FP_ENROUTE_HRS, XSB_FP_ENROUTE_MINS, XSB_FP_FUEL_HRS, XSB_FP_FUEL_MINS, XSB_FP_ALTERNATE_AIRPORT, XSB_FP_REMARKS, XSB_FP_ROUTE, XSB_MIC_OPEN and XSB_MIC_ENABLED. The DataRefs XSB_VERS_NUMBER, XSB_VERS_STRING, XSB_CON_STATUS and XSB_MIC_OPEN are readonly, all other DataRefs are writable. Look into the module file XSquawkBox.lua and study the XSquawkBox documentation, to find out how they work. Independent from the module, you get these functions to command the XSquawkBox plugin: 3.2.1 XSBConnect() Connects the plugin to VATSIM, using the login data from the DataRefs. No login dialog will be shown. 3.2.2 XSBUserLogin() Nearly the same as XSBConnect(), but it will show the login dialog and stop. The user can take a look at the data and click on »connect« or »cancel«. If you are unsure what function is the best to connect to VATSIM by a lua script, choose this. (You will not surprise the user.) 3.2.3 XSBDisconnect() Disconnects from the VATSIM network instantly, without any dialog. Page 44 of 122 3.3 The Bit Module 3 MODULES 3.2.4 XSBShowFlightplan() Shows the flightplan dialog of XSquawkBox. The flightplan uses the DataRefs defined by the module, so you can preload them with data before calling XSquawkBox to show the flightplan dialog. The user can click on »send« or »cancel«. If he cancels the dialog, all changes are not put into the DataRefs. If he sends the plan to the VATSIM server, changes to the values are put into the DataRefs (the next time a per-frame call starts, section »Understanding PLCs« explains why). 3.2.5 XSBSendFlightplan() This function fires the flightplan filled by the DataRefs directly to VATSIM, without any dialog. This behavior can surprise a user, so use it with care! 3.2.6 frequency = XSBLookupATC( "name string" ) This function wants a string as it’s argument and returns an integer value showing the frequency of the ATC you ask for, or 0 (zero) if a controller with the given name is not online. This allows a script like this: 1 local informed_the_user = false 2 3 4 5 6 7 8 function check_Wooge_service ( ) i f XSBLookupATC ( "EDWG_I_TWR" ) == 12240 and i n f o r m e d _ t h e _ u s e r == f a l s e t h e n p r i n t ( " H u r r a ! H u r r a ! Wooge i s t b e s e t z t ! " ) informed_the_user = true end end 9 10 do_sometimes ( " check_wooge_service ( ) " ) The unit of the return value is 10 kHz, to get it compatible to X-Plane’s DataRefs (see module radio). The original value returned by XSquawkBox uses the unit 1 kHz, FlyWithLua makes the translation for you. 3.3 The Bit Module The Bit module is included as a part of the LuaJIT system. A documentation to bitwise operations provided by these module can be found here: http://bitop.luajit.org/ To use the Bit module, simply start your script with: require("bit") Page 45 of 122 4 OPENAL SOUND 4 OpenAL sound Since version 2.3 FlyWithLua supports OpenAL sound. The OpenAL sound system is part of X-Plane, so no additional library or module is needed. But before you play a sound, you must understand how the sound system works. 4.1 Buffers, Sounds and Listeners If you are familiar with OpenAL, you know that the sound system uses tree parts, buffers, sounds and listeners. FlyWithLua combines these different parts into one table, where all the sound stuff is stored. So forget everything you know about OpenAL and think about sound as system represented by only one big table. If you force FlyWithLua to write a debug file, you will see the sound system table (if it contains sounds). 4.2 Loading and defining sounds To use the sound system, you first have to load a sound file into memory. At the moment, only WAV files are allowed, no MP3 or OGG files. You fill the sound system table by loading a WAV file and remembering the position inside the table. The first sound you load gets number 0 (zero), as this is typical for C++ plugins (FlyWithLua is written in C/C++). 4.2.1 table position = load_WAV_file( filename ) a) table position = The index value, where your file is stored into the sound system table. Index values starts from 0 (Zero). b) filename = The name of the WAV file to load. This function loads a WAV file into the sound system table and gives back the index value. This is an example: 1 r o t a t e _ s o u n d = load_WAV_file ( SCRIPT_DIRECTORY . . " s o u n d s / r o t a t e . wav " ) After you have loaded a sound file, you can define some parameters to the sound, like the loop, pitch and gain value. By default, a sound is only played once (loop = false), with normal pitch (pitch = 1.0) and full gain (gain = 1.0). If you want other than the default values, modify them with these functions: Page 46 of 122 4.2 Loading and defining sounds 4 OPENAL SOUND 4.2.2 let_sound_loop( table position, boolean value ) a) table position = The index value, where your file is stored into the sound system table. Index values starts from 0 (Zero). b) boolean value = This has to be true, if the sound should loop, else false. 4.2.3 set_sound_pitch( table position, float value ) a) table position = The index value, where your file is stored into the sound system table. Index values starts from 0 (Zero). b) float value = The value for the pitch. The default value is 1.0 for an unmodified, normal pitch. 4.2.4 set_sound_gain( table position, float value ) a) table position = The index value, where your file is stored into the sound system table. Index values starts from 0 (Zero). b) float value = The value for the gain. The default value is 1.0 for full gain. Here is an example: 1 2 3 4 5 −− l o a d s o u n d " c a b i n crew , p r e p a r e f o r l a n d i n g " c c _ p r e p a r e _ l a n d i n g _ s o u n d = load_WAV_file ( SCRIPT_DIRECTORY . . " s o u n d s / c c p r e p l a n d . wav " ) −− f a s t s p e a k i n g , h e l i u m b r e a t h i n g p i l o t w h i s p e r i n g set_sound_pitch ( cc_prepare_landing_sound , 1.8) set_sound_gain ( cc_prepare_landing_sound , 0.25) 4.2.5 unload_all_sounds( ) This will unload all sounds from memory. After this the sound system table is enpty and all your stored index values are useless. FlyWithLua uses this function to clean up the memory before reloading all scripts. You should avoid to directly access this cleanup, if you want to share your script, or other scripts of the users can be crashed by killing there sounds. Page 47 of 122 4.2 Loading and defining sounds 4 OPENAL SOUND 4.2.6 replace_WAV_file(table position, filename ) a) table position = The index value, where your file is stored into the sound system table. Index values starts from 0 (Zero). b) filename = The name of the WAV file to load. This function loads a WAV file into a given position of the sound system table. The position must contain a sound file, as this function only replaces the WAV buffer in memory. You can’t create a new element in the sound system table. You can use this to adjust the content of sounds. This is an example: 1 ... 2 3 4 −− l o a d s o u n d " Good m o r n i n g S i r " welcome_sound = load_WAV_file ( SCRIPT_DIRECTORY . . " s o u n d s / g o o d m o r n i n g . wav " ) 5 6 ... 7 8 9 10 function teach_copilot_Frisian () r e p l a c e _ W A V _ f i l e ( welcome_sound , SCRIPT_DIRECTORY . . " s o u n d s / moinmoin . wav " ) end Page 48 of 122 4.3 Using the sounds from the sound table 4 OPENAL SOUND 4.3 Using the sounds from the sound table After you filled the sound system table with WAV files and all parameters are defined, you can use the sounds by using these functions: 4.3.1 play_sound( table position ) a) table position = The index value, where your sound is stored into the sound system table. Index values starts from 0 (Zero). This functions starts playing the sound at the given index. If the loop parameter is set to false (the dafault value), playing will stop automatically, else it will restart from the beginning until you stop the sound. 4.3.2 stop_sound( table position ) a) table position = The index value, where your sound is stored into the sound system table. Index values starts from 0 (Zero). This functions stops the sound at the given index. This is especially useful, if the sound is forced to loop. FlyWithLua will not remember the position (duration) where you stop the sound. 4.3.3 pause_sound( table position ) a) table position = The index value, where your sound is stored into the sound system table. Index values starts from 0 (Zero). This functions pause the sound at the given index. This is especially useful, if the sound is forced to loop. FlyWithLua will remember the position (duration) where you pause the sound. When you call the play_sound() function the next time, it will continue at this position (duration). 4.3.4 rewind_sound( table position ) a) table position = The index value, where your sound is stored into the sound system table. Index values starts from 0 (Zero). This functions rewinds the sound at the given index. In other words, the sound will continue at the beginning position, if you restart playing. Page 49 of 122 5 OPENGL GRAPHICS 5 OpenGL graphics If you want to draw more than text, you can get directly access to a few OpenGL functions. All OpenGL functions provided by FlyWithLua will not check the arguments! This is a performance issue. OpenGL is for advanced coders only. If you make a mistake and send a nil argument direct into your graphic card, it’s your blame seeing the hellfire of a black hole eating up your computer hardware. ;) 5.1 Functions of OpenGL FlyWithLua provides these OpenGL stuff: 5.1.1 glBegin_POINTS() 5.1.2 glBegin_LINES() 5.1.3 glBegin_LINE_STRIP() 5.1.4 glBegin_LINE_LOOP() 5.1.5 glBegin_POLYGON() 5.1.6 glBegin_TRIANGLES() 5.1.7 glBegin_TRIANGLE_STRIP() 5.1.8 glBegin_TRIANGLE_FAN() 5.1.9 glBegin_QUADS() 5.1.10 glBegin_QUAD_STRIP() 5.1.11 glEnd() 5.1.12 glVertex2f(x, y) 5.1.13 glVertex3f(x, y, z) Page 50 of 122 5.1 Functions of OpenGL 5 OPENGL GRAPHICS 5.1.14 glLineWidth(width) 5.1.15 glColor3f(red, green, blue) 5.1.16 glColor4f(red, green, blue, alpha) 5.1.17 glRectf(x1, y1, x2, y2) All arguments are float numbers, but as Lua don’t know the difference between int, float and double, you can fire into your graphic card any type of number, as long as it’s not a string or table. Important: FlyWithLua draws when X-Plane is in "window draw state". Everything you can do is drawing 2D with screen pixel coordinates starting from left bottom. The x argument is "pixels to the right", the y arguments is "pixels up". The z coordinate must be 0 (zero), or you will not see what you want to draw. So glVertex3f() is pretty useless at the moment. It is included for further versions of FlyWithLua. The same for glColor4f, as the window draw state ignores the alpha values.7 If you want more graphic features, than you might better use other tools like SASL. You should not change OpenGL states directly. Use the XPLMSetGraphicsState() function instead. This function can be accessed directly through FlyWithLua (without any error checking!). 5.1.18 XPLMSetGraphicsState(EnableFog, NumberTexUnits, EnableLighting, EnableAlphaTesting, EnableAlphaBlending, EnableDepthTesting, EnableDepthWriting) Sets the OpenGL graphics state. All arguments are integers, 1 is on, 0 is off. FlyWithLua starts every drawing with XPLMSetGraphicsState(0,0,0,1,1,0,0) -- set alpha testing and blending on The line above is executed before everything from do_every_draw() is done. But commands from other scripts may change the state, so you should always reset the state before you draw any 2D stuff. 7 This is no longer true in FlyWithLua 2.0, but you have to use XPLMSetGraphicsState() when a draw_string() function is used. Page 51 of 122 6 THE GRAPHICS MODULE 6 The graphics module If you start a script with a line like require("graphics"), you will be able to use all functions delivered by the graphics module. 6.1 Functions of graphics module The functions are: 6.1.1 x_result, y_result = graphics.move_angle( x, y, angle, length ) a) x = Horizontal position where your calculation starts (left begins with 0). b) y = Vertical position where your calculation starts (bottom begins with 0). c) angle = Angle to the point you are interested in. A value of 0 will point upwards, values go clockwise (90 is to the right, 180 is downwards, 270 is to the left). d) length = The length of your (virtual) line in pixel. e) x_result = The horizontal coordinate in screen pixel of the end point of your (virtual) line. f) y_result = The vertical coordinate in screen pixel of the end point of your (virtual) line. A helper function used by other graphics functions to do the calculations. You may not need this function directly. 6.1.2 graphics.draw_line( x1, y1, x2, y2 ) x1, y1 and x2, y2 are the screen coordinates of the start and end point of the line, you want to draw. 6.1.3 graphics.draw_rectangle( x1, y1, x2, y2 ) x1, y1 and x2, y2 are the screen coordinates of two opposite corner points of the rectangle, you want to draw. 6.1.4 graphics.draw_triangle( x1, y1, x2, y2, x3, y3 ) x1, y1 and x2, y2 and x3, y3 are the screen coordinates of the points of a triangle, you want to draw. Page 52 of 122 6.1 Functions of graphics module 6 THE GRAPHICS MODULE 6.1.5 graphics.set_color( red, green, blue, alpha ) a) red = A float value from 0.0 to 1.0 choosing the red part of a RGB color. b) green = A float value from 0.0 to 1.0 choosing the green part of a RGB color. c) blue = A float value from 0.0 to 1.0 choosing the blue part of a RGB color. d) alpha = The alpha value (transparency) of the color. An alpha value of 1.0 will draw only your stuff, a value of 0.0 makes it invisible. If the alpha arguments is missing, it will be set to 1.0. 6.1.6 graphics.set_width( width ) a) width = The line width you want to use from now on. 6.1.7 graphics.draw_angle_line( x, y, angle, length ) a) x = Horizontal position where your line starts (left begins with 0). b) y = Vertical position where your line starts (bottom begins with 0). c) angle = Angle where to draw the line. A value of 0 will point upwards, values go clockwise (90 is to the right, 180 is downwards, 270 is to the left). d) length = The length of your line in pixel. Draws a line from a given point with a given angle and length. Very useful for drawing round instruments. 6.1.8 graphics.draw_angle_arrow( x, y, angle, length, arrowhead’s length, line width ) a) x = Horizontal position where your arrow starts (left begins with 0). b) y = Vertical position where your arrow starts (bottom begins with 0). c) angle = Angle where to draw the arrow. A value of 0 will point upwards, values go clockwise (90 is to the right, 180 is downwards, 270 is to the left). d) length = The length of your arrow in pixel. e) arrowhead’s length = The length of the arrowhead in pixel. When the value is missing, 7.5 will be used. f) line width = The width of the line (default is 1). Page 53 of 122 6.1 Functions of graphics module 6 THE GRAPHICS MODULE Draws an arrow from a given point with a given angle and length. Very useful for drawing round instruments. You can modify the design by choosing the width and the length of the arrowhead. 6.1.9 graphics.draw_circle( x, y, radius, line width ) If you leave the line width argument away, a line width of 1.0 will be used. 6.1.10 graphics.draw_filled_circle( x, y, radius ) Draws a circle filled with the actual color. 6.1.11 graphics.draw_arc( x, y, start angle, end angle, radius, line width ) Draws an arc defined by a start and an end angle. If you leave the line width argument away, a line width of 1.0 will be used. 6.1.12 graphics.draw_filled_arc( x, y, start angle, end angle, radius ) Draws an arc defined by a start and an end angle. The arc is filled with the actual color. 6.1.13 graphics.draw_tick_mark( x, y, angle, radius, length, width ) Draws a tick mark to the inner side of a given circle (by x, y, radius). The position of the tick mark has to be set as an angle. You can change the design of the inner tick mark line by setting it’s length and width. If you leave the parameters away, the default length is 10.0 and the default width is 1.0. This will only draw the tick mark, not the circle. So you can use it for circles and arcs. 6.1.14 graphics.draw_outer_tracer( x, y, angle, radius, size ) Draws an outer tracer (a little triangle) to a given circle (by x, y, radius). The position of the tracer has to be set as an angle. You can change the design of the tracer by setting it’s size. If you leave the size parameter away, the default size of 7.5 will be used. This will only draw the tracer, not the circle. So you can use it for circles and arcs. Page 54 of 122 6.1 Functions of graphics module 6 THE GRAPHICS MODULE 6.1.15 graphics.draw_inner_tracer( x, y, angle, radius, size ) The same as before, but the little triangle will be placed inside the circle. Page 55 of 122 7 HUD MODULE 7 HUD module 7.1 An Interactive HUD Normally X-Plane has a forward view with HUD but all HUD elements are unchangeable given by X-Plane, and they are not interactive. Since FlyWithLua 2.2 you can react on mouse clicks and mouse wheel movement. So we made a little demo how to use it by creating the HUD module. The module allows to define multiples »HUDs«. A HUD, as defined by the module, is a rectangle area of the screen containing elements, who are rectangle areas as well. 7.2 An Example Let’s start with an example. 1 r e q u i r e "HUD" 2 3 HUD. begin_HUD ( 1 0 0 , 2 0 0 , 8 0 , 4 5 , " MyExample " ) 4 5 6 HUD. c r e a t e _ e l e m e n t ( " c a p t i o n " , 0 , 3 0 , 8 0 , 1 5 ) HUD. d r a w _ s t r i n g ( 1 2 , 3 , 1 0 , " H e l l o World ! " ) 7 8 HUD. end_HUD ( ) Create a Lua script file with these code and execute it. Now change the view to forward with HUD and you will see your result. Seeing »Hello World!« isn’t really cool for pilots, so we modify the script a little bit. 1 r e q u i r e "HUD" 2 3 d a t a r e f ( " QNH_Pilot " , " sim / c o c k p i t 2 / g a u g e s / a c t u a t o r s / b a r o m e t e r _ s e t t i n g _ i n _ h g _ p i l o t " , " writable " ) 4 5 HUD. begin_HUD ( 1 0 0 , 2 0 0 , 8 0 , 4 5 , " MyExample " ) 6 7 8 HUD. c r e a t e _ e l e m e n t ( " c a p t i o n " , 0 , 3 0 , 8 0 , 1 5 ) HUD. d r a w _ s t r i n g ( 1 2 , 3 , 1 0 , " H e l l o World ! " ) 9 10 11 12 HUD. c r e a t e _ e l e m e n t ( " b a r o " , 0 , 0 , 8 0 , 3 0 ) HUD. d r a w _ s t r i n g ( 1 2 , 2 0 , 1 0 , "BARO" ) HUD. d r a w _ f s t r i n g ( 1 2 , 3 , 1 8 , " %2.2 f " , " QNH_Pilot " ) 13 14 HUD. end_HUD ( ) Okay, this is a little more useful to pilots. We can see the barometer setting of the pilot’s altimeter. The first lines of code are loading the module »HUD« and define a DataRef variable QNH_Pilot. Then line no. 5 starts creating a HUD container. The parameters are x, y, width, hight and an Page 56 of 122 7.2 An Example 7 HUD MODULE unique name of the HUD container (useful to debug the code). The screen coordinates x and y are from the bottom left corner of the screen. If you want them relative to the right or upper border of the screen use negative values. The parameters of the next function are relative to the HUD container. So 0, 30, 80, 15 meens a screen area from 100/230 to 180/245, as this is 0/30 to 80/45 inside the HUD container. We always define elements by there lower left corner. If we have to define an area instead of only a point, we add width and hight, not an upper right corner! The same in line 8, the parameters 12, 20 points to 112/233 on the screen, as all sub-elements are relative to there »mother« element or container. This is a big advantage, as you can move the whole container or element, just by changing one pair of parameters. All lower elements will follow there parents. The function draw_fstring() allows us to format the output. The fourth parameter must be a string, not an expression! If you forget the brackets around QNH_Pilot, your code won’t work. To make it even more useful, we will now add some interaction. 1 r e q u i r e "HUD" 2 3 d a t a r e f ( " QNH_Pilot " , " sim / c o c k p i t 2 / g a u g e s / a c t u a t o r s / b a r o m e t e r _ s e t t i n g _ i n _ h g _ p i l o t " , " writable " ) 4 5 HUD. begin_HUD ( −81 , −200 , 8 0 , 4 5 , " MyExample " ) 6 7 8 9 HUD. c r e a t e _ e l e m e n t ( " c a p t i o n " , 0 , 3 0 , 8 0 , 1 5 ) HUD. d r a w _ s t r i n g ( 1 2 , 3 , 1 0 , " s e t t o STD" ) HUD. c r e a t e _ c l i c k _ a c t i o n ( 0 , 0 , 8 0 , 1 5 , " QNH_Pilot = 2 9 . 9 2 " ) 10 11 12 13 HUD. c r e a t e _ e l e m e n t ( " b a r o " , 0 , 0 , 8 0 , 3 0 , 0 , 0 , 0 , 0 ) HUD. d r a w _ s t r i n g ( 1 2 , 2 0 , 1 0 , "BARO" ) HUD. d r a w _ f s t r i n g ( 1 2 , 3 , 1 8 , " %2.2 f " , " QNH_Pilot " ) 14 15 HUD. end_HUD ( ) The first change is that we are now positioned from the upper right of the screen (see line no. 5). An element is made with a border around it. You can give the color values red, green, blue and alpha. By making alpha 0 (zero), the border will be invisible (see line no. 11). The most important change is in line no. 9. We define an action, when the user (pilot) clicks inside the element. The action itself must be given as a string, not as an expression. But we are not limited to mouse clicks. We can also react on mouse wheel movements. Let’s modify the code once again. Page 57 of 122 7.2 1 An Example 7 HUD MODULE r e q u i r e "HUD" 2 3 d a t a r e f ( " QNH_Pilot " , " sim / c o c k p i t 2 / g a u g e s / a c t u a t o r s / b a r o m e t e r _ s e t t i n g _ i n _ h g _ p i l o t " , " writable " ) 4 5 HUD. begin_HUD ( −81 , −200 , 8 0 , 4 5 , " MyExample " ) 6 7 8 9 HUD. c r e a t e _ e l e m e n t ( " c a p t i o n " , 0 , 3 0 , 8 0 , 1 5 ) HUD. d r a w _ s t r i n g ( 1 2 , 3 , 1 0 , " s e t t o STD" ) HUD. c r e a t e _ c l i c k _ a c t i o n ( 0 , 0 , 8 0 , 1 5 , " QNH_Pilot = 2 9 . 9 2 " ) 10 11 12 13 14 HUD. HUD. HUD. HUD. c r e a t e _ e l e m e n t ( " baro " , 0 , 0 , 80 , 30 , 0 , 0 , 0 , 0) d r a w _ s t r i n g ( 1 2 , 2 0 , 1 0 , "BARO" ) d r a w _ f s t r i n g ( 1 2 , 3 , 1 8 , " %2.2 f " , " QNH_Pilot " ) c r e a t e _ w h e e l _ a c t i o n ( 0 , 0 , 8 0 , 3 0 , " QNH_Pilot = QNH_Pilot + MOUSE_WHEEL_CLICKS / 100 ") 15 16 HUD. end_HUD ( ) One more line of code adds a lot of additional fun. In line no. 14 we create a reaction on mouse wheel movement. The variable MOUSE_WHEEL_CLICKS is a read-only FlyWithLua predefined variable. It’s value is positive or negative depending on the direction of the movement. Now we make the last modification of the example. 1 r e q u i r e "HUD" 2 3 4 d a t a r e f ( " QNH_Pilot " , " sim / c o c k p i t 2 / g a u g e s / a c t u a t o r s / b a r o m e t e r _ s e t t i n g _ i n _ h g _ p i l o t " , " writable " ) d a t a r e f ( "GPU" , " sim / c o c k p i t / e l e c t r i c a l / gpu_on " , " w r i t a b l e " ) 5 6 HUD. begin_HUD ( −81 , −200 , 8 0 , 4 5 , " MyExample " ) 7 8 9 10 HUD. c r e a t e _ e l e m e n t ( " c a p t i o n " , 0 , 3 0 , 8 0 , 1 5 ) HUD. d r a w _ s t r i n g ( 1 2 , 3 , 1 0 , " s e t t o STD" ) HUD. c r e a t e _ c l i c k _ a c t i o n ( 0 , 0 , 8 0 , 1 5 , " QNH_Pilot = 2 9 . 9 2 " ) 11 12 13 14 15 HUD. HUD. HUD. HUD. c r e a t e _ e l e m e n t ( " baro " , 0 , 0 , 80 , 30 , 0 , 0 , 0 , 0) d r a w _ s t r i n g ( 1 2 , 2 0 , 1 0 , "BARO" ) d r a w _ f s t r i n g ( 1 2 , 3 , 1 8 , " %2.2 f " , " QNH_Pilot " ) c r e a t e _ w h e e l _ a c t i o n ( 0 , 0 , 8 0 , 3 0 , " QNH_Pilot = QNH_Pilot + MOUSE_WHEEL_CLICKS / 100 ") 16 17 HUD. end_HUD ( ) 18 19 20 21 22 23 24 HUD. begin_HUD ( 1 0 0 , 1 , 3 0 , 1 2 , "GPU" , " a l w a y s " ) HUD. c r e a t e _ e l e m e n t ( "GPU" , 0 , 0 , 3 0 , 1 2 ) HUD. d r a w _ s t r i n g ( 4 , 2 , 1 0 , "GPU" ) HUD. c r e a t e _ b a c k l i g h t _ i n d i c a t o r ( 0 , 0 , 3 0 , 1 2 , "GPU == 1 " , 0 , 1 , 0 , 0 . 5 ) HUD. c r e a t e _ c l i c k _ s w i t c h ( 0 , 0 , 3 0 , 1 2 , "GPU" , 0 , 1 ) HUD. end_HUD ( ) Lines 19 to 24 define another HUD container. The additional container is defined as "always", so it will appear if the mouse hovers over it, no matter of the view mode. You find the example file as HUD module example.lua and an even more complex script HUD module test.lua in the Scripts (disabled) folder. Page 58 of 122 7.3 Functions from HUD module 7 HUD MODULE 7.3 Functions from HUD module 7.3.1 HUD.begin_HUD( x, y, width, hight, "name", "always" ) a) x = Horizontal position of the HUD container’s lower left corner. Use negative value to get relative to the right screen border. b) y = Vertical position of the HUD container’s lower left corner. Use negative value to get relative to the upper screen border. c) width = The width in pixel. d) hight = The hight in pixel. e) name = The name of the container. A string for debugging only. f) "always" = If this optional string is given as the last parameter, the container will always be visible if the mouse hovers over it. Otherwise it will be visible in forward with HUD view mode only. This starts the description of a container. A must use function, or you will never see something on your screen. 7.3.2 HUD.end_HUD( ) This ends the description of a container, generates a Lua script file and executes this file. This can be followed by the next container, as long as the container names are different. 7.3.3 HUD.create_element( "name", x, y, width, hight, red, green, blue, alpha ) a) name = The name of the element. A string for debugging only. b) x = Horizontal position of the element relative to it’s container. Negative values are not allowed. c) y = Vertical position of the element relative to it’s container. Negative values are not allowed. d) width = The width in pixel. e) hight = The hight in pixel. f) red, green, blue, alpha = OpenGL color code of the border around the element. All values are floating point numbers from 0.0 (zero) to 1.0 (one). Creates an element. Elements can contain strings, colored indicators and actions. There is no HUD.end_element() function. Just start the next element or close the HUD description. Page 59 of 122 7.3 Functions from HUD module 7 HUD MODULE 7.3.4 HUD.draw_string( x, y, fontsize, "string", red, green, blue, alpha ) a) x = Horizontal position of the string relative to it’s element. b) y = Vertical position of the string relative to it’s element. c) fontsize = The size of the font. Value can be 8, 10, 12 or 18. d) "string" = The string to be printed. e) red, green, blue, alpha = OpenGL color code of the border around the element. All values are floating point numbers from 0.0 (zero) to 1.0 (one). This will draw a string inside the element. Keep in mind that all coordinates are relative to the parent. The string will be printed directly onto the screen without any evaluation. 7.3.5 HUD.draw_fstring( x, y, fontsize, "format", "expression", red, green, blue, alpha ) a) x = Horizontal position of the string relative to it’s element. b) y = Vertical position of the string relative to it’s element. c) fontsize = The size of the font. Value can be 8, 10, 12 or 18. d) "format" = The format string, as used by Lua’s string.format() function. e) "expression" = A string to be evaluated to get the values for the format string. f) red, green, blue, alpha = OpenGL color code of the border around the element. All values are floating point numbers from 0.0 (zero) to 1.0 (one). If you want to print variable values, use the HUD.draw_fstring() function. The output can be well formatted, as known from the string.format() function in pure Lua. 7.3.6 HUD.create_backlight_indicator( x, y, width, hight, "condition", red, green, blue, alpha ) a) x = Horizontal position of the area relative to the element. b) y = Vertical position of the area relative to the element. c) width = The width in pixel. d) hight = The hight in pixel. e) "condition" = A string containing a Lua expression that can be evaluated as true or false. f) red, green, blue, alpha = OpenGL color code of the background. All values are floating point numbers from 0.0 (zero) to 1.0 (one). Page 60 of 122 7.3 Functions from HUD module 7 HUD MODULE If the expression results into true, the area will be filled with the given color. 7.3.7 HUD.create_click_action( x, y, width, hight, "action" ) a) x = Horizontal position of the click-sensitive area relative to the element. b) y = Vertical position of the click-sensitive area relative to the element. c) width = The width in pixel. d) hight = The hight in pixel. e) "action" = A string containing the Lua code to be executed when the user clicks into the sensitive area. If the user clicks into the sensitive area, the action will be done. This will always resume the click. 7.3.8 HUD.create_click_switch( x, y, width, hight, "variable", value, alternative value ) a) x = Horizontal position of the click-sensitive area relative to the element. b) y = Vertical position of the click-sensitive area relative to the element. c) width = The width in pixel. d) hight = The hight in pixel. e) "variable" = The Lua variable to be set. f) value = The value to be set. g) alternative value = The alternative value. If the user clicks into the sensitive area, the variable will be set to the value. But if it contains the value, it will be set to the alternative value. Page 61 of 122 7.3 Functions from HUD module 7 HUD MODULE 7.3.9 HUD.create_wheel_action( x, y, width, hight, "action" ) a) x = Horizontal position of the sensitive area relative to the element. b) y = Vertical position of the sensitive area relative to the element. c) width = The width in pixel. d) hight = The hight in pixel. e) "action" = A string containing the Lua code to be executed when the user moves the mouse wheel inside the sensitive area. If the user moves the mouse wheel while the mouse pointer is inside the sensitive area, the action will be done. This will always resume the wheel movement. You can use the predefined variable MOUSE_WHEEL_CLICKS to define the action. The variable is positive or negative depending on the direction of the wheel movement. It’s value depends on the operation system, on Windows you will get little integer steps. Page 62 of 122 8 XPLMNAVIGATION 8 XPLMNavigation You can use all functions from the XPLMNavigation SDK Part delivered by Sandy Barbour. Get more info on it here: http://www.xsquawkbox.net/xpsdk/mediawiki/XPLMNavigation When you use the functions in Lua, you will have to use a different spelling, as Lua does not need pointers to return more than one value. The correct spelling is shown in the following function description. 8.1 Functions from XPLMNavigation 8.1.1 nav_reference = XPLMGetFirstNavAid() 8.1.2 next_nav_reference = XPLMGetNextNavAid( inNavAidRef ) 8.1.3 first_nav_reference = XPLMFindFirstNavAidOfType( inType ) 8.1.4 last_nav_reference = XPLMFindLastNavAidOfType( inType ) 8.1.5 nav_reference = XPLMFindNavAid( inNameFragment, inIDFragment, inLat, inLon, inFrequency, inType) 8.1.6 outType, outLatitude, outLongitude, outHeight, outFrequency, outHeading, outID, outName = XPLMGetNavAidInfo( inRef ) 8.1.7 index_count = XPLMCountFMSEntries() 8.1.8 index = XPLMGetDisplayedFMSEntry() 8.1.9 index = XPLMGetDestinationFMSEntry() 8.1.10 XPLMSetDisplayedFMSEntry( inIndex ) 8.1.11 XPLMSetDestinationFMSEntry( inIndex ) 8.1.12 outType, outID, outRef, outAltitude, outLat, outLon = XPLMGetFMSEntryInfo( inIndex ) Page 63 of 122 8.1 Functions from XPLMNavigation 8 XPLMNAVIGATION 8.1.13 XPLMSetFMSEntryInfo( inIndex, inRef, inAltitude) 8.1.14 XPLMSetFMSEntryLatLon( inIndex, inLat, inLon, inAltitude) 8.1.15 XPLMClearFMSEntry( inIndex ) If you have to use inType or outType, you will have to manage integer values, not strings. You can use the variables (Lua does not know constants) XPLM_NAV_NOT_FOUND, xplm_Nav_Unknown, xplm_Nav_Airport, xplm_Nav_NDB, xplm_Nav_VOR, xplm_Nav_ILS, xplm_Nav_Localizer, xplm_Nav_GlideSlope, xplm_Nav_OuterMarker, xplm_Nav_MiddleMarker, xplm_Nav_InnerMarker, xplm_Nav_Fix, xplm_Nav_DME and xplm_Nav_LatLon. The value outReg from XPLMGetNavAidInfo( inIndex ) will not be returned by Lua, because it’s useless for your script. If you want a ninth value, you will always get a nil value. Page 64 of 122 9 ACCESS HID DEVICES 9 Access HID devices Since version 2.1 of FlyWithLua, you can access HID devices at low level. This is really cool for cockpit builders. Normal users can skip this section. 9.1 Pre-defined variables 9.1.1 NUMBER_OF_HID_DEVICES Integer value showing the number of HID devices, you can access from FlyWithLua. 9.1.2 ALL_HID_DEVICES All HID devices found are stored in a table ALL_HID_DEVICES. The table has elements indexed from 1 to NUMBER_OF_HID_DEVICES. Each elements has sub-elements. You can see all subelements when you write a debug file. Here is an example of the first HID device on my Windows 7 development system: 630 631 632 633 634 635 636 637 638 639 ALL_HID_DEVICES [ 1 ] . v e n d o r _ i d = 1103 ( 0 x 4 4 f ) ALL_HID_DEVICES [ 1 ] . p r o d u c t _ i d = 45322 ( 0 xb10a ) ALL_HID_DEVICES [ 1 ] . r e l e a s e _ n u m b e r = 1280 ( 0 x500 ) ALL_HID_DEVICES [ 1 ] . i n t e r f a c e _ n u m b e r = −1 ( 0 x f f f f f f f f ) ALL_HID_DEVICES [ 1 ] . u s a g e _ p a g e = 1 ( 0 x1 ) ALL_HID_DEVICES [ 1 ] . u s a g e = 4 ( 0 x4 ) ALL_HID_DEVICES [ 1 ] . p a t h = \ \ ? \ h i d # v i d _ 0 4 4 f&p i d _ b 1 0 a #7&5 a04db4 &0&0000#{4 d1e55b2−f 1 6 f −11 c f −88cb −001111000030} ALL_HID_DEVICES [ 1 ] . s e r i a l _ n u m b e r = ? ALL_HID_DEVICES [ 1 ] . m a n u f a c t u r e r _ s t r i n g = T h r u s t m a s t e r ALL_HID_DEVICES [ 1 ] . p r o d u c t _ s t r i n g = T . 1 6 0 0 0M 9.2 HID related functions To access to HID devices, FlyWithLua uses a C-library HIDAPI from Alan Ott, Signal 11 Software. As Lua isn’t C, the function calls are different. 9.2.1 table, number = create_HID_table() a) table = A table to be filled with the complete info about all HID devices found. b) number = An integer representing the number of elements in the table. Every time Lua restarts, it will generate a table and a number as a global variable set like shown above. The code line to do this is: ALL_HID_DEVICES, NUMBER_OF_HID_DEVICES = create_HID_table() Page 65 of 122 9.2 HID related functions 9 ACCESS HID DEVICES If you plug a device in or out, the global variables won’t change. As most of the pre-defined variables, they are filled when Lua (re)starts. There are dynamic pre-defined variables like MOUSE_X, MOUSE_Y, SCREEN_WIDTH or SCREEN_HIGHT, but a dynamic filled variable consumes more CPU time. As pluggin in and out devices is not a typical behavior of simulator pilots during flight, the decision was not to listen to HID plugging events. The function create_HID_table() will replace all the enumeration stuff from the original HIDAPI library. Just examine the table ALL_HID_DEVICES if you need info generated by the following functions not implemented into FlyWithLua: hid_enumerate() and hid_free_enumerate() 9.2.2 device = hid_open( vendor_ID, product_ID ) a) device = A C-pointer to the object generated by the HIDAPI function to handle the HID device (a userdata variable). b) vendor_ID = The vendor ID of the device you want to open, an integer number. c) product_ID = The product ID of the device you want to open, an integer number. This will open the first device matching to your given IDs. Unlike the original library HIDAPI you can’t search for a serial number. This is a limitation to Lua, that can’t handle wchar strings. Use the next function, if you need to access a discrete device from a set of unique devices: 9.2.3 device = hid_open_path( path ) a) device = A C-pointer to the object generated by the HIDAPI function to handle the HID device (a userdata variable). b) path = The path representing the device you want to open. This string can be taken from the global variable like: ALL_HID_DEVICES[n].path. 9.2.4 hid_close( device ) a) device = The pointer to the devices, given by the opening function. This function closes a connection to a HID device. When Lua restarts, it automatically closes all open connections to HID devices. It’s not a good way to create code, but if you like you can forget to close the connections and let Lua do the work. Page 66 of 122 9.2 HID related functions 9 ACCESS HID DEVICES 9.2.5 hid_write( device, report ID, value, ... ) a) device = The pointer to the devices, given by the opening function. b) report ID = The report ID you want to write. For devices which only support a single report, this must be set to 0 (zero). c) value, ... = A set of integer values (range 0 to 255) to be written. FlyWithLua will automatically count the number of values you give to the function hid_write, unlike the original HIDAPI library, you can’t give a string plus number of elements. 9.2.6 nov, variable, ... = hid_read_timeout( device, nov wanted, milliseconds ) a) nov = A variable to store the number of values returned by the function. If the number is lower than the number of variables you want to be filled, the variables without a return value are filled with nil (typical to Lua). b) variable, ... = A list of variables to store the values in. c) device = The pointer to the devices, given by the opening function. d) nov wanted = The number of values you want to receive. e) milliseconds = The timeout in milliseconds the functions waits until receive process is canceled. You must provide a set of variables to store every byte of the returned message, not a single string variable. If you are familiar to the original HIDAPI library, this may be unusual to you. 9.2.7 nov, variable, ... = hid_read_timeout( device, nov wanted ) The same as above, but without a timeout. If the device declines to answer, the simulator will freeze. As this is normally not wanted, set the device connection to non-blocking: 9.2.8 success = hid_set_nonblocking( device, nonblock ) a) success = A variable to check if the execution was successfully. If the function does it’s job right, the return value will be 0 (zero), otherwise it returns -1. b) device = The pointer to the devices, given by the opening function. c) nonblock = Defines if the connection should be non-blocking (set this value to 1) or blocking (set this value to 0). Page 67 of 122 9.2 HID related functions 9 ACCESS HID DEVICES 9.2.9 nobw = hid_send_feature_report( device, report ID, value, ... ) a) nobw = The number of bytes written by the function. Use it to control, if the function completes it’s job as expected. The number of bytes should be equal to the number of values plus one for the report ID. In case of an error, it will return -1. b) device = The pointer to the devices, given by the opening function. c) report ID = The report ID you want to write. For devices which only support a single report, this must be set to 0 (zero). d) value, ... = A set of integer values (range 0 to 255) to be written. This function sends a feature report to the HID device. Time for an example? Let’s set the brightness of a Saitek BIP panel to 80%. 1 2 −− d e f i n e t h e b r i g h t n e s s we want t o s e t ( r a n g e 0 t o 1 0 0 ) B I P _ b r i g h t n e s s = 80 3 4 5 −− v e n d o r ID and p r o d u c t ID f o r a BIP = 0 x6a3 and 0 x b 4 e m y _ f i r s t _ B I P = h i d _ o p e n ( 0 x6a3 , 0 xb4e ) 6 7 8 9 10 11 12 13 −− c h e c k i f t h e d e v i c e was o p e n e d and s e t b r i g h t n e s s i f m y _ f i r s t _ B I P == n i l t h e n p r i n t ( "Oh , no ! We can ’ t f i n d o u r BIP p a n e l ! " ) else −− 0 xb2 = r e p o r t ID f o r b r i g h t n e s s h i d _ s e n d _ f e a t u r e _ r e p o r t ( m y _ f i r s t _ B I P , 0 xb2 , B I P _ b r i g h t n e s s ) end 14 15 16 −− c l o s e t h e c o n n e c t i o n t o t h e d e v i c e hid_close ( my_first_BIP ) As you can see, we ignored the return value ot the function hid_sent_feature_report(). This is allowed, and we control the function by observing the panel brightness with our eyes. 9.2.10 nobw = hid_send_filled_feature_report( device, report ID, nobts, value, ... ) a) nobw = The number of bytes written by the function. Use it to control, if the function completes it’s job as expected. The number of bytes should be equal to the number of values plus one for the report ID. In case of an error, it will return -1. b) device = The pointer to the devices, given by the opening function. c) report ID = The report ID you want to write. For devices which only support a single report, this must be set to 0 (zero). d) nobts = The number of bytes to send. If you give too less values, it will be filled up with zeros. e) value, ... = A set of integer values (range 0 to 255) to be written. Page 68 of 122 9.3 The Arcaze USB module 9 ACCESS HID DEVICES This function sends a feature report to the HID device like the one above. The only difference is, that it can fill up the data to send with zeros to a given lenght of data. 9.2.11 nobr, report ID, variable, ... = hid_get_feature_report( device, novw ) a) nobr = Number of bytes received (including the report ID). b) report ID = The report ID the device sends. c) variable, ... = A list of variables to store the values in. d) device = The pointer to the devices, given by the opening function. e) novw = Number of values we want to receive. This will not include the report ID! Never confuse the number of bytes with the number of values. If you want to receive a feature report containing four bytes of data, use this line of code: novr, report_id, one, two, three, four = hid_get_feature_report(my_device, 4) 9.3 The Arcaze USB module If you want to build your own home cockpit, but you don’t like the prebuild systems, why not building your own design. With a little USB board you are able to communicate with FlyWithLua using USB HID feature reports. I will demonstrate this with an example using the Arcaze USB board and his additional display driver board. Both are relative cheap hardware and you can order it here: http://simple-solutions.de Of course this will work with every other hardware too, so why should you use the Arcaze USB device? Because it’s very well documented: http://wiki.simple-solutions.de/en/products/Arcaze/Arcaze-USB/Arcaze-USB_SDK/ Feature_Report_Protocol In FlyWithLua there is a module to access the Arcaze device via USB HID feature reports. Even if you have absolutely no idea of feature reports, it’s very easy to use the Arcase with the included module. The first thing to do is loading the module, if you want to get access to it. require "arcaze" Start your script with this line, to load the module. Then you will have to connect to the device. Let’s say you have only one Arcaze connected, so we can do this: my_arcaze = arcaze.open_first_device() Page 69 of 122 9.3 The Arcaze USB module 9 ACCESS HID DEVICES Please remember to start all commands from a module with the name of the module and a dot between the name and the command. After that, the variable my_arcase will contain a handle to the device. If you have more than one device, this is the way you identify them. If the pins A1 and A2 are connected to a rotary encoder, we can read it’s relative position value. encoder_value = arcaze.read_encoders( my_arcaze ) Now we want to show the value of the encoder on the seven-segment-display. We connect a six element display to the display driver 32 board at channel »2A«. First we init the display driver: arcaze.init_display( my_arcaze, "2A", 15, 6) This little line of code shows the value: arcaze.show(my_first_arcaze, "2A", 0xff, encoder_value) Repeat the reading and writing every frame to play around with the little Arcaze board. Here are all functions the arcaze module provides: 9.3.1 device = arcaze.open_first_device() a) device = The pointer to the devices, or -1 if it fails to open the Arcaze USB. This will open the first Arcaze USB board FlyWithLua will find. If there is no Arcaze to open, the function will return -1 instead of the device handler. 9.3.2 A1, A2, A3, ..., B19, B20 = arcaze.read_pins( device ) a) A1, A2, A3, ..., B19, B20 = The variables to be filled with the pins values (0 or 1). b) device = The pointer to the devices, given by the opening function. This will fill up to 40 variables with the value 0 (zero) or 1 (one), depending on the input pins on the Arcaze Board. The first variable will be filled depending on the two pins labled A1, the second depending on A2 and so on. If the pin pair is connected (closed), than a value of 1 is returned, else the value is 0 (and the pin pair isn’t connected or open). Use the underscore to skip a value. If the pin pairs A3 and A4 are connected to a rotary encoder, and A1, A2 and A5 should control battery, avoinics and generator switch, than you can write a little script like this: Page 70 of 122 9.3 1 The Arcaze USB module 9 ACCESS HID DEVICES require " arcaze " 2 3 4 −− open t h e d e v i c e my_arcaze = a r c a z e . o p e n _ f i r s t _ d e v i c e ( ) 5 6 7 8 9 −− u s e D a t a R e f s t o c o n t r o l t h e s i m D a t a R e f ( " b a t t e r y " , " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " , " w r i t a b l e " ) D a t a R e f ( " a v o i n i c s " , " sim / c o c k p i t / e l e c t r i c a l / a v i o n i c s _ o n " , " w r i t a b l e " ) D a t a R e f ( " g e n e r a t o r " , " sim / c o c k p i t / e l e c t r i c a l / g e n e r a t o r _ o n " , " w r i t a b l e " , 0 ) 10 11 12 −− s e t t h e D a t a R e f s by r e a d i n g p i n p a i r s A1 , A2 and A5 do_every_frame ( " b a t t e r y , a v o i n i c s , _ , _ , g e n e r a t o r = a r c a z e . r e a d _ p i n s ( my_arcaze ) " ) 9.3.3 ADC1, ADC2, ADC3, ADC4, ADC5, ADC6 = arcaze.read_ADCs( device ) a) ADC1, ADC2, ADC3, ADC4, ADC5, ADC6 = The variables to be filled with the six ADC values (range 0 to 4095). b) device = The pointer to the devices, given by the opening function. Read out the six analog values as 12-bit integer values. This is used to connect potis to XPlane. 9.3.4 E1, E2, E3, ..., E19, E20 = arcaze.read_encoders( device ) a) E1, E2, E3, ..., E19, E20 = The variables to be filled with the encoder values (range 0 to 65535). b) device = The pointer to the devices, given by the opening function. Encoders must be connected to pairs of pin pairs. The first encoder E1 must be connected to the pin pairs A1 and A2, the last encoder E20 must be connected to the pin pairs B19 and B20. The rotary encoder logic of the Arcaze USB board will produce a relative value between 0x0000 and 0xffff. It will count all positive and negative edges of both channels, so normally it makes 4 steps for each turn. The values can start whereever they want, so don’t expect 0x0000 when the device is pluged into the USB port. See the example script little arcaze radio.lua to see the usage of rotary encoders in detail. 9.3.5 arcaze.set_all_pins_for_input( device ) a) device = The pointer to the devices, given by the opening function. All 40 pin pairs can be used as inputs or outputs. This function will set all pin pairs to input mode. Use it before defining the output pins, to make tabula rasa. Page 71 of 122 9.3 The Arcaze USB module 9 ACCESS HID DEVICES 9.3.6 arcaze.set_pin_direction( device, pin, direction ) a) device = The pointer to the devices, given by the opening function. b) pin = The number of the pin pair to be set. Pin pair A1 is 0, B20 is 39. c) direction = The direction to be set. Can be "input" or 0 for input direction, else use "output" or 1 for output direction. This will set the direction of a pin pair. Please remember that the number starts with 0, similar to X-Plane’s joystick button numbers, as this is the standard counting of pure C code. You are operating really low level here! 9.3.7 arcaze.set_pin( device, pin, value ) a) device = The pointer to the devices, given by the opening function. b) pin = The number of the pin pair to be set. Pin pair A1 is 0, B20 is 39. c) value = The value to be set. Can be "off" or 0 to open the output, else use "on" or 1 to close the output pins. This will set a pin pair. Please remember that the number starts with 0, similar to X-Plane’s joystick button numbers, as this is the standard counting of pure C code. You should never set a pin that is defined as an input pin! 9.3.8 arcaze.init_display( device, address, intensity, scan_limit ) a) device = The pointer to the devices, given by the opening function. b) address = The address where the display unit is connected to, for example "1a" (range "1a" to "4b", a string value). The address is printed onto the display driver board. c) intensity = The value for the LED intensity (range 0 to 15). d) scan_limit = The number of digits to be filled (range 4 to 8). Use this once to initialize the display driver. Page 72 of 122 9.3 The Arcaze USB module 9 ACCESS HID DEVICES 9.3.9 arcaze.init_display( device, address ) a) device = The pointer to the devices, given by the opening function. b) address = The address where the display unit is connected to, for example "1a" (range "1a" to "4b", a string value). The address is printed onto the display driver board. If you let the intensity and scan limit away, FlyWithLua will guess an intensity of 15 (full) and 8 digits. The display will show some nonsense data until you use this function: 9.3.10 arcaze.show( device, address, mask, value_string ) a) device = The pointer to the devices, given by the opening function. b) address = The address where the display unit is connected to, for example "1a" (range "1a" to "4b", a string value). The address is printed onto the display driver board. c) mask = A bit mask of the digits to be set (range 0x00 to 0xff). If unsure, use 0xff. d) value_string = The string to be displayed. The mask will define the digits to be manipulated as a bit mask. If you say 0xff as the mask, all eight digits will be set. The value string is a string containing a numeric value. A decimal point "." can be used and the negative sign "-". A space will left the digit blank. All ports "1a", "2a", "3a" and "4a" are filled from right to left, so you can do this: arcaze.show( my_arcaze, "1a", 0xff, "-4") But on the left-filled ports, you should write a code like this: arcaze.show( my_arcaze, "1b", 0xff, " -4") This code has six spaces in front of the substring "-4", to be shown on the right side of the display. See the example script little arcaze radio.lua to see the usage of seven segment displays. Page 73 of 122 10 CLASSIC AND MODERN MODE 10 Classic and modern mode Definition: All scripts are »modern type« scripts, until you use one of the functions XPLMSetDatai(), XPLMSetDataf(), XPLMSetDatad(), XPLMSetDatavi() or XPLMSetDatavf(). If a script uses one of these functions, in FlyWithLua version 2.1 the use of dataref(), get(), set() and set_array() is prohibited. Since version 2.2.1 you can mix them without any error. The command XPLMSetDatab() is not possible in FlyWithLua, use a modern script file, if you need access to string DataRefs. If performance is not an issue, you can try get() and set(). Remember that the most important strings are in the predefined variables PLANE_ICAO, PLANE_TAILNUMBER and XSB_METAR. All modern script files work like a big PLC. You define the DataRefs you want as input and/or output, and FlyWithLua handles all the value transfer from/to the simulator. No worry about different types, clean code with perfect readability. But all the comfort you get will cost a little bit of performance. If you are a performance fetishist wanting to squeeze out your CPU, but you only have poor skills in C/C++, then you will probably want to write »classic mode« script files. Or in shorter words, classic mode leaves away the comfort and gives DataRef handling to your responsibility. A big chance to get a super fast, ugly to read script code. 10.1 Reading classic functions To pull a value out of the simulator, you must use one of the following functions, depending on the type of the DataRef. 10.1.1 variable = XPLMGetDatai( DataRef ) a) variable = The lua variable, you want the value to be pushed in. b) DataRef = The reference(!) of the DataRef you want to read out. Reading out an integer DataRef. If the DataRef is not readable as an integer, the simulator may blow up without a warning! Page 74 of 122 10.1 Reading classic functions 10 CLASSIC AND MODERN MODE 10.1.2 variable = XPLMGetDataf( DataRef ) a) variable = The lua variable, you want the value to be pushed in. b) DataRef = The reference(!) of the DataRef you want to read out. Reading out a float DataRef. If the DataRef is not readable as a float, the simulator may blow up without a warning! 10.1.3 variable = XPLMGetDatad( DataRef ) a) variable = The lua variable, you want the value to be pushed in. b) DataRef = The reference(!) of the DataRef you want to read out. Reading out a double DataRef. If the DataRef is not readable as a double, the simulator may blow up without a warning! 10.1.4 table = XPLMGetDatavi( DataRef, inIndex, inMax ) a) table = The lua table, you want the value to be pushed in. b) DataRef = The reference(!) of the DataRef you want to read out. c) inIndex = The index where you want to start. d) inMax = The number of values you want to access. Reading out an integer DataRef array. If the DataRef is not readable as an integer array, or are you using index values outside the range of the DataRef, the simulator may blow up without a warning! The lua table will store the values with correct index seen from the world of XPlane. The first value can be indexed 0 (zero), if you start from the first entry of an array DataRef. This is cool for C/C++ code, but uncool for Lua. So avoid ipairs() on tables made by XPLMGetDatavi() commands. 10.1.5 table = XPLMGetDatavf( DataRef ) a) table = The lua table, you want the value to be pushed in. b) DataRef = The reference(!) of the DataRef you want to read out. c) inIndex = The index where you want to start. d) inMax = The number of values you want to access. Page 75 of 122 10.1 Reading classic functions 10 CLASSIC AND MODERN MODE The same as above, but for float array DataRefs. 10.1.6 userdata variable = XPLMFindDataRef( DataRef Name ) a) userdata variable = The lua variable, you want the reference to be pushed in. b) DataRef name = The name of the DataRef you want to know as a string. The Lua variable will be filled with a »userdata«. This means, that you can’t do anything with it’s value. It’s only needed for the XPLM functions. 10.1.7 datatype variable = XPLMGetDataRefTypes( DataRef reference ) a) datatype variable = The lua variable, you want the DataRef type to be pushed in. b) DataRef reference = The reference of the DataRef you want to know it’s type. The Lua variable will be filled with an integer, showing the type of the DataRef. The DataRefs must be given by it’s reference, not by it’s name. Page 76 of 122 10.1 Reading classic functions 10 CLASSIC AND MODERN MODE This is a little example: Forget the rest of the code and look at lines 8 to 25. They show how to handle an array DataRef by reading and manipulation the battery. (In the more modern versions of X-Plane there is in fact more than one battery! But most planes only interact with the first battery.) As you can see in line 8, the following functions will read references instead of strings. It is not possible to write: t = XPLMGetDatavi("sim/cockpit/electrical/battery_array_on", 0, 4) You will crash the simulator if you try it. To find the right argument, you first have to use XPLMFindDataRef() - a very slow function. If you like performance, use it only as often as needed. In line no. 9 you force Lua to print batref, the variable holding the reference to the battery DataRef. Lua says »FlyWithLua Error: nothing to say.« because a reference is strored as a userdata, and can not be converted into a string, so the automatic converter results in nil and the print() function can’t print it. Page 77 of 122 10.2 Writing classic functions 10 CLASSIC AND MODERN MODE 10.2 Writing classic functions To give values back to X-Plane, use one of these functions: 10.2.1 XPLMSetDatai( DataRef, variable or value) a) DataRef = The reference(!) of the DataRef you want to write into. b) variable or value = The lua variable, carrying the value you want to write, or a value directly. Use this for integer DataRefs. 10.2.2 XPLMSetDataf( DataRef, variable or value) a) DataRef = The reference(!) of the DataRef you want to write into. b) variable or value = The lua variable, carrying the value you want to write, or a value directly. Use this for float DataRefs. 10.2.3 XPLMSetDatad( DataRef, variable or value) a) DataRef = The reference(!) of the DataRef you want to write into. b) variable or value = The lua variable, carrying the value you want to write, or a value directly. Use this for double DataRefs. 10.2.4 XPLMSetDatavi( DataRef, table, inIndex, inMax ) a) DataRef = The reference(!) of the DataRef you want to write into. b) table = The lua table, carrying the values you want to write, with correct indexes! c) inIndex = The index where you want to start. d) inMax = The number of values you want to access. Use this for integer array DataRefs. Page 78 of 122 11 THE LUA WAY TO ACCESS DATAREFS 10.2.5 XPLMSetDatavf( DataRef, table, inIndex, inMax ) a) DataRef = The reference(!) of the DataRef you want to write into. b) table = The lua table, carrying the values you want to write, with correct indexes! c) inIndex = The index where you want to start. d) inMax = The number of values you want to access. Use this for float array DataRefs. 11 The Lua way to access DataRefs Classic code can be fine and super fast, but it is hard to read (and write). And all the methods to access DataRefs we know until now aren’t good Lua code. The classic approach comes from the SDK’s C code, so it fakes C-style to Lua. The modern style fakes a PLC and transfers DataRefs to Lua variables. A clever code, but a little bit confusing when we want to access array DataRefs. And it consumes more CPU power. 11.1 A magic metatable The Lua way of life is to use a metatable. Metatables are one of the primary features making Lua unique to other programming languages. FlyWithLua respects this of course, and provides a magic metatable DATAREF_META_TABLE. The magic metatable is defined in the file FlyWithLua.ini: 42 43 44 45 −− c r e a t e a magic m e t a t a b l e DATAREF_META_TABLE = {} DATAREF_META_TABLE . _ _ i n d e x = f u n c t i o n ( t , key ) r e t u r n p e e k ( t . r e f e r e n c e , t . r e f t y p e , key ) end DATAREF_META_TABLE . _ _ n e w i n d e x = f u n c t i o n ( t , key , v a l u e ) poke ( t . r e f e r e n c e , t . r e f t y p e , key , v a l u e ) end There are two undocumented functions in this metatable, peek() and poke(). Don’t care about them. You do not need these functions, if you want to write a Lua-style script. More important is, how the metatable works. It expects two values, reference and reftype as elements of the table it is attached to. Let’s create an empty table, add the elements needed by the metatable and attach the metatable to the table. In this example, we will access the well known battery DataRef. 1 2 3 4 b a t t e r y = {} b a t t e r y . r e f e r e n c e = XPLMFindDataRef ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ a r r a y _ o n " ) b a t t e r y . r e f t y p e = XPLMGetDataRefTypes ( b a t t e r y . r e f e r e n c e ) s e t m e t a t a b l e ( b a t t e r y , DATAREF_META_TABLE ) Page 79 of 122 11.1 A magic metatable 11 THE LUA WAY TO ACCESS DATAREFS Now we test the code (write through XSquawkBox’s input line). >print( battery[0] ) Lua takes a look inside the table battery, searching for an element indexed with 0. The table itself does not provide an element with this index. Lua knows, that a metatable is attached to the table battery, so it looks into the metatable for an element with the given index. As there is no element DATAREF_META_TABLE[0], Lua looks for an element __index inside the metatable. An element __index can be found as a function, and Lua now calls it this way: DATAREF_META_TABLE.__index( battery, 0 ) And the function finally returns: peek( battery.reference, battery.reftype, 0 ) This is resolved to 0, if the first battery is off, or to 1 if it is on. Because of peek() and poke() ignoring the index, if the DataRef points to a single value instead of an array, you can do the same magic access with all DataRefs X-Plane provides. The only circumstance is to give a dummy index of 0 (zero) to all non-array DataRefs. And if you give an index to a string DataRef, it will begin with the character at the index position (starting at position 0, not 1). As peek() and poke() are C-style functions, all indexes will start at 0 (zero), not 1 (like Lua starts array indexes). This behavior was implemented, because of X-Plane’s DataRefs will always use 0 for the first index. To shorten the code, FlyWithLua provides a function to declare a magic table in one single line of code: 11.1.1 table = dataref_table( DataRef ) a) table = The lua table, you want to contain your magic. b) DataRef = The name of the DataRef as a string. Creating a new magic table to access the given DataRef, auto detecting the type of the DataRef. The function dataref_table() initializes a direct access to the DataRef. This causes a conflict to the PLC8 behavior of FlyWithLua. As a consequence dataref_table() always forces the script to be classic code. Never use dataref() and dataref_table() together in the same script file! 8 See section »Understanding PLCs« for more info. Page 80 of 122 12 MANAGE YOUR JOYSTICKS 12 Manage your joysticks FlyWithLua was made to replace the plugin Button2DataRef, a very popular plugin to manage your button and axis assignments. You can completely replace it with FlyWithLua 2.0. This section of the quick manual will show a step-by-step creating process of a joystick config script. 12.1 Get a basic configuration The first step is to load your most used plane into the simulator, click into the joystick configuration menu of X-Plane, and setup everything as you want it to be. Then restart X-Plane (with the same plane). Every time X-Plane is shut down, it saves your last settings in a preference file: «place where you store the sim»/X-Plane 10/Output/preferences/X-Plane.prf This file keeps all joystick settings for the next run in a non Lua friendly way: 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 ... _joy_AXIS_use97 0 _joy_AXIS_use98 0 _joy_AXIS_use99 0 _joy_BUTN_use0 sim / a u t o p i l o t / f d i r _ o n _joy_BUTN_use1 sim / none / none _joy_BUTN_use2 sim / view / c h a s e _joy_BUTN_use3 sim / view / c h a s e _joy_BUTN_use4 sim / g e n e r a l / z o o m _ i n _ f a s t _joy_BUTN_use5 sim / g e n e r a l / z o o m _ o u t _ f a s t _joy_BUTN_use6 sim / none / none ... You could copy&paste the values out of this code, but there is an easier way. FlyWithLua translates X-Plane’s preferences saved inside the X-Plane.prf file into a Lua readable script code. The code is written to a file named: «sim storage»/X-Plane 10/Resources/plugins/FlyWithLua/initial_assingments.txt And yes, it’s a »txt« file, not a script file, because it is made for copy&paste only. You will find the lines above translated into: 12 13 14 15 16 17 18 19 ... clear_all_button_assignments () set_button_assignment ( (0∗40) + set_button_assignment ( (0∗40) + set_button_assignment ( (0∗40) + set_button_assignment ( (0∗40) + set_button_assignment ( (0∗40) + ... 0, 2, 3, 4, 5, " sim / a u t o p i l o t / f d i r _ o n " ) " sim / view / c h a s e " ) " sim / view / c h a s e " ) " sim / g e n e r a l / z o o m _ i n _ f a s t " ) " sim / g e n e r a l / z o o m _ o u t _ f a s t " ) Page 81 of 122 12.2 Define your sticks 12 MANAGE YOUR JOYSTICKS FlyWithLua ignores all lines defining axis or buttons to nothing. This is done by the two function calls clear_all_axis_assignments() (in line 5) and clear_all_button_assignments(). This prevents you from coding 1600 button assignments and 100 axis assignments. Write a new script file into the Scripts folder and fill it with a copy of the automatic generated configuration code. 12.2 Define your sticks As you can see, FlyWithLua has made all button numbers to mathematic exercises. Why that? You can easily see that all your joysticks start with it’s first button numbered by a multiple of 40. FlyWithLua prepared the code to use a nice standard function of text editors: »replace«. Go to the beginning of your configuration script and type in a line like this: LeftHandSteeringStick = 0 Then use your editor to replace all »(0*40)« with »LeftHandSteeringStick«. The result will look similar to this code: 12 13 ... LeftHandSteeringStick = 0 14 15 16 17 18 19 20 21 clear_all_button_assignments () set_button_assignment ( LeftHandSteeringStick set_button_assignment ( LeftHandSteeringStick set_button_assignment ( LeftHandSteeringStick set_button_assignment ( LeftHandSteeringStick set_button_assignment ( LeftHandSteeringStick ... + + + + + 0, 2, 3, 4, 5, " sim / a u t o p i l o t / f d i r _ o n " ) " sim / view / c h a s e " ) " sim / view / c h a s e " ) " sim / g e n e r a l / z o o m _ i n _ f a s t " ) " sim / g e n e r a l / z o o m _ o u t _ f a s t " ) Now you are prepared to buy a new USB device. If your stick in your left hand starts at button number 160 after you plug in an other joystick device, you will only have to change one line in your code (line 13 in the example above). This is very cool if you are tired of still reconfiguring your setup, while all the other members of the LAN party starts flying. 12.3 Define type specific assignments Next step is to define settings, where plane types other than your favorite one need to be reconfigured. If your most used plane is a C172, and you want to fly the MD902 Explorer and a Bell 206 too, define a »helicopter class«. The helicopter needs all »little helpers« like nullzone, sensitivity or augment at value 0.0 - to give maximum control into your hand. And of course the throttle has to be replaced by a collective. Page 82 of 122 12.4 134 135 136 137 138 139 140 141 142 143 144 145 146 Lua for cockpit builders 12 −− a l l h e l i c o p t e r w i l l g e t a u n i q u e s e t t i n g a s d e f a u l t function set_helicopter_assignments () s e t _ a x i s _ a s s i g n m e n t (12 , " c o l l e c t i v e " , " normal " ) s e t ( " sim / j o y s t i c k / j o y s t i c k _ p i t c h _ n u l l z o n e " , s e t ( " sim / j o y s t i c k / j o y s t i c k _ r o l l _ n u l l z o n e " , s e t ( " sim / j o y s t i c k / j o y s t i c k _ h e a d i n g _ n u l l z o n e " , s e t ( " sim / j o y s t i c k / j o y s t i c k _ p i t c h _ a u g m e n t " , s e t ( " sim / j o y s t i c k / j o y s t i c k _ r o l l _ a u g m e n t " , s e t ( " sim / j o y s t i c k / j o y s t i c k _ h e a d i n g _ a u g m e n t " , s e t ( " sim / j o y s t i c k / j o y s t i c k _ p i t c h _ s e n s i t i v i t y " , s e t ( " sim / j o y s t i c k / j o y s t i c k _ r o l l _ s e n s i t i v i t y " , s e t ( " sim / j o y s t i c k / j o y s t i c k _ h e a d i n g _ s e n s i t i v i t y " , end 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 MANAGE YOUR JOYSTICKS ) ) ) ) ) ) ) ) ) After these lines of code, you can bind individual Helicopters (or Planes) to your aircraft types: 156 157 158 159 −− MD902 E x p l o r e r ( EXPL ) i f PLANE_ICAO == "EXPL" t h e n set_helicopter_assignments () end 160 161 162 163 164 −− B e l l 206 D r e a m f o i l i f PLANE_ICAO == " B06 " t h e n set_helicopter_assignments () end If a plane has no ICAO code, go into the Planemaker and correct it. Or you can use the predefined variable PLANE_TAILNUMBER instead of PLANE_ICAO, if it is unique to this individual aircraft. The work has to be done manually, there is no automatic way to define classes and bind aircrafts to them. But it is easy to copy some lines and change the values. If you redefine a button command, use the »button advanced« menu of X-Plane. You can copy&paste the command string. Finish! From now on you will always start with the right setting in all aircrafts you cover by your script. 12.4 Lua for cockpit builders If you build a home cockpit like with Button2DataRef, you may want to bind buttons to DataRefs. This is different to handle, if you are used to write B2D code, but not too complex. FlyWithLua offers two functions, button() and last_button(). The function button() needs the number of the button as its argument and delivers true if the button is pressed, else you get false returned. The function last_buuton() gives the state one frame ago. Let’s compare some code. First you get the old Button2DataRef code, followed by the Lua code, doing exactly the same. Page 83 of 122 12.4 Lua for cockpit builders 12 MANAGE YOUR JOYSTICKS This is a typical configuration of a switch firing into a joystick button. If it is pressed (on), the battery should be on, else it should be set to off: #IF BUTTON 13 SWITCHES FROM 1 TO 1 SET sim/cockpit/electrical/battery_on TO 1 #IF BUTTON 13 SWITCHES FROM 0 TO 0 SET sim/cockpit/electrical/battery_on TO 0 1 2 3 4 5 i f b u t t o n ( 1 3 ) then s e t ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " , 1 ) else s e t ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " , 0 ) end Next we want to release the parking brake if button 166 is released: #IF BUTTON 166 SWITCHES FROM 1 TO 0 SET sim/flightmodel/controls/parkbrake TO 0 1 2 3 i f n o t b u t t o n ( 1 6 6 ) and l a s t _ b u t t o n ( 1 6 6 ) t h e n s e t ( " sim / f l i g h t m o d e l / c o n t r o l s / p a r k b r a k e " , 0 ) end Remember to put all your »button code« into a function called every frame. This can be done in this way: 1 2 3 4 5 function parkbrake_button () i f n o t b u t t o n ( 1 6 6 ) and l a s t _ b u t t o n ( 1 6 6 ) t h e n s e t ( " sim / f l i g h t m o d e l / c o n t r o l s / p a r k b r a k e " , 0 ) end end 6 7 do_every_frame ( " parkbrake_button ( ) " ) Or shorter by using the »[[« and »]]« string delimiters: 1 2 3 4 do_every_frame ( [ [ i f n o t b u t t o n ( 1 6 6 ) and l a s t _ b u t t o n ( 1 6 6 ) t h e n s e t ( " sim / f l i g h t m o d e l / c o n t r o l s / p a r k b r a k e " , 0 ) end ] ] ) It will produce an useless empty line of code inside the do_every_frame() routine, but this is not a performance issue, as Lua will interpret a byte-code. The byte-code is compiled on the fly and does not contain empty lines or comments slowing down the interpreter. The next bad thing different to Button2DataRef is, that the function set() is much slower than SET in B2D code. If you want the speed of Button2DataRef, define a DataRef first. A fast code looks like this: 1 2 3 4 5 d a t a r e f ( " x p _ p a r k b r a k e " , " sim / f l i g h t m o d e l / c o n t r o l s / p a r k b r a k e " , " w r i t a b l e " ) do_every_frame ( [ [ i f n o t b u t t o n ( 1 6 6 ) and l a s t _ b u t t o n ( 1 6 6 ) t h e n xp_parkbrake = 0 end ] ] ) Developers first law: Good code can prevent your simulator becoming a flip-book. As a consequence never use set(), set_array() or get() in code looping every frame. Page 84 of 122 13 UNDERSTANDING PLCS 13 Understanding PLCs A programmable logic controller will do his work in endless cycling steps. These steps will loop: a) Read input values. b) Do the calculations. c) Write the output values. The »modern part« of FlyWithLua works like a PLC. Let’s have a look into an example file: 1 2 3 4 5 d a t a r e f ( " x p _ b a t t e r y " , " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " , " w r i t a b l e " ) d a t a r e f ( " r o _ b a t t e r y " , " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " ) xp_battery = 1 print ( xp_battery ) print ( ro_battery ) You will get a warning in Log.txt, but the script will run. Turn off the battery and reload the script. You will get: 1 0 The first printed result is what we expect. The battery was turned on, so after this we get a value of 1 when querying the actual value of the battery. But is it really the actual value of the battery? No, it is the actual value of the variable connected to the battery. Remember the PLC steps. We are in the calculating step, and as a fact of this, the value isn’t transfered from the variable xp_battery to the DataRef "sim/cockpit/electrical/battery_on" yet. If all calculations are finished, FlyWithLua (who is acting like a PLC) goes to the writing step, pushing all writable variable-DataRef bindings towards X-Plane. (After that it copies all DataRefs to there variables, independent of the writable or readonly state, and comes to the next calculation.) So the second output is 0 and not 1, as the variable ro_battery was filled with 0 one PLC step before (you turned off the battery before reloading the script). If you do not »think as a PLC does«, you might get unexpected results from your own code. There are three possible solution. The first is to learn to think like a PLC. Page 85 of 122 13 UNDERSTANDING PLCS But if you don’t like PLCs logic, you can use the functions get(), set() and set_array(). This is the second solution. Look at the screen shot some pages before. The program starts: 1 2 3 d a t a r e f ( " b a t t e r y " , " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " , " w r i t a b l e " ) s e t ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " , 1 ) print ( battery ) The result of this code is (reloading again with batteries off): 1 Hey! Is this real? Didn’t we say battery was filled with the value of 0 during the pulling step before calculation? Sure, we can try it out by adding a line of code: 1 2 3 4 d a t a r e f ( " b a t t e r y " , " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " , " w r i t a b l e " ) print ( battery ) s e t ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " , 1 ) print ( battery ) The output is now: 0 1 Strange! The reason to this behavior is, that set() will freeze the calculation, force a push step, does its own manipulation, forces a pull step and gets back to the calculation point, where it freezes the code. This doesn’t even sounds complicated, it is enormous time consuming. So if you are a friend of performance, use set() only in time-uncritical situations. The same behavior is shown by get() and set_array(). Coming to the third solution to escape from PLC logic. You can write classic mode script files. If you do all the XPLM voodoo on your own, everything goes directly from or to the simulator. But you will have to handle references and types. And you will get no error handling from FlyWithLua. To be able to handle all three coding styles in a wildly mix of multiple script files, FlyWithLua needs two functions to switch his mode, begin_classic_mode() and end_classic_mode(). Please do not care about these two functions in your scripts. Just write script files who use only one mode, a style mix of dataref() and get()/set() (modern mode), or clean XPLM based code (classic mode). FlyWithLua will automatically insert the functions to change the mode if needed. You may check this by writing a debug file to disk. As switching between modes will consume CPU, sort your scripts. You can start all modern style scripts with an uppercase letter and all classic style scripts with a lowercase letter. This will force only one switch, the minimum if you use both styles. Or name scripts written in one style like a lock and key service in a telephone directory (»AAAAAA_my_lttle_script.lua«). Page 86 of 122 14 BASIC KNOWLEDGE ABOUT DATAREFS 14 Basic knowledge about DataRefs If you are unfamiliar to DataRefs, you should read this section, if you already know everything about DataRefs, you can skip it. DataRefs are the main connection to X-Plane’s bowels. Most of them are writable, giving you an enormous feature to tweak the simulator. But you need to know how they work and how to access them. 14.1 What are DataRefs? As your Lua scripts uses global variables, X-Plane itself has it’s own »variables«. But plugins can’t see them, as they are not published to the plugins in a direct way. The plugin SDK/API offers C/C++ code to access the »inner heart« of X-Plane by DataRefs. Definition: A DataRef is a reference to a place in memory, where X-Plane stores a special value. Each DataRef consists of a »folder/file-type« string giving the DataRef it’s name and the reference itself. 14.2 Find the right DataRefs If you want to find a DataRef to be used by your script, first take a look onto this site: http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html This is the official website representing all DataRefs X-Plane offers. Always check this site and use the search function of your web browser to look out for a good DataRef to use. If you found an useful DataRef, do not stop your search. Sometimes there are more than one DataRefs pointing to the same (logical) value. For example there is a DataRef »sim/cockpit/ electrical/battery_on«. This DataRefs allows you to turn on/off the battery. But there is another DataRef »sim/cockpit/electrical/battery_array_on«. The first DataRef only points to the first battery of the aircraft, but the second DataRef points to an array of up to eight batteries. Most aircrafts will only simulate one battery, so it’s enough to control it by the first DataRef. If an aircraft uses more than one battery, your script may result in mysterious behavior. Take a look onto the columns of the official DataRef website. The third column shown in what version of X-Plane the DataRef is present. If you look onto the example above, you can see that all planes since X-Plane 6.60 will provide access to only one main battery, but since version 8.20 there are up to eight batteries. Page 87 of 122 14.3 Accessing DataRefs 14 BASIC KNOWLEDGE ABOUT DATAREFS If you read out the value behind »sim/cockpit/misc/has_radar« using X-Plane 9.70, and you send it to a friend using X-Plane 10.10r3, he might be surprised by the beauty of your code. 14.3 Accessing DataRefs A very important issue is, that the value in memory the DataRef points to, has a special byte structure. The DataRef itself only points to the first byte, no matter what structure (or length) the value has. It is your part to access the value the right way. You can see the byte structure in the second column of the official DataRef list. If you find only int, float or double, it is a single value behind the DataRef. If there are brackets behind the type, it is an array of this type. Only look into the second column to examine the type, not in the name of the DataRef. For example »sim/cockpit/electrical/battery_array_on« is an array of eight integer values, but »sim/weather/cloud_type[2]« is only one single integer value, not an array of 2 values. If you want access to a DataRef, you will first have to find out the reference. Use the function XPLMFindDataRef() to get the reference. For example: battery_ref = XPLMFindDataRef("sim/cockpit/electrical/battery_array_on") Now you have the reference address stored in a variable named battery_ref. If you want to turn on the third battery, you must access the value stored behind the DataRef like this: 1 2 −− f i n d o u t t h e r e f e r e n c e b a t t e r y _ r e f = XPLMFindDataRef ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ a r r a y _ o n " ) 3 4 5 −− f i l l a t a b l e w i t h t h e v a l u e s f r o m t h e D a t a R e f b a t t e r y _ a r r a y = XPLMGetDatavi ( b a t t e r y _ r e f , 0 , 8 ) 6 7 8 −− c h a n g e t h e v a l u e r e p r e s e n t i n g t h e t h i r d b a t t e r y battery_array [2] = 1 9 10 11 −− w r i t e t h e v a l u e s b a c k t o X−P l a n e XPLMSetDatavi ( b a t t e r y _ r e f , b a t t e r y _ a r r a y , 0 , 8 ) In this code, you change battery_array[2] to turn on the third battery. All of X-Plane’s arrays start at level 0 (like C/C++ does), not level 1 (like Lua does). The last value is battery_array[7]. In the functions XPLMGetDatavi() and XPLMSetDatavi() your arguments are »0, 8«, you start at position 0 and get/set 8 values. You can get all batteries into one table, do your changes to them, and at the last step write them all back to X-Plane. Between getting and setting the values can be as much of code as you like. But you do not need to get/set them all. If you only want to access to the third value, write a code like this instead: Page 88 of 122 14.4 1 2 Observe the DataRef 14 BASIC KNOWLEDGE ABOUT DATAREFS −− f i n d o u t t h e r e f e r e n c e b a t t e r y _ r e f = XPLMFindDataRef ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ a r r a y _ o n " ) 3 4 5 −− f i l l a t a b l e w i t h t h e v a l u e s f r o m t h e D a t a R e f b a t t e r y _ a r r a y = XPLMGetDatavi ( b a t t e r y _ r e f , 2 , 1 ) 6 7 8 −− c h a n g e t h e v a l u e r e p r e s e n t i n g t h e t h i r d b a t t e r y battery_array [2] = 1 9 10 11 −− w r i t e t h e v a l u e s b a c k t o X−P l a n e XPLMSetDatavi ( b a t t e r y _ r e f , b a t t e r y _ a r r a y , 2 , 1 ) Keep in mind that you still use a table, even if you want to access a single value. If the DataRef points to an array, you must use the »array-function«. If you use the modern code to access, FlyWithLua will always translate into single values. The same code written in modern style will look like this: 1 2 −− d e f i n e t h e c o n n e c t i o n t o t h e t h i r d b a t t e r y d a t a r e f ( " t h i r d _ b a t t e r y _ o n " , " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ a r r a y _ o n " , " w r i t a b l e " , 2 ) 3 4 5 −− c h a n g e t h e v a l u e o f t h e t h i r d b a t t e r y third_battery_on = 1 The modern code style is shorter, but it is impossible to get/set complete arrays at once to/from a Lua table. 14.4 Observe the DataRef You should observe the behavior of a DataRef with the tool DataRef Editor. Sometimes a DataRef (to be exact: the value behind it) will not behave as you aspect it. Let’s have an example. You want to turn on the carb heat if there is ice in the engine. Your code is this: 1 2 d a t a r e f ( " c a r b _ h e a t " , " sim / c o c k p i t 2 / e n g i n e / a c t u a t o r s / c a r b _ h e a t _ r a t i o " , " w r i t a b l e " , 0 ) d a t a r e f ( " i c i n g " , " sim / f l i g h t m o d e l / e n g i n e / ENGN_crbice " , " r e a d o n l y " ) 3 4 5 6 7 8 9 10 11 function automatic_carb_heat () −− i f c a r b i s i c i n g t h e n s w i t c h h e a t on i f ( i c i n g == 1 ) t h e n carb_heat = 1 else carb_heat = 0 end end Everything seems to be fine, FlyWithLua didn’t produce an error message and all other stuff works well. Only your function automatic_carb_heat() will not react to ice inside the engine. Let’s observe the value of sim/flightmodel/engine/ENGN_crbice with the DataRef Editor. Page 89 of 122 14.4 Observe the DataRef 14 BASIC KNOWLEDGE ABOUT DATAREFS Your code expects an integer value (yes = 1, no = 0), like the third_battery_on in the example above. But sim/flightmodel/engine/ENGN_crbice is an array of float values, representing a ratio of icing. The values start at zero, but they will grow up slowly. Before they reach a value of 1.0, your plane usually crashed into ground. If you absolutely dislike ice in your engine, change the code to: 1 2 d a t a r e f ( " c a r b _ h e a t " , " sim / c o c k p i t 2 / e n g i n e / a c t u a t o r s / c a r b _ h e a t _ r a t i o " , " w r i t a b l e " , 0 ) d a t a r e f ( " i c i n g " , " sim / f l i g h t m o d e l / e n g i n e / ENGN_crbice " , " r e a d o n l y " ) 3 4 5 6 7 8 9 10 11 function automatic_carb_heat () −− i f c a r b i s i c i n g t h e n s w i t c h h e a t on i f ( i c i n g > 0) then carb_heat = 1 else carb_heat = 0 end end As the value for the carb heat is a ratio too, but you are firing all or nothing, you may be too power consuming. In times fuel gets more and more expansive, it might be better to give more power to the carb heat, if there is more ice in the engine. Let your fantasy create a nice mathematic model for the relationship level of ice —> level of carb heat. 1 2 d a t a r e f ( " c a r b _ h e a t " , " sim / c o c k p i t 2 / e n g i n e / a c t u a t o r s / c a r b _ h e a t _ r a t i o " , " w r i t a b l e " , 0 ) d a t a r e f ( " i c i n g " , " sim / f l i g h t m o d e l / e n g i n e / ENGN_crbice " , " r e a d o n l y " ) 3 4 5 6 7 8 9 10 11 function automatic_carb_heat () −− i f c a r b i s i c i n g t h e n s w i t c h h e a t on i f ( i c i n g > 0) then carb_heat = 0.25 + icing ∗ 3/4 else carb_heat = 0 end end Now you are using all benefits of float ratio values. The world is more than black/white, there are nearly9 unlimited shades of gray. 9 As a C/C++ float value is stored into a limited number of bytes, it represents a collection of discrete values. Page 90 of 122 15 TAKE LUA INTO CONSIDERATION 15 Take Lua into consideration There are some specials Lua offer, a script developer needs to know. In this section, we will show some common mistakes/problems in scripting. If you are familiar with Lua, skip this section. We will write an example file, not included in the FlyWithLua package. To follow this text, turn on X-Plane and start a text editor. Then copy&paste the examples and see how X-Plane reacts. 15.1 Strings inside of strings Let’s start with a typical script. You make a script printing out the solution of the universe. 1 2 xyz = 42 d o _ e v e r y _ d r a w ( " d r a w _ s t r i n g ( 5 0 , 5 0 0 , s t r i n g . f o r m a t ( " The s o l u t i o n i s %i " , xyz ) ) " ) The script seems to be ok, but FlyWithLua reacts like this (lines copied from Log.txt): 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 F l y W i t h L u a I n f o : Lua e n g i n e ( r e ) s t a r t e d . LUA_RUN = 2 , SDK_VERSION = 2 1 0 , XPLANE_VERSION = 1 0 1 0 1 , XPLANE_LANGUAGE = German and XPLANE_HOSTID = 1 F l y W i t h L u a : Load i n i f i l e . F l y W i t h L u a : S e a r c h i n g f o r Lua s c r i p t f i l e s F l y W i t h L u a : S o r t i n g Lua s c r i p t f i l e s F l y W i t h L u a : S t a r t l o a d i n g s c r i p t f i l e R e s o u r c e s / p l u g i n s / F l y W i t h L u a / S c r i p t s / ALPHA . l u a F l y W i t h L u a I n f o : No c l a s s i c commands f o u n d i n s i d e t h e f i l e . F l y W i t h L u a : Lua h a s c r a s h e d , c a n ’ t e x e c u t e s c r i p t f i l e : R e s o u r c e s / p l u g i n s / F l y W i t h L u a / S c r i p t s / ALPHA . l u a F l y W i t h L u a : E r r o r l o a d i n g s c r i p t f i l e R e s o u r c e s / p l u g i n s / F l y W i t h L u a / S c r i p t s / ALPHA . l u a F l y W i t h L u a Debug I n f o : The Lua s t a c k c o n t a i n s t h e f o l l o w i n g e l e m e n t s : R e s o u r c e s / p l u g i n s / F l y W i t h L u a / S c r i p t s / ALPHA . l u a : 2 : ’ ) ’ e x p e c t e d n e a r ’ The ’ F l y W i t h L u a Debug I n f o : Debug f i l e w r i t t e n t o "<>/ FlyWithLua_Debug . t x t " . Lua crashes! The reason is, that you put a string into a string the wrong way. The reason is not, that you forgot a closing bracket. Always think twice when reading an error message! A string can be set between the char »"« (double quotation), but you can also use »’« (single quotation). This can set an inner string into an outer string. The correct script looks like this: 1 2 xyz = 42 d o _ e v e r y _ d r a w ( ’ d r a w _ s t r i n g ( 5 0 , 5 0 0 , s t r i n g . f o r m a t ( " The s o l u t i o n i s %i " , xyz ) ) ’ ) Or you can write it this way: 1 2 xyz = 42 d o _ e v e r y _ d r a w ( " d r a w _ s t r i n g ( 5 0 , 5 0 0 , s t r i n g . f o r m a t ( ’ The s o l u t i o n i s %i ’ , xyz ) ) " ) Page 91 of 122 15.2 Multiple line strings 15 TAKE LUA INTO CONSIDERATION 15.2 Multiple line strings If you want to declare a string over more than one line, you must use the delimiters »[[« (at the beginning) and »]]« (at the end). This can look like: 1 2 3 xyz = 42 d o _ e v e r y _ d r a w ( [ [ d r a w _ s t r i n g ( 5 0 , 5 2 0 , " T h i s i s a l l you n e e d t o know : " ) d r a w _ s t r i n g ( 5 0 , 5 0 0 , s t r i n g . f o r m a t ( " The s o l u t i o n i s %i " , xyz ) ) ] ] ) There are a lot of more tricks handling strings. Please take a look into the official Lua 5.2 manual. 15.3 Global or local variables? We will now name the script from above to ALPHA.lua and write a second script named BETA.lua: 1 xyz = 17 2 3 4 5 function beta_print_xyz () d r a w _ s t r i n g ( 5 0 , 4 5 0 , s t r i n g . f o r m a t ( "BETA : xyz = %i " , xyz ) ) end 6 7 do_every_draw ( " b e t a _ p r i n t _ x y z ( ) " ) This will produce no error message, all scripts run as they should, but the solution is now 17. Why? It is because the first line of BETA.lua will overwrite variable xyz first initialized in line 1 of script ALPHA.lua (all scripts are compiled in alphabetical order). All variables used in Lua are global variables, and every code can see and manipulate the variable! But you can make a variable local. Make it local means, it can’t be seen from code outside of the block it is declared in. To declare a local variable, just write »local« in front of the first use of the variable. Change BETA.lua to: 1 l o c a l xyz = 17 2 3 4 5 function beta_print_xyz () d r a w _ s t r i n g ( 5 0 , 4 5 0 , s t r i n g . f o r m a t ( "BETA : xyz = %i " , xyz ) ) end 6 7 do_every_draw ( " b e t a _ p r i n t _ x y z ( ) " ) Now the solution switches back to 42. What happened here? The first line of BETA.lua declares a local variable xyz. The global variable xyz from script ALPHA.lua still exists, but a local variable will hide a global variable with an identical name. All code inside the active block will now use the local variable instead of the global. A function defined in the same block will now see the local variable xyz of the code block made by the script file (a file is a block of code). Page 92 of 122 15.4 Tables are tables 15 TAKE LUA INTO CONSIDERATION Now we know how clever a script can be written, we will change the code of ALPHA.lua too. This will avoid conflicts with scripts using a global variable named xyz, even if we do not have a script like this at the moment. But we could get one from a friend or download one from the Internet. 1 2 3 l o c a l xyz = 42 d o _ e v e r y _ d r a w ( [ [ d r a w _ s t r i n g ( 5 0 , 5 2 0 , " T h i s i s a l l you n e e d t o know : " ) d r a w _ s t r i n g ( 5 0 , 5 0 0 , s t r i n g . f o r m a t ( " The s o l u t i o n i s %i " , xyz ) ) ] ] ) Crash! Was it not clever to declare every variable as local? What happened now? The reason is, that the command do_every_draw() always creates a totally new block of code, containing all strings from all scripts in it. This block is completely independent to all other code you give to Lua. If you write a debug file, you can see this block inside the file FlyWithLua_debug.txt in X-Plane’s main directory. The functions do_sometimes(), do_often() and do-every_frame() behave the same way. add_macro() creates two independent blocks of code and create_command() creates three independent blocks of code, while add_ATC_macro() creates only one. The trick in BETA.lua is, that we call a function beta_print_xyz() from the do_every_draw() command block. Lua jumps into the function beta_print_xyz(). This is a jump into the block automatically created by the file BETA.lua. Important info: A jump from one block into another block (as made by a function call) changes the view onto the variables (if local variables are declared)! Conclusion: If you want to write code that does not make unwanted side-effects to other scripts, keep all variables local and mask everything you want to be done continuously in a function. Next conclusion: As the function DataRef() always creates a global variable, you must write classic code, if you plan to share a script online. You will never know if the user, who downloaded your script, uses the same DataRef() binding as your script does. As long as you are the only user of your script, you can do whatever you want. Just like in real life. 15.4 Tables are tables If you use tables in Lua, you can’t use the name of the table as a variable. This code will fail without a warning (it simply redefines the variable to hold a number instead of a table): 1 2 l o c a l b a t t e r y = d a t a r e f _ t a b l e ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " ) battery = 1 If you want to turn on the battery, use this code: 1 2 l o c a l b a t t e r y = d a t a r e f _ t a b l e ( " sim / c o c k p i t / e l e c t r i c a l / b a t t e r y _ o n " ) battery [0] = 1 Page 93 of 122 16 FLYWITHLUA 2.7.X ADDED NEW FEATURES 16 FlyWithLua 2.7.x Added New Features Starting with version 2.7.0, FlyWithLua now supports creating native plugin windows. There are two ways to use them: graphics module: You can use the existing graphics module of FlyWithLua to draw into the windows, see examples included in the "Scripts (disabled)" folder that start with "floating_wnd". imgui: FlyWithLua now supports creating imgui GUIs. imgui is an awesome 3rd party GUI library that makes it really easy to create stunning GUIs. See the examples included in the "Scripts (disabled)" folder that start with "imgui". Why would you want to use these new capabilities instead of using existing GUI libraries written in lua? The windows can be moved and popped out to a native window The performance is better because the GUI isn’t created by lua (for imgui windows) Imgui is more powerful than the existing libraries - for example, you can load an image file (even png and jpg) and use it as a background image or button or whatever... Both window types automatically (!) work in VR Here are a few teasing images to see what is now possible. Page 94 of 122 16 FLYWITHLUA 2.7.X ADDED NEW FEATURES Page 95 of 122 16 FLYWITHLUA 2.7.X ADDED NEW FEATURES Page 96 of 122 16 FLYWITHLUA 2.7.X ADDED NEW FEATURES The following functions don’t work with floating windows in VR anymore, no matter if using imgui or FWL: do_every_frame, do_on_mouse_click. Instead of using do_every_frame, you can register a drawing function that is called everytime XPlane draws the window. It gets passed the x and y coordinates of the window’s lower left corner as absolute coordinates. This is required because FWL’s graphics functions take absolute screen coordinates and you have to make sure that you actually draw into the window. For example, if you pass (0, 0) as coordinates to a lua function that draws a dot, the pixel would always be in the lower left of the screen because they are absolute coordinates. But if the window doesn’t cover that part of the screen, the pixel wouldn’t be visible in the window. So if you want to put the pixel in the lower left of the window, pass (x, y) as coordinates to the lua functions. If you want a button on top of the window with a padding of 10px from the borders, you would draw a rectangle with the first point at (x + 10, y + height - 10). I attached a sketch to visualize this. x and y are passed as paramters to your drawing function by FlyWithLua and you must use them to place your pixels inside the window. And instead of do_on_mouse_click, you can register an onclick function with the floating window. That function is only called if the click actually happened inside the window, so it gets passed relative coordinates already. So if the user clicks on the upper left of your button’s rectangle, it would get passed (10, height - 10). You would then have to set a flag to remember that the user clicked somewhere so that you can then check this variable the next time the window is drawn. Using imgui, you don’t need to care about drawing the controls yourself or mapping the clicks back to the controls. In fact, you don’t need to register an onclick function at all. imgui is an immediate GUI, that means the controls don’t have any internal state. You can’t ask a checkbox if it is currently checked and you can’t set a callback function to a button. They just have no state at all. How does it work then? For imgui windows, you register a builder function. That function is also called everytime the window is drawn. If you want to place a button, you just call imgui.Button("My Button") When you do that, imgui does not only draw the button, it also checks if the mouse button was released over this area in the last frame. And if that’s the case, the Button function returns true! It’s a little weird to get used to in the beginning, but all imgui functions actually return boolean values to indicate whether they were just being used. So if you want to react on the button, you just do something like this: if imgui.Button("My Button") then command_once("some x-plane command") end In the next frame, the mouse will not have been released over this button again, so the button doesn’t return true in the next frame, resulting in your action being only called once as desired. The same goes for other controls like sliders and checkboxes - they return a boolean to indicate if you need to react on a change. Page 97 of 122 16 FLYWITHLUA 2.7.X ADDED NEW FEATURES Page 98 of 122 17 FLOATING WINDOWS 17 Floating Windows Starting with version 2.7.0 NG we have floating windows that can be in 2d, popped out or in VR. They are X-Plane 11 Window API style windows that can be created with Lua. There are examples of these kind of windows in the "Scripts (disabled)" folder anf are prefaced with "floating_wnd_". 17.1 demo_floating_wnd = float_wnd_create(800,450,1,false) This is how you create a floating windows in lua. "demo_floating_wnd" is the name of the handle of the window that is created. The first two parameters specify the size of the window in boxels. Boxels are scalable pixels.If the UI scale is set to 100% in the settings, a boxel equals a pixel. However, if the user sets the UI scale to more than 100%, a boxel will be scaled to span multiple pixels. The third parameter specifies the window decoration. The following decorations are possible: 0: "X-Plane will draw no decoration for your window, and apply no automatic click handlers. The window will not stop click from passing through its bounds. This is suitable for "windows" which request, say, the full screen bounds, then only draw in a small portion of the available area." 1: "The default decoration for "native" windows, like the map. Provides a solid background, as well as click handlers for resizing and dragging the window." 2: "X-Plane will draw no decoration for your window, nor will it provide resize handlers for your window edges, but it will stop clicks from passing through your windows bounds." 3: "Like 2, but with resizing; X-Plane will draw no decoration for your window, but it will stop clicks from passing through your windows bounds, and provide automatic mouse handlers for resizing." The last parameter configures whether you want to use imgui for the floating window. The return value is a handle to the window that can be used to configure additional things. 17.2 float_wnd_set_title(demo_floating_wnd, "floating window Demo") This function sets the title of the window if it has a decoration. The first parameter must be a handle to the window previously created with the float_wind_create. The second parameter is the title of the window. Page 99 of 122 17.3 float_wnd_set_position(demo_floating_wnd, 775, 650) 17 FLOATING WINDOWS 17.3 float_wnd_set_position(demo_floating_wnd, 775, 650) This function sets the initial position of the window. The first parameter must be a handle to a window previously created with float_wnd_create. The second and third parameters set the initial position of the window. 17.4 float_wnd_set_ondraw(demo_floating_wnd, "on_draw_floating_window") This function sets the name of the ondraw function. The first parameter must be a handle to a window previously created with float_wnd_create. The second parameter sets the name of the ondraw function. 17.5 float_wnd_set_onclick(demo_floating_wnd, "on_click_floating_window") This function sets the name of the onclick function. The first parameter must be a handle to a window previously created with float_wnd_create. The second parameter sets the name of the onclick function. 17.6 float_wnd_set_onclose(demo_floating_wnd, "on_close_floating_window") This function sets the name of the onclose function. The first parameter must be a handle to a window previously created with float_wnd_create. The second parameter sets the name of the onclose function. 17.7 xplm_wnd = float_wnd_get_xplm_handle(demo_floating_wnd) This function gets the xplm handle of the window created above to be used with ffi. "xplm_wnd" is the name of the xplm handle of the window that was created. The first parameter must be a handle to a window previously created with float_wnd_create. Page 100 of 122 17.8 function on_draw_floating_window(demo_floating_wnd, x3, 17 y3) FLOATING WINDOWS 17.8 function on_draw_floating_window(demo_floating_wnd, x3, y3) The first parameter must be a handle to a window previously created with float_wnd_create. x and y are the origin of the window, i.e. the lower left x increases to the right, y increases to the top 17.9 function on_click_floating_window(demo_floating_wnd, x3, y3) The first parameter must be a handle to a window previously created with float_wnd_create. x and y are relative from the origin of the window, i.e. the lower left 17.10 function on_close_floating_window(demo_floating_wnd) When on_close it called, it is illegal to do anything with the wnd variable. It is also not allowed to create new windows in on_close! The first parameter must be a handle to a window previously created with float_wnd_create. Page 101 of 122 17.11 is_popped = float_wnd_is_popped(fwl_wnd) 17 FLOATING WINDOWS 17.11 is_popped = float_wnd_is_popped(fwl_wnd) "is_popped" is the name of the boolean to know if the created window is popped out. The parameter must be a handle to a window previously created with float_wnd_create. 17.12 is_visible = float_wnd_get_visible(fwl_wnd) "is_visable" is the name of the boolean to know if the created window is visable. The parameter must be a handle to a window previously created with float_wnd_create. 17.13 is_front = float_wnd_is_front(fwl_wnd) "is_front" is the name of the boolean to know if the created window is in front. The parameter must be a handle to a window previously created with float_wnd_create. 17.14 is_vr = float_wnd_is_vr(fwl_wnd) "is_vr" is the name of the boolean to know if the created window is in vr. The parameter must be a handle to a window previously created with float_wnd_create. 17.15 function float_wnd_bring_to_front(fwl_wnd) The function brings the created window to the front. The parameter must be a handle to a window previously created with float_wnd_create. 17.16 function float_wnd_set_resizing_limits(fwl_wnd, minWidth, minHeight, maxWidth, maxHeight) The function sets the resizing limits for the created window. The first parameter must be a handle to a window previously created with float_wnd_create. The second parameter sets the minimum width for the created window. The third parameter sets the minimum height for the created window. The forth parameter sets the maximum width for the created window. The fifth paramter sets the maximum height for the created window. Page 102 of 122 17.17 function float_wnd_set_positioning_mode(fwl_wnd, 1, -1) 17 FLOATING WINDOWS 17.17 function float_wnd_set_positioning_mode(fwl_wnd, 1, -1) This function sets the positioning mode of the created window. The first parameter must be a handle to a window previously created with float_wnd_create. The second parameter sets the policy for how X-Plane will position whe created window. Here are the aviable values for the policy. xplm_WindowPositionFree xplm_WindowCenterOnMonitor xplm_WindowFullScreenOnMonitor xplm_WindowFullScreenOnAllMonitors xplm_WindowPopOut xplm_WindowVR 0 1 2 3 4 5 The thrid parameter is the monitor index. 17.18 function float_wnd_set_gravity(fwl_wnd, gLeft, gTop, gRight, gBottom) A window’s "gravity" controls how the window shifts as the whole X-Plane window resizes. A gravity of 1 means the window maintains its positioning relative to the right or top edges, 0 the left/bottom, and 0.5 keeps it centered. Default gravity is (0, 1, 0, 1), meaning your window will maintain its position relative to the top left and will not change size as its containing window grows. If you wanted, say, a window that sticks to the top of the screen (with a constant height), but which grows to take the full width of the window, you would pass (0, 1, 1, 1). Because your left and right edges would maintain their positioning relative to their respective edges of the screen, the whole width of your window would change with the X-Plane window. Only applies to modern windows. (Windows created using the deprecated XPLMCreateWindow(), or windows compiled against a pre-XPLM300 version of the SDK will simply get the default gravity.) The first parameter must be a handle to a window previously created with float_wnd_create. The remaining parameters follow the discription above. Page 103 of 122 17.19 function float_wnd_set_geometry(fwl_wnd, nLeft, nTop,17 nRight, FLOATING nBottom)WINDOWS 17.19 function float_wnd_set_geometry(fwl_wnd, nLeft, nTop, nRight, nBottom) This routine allows you to set the position and size of a normal 2d window. for floating window, equivalent to - void XPLMSetWindowGeometry(XPLMWindowID inWindowID, int inLeft, int inTop, int inRight, int inBottom)) The units and coordinate system match those of XPLMGetWindowGeometry(). That is, modern windows use global desktop boxel coordinates, while legacy windows use pixels relative to the main X-Plane display. Note that this only applies to "floating" windows (that is, windows that are drawn within the X-Plane simulation windows, rather than being "popped out" into their own first-class operating system windows). To set the position of windows whose positioning mode is xplm_WindowPopOut, you’ll need to instead use XPLMSetWindowGeometryOS(). The first parameter must be a handle to a window previously created with float_wnd_create. The remaining parameters set position and size of the created window. 17.20 function float_wnd_set_geometry(fwl_wnd, pLeft, pTop, pRight, pBottom) This routine allows you to set the position and size of a poped out or OS 2d window. for OS window, equivalent to - void XPLMSetWindowGeometryOS(XPLMWindowID inWindowID, int inLeft, int inTop, int inRight, int inBottom) This routine allows you to set the position and size, in operating system pixel coordinates, of a popped out window (that is, a window whose positioning mode is xplm_WindowPopOut, which exists outside the X-Plane simulation window, in its own first-class operating system window). Note that you are responsible for ensuring both that your window is popped out (using XPLMWindowIsPoppedOut()) and that a monitor really exists at the OS coordinates you provide (using XPLMGetAllMonitorBoundsOS()). The first parameter must be a handle to a window previously created with float_wnd_create. The remaining parameters set position and size of the created window. Page 104 of 122 17.21 function float_wnd_set_geometry(fwl_wnd, vrWidth, vrHeight) 17 FLOATING WINDOWS 17.21 function float_wnd_set_geometry(fwl_wnd, vrWidth, vrHeight) This routine allows you to set the size of a VR window. for VR window, equivalent to - void XPLMSetWindowGeometryVR(XPLMWindowID inWindowID, int widthBoxels, int heightBoxels) This routine allows you to set the size, in boxels, of a window in VR (that is, a window whose positioning mode is xplm_WindowVR). Note that you are responsible for ensuring your window is in VR (using XPLMWindowIsInVR()). The first parameter must be a handle to a window previously created with float_wnd_create. The remaining parameters sets the size of the created window increasing or decreasing from a centered position. Page 105 of 122 17.22 nwinLeft, nwinTop, nwinRight, nwinBottom = float_wnd_get_geometry(fwl_wnd) 17 FLOATING WINDOWS 17.22 nwinLeft, nwinTop, nwinRight, nwinBottom = float_wnd_get_geometry(fwl_wnd) This routine returns the position and size of a normal 2d window. The units and coordinate system vary depending on the type of window you have. for floating window, equivalent to - void XPLMGetWindowGeometry(XPLMWindowID inWindowID, int * outLeft, int * outTop, int * outRight, int * outBottom) If this is a legacy window (one compiled against a pre-XPLM300 version of the SDK, or an XPLM300 window that was not created using XPLMCreateWindowEx()), the units are pixels relative to the main X-Plane display. If, on the other hand, this is a new X-Plane 11-style window (compiled against the XPLM300 SDK and created using XPLMCreateWindowEx()), the units are global desktop boxels. Pass NULL to not receive any paramter. "nwinLeft" Name of variable for the left side of the created window. "nwinTop" Name of variable for the top of the created window. "nwinRight" Name of variable for the right side of the created window. "nwinBottom" Name of variable for the bottom of the created window. The first parameter must be a handle to a window previously created with float_wnd_create. Page 106 of 122 17.23 pwinLeft, pwinTop, pwinRight, pwinBottom = float_wnd_get_geometry(fwl_wnd) 17 FLOATING WINDOWS 17.23 pwinLeft, pwinTop, pwinRight, pwinBottom = float_wnd_get_geometry(fwl_wnd) This routine returns the position and size of a poped out or OS window. The units and coordinate system vary depending on the type of window you have. for OS window, equivalent to - void XPLMGetWindowGeometryOS(XPLMWindowID inWindowID, int * outLeft, int * outTop, int * outRight, int * outBottom) This routine allows you to set the position and size, in operating system pixel coordinates, of a popped out window (that is, a window whose positioning mode is xplm_WindowPopOut, which exists outside the X-Plane simulation window, in its own first-class operating system window). Note that you are responsible for ensuring both that your window is popped out (using XPLMWindowIsPoppedOut()) and that a monitor really exists at the OS coordinates you provide (using XPLMGetAllMonitorBoundsOS()). "pwinLeft" Name of variable for the left side of the created window. "pwinTop" Name of variable for the top of the created window. "pwinRight" Name of variable for the right side of the created window. "pwinBottom" Name of variable for the bottom of the created window. The first parameter must be a handle to a window previously created with float_wnd_create. 17.24 vrwinWidth, vrwinHeight = float_wnd_get_geometry(fwl_wnd) This routine returns the size of a VR window. for VR window, equivalent to - void XPLMGetWindowGeometryVR(XPLMWindowID inWindowID, int * outWidthBoxels, int * outHeightBoxels) Returns the width and height, in boxels, of a window in VR. Note that you are responsible for ensuring your window is in VR (using XPLMWindowIsInVR()). "vrWidth" Name of variable to set vr width of the created window. "vrHeight" Name of variable to set vr height of the created window. The first parameter must be a handle to a window previously created with float_wnd_create. Page 107 of 122 17.25 msgx, msgy = XPLMGetMouseLocationGlobal() 17 FLOATING WINDOWS 17.25 msgx, msgy = XPLMGetMouseLocationGlobal() Returns the current mouse location in global desktop boxels. Unlike XPLMGetMouseLocation(), the bottom left of the main X-Plane window is not guaranteed to be (0, 0)—instead, the origin is the lower left of the entire global desktop space. In addition, this routine gives the real mouse location when the mouse goes to X-Plane windows other than the primary display. Thus, it can be used with both pop-out windows and secondary monitors. This is the mouse location function to use with modern windows (i.e., those created by XPLMCreateWindowEx()). Pass NULL to not receive info about either parameter. "msgx" Name of variable to get global x mouse location of the created window. "msgy" Name of variable to get global y mouse location of the created window. 17.26 ssWidth, ssHeight = XPLMGetScreenSize() This routine returns the size of the main X-Plane OpenGL window in pixels. This number can be used to get a rough idea of the amount of detail the user will be able to see when drawing in 3-d. "ssWidth" Name of variable to get screen size width. "ssHeight" Name of variable to get screen size height. Page 108 of 122 17.27 bLeft, bTop, bRight, bBottom = XPLMGetScreenBoundsGlobal() 17 FLOATING WINDOWS 17.27 bLeft, bTop, bRight, bBottom = XPLMGetScreenBoundsGlobal() This routine returns the bounds of the "global" X-Plane desktop, in boxels. Unlike the nonglobal version XPLMGetScreenSize(), this is multi-monitor aware. There are three primary consequences of multimonitor awareness. First, if the user is running X-Plane in full-screen on two or more monitors (typically configured using one full-screen window per monitor), the global desktop will be sized to include all XPlane windows. Second, the origin of the screen coordinates is not guaranteed to be (0, 0). Suppose the user has two displays side-by-side, both running at 1080p. Suppose further that they’ve configured their OS to make the left display their "primary" monitor, and that X-Plane is running in full-screen on their right monitor only. In this case, the global desktop bounds would be the rectangle from (1920, 0) to (3840, 1080). If the user later asked X-Plane to draw on their primary monitor as well, the bounds would change to (0, 0) to (3840, 1080). Finally, if the usable area of the virtual desktop is not a perfect rectangle (for instance, because the monitors have different resolutions or because one monitor is configured in the operating system to be above and to the right of the other), the global desktop will include any wasted space. Thus, if you have two 1080p monitors, and monitor 2 is configured to have its bottom left touch monitor 1’s upper right, your global desktop area would be the rectangle from (0, 0) to (3840, 2160). Note that popped-out windows (windows drawn in their own operating system windows, rather than "floating" within X-Plane) are not included in these bounds. "bLeft" Name of variable for the left bounds of the "global" X-Plane desktop, in boxels. "bTop" Name of variable for the top bounds of the "global" X-Plane desktop, in boxels. "bRight" Name of variable for the right bounds of the "global" X-Plane desktop, in boxels. "bBottom" Name of variable for the bottom bounds of the "global" X-Plane desktop, in boxels. Page 109 of 122 17.28 tOS = XPLMGetAllMonitorBoundsOS() 17 FLOATING WINDOWS 17.28 tOS = XPLMGetAllMonitorBoundsOS() This routine immediately calls you back with the bounds (in pixels) of each monitor within the operating system’s global desktop space. Note that unlike XPLMGetAllMonitorBoundsGlobal(), this may include monitors that have no X-Plane window on them. Note that this function’s monitor indices match those provided by XPLMGetAllMonitorBoundsGlobal(), but the coordinates are different (since the X-Plane global desktop may not match the operating system’s global desktop, and one X-Plane boxel may be larger than one pixel). This function returns the following values in a lua multi-dimensional table (an element for each monitor): int MonitorIndex; int inLeft; int inTop; int inRight; int inBottom; To access the values in FWL, use the following format: tOS[x].yyyy, where x = monitor number, and yyyy is table member. Example for monitor 1: tOS[1].inLeft; tOS[1].inBottom; monitor 2: tOS[2].inLeft; tOS[2].inBottom 17.29 tGB = XPLMGetAllMonitorBoundsGlobal() This routine immediately calls you back with the bounds (in boxels) of each full-screen X-Plane window within the X-Plane global desktop space. Note that if a monitor is *not* covered by an X-Plane window, you cannot get its bounds this way. Likewise, monitors with only an X-Plane window (not in full-screen mode) will not be included. If X-Plane is running in full-screen and your monitors are of the same size and configured contiguously in the OS, then the combined global bounds of all full-screen monitors will match the total global desktop bounds, as returned by XPLMGetScreenBoundsGlobal(). (Of course, if X-Plane is running in windowed mode, this will not be the case. Likewise, if you have differently sized monitors, the global desktop space will include wasted space.) Note that this function’s monitor indices match those provided by XPLMGetAllMonitorBoundsOS(), but the coordinates are different (since the X-Plane global desktop may not match the operating system’s global desktop, and one X-Plane boxel may be larger than one pixel due to 150% or 200% scaling). This function returns the following values in a lua multi-dimensional table (an element for each monitor): int MonitorIndex; int inLeft; int inTop; int inRight; int inBottom; To access the values in FWL, use the following format: tOS[x].yyyy, where x = monitor number, and yyyy is table member. Example for monitor 1: tOS[1].inLeft; tOS[1].inBottom; monitor 2: tOS[2].inLeft; tOS[2].inBottom Page 110 of 122 18 IMGUI WINDOWS 18 Imgui Windows Starting with version 2.7.0 NG we have imgui windows that can be in 2d, popped out or in VR. They are X-Plane 11 Window API style windows that use imgui widgets and can be created with Lua. There are examples of these kind of windows in the "Scripts (disabled)" folder and are prefaced with "imgui_". 18.1 demo_wnd = float_wnd_create(800, 450, 1, true) The first two parameters specify the size of the window in boxels. Boxels are scalable pixes. If the UI scale is set to 100% in the settings, a boxel equals a pixel. However, if the user sets the UI scale to more than 100%, a boxel will be scaled to span multiple pixels. The third parameter specifies the window decoration. The following decorations are possible: 0: "X-Plane will draw no decoration for your window, and apply no automatic click handlers. The window will not stop click from passing through its bounds. This is suitable for "windows" which request, say, the full screen bounds, then only draw in a small portion of the available area." 1: "The default decoration for "native" windows, like the map. Provides a solid background, as well as click handlers for resizing and dragging the window." 2: "X-Plane will draw no decoration for your window, nor will it provide resize handlers for your window edges, but it will stop clicks from passing through your windows bounds." 3: "Like 2, but with resizing; X-Plane will draw no decoration for your window, but it will stop clicks from passing through your windows bounds, and provide automatic mouse handlers for resizing." The last parameter configures whether you want to use imgui for the floating window. The return value is a handle to the window that can be used to configure additional things. 18.2 float_wnd_set_title(demo_wnd, "Demo Window") This function sets the title of the window if it has a decoration. The first parameter must be a handle to a window previously created with float_wnd_create. The second parameter is the title of the window. Page 111 of 122 18.3 float_wnd_set_position(demo_wnd, 775, 650) 18 IMGUI WINDOWS 18.3 float_wnd_set_position(demo_wnd, 775, 650) This function sets the initial position of the window. The first parameter must be a handle to a window previously created with float_wnd_create. The second and third parameters set the initial position of the window. 18.4 float_wnd_set_imgui_builder(demo_wnd, "build_demo") imgui GUIs are built each frame. You will need to supply a function that is called every frame. The first parameter must be a handle to a window previously created with float_wnd_create. The second parameter must be the name of a function. Note that unlike other function in FlyWithLua, you can only pass the name of a function here, not an arbitrary lua string. This function will be called for every frame while the window is visible, so you don’t need an additional do_every_frame unless you also need to do things while the window is closed. 18.5 float_wnd_set_onclose(demo_wnd, "closed_demo") If the window has a decoration with a close button (like decoration 1), you might need a way to know when the users closes the window. This function can be used to setup a function to be called when the user closes the window. The first parameter must be a handle to a window previously created with float_wnd_create and the second parameter must be the name of a function. Note that unlike other function in FlyWithLua, you can only pass the name of a function here, not an arbitrary lua string. This function will be called when the user closes the window. After the window is closed, its builder function will not be called again and it is illegal to use the window handle variable returned by float_wnd_create, e.g. it is illegal to set the window title after it was closed. Page 112 of 122 18.6 image_id = float_wnd_load_image(SCRIPT_DIRECTORY .. "/imgui_demo.jpg") 18 IMGUI WINDOWS 18.6 image_id = float_wnd_load_image(SCRIPT_DIRECTORY .. "/imgui_demo.jpg") imgui supports drawing images, so let’s load one. The images must always be loaded globally to make sure they are only loaded once per script! imgui widgets have no state, that means we need to store states of checkboxes, radio buttons etc. globally. makeRed = false sliderVal = 0 choice = 1 angle = 0 text = "" 18.7 function build_demo(wnd, x, y) This function is called for every frame. Use this function to create your GUI. The first parameter (wnd) is the handle of the window. It is the same handle as the one returned by float_wnd_create. It is required if you have multiple windows that use the same builder function so you know which of these windows is currently being built. The x and y parameters are the current position of the window in OpenGL coordinates, i.e. the position of the lower left corner in global screen coordinates. These coordinates are only needed for using the graphics module on top of the imgui GUI. If you only use imgui, these coordinates are not required because imgui has its own coordinate system inside the window. 18.8 function closed_demo(wnd) This function is called when the user closes the window. Drawing or calling imgui functions is not allowed in this function as the window is already destroyed. Page 113 of 122 19 DEBUGGING 19 Debugging To domesticate the beast is a hard job sometimes. If you are searching an error inside your code, FlyWithLua gives you a nice little weapon. Just click the menu entry Write Debug file. It writes a file FlyWithLua_debug.txt into X-Plane’s main directory. You may observe the file with a text editor able to recognize changes. Inside the file you will find the content of Lua’s stack, the list of DataRefs handled by the plugin and the content of the internal tables used by the plugin. The debug info will show all global variables too. They are shown as name plus value. The debug info ends with a list of all global tables and functions. A lot of information to dive deep into FlyWithLua’s bowels. If this is still not enough, and you need to look into a local variable or need a continuous observation of a variable, write a debug code like this (debugging the example one section before): 1 2 d a t a r e f ( " c a r b _ h e a t " , " sim / c o c k p i t 2 / e n g i n e / a c t u a t o r s / c a r b _ h e a t _ r a t i o " , " w r i t a b l e " , 0 ) d a t a r e f ( " i c i n g " , " sim / f l i g h t m o d e l / e n g i n e / ENGN_crbice " , " r e a d o n l y " ) 3 4 5 6 function automatic_carb_heat () −− debug t h e v a r i a b l e m y _ d e b u g _ s t r i n g = s t r i n g . f o r m a t ( " b e f o r e : i c i n g = %f , c a r b _ h e a t = %f " , i c i n g , carb_heat ) 7 8 9 10 11 12 13 14 15 −− i f c a r b i s i c i n g t h e n s w i t c h h e a t on i f ( i c i n g == 1 ) t h e n carb_heat = 1 else carb_heat = 0 end m y _ d e b u g _ s t r i n g = m y _ d e b u g _ s t r i n g . . s t r i n g . f o r m a t ( " ==> a f t e r : i c i n g = %f , c a r b _ h e a t = %f " , i c i n g , c a r b _ h e a t ) end 16 17 do_often ( " automatic_carb_heat () " ) 18 19 d o _ e v e r y _ d r a w ( " i f m y _ d e b u g _ s t r i n g t h e n d r a w _ s t r i n g ( 2 0 , 2 0 , m y _ d e b u g _ s t r i n g ) end " ) This example is pretty overobserved, but it shows how to observe more than one variable more than one time. Please recognize the code in line 19. If you do not use the if statement, FlyWithLua will crash, when the first drawing frame has nothing to draw. If you don’t like the if statement in the last line, start the script with: my_debug_string = "No values at the moment." After you solved the problem, simply remove the debug code. Page 114 of 122 20 INTEGRATE FOREIGN LIBRARIES 20 Integrate foreign libraries FlyWithLua provides a lot of features and functions, but there may be some function missing, and you want to integrate a foreign library filling the hole. Some libraries are easy to integrate. All you have to do is, to place the related files into the Modules folder. If you choose a binary file, it must be compiled with MinGW on Windows or GnuCC on Linux or Macintosh. Windows binaries end with .dll, Linux and Macintosh binaries end with .so (it is important on Linux, that you copy the files into the Modules folder, FlyWithLua will not search in your Linux library path). We will look at two libraries made by Gerald Franz, LuaXML and proteaAudio, to show the integration on an example. For the LuaXML library, we only need to download it and copy the files LuaXML.lua and LuaXML_lib.dll (or LuaXML_lib.dll) from the downloaded ZIP archive into the Modules folder. To integrate the proteaAudio library, we do the same for the binary file (proAudioRt.dll or proAudioRt.so), but we have to do something more. Using the library in a script file will force you not only to load the module (with a code line containing require("proAudioRt")). You will also have to initialize and close the sound device. This can be done in a script for sure, but if multiple scripts use the same sound module and each script opens and closes the sound device independent from the other scripts, your set of scripts will fail. To avoid a fail like this, FlyWithLua gives you two files in it’s main folder you can edit, user.ini and user.exit. During startup, the script user.ini will be executed. So fill this script with code like this: 1 2 3 4 5 6 7 −− l o a d t h e s o u n d l i b r a r y and i n i t i t require ( " proAudioRt " ) i f proAudio . c r e a t e ( ) then logMsg ( " F l y W i t h L u a I n f o : Sound e n g i n e i s r u n n i n g . " ) else logMsg ( " F l y W i t h L u a E r r o r : Can ’ t i n i t t h e s o u n d l i b r a r y ! " ) end And shut down the sound device inside the file user.exit: 1 2 3 −− s h o t down s o u n d e n g i n e proAudio . d e s t r o y ( ) logMsg ( " F l y W i t h L u a I n f o : Sound e n g i n e i s down . " ) Now every script behaves like the sound library was »battery included« in FlyWithLua. The LuaXML library is included into FlyWithLua. If you want to use proteaAudio, you will have to integrate it as described. Two example files for proteaAudio are included into FlyWithLua, to help you making the first steps with this library. Page 115 of 122 21 THE NEW 64-BIT ARCHITECTURE 21 The new 64-bit architecture Since X-Plane 10.20 the simulator can run in 64-bit. As the simulator integrates the plugins as dynamic libraries, the plugins will also have to use 64-bit code. FlyWithLua can run under both conditions, 32 and 64 bit. But you will sometimes have to write different code for 32 and 64 bit. 21.1 Architecture exclusive script loading If you rename a script, and change it’s ending from .lua to .lua64, FlyWithLua will only load this file, if it is running in 64-bit mode. If you rename it’s ending to .lua32, the script file will only be loaded when running in 32-bit. Else the file will be ignored. You can also use .Lua64, .LUA64, Lua32 or LUA32. If you use the ending .lua, .Lua or .LUA, it will be loaded in 32-bit and 64-bit architecture. As a script is usually architecture independent, the ending without the number should be the normal case. 21.2 Checking architecture inside a script You can read out the global variable SYSTEM_ARCHITECTURE. It contains a number, either 64 or 32. If you need different code, use this system variable together with an if statement. 21.3 64-bit DLLs If you want to use a dynamic library, you can call the library with the require command. The DLL’s name must end as »_64.dll« on Windows, »_64.so« on Mac or Linux, if it has to be loaded in 64-bit. If your script starts with a line like this: 1 r e q u i r e ( " LuaXML_lib " ) there must be two DLLs to make it running under 32-bit and 64-bit, »LuaXML_lib.dll« and »LuaXML_lib_64.dll«. This is for Windows system, for Mac and Linux, you will have to provide »LuaXML_lib.so« and »LuaXML_lib_64.so«. Page 116 of 122 22 Q&A 22 Q&A At the end of this little manual, I will answer to some popular questions. 22.1 My script doesn’t work. What can I do? 22.1.1 Check the debug info file and Log.txt Take a look into the file Log.txt provided by X-Plane. The easy way is clicking on X-Plane’s menu item »special—>show dev console«, the more complex way is to use a text editor or a command line tool like tail. Search the end of the file Log.txt for error reports generated by FlyWithLua. If FlyWithLua provides error or warning messages while loading or executing your script, pass the messages through your brain, take your conclusions and edit your script file to eliminate the problem. If Lua stops working, it will automatically write a debug file in X-Plane’s main directory called FlyWithLua_debug.txt. This file will also help to understand what’s going wrong. If it wasn’t generated automatically, force a generation by clicking on »Plugins—>FlyWithLua—>Write Debug file«. 22.1.2 Check for conflicts to other scripts If the analysis made above didn’t solve your problem, move all other scripts to the disabled folder and restart Lua. If the problem disappears, you might have a conflict to other scripts. Move the other scripts back to the active scripts folder one by one and restart Lua after each file movement. By this way, you will find out the scripts, who causes the conflict. Compare the code of all scripts causing the error. In most cases you will use a variable with a global scope in more than one script. Rename the variable in one of the scripts or make them local. Do this only for »normal« variables, not for variables connected to DataRefs. If a DataRef is connected to more than one variable, rename all variables of this DataRef to be unique. FlyWithLua prints a warning message into the Log.txt file, if a DataRef is connected to multiple variables. 22.1.3 I really can’t solve it! Then ask for assistance. Most X-Plane related forums in the Internet will provide a developer corner. Page 117 of 122 22.2 How to ask the developer of FlyWithLua for help? 22 Q&A 22.2 How to ask the developer of FlyWithLua for help? If nobody can solve your problem, you may ask me for some help. Write a mail to: carsten.lynker@gmail.com Your mail should use the word »FlyWithLua« in it’s subject line and must have a meaningful subject, otherwise it will be deleted without reading. The body of your mail must describe your problem in detail. As I have no time for an endless Q&A ping-pong, a mail without a detailed report of your problem will move directly into dev/null. Attached to the mail, you must send the FlyWithLua_debug.txt file and all lines of your Log.txt file starting with »FlyWithLua« (make a tail | grep on Mac or Linux or use mTail on Windows). And of course attach your script. If you are using foreign binary libraries — forget it. I will not execute foreign binaries on my development system. If you use plain Lua modules in your script, not included to FlyWithLua, attach them as well. A reaction to a mail can take several days (or weeks if I am busy). Keep in mind that this is my hobby, not my job. 22.3 Is the debug file privacy safe? By default, yes. It will hide your VATSIM ID, your real name and (most important) your VATSIM password – as long as you only use the pre-defined variables to access XSquawkBox. If you are using DataRef access to online plugins (an IVAO client or something alike), delete private info manually before sending the file to friend, a forum or to me. 22.4 Where are the Splines? Splines are a nice feature of Button2DataRef, but FlyWithLua does not provide spline functions. Button2DataRef isn’t able to calculate math expressions. So you must use splines. In most cases splines are slower than direct formulas. If you want to script a relationship defined by data points, take a look on this website: http://arachnoid.com/polysolve/index.html#The_Program It will help you to find a low-order polynomial expression to calculate your relationship. If you want to make a mixture spline, where half way of your input device divides the mixture by 1/4 to 3/4, enter these data points into the website: Page 118 of 122 22.5 Feature requests 22 Q&A 0 0 0.5 0.75 1 1 Click on the »Output Form« button to get a result like this: 1 2 3 4 5 Mode : n o r m a l x , y a n a l y s i s Polynomial degree 2 , 3 x , y data pairs . C o r r e l a t i o n c o e f f i c i e n t ( r ^2) = 1.0000000000000004 S t a n d a r d e r r o r = 4 . 7 4 2 8 7 4 8 4 0 2 6 7 5 4 7 e −16 C o e f f i c i e n t o u t p u t form : m a t h e m a t i c a l f u n c t i o n : 6 7 8 9 f ( x ) = −1.6653345369377348 e −016 ∗ x ^0 + 2 . 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 7 e +000 ∗ x ^1 + −1.0000000000000027 e +000 ∗ x ^2 10 11 C o p y r i g h t ( c ) 2 0 1 2 , P . L u t u s −− h t t p : / / a r a c h n o i d . com . A l l R i g h t s R e s e r v e d . The value -1.6653345369377348e-016 represents nearly nothing, so your script can use a calculation like this: y = 0*x^0 + 2*x^1 + (-1)*x^2 y = 2*x - x^2 22.5 Feature requests If you need a feature FlyWithLua doesn’t provide, you can start your own trunk of development, as this is allowed by the license we use10 . But please first try to ask, if we implement your wish into the official FlyWithLua project. This can help to protect users getting confused by thousands of different flavors of Lua Scripting plugins. 22.6 Can I store Lua files inside the aircraft’s folder? Not by default. You should leave an aircraft as it is, to avoid surprising behavior when you (or X-Plane) update the plane. But if you want to store all your joystick configuration inside the aircraft’s folder, write a »loader« script into the script folder and fill it like this: 1 d o f i l e ( AIRCRAFT_PATH . . " m y _ j o y s t i c k _ c o n f i g u r a t i o n . l u a " ) Then this script will load the other script, but make sure that every plane you load has a file that can be read. It can be empty, if you have no Lua code for that plane. 10 See section »License« for detailed info. Page 119 of 122 22.7 I want full access to X-Plane’s plugin SDK! 22 Q&A 22.7 I want full access to X-Plane’s plugin SDK! The plugin SDK is a complete interface to X-Plane from Delphi, C or C++, but not from Lua. FlyWithLua offers the most important functions of the plugin SDK for little scripts. But you may want more. The nice side effect choosing LuaJIT as the Lua engine of FlyWithLua is, that you can take advantage of the FFI library. It allows to access every C-function from Lua. And you can declare C-types to be used in Lua code. For a documentation of FFI see: http://luajit.org/ext_ffi.html Let’s make an example. Reloading the scenery is possible, even if there is no XPLMReloadScenery() function provided by FlyWithLua. We will create a macro doing the reload: 12 13 14 −− l o a d t h e XPLM l i b r a r y local f f i = require ( " f f i " ) l o c a l XPLM = f f i . l o a d ( "XPLM" ) 15 16 17 −− d e f i n e t h e XPLMReloadScenery ( ) C−f u n c t i o n t o be u s e d f r o m Lua f f i . c d e f ( " v o i d XPLMReloadScenery ( v o i d ) " ) 18 19 20 21 22 23 −− d e f i n e a g l o b a l f u n c t i o n ( macros can o n l y a c c e s s g l o b a l f u n c t i o n s ) function reload_scenery () XPLMSpeakString ( " P l e a s e w a i t , X−P l a n e i s r e l o a d i n g t h e s c e n e r y . Don ’ t p a n i c ! The s i m u l a t o r w i l l f r e e z e f o r a moment . " ) XPLM . XPLMReloadScenery ( ) end 24 25 26 −− c r e a t e t h e macro add_macro ( " R e l o a d t h e s c e n e r y " , " r e l o a d _ s c e n e r y ( ) " ) Other example files can be found in the Scripts (disabled) folder. If you want to use the FFI access to the plugin SDK in more than one script, you can collect all definitions of the C-functions in a Lua module and start your script with: require "name of your module" Page 120 of 122 22.8 Using Lua For Windows 22 Q&A 22.8 Using Lua For Windows If you don’t like the extreme fast LuaJIT engine, and you are using Lua For Windows on a Windows system, then you can replace the Lua engine of FlyWithLua. When you change the engine, you will loose the libraries included by LuaJIT (FFI and Bit). Doing the following steps changes the Lua engine of FlyWithLua. Every change on basic binary files is not supported by the FlyWithLua developer team! If you get problems after an engine change, solve it on your own or come back to the official FlyWithLua. This has to be done: a) Download Lua For Windows and run the Installer. b) Delete the file FlyWithLua\lua51.dll in X-Plane’s plugin folder. c) Go into the subfolder Lua\5.1\, of your just installed Lua For Windows. d) Copy the files lua51.dll and lua5.1.dll into FlyWithLua, replacing the old lua51.dll. e) Copy all files you find inside the Lua For Windows folder Lua\5.1\clibs into the FlyWithLua subfolder Modules. That’s all. Now you are using the Lua interpreter provided by Lua For Windows, not the Just-InTime compiler LuaJIT. If you don’t have thousands of lines with Lua code, you will not »feel« the difference in execution speed. But you now access an enormous pool of usable libraries like Lua Socket. Page 121 of 122 24 LICENSE 23 Credits This plugin was made by Carsten Lynker (main developer, Windows code), Snagar (early Macintosh and Linux code), William B. Good (Macintosh and Linux code) and Ingo Alm (lector and bughunter). FlyWithLua uses source code from HIDAPI, created by Alan Ott, Signal 11 Software, from The LuaJIT Project, created by Mike Pall and code snippets from Kein-Hong Man’s luahidapi. FlyWithLua uses two library packages made by Gerald Franz, LuaXML and proteaAudio. Graphic design of the logo by Alexandre Nakonechnyj, Copyright Âl’ 1998 Lua.org11 . If you find a bug produced by the plugin, please email: carsten.lynker@gmail.com Happy landings! 24 License Copyright (c) 2012 Carsten Lynker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 For more information about license and design of Lua see www.lua.org. Page 122 of 122
Source Exif Data:
File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.5 Linearized : No Page Count : 122 Page Mode : UseOutlines Author : Title : FlyWithLua Quick Reference Manual Subject : Creator : LaTeX with hyperref package Producer : pdfTeX-1.40.16 Create Date : 2018:11:21 18:27:34-05:00 Modify Date : 2018:11:21 18:27:34-05:00 Trapped : False PTEX Fullbanner : This is pdfTeX, Version 3.14159265-2.6-1.40.16 (TeX Live 2015/Debian) kpathsea version 6.2.1EXIF Metadata provided by EXIF.tools