diff --git a/Nasal/controls.nas b/Nasal/controls.nas new file mode 100644 index 000000000..67c4d6163 --- /dev/null +++ b/Nasal/controls.nas @@ -0,0 +1,172 @@ +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); +} + +## +# Wrapper around stepProps() which emulates the "old" flap behavior for +# configurations that aren't using the new mechanism. +# +stepFlaps = func { + 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); +} + +## +# 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. +# +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]); } +adjPropeller = func { + adjEngControl("propeller-pitch", arg[0]); } + +adjEngControl = func { + engs = props.globals.getNode("/controls/engines").getChildren("engine"); + delta = arg[1] * THROTTLE_RATE * getprop("/sim/time/delta-reltime-sec"); + foreach(e; engs) { + node = e.getNode(arg[0], 1); + node.setValue(node.getValue + delta); + } +} + +## +# 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()); } + diff --git a/Nasal/gui.nas b/Nasal/gui.nas new file mode 100644 index 000000000..b22fc0794 --- /dev/null +++ b/Nasal/gui.nas @@ -0,0 +1,69 @@ +## +# Pop up a "tip" dialog for a moment, then remove it. The delay in +# seconds can be specified as the second argument. The default is 1 +# second. Note that the tip dialog is a shared resource. If +# someone else comes along and wants to pop a tip up before your delay +# is finished, you lose. :) +# +popupTip = func { + msg = arg[0]; + delay = if(size(arg) > 1) {arg[1]} else {DELAY}; + labelNode.setValue(msg); + + # The "width" value here is a hardcoded hack that assumes 9 pixels + # as a "typical" character width and adds some extra for the + # widgets to play with. Bah. Pui needs a layout engine... + width = 9 * size(msg) + 40; + popupNode.getNode("width").setIntValue(width); + popupNode.getNode("x").setIntValue((screenWProp.getValue() - width)/2); + popupNode.getNode("y").setIntValue(screenHProp.getValue() - 140); + + popdown(); + fgcommand("dialog-new", popupNode); + fgcommand("dialog-show", tipArg); + + currTimer = currTimer + 1; + thisTimer = currTimer; + settimer(func { if(currTimer == thisTimer) { popdown() } }, DELAY); +} + +######################################################################## +# Private Stuff: +######################################################################## + +## +# Initialize property nodes via a timer, to insure the props module is +# loaded. See notes in view.nas +# +screenWProp = screenHProp = popupNode = labelNode = tipArg = nil; +INIT = func { + screenWProp = props.globals.getNode("/sim/startup/xsize"); + screenHProp = props.globals.getNode("/sim/startup/ysize"); + + # Set up the dialog property node: + tmpl = { name : "PopTip", modal : 0, + x : 100, y : 100, width : 120, height : 40, + text : { x : 10, y : 6, label : "NOTE" } }; + popupNode = props.Node.new(tmpl); + labelNode = popupNode.getNode("text/label"); + fgcommand("dialog-new", popupNode); + + # Cache the command argument for popup/popdown + tipArg = props.Node.new({ "dialog-name" : "PopTip" }); +} +settimer(INIT, 0); + +## +# How many seconds do we show the tip? +# +DELAY = 1.0; + +## +# Pop down the tip dialog, if it is visible. +# +popdown = func { fgcommand("dialog-close", tipArg); } + +# Marker for the "current" timer. This value gets stored in the +# closure of the timer function, and is used to check that there +# hasn't been a more recent timer set that should override. +currTimer = 0; diff --git a/Nasal/props.nas b/Nasal/props.nas index cee3defb4..a48c99ef6 100644 --- a/Nasal/props.nas +++ b/Nasal/props.nas @@ -132,3 +132,20 @@ wrapNode = func { { parents : [Node], _g : arg[0] } } # props.globals = wrapNode(_globals()); +## +# Sets all indexed property children to a single value. arg[0] +# specifies a property name (e.g. /controls/engines/engine), arg[1] a +# path under each node of that name to set (e.g. "throttle"), arg[2] +# is the value. +# +setAll = func { + node = props.globals.getNode(arg[0]); + if(node == nil) { return; } + name = node.getName(); + node = node.getParent(); + if(node == nil) { return; } + children = node.getChildren(); + foreach(c; children) { + c.getNode(arg[1], 1).setValue(arg[2]); + } +} diff --git a/Nasal/view.nas b/Nasal/view.nas index 10c0ed9f5..44bb895ea 100644 --- a/Nasal/view.nas +++ b/Nasal/view.nas @@ -20,24 +20,9 @@ # dependencies between modules. # fovProp = nil; -screenProp = nil; -popupNode = nil; -labelNode = nil; -fovDialog = nil; INIT = func { fovProp = props.globals.getNode("/sim/current-view/field-of-view"); - screenProp = props.globals.getNode("/sim/startup/xsize"); - - # Set up the dialog property node: - tmpl = { name : "fov", modal : 0, width : 120, height : 40, - text : { x : 10, y : 6, label : "FOV:" } }; - popupNode = props.Node.new(tmpl); - text = popupNode.getNode("text", 1); - labelNode = popupNode.getNode("text/label"); - fgcommand("dialog-new", popupNode); - # Cache the command argument for popup/popdown - fovDialog = props.Node.new({ "dialog-name" : "fov" }); } settimer(INIT, 0); @@ -53,10 +38,9 @@ settimer(INIT, 0); STEPS = 40; ACUITY = 1/60; # Maximum angle subtended by one pixel (== 1 arc minute) max = min = mul = 0; - calcMul = func { max = 120; # Fixed at 120 degrees - min = screenProp.getValue() * ACUITY; + min = getprop("/sim/startup/xsize") * ACUITY; mul = math.exp(math.ln(max/min) / STEPS); } @@ -79,7 +63,7 @@ increase = func { if(val == max) { return; } if(val > max) { val = max } fovProp.setDoubleValue(val); - popup(val); + gui.popupTip("FOV: " ~ format(val)); } ## @@ -88,22 +72,58 @@ increase = func { decrease = func { calcMul(); val = fovProp.getValue() / mul; - if(val == min) { return; } - #if(val < min) { val = min } # Comment out for now. + msg = "FOV: " ~ format(val); + if(val < min) { msg = msg ~ " (overzoom)"; } fovProp.setDoubleValue(val); - popup(val); + gui.popupTip(msg); } ## -# Pop up the "fov" dialog for a moment. +# Handler. Reset view to default. # -popdown = func { fgcommand("dialog-close", fovDialog); } -popup = func { - # Make sure it isn't already showing, initialize it, show it, - # and kill it automatically after a second - popdown(); - labelNode.setValue("FOV: " ~ format(arg[0])); - fgcommand("dialog-show", fovDialog); - settimer(popdown, 1); +resetView = func { + setprop("/sim/current-view/goal-heading-offset-deg", + getprop("/sim/current-view/config/heading-offset-deg")); + setprop("/sim/current-view/goal-pitch-offset-deg", + getprop("/sim/current-view/config/pitch-offset-deg")); + setprop("/sim/current-view/field-of-view", + getprop("/sim/current-view/config/field-of-view-deg")) } +## +# Handler. Step to the next view. +# +stepView = func { + curr = getprop("/sim/current-view/view-number"); + views = props.globals.getNode("/sim").getChildren("view"); + curr = curr + arg[0]; + if (curr < 0) { curr = size(views) - 1; } + elsif(curr >= size(views)) { curr = 0; } + setprop("/sim/current-view/view-number", curr); + + # And pop up a nice reminder + gui.popupTip(views[curr].getNode("name").getValue()); +} + +## +# Standard view "slew" rate, in degrees/sec. +# +VIEW_PAN_RATE = 60; + +## +# Pans the view horizontally. The argument specifies a relative rate +# (or number of "steps" -- same thing) to the standard rate. +# +panViewDir = func { + controls.slewProp("/sim/current-view/goal-heading-offset-deg", + arg[0] * VIEW_PAN_RATE); +} + +## +# Pans the view vertically. The argument specifies a relative rate +# (or number of "steps" -- same thing) to the standard rate. +# +panViewPitch = func { + controls.slewProp("/sim/current-view/goal-pitch-offset-deg", + arg[0] * VIEW_PAN_RATE); +} diff --git a/keyboard.xml b/keyboard.xml index 93fe74786..23d02cd15 100644 --- a/keyboard.xml +++ b/keyboard.xml @@ -159,90 +159,24 @@ calculated by adding 256 to the GLUT key value in glut.h. SPACE Fire Starter on Selected Engine(s) - - - /sim/input/selected/engine[0] - - property-assign - /controls/engines/engine[0]/starter - true + nasal + - - - - /sim/input/selected/engine[1] - - property-assign - /controls/engines/engine[1]/starter - true - - - - - /sim/input/selected/engine[2] - - property-assign - /controls/engines/engine[2]/starter - true - - - - - /sim/input/selected/engine[3] - - property-assign - /controls/engines/engine[3]/starter - true - - - property-assign - /controls/engines/engine[0]/starter - false - - - property-assign - /controls/engines/engine[1]/starter - false - - - property-assign - /controls/engines/engine[2]/starter - false - - - property-assign - /controls/engines/engine[3]/starter - false + nasal + - ! Select first engine - property-assign - /sim/input/selected/engine[0] - true - - - property-assign - /sim/input/selected/engine[1] - false - - - property-assign - /sim/input/selected/engine[2] - false - - - property-assign - /sim/input/selected/engine[3] - false + nasal + @@ -250,24 +184,8 @@ calculated by adding 256 to the GLUT key value in glut.h. # Select third engine - property-assign - /sim/input/selected/engine[0] - false - - - property-assign - /sim/input/selected/engine[1] - false - - - property-assign - /sim/input/selected/engine[2] - true - - - property-assign - /sim/input/selected/engine[3] - false + nasal + @@ -275,24 +193,8 @@ calculated by adding 256 to the GLUT key value in glut.h. $ Select fourth engine - property-assign - /sim/input/selected/engine[0] - false - - - property-assign - /sim/input/selected/engine[1] - false - - - property-assign - /sim/input/selected/engine[2] - false - - - property-assign - /sim/input/selected/engine[3] - true + nasal + @@ -424,19 +326,8 @@ calculated by adding 256 to the GLUT key value in glut.h. 5 Center aileron, elevator, and rudder. - property-assign - /controls/flight/aileron - 0.0 - - - property-assign - /controls/flight/elevator - 0.0 - - - property-assign - /controls/flight/rudder - 0.0 + nasal + @@ -516,24 +407,8 @@ calculated by adding 256 to the GLUT key value in glut.h. @ Select second engine - property-assign - /sim/input/selected/engine[0] - false - - - property-assign - /sim/input/selected/engine[1] - true - - - property-assign - /sim/input/selected/engine[2] - false - - - property-assign - /sim/input/selected/engine[3] - false + nasal + @@ -600,12 +475,8 @@ calculated by adding 256 to the GLUT key value in glut.h. V Scroll in reverse through views. - property-adjust - /sim/current-view/view-number - -1 - 0 - 5 - true + nasal + @@ -639,9 +510,8 @@ calculated by adding 256 to the GLUT key value in glut.h. [ Decrease flaps. - property-adjust - /controls/flight/flaps - -0.34 + nasal + @@ -649,9 +519,8 @@ calculated by adding 256 to the GLUT key value in glut.h. ] Increase flaps. - property-adjust - /controls/flight/flaps - 0.34 + nasal + @@ -670,36 +539,13 @@ calculated by adding 256 to the GLUT key value in glut.h. b Apply all brakes. - property-assign - /controls/gear/wheel[0]/brake - 1.0 - - - property-assign - /controls/gear/wheel[1]/brake - 1.0 - - - property-assign - /controls/gear/wheel[2]/brake - 1.0 + nasal + - Release all brakes. - property-assign - /controls/gear/wheel[0]/brake - 0.0 - - - property-assign - /controls/gear/wheel[1]/brake - 0.0 - - - property-assign - /controls/gear/wheel[2]/brake - 0.0 + nasal + @@ -708,34 +554,15 @@ calculated by adding 256 to the GLUT key value in glut.h. c Toggle 3D/2D cockpit - - /sim/allow-toggle-cockpit - - property-assign - /sim/current-view/heading-offset-deg - 0 - - - - /sim/allow-toggle-cockpit - - property-assign - /sim/current-view/pitch-offset-deg - 0 - - - - /sim/allow-toggle-cockpit - - property-toggle - /sim/view/internal - - - - /sim/allow-toggle-cockpit - - property-toggle - /sim/virtual-cockpit + nasal + @@ -853,14 +680,10 @@ calculated by adding 256 to the GLUT key value in glut.h. v Cycle view - Scroll in reverse through views. + Scroll through views. - property-adjust - /sim/current-view/view-number - 1 - 0 - 6 - true + nasal + @@ -885,40 +708,8 @@ calculated by adding 256 to the GLUT key value in glut.h. { Decrease Magneto on Selected Engine - first engine - - /sim/input/selected/engine[0] - - property-adjust - /controls/engines/engine[0]/magnetos - -1 - - - second engine - - /sim/input/selected/engine[1] - - property-adjust - /controls/engines/engine[1]/magnetos - -1 - - - third engine - - /sim/input/selected/engine[2] - - property-adjust - /controls/engines/engine[2]/magnetos - -1 - - - fourth engine - - /sim/input/selected/engine[3] - - property-adjust - /controls/engines/engine[3]/magnetos - -1 + nasal + @@ -926,40 +717,8 @@ calculated by adding 256 to the GLUT key value in glut.h. } Increase Magneto on Selected Engine - first engine - - /sim/input/selected/engine[0] - - property-adjust - /controls/engines/engine[0]/magnetos - 1 - - - second engine - - /sim/input/selected/engine[1] - - property-adjust - /controls/engines/engine[1]/magnetos - 1 - - - third engine - - /sim/input/selected/engine[2] - - property-adjust - /controls/engines/engine[2]/magnetos - 1 - - - fourth engine - - /sim/input/selected/engine[3] - - property-adjust - /controls/engines/engine[3]/magnetos - 1 + nasal + @@ -967,24 +726,8 @@ calculated by adding 256 to the GLUT key value in glut.h. ~ Select all engines - property-assign - /sim/input/selected/engine[0] - true - - - property-assign - /sim/input/selected/engine[1] - true - - - property-assign - /sim/input/selected/engine[2] - true - - - property-assign - /sim/input/selected/engine[3] - true + nasal + @@ -1145,19 +888,8 @@ calculated by adding 256 to the GLUT key value in glut.h. Keypad 5 Center aileron, elevator, and rudder. - property-assign - /controls/flight/aileron - 0.0 - - - property-assign - /controls/flight/elevator - 0.0 - - - property-assign - /controls/flight/rudder - 0.0 + nasal + diff --git a/mice.xml b/mice.xml index 961bb5122..74d04e1f4 100644 --- a/mice.xml +++ b/mice.xml @@ -157,20 +157,8 @@ The current mode for each mouse is held in the - - - - /devices/status/mice/mouse[0]/button[0] - - /devices/status/mice/mouse[0]/button[1] - - - property-adjust - /controls/engines/engine/throttle - -4.0 - 0.0 - 1.0 - false + nasal +