startEngine = func { sel = props.globals.getNode("/sim/input/selected"); engs = props.globals.getNode("/controls/engines").getChildren("engine"); for(i=0; i 0) { val = -val; } props.setAll("/controls/engines/engine", "throttle", val); } # Joystick axis handlers (uses cmdarg). Shouldn't be called from # other contexts. throttleAxis = func { val = cmdarg().getNode("setting").getValue(); if(size(arg) > 0) { val = -val; } props.setAll("/controls/engines/engine", "throttle", (1 - val)/2); } mixtureAxis = func { val = cmdarg().getNode("setting").getValue(); if(size(arg) > 0) { val = -val; } props.setAll("/controls/engines/engine", "mixture", (1 - val)/2); } propellerAxis = func { val = cmdarg().getNode("setting").getValue(); if(size(arg) > 0) { val = -val; } props.setAll("/controls/engines/engine", "propeller-pitch", (1 - val)/2); } carbHeatAxis = func { val = cmdarg().getNode("setting").getValue(); if(size(arg) > 0) { val = -val; } props.setAll("/controls/anti-ice/engine", "carb-heat", (1 - val)/2); } ## # Wrapper around stepProps() which emulates the "old" flap behavior for # configurations that aren't using the new mechanism. # flapsDown = func { if(arg[0] == 0) { return; } if(props.globals.getNode("/sim/flaps") != nil) { stepProps("/controls/flight/flaps", "/sim/flaps", arg[0]); return; } # Hard-coded flaps movement in 3 equal steps: val = 0.3333334 * arg[0] + getprop("/controls/flight/flaps"); if(val > 1) { val = 1 } elsif(val < 0) { val = 0 } setprop("/controls/flight/flaps", val); } stepSpoilers = func { if(props.globals.getNode("/sim/spoilers") != nil) { stepProps("/controls/flight/spoilers", "/sim/spoilers", arg[0]); return; } # Hard-coded spoilers movement in 4 equal steps: val = 0.25 * arg[0] + getprop("/controls/flight/spoilers"); if(val > 1) { val = 1 } elsif(val < 0) { val = 0 } setprop("/controls/flight/spoilers", val); } stepSlats = func { if(props.globals.getNode("/sim/slats") != nil) { stepProps("/controls/flight/slats", "/sim/slats", arg[0]); return; } # Hard-coded slats movement in 4 equal steps: val = 0.25 * arg[0] + getprop("/controls/flight/slats"); if(val > 1) { val = 1 } elsif(val < 0) { val = 0 } setprop("/controls/flight/slats", val); } ## # Steps through an "array" of property settings. The first argument # specifies a destination property. The second is a string containing # a global property tree. This tree should contain an array of # indexed children. This function will maintain a # child, which contains the index of the currently # active setting. The third argument specifies an integer delta, # indicating how many steps to move through the setting array. # Note that because of the magic of the property system, this # mechanism works for all scalar property types (bool, int, double, # string). # # TODO: This interface could easily be extended to allow for wrapping, # in addition to clamping, allowing a "cycle" of settings to be # defined. It could also be hooked up with the interpolate() call, # which would allow the removal of the transition-time feature from # YASim. Finally, other pre-existing features (the views and engine # magnetos, for instance), work similarly but not compatibly, and # could be integrated. # stepProps = func { dst = props.globals.getNode(arg[0]); array = props.globals.getNode(arg[1]); delta = arg[2]; if(dst == nil or array == nil) { return; } sets = array.getChildren("setting"); curr = array.getNode("current-setting", 1).getValue(); if(curr == nil) { curr = 0; } curr = curr + delta; if (curr < 0) { curr = 0; } elsif(curr >= size(sets)) { curr = size(sets) - 1; } array.getNode("current-setting").setIntValue(curr); dst.setValue(sets[curr].getValue()); } ## # "Slews" a property smoothly, without dependence on the simulator # frame rate. The first argument is the property name. The second is # a rate, in units per second. NOTE: this modifies the property for # the current frame only; it is intended to be called by bindings # which repeat each frame. If you want to cause motion over time, see # interpolate(). # slewProp = func { prop = arg[0]; delta = arg[1] * getprop("/sim/time/delta-realtime-sec"); setprop(prop, getprop(prop) + delta); } # Standard trim rate, in units per second. Remember that the full # range of a trim axis is 2.0. Should probably read this out of a # property... TRIM_RATE = 0.045; ## # Handlers. These are suitable for binding to repeatable button press # events. They are *not* good for binding to the keyboard, since (at # least) X11 synthesizes its own key repeats. # elevatorTrim = func { slewProp("/controls/flight/elevator-trim", arg[0] * TRIM_RATE); } aileronTrim = func { slewProp("/controls/flight/aileron-trim", arg[0] * TRIM_RATE); } rudderTrim = func { slewProp("/controls/flight/rudder-trim", arg[0] * TRIM_RATE); } THROTTLE_RATE = 0.33; adjThrottle = func { adjEngControl("throttle", arg[0]); } adjMixture = func { adjEngControl("mixture", arg[0]); } adjCondition = func { adjEngControl("condition", arg[0]); } adjPropeller = func { adjEngControl("propeller-pitch", arg[0]); } adjEngControl = func { engs = props.globals.getNode("/controls/engines").getChildren("engine"); selected = props.globals.getNode("/sim/input/selected"); delta = arg[1] * THROTTLE_RATE * getprop("/sim/time/delta-realtime-sec"); foreach(e; engs) { if(selected.getChild("engine", e.getIndex(), 1).getBoolValue()) { node = e.getNode(arg[0], 1); node.setValue(node.getValue() + delta); } } } ## # arg[0] is the throttle increment # arg[1] is the auto-throttle target speed increment incThrottle = func { auto = props.globals.getNode("/autopilot/locks/speed", 1); if ( !auto.getValue() ) { engs = props.globals.getNode("/controls/engines").getChildren("engine"); foreach(e; engs) { node = e.getNode("throttle", 1); node.setValue(node.getValue() + arg[0]); if ( node.getValue() < -1.0 ) { node.setValue( -1.0 ); } if ( node.getValue() > 1.0 ) { node.setValue( 1.0 ); } } } else { node = props.globals.getNode("/autopilot/settings/target-speed-kt", 1); if ( node.getValue() == nil ) { node.setValue( 0.0 ); } node.setValue(node.getValue() + arg[1]); if ( node.getValue() < 0.0 ) { node.setValue( 0.0 ); } } } ## # arg[0] is the aileron increment # arg[1] is the autopilot target heading increment incAileron = func { auto = props.globals.getNode("/autopilot/locks/heading", 1); if ( !auto.getValue() ) { aileron = props.globals.getNode("/controls/flight/aileron"); if ( aileron.getValue() == nil ) { aileron.setValue( 0.0 ); } aileron.setValue(aileron.getValue() + arg[0]); if ( aileron.getValue() < -1.0 ) { aileron.setValue( -1.0 ); } if ( aileron.getValue() > 1.0 ) { aileron.setValue( 1.0 ); } } if ( auto.getValue() == "dg-heading-hold" ) { node = props.globals.getNode("/autopilot/settings/heading-bug-deg", 1); if ( node.getValue() == nil ) { node.setValue( 0.0 ); } node.setValue(node.getValue() + arg[1]); if ( node.getValue() < 0.0 ) { node.setValue( node.getValue() + 360.0 ); } if ( node.getValue() > 360.0 ) { node.setValue( node.getValue() - 360.0 ); } } if ( auto.getValue() == "true-heading-hold" ) { node = props.globals.getNode("/autopilot/settings/true-heading-deg", 1); if ( node.getValue() == nil ) { node.setValue( 0.0 ); } node.setValue(node.getValue() + arg[1]); if ( node.getValue() < 0.0 ) { node.setValue( node.getValue() + 360.0 ); } if ( node.getValue() > 360.0 ) { node.setValue( node.getValue() - 360.0 ); } } } ## # arg[0] is the elevator increment # arg[1] is the autopilot target alitude increment incElevator = func { auto = props.globals.getNode("/autopilot/locks/altitude", 1); if ( !auto.getValue() or auto.getValue() == 0 ) { elevator = props.globals.getNode("/controls/flight/elevator"); if ( elevator.getValue() == nil ) { elevator.setValue( 0.0 ); } elevator.setValue(elevator.getValue() + arg[0]); if ( elevator.getValue() < -1.0 ) { elevator.setValue( -1.0 ); } if ( elevator.getValue() > 1.0 ) { elevator.setValue( 1.0 ); } } elsif ( auto.getValue() == "altitude-hold" ) { node = props.globals.getNode("/autopilot/settings/target-altitude-ft", 1); if ( node.getValue() == nil ) { node.setValue( 0.0 ); } node.setValue(node.getValue() + arg[1]); if ( node.getValue() < 0.0 ) { node.setValue( 0.0 ); } } } ## # Joystick axis handlers. Don't call from other contexts. # elevatorTrimAxis = func { elevatorTrim(cmdarg().getNode("value").getValue()); } aileronTrimAxis = func { aileronTrim(cmdarg().getNode("value").getValue()); } rudderTrimAxis = func { rudderTrim(cmdarg().getNode("value").getValue()); } ## # Gear handling. # gearDown = func { if (arg[0] < 0) { setprop("/controls/gear/gear-down", 0); } elsif (arg[0] > 0) { setprop("/controls/gear/gear-down", 1); } } gearToggle = func { gearDown(getprop("/controls/gear/gear-down") > 0 ? -1 : 1); } ## # Brake handling. # fullBrakeTime = 0.5; applyBrakes = func(v, which = 0) { if (which <= 0) { interpolate("/controls/gear/brake-left", v, fullBrakeTime); } if (which >= 0) { interpolate("/controls/gear/brake-right", v, fullBrakeTime); } } applyParkingBrake = func(v) { if (!v) { return; } var p = "/controls/gear/brake-parking"; setprop(p, var i = !getprop(p)); return i; }