var startEngine = func { var sel = props.globals.getNode("/sim/input/selected"); var engs = props.globals.getNode("/controls/engines").getChildren("engine"); for(var i=0; i 0) val = -val; setprop(throttle, val); } } # Joystick axis handlers (uses cmdarg). Shouldn't be called from # other contexts. var throttleAxis = func { var val = cmdarg().getNode("setting").getValue(); if(size(arg) > 0) val = -val; var sel = props.globals.getNode("/sim/input/selected").getChildren("engine"); foreach(var n; sel) if(n.getValue()) setprop("/controls/engines/engine[" ~ n.getIndex() ~ "]/throttle", (1 - val)/2); } var mixtureAxis = func { var val = cmdarg().getNode("setting").getValue(); if(size(arg) > 0) { val = -val; } props.setAll("/controls/engines/engine", "mixture", (1 - val)/2); } var propellerAxis = func { var val = cmdarg().getNode("setting").getValue(); if(size(arg) > 0) { val = -val; } props.setAll("/controls/engines/engine", "propeller-pitch", (1 - val)/2); } var carbHeatAxis = func { var 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. # var 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: var val = 0.3333334 * arg[0] + getprop("/controls/flight/flaps"); if(val > 1) { val = 1 } elsif(val < 0) { val = 0 } setprop("/controls/flight/flaps", val); } var 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: var val = 0.25 * arg[0] + getprop("/controls/flight/spoilers"); if(val > 1) { val = 1 } elsif(val < 0) { val = 0 } setprop("/controls/flight/spoilers", val); } var 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: var 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. # var stepProps = func { var dst = props.globals.getNode(arg[0]); var array = props.globals.getNode(arg[1]); var delta = arg[2]; if(dst == nil or array == nil) { return; } var sets = array.getChildren("setting"); var 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(). # var slewProp = func(prop, delta) { delta *= 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... var 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. # var elevatorTrim = func { slewProp("/controls/flight/elevator-trim", arg[0] * TRIM_RATE); } var aileronTrim = func { slewProp("/controls/flight/aileron-trim", arg[0] * TRIM_RATE); } var rudderTrim = func { slewProp("/controls/flight/rudder-trim", arg[0] * TRIM_RATE); } var THROTTLE_RATE = 0.33; var adjThrottle = func { adjEngControl("throttle", arg[0]); } var adjMixture = func { adjEngControl("mixture", arg[0]); } var adjCondition = func { adjEngControl("condition", arg[0]); } var adjPropeller = func { adjEngControl("propeller-pitch", arg[0]); } var adjEngControl = func { var engs = props.globals.getNode("/controls/engines").getChildren("engine"); var selected = props.globals.getNode("/sim/input/selected"); var delta = arg[1] * THROTTLE_RATE * getprop("/sim/time/delta-realtime-sec"); foreach(var e; engs) { if(selected.getChild("engine", e.getIndex(), 1).getBoolValue()) { var 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 var incThrottle = func { var auto = props.globals.getNode("/autopilot/locks/speed", 1); var sel = props.globals.getNode("/sim/input/selected"); if (!auto.getValue()) { var engs = props.globals.getNode("/controls/engines").getChildren("engine"); foreach(var e; engs) { if(sel.getChild("engine", e.getIndex(), 1).getBoolValue()) { var node = e.getNode("throttle", 1); var val = node.getValue() + arg[0]; node.setValue(val < -1.0 ? -1.0 : val > 1.0 ? 1.0 : val); } } } else { var 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 var incAileron = func { var auto = props.globals.getNode("/autopilot/locks/heading", 1); if (!auto.getValue()) { var 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") { var 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") { var 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 altitude increment var incElevator = func { var auto = props.globals.getNode("/autopilot/locks/altitude", 1); if (!auto.getValue() or auto.getValue() == 0) { var 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") { var 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. # var elevatorTrimAxis = func { elevatorTrim(cmdarg().getNode("value").getValue()); } var aileronTrimAxis = func { aileronTrim(cmdarg().getNode("value").getValue()); } var rudderTrimAxis = func { rudderTrim(cmdarg().getNode("value").getValue()); } ## # Gear handling. # var gearDown = func(v) { if (v < 0) { setprop("/controls/gear/gear-down", 0); } elsif (v > 0) { setprop("/controls/gear/gear-down", 1); } } var gearToggle = func { gearDown(getprop("/controls/gear/gear-down") > 0 ? -1 : 1); } ## # Brake handling. # var fullBrakeTime = 0.5; var 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); } } var applyParkingBrake = func(v) { if (!v) { return; } var p = "/controls/gear/brake-parking"; setprop(p, var i = !getprop(p)); return i; } ## # Weapon handling. # var trigger = func(b) setprop("/controls/armament/trigger", b); var weaponSelect = func(d) { var ws = props.globals.getNode("/controls/armament/selected", 1); var n = ws.getValue(); if (n == nil) { n = 0; } ws.setIntValue(n + d); } ## # Communication. # var ptt = func(b) setprop("/instrumentation/comm/ptt", b);